@mastra/code-sdk 1.0.3-alpha.0 → 1.1.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/dist/acp/agent.js +155 -167
  3. package/dist/acp/agent.js.map +1 -1
  4. package/dist/acp/event-mapper.js +193 -168
  5. package/dist/acp/event-mapper.js.map +1 -1
  6. package/dist/acp/index.js +60 -49
  7. package/dist/acp/index.js.map +1 -1
  8. package/dist/acp/server.js +31 -30
  9. package/dist/acp/server.js.map +1 -1
  10. package/dist/acp.js +1 -4
  11. package/dist/agents/credential-resolver.js +40 -29
  12. package/dist/agents/credential-resolver.js.map +1 -1
  13. package/dist/agents/custom-provider-source.js +16 -9
  14. package/dist/agents/custom-provider-source.js.map +1 -1
  15. package/dist/agents/instructions.js +27 -36
  16. package/dist/agents/instructions.js.map +1 -1
  17. package/dist/agents/mastracode-gateway.js +355 -428
  18. package/dist/agents/mastracode-gateway.js.map +1 -1
  19. package/dist/agents/memory.js +87 -78
  20. package/dist/agents/memory.js.map +1 -1
  21. package/dist/agents/model.js +114 -119
  22. package/dist/agents/model.js.map +1 -1
  23. package/dist/agents/modes/build.js +12 -13
  24. package/dist/agents/modes/build.js.map +1 -1
  25. package/dist/agents/modes/explore.js +14 -13
  26. package/dist/agents/modes/explore.js.map +1 -1
  27. package/dist/agents/modes/plan.js +14 -15
  28. package/dist/agents/modes/plan.js.map +1 -1
  29. package/dist/agents/prompts/agent-instructions.js +78 -69
  30. package/dist/agents/prompts/agent-instructions.js.map +1 -1
  31. package/dist/agents/prompts/build.js +19 -16
  32. package/dist/agents/prompts/build.js.map +1 -1
  33. package/dist/agents/prompts/fast.js +8 -4
  34. package/dist/agents/prompts/fast.js.map +1 -1
  35. package/dist/agents/prompts/index.js +48 -47
  36. package/dist/agents/prompts/index.js.map +1 -1
  37. package/dist/agents/prompts/model.js +6 -5
  38. package/dist/agents/prompts/model.js.map +1 -1
  39. package/dist/agents/prompts/plan.js +20 -16
  40. package/dist/agents/prompts/plan.js.map +1 -1
  41. package/dist/agents/prompts/tool-guidance.js +77 -109
  42. package/dist/agents/prompts/tool-guidance.js.map +1 -1
  43. package/dist/agents/sandbox-filesystem.js +245 -259
  44. package/dist/agents/sandbox-filesystem.js.map +1 -1
  45. package/dist/agents/sandbox-reattach.js +9 -11
  46. package/dist/agents/sandbox-reattach.js.map +1 -1
  47. package/dist/agents/thread-caveman-state.js +92 -81
  48. package/dist/agents/thread-caveman-state.js.map +1 -1
  49. package/dist/agents/tool-availability.js +62 -80
  50. package/dist/agents/tool-availability.js.map +1 -1
  51. package/dist/agents/tools.js +99 -139
  52. package/dist/agents/tools.js.map +1 -1
  53. package/dist/agents/workspace.js +232 -225
  54. package/dist/agents/workspace.js.map +1 -1
  55. package/dist/analytics.js +139 -128
  56. package/dist/analytics.js.map +1 -1
  57. package/dist/auth/authorization-input.js +39 -25
  58. package/dist/auth/authorization-input.js.map +1 -1
  59. package/dist/auth/device-code.js +112 -80
  60. package/dist/auth/device-code.js.map +1 -1
  61. package/dist/auth/index.js +2 -9
  62. package/dist/auth/pkce.js +27 -16
  63. package/dist/auth/pkce.js.map +1 -1
  64. package/dist/auth/providers/anthropic.js +107 -93
  65. package/dist/auth/providers/anthropic.js.map +1 -1
  66. package/dist/auth/providers/github-copilot.js +304 -293
  67. package/dist/auth/providers/github-copilot.js.map +1 -1
  68. package/dist/auth/providers/openai-codex.js +442 -460
  69. package/dist/auth/providers/openai-codex.js.map +1 -1
  70. package/dist/auth/providers/xai.js +179 -145
  71. package/dist/auth/providers/xai.js.map +1 -1
  72. package/dist/auth/storage.js +182 -175
  73. package/dist/auth/storage.js.map +1 -1
  74. package/dist/auth/types.js +1 -1
  75. package/dist/clipboard/index.js +206 -172
  76. package/dist/clipboard/index.js.map +1 -1
  77. package/dist/constants.js +11 -16
  78. package/dist/constants.js.map +1 -1
  79. package/dist/error-classification.js +19 -11
  80. package/dist/error-classification.js.map +1 -1
  81. package/dist/evals/context-builder.js +145 -104
  82. package/dist/evals/context-builder.js.map +1 -1
  83. package/dist/evals/index.js +4 -7
  84. package/dist/evals/scorers/classify-command.js +62 -41
  85. package/dist/evals/scorers/classify-command.js.map +1 -1
  86. package/dist/evals/scorers/efficiency.js +239 -181
  87. package/dist/evals/scorers/efficiency.js.map +1 -1
  88. package/dist/evals/scorers/extract-tools.js +51 -47
  89. package/dist/evals/scorers/extract-tools.js.map +1 -1
  90. package/dist/evals/scorers/index.js +1 -5
  91. package/dist/evals/scorers/outcome.js +321 -212
  92. package/dist/evals/scorers/outcome.js.map +1 -1
  93. package/dist/goal-manager.js +266 -261
  94. package/dist/goal-manager.js.map +1 -1
  95. package/dist/goal-signal.js +14 -13
  96. package/dist/goal-signal.js.map +1 -1
  97. package/dist/headless/cli.js +153 -154
  98. package/dist/headless/cli.js.map +1 -1
  99. package/dist/headless/flags.js +180 -178
  100. package/dist/headless/flags.js.map +1 -1
  101. package/dist/headless/format.js +72 -71
  102. package/dist/headless/format.js.map +1 -1
  103. package/dist/headless/index.js +5 -31
  104. package/dist/headless/policy.js +35 -31
  105. package/dist/headless/policy.js.map +1 -1
  106. package/dist/headless/run-mc.js +332 -334
  107. package/dist/headless/run-mc.js.map +1 -1
  108. package/dist/headless/types.js +16 -7
  109. package/dist/headless/types.js.map +1 -1
  110. package/dist/hooks/config.js +56 -61
  111. package/dist/hooks/config.js.map +1 -1
  112. package/dist/hooks/executor.js +118 -116
  113. package/dist/hooks/executor.js.map +1 -1
  114. package/dist/hooks/index.js +4 -15
  115. package/dist/hooks/manager.js +221 -280
  116. package/dist/hooks/manager.js.map +1 -1
  117. package/dist/hooks/types.js +5 -4
  118. package/dist/hooks/types.js.map +1 -1
  119. package/dist/index.d.ts.map +1 -1
  120. package/dist/index.js +632 -712
  121. package/dist/index.js.map +1 -1
  122. package/dist/ipc/ipc-reporter.js +19 -22
  123. package/dist/ipc/ipc-reporter.js.map +1 -1
  124. package/dist/lsp/client.js +248 -306
  125. package/dist/lsp/client.js.map +1 -1
  126. package/dist/lsp/index.js +1 -6
  127. package/dist/lsp/language.js +39 -43
  128. package/dist/lsp/language.js.map +1 -1
  129. package/dist/lsp/manager.js +78 -93
  130. package/dist/lsp/manager.js.map +1 -1
  131. package/dist/lsp/server.js +151 -120
  132. package/dist/lsp/server.js.map +1 -1
  133. package/dist/lsp/workspace.js +50 -47
  134. package/dist/lsp/workspace.js.map +1 -1
  135. package/dist/mcp/config.js +210 -199
  136. package/dist/mcp/config.js.map +1 -1
  137. package/dist/mcp/index.js +2 -9
  138. package/dist/mcp/manager.js +482 -475
  139. package/dist/mcp/manager.js.map +1 -1
  140. package/dist/mcp/types.js +1 -1
  141. package/dist/onboarding/custom-packs.js +14 -17
  142. package/dist/onboarding/custom-packs.js.map +1 -1
  143. package/dist/onboarding/custom-providers.js +22 -20
  144. package/dist/onboarding/custom-providers.js.map +1 -1
  145. package/dist/onboarding/index.d.ts +1 -1
  146. package/dist/onboarding/index.d.ts.map +1 -1
  147. package/dist/onboarding/index.js +3 -21
  148. package/dist/onboarding/om-settings.d.ts +5 -0
  149. package/dist/onboarding/om-settings.d.ts.map +1 -1
  150. package/dist/onboarding/om-settings.js +41 -20
  151. package/dist/onboarding/om-settings.js.map +1 -1
  152. package/dist/onboarding/packs.d.ts +4 -6
  153. package/dist/onboarding/packs.d.ts.map +1 -1
  154. package/dist/onboarding/packs.js +154 -105
  155. package/dist/onboarding/packs.js.map +1 -1
  156. package/dist/onboarding/settings.js +575 -517
  157. package/dist/onboarding/settings.js.map +1 -1
  158. package/dist/permissions.js +112 -99
  159. package/dist/permissions.js.map +1 -1
  160. package/dist/plugin.js +21 -23
  161. package/dist/plugin.js.map +1 -1
  162. package/dist/plugins/dependencies.js +64 -87
  163. package/dist/plugins/dependencies.js.map +1 -1
  164. package/dist/plugins/install.js +159 -169
  165. package/dist/plugins/install.js.map +1 -1
  166. package/dist/plugins/loader.js +170 -185
  167. package/dist/plugins/loader.js.map +1 -1
  168. package/dist/plugins/manager.js +374 -380
  169. package/dist/plugins/manager.js.map +1 -1
  170. package/dist/plugins/manifest.js +38 -63
  171. package/dist/plugins/manifest.js.map +1 -1
  172. package/dist/plugins/package-link.js +48 -64
  173. package/dist/plugins/package-link.js.map +1 -1
  174. package/dist/plugins/paths.js +18 -19
  175. package/dist/plugins/paths.js.map +1 -1
  176. package/dist/plugins/registry.js +97 -81
  177. package/dist/plugins/registry.js.map +1 -1
  178. package/dist/plugins/scaffold.js +62 -98
  179. package/dist/plugins/scaffold.js.map +1 -1
  180. package/dist/plugins/types.js +1 -1
  181. package/dist/processors/plan-rejection-abort.js +45 -44
  182. package/dist/processors/plan-rejection-abort.js.map +1 -1
  183. package/dist/providers/amazon-bedrock-gateway.js +134 -125
  184. package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
  185. package/dist/providers/amazon-bedrock.js +68 -47
  186. package/dist/providers/amazon-bedrock.js.map +1 -1
  187. package/dist/providers/claude-max.js +153 -133
  188. package/dist/providers/claude-max.js.map +1 -1
  189. package/dist/providers/github-copilot.js +232 -230
  190. package/dist/providers/github-copilot.js.map +1 -1
  191. package/dist/providers/openai-codex.js +271 -260
  192. package/dist/providers/openai-codex.js.map +1 -1
  193. package/dist/providers/xai.js +57 -50
  194. package/dist/providers/xai.js.map +1 -1
  195. package/dist/schema.js +91 -89
  196. package/dist/schema.js.map +1 -1
  197. package/dist/theme-palette.js +18 -14
  198. package/dist/theme-palette.js.map +1 -1
  199. package/dist/tool-names.js +46 -37
  200. package/dist/tool-names.js.map +1 -1
  201. package/dist/tools/index.js +2 -8
  202. package/dist/tools/request-sandbox-access.js +71 -80
  203. package/dist/tools/request-sandbox-access.js.map +1 -1
  204. package/dist/tools/utils.js +26 -16
  205. package/dist/tools/utils.js.map +1 -1
  206. package/dist/tools/web-search.js +51 -55
  207. package/dist/tools/web-search.js.map +1 -1
  208. package/dist/utils/binaries.js +65 -61
  209. package/dist/utils/binaries.js.map +1 -1
  210. package/dist/utils/debug-log.js +51 -47
  211. package/dist/utils/debug-log.js.map +1 -1
  212. package/dist/utils/errors.js +157 -192
  213. package/dist/utils/errors.js.map +1 -1
  214. package/dist/utils/gateway-sync.js +49 -37
  215. package/dist/utils/gateway-sync.js.map +1 -1
  216. package/dist/utils/path-security.js +7 -6
  217. package/dist/utils/path-security.js.map +1 -1
  218. package/dist/utils/plan-diff.js +58 -39
  219. package/dist/utils/plan-diff.js.map +1 -1
  220. package/dist/utils/plans.js +92 -74
  221. package/dist/utils/plans.js.map +1 -1
  222. package/dist/utils/project.js +318 -256
  223. package/dist/utils/project.js.map +1 -1
  224. package/dist/utils/signals-pubsub.js +117 -99
  225. package/dist/utils/signals-pubsub.js.map +1 -1
  226. package/dist/utils/slash-command-loader.js +118 -154
  227. package/dist/utils/slash-command-loader.js.map +1 -1
  228. package/dist/utils/slash-command-processor.js +92 -83
  229. package/dist/utils/slash-command-processor.js.map +1 -1
  230. package/dist/utils/stdin-pipe.js +87 -63
  231. package/dist/utils/stdin-pipe.js.map +1 -1
  232. package/dist/utils/storage-factory.js +104 -84
  233. package/dist/utils/storage-factory.js.map +1 -1
  234. package/dist/utils/storage-maintenance.js +249 -211
  235. package/dist/utils/storage-maintenance.js.map +1 -1
  236. package/dist/utils/thread-lock.js +96 -92
  237. package/dist/utils/thread-lock.js.map +1 -1
  238. package/dist/utils/token-estimator.js +12 -12
  239. package/dist/utils/token-estimator.js.map +1 -1
  240. package/dist/utils/update-check.d.ts +1 -1
  241. package/dist/utils/update-check.js +341 -259
  242. package/dist/utils/update-check.js.map +1 -1
  243. package/dist/voice/stt-registry.js +131 -113
  244. package/dist/voice/stt-registry.js.map +1 -1
  245. package/package.json +11 -11
  246. package/dist/acp.js.map +0 -1
  247. package/dist/auth/index.js.map +0 -1
  248. package/dist/auth/types.js.map +0 -1
  249. package/dist/evals/index.js.map +0 -1
  250. package/dist/evals/scorers/index.js.map +0 -1
  251. package/dist/headless/index.js.map +0 -1
  252. package/dist/hooks/index.js.map +0 -1
  253. package/dist/lsp/index.js.map +0 -1
  254. package/dist/mcp/index.js.map +0 -1
  255. package/dist/mcp/types.js.map +0 -1
  256. package/dist/onboarding/index.js.map +0 -1
  257. package/dist/plugins/types.js.map +0 -1
  258. package/dist/tools/index.js.map +0 -1
