@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,108 +1,112 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
1
  import { getAppDataDir } from "./project.js";
4
- class ThreadLockError extends Error {
5
- constructor(threadId, ownerPid) {
6
- super(`Thread ${threadId} is locked by another process (PID ${ownerPid})`);
7
- this.threadId = threadId;
8
- this.ownerPid = ownerPid;
9
- this.name = "ThreadLockError";
10
- }
11
- threadId;
12
- ownerPid;
13
- }
2
+ import * as fs$1 from "fs";
3
+ import * as path$1 from "path";
4
+ //#region src/utils/thread-lock.ts
5
+ /**
6
+ * Thread lock — ensures only one process writes to a thread at a time.
7
+ *
8
+ * Uses filesystem lock files: <appDataDir>/locks/<threadId>.lock
9
+ * Each lock file contains the PID of the owning process.
10
+ * Stale locks (from crashed processes) are detected and reclaimed.
11
+ */
12
+ var ThreadLockError = class extends Error {
13
+ threadId;
14
+ ownerPid;
15
+ constructor(threadId, ownerPid) {
16
+ super(`Thread ${threadId} is locked by another process (PID ${ownerPid})`);
17
+ this.threadId = threadId;
18
+ this.ownerPid = ownerPid;
19
+ this.name = "ThreadLockError";
20
+ }
21
+ };
14
22
  function getLocksDir() {
15
- const dir = path.join(getAppDataDir(), "locks");
16
- if (!fs.existsSync(dir)) {
17
- fs.mkdirSync(dir, { recursive: true });
18
- }
19
- return dir;
23
+ const dir = path$1.join(getAppDataDir(), "locks");
24
+ if (!fs$1.existsSync(dir)) fs$1.mkdirSync(dir, { recursive: true });
25
+ return dir;
20
26
  }
21
27
  function getLockPath(threadId) {
22
- const safeId = threadId.replace(/[^a-zA-Z0-9_-]/g, "_");
23
- return path.join(getLocksDir(), `${safeId}.lock`);
28
+ const safeId = threadId.replace(/[^a-zA-Z0-9_-]/g, "_");
29
+ return path$1.join(getLocksDir(), `${safeId}.lock`);
24
30
  }
25
31
  function isProcessAlive(pid) {
26
- try {
27
- process.kill(pid, 0);
28
- return true;
29
- } catch {
30
- return false;
31
- }
32
+ try {
33
+ process.kill(pid, 0);
34
+ return true;
35
+ } catch {
36
+ return false;
37
+ }
32
38
  }
39
+ /**
40
+ * Attempt to acquire a lock for the given thread.
41
+ * Throws ThreadLockError if another live process holds the lock.
42
+ * Reclaims stale locks from dead processes.
43
+ */
33
44
  function acquireThreadLock(threadId) {
34
- const lockPath = getLockPath(threadId);
35
- const myPid = process.pid;
36
- if (fs.existsSync(lockPath)) {
37
- try {
38
- const content = fs.readFileSync(lockPath, "utf-8").trim();
39
- const ownerPid = parseInt(content, 10);
40
- if (!isNaN(ownerPid) && ownerPid !== myPid && isProcessAlive(ownerPid)) {
41
- throw new ThreadLockError(threadId, ownerPid);
42
- }
43
- } catch (error) {
44
- if (error instanceof ThreadLockError) throw error;
45
- }
46
- }
47
- fs.writeFileSync(lockPath, String(myPid), { mode: 420 });
45
+ const lockPath = getLockPath(threadId);
46
+ const myPid = process.pid;
47
+ if (fs$1.existsSync(lockPath)) try {
48
+ const content = fs$1.readFileSync(lockPath, "utf-8").trim();
49
+ const ownerPid = parseInt(content, 10);
50
+ if (!isNaN(ownerPid) && ownerPid !== myPid && isProcessAlive(ownerPid)) throw new ThreadLockError(threadId, ownerPid);
51
+ } catch (error) {
52
+ if (error instanceof ThreadLockError) throw error;
53
+ }
54
+ fs$1.writeFileSync(lockPath, String(myPid), { mode: 420 });
48
55
  }
