@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.
- package/CHANGELOG.md +49 -0
- package/dist/acp/agent.js +155 -167
- package/dist/acp/agent.js.map +1 -1
- package/dist/acp/event-mapper.js +193 -168
- package/dist/acp/event-mapper.js.map +1 -1
- package/dist/acp/index.js +60 -49
- package/dist/acp/index.js.map +1 -1
- package/dist/acp/server.js +31 -30
- package/dist/acp/server.js.map +1 -1
- package/dist/acp.js +1 -4
- package/dist/agents/credential-resolver.js +40 -29
- package/dist/agents/credential-resolver.js.map +1 -1
- package/dist/agents/custom-provider-source.js +16 -9
- package/dist/agents/custom-provider-source.js.map +1 -1
- package/dist/agents/instructions.js +27 -36
- package/dist/agents/instructions.js.map +1 -1
- package/dist/agents/mastracode-gateway.js +355 -428
- package/dist/agents/mastracode-gateway.js.map +1 -1
- package/dist/agents/memory.js +87 -78
- package/dist/agents/memory.js.map +1 -1
- package/dist/agents/model.js +114 -119
- package/dist/agents/model.js.map +1 -1
- package/dist/agents/modes/build.js +12 -13
- package/dist/agents/modes/build.js.map +1 -1
- package/dist/agents/modes/explore.js +14 -13
- package/dist/agents/modes/explore.js.map +1 -1
- package/dist/agents/modes/plan.js +14 -15
- package/dist/agents/modes/plan.js.map +1 -1
- package/dist/agents/prompts/agent-instructions.js +78 -69
- package/dist/agents/prompts/agent-instructions.js.map +1 -1
- package/dist/agents/prompts/build.js +19 -16
- package/dist/agents/prompts/build.js.map +1 -1
- package/dist/agents/prompts/fast.js +8 -4
- package/dist/agents/prompts/fast.js.map +1 -1
- package/dist/agents/prompts/index.js +48 -47
- package/dist/agents/prompts/index.js.map +1 -1
- package/dist/agents/prompts/model.js +6 -5
- package/dist/agents/prompts/model.js.map +1 -1
- package/dist/agents/prompts/plan.js +20 -16
- package/dist/agents/prompts/plan.js.map +1 -1
- package/dist/agents/prompts/tool-guidance.js +77 -109
- package/dist/agents/prompts/tool-guidance.js.map +1 -1
- package/dist/agents/sandbox-filesystem.js +245 -259
- package/dist/agents/sandbox-filesystem.js.map +1 -1
- package/dist/agents/sandbox-reattach.js +9 -11
- package/dist/agents/sandbox-reattach.js.map +1 -1
- package/dist/agents/thread-caveman-state.js +92 -81
- package/dist/agents/thread-caveman-state.js.map +1 -1
- package/dist/agents/tool-availability.js +62 -80
- package/dist/agents/tool-availability.js.map +1 -1
- package/dist/agents/tools.js +99 -139
- package/dist/agents/tools.js.map +1 -1
- package/dist/agents/workspace.js +232 -225
- package/dist/agents/workspace.js.map +1 -1
- package/dist/analytics.js +139 -128
- package/dist/analytics.js.map +1 -1
- package/dist/auth/authorization-input.js +39 -25
- package/dist/auth/authorization-input.js.map +1 -1
- package/dist/auth/device-code.js +112 -80
- package/dist/auth/device-code.js.map +1 -1
- package/dist/auth/index.js +2 -9
- package/dist/auth/pkce.js +27 -16
- package/dist/auth/pkce.js.map +1 -1
- package/dist/auth/providers/anthropic.js +107 -93
- package/dist/auth/providers/anthropic.js.map +1 -1
- package/dist/auth/providers/github-copilot.js +304 -293
- package/dist/auth/providers/github-copilot.js.map +1 -1
- package/dist/auth/providers/openai-codex.js +442 -460
- package/dist/auth/providers/openai-codex.js.map +1 -1
- package/dist/auth/providers/xai.js +179 -145
- package/dist/auth/providers/xai.js.map +1 -1
- package/dist/auth/storage.js +182 -175
- package/dist/auth/storage.js.map +1 -1
- package/dist/auth/types.js +1 -1
- package/dist/clipboard/index.js +206 -172
- package/dist/clipboard/index.js.map +1 -1
- package/dist/constants.js +11 -16
- package/dist/constants.js.map +1 -1
- package/dist/error-classification.js +19 -11
- package/dist/error-classification.js.map +1 -1
- package/dist/evals/context-builder.js +145 -104
- package/dist/evals/context-builder.js.map +1 -1
- package/dist/evals/index.js +4 -7
- package/dist/evals/scorers/classify-command.js +62 -41
- package/dist/evals/scorers/classify-command.js.map +1 -1
- package/dist/evals/scorers/efficiency.js +239 -181
- package/dist/evals/scorers/efficiency.js.map +1 -1
- package/dist/evals/scorers/extract-tools.js +51 -47
- package/dist/evals/scorers/extract-tools.js.map +1 -1
- package/dist/evals/scorers/index.js +1 -5
- package/dist/evals/scorers/outcome.js +321 -212
- package/dist/evals/scorers/outcome.js.map +1 -1
- package/dist/goal-manager.js +266 -261
- package/dist/goal-manager.js.map +1 -1
- package/dist/goal-signal.js +14 -13
- package/dist/goal-signal.js.map +1 -1
- package/dist/headless/cli.js +153 -154
- package/dist/headless/cli.js.map +1 -1
- package/dist/headless/flags.js +180 -178
- package/dist/headless/flags.js.map +1 -1
- package/dist/headless/format.js +72 -71
- package/dist/headless/format.js.map +1 -1
- package/dist/headless/index.js +5 -31
- package/dist/headless/policy.js +35 -31
- package/dist/headless/policy.js.map +1 -1
- package/dist/headless/run-mc.js +332 -334
- package/dist/headless/run-mc.js.map +1 -1
- package/dist/headless/types.js +16 -7
- package/dist/headless/types.js.map +1 -1
- package/dist/hooks/config.js +56 -61
- package/dist/hooks/config.js.map +1 -1
- package/dist/hooks/executor.js +118 -116
- package/dist/hooks/executor.js.map +1 -1
- package/dist/hooks/index.js +4 -15
- package/dist/hooks/manager.js +221 -280
- package/dist/hooks/manager.js.map +1 -1
- package/dist/hooks/types.js +5 -4
- package/dist/hooks/types.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +632 -712
- package/dist/index.js.map +1 -1
- package/dist/ipc/ipc-reporter.js +19 -22
- package/dist/ipc/ipc-reporter.js.map +1 -1
- package/dist/lsp/client.js +248 -306
- package/dist/lsp/client.js.map +1 -1
- package/dist/lsp/index.js +1 -6
- package/dist/lsp/language.js +39 -43
- package/dist/lsp/language.js.map +1 -1
- package/dist/lsp/manager.js +78 -93
- package/dist/lsp/manager.js.map +1 -1
- package/dist/lsp/server.js +151 -120
- package/dist/lsp/server.js.map +1 -1
- package/dist/lsp/workspace.js +50 -47
- package/dist/lsp/workspace.js.map +1 -1
- package/dist/mcp/config.js +210 -199
- package/dist/mcp/config.js.map +1 -1
- package/dist/mcp/index.js +2 -9
- package/dist/mcp/manager.js +482 -475
- package/dist/mcp/manager.js.map +1 -1
- package/dist/mcp/types.js +1 -1
- package/dist/onboarding/custom-packs.js +14 -17
- package/dist/onboarding/custom-packs.js.map +1 -1
- package/dist/onboarding/custom-providers.js +22 -20
- package/dist/onboarding/custom-providers.js.map +1 -1
- package/dist/onboarding/index.d.ts +1 -1
- package/dist/onboarding/index.d.ts.map +1 -1
- package/dist/onboarding/index.js +3 -21
- package/dist/onboarding/om-settings.d.ts +5 -0
- package/dist/onboarding/om-settings.d.ts.map +1 -1
- package/dist/onboarding/om-settings.js +41 -20
- package/dist/onboarding/om-settings.js.map +1 -1
- package/dist/onboarding/packs.d.ts +4 -6
- package/dist/onboarding/packs.d.ts.map +1 -1
- package/dist/onboarding/packs.js +154 -105
- package/dist/onboarding/packs.js.map +1 -1
- package/dist/onboarding/settings.js +575 -517
- package/dist/onboarding/settings.js.map +1 -1
- package/dist/permissions.js +112 -99
- package/dist/permissions.js.map +1 -1
- package/dist/plugin.js +21 -23
- package/dist/plugin.js.map +1 -1
- package/dist/plugins/dependencies.js +64 -87
- package/dist/plugins/dependencies.js.map +1 -1
- package/dist/plugins/install.js +159 -169
- package/dist/plugins/install.js.map +1 -1
- package/dist/plugins/loader.js +170 -185
- package/dist/plugins/loader.js.map +1 -1
- package/dist/plugins/manager.js +374 -380
- package/dist/plugins/manager.js.map +1 -1
- package/dist/plugins/manifest.js +38 -63
- package/dist/plugins/manifest.js.map +1 -1
- package/dist/plugins/package-link.js +48 -64
- package/dist/plugins/package-link.js.map +1 -1
- package/dist/plugins/paths.js +18 -19
- package/dist/plugins/paths.js.map +1 -1
- package/dist/plugins/registry.js +97 -81
- package/dist/plugins/registry.js.map +1 -1
- package/dist/plugins/scaffold.js +62 -98
- package/dist/plugins/scaffold.js.map +1 -1
- package/dist/plugins/types.js +1 -1
- package/dist/processors/plan-rejection-abort.js +45 -44
- package/dist/processors/plan-rejection-abort.js.map +1 -1
- package/dist/providers/amazon-bedrock-gateway.js +134 -125
- package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
- package/dist/providers/amazon-bedrock.js +68 -47
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/claude-max.js +153 -133
- package/dist/providers/claude-max.js.map +1 -1
- package/dist/providers/github-copilot.js +232 -230
- package/dist/providers/github-copilot.js.map +1 -1
- package/dist/providers/openai-codex.js +271 -260
- package/dist/providers/openai-codex.js.map +1 -1
- package/dist/providers/xai.js +57 -50
- package/dist/providers/xai.js.map +1 -1
- package/dist/schema.js +91 -89
- package/dist/schema.js.map +1 -1
- package/dist/theme-palette.js +18 -14
- package/dist/theme-palette.js.map +1 -1
- package/dist/tool-names.js +46 -37
- package/dist/tool-names.js.map +1 -1
- package/dist/tools/index.js +2 -8
- package/dist/tools/request-sandbox-access.js +71 -80
- package/dist/tools/request-sandbox-access.js.map +1 -1
- package/dist/tools/utils.js +26 -16
- package/dist/tools/utils.js.map +1 -1
- package/dist/tools/web-search.js +51 -55
- package/dist/tools/web-search.js.map +1 -1
- package/dist/utils/binaries.js +65 -61
- package/dist/utils/binaries.js.map +1 -1
- package/dist/utils/debug-log.js +51 -47
- package/dist/utils/debug-log.js.map +1 -1
- package/dist/utils/errors.js +157 -192
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/gateway-sync.js +49 -37
- package/dist/utils/gateway-sync.js.map +1 -1
- package/dist/utils/path-security.js +7 -6
- package/dist/utils/path-security.js.map +1 -1
- package/dist/utils/plan-diff.js +58 -39
- package/dist/utils/plan-diff.js.map +1 -1
- package/dist/utils/plans.js +92 -74
- package/dist/utils/plans.js.map +1 -1
- package/dist/utils/project.js +318 -256
- package/dist/utils/project.js.map +1 -1
- package/dist/utils/signals-pubsub.js +117 -99
- package/dist/utils/signals-pubsub.js.map +1 -1
- package/dist/utils/slash-command-loader.js +118 -154
- package/dist/utils/slash-command-loader.js.map +1 -1
- package/dist/utils/slash-command-processor.js +92 -83
- package/dist/utils/slash-command-processor.js.map +1 -1
- package/dist/utils/stdin-pipe.js +87 -63
- package/dist/utils/stdin-pipe.js.map +1 -1
- package/dist/utils/storage-factory.js +104 -84
- package/dist/utils/storage-factory.js.map +1 -1
- package/dist/utils/storage-maintenance.js +249 -211
- package/dist/utils/storage-maintenance.js.map +1 -1
- package/dist/utils/thread-lock.js +96 -92
- package/dist/utils/thread-lock.js.map +1 -1
- package/dist/utils/token-estimator.js +12 -12
- package/dist/utils/token-estimator.js.map +1 -1
- package/dist/utils/update-check.d.ts +1 -1
- package/dist/utils/update-check.js +341 -259
- package/dist/utils/update-check.js.map +1 -1
- package/dist/voice/stt-registry.js +131 -113
- package/dist/voice/stt-registry.js.map +1 -1
- package/package.json +11 -11
- package/dist/acp.js.map +0 -1
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/types.js.map +0 -1
- package/dist/evals/index.js.map +0 -1
- package/dist/evals/scorers/index.js.map +0 -1
- package/dist/headless/index.js.map +0 -1
- package/dist/hooks/index.js.map +0 -1
- package/dist/lsp/index.js.map +0 -1
- package/dist/mcp/index.js.map +0 -1
- package/dist/mcp/types.js.map +0 -1
- package/dist/onboarding/index.js.map +0 -1
- package/dist/plugins/types.js.map +0 -1
- 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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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":"
|
|
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
|
-
|
|
4
|
-
|
|
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
|
-
|
|
8
|
+
return estimateTokenCount(sanitizeInput(text));
|
|
8
9
|
}
|
|
9
10
|
function truncateStringForTokenEstimate(text, desiredTokenCount, fromEnd = true) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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":"
|
|
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
|
|
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;
|