@@ -1,72 +1,96 @@
1
- import * as fs from "node:fs";
2
- import * as tty from "node:tty";
1
+ import * as fs$1 from "fs";
2
+ import * as tty from "tty";
3
+ //#region src/utils/stdin-pipe.ts
4
+ /** Maximum piped input size (1 MB). Content beyond this is truncated. */
3
5
  const MAX_PIPE_BYTES = 1024 * 1024;
6
+ /**
7
+ * Matches all ANSI escape sequences — SGR (colors), cursor movement, line
8
+ * clears, scrolling, window titles, etc.
9
+ */
4
10
  const ANSI_RE = /\x1b(?:\[[0-9;?]*[A-Za-z]|\][^\x07\x1b]*(?:\x07|\x1b\\)?|\([A-Z])/g;
11
+ /**
12
+ * Clean up raw piped output so the agent sees readable text, not terminal
13
+ * control noise.
14
+ *
15
+ * 1. Strip all ANSI escape sequences (colors, cursor movement, line clears).
16
+ * 2. Simulate carriage-return overwrites: for each line, split on `\r` and
17
+ * keep only the last segment — this is what the terminal would display
18
+ * after a spinner/progress bar finishes.
19
+ * 3. Collapse runs of blank lines.
20
+ */
5
21
  function sanitizePipedOutput(raw) {
6
- let text = raw.replace(ANSI_RE, "");
7
- text = text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
8
- text = text.split("\n").map((line) => {
9
- if (!line.includes("\r")) return line;
10
- const segments = line.split("\r");
11
- for (let i = segments.length - 1; i >= 0; i--) {
12
- if (segments[i].length > 0) return segments[i];
13
- }
14
- return "";
15
- }).join("\n");
16
- text = text.replace(/\n{3,}/g, "\n\n");
17
- return text.trim();
22
+ let text = raw.replace(ANSI_RE, "");
23
+ text = text.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "");
24
+ text = text.split("\n").map((line) => {
25
+ if (!line.includes("\r")) return line;
26
+ const segments = line.split("\r");
27
+ for (let i = segments.length - 1; i >= 0; i--) if (segments[i].length > 0) return segments[i];
28
+ return "";
29
+ }).join("\n");
30
+ text = text.replace(/\n{3,}/g, "\n\n");
31
+ return text.trim();
18
32
  }