56
+ /**
57
+ * Release the lock for the given thread (only if we own it).
58
+ */
49
59
  function releaseThreadLock(threadId) {
50
- const lockPath = getLockPath(threadId);
51
- const myPid = process.pid;
52
- try {
53
- if (!fs.existsSync(lockPath)) return;
54
- const content = fs.readFileSync(lockPath, "utf-8").trim();
55
- const ownerPid = parseInt(content, 10);
56
- if (ownerPid === myPid) {
57
- fs.unlinkSync(lockPath);
58
- }
59
- } catch {
60
- }
60
+ const lockPath = getLockPath(threadId);
61
+ const myPid = process.pid;
62
+ try {
63
+ if (!fs$1.existsSync(lockPath)) return;
64
+ const content = fs$1.readFileSync(lockPath, "utf-8").trim();
65
+ if (parseInt(content, 10) === myPid) fs$1.unlinkSync(lockPath);
66
+ } catch {}
61
67
  }
68
+ /**
69
+ * Check if a thread is locked by another process.
70
+ * Returns the PID of the owner if locked, null otherwise.
71
+ */
62
72
  function getThreadLockOwner(threadId) {
63
- const lockPath = getLockPath(threadId);
64
- try {
65
- if (!fs.existsSync(lockPath)) return null;
66
- const content = fs.readFileSync(lockPath, "utf-8").trim();
67
- const ownerPid = parseInt(content, 10);
68
- if (isNaN(ownerPid)) return null;
69
- if (ownerPid === process.pid) return null;
70
- if (!isProcessAlive(ownerPid)) {
71
- try {
72
- fs.unlinkSync(lockPath);
73
- } catch {
74
- }
75
- return null;
76
- }
77
- return ownerPid;
78
- } catch {
79
- return null;
80
- }
73
+ const lockPath = getLockPath(threadId);
74
+ try {
75
+ if (!fs$1.existsSync(lockPath)) return null;
76
+ const content = fs$1.readFileSync(lockPath, "utf-8").trim();
77
+ const ownerPid = parseInt(content, 10);
78
+ if (isNaN(ownerPid)) return null;
79
+ if (ownerPid === process.pid) return null;
80
+ if (!isProcessAlive(ownerPid)) {
81
+ try {
82
+ fs$1.unlinkSync(lockPath);
83
+ } catch {}
84
+ return null;
85
+ }
86
+ return ownerPid;
87
+ } catch {
88
+ return null;
89
+ }
81
90
  }
91
+ /**
92
+ * Release all thread locks owned by this process.
93
+ * Call this on process exit.
94
+ */
82
95
  function releaseAllThreadLocks() {
83
- try {
84
- const locksDir = getLocksDir();
85
- const files = fs.readdirSync(locksDir);
86
- const myPid = String(process.pid);
87
- for (const file of files) {
88
- if (!file.endsWith(".lock")) continue;
89
- const lockPath = path.join(locksDir, file);
90
- try {
91
- const content = fs.readFileSync(lockPath, "utf-8").trim();
92
- if (content === myPid) {
93
- fs.unlinkSync(lockPath);
94
- }
95
- } catch {
96
- }
97
- }
98
- } catch {
99
- }
96
+ try {
97
+ const locksDir = getLocksDir();
98
+ const files = fs$1.readdirSync(locksDir);
99
+ const myPid = String(process.pid);
100
+ for (const file of files) {
101
+ if (!file.endsWith(".lock")) continue;
102
+ const lockPath = path$1.join(locksDir, file);
103
+ try {
104
+ if (fs$1.readFileSync(lockPath, "utf-8").trim() === myPid) fs$1.unlinkSync(lockPath);
105
+ } catch {}
106
+ }
107
+ } catch {}
100
108
  }
