@pixelbyte-software/pixcode 1.54.10 → 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.
Files changed (282) hide show
  1. package/CODE_OF_CONDUCT.md +41 -41
  2. package/CONTRIBUTING.md +156 -156
  3. package/LICENSE +21 -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 +435 -418
  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-DWOW_Jn0.js → index-Bcqs_1vc.js} +114 -114
  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/features.html +133 -133
  19. package/dist/generate-icons.js +48 -48
  20. package/dist/humans.txt +15 -15
  21. package/dist/icons/codex-white.svg +3 -3
  22. package/dist/icons/codex.svg +3 -3
  23. package/dist/icons/cursor-white.svg +11 -11
  24. package/dist/icons/qwen-logo.svg +14 -14
  25. package/dist/index.html +58 -58
  26. package/dist/landing.html +268 -268
  27. package/dist/llms-full.txt +119 -119
  28. package/dist/llms.txt +53 -53
  29. package/dist/manifest.json +60 -60
  30. package/dist/openapi.yaml +1696 -1696
  31. package/dist/orchestration.html +125 -125
  32. package/dist/robots.txt +4 -4
  33. package/dist/site.css +692 -692
  34. package/dist/sitemap.xml +51 -51
  35. package/dist/sw.js +132 -132
  36. package/dist-server/server/cli.js +100 -100
  37. package/dist-server/server/daemon/manager.js +33 -33
  38. package/dist-server/server/daemon-manager.js +64 -64
  39. package/dist-server/server/routes/commands.js +25 -25
  40. package/dist-server/server/routes/git.js +17 -17
  41. package/dist-server/server/routes/live-view.js +46 -46
  42. package/dist-server/server/services/public-api-manifest.js +51 -51
  43. package/package.json +224 -224
  44. package/scripts/fix-node-pty.js +67 -67
  45. package/scripts/github/create-v1.38-issues.mjs +351 -351
  46. package/scripts/github/create-vscode-workbench-issues.mjs +121 -121
  47. package/scripts/smoke/backend-resource-bounds.mjs +152 -152
  48. package/scripts/smoke/changes-panel-layout.mjs +48 -48
  49. package/scripts/smoke/chat-composer-fixed-layout.mjs +55 -55
  50. package/scripts/smoke/chat-message-timeline-order.mjs +41 -41
  51. package/scripts/smoke/chat-realtime-hydration.mjs +44 -44
  52. package/scripts/smoke/chat-session-provider-pools.mjs +35 -35
  53. package/scripts/smoke/chat-session-state.mjs +19 -19
  54. package/scripts/smoke/code-editor-theme.mjs +55 -55
  55. package/scripts/smoke/code-editor-vscode-engine.mjs +91 -91
  56. package/scripts/smoke/command-center-agent-writes.mjs +79 -79
  57. package/scripts/smoke/command-center-non-git.mjs +46 -46
  58. package/scripts/smoke/context-packet.mjs +43 -43
  59. package/scripts/smoke/control-room-ux-redesign.mjs +91 -91
  60. package/scripts/smoke/daemon-entrypoint.mjs +20 -20
  61. package/scripts/smoke/default-landing-routing.mjs +33 -33
  62. package/scripts/smoke/desktop-native-notifications.mjs +30 -30
  63. package/scripts/smoke/desktop-tray-icon.mjs +33 -33
  64. package/scripts/smoke/discord-release-workflow.mjs +24 -24
  65. package/scripts/smoke/git-install-update.mjs +255 -255
  66. package/scripts/smoke/handoff-artifact-protocol.mjs +50 -50
  67. package/scripts/smoke/live-view-diagnostics.mjs +53 -53
  68. package/scripts/smoke/live-view-environment.mjs +92 -92
  69. package/scripts/smoke/live-view-integration.mjs +450 -450
  70. package/scripts/smoke/mac-desktop-runtime.mjs +37 -37
  71. package/scripts/smoke/mobile-tunnel-guidance.mjs +29 -29
  72. package/scripts/smoke/mobile-ux.mjs +76 -76
  73. package/scripts/smoke/model-registry.mjs +36 -36
  74. package/scripts/smoke/multi-project-ui.mjs +45 -45
  75. package/scripts/smoke/multi-worker-slots.mjs +42 -42
  76. package/scripts/smoke/notification-center.mjs +87 -87
  77. package/scripts/smoke/notification-inapp-preference.mjs +23 -23
  78. package/scripts/smoke/notification-taxonomy.mjs +58 -58
  79. package/scripts/smoke/orchestration-api.mjs +172 -172
  80. package/scripts/smoke/orchestration-execution-dashboard.mjs +33 -33
  81. package/scripts/smoke/orchestration-live-run.mjs +176 -176
  82. package/scripts/smoke/orchestration-mobile-scroll.mjs +29 -29
  83. package/scripts/smoke/orchestration-model-sync.mjs +30 -30
  84. package/scripts/smoke/orchestration-permission-fallback.mjs +34 -34
  85. package/scripts/smoke/orchestration-runtime-guards.mjs +48 -48
  86. package/scripts/smoke/orchestration-user-facing-output.mjs +25 -25
  87. package/scripts/smoke/permission-policy.mjs +50 -50
  88. package/scripts/smoke/pixcode-workbench-1-48.mjs +167 -167
  89. package/scripts/smoke/provider-models-opencode-live.mjs +66 -66
  90. package/scripts/smoke/provider-rest-api.mjs +124 -124
  91. package/scripts/smoke/provider-selection-status.mjs +52 -52
  92. package/scripts/smoke/run-state-refresh.mjs +52 -52
  93. package/scripts/smoke/runtime-manager.mjs +99 -99
  94. package/scripts/smoke/shell-manual-disconnect.mjs +30 -30
  95. package/scripts/smoke/side-panel-editor-layout.mjs +34 -34
  96. package/scripts/smoke/static-root-routing.mjs +21 -21
  97. package/scripts/smoke/strict-handoff-compact.mjs +60 -60
  98. package/scripts/smoke/taskmaster-config.mjs +24 -24
  99. package/scripts/smoke/taskmaster-execution-telegram.mjs +3 -3
  100. package/scripts/smoke/taskmaster-onboarding.mjs +3 -3
  101. package/scripts/smoke/taskmaster-run-graph.mjs +3 -3
  102. package/scripts/smoke/telegram-control.mjs +331 -331
  103. package/scripts/smoke/tunnel-persistence.mjs +56 -56
  104. package/scripts/smoke/update-issue-progress.mjs +69 -69
  105. package/scripts/smoke/update-ux.mjs +55 -55
  106. package/scripts/smoke/v138-completion.mjs +132 -132
  107. package/scripts/smoke/v138-desktop-release-hardening.mjs +69 -69
  108. package/scripts/smoke/v138-diagnostics.mjs +63 -63
  109. package/scripts/smoke/v138-issue-planner.mjs +33 -33
  110. package/scripts/smoke/v143-remote-control.mjs +76 -76
  111. package/scripts/smoke/v144-production-loop.mjs +47 -47
  112. package/scripts/smoke/v145-platformization.mjs +46 -46
  113. package/scripts/smoke/v146-control-room-ui.mjs +150 -150
  114. package/scripts/smoke/version-modal-autoshow.mjs +29 -29
  115. package/scripts/smoke/vscode-workbench-layout.mjs +63 -63
  116. package/scripts/smoke/vscode-workbench-polish.mjs +461 -461
  117. package/scripts/smoke/workflow-fallback-replay.mjs +56 -56
  118. package/scripts/smoke/workflow-templates.mjs +43 -43
  119. package/scripts/smoke/workflow-trace-timeline.mjs +46 -46
  120. package/scripts/update-git-install.mjs +298 -298
  121. package/server/claude-sdk.js +927 -927
  122. package/server/cli.js +1106 -1106
  123. package/server/constants/config.js +4 -4
  124. package/server/cursor-cli.js +344 -344
  125. package/server/daemon/manager.js +563 -563
  126. package/server/daemon-manager.js +964 -964
  127. package/server/database/db.js +988 -988
  128. package/server/database/json-store.js +197 -197
  129. package/server/gemini-cli.js +550 -550
  130. package/server/gemini-response-handler.js +79 -79
  131. package/server/index.js +5671 -5671
  132. package/server/load-env.js +35 -35
  133. package/server/middleware/account-lockout.js +112 -112
  134. package/server/middleware/auth.js +277 -277
  135. package/server/middleware/rate-limiter.js +87 -87
  136. package/server/modules/orchestration/a2a/adapter-registry.ts +108 -108
  137. package/server/modules/orchestration/a2a/adapters/abstract-a2a.adapter.ts +63 -63
  138. package/server/modules/orchestration/a2a/adapters/claude-code.adapter.ts +286 -286
  139. package/server/modules/orchestration/a2a/adapters/codex.adapter.ts +244 -244
  140. package/server/modules/orchestration/a2a/adapters/cursor.adapter.ts +249 -249
  141. package/server/modules/orchestration/a2a/adapters/gemini.adapter.ts +248 -248
  142. package/server/modules/orchestration/a2a/adapters/json-event.adapter.test.ts +60 -60
  143. package/server/modules/orchestration/a2a/adapters/json-event.adapter.ts +101 -101
  144. package/server/modules/orchestration/a2a/adapters/opencode.adapter.ts +248 -248
  145. package/server/modules/orchestration/a2a/adapters/qwen.adapter.ts +248 -248
  146. package/server/modules/orchestration/a2a/agent-card.ts +55 -55
  147. package/server/modules/orchestration/a2a/auth.middleware.ts +29 -29
  148. package/server/modules/orchestration/a2a/bus.ts +46 -46
  149. package/server/modules/orchestration/a2a/routes.ts +586 -586
  150. package/server/modules/orchestration/a2a/task-dispatcher.ts +345 -345
  151. package/server/modules/orchestration/a2a/task-store.ts +178 -178
  152. package/server/modules/orchestration/a2a/types.ts +126 -126
  153. package/server/modules/orchestration/a2a/validator.ts +113 -113
  154. package/server/modules/orchestration/index.ts +99 -99
  155. package/server/modules/orchestration/preview/port-watcher.ts +112 -112
  156. package/server/modules/orchestration/preview/preview-proxy.ts +60 -60
  157. package/server/modules/orchestration/preview/types.ts +19 -19
  158. package/server/modules/orchestration/security/permission-policy.ts +401 -401
  159. package/server/modules/orchestration/tasks/orchestration-task-store.ts +41 -41
  160. package/server/modules/orchestration/tasks/orchestration-task.routes.ts +81 -81
  161. package/server/modules/orchestration/tasks/orchestration-task.service.ts +201 -201
  162. package/server/modules/orchestration/tasks/orchestration-task.types.ts +40 -40
  163. package/server/modules/orchestration/tasks/task-run-graph.ts +155 -155
  164. package/server/modules/orchestration/workflows/approval-queue.ts +106 -106
  165. package/server/modules/orchestration/workflows/built-in-workflows.ts +127 -127
  166. package/server/modules/orchestration/workflows/context-packet.ts +186 -186
  167. package/server/modules/orchestration/workflows/handoff-artifact.ts +175 -175
  168. package/server/modules/orchestration/workflows/workflow-fallback-policy.ts +161 -161
  169. package/server/modules/orchestration/workflows/workflow-replay.ts +254 -254
  170. package/server/modules/orchestration/workflows/workflow-runner.ts +2062 -2062
  171. package/server/modules/orchestration/workflows/workflow-store.ts +97 -97
  172. package/server/modules/orchestration/workflows/workflow-templates.ts +272 -272
  173. package/server/modules/orchestration/workflows/workflow-trace.ts +424 -424
  174. package/server/modules/orchestration/workflows/workflow.routes.ts +586 -586
  175. package/server/modules/orchestration/workflows/workflow.types.ts +111 -111
  176. package/server/modules/orchestration/workflows/workspace-target.ts +122 -122
  177. package/server/modules/orchestration/workspace/docker-workspace.ts +136 -136
  178. package/server/modules/orchestration/workspace/path-safety.ts +55 -55
  179. package/server/modules/orchestration/workspace/types.ts +52 -52
  180. package/server/modules/orchestration/workspace/workspace-manager.ts +102 -102
  181. package/server/modules/orchestration/workspace/worktree-workspace.ts +126 -126
  182. package/server/modules/providers/index.ts +2 -2
  183. package/server/modules/providers/list/claude/claude-auth.provider.ts +146 -146
  184. package/server/modules/providers/list/claude/claude-mcp.provider.ts +135 -135
  185. package/server/modules/providers/list/claude/claude-sessions.provider.ts +306 -306
  186. package/server/modules/providers/list/claude/claude.provider.ts +15 -15
  187. package/server/modules/providers/list/codex/codex-auth.provider.ts +117 -117
  188. package/server/modules/providers/list/codex/codex-mcp.provider.ts +135 -135
  189. package/server/modules/providers/list/codex/codex-sessions.provider.ts +319 -319
  190. package/server/modules/providers/list/codex/codex.provider.ts +15 -15
  191. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +147 -147
  192. package/server/modules/providers/list/cursor/cursor-mcp.provider.ts +108 -108
  193. package/server/modules/providers/list/cursor/cursor-sessions.provider.ts +421 -421
  194. package/server/modules/providers/list/cursor/cursor.provider.ts +15 -15
  195. package/server/modules/providers/list/gemini/gemini-auth.provider.ts +173 -173
  196. package/server/modules/providers/list/gemini/gemini-mcp.provider.ts +110 -110
  197. package/server/modules/providers/list/gemini/gemini-sessions.provider.ts +227 -227
  198. package/server/modules/providers/list/gemini/gemini.provider.ts +15 -15
  199. package/server/modules/providers/list/opencode/opencode-auth.provider.ts +131 -131
  200. package/server/modules/providers/list/opencode/opencode-sessions.provider.ts +286 -286
  201. package/server/modules/providers/list/qwen/qwen-auth.provider.ts +146 -146
  202. package/server/modules/providers/list/qwen/qwen-sessions.provider.ts +265 -265
  203. package/server/modules/providers/provider.registry.ts +40 -40
  204. package/server/modules/providers/provider.routes.ts +982 -982
  205. package/server/modules/providers/services/mcp.service.ts +86 -86
  206. package/server/modules/providers/services/provider-auth.service.ts +26 -26
  207. package/server/modules/providers/services/sessions.service.ts +45 -45
  208. package/server/modules/providers/shared/base/abstract.provider.ts +20 -20
  209. package/server/modules/providers/shared/mcp/mcp.provider.ts +151 -151
  210. package/server/modules/providers/tests/mcp.test.ts +293 -293
  211. package/server/openai-codex.js +468 -468
  212. package/server/opencode-cli.js +491 -491
  213. package/server/opencode-response-handler.js +111 -111
  214. package/server/projects.js +3135 -3135
  215. package/server/qwen-code-cli.js +410 -410
  216. package/server/routes/agent.js +1462 -1462
  217. package/server/routes/auth.js +298 -298
  218. package/server/routes/codex.js +20 -20
  219. package/server/routes/commands.js +581 -581
  220. package/server/routes/cursor.js +61 -61
  221. package/server/routes/diagnostics.js +44 -44
  222. package/server/routes/gemini.js +25 -25
  223. package/server/routes/git.js +1822 -1822
  224. package/server/routes/live-view.js +434 -434
  225. package/server/routes/mcp-utils.js +13 -13
  226. package/server/routes/messages.js +97 -97
  227. package/server/routes/network.js +143 -143
  228. package/server/routes/platformization.js +231 -231
  229. package/server/routes/plugins.js +690 -690
  230. package/server/routes/production-agent-loop.js +90 -90
  231. package/server/routes/projects.js +918 -918
  232. package/server/routes/public-api.js +34 -34
  233. package/server/routes/qwen.js +27 -27
  234. package/server/routes/remote.js +56 -56
  235. package/server/routes/settings.js +321 -321
  236. package/server/routes/telegram.js +163 -163
  237. package/server/routes/user.js +125 -125
  238. package/server/routes/webhooks.js +63 -63
  239. package/server/services/control-room.js +102 -102
  240. package/server/services/diagnostics.js +165 -165
  241. package/server/services/external-access.js +403 -403
  242. package/server/services/install-jobs.js +715 -715
  243. package/server/services/live-view.js +956 -956
  244. package/server/services/managed-runtimes.js +493 -493
  245. package/server/services/model-registry.js +144 -144
  246. package/server/services/notification-orchestrator.js +365 -365
  247. package/server/services/notification-taxonomy.js +204 -204
  248. package/server/services/platformization.js +1052 -1052
  249. package/server/services/production-agent-loop.js +248 -248
  250. package/server/services/provider-cli-versions.js +149 -149
  251. package/server/services/provider-credentials.js +189 -189
  252. package/server/services/provider-models.js +396 -396
  253. package/server/services/public-api-manifest.js +181 -181
  254. package/server/services/qr-login.js +126 -126
  255. package/server/services/remote-connection.js +127 -127
  256. package/server/services/runtime-manager.js +323 -323
  257. package/server/services/startup-update.js +259 -259
  258. package/server/services/telegram/bot.js +393 -393
  259. package/server/services/telegram/control-center.js +2453 -2453
  260. package/server/services/telegram/telegram-gateway.js +314 -314
  261. package/server/services/telegram/telegram-http-client.js +201 -201
  262. package/server/services/telegram/translations.js +470 -470
  263. package/server/services/webhooks.js +216 -216
  264. package/server/sessionManager.js +225 -225
  265. package/server/setup-wizard.js +283 -283
  266. package/server/shared/interfaces.ts +54 -54
  267. package/server/shared/types.ts +172 -172
  268. package/server/shared/utils.ts +193 -193
  269. package/server/tsconfig.json +36 -36
  270. package/server/utils/colors.js +21 -21
  271. package/server/utils/commandParser.js +305 -305
  272. package/server/utils/frontmatter.js +18 -18
  273. package/server/utils/gitConfig.js +34 -34
  274. package/server/utils/plugin-loader.js +461 -461
  275. package/server/utils/plugin-process-manager.js +185 -185
  276. package/server/utils/port-access.js +227 -227
  277. package/server/utils/runtime-paths.js +37 -37
  278. package/server/utils/security-log.js +84 -84
  279. package/server/utils/url-detection.js +71 -71
  280. package/server/vite-daemon.js +79 -79
  281. package/shared/modelConstants.js +161 -161
  282. package/shared/networkHosts.js +22 -22