33
+ /**
34
+ * If stdin is a pipe (not a TTY), read **all** data until EOF and return it as
35
+ * a string. The returned promise only resolves once the write end of the pipe
36
+ * is closed — i.e. the sending process has fully exited — so callers are
37
+ * guaranteed to receive the complete output, not a partial snapshot from a
38
+ * program that writes progressively.
39
+ *
40
+ * Returns `null` when:
41
+ * - stdin is a TTY (interactive terminal)
42
+ * - the pipe was empty or contained only whitespace
43
+ */
19
44
  async function drainPipedStdin() {
20
- if (process.stdin.isTTY) return null;
21
- const chunks = [];
22
- let totalBytes = 0;
23
- let truncated = false;
24
- for await (const chunk of process.stdin) {
25
- const buf = chunk;
26
- if (totalBytes + buf.length > MAX_PIPE_BYTES) {
27
- const remaining = MAX_PIPE_BYTES - totalBytes;
28
- if (remaining > 0) {
29
- chunks.push(Buffer.from(buf.subarray(0, remaining)));
30
- }
31
- buf.fill(0);
32
- totalBytes = MAX_PIPE_BYTES;
33
- truncated = true;
34
- continue;
35
- }
36
- chunks.push(buf);
37
- totalBytes += buf.length;
38
- }
39
- if (truncated) {
40
- process.stderr.write(`Warning: Piped input exceeded ${MAX_PIPE_BYTES / 1024 / 1024}MB and was truncated.
41
- `);
42
- }
43
- const raw = Buffer.concat(chunks).toString("utf-8");
44
- for (const buf of chunks) {
45
- buf.fill(0);
46
- }
47
- chunks.length = 0;
48
- const content = sanitizePipedOutput(raw);
49
- return content.length > 0 ? content : null;
45
+ if (process.stdin.isTTY) return null;
46
+ const chunks = [];
47
+ let totalBytes = 0;
48
+ let truncated = false;
49
+ for await (const chunk of process.stdin) {
50
+ const buf = chunk;
51
+ if (totalBytes + buf.length > MAX_PIPE_BYTES) {
52
+ const remaining = MAX_PIPE_BYTES - totalBytes;
53
+ if (remaining > 0) chunks.push(Buffer.from(buf.subarray(0, remaining)));
54
+ buf.fill(0);
55
+ totalBytes = MAX_PIPE_BYTES;
56
+ truncated = true;
57
+ continue;
58
+ }
59
+ chunks.push(buf);
60
+ totalBytes += buf.length;
61
+ }
62
+ if (truncated) process.stderr.write(`Warning: Piped input exceeded ${MAX_PIPE_BYTES / 1024 / 1024}MB and was truncated.\n`);
63
+ const raw = Buffer.concat(chunks).toString("utf-8");
64
+ for (const buf of chunks) buf.fill(0);
65
+ chunks.length = 0;
66
+ const content = sanitizePipedOutput(raw);
67
+ return content.length > 0 ? content : null;
50
68
  }
69
+ /**
70
+ * Reopen `/dev/tty` (or `CON` on Windows) as `process.stdin` so that the
71
+ * interactive TUI can read keyboard input after the original piped stdin has
72
+ * been consumed.
73
+ *
74
+ * @returns `true` if the swap succeeded, `false` if a TTY could not be opened
75
+ * (e.g. running in a headless CI container with no controlling terminal).
76
+ */
51
77
  function reopenStdinFromTTY() {
52
- const ttyPath = process.platform === "win32" ? "CON" : "/dev/tty";
53
- let fd;
54
- try {
55
- fd = fs.openSync(ttyPath, "r");
56
- } catch {
57
- return false;
58
- }
59
- const ttyStream = new tty.ReadStream(fd);
60
- Object.defineProperty(process, "stdin", {
61
- value: ttyStream,
62
- writable: true,
63
- configurable: true
64
- });
65
- return true;
78
+ const ttyPath = process.platform === "win32" ? "CON" : "/dev/tty";
79
+ let fd;
80
+ try {
81
+ fd = fs$1.openSync(ttyPath, "r");
82
+ } catch {
83
+ return false;
84
+ }
85
+ const ttyStream = new tty.ReadStream(fd);
86
+ Object.defineProperty(process, "stdin", {
87
+ value: ttyStream,
88
+ writable: true,
89
+ configurable: true
90
+ });
91
+ return true;
66
92
  }