101
- export {
102
- ThreadLockError,
103
- acquireThreadLock,
104
- getThreadLockOwner,
105
- releaseAllThreadLocks,
106
- releaseThreadLock
107
- };
109
+ //#endregion
110
+ export { ThreadLockError, acquireThreadLock, getThreadLockOwner, releaseAllThreadLocks, releaseThreadLock };
111
+
108
112
  //# sourceMappingURL=thread-lock.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/thread-lock.ts"],"sourcesContent":["/**\n * Thread lock — ensures only one process writes to a thread at a time.\n *\n * Uses filesystem lock files: <appDataDir>/locks/<threadId>.lock\n * Each lock file contains the PID of the owning process.\n * Stale locks (from crashed processes) are detected and reclaimed.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { getAppDataDir } from './project.js';\n\nexport class ThreadLockError extends Error {\n constructor(\n public readonly threadId: string,\n public readonly ownerPid: number,\n ) {\n super(`Thread ${threadId} is locked by another process (PID ${ownerPid})`);\n this.name = 'ThreadLockError';\n }\n}\n\nfunction getLocksDir(): string {\n const dir = path.join(getAppDataDir(), 'locks');\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\nfunction getLockPath(threadId: string): string {\n // Sanitize thread ID for filesystem safety\n const safeId = threadId.replace(/[^a-zA-Z0-9_-]/g, '_');\n return path.join(getLocksDir(), `${safeId}.lock`);\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempt to acquire a lock for the given thread.\n * Throws ThreadLockError if another live process holds the lock.\n * Reclaims stale locks from dead processes.\n */\nexport function acquireThreadLock(threadId: string): void {\n const lockPath = getLockPath(threadId);\n const myPid = process.pid;\n\n // Check for existing lock\n if (fs.existsSync(lockPath)) {\n try {\n const content = fs.readFileSync(lockPath, 'utf-8').trim();\n const ownerPid = parseInt(content, 10);\n\n if (!isNaN(ownerPid) && ownerPid !== myPid && isProcessAlive(ownerPid)) {\n throw new ThreadLockError(threadId, ownerPid);\n }\n // Stale lock (dead process) or our own lock — reclaim it\n } catch (error) {\n if (error instanceof ThreadLockError) throw error;\n // File read error — try to overwrite\n }\n }\n\n // Write our PID to the lock file\n fs.writeFileSync(lockPath, String(myPid), { mode: 0o644 });\n}\n\n/**\n * Release the lock for the given thread (only if we own it).\n */\nexport function releaseThreadLock(threadId: string): void {\n const lockPath = getLockPath(threadId);\n const myPid = process.pid;\n\n try {\n if (!fs.existsSync(lockPath)) return;\n\n const content = fs.readFileSync(lockPath, 'utf-8').trim();\n const ownerPid = parseInt(content, 10);\n\n // Only remove if we own it\n if (ownerPid === myPid) {\n fs.unlinkSync(lockPath);\n }\n } catch {\n // Best-effort cleanup — ignore errors\n }\n}\n\n/**\n * Check if a thread is locked by another process.\n * Returns the PID of the owner if locked, null otherwise.\n */\nexport function getThreadLockOwner(threadId: string): number | null {\n const lockPath = getLockPath(threadId);\n\n try {\n if (!fs.existsSync(lockPath)) return null;\n\n const content = fs.readFileSync(lockPath, 'utf-8').trim();\n const ownerPid = parseInt(content, 10);\n\n if (isNaN(ownerPid)) return null;\n if (ownerPid === process.pid) return null; // Our own lock\n if (!isProcessAlive(ownerPid)) {\n // Stale lock — clean it up\n try {\n fs.unlinkSync(lockPath);\n } catch {}\n return null;\n }\n\n return ownerPid;\n } catch {\n return null;\n }\n}\n\n/**\n * Release all thread locks owned by this process.\n * Call this on process exit.\n */\nexport function releaseAllThreadLocks(): void {\n try {\n const locksDir = getLocksDir();\n const files = fs.readdirSync(locksDir);\n const myPid = String(process.pid);\n\n for (const file of files) {\n if (!file.endsWith('.lock')) continue;\n const lockPath = path.join(locksDir, file);\n try {\n const content = fs.readFileSync(lockPath, 'utf-8').trim();\n if (content === myPid) {\n fs.unlinkSync(lockPath);\n }\n } catch {\n // Best-effort\n }\n }\n } catch {\n // Best-effort\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"file":"thread-lock.js","names":["path","fs"],"sources":["../../src/utils/thread-lock.ts"],"sourcesContent":["/**\n * Thread lock — ensures only one process writes to a thread at a time.\n *\n * Uses filesystem lock files: <appDataDir>/locks/<threadId>.lock\n * Each lock file contains the PID of the owning process.\n * Stale locks (from crashed processes) are detected and reclaimed.\n */\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport { getAppDataDir } from './project.js';\n\nexport class ThreadLockError extends Error {\n constructor(\n public readonly threadId: string,\n public readonly ownerPid: number,\n ) {\n super(`Thread ${threadId} is locked by another process (PID ${ownerPid})`);\n this.name = 'ThreadLockError';\n }\n}\n\nfunction getLocksDir(): string {\n const dir = path.join(getAppDataDir(), 'locks');\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n return dir;\n}\n\nfunction getLockPath(threadId: string): string {\n // Sanitize thread ID for filesystem safety\n const safeId = threadId.replace(/[^a-zA-Z0-9_-]/g, '_');\n return path.join(getLocksDir(), `${safeId}.lock`);\n}\n\nfunction isProcessAlive(pid: number): boolean {\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Attempt to acquire a lock for the given thread.\n * Throws ThreadLockError if another live process holds the lock.\n * Reclaims stale locks from dead processes.\n */\nexport function acquireThreadLock(threadId: string): void {\n const lockPath = getLockPath(threadId);\n const myPid = process.pid;\n\n // Check for existing lock\n if (fs.existsSync(lockPath)) {\n try {\n const content = fs.readFileSync(lockPath, 'utf-8').trim();\n const ownerPid = parseInt(content, 10);\n\n if (!isNaN(ownerPid) && ownerPid !== myPid && isProcessAlive(ownerPid)) {\n throw new ThreadLockError(threadId, ownerPid);\n }\n // Stale lock (dead process) or our own lock — reclaim it\n } catch (error) {\n if (error instanceof ThreadLockError) throw error;\n // File read error — try to overwrite\n }\n }\n\n // Write our PID to the lock file\n fs.writeFileSync(lockPath, String(myPid), { mode: 0o644 });\n}\n\n/**\n * Release the lock for the given thread (only if we own it).\n */\nexport function releaseThreadLock(threadId: string): void {\n const lockPath = getLockPath(threadId);\n const myPid = process.pid;\n\n try {\n if (!fs.existsSync(lockPath)) return;\n\n const content = fs.readFileSync(lockPath, 'utf-8').trim();\n const ownerPid = parseInt(content, 10);\n\n // Only remove if we own it\n if (ownerPid === myPid) {\n fs.unlinkSync(lockPath);\n }\n } catch {\n // Best-effort cleanup — ignore errors\n }\n}\n\n/**\n * Check if a thread is locked by another process.\n * Returns the PID of the owner if locked, null otherwise.\n */\nexport function getThreadLockOwner(threadId: string): number | null {\n const lockPath = getLockPath(threadId);\n\n try {\n if (!fs.existsSync(lockPath)) return null;\n\n const content = fs.readFileSync(lockPath, 'utf-8').trim();\n const ownerPid = parseInt(content, 10);\n\n if (isNaN(ownerPid)) return null;\n if (ownerPid === process.pid) return null; // Our own lock\n if (!isProcessAlive(ownerPid)) {\n // Stale lock — clean it up\n try {\n fs.unlinkSync(lockPath);\n } catch {}\n return null;\n }\n\n return ownerPid;\n } catch {\n return null;\n }\n}\n\n/**\n * Release all thread locks owned by this process.\n * Call this on process exit.\n */\nexport function releaseAllThreadLocks(): void {\n try {\n const locksDir = getLocksDir();\n const files = fs.readdirSync(locksDir);\n const myPid = String(process.pid);\n\n for (const file of files) {\n if (!file.endsWith('.lock')) continue;\n const lockPath = path.join(locksDir, file);\n try {\n const content = fs.readFileSync(lockPath, 'utf-8').trim();\n if (content === myPid) {\n fs.unlinkSync(lockPath);\n }\n } catch {\n // Best-effort\n }\n }\n } catch {\n // Best-effort\n }\n}\n"],"mappings":";;;;;;;;;;;AAWA,IAAa,kBAAb,cAAqC,MAAM;CAEvB;CACA;CAFlB,YACE,UACA,UACA;EACA,MAAM,UAAU,SAAS,qCAAqC,SAAS,EAAE;EAHzD,KAAA,WAAA;EACA,KAAA,WAAA;EAGhB,KAAK,OAAO;CACd;AACF;AAEA,SAAS,cAAsB;CAC7B,MAAM,MAAMA,OAAK,KAAK,cAAc,GAAG,OAAO;CAC9C,IAAI,CAACC,KAAG,WAAW,GAAG,GACpB,KAAG,UAAU,KAAK,EAAE,WAAW,KAAK,CAAC;CAEvC,OAAO;AACT;AAEA,SAAS,YAAY,UAA0B;CAE7C,MAAM,SAAS,SAAS,QAAQ,mBAAmB,GAAG;CACtD,OAAOD,OAAK,KAAK,YAAY,GAAG,GAAG,OAAO,MAAM;AAClD;AAEA,SAAS,eAAe,KAAsB;CAC5C,IAAI;EACF,QAAQ,KAAK,KAAK,CAAC;EACnB,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;;AAOA,SAAgB,kBAAkB,UAAwB;CACxD,MAAM,WAAW,YAAY,QAAQ;CACrC,MAAM,QAAQ,QAAQ;CAGtB,IAAIC,KAAG,WAAW,QAAQ,GACxB,IAAI;EACF,MAAM,UAAUA,KAAG,aAAa,UAAU,OAAO,CAAC,CAAC,KAAK;EACxD,MAAM,WAAW,SAAS,SAAS,EAAE;EAErC,IAAI,CAAC,MAAM,QAAQ,KAAK,aAAa,SAAS,eAAe,QAAQ,GACnE,MAAM,IAAI,gBAAgB,UAAU,QAAQ;CAGhD,SAAS,OAAO;EACd,IAAI,iBAAiB,iBAAiB,MAAM;CAE9C;CAIF,KAAG,cAAc,UAAU,OAAO,KAAK,GAAG,EAAE,MAAM,IAAM,CAAC;AAC3D;;;;AAKA,SAAgB,kBAAkB,UAAwB;CACxD,MAAM,WAAW,YAAY,QAAQ;CACrC,MAAM,QAAQ,QAAQ;CAEtB,IAAI;EACF,IAAI,CAACA,KAAG,WAAW,QAAQ,GAAG;EAE9B,MAAM,UAAUA,KAAG,aAAa,UAAU,OAAO,CAAC,CAAC,KAAK;EAIxD,IAHiB,SAAS,SAAS,EAGxB,MAAM,OACf,KAAG,WAAW,QAAQ;CAE1B,QAAQ,CAER;AACF;;;;;AAMA,SAAgB,mBAAmB,UAAiC;CAClE,MAAM,WAAW,YAAY,QAAQ;CAErC,IAAI;EACF,IAAI,CAACA,KAAG,WAAW,QAAQ,GAAG,OAAO;EAErC,MAAM,UAAUA,KAAG,aAAa,UAAU,OAAO,CAAC,CAAC,KAAK;EACxD,MAAM,WAAW,SAAS,SAAS,EAAE;EAErC,IAAI,MAAM,QAAQ,GAAG,OAAO;EAC5B,IAAI,aAAa,QAAQ,KAAK,OAAO;EACrC,IAAI,CAAC,eAAe,QAAQ,GAAG;GAE7B,IAAI;IACF,KAAG,WAAW,QAAQ;GACxB,QAAQ,CAAC;GACT,OAAO;EACT;EAEA,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;;;;;AAMA,SAAgB,wBAA8B;CAC5C,IAAI;EACF,MAAM,WAAW,YAAY;EAC7B,MAAM,QAAQA,KAAG,YAAY,QAAQ;EACrC,MAAM,QAAQ,OAAO,QAAQ,GAAG;EAEhC,KAAK,MAAM,QAAQ,OAAO;GACxB,IAAI,CAAC,KAAK,SAAS,OAAO,GAAG;GAC7B,MAAM,WAAWD,OAAK,KAAK,UAAU,IAAI;GACzC,IAAI;IAEF,IADgBC,KAAG,aAAa,UAAU,OAAO,CAAC,CAAC,KACzC,MAAM,OACd,KAAG,WAAW,QAAQ;GAE1B,QAAQ,CAER;EACF;CACF,QAAQ,CAER;AACF"}