@@ -1,193 +1,193 @@
1
-
2
- import { randomUUID } from 'node:crypto';
3
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
4
- import path from 'node:path';
5
-
6
- import type { NextFunction, Request, RequestHandler, Response } from 'express';
7
-
8
- import type {
9
- AnyRecord,
10
- ApiSuccessShape,
11
- AppErrorOptions,
12
- NormalizedMessage,
13
- } from '@/shared/types.js';
14
-
15
- type NormalizedMessageInput =
16
- {
17
- kind: NormalizedMessage['kind'];
18
- provider: NormalizedMessage['provider'];
19
- id?: string | null;
20
- sessionId?: string | null;
21
- timestamp?: string | null;
22
- } & Record<string, unknown>;
23
-
24
- export function createApiSuccessResponse<TData>(
25
- data: TData,
26
- ): ApiSuccessShape<TData> {
27
- return {
28
- success: true,
29
- data,
30
- };
31
- }
32
-
33
- export function asyncHandler(
34
- handler: (req: Request, res: Response, next: NextFunction) => Promise<unknown>
35
- ): RequestHandler {
36
- return (req, res, next) => {
37
- void Promise.resolve(handler(req, res, next)).catch(next);
38
- };
39
- }
40
-
41
- // --------- Global app error class for consistent error handling across the server ---------
42
- export class AppError extends Error {
43
- readonly code: string;
44
- readonly statusCode: number;
45
- readonly details?: unknown;
46
-
47
- constructor(message: string, options: AppErrorOptions = {}) {
48
- super(message);
49
- this.name = 'AppError';
50
- this.code = options.code ?? 'INTERNAL_ERROR';
51
- this.statusCode = options.statusCode ?? 500;
52
- this.details = options.details;
53
- }
54
- }
55
-
56
- // -------------------------------------------------------------------------------------------
57
-
58
- // ------------------------ Normalized provider message helpers ------------------------
59
- /**
60
- * Generates a stable unique id for normalized provider messages.
61
- */
62
- export function generateMessageId(prefix = 'msg'): string {
63
- return `${prefix}_${randomUUID()}`;
64
- }
65
-
66
- /**
67
- * Creates a normalized provider message and fills the shared envelope fields.
68
- *
69
- * Provider adapters and live SDK handlers pass through provider-specific fields,
70
- * while this helper guarantees every emitted event has an id, session id,
71
- * timestamp, and provider marker.
72
- */
73
- export function createNormalizedMessage(fields: NormalizedMessageInput): NormalizedMessage {
74
- return {
75
- ...fields,
76
- id: fields.id || generateMessageId(fields.kind),
77
- sessionId: fields.sessionId || '',
78
- timestamp: fields.timestamp || new Date().toISOString(),
79
- provider: fields.provider,
80
- };
81
- }
82
-
83
- // -------------------------------------------------------------------------------------------
84
-
85
- // ------------------------ The following are mainly for provider MCP runtimes ------------------------
86
- /**
87
- * Safely narrows an unknown value to a plain object record.
88
- *
89
- * This deliberately rejects arrays, `null`, and primitive values so callers can
90
- * treat the returned value as a JSON-style object map without repeating the same
91
- * defensive shape checks at every config read site.
92
- */
93
- export const readObjectRecord = (value: any): AnyRecord | null => {
94
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
95
- return null;
96
- }
97
-
98
- return value as AnyRecord;
99
- };
100
-
101
- /**
102
- * Reads an optional string from unknown input and normalizes empty or whitespace-only
103
- * values to `undefined`.
104
- *
105
- * This is useful when parsing config files where a field may be missing, present
106
- * with the wrong type, or present as an empty string that should be treated as
107
- * "not configured".
108
- */
109
- export const readOptionalString = (value: unknown): string | undefined => {
110
- if (typeof value !== 'string') {
111
- return undefined;
112
- }
113
-
114
- const normalized = value.trim();
115
- return normalized.length > 0 ? normalized : undefined;
116
- };
117
-
118
- /**
119
- * Reads an optional string array from unknown input.
120
- *
121
- * Non-array values are ignored, and any array entries that are not strings are
122
- * filtered out. This lets provider config readers consume loosely shaped JSON/TOML
123
- * data without failing on incidental invalid members.
124
- */
125
- export const readStringArray = (value: unknown): string[] | undefined => {
126
- if (!Array.isArray(value)) {
127
- return undefined;
128
- }
129
-
130
- return value.filter((entry): entry is string => typeof entry === 'string');
131
- };
132
-
133
- /**
134
- * Reads an optional string-to-string map from unknown input.
135
- *
136
- * The function first ensures the source value is a plain object, then keeps only
137
- * keys whose values are strings. If no valid entries remain, it returns `undefined`
138
- * so callers can distinguish "no usable map" from an empty object that was
139
- * intentionally authored downstream.
140
- */
141
- export const readStringRecord = (value: unknown): Record<string, string> | undefined => {
142
- const record = readObjectRecord(value);
143
- if (!record) {
144
- return undefined;
145
- }
146
-
147
- const normalized: Record<string, string> = {};
148
- for (const [key, entry] of Object.entries(record)) {
149
- if (typeof entry === 'string') {
150
- normalized[key] = entry;
151
- }
152
- }
153
-
154
- return Object.keys(normalized).length > 0 ? normalized : undefined;
155
- };
156
-
157
- /**
158
- * Reads a JSON config file and guarantees a plain object result.
159
- *
160
- * Missing files are treated as an empty config object so provider-specific MCP
161
- * readers can operate against first-run environments without special-case file
162
- * existence checks. If the file exists but contains invalid JSON, the parse error
163
- * is preserved and rethrown.
164
- */
165
- export const readJsonConfig = async (filePath: string): Promise<Record<string, unknown>> => {
166
- try {
167
- const content = await readFile(filePath, 'utf8');
168
- const parsed = JSON.parse(content) as Record<string, unknown>;
169
- return readObjectRecord(parsed) ?? {};
170
- } catch (error) {
171
- const code = (error as NodeJS.ErrnoException).code;
172
- if (code === 'ENOENT') {
173
- return {};
174
- }
175
-
176
- throw error;
177
- }
178
- };
179
-
180
- /**
181
- * Writes a JSON config file with stable, human-readable formatting.
182
- *
183
- * The parent directory is created automatically so callers can persist config into
184
- * provider-specific folders without pre-creating the directory tree. Output always
185
- * ends with a trailing newline to keep the file diff-friendly.
186
- */
187
- export const writeJsonConfig = async (filePath: string, data: Record<string, unknown>): Promise<void> => {
188
- await mkdir(path.dirname(filePath), { recursive: true });
189
- await writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
190
- };
191
-
192
- // -------------------------------------------------------------------------------------------
193
-
1
+
2
+ import { randomUUID } from 'node:crypto';
3
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+
6
+ import type { NextFunction, Request, RequestHandler, Response } from 'express';
7
+
8
+ import type {
9
+ AnyRecord,
10
+ ApiSuccessShape,
11
+ AppErrorOptions,
12
+ NormalizedMessage,
13
+ } from '@/shared/types.js';
14
+
15
+ type NormalizedMessageInput =
16
+ {
17
+ kind: NormalizedMessage['kind'];
18
+ provider: NormalizedMessage['provider'];
19
+ id?: string | null;
20
+ sessionId?: string | null;
21
+ timestamp?: string | null;
22
+ } & Record<string, unknown>;
23
+
24
+ export function createApiSuccessResponse<TData>(
25
+ data: TData,
26
+ ): ApiSuccessShape<TData> {
27
+ return {
28
+ success: true,
29
+ data,
30
+ };
31
+ }
32
+
33
+ export function asyncHandler(
34
+ handler: (req: Request, res: Response, next: NextFunction) => Promise<unknown>
35
+ ): RequestHandler {
36
+ return (req, res, next) => {
37
+ void Promise.resolve(handler(req, res, next)).catch(next);
38
+ };
39
+ }
40
+
41
+ // --------- Global app error class for consistent error handling across the server ---------
42
+ export class AppError extends Error {
43
+ readonly code: string;
44
+ readonly statusCode: number;
45
+ readonly details?: unknown;
46
+
47
+ constructor(message: string, options: AppErrorOptions = {}) {
48
+ super(message);
49
+ this.name = 'AppError';
50
+ this.code = options.code ?? 'INTERNAL_ERROR';
51
+ this.statusCode = options.statusCode ?? 500;
52
+ this.details = options.details;
53
+ }
54
+ }
55
+
56
+ // -------------------------------------------------------------------------------------------
57
+
58
+ // ------------------------ Normalized provider message helpers ------------------------
59
+ /**
60
+ * Generates a stable unique id for normalized provider messages.
61
+ */
62
+ export function generateMessageId(prefix = 'msg'): string {
63
+ return `${prefix}_${randomUUID()}`;
64
+ }
65
+
66
+ /**
67
+ * Creates a normalized provider message and fills the shared envelope fields.
68
+ *
69
+ * Provider adapters and live SDK handlers pass through provider-specific fields,
70
+ * while this helper guarantees every emitted event has an id, session id,
71
+ * timestamp, and provider marker.
72
+ */
73
+ export function createNormalizedMessage(fields: NormalizedMessageInput): NormalizedMessage {
74
+ return {
75
+ ...fields,
76
+ id: fields.id || generateMessageId(fields.kind),
77
+ sessionId: fields.sessionId || '',
78
+ timestamp: fields.timestamp || new Date().toISOString(),
79
+ provider: fields.provider,
80
+ };
81
+ }
82
+
83
+ // -------------------------------------------------------------------------------------------
84
+
85
+ // ------------------------ The following are mainly for provider MCP runtimes ------------------------
86
+ /**
87
+ * Safely narrows an unknown value to a plain object record.
88
+ *
89
+ * This deliberately rejects arrays, `null`, and primitive values so callers can
90
+ * treat the returned value as a JSON-style object map without repeating the same
91
+ * defensive shape checks at every config read site.
92
+ */
93
+ export const readObjectRecord = (value: any): AnyRecord | null => {
94
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
95
+ return null;
96
+ }
97
+
98
+ return value as AnyRecord;
99
+ };
100
+
101
+ /**
102
+ * Reads an optional string from unknown input and normalizes empty or whitespace-only
103
+ * values to `undefined`.
104
+ *
105
+ * This is useful when parsing config files where a field may be missing, present
106
+ * with the wrong type, or present as an empty string that should be treated as
107
+ * "not configured".
108
+ */
109
+ export const readOptionalString = (value: unknown): string | undefined => {
110
+ if (typeof value !== 'string') {
111
+ return undefined;
112
+ }
113
+
114
+ const normalized = value.trim();
115
+ return normalized.length > 0 ? normalized : undefined;
116
+ };
117
+
118
+ /**
119
+ * Reads an optional string array from unknown input.
120
+ *
121
+ * Non-array values are ignored, and any array entries that are not strings are
122
+ * filtered out. This lets provider config readers consume loosely shaped JSON/TOML
123
+ * data without failing on incidental invalid members.
124
+ */
125
+ export const readStringArray = (value: unknown): string[] | undefined => {
126
+ if (!Array.isArray(value)) {
127
+ return undefined;
128
+ }
129
+
130
+ return value.filter((entry): entry is string => typeof entry === 'string');
131
+ };
132
+
133
+ /**
134
+ * Reads an optional string-to-string map from unknown input.
135
+ *
136
+ * The function first ensures the source value is a plain object, then keeps only
137
+ * keys whose values are strings. If no valid entries remain, it returns `undefined`
138
+ * so callers can distinguish "no usable map" from an empty object that was
139
+ * intentionally authored downstream.
140
+ */
141
+ export const readStringRecord = (value: unknown): Record<string, string> | undefined => {
142
+ const record = readObjectRecord(value);
143
+ if (!record) {
144
+ return undefined;
145
+ }
146
+
147
+ const normalized: Record<string, string> = {};
148
+ for (const [key, entry] of Object.entries(record)) {
149
+ if (typeof entry === 'string') {
150
+ normalized[key] = entry;
151
+ }
152
+ }
153
+
154
+ return Object.keys(normalized).length > 0 ? normalized : undefined;
155
+ };
156
+
157
+ /**
158
+ * Reads a JSON config file and guarantees a plain object result.
159
+ *
160
+ * Missing files are treated as an empty config object so provider-specific MCP
161
+ * readers can operate against first-run environments without special-case file
162
+ * existence checks. If the file exists but contains invalid JSON, the parse error
163
+ * is preserved and rethrown.
164
+ */
165
+ export const readJsonConfig = async (filePath: string): Promise<Record<string, unknown>> => {
166
+ try {
167
+ const content = await readFile(filePath, 'utf8');
168
+ const parsed = JSON.parse(content) as Record<string, unknown>;
169
+ return readObjectRecord(parsed) ?? {};
170
+ } catch (error) {
171
+ const code = (error as NodeJS.ErrnoException).code;
172
+ if (code === 'ENOENT') {
173
+ return {};
174
+ }
175
+
176
+ throw error;
177
+ }
178
+ };
179
+
180
+ /**
181
+ * Writes a JSON config file with stable, human-readable formatting.
182
+ *
183
+ * The parent directory is created automatically so callers can persist config into
184
+ * provider-specific folders without pre-creating the directory tree. Output always
185
+ * ends with a trailing newline to keep the file diff-friendly.
186
+ */
187
+ export const writeJsonConfig = async (filePath: string, data: Record<string, unknown>): Promise<void> => {
188
+ await mkdir(path.dirname(filePath), { recursive: true });
189
+ await writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
190
+ };
191
+
192
+ // -------------------------------------------------------------------------------------------
193
+
@@ -1,36 +1,36 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "lib": ["ES2022"],
7
- // baseUrl is the project root (one level above this config file) so that tsc-alias
8
- // resolves @/ imports relative to the compiled output structure in dist-server/server/.
9
- // With rootDir ".." tsc emits server files under dist-server/server/, so paths must
10
- // include the "server/" prefix to match that layout.
11
- "baseUrl": "..",
12
- "paths": {
13
- "@/*": ["server/*"]
14
- },
15
- // The backend is still mostly JavaScript today, so allowJs lets us add a real
16
- // TypeScript build without forcing a large rename before the tooling is usable.
17
- "allowJs": true,
18
- // Keep the migration incremental: existing JS keeps building, while any new TS files
19
- // still go through the normal TypeScript pipeline and strict checks.
20
- "checkJs": false,
21
- "strict": true,
22
- "noEmitOnError": true,
23
- // The backend build emits both /server and /shared into dist-server, so rootDir must
24
- // stay one level above this file even though the config itself now lives in /server.
25
- "rootDir": "..",
26
- "outDir": "../dist-server",
27
- "sourceMap": true,
28
- "resolveJsonModule": true,
29
- "esModuleInterop": true,
30
- "allowSyntheticDefaultImports": true,
31
- "skipLibCheck": true,
32
- "types": ["node"]
33
- },
34
- "include": ["./**/*.js", "./**/*.ts", "../shared/**/*.js", "../shared/**/*.ts"],
35
- "exclude": ["../dist", "../dist-server", "../node_modules", "../src"]
36
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "lib": ["ES2022"],
7
+ // baseUrl is the project root (one level above this config file) so that tsc-alias
8
+ // resolves @/ imports relative to the compiled output structure in dist-server/server/.
9
+ // With rootDir ".." tsc emits server files under dist-server/server/, so paths must
10
+ // include the "server/" prefix to match that layout.
11
+ "baseUrl": "..",
12
+ "paths": {
13
+ "@/*": ["server/*"]
14
+ },
15
+ // The backend is still mostly JavaScript today, so allowJs lets us add a real
16
+ // TypeScript build without forcing a large rename before the tooling is usable.
17
+ "allowJs": true,
18
+ // Keep the migration incremental: existing JS keeps building, while any new TS files
19
+ // still go through the normal TypeScript pipeline and strict checks.
20
+ "checkJs": false,
21
+ "strict": true,
22
+ "noEmitOnError": true,
23
+ // The backend build emits both /server and /shared into dist-server, so rootDir must
24
+ // stay one level above this file even though the config itself now lives in /server.
25
+ "rootDir": "..",
26
+ "outDir": "../dist-server",
27
+ "sourceMap": true,
28
+ "resolveJsonModule": true,
29
+ "esModuleInterop": true,
30
+ "allowSyntheticDefaultImports": true,
31
+ "skipLibCheck": true,
32
+ "types": ["node"]
33
+ },
34
+ "include": ["./**/*.js", "./**/*.ts", "../shared/**/*.js", "../shared/**/*.ts"],
35
+ "exclude": ["../dist", "../dist-server", "../node_modules", "../src"]
36
+ }
@@ -1,21 +1,21 @@
1
- // ANSI color codes for terminal output
2
- const colors = {
3
- reset: '\x1b[0m',
4
- bright: '\x1b[1m',
5
- cyan: '\x1b[36m',
6
- green: '\x1b[32m',
7
- yellow: '\x1b[33m',
8
- blue: '\x1b[34m',
9
- dim: '\x1b[2m',
10
- };
11
-
12
- const c = {
13
- info: (text) => `${colors.cyan}${text}${colors.reset}`,
14
- ok: (text) => `${colors.green}${text}${colors.reset}`,
15
- warn: (text) => `${colors.yellow}${text}${colors.reset}`,
16
- tip: (text) => `${colors.blue}${text}${colors.reset}`,
17
- bright: (text) => `${colors.bright}${text}${colors.reset}`,
18
- dim: (text) => `${colors.dim}${text}${colors.reset}`,
19
- };
20
-
21
- export { colors, c };
1
+ // ANSI color codes for terminal output
2
+ const colors = {
3
+ reset: '\x1b[0m',
4
+ bright: '\x1b[1m',
5
+ cyan: '\x1b[36m',
6
+ green: '\x1b[32m',
7
+ yellow: '\x1b[33m',
8
+ blue: '\x1b[34m',
9
+ dim: '\x1b[2m',
10
+ };
11
+
12
+ const c = {
13
+ info: (text) => `${colors.cyan}${text}${colors.reset}`,
14
+ ok: (text) => `${colors.green}${text}${colors.reset}`,
15
+ warn: (text) => `${colors.yellow}${text}${colors.reset}`,
16
+ tip: (text) => `${colors.blue}${text}${colors.reset}`,
17
+ bright: (text) => `${colors.bright}${text}${colors.reset}`,
18
+ dim: (text) => `${colors.dim}${text}${colors.reset}`,
19
+ };
20
+
21
+ export { colors, c };