67
- export {
68
- drainPipedStdin,
69
- reopenStdinFromTTY,
70
- sanitizePipedOutput
71
- };
93
+ //#endregion
94
+ export { drainPipedStdin, reopenStdinFromTTY, sanitizePipedOutput };
95
+
72
96
  //# sourceMappingURL=stdin-pipe.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/stdin-pipe.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as tty from 'node:tty';\n\n/** Maximum piped input size (1 MB). Content beyond this is truncated. */\nconst MAX_PIPE_BYTES = 1024 * 1024;\n\n/**\n * Matches all ANSI escape sequences — SGR (colors), cursor movement, line\n * clears, scrolling, window titles, etc.\n */\nconst ANSI_RE = /\\x1b(?:\\[[0-9;?]*[A-Za-z]|\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)?|\\([A-Z])/g;\n\n/**\n * Clean up raw piped output so the agent sees readable text, not terminal\n * control noise.\n *\n * 1. Strip all ANSI escape sequences (colors, cursor movement, line clears).\n * 2. Simulate carriage-return overwrites: for each line, split on `\\r` and\n * keep only the last segment — this is what the terminal would display\n * after a spinner/progress bar finishes.\n * 3. Collapse runs of blank lines.\n */\nexport function sanitizePipedOutput(raw: string): string {\n // Strip ANSI escapes first\n let text = raw.replace(ANSI_RE, '');\n\n // Strip binary control characters (everything below 0x20 except \\t, \\n, \\r)\n text = text.replace(/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/g, '');\n\n // Simulate \\r overwrites: for each line, the last \\r-segment wins\n text = text\n .split('\\n')\n .map(line => {\n if (!line.includes('\\r')) return line;\n const segments = line.split('\\r');\n // Walk backwards to find the last non-empty segment — a trailing \\r\n // produces an empty final segment that would otherwise discard visible text.\n for (let i = segments.length - 1; i >= 0; i--) {\n if (segments[i]!.length > 0) return segments[i]!;\n }\n return '';\n })\n .join('\\n');\n\n // Collapse 3+ consecutive blank lines into 2\n text = text.replace(/\\n{3,}/g, '\\n\\n');\n\n return text.trim();\n}\n\n/**\n * If stdin is a pipe (not a TTY), read **all** data until EOF and return it as\n * a string. The returned promise only resolves once the write end of the pipe\n * is closed — i.e. the sending process has fully exited — so callers are\n * guaranteed to receive the complete output, not a partial snapshot from a\n * program that writes progressively.\n *\n * Returns `null` when:\n * - stdin is a TTY (interactive terminal)\n * - the pipe was empty or contained only whitespace\n */\nexport async function drainPipedStdin(): Promise<string | null> {\n if (process.stdin.isTTY) return null;\n\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n let truncated = false;\n\n for await (const chunk of process.stdin) {\n const buf = chunk as Buffer;\n\n if (totalBytes + buf.length > MAX_PIPE_BYTES) {\n // Take only what fits under the cap — copy so we don't retain the\n // original backing buffer, then zero the source.\n const remaining = MAX_PIPE_BYTES - totalBytes;\n if (remaining > 0) {\n chunks.push(Buffer.from(buf.subarray(0, remaining)));\n }\n buf.fill(0);\n totalBytes = MAX_PIPE_BYTES;\n truncated = true;\n // Keep draining so we don't leave unread data in the pipe, but don't\n // store it — we still need to wait for EOF.\n continue;\n }\n\n chunks.push(buf);\n totalBytes += buf.length;\n }\n\n if (truncated) {\n process.stderr.write(`Warning: Piped input exceeded ${MAX_PIPE_BYTES / 1024 / 1024}MB and was truncated.\\n`);\n }\n\n const raw = Buffer.concat(chunks).toString('utf-8');\n\n // Zero out raw buffers so piped secrets don't linger in memory\n for (const buf of chunks) {\n buf.fill(0);\n }\n chunks.length = 0;\n\n const content = sanitizePipedOutput(raw);\n return content.length > 0 ? content : null;\n}\n\n/**\n * Reopen `/dev/tty` (or `CON` on Windows) as `process.stdin` so that the\n * interactive TUI can read keyboard input after the original piped stdin has\n * been consumed.\n *\n * @returns `true` if the swap succeeded, `false` if a TTY could not be opened\n * (e.g. running in a headless CI container with no controlling terminal).\n */\nexport function reopenStdinFromTTY(): boolean {\n const ttyPath = process.platform === 'win32' ? 'CON' : '/dev/tty';\n\n let fd: number;\n try {\n fd = fs.openSync(ttyPath, 'r');\n } catch {\n return false;\n }\n\n const ttyStream = new tty.ReadStream(fd);\n\n Object.defineProperty(process, 'stdin', {\n value: ttyStream,\n writable: true,\n configurable: true,\n });\n\n return true;\n}\n"],"mappings":"AAAA,YAAY,QAAQ;AACpB,YAAY,SAAS;AAGrB,MAAM,iBAAiB,OAAO;AAM9B,MAAM,UAAU;AAYT,SAAS,oBAAoB,KAAqB;AAEvD,MAAI,OAAO,IAAI,QAAQ,SAAS,EAAE;AAGlC,SAAO,KAAK,QAAQ,qCAAqC,EAAE;AAG3D,SAAO,KACJ,MAAM,IAAI,EACV,IAAI,UAAQ;AACX,QAAI,CAAC,KAAK,SAAS,IAAI,EAAG,QAAO;AACjC,UAAM,WAAW,KAAK,MAAM,IAAI;AAGhC,aAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,UAAI,SAAS,CAAC,EAAG,SAAS,EAAG,QAAO,SAAS,CAAC;AAAA,IAChD;AACA,WAAO;AAAA,EACT,CAAC,EACA,KAAK,IAAI;AAGZ,SAAO,KAAK,QAAQ,WAAW,MAAM;AAErC,SAAO,KAAK,KAAK;AACnB;AAaA,eAAsB,kBAA0C;AAC9D,MAAI,QAAQ,MAAM,MAAO,QAAO;AAEhC,QAAM,SAAmB,CAAC;AAC1B,MAAI,aAAa;AACjB,MAAI,YAAY;AAEhB,mBAAiB,SAAS,QAAQ,OAAO;AACvC,UAAM,MAAM;AAEZ,QAAI,aAAa,IAAI,SAAS,gBAAgB;AAG5C,YAAM,YAAY,iBAAiB;AACnC,UAAI,YAAY,GAAG;AACjB,eAAO,KAAK,OAAO,KAAK,IAAI,SAAS,GAAG,SAAS,CAAC,CAAC;AAAA,MACrD;AACA,UAAI,KAAK,CAAC;AACV,mBAAa;AACb,kBAAY;AAGZ;AAAA,IACF;AAEA,WAAO,KAAK,GAAG;AACf,kBAAc,IAAI;AAAA,EACpB;AAEA,MAAI,WAAW;AACb,YAAQ,OAAO,MAAM,iCAAiC,iBAAiB,OAAO,IAAI;AAAA,CAAyB;AAAA,EAC7G;AAEA,QAAM,MAAM,OAAO,OAAO,MAAM,EAAE,SAAS,OAAO;AAGlD,aAAW,OAAO,QAAQ;AACxB,QAAI,KAAK,CAAC;AAAA,EACZ;AACA,SAAO,SAAS;AAEhB,QAAM,UAAU,oBAAoB,GAAG;AACvC,SAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;AAUO,SAAS,qBAA8B;AAC5C,QAAM,UAAU,QAAQ,aAAa,UAAU,QAAQ;AAEvD,MAAI;AACJ,MAAI;AACF,SAAK,GAAG,SAAS,SAAS,GAAG;AAAA,EAC/B,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,IAAI,IAAI,WAAW,EAAE;AAEvC,SAAO,eAAe,SAAS,SAAS;AAAA,IACtC,OAAO;AAAA,IACP,UAAU;AAAA,IACV,cAAc;AAAA,EAChB,CAAC;AAED,SAAO;AACT;","names":[]}