@@ -1,21 +1,21 @@
1
1
  import { estimateTokenCount, sliceByTokens } from "tokenx";
2
+ //#region src/utils/token-estimator.ts
2
3
  function sanitizeInput(text) {
3
- if (!text) return "";
4
- return (typeof text === `string` ? text : JSON.stringify(text)).replaceAll(`<|endoftext|>`, ``).replaceAll(`<|endofprompt|>`, ``);
4
+ if (!text) return "";
5
+ return (typeof text === `string` ? text : JSON.stringify(text)).replaceAll(`<|endoftext|>`, ``).replaceAll(`<|endofprompt|>`, ``);
5
6
  }
6
7
  function tokenEstimate(text) {
7
- return estimateTokenCount(sanitizeInput(text));
8
+ return estimateTokenCount(sanitizeInput(text));
8
9
  }
9
10
  function truncateStringForTokenEstimate(text, desiredTokenCount, fromEnd = true) {
10
- const sanitized = sanitizeInput(text);
11
- const totalTokens = estimateTokenCount(sanitized);
12
- if (totalTokens <= desiredTokenCount) return sanitized;
13
- const kept = fromEnd ? sliceByTokens(sanitized, -desiredTokenCount) : sliceByTokens(sanitized, 0, desiredTokenCount);
14
- return `[Truncated ~${totalTokens - desiredTokenCount} tokens]
11
+ const sanitized = sanitizeInput(text);
12
+ const totalTokens = estimateTokenCount(sanitized);
13
+ if (totalTokens <= desiredTokenCount) return sanitized;
14
+ const kept = fromEnd ? sliceByTokens(sanitized, -desiredTokenCount) : sliceByTokens(sanitized, 0, desiredTokenCount);
15
+ return `[Truncated ~${totalTokens - desiredTokenCount} tokens]
15
16
  ${kept}`;
16
17
  }
17
- export {
18
- tokenEstimate,
19
- truncateStringForTokenEstimate
20
- };
18
+ //#endregion
19
+ export { tokenEstimate, truncateStringForTokenEstimate };
20
+
21
21
  //# sourceMappingURL=token-estimator.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/token-estimator.ts"],"sourcesContent":["import { estimateTokenCount, sliceByTokens } from 'tokenx';\n\nfunction sanitizeInput(text: string | object) {\n if (!text) return '';\n return (typeof text === `string` ? text : JSON.stringify(text))\n .replaceAll(`<|endoftext|>`, ``)\n .replaceAll(`<|endofprompt|>`, ``);\n}\n\nexport function tokenEstimate(text: string | object): number {\n return estimateTokenCount(sanitizeInput(text));\n}\n\nexport function truncateStringForTokenEstimate(text: string, desiredTokenCount: number, fromEnd = true) {\n const sanitized = sanitizeInput(text);\n const totalTokens = estimateTokenCount(sanitized);\n\n if (totalTokens <= desiredTokenCount) return sanitized;\n\n const kept = fromEnd ? sliceByTokens(sanitized, -desiredTokenCount) : sliceByTokens(sanitized, 0, desiredTokenCount);\n\n return `[Truncated ~${totalTokens - desiredTokenCount} tokens]\n${kept}`;\n}\n"],"mappings":"AAAA,SAAS,oBAAoB,qBAAqB;AAElD,SAAS,cAAc,MAAuB;AAC5C,MAAI,CAAC,KAAM,QAAO;AAClB,UAAQ,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI,GAC1D,WAAW,iBAAiB,EAAE,EAC9B,WAAW,mBAAmB,EAAE;AACrC;AAEO,SAAS,cAAc,MAA+B;AAC3D,SAAO,mBAAmB,cAAc,IAAI,CAAC;AAC/C;AAEO,SAAS,+BAA+B,MAAc,mBAA2B,UAAU,MAAM;AACtG,QAAM,YAAY,cAAc,IAAI;AACpC,QAAM,cAAc,mBAAmB,SAAS;AAEhD,MAAI,eAAe,kBAAmB,QAAO;AAE7C,QAAM,OAAO,UAAU,cAAc,WAAW,CAAC,iBAAiB,IAAI,cAAc,WAAW,GAAG,iBAAiB;AAEnH,SAAO,eAAe,cAAc,iBAAiB;AAAA,EACrD,IAAI;AACN;","names":[]}
1
+ {"version":3,"file":"token-estimator.js","names":[],"sources":["../../src/utils/token-estimator.ts"],"sourcesContent":["import { estimateTokenCount, sliceByTokens } from 'tokenx';\n\nfunction sanitizeInput(text: string | object) {\n if (!text) return '';\n return (typeof text === `string` ? text : JSON.stringify(text))\n .replaceAll(`<|endoftext|>`, ``)\n .replaceAll(`<|endofprompt|>`, ``);\n}\n\nexport function tokenEstimate(text: string | object): number {\n return estimateTokenCount(sanitizeInput(text));\n}\n\nexport function truncateStringForTokenEstimate(text: string, desiredTokenCount: number, fromEnd = true) {\n const sanitized = sanitizeInput(text);\n const totalTokens = estimateTokenCount(sanitized);\n\n if (totalTokens <= desiredTokenCount) return sanitized;\n\n const kept = fromEnd ? sliceByTokens(sanitized, -desiredTokenCount) : sliceByTokens(sanitized, 0, desiredTokenCount);\n\n return `[Truncated ~${totalTokens - desiredTokenCount} tokens]\n${kept}`;\n}\n"],"mappings":";;AAEA,SAAS,cAAc,MAAuB;CAC5C,IAAI,CAAC,MAAM,OAAO;CAClB,QAAQ,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI,EAAA,CAC1D,WAAW,iBAAiB,EAAE,CAAC,CAC/B,WAAW,mBAAmB,EAAE;AACrC;AAEA,SAAgB,cAAc,MAA+B;CAC3D,OAAO,mBAAmB,cAAc,IAAI,CAAC;AAC/C;AAEA,SAAgB,+BAA+B,MAAc,mBAA2B,UAAU,MAAM;CACtG,MAAM,YAAY,cAAc,IAAI;CACpC,MAAM,cAAc,mBAAmB,SAAS;CAEhD,IAAI,eAAe,mBAAmB,OAAO;CAE7C,MAAM,OAAO,UAAU,cAAc,WAAW,CAAC,iBAAiB,IAAI,cAAc,WAAW,GAAG,iBAAiB;CAEnH,OAAO,eAAe,cAAc,kBAAkB;EACtD;AACF"}
@@ -19,7 +19,7 @@ export declare function detectPackageManager(): Promise<PackageManager>;
19
19
  */
20
20
  export declare function getInstallCommand(pm: PackageManager, version?: string): string;
21
21
  /**
22
- * Read the current version, injected at build time by tsup's `define` option.
22
+ * Read the current version, injected at build time by tsdown's `define` option.
23
23
  * Falls back to reading package.json at runtime (e.g. when running from source with tsx).
24
24
  */
25
25
  export declare function getCurrentVersion(): string;