1
+ {"version":3,"file":"stdin-pipe.js","names":["fs"],"sources":["../../src/utils/stdin-pipe.ts"],"sourcesContent":["import * as fs from 'node:fs';\nimport * as tty from 'node:tty';\n\n/** Maximum piped input size (1 MB). Content beyond this is truncated. */\nconst MAX_PIPE_BYTES = 1024 * 1024;\n\n/**\n * Matches all ANSI escape sequences — SGR (colors), cursor movement, line\n * clears, scrolling, window titles, etc.\n */\nconst ANSI_RE = /\\x1b(?:\\[[0-9;?]*[A-Za-z]|\\][^\\x07\\x1b]*(?:\\x07|\\x1b\\\\)?|\\([A-Z])/g;\n\n/**\n * Clean up raw piped output so the agent sees readable text, not terminal\n * control noise.\n *\n * 1. Strip all ANSI escape sequences (colors, cursor movement, line clears).\n * 2. Simulate carriage-return overwrites: for each line, split on `\\r` and\n * keep only the last segment — this is what the terminal would display\n * after a spinner/progress bar finishes.\n * 3. Collapse runs of blank lines.\n */\nexport function sanitizePipedOutput(raw: string): string {\n // Strip ANSI escapes first\n let text = raw.replace(ANSI_RE, '');\n\n // Strip binary control characters (everything below 0x20 except \\t, \\n, \\r)\n text = text.replace(/[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\x7F]/g, '');\n\n // Simulate \\r overwrites: for each line, the last \\r-segment wins\n text = text\n .split('\\n')\n .map(line => {\n if (!line.includes('\\r')) return line;\n const segments = line.split('\\r');\n // Walk backwards to find the last non-empty segment — a trailing \\r\n // produces an empty final segment that would otherwise discard visible text.\n for (let i = segments.length - 1; i >= 0; i--) {\n if (segments[i]!.length > 0) return segments[i]!;\n }\n return '';\n })\n .join('\\n');\n\n // Collapse 3+ consecutive blank lines into 2\n text = text.replace(/\\n{3,}/g, '\\n\\n');\n\n return text.trim();\n}\n\n/**\n * If stdin is a pipe (not a TTY), read **all** data until EOF and return it as\n * a string. The returned promise only resolves once the write end of the pipe\n * is closed — i.e. the sending process has fully exited — so callers are\n * guaranteed to receive the complete output, not a partial snapshot from a\n * program that writes progressively.\n *\n * Returns `null` when:\n * - stdin is a TTY (interactive terminal)\n * - the pipe was empty or contained only whitespace\n */\nexport async function drainPipedStdin(): Promise<string | null> {\n if (process.stdin.isTTY) return null;\n\n const chunks: Buffer[] = [];\n let totalBytes = 0;\n let truncated = false;\n\n for await (const chunk of process.stdin) {\n const buf = chunk as Buffer;\n\n if (totalBytes + buf.length > MAX_PIPE_BYTES) {\n // Take only what fits under the cap — copy so we don't retain the\n // original backing buffer, then zero the source.\n const remaining = MAX_PIPE_BYTES - totalBytes;\n if (remaining > 0) {\n chunks.push(Buffer.from(buf.subarray(0, remaining)));\n }\n buf.fill(0);\n totalBytes = MAX_PIPE_BYTES;\n truncated = true;\n // Keep draining so we don't leave unread data in the pipe, but don't\n // store it — we still need to wait for EOF.\n continue;\n }\n\n chunks.push(buf);\n totalBytes += buf.length;\n }\n\n if (truncated) {\n process.stderr.write(`Warning: Piped input exceeded ${MAX_PIPE_BYTES / 1024 / 1024}MB and was truncated.\\n`);\n }\n\n const raw = Buffer.concat(chunks).toString('utf-8');\n\n // Zero out raw buffers so piped secrets don't linger in memory\n for (const buf of chunks) {\n buf.fill(0);\n }\n chunks.length = 0;\n\n const content = sanitizePipedOutput(raw);\n return content.length > 0 ? content : null;\n}\n\n/**\n * Reopen `/dev/tty` (or `CON` on Windows) as `process.stdin` so that the\n * interactive TUI can read keyboard input after the original piped stdin has\n * been consumed.\n *\n * @returns `true` if the swap succeeded, `false` if a TTY could not be opened\n * (e.g. running in a headless CI container with no controlling terminal).\n */\nexport function reopenStdinFromTTY(): boolean {\n const ttyPath = process.platform === 'win32' ? 'CON' : '/dev/tty';\n\n let fd: number;\n try {\n fd = fs.openSync(ttyPath, 'r');\n } catch {\n return false;\n }\n\n const ttyStream = new tty.ReadStream(fd);\n\n Object.defineProperty(process, 'stdin', {\n value: ttyStream,\n writable: true,\n configurable: true,\n });\n\n return true;\n}\n"],"mappings":";;;;AAIA,MAAM,iBAAiB,OAAO;;;;;AAM9B,MAAM,UAAU;;;;;;;;;;;AAYhB,SAAgB,oBAAoB,KAAqB;CAEvD,IAAI,OAAO,IAAI,QAAQ,SAAS,EAAE;CAGlC,OAAO,KAAK,QAAQ,qCAAqC,EAAE;CAG3D,OAAO,KACJ,MAAM,IAAI,CAAC,CACX,KAAI,SAAQ;EACX,IAAI,CAAC,KAAK,SAAS,IAAI,GAAG,OAAO;EACjC,MAAM,WAAW,KAAK,MAAM,IAAI;EAGhC,KAAK,IAAI,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KACxC,IAAI,SAAS,EAAE,CAAE,SAAS,GAAG,OAAO,SAAS;EAE/C,OAAO;CACT,CAAC,CAAC,CACD,KAAK,IAAI;CAGZ,OAAO,KAAK,QAAQ,WAAW,MAAM;CAErC,OAAO,KAAK,KAAK;AACnB;;;;;;;;;;;;AAaA,eAAsB,kBAA0C;CAC9D,IAAI,QAAQ,MAAM,OAAO,OAAO;CAEhC,MAAM,SAAmB,CAAC;CAC1B,IAAI,aAAa;CACjB,IAAI,YAAY;CAEhB,WAAW,MAAM,SAAS,QAAQ,OAAO;EACvC,MAAM,MAAM;EAEZ,IAAI,aAAa,IAAI,SAAS,gBAAgB;GAG5C,MAAM,YAAY,iBAAiB;GACnC,IAAI,YAAY,GACd,OAAO,KAAK,OAAO,KAAK,IAAI,SAAS,GAAG,SAAS,CAAC,CAAC;GAErD,IAAI,KAAK,CAAC;GACV,aAAa;GACb,YAAY;GAGZ;EACF;EAEA,OAAO,KAAK,GAAG;EACf,cAAc,IAAI;CACpB;CAEA,IAAI,WACF,QAAQ,OAAO,MAAM,iCAAiC,iBAAiB,OAAO,KAAK,wBAAwB;CAG7G,MAAM,MAAM,OAAO,OAAO,MAAM,CAAC,CAAC,SAAS,OAAO;CAGlD,KAAK,MAAM,OAAO,QAChB,IAAI,KAAK,CAAC;CAEZ,OAAO,SAAS;CAEhB,MAAM,UAAU,oBAAoB,GAAG;CACvC,OAAO,QAAQ,SAAS,IAAI,UAAU;AACxC;;;;;;;;;AAUA,SAAgB,qBAA8B;CAC5C,MAAM,UAAU,QAAQ,aAAa,UAAU,QAAQ;CAEvD,IAAI;CACJ,IAAI;EACF,KAAKA,KAAG,SAAS,SAAS,GAAG;CAC/B,QAAQ;EACN,OAAO;CACT;CAEA,MAAM,YAAY,IAAI,IAAI,WAAW,EAAE;CAEvC,OAAO,eAAe,SAAS,SAAS;EACtC,OAAO;EACP,UAAU;EACV,cAAc;CAChB,CAAC;CAED,OAAO;AACT"}
@@ -1,101 +1,121 @@
1
- import { LibSQLStore, LibSQLVector } from "@mastra/libsql";
2
- import { PostgresStore } from "@mastra/pg";
3
1
  import { getDatabasePath, getVectorDatabasePath } from "./project.js";
4
2
  import { DEFAULT_RETENTION } from "./storage-maintenance.js";
3
+ import { LibSQLStore, LibSQLVector } from "@mastra/libsql";
4
+ import { PostgresStore } from "@mastra/pg";
5
+ //#region src/utils/storage-factory.ts
5
6
  const MASTRA_CODE_LOCAL_PRAGMAS = {
6
- cacheSize: -128e3,
7
- mmapSize: 536870912
7
+ cacheSize: -128e3,
8
+ mmapSize: 536870912
8
9
  };
10
+ /**
11
+ * Construct a LibSQL store for an arbitrary url/authToken, applying the same
12
+ * local pragmas the default factory uses.
13
+ */
9
14
  function buildLibSQLStore(opts) {
10
- return new LibSQLStore({
11
- id: opts.id ?? "mastra-code-storage",
12
- url: opts.url,
13
- ...opts.authToken ? { authToken: opts.authToken } : {},
14
- ...opts.retention ? { retention: opts.retention } : {},
15
- localPragmas: MASTRA_CODE_LOCAL_PRAGMAS
16
- });
15
+ return new LibSQLStore({
16
+ id: opts.id ?? "mastra-code-storage",
17
+ url: opts.url,
18
+ ...opts.authToken ? { authToken: opts.authToken } : {},
19
+ ...opts.retention ? { retention: opts.retention } : {},
20
+ localPragmas: MASTRA_CODE_LOCAL_PRAGMAS
21
+ });
17
22
  }
23
+ /** Construct a LibSQL vector store for an arbitrary url/authToken. */
18
24
  function buildLibSQLVector(opts) {
19
- return new LibSQLVector({
20
- id: opts.id ?? "mastra-code-vectors",
21
- url: opts.url,
22
- ...opts.authToken ? { authToken: opts.authToken } : {}
23
- });
25
+ return new LibSQLVector({
26
+ id: opts.id ?? "mastra-code-vectors",
27
+ url: opts.url,
28
+ ...opts.authToken ? { authToken: opts.authToken } : {}
29
+ });
24
30
  }
25
31
  function createFallbackLibSQL() {
26
- return buildLibSQLStore({ url: `file:${getDatabasePath()}`, retention: DEFAULT_RETENTION });
32
+ return buildLibSQLStore({
33
+ url: `file:${getDatabasePath()}`,
34
+ retention: DEFAULT_RETENTION
35
+ });
27
36
  }
37
+ /**
38
+ * Create a storage instance from the resolved config.
39
+ *
40
+ * - `libsql` backend → LibSQLStore (always available)
41
+ * - `pg` backend → PostgresStore, falls back to LibSQL on connection failure
42
+ */
28
43
  async function createStorage(config) {
29
- if (config.backend === "pg") {
30
- return createPgStorage(config);
31
- }
32
- return {
33
- storage: buildLibSQLStore({ url: config.url, authToken: config.authToken, retention: DEFAULT_RETENTION }),
34
- backend: "libsql"
35
- };
44
+ if (config.backend === "pg") return createPgStorage(config);
45
+ return {
46
+ storage: buildLibSQLStore({
47
+ url: config.url,
48
+ authToken: config.authToken,
49
+ retention: DEFAULT_RETENTION
50
+ }),
51
+ backend: "libsql"
52
+ };
36
53
  }
37
54
  async function createPgStorage(config) {
38
- if (!config.connectionString && !config.host) {
39
- return {
40
- storage: createFallbackLibSQL(),
41
- backend: "libsql",
42
- warning: "PostgreSQL backend selected but no connection info configured. Using LibSQL fallback. Set a connection string via /settings."
43
- };
44
- }
45
- const base = {
46
- id: "mastra-code-storage",
47
- retention: DEFAULT_RETENTION,
48
- ...config.schemaName ? { schemaName: config.schemaName } : {},
49
- ...config.disableInit ? { disableInit: config.disableInit } : {},
50
- ...config.skipDefaultIndexes ? { skipDefaultIndexes: config.skipDefaultIndexes } : {}
51
- };
52
- const store = config.connectionString ? new PostgresStore({ ...base, connectionString: config.connectionString }) : new PostgresStore({
53
- ...base,
54
- host: config.host,
55
- port: config.port,
56
- database: config.database,
57
- user: config.user,
58
- password: config.password
59
- });
60
- try {
61
- await store.init();
62
- } catch (err) {
63
- const msg = err?.message ?? String(err);
64
- const target = config.connectionString ?? `${config.host}:${config.port ?? 5432}`;
65
- try {
66
- await store.close();
67
- } catch {
68
- }
69
- return {
70
- storage: createFallbackLibSQL(),
71
- backend: "libsql",
72
- warning: `Failed to connect to PostgreSQL at ${target}: ${msg}
73
- Using LibSQL fallback. Fix the connection via /settings.`
74
- };
75
- }
76
- return { storage: store, backend: "pg" };
55
+ if (!config.connectionString && !config.host) return {
56
+ storage: createFallbackLibSQL(),
57
+ backend: "libsql",
58
+ warning: "PostgreSQL backend selected but no connection info configured. Using LibSQL fallback. Set a connection string via /settings."
59
+ };
60
+ const base = {
61
+ id: "mastra-code-storage",
62
+ retention: DEFAULT_RETENTION,
63
+ ...config.schemaName ? { schemaName: config.schemaName } : {},
64
+ ...config.disableInit ? { disableInit: config.disableInit } : {},
65
+ ...config.skipDefaultIndexes ? { skipDefaultIndexes: config.skipDefaultIndexes } : {}
66
+ };
67
+ const store = config.connectionString ? new PostgresStore({
68
+ ...base,
69
+ connectionString: config.connectionString
70
+ }) : new PostgresStore({
71
+ ...base,
72
+ host: config.host,
73
+ port: config.port,
74
+ database: config.database,
75
+ user: config.user,
76
+ password: config.password
77
+ });
78
+ try {
79
+ await store.init();
80
+ } catch (err) {
81
+ const msg = err?.message ?? String(err);
82
+ const target = config.connectionString ?? `${config.host}:${config.port ?? 5432}`;
83
+ try {
84
+ await store.close();
85
+ } catch {}
86
+ return {
87
+ storage: createFallbackLibSQL(),
88
+ backend: "libsql",
89
+ warning: `Failed to connect to PostgreSQL at ${target}: ${msg}\nUsing LibSQL fallback. Fix the connection via /settings.`
90
+ };
91
+ }
92
+ return {
93
+ storage: store,
94
+ backend: "pg"
95
+ };
77
96
  }
97
+ /**
98
+ * Create a vector store for recall search.
99
+ * Uses a separate LibSQL file to avoid bloating the main storage DB with embedding data.
100
+ * For PG backends, reuses the same connection (PG handles the extra tables fine).
101
+ */
78
102
  async function createVectorStore(config, effectiveBackend = config.backend) {
79
- if (effectiveBackend === "pg") {
80
- const pgConfig = config;
81
- if (!pgConfig.connectionString && !pgConfig.host) return void 0;
82
- const { PgVector } = await import("@mastra/pg");
83
- return new PgVector({
84
- id: "mastra-code-vectors",
85
- connectionString: pgConfig.connectionString ?? `postgresql://${pgConfig.user}:${pgConfig.password}@${pgConfig.host}:${pgConfig.port ?? 5432}/${pgConfig.database}`
86
- });
87
- }
88
- const libsqlConfig = config;
89
- return buildLibSQLVector({
90
- url: libsqlConfig.vectorUrl ?? `file:${getVectorDatabasePath()}`,
91
- authToken: libsqlConfig.vectorAuthToken
92
- });
103
+ if (effectiveBackend === "pg") {
104
+ const pgConfig = config;
105
+ if (!pgConfig.connectionString && !pgConfig.host) return void 0;
106
+ const { PgVector } = await import("@mastra/pg");
107
+ return new PgVector({
108
+ id: "mastra-code-vectors",
109
+ connectionString: pgConfig.connectionString ?? `postgresql://${pgConfig.user}:${pgConfig.password}@${pgConfig.host}:${pgConfig.port ?? 5432}/${pgConfig.database}`
110
+ });
111
+ }
112
+ const libsqlConfig = config;
113
+ return buildLibSQLVector({
114
+ url: libsqlConfig.vectorUrl ?? `file:${getVectorDatabasePath()}`,
115
+ authToken: libsqlConfig.vectorAuthToken
116
+ });
93
117
  }
94
- export {
95
- MASTRA_CODE_LOCAL_PRAGMAS,
96
- buildLibSQLStore,
97
- buildLibSQLVector,
98
- createStorage,
99
- createVectorStore
100
- };
118
+ //#endregion
119
+ export { MASTRA_CODE_LOCAL_PRAGMAS, buildLibSQLStore, buildLibSQLVector, createStorage, createVectorStore };
120
+
101
121
  //# sourceMappingURL=storage-factory.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/storage-factory.ts"],"sourcesContent":["/**\n * Storage factory — creates the appropriate storage backend based on resolved config.\n *\n * If PG is selected but fails to connect, falls back to LibSQL so the TUI\n * can start and the user can fix the connection via /settings.\n */\n\nimport type { MastraCompositeStore, RetentionConfig } from '@mastra/core/storage';\nimport type { MastraVector } from '@mastra/core/vector';\nimport { LibSQLStore, LibSQLVector } from '@mastra/libsql';\nimport { PostgresStore } from '@mastra/pg';\n\nimport type { StorageConfig, PgStorageConfig } from './project.js';\nimport { getDatabasePath, getVectorDatabasePath } from './project.js';\nimport { DEFAULT_RETENTION } from './storage-maintenance.js';\n\nexport const MASTRA_CODE_LOCAL_PRAGMAS = {\n cacheSize: -128000,\n mmapSize: 536870912,\n};\n\n/**\n * Construct a LibSQL store for an arbitrary url/authToken, applying the same\n * local pragmas the default factory uses.\n */\nexport function buildLibSQLStore(opts: {\n id?: string;\n url: string;\n authToken?: string;\n retention?: RetentionConfig;\n}): MastraCompositeStore {\n return new LibSQLStore({\n id: opts.id ?? 'mastra-code-storage',\n url: opts.url,\n ...(opts.authToken ? { authToken: opts.authToken } : {}),\n ...(opts.retention ? { retention: opts.retention } : {}),\n localPragmas: MASTRA_CODE_LOCAL_PRAGMAS,\n });\n}\n\n/** Construct a LibSQL vector store for an arbitrary url/authToken. */\nexport function buildLibSQLVector(opts: { id?: string; url: string; authToken?: string }): MastraVector {\n return new LibSQLVector({\n id: opts.id ?? 'mastra-code-vectors',\n url: opts.url,\n ...(opts.authToken ? { authToken: opts.authToken } : {}),\n });\n}\n\nexport interface StorageResult {\n storage: MastraCompositeStore;\n /** The effective backend after any fallback logic has run. */\n backend: 'libsql' | 'pg';\n /** Non-null when PG was requested but failed — contains a user-facing warning. */\n warning?: string;\n}\n\nfunction createFallbackLibSQL(): MastraCompositeStore {\n return buildLibSQLStore({ url: `file:${getDatabasePath()}`, retention: DEFAULT_RETENTION });\n}\n\n/**\n * Create a storage instance from the resolved config.\n *\n * - `libsql` backend → LibSQLStore (always available)\n * - `pg` backend → PostgresStore, falls back to LibSQL on connection failure\n */\nexport async function createStorage(config: StorageConfig): Promise<StorageResult> {\n if (config.backend === 'pg') {\n return createPgStorage(config);\n }\n\n // Default: LibSQL\n return {\n storage: buildLibSQLStore({ url: config.url, authToken: config.authToken, retention: DEFAULT_RETENTION }),\n backend: 'libsql',\n };\n}\n\nasync function createPgStorage(config: PgStorageConfig): Promise<StorageResult> {\n // No connection info → fall back with guidance\n if (!config.connectionString && !config.host) {\n return {\n storage: createFallbackLibSQL(),\n backend: 'libsql',\n warning:\n 'PostgreSQL backend selected but no connection info configured. ' +\n 'Using LibSQL fallback. Set a connection string via /settings.',\n };\n }\n\n const base = {\n id: 'mastra-code-storage' as const,\n retention: DEFAULT_RETENTION,\n ...(config.schemaName ? { schemaName: config.schemaName } : {}),\n ...(config.disableInit ? { disableInit: config.disableInit } : {}),\n ...(config.skipDefaultIndexes ? { skipDefaultIndexes: config.skipDefaultIndexes } : {}),\n };\n\n const store = config.connectionString\n ? new PostgresStore({ ...base, connectionString: config.connectionString })\n : new PostgresStore({\n ...base,\n host: config.host!,\n port: config.port,\n database: config.database,\n user: config.user,\n password: config.password,\n });\n\n // Test the connection before committing — if it fails, fall back to LibSQL\n // so the user can fix the config via /settings.\n try {\n await store.init();\n } catch (err: any) {\n const msg = err?.message ?? String(err);\n const target = config.connectionString ?? `${config.host}:${config.port ?? 5432}`;\n try {\n await store.close();\n } catch {\n // ignore cleanup errors\n }\n return {\n storage: createFallbackLibSQL(),\n backend: 'libsql',\n warning:\n `Failed to connect to PostgreSQL at ${target}: ${msg}\\n` +\n 'Using LibSQL fallback. Fix the connection via /settings.',\n };\n }\n\n return { storage: store, backend: 'pg' };\n}\n\n/**\n * Create a vector store for recall search.\n * Uses a separate LibSQL file to avoid bloating the main storage DB with embedding data.\n * For PG backends, reuses the same connection (PG handles the extra tables fine).\n */\nexport async function createVectorStore(\n config: StorageConfig,\n effectiveBackend: 'libsql' | 'pg' = config.backend,\n): Promise<MastraVector | undefined> {\n if (effectiveBackend === 'pg') {\n // PG can handle vector tables in the same database\n const pgConfig = config as PgStorageConfig;\n if (!pgConfig.connectionString && !pgConfig.host) return undefined;\n\n const { PgVector } = await import('@mastra/pg');\n return new PgVector({\n id: 'mastra-code-vectors',\n connectionString:\n pgConfig.connectionString ??\n `postgresql://${pgConfig.user}:${pgConfig.password}@${pgConfig.host}:${pgConfig.port ?? 5432}/${pgConfig.database}`,\n });\n }\n\n // LibSQL: separate file for vectors. Per-tenant configs supply an explicit\n // vectorUrl so each tenant's recall vectors are isolated; otherwise use the\n // shared default file.\n const libsqlConfig = config as { vectorUrl?: string; vectorAuthToken?: string };\n return buildLibSQLVector({\n url: libsqlConfig.vectorUrl ?? `file:${getVectorDatabasePath()}`,\n authToken: libsqlConfig.vectorAuthToken,\n });\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"storage-factory.js","names":[],"sources":["../../src/utils/storage-factory.ts"],"sourcesContent":["/**\n * Storage factory — creates the appropriate storage backend based on resolved config.\n *\n * If PG is selected but fails to connect, falls back to LibSQL so the TUI\n * can start and the user can fix the connection via /settings.\n */\n\nimport type { MastraCompositeStore, RetentionConfig } from '@mastra/core/storage';\nimport type { MastraVector } from '@mastra/core/vector';\nimport { LibSQLStore, LibSQLVector } from '@mastra/libsql';\nimport { PostgresStore } from '@mastra/pg';\n\nimport type { StorageConfig, PgStorageConfig } from './project.js';\nimport { getDatabasePath, getVectorDatabasePath } from './project.js';\nimport { DEFAULT_RETENTION } from './storage-maintenance.js';\n\nexport const MASTRA_CODE_LOCAL_PRAGMAS = {\n cacheSize: -128000,\n mmapSize: 536870912,\n};\n\n/**\n * Construct a LibSQL store for an arbitrary url/authToken, applying the same\n * local pragmas the default factory uses.\n */\nexport function buildLibSQLStore(opts: {\n id?: string;\n url: string;\n authToken?: string;\n retention?: RetentionConfig;\n}): MastraCompositeStore {\n return new LibSQLStore({\n id: opts.id ?? 'mastra-code-storage',\n url: opts.url,\n ...(opts.authToken ? { authToken: opts.authToken } : {}),\n ...(opts.retention ? { retention: opts.retention } : {}),\n localPragmas: MASTRA_CODE_LOCAL_PRAGMAS,\n });\n}\n\n/** Construct a LibSQL vector store for an arbitrary url/authToken. */\nexport function buildLibSQLVector(opts: { id?: string; url: string; authToken?: string }): MastraVector {\n return new LibSQLVector({\n id: opts.id ?? 'mastra-code-vectors',\n url: opts.url,\n ...(opts.authToken ? { authToken: opts.authToken } : {}),\n });\n}\n\nexport interface StorageResult {\n storage: MastraCompositeStore;\n /** The effective backend after any fallback logic has run. */\n backend: 'libsql' | 'pg';\n /** Non-null when PG was requested but failed — contains a user-facing warning. */\n warning?: string;\n}\n\nfunction createFallbackLibSQL(): MastraCompositeStore {\n return buildLibSQLStore({ url: `file:${getDatabasePath()}`, retention: DEFAULT_RETENTION });\n}\n\n/**\n * Create a storage instance from the resolved config.\n *\n * - `libsql` backend → LibSQLStore (always available)\n * - `pg` backend → PostgresStore, falls back to LibSQL on connection failure\n */\nexport async function createStorage(config: StorageConfig): Promise<StorageResult> {\n if (config.backend === 'pg') {\n return createPgStorage(config);\n }\n\n // Default: LibSQL\n return {\n storage: buildLibSQLStore({ url: config.url, authToken: config.authToken, retention: DEFAULT_RETENTION }),\n backend: 'libsql',\n };\n}\n\nasync function createPgStorage(config: PgStorageConfig): Promise<StorageResult> {\n // No connection info → fall back with guidance\n if (!config.connectionString && !config.host) {\n return {\n storage: createFallbackLibSQL(),\n backend: 'libsql',\n warning:\n 'PostgreSQL backend selected but no connection info configured. ' +\n 'Using LibSQL fallback. Set a connection string via /settings.',\n };\n }\n\n const base = {\n id: 'mastra-code-storage' as const,\n retention: DEFAULT_RETENTION,\n ...(config.schemaName ? { schemaName: config.schemaName } : {}),\n ...(config.disableInit ? { disableInit: config.disableInit } : {}),\n ...(config.skipDefaultIndexes ? { skipDefaultIndexes: config.skipDefaultIndexes } : {}),\n };\n\n const store = config.connectionString\n ? new PostgresStore({ ...base, connectionString: config.connectionString })\n : new PostgresStore({\n ...base,\n host: config.host!,\n port: config.port,\n database: config.database,\n user: config.user,\n password: config.password,\n });\n\n // Test the connection before committing — if it fails, fall back to LibSQL\n // so the user can fix the config via /settings.\n try {\n await store.init();\n } catch (err: any) {\n const msg = err?.message ?? String(err);\n const target = config.connectionString ?? `${config.host}:${config.port ?? 5432}`;\n try {\n await store.close();\n } catch {\n // ignore cleanup errors\n }\n return {\n storage: createFallbackLibSQL(),\n backend: 'libsql',\n warning:\n `Failed to connect to PostgreSQL at ${target}: ${msg}\\n` +\n 'Using LibSQL fallback. Fix the connection via /settings.',\n };\n }\n\n return { storage: store, backend: 'pg' };\n}\n\n/**\n * Create a vector store for recall search.\n * Uses a separate LibSQL file to avoid bloating the main storage DB with embedding data.\n * For PG backends, reuses the same connection (PG handles the extra tables fine).\n */\nexport async function createVectorStore(\n config: StorageConfig,\n effectiveBackend: 'libsql' | 'pg' = config.backend,\n): Promise<MastraVector | undefined> {\n if (effectiveBackend === 'pg') {\n // PG can handle vector tables in the same database\n const pgConfig = config as PgStorageConfig;\n if (!pgConfig.connectionString && !pgConfig.host) return undefined;\n\n const { PgVector } = await import('@mastra/pg');\n return new PgVector({\n id: 'mastra-code-vectors',\n connectionString:\n pgConfig.connectionString ??\n `postgresql://${pgConfig.user}:${pgConfig.password}@${pgConfig.host}:${pgConfig.port ?? 5432}/${pgConfig.database}`,\n });\n }\n\n // LibSQL: separate file for vectors. Per-tenant configs supply an explicit\n // vectorUrl so each tenant's recall vectors are isolated; otherwise use the\n // shared default file.\n const libsqlConfig = config as { vectorUrl?: string; vectorAuthToken?: string };\n return buildLibSQLVector({\n url: libsqlConfig.vectorUrl ?? `file:${getVectorDatabasePath()}`,\n authToken: libsqlConfig.vectorAuthToken,\n });\n}\n"],"mappings":";;;;;AAgBA,MAAa,4BAA4B;CACvC,WAAW;CACX,UAAU;AACZ;;;;;AAMA,SAAgB,iBAAiB,MAKR;CACvB,OAAO,IAAI,YAAY;EACrB,IAAI,KAAK,MAAM;EACf,KAAK,KAAK;EACV,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;EACtD,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;EACtD,cAAc;CAChB,CAAC;AACH;;AAGA,SAAgB,kBAAkB,MAAsE;CACtG,OAAO,IAAI,aAAa;EACtB,IAAI,KAAK,MAAM;EACf,KAAK,KAAK;EACV,GAAI,KAAK,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;CACxD,CAAC;AACH;AAUA,SAAS,uBAA6C;CACpD,OAAO,iBAAiB;EAAE,KAAK,QAAQ,gBAAgB;EAAK,WAAW;CAAkB,CAAC;AAC5F;;;;;;;AAQA,eAAsB,cAAc,QAA+C;CACjF,IAAI,OAAO,YAAY,MACrB,OAAO,gBAAgB,MAAM;CAI/B,OAAO;EACL,SAAS,iBAAiB;GAAE,KAAK,OAAO;GAAK,WAAW,OAAO;GAAW,WAAW;EAAkB,CAAC;EACxG,SAAS;CACX;AACF;AAEA,eAAe,gBAAgB,QAAiD;CAE9E,IAAI,CAAC,OAAO,oBAAoB,CAAC,OAAO,MACtC,OAAO;EACL,SAAS,qBAAqB;EAC9B,SAAS;EACT,SACE;CAEJ;CAGF,MAAM,OAAO;EACX,IAAI;EACJ,WAAW;EACX,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;EAC7D,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;EAChE,GAAI,OAAO,qBAAqB,EAAE,oBAAoB,OAAO,mBAAmB,IAAI,CAAC;CACvF;CAEA,MAAM,QAAQ,OAAO,mBACjB,IAAI,cAAc;EAAE,GAAG;EAAM,kBAAkB,OAAO;CAAiB,CAAC,IACxE,IAAI,cAAc;EAChB,GAAG;EACH,MAAM,OAAO;EACb,MAAM,OAAO;EACb,UAAU,OAAO;EACjB,MAAM,OAAO;EACb,UAAU,OAAO;CACnB,CAAC;CAIL,IAAI;EACF,MAAM,MAAM,KAAK;CACnB,SAAS,KAAU;EACjB,MAAM,MAAM,KAAK,WAAW,OAAO,GAAG;EACtC,MAAM,SAAS,OAAO,oBAAoB,GAAG,OAAO,KAAK,GAAG,OAAO,QAAQ;EAC3E,IAAI;GACF,MAAM,MAAM,MAAM;EACpB,QAAQ,CAER;EACA,OAAO;GACL,SAAS,qBAAqB;GAC9B,SAAS;GACT,SACE,sCAAsC,OAAO,IAAI,IAAI;EAEzD;CACF;CAEA,OAAO;EAAE,SAAS;EAAO,SAAS;CAAK;AACzC;;;;;;AAOA,eAAsB,kBACpB,QACA,mBAAoC,OAAO,SACR;CACnC,IAAI,qBAAqB,MAAM;EAE7B,MAAM,WAAW;EACjB,IAAI,CAAC,SAAS,oBAAoB,CAAC,SAAS,MAAM,OAAO,KAAA;EAEzD,MAAM,EAAE,aAAa,MAAM,OAAO;EAClC,OAAO,IAAI,SAAS;GAClB,IAAI;GACJ,kBACE,SAAS,oBACT,gBAAgB,SAAS,KAAK,GAAG,SAAS,SAAS,GAAG,SAAS,KAAK,GAAG,SAAS,QAAQ,KAAK,GAAG,SAAS;EAC7G,CAAC;CACH;CAKA,MAAM,eAAe;CACrB,OAAO,kBAAkB;EACvB,KAAK,aAAa,aAAa,QAAQ,sBAAsB;EAC7D,WAAW,aAAa;CAC1B,CAAC;AACH"}