oh-my-opencode 4.18.0 → 4.18.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 (226) hide show
  1. package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +3677 -0
  2. package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +3154 -0
  3. package/.agents/skills/work-with-pr/SKILL.md +16 -37
  4. package/.agents/skills/work-with-pr-workspace/evals/evals.json +3 -3
  5. package/.opencode/skills/work-with-pr/SKILL.md +16 -37
  6. package/.opencode/skills/work-with-pr-workspace/evals/evals.json +3 -3
  7. package/bin/AGENTS.md +33 -0
  8. package/dist/cli/index.js +500 -158
  9. package/dist/cli-node/index.js +500 -158
  10. package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
  11. package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
  12. package/dist/hooks/comment-checker/hook.d.ts +8 -1
  13. package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
  14. package/dist/index.js +956 -702
  15. package/dist/plugin/messages-transform.d.ts +1 -0
  16. package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
  17. package/dist/tui.js +43 -4
  18. package/package.json +16 -16
  19. package/packages/git-bash-mcp/dist/cli.js +81 -19
  20. package/packages/lsp-core/package.json +4 -0
  21. package/packages/lsp-core/src/index.ts +1 -0
  22. package/packages/lsp-core/src/lsp/cleanup-errors.test.ts +18 -0
  23. package/packages/lsp-core/src/lsp/cleanup-errors.ts +12 -3
  24. package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +261 -0
  25. package/packages/lsp-core/src/lsp/client-wrapper.test.ts +63 -0
  26. package/packages/lsp-core/src/lsp/client-wrapper.ts +35 -5
  27. package/packages/lsp-core/src/lsp/client.ts +262 -80
  28. package/packages/lsp-core/src/lsp/config-loader.ts +5 -17
  29. package/packages/lsp-core/src/lsp/connection.ts +12 -6
  30. package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +221 -0
  31. package/packages/lsp-core/src/lsp/directory-diagnostics.ts +61 -28
  32. package/packages/lsp-core/src/lsp/errors.ts +11 -0
  33. package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +283 -0
  34. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-contract-probe.ts +196 -0
  35. package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +215 -0
  36. package/packages/lsp-core/src/lsp/formatters.ts +3 -0
  37. package/packages/lsp-core/src/lsp/json-rpc-connection-cancellation.test.ts +97 -0
  38. package/packages/lsp-core/src/lsp/json-rpc-connection.ts +73 -5
  39. package/packages/lsp-core/src/lsp/server-install-state.ts +3 -6
  40. package/packages/lsp-core/src/lsp/transport-protocol.ts +52 -0
  41. package/packages/lsp-core/src/lsp/transport.ts +96 -70
  42. package/packages/lsp-core/src/lsp/workspace-apply-edit-failure.ts +19 -0
  43. package/packages/lsp-core/src/lsp/workspace-apply-edit-lease.integration.test.ts +214 -0
  44. package/packages/lsp-core/src/lsp/workspace-apply-edit-sync.integration.test.ts +113 -0
  45. package/packages/lsp-core/src/lsp/workspace-apply-edit-test-support.ts +163 -0
  46. package/packages/lsp-core/src/lsp/workspace-apply-edit.integration.test.ts +163 -0
  47. package/packages/lsp-core/src/lsp/workspace-document-state.test.ts +67 -0
  48. package/packages/lsp-core/src/lsp/workspace-document-state.ts +368 -0
  49. package/packages/lsp-core/src/lsp/workspace-edit-adversarial.test.ts +113 -0
  50. package/packages/lsp-core/src/lsp/workspace-edit-commit.test.ts +140 -0
  51. package/packages/lsp-core/src/lsp/workspace-edit-commit.ts +220 -0
  52. package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.test.ts +56 -0
  53. package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.ts +30 -0
  54. package/packages/lsp-core/src/lsp/workspace-edit-fingerprint.ts +44 -0
  55. package/packages/lsp-core/src/lsp/workspace-edit-options.test.ts +147 -0
  56. package/packages/lsp-core/src/lsp/workspace-edit-parse-helpers.ts +59 -0
  57. package/packages/lsp-core/src/lsp/workspace-edit-parser.ts +130 -0
  58. package/packages/lsp-core/src/lsp/workspace-edit-path.ts +98 -0
  59. package/packages/lsp-core/src/lsp/workspace-edit-plan-types.ts +60 -0
  60. package/packages/lsp-core/src/lsp/workspace-edit-plan.ts +73 -0
  61. package/packages/lsp-core/src/lsp/workspace-edit-prevalidation.test.ts +174 -0
  62. package/packages/lsp-core/src/lsp/workspace-edit-resource-parser.ts +89 -0
  63. package/packages/lsp-core/src/lsp/workspace-edit-simulation.ts +183 -0
  64. package/packages/lsp-core/src/lsp/workspace-edit-snapshot.ts +53 -0
  65. package/packages/lsp-core/src/lsp/workspace-edit-text.ts +125 -0
  66. package/packages/lsp-core/src/lsp/workspace-edit-types.ts +121 -0
  67. package/packages/lsp-core/src/lsp/workspace-edit.characterization.test.ts +95 -0
  68. package/packages/lsp-core/src/lsp/workspace-edit.ts +49 -200
  69. package/packages/lsp-core/src/lsp/workspace-mutation-controller.ts +182 -0
  70. package/packages/lsp-core/src/mcp.ts +18 -7
  71. package/packages/lsp-core/src/missing-dependency-result.test.ts +105 -0
  72. package/packages/lsp-core/src/missing-dependency-result.ts +57 -0
  73. package/packages/lsp-core/src/post-edit/index.ts +1 -0
  74. package/packages/lsp-core/src/post-edit/orchestration.test.ts +157 -0
  75. package/packages/lsp-core/src/post-edit/orchestration.ts +178 -0
  76. package/packages/lsp-core/src/request-context.test.ts +171 -0
  77. package/packages/lsp-core/src/request-context.ts +222 -9
  78. package/packages/lsp-core/src/tool-surface.test.ts +4 -1
  79. package/packages/lsp-core/src/tools/diagnostics.ts +32 -13
  80. package/packages/lsp-core/src/tools/navigation.ts +12 -12
  81. package/packages/lsp-core/src/tools/rename.ts +10 -15
  82. package/packages/lsp-core/src/tools/symbols.ts +11 -11
  83. package/packages/lsp-core/src/tools/types.ts +2 -1
  84. package/packages/lsp-daemon/dist/cli.js +3330 -764
  85. package/packages/lsp-daemon/dist/client.d.ts +105 -0
  86. package/packages/lsp-daemon/dist/client.js +5995 -0
  87. package/packages/lsp-daemon/dist/daemon-client.d.ts +12 -7
  88. package/packages/lsp-daemon/dist/daemon-client.js +139 -32
  89. package/packages/lsp-daemon/dist/daemon-server.d.ts +1 -0
  90. package/packages/lsp-daemon/dist/daemon-server.js +40 -15
  91. package/packages/lsp-daemon/dist/ensure-daemon.d.ts +10 -8
  92. package/packages/lsp-daemon/dist/ensure-daemon.js +135 -51
  93. package/packages/lsp-daemon/dist/index.d.ts +2 -2
  94. package/packages/lsp-daemon/dist/index.js +3093 -786
  95. package/packages/lsp-daemon/dist/ipc-protocol.d.ts +46 -0
  96. package/packages/lsp-daemon/dist/ipc-protocol.js +187 -0
  97. package/packages/lsp-daemon/dist/lock.js +14 -4
  98. package/packages/lsp-daemon/dist/ownership.d.ts +49 -0
  99. package/packages/lsp-daemon/dist/ownership.js +168 -0
  100. package/packages/lsp-daemon/dist/paths.d.ts +33 -9
  101. package/packages/lsp-daemon/dist/paths.js +72 -33
  102. package/packages/lsp-daemon/dist/proxy.d.ts +5 -0
  103. package/packages/lsp-daemon/dist/proxy.js +123 -16
  104. package/packages/lsp-daemon/dist/request-routing.d.ts +7 -2
  105. package/packages/lsp-daemon/dist/request-routing.js +71 -22
  106. package/packages/lsp-daemon/dist/run-daemon.js +9 -2
  107. package/packages/lsp-daemon/dist/runtime-contract.d.ts +21 -0
  108. package/packages/lsp-daemon/dist/runtime-contract.js +58 -0
  109. package/packages/lsp-daemon/dist/socket-jsonrpc.js +6 -1
  110. package/packages/lsp-daemon/package.json +12 -3
  111. package/packages/lsp-tools-mcp/dist/cli.js +2189 -454
  112. package/packages/lsp-tools-mcp/dist/lsp/manager.js +1741 -148
  113. package/packages/lsp-tools-mcp/dist/mcp.js +2206 -471
  114. package/packages/lsp-tools-mcp/dist/request-context.js +176 -6
  115. package/packages/lsp-tools-mcp/dist/tools.js +2119 -447
  116. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  117. package/packages/omo-codex/plugin/.mcp.json +2 -1
  118. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  119. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  120. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +100 -28
  121. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
  122. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  123. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
  124. package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
  125. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
  126. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
  127. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  128. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  129. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  130. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  131. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  132. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  133. package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
  134. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +55 -0
  135. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +3033 -936
  136. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook-cli.js +0 -4
  137. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.d.ts +5 -2
  138. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +41 -62
  139. package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.d.ts +1 -1
  140. package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.js +24 -15
  141. package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.d.ts +3 -7
  142. package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.js +23 -49
  143. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  144. package/packages/omo-codex/plugin/components/lsp/package.json +3 -2
  145. package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-daemon.mjs +31 -1
  146. package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-daemon.test.mjs +76 -0
  147. package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.mjs +201 -0
  148. package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.test.mjs +55 -0
  149. package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +0 -4
  150. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +49 -71
  151. package/packages/omo-codex/plugin/components/lsp/src/daemon-cli-path.ts +26 -15
  152. package/packages/omo-codex/plugin/components/lsp/src/lsp-session-state.ts +26 -64
  153. package/packages/omo-codex/plugin/components/lsp/test/codex-hook-unavailable.test.ts +16 -17
  154. package/packages/omo-codex/plugin/components/lsp/test/codex-hook.test.ts +30 -4
  155. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +20 -5
  156. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
  157. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
  158. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  159. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  160. package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
  161. package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
  162. package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +2 -2
  163. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  164. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  165. package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +1 -1
  166. package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
  167. package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +15 -1
  168. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +20 -0
  169. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  170. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  171. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  172. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  173. package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
  174. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  175. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  176. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +50 -33
  177. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
  178. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
  179. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
  180. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  181. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  182. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
  183. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +6 -5
  184. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
  185. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
  186. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  187. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  188. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  189. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  190. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  191. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  192. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  193. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  194. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  195. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  196. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  197. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  198. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  199. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  200. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  201. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  202. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  203. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  204. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  205. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  206. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  207. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  208. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  209. package/packages/omo-codex/plugin/package-lock.json +26 -14
  210. package/packages/omo-codex/plugin/package.json +1 -1
  211. package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +2 -3
  212. package/packages/omo-codex/plugin/scripts/build-components.mjs +13 -1
  213. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +9 -1
  214. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
  215. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +2 -1
  216. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
  217. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
  218. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +6 -5
  219. package/packages/omo-codex/plugin/test/aggregate-build.test.mjs +8 -0
  220. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +128 -15
  221. package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +10 -0
  222. package/packages/omo-codex/plugin/test/lsp-prebuild-layouts.test.mjs +2 -0
  223. package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
  224. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
  225. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +34 -3
  226. package/packages/omo-codex/scripts/install-dist/install-local.mjs +328 -63
@@ -0,0 +1,46 @@
1
+ import { type Stats } from "node:fs";
2
+ import type { DaemonPaths } from "./paths.js";
3
+ export declare const OMO_DAEMON_PROTOCOL_VERSION = 1;
4
+ export declare const AUTH_ERROR_CODE = -32001;
5
+ export declare const PROTOCOL_ERROR_CODE = -32002;
6
+ export type DaemonAuthEnvelope = {
7
+ readonly protocolVersion: typeof OMO_DAEMON_PROTOCOL_VERSION;
8
+ readonly token: string;
9
+ };
10
+ export type JsonRpcErrorResponse = {
11
+ readonly jsonrpc: "2.0";
12
+ readonly id: string | number | null;
13
+ readonly error: {
14
+ readonly code: number;
15
+ readonly message: string;
16
+ readonly data: {
17
+ readonly code: string;
18
+ };
19
+ };
20
+ };
21
+ export type AuthenticatedMessage = {
22
+ readonly input: Record<string, unknown>;
23
+ readonly id: string | number | null;
24
+ readonly method: string | undefined;
25
+ };
26
+ export type PrivateDirectoryStats = Pick<Stats, "dev" | "ino" | "uid" | "isDirectory" | "isSymbolicLink">;
27
+ export interface PrivateDirectoryOptions {
28
+ readonly currentUid?: () => number | undefined;
29
+ readonly lstat?: (path: string) => PrivateDirectoryStats;
30
+ }
31
+ export declare class UnsafePrivateDirectoryError extends Error {
32
+ readonly path: string;
33
+ readonly reason: "changed_during_chmod" | "not_directory" | "symlink" | "wrong_owner";
34
+ readonly name = "UnsafePrivateDirectoryError";
35
+ readonly code = "unsafe_private_directory";
36
+ constructor(path: string, reason: "changed_during_chmod" | "not_directory" | "symlink" | "wrong_owner");
37
+ }
38
+ export declare function authEnvelope(token: string): DaemonAuthEnvelope;
39
+ export declare function readAuthToken(paths: DaemonPaths): string | null;
40
+ export declare function readOrCreateAuthToken(paths: DaemonPaths): string;
41
+ export declare function rotateAuthToken(paths: DaemonPaths): string;
42
+ export declare function authenticateMessage(raw: unknown, expectedToken: string): AuthenticatedMessage | JsonRpcErrorResponse;
43
+ export declare function isAuthErrorResponse(message: unknown): boolean;
44
+ export declare function writePrivateFile(path: string, data: string): void;
45
+ export declare function ensurePrivateDirectory(path: string, options?: PrivateDirectoryOptions): void;
46
+ export declare function setPrivateFileMode(path: string): void;
@@ -0,0 +1,187 @@
1
+ import { randomBytes, timingSafeEqual } from "node:crypto";
2
+ import { chmodSync, closeSync, constants, fchmodSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, unlinkSync, writeSync, } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ import { isPlainRecord } from "@oh-my-opencode/mcp-stdio-core/record";
5
+ export const OMO_DAEMON_PROTOCOL_VERSION = 1;
6
+ export const AUTH_ERROR_CODE = -32001;
7
+ export const PROTOCOL_ERROR_CODE = -32002;
8
+ const AUTH_TOKEN_BYTES = 32;
9
+ export class UnsafePrivateDirectoryError extends Error {
10
+ constructor(path, reason) {
11
+ super(`unsafe private directory ${path}: ${reason}`);
12
+ this.path = path;
13
+ this.reason = reason;
14
+ this.name = "UnsafePrivateDirectoryError";
15
+ this.code = "unsafe_private_directory";
16
+ }
17
+ }
18
+ export function authEnvelope(token) {
19
+ return { protocolVersion: OMO_DAEMON_PROTOCOL_VERSION, token };
20
+ }
21
+ export function readAuthToken(paths) {
22
+ try {
23
+ const token = readFileSync(paths.auth, "utf8").trim();
24
+ return token.length > 0 ? token : null;
25
+ }
26
+ catch (error) {
27
+ if (error instanceof Error)
28
+ return null;
29
+ throw error;
30
+ }
31
+ }
32
+ export function readOrCreateAuthToken(paths) {
33
+ const existing = readAuthToken(paths);
34
+ if (existing)
35
+ return existing;
36
+ return createAuthToken(paths);
37
+ }
38
+ export function rotateAuthToken(paths) {
39
+ try {
40
+ unlinkSync(paths.auth);
41
+ }
42
+ catch (error) {
43
+ if (!(error instanceof Error))
44
+ throw error;
45
+ }
46
+ return createAuthToken(paths);
47
+ }
48
+ export function authenticateMessage(raw, expectedToken) {
49
+ const id = jsonRpcId(raw);
50
+ if (!isPlainRecord(raw))
51
+ return authError(id);
52
+ const params = raw["params"];
53
+ if (!isPlainRecord(params))
54
+ return authError(id);
55
+ const envelope = params["_omo"];
56
+ if (!isPlainRecord(envelope))
57
+ return authError(id);
58
+ const protocolVersion = envelope["protocolVersion"];
59
+ if (protocolVersion !== OMO_DAEMON_PROTOCOL_VERSION)
60
+ return protocolError(id);
61
+ const token = envelope["token"];
62
+ if (typeof token !== "string" || !tokenMatches(token, expectedToken))
63
+ return authError(id);
64
+ const cleanParams = { ...params };
65
+ delete cleanParams["_omo"];
66
+ return { input: { ...raw, params: cleanParams }, id, method: typeof raw["method"] === "string" ? raw["method"] : undefined };
67
+ }
68
+ export function isAuthErrorResponse(message) {
69
+ if (!isPlainRecord(message))
70
+ return false;
71
+ const error = message["error"];
72
+ if (!isPlainRecord(error))
73
+ return false;
74
+ const data = error["data"];
75
+ return error["code"] === AUTH_ERROR_CODE && isPlainRecord(data) && data["code"] === "daemon_authentication_failed";
76
+ }
77
+ export function writePrivateFile(path, data) {
78
+ const fd = openSync(path, "w", 0o600);
79
+ try {
80
+ writeSync(fd, data);
81
+ }
82
+ finally {
83
+ closeSync(fd);
84
+ }
85
+ setPrivateFileMode(path);
86
+ }
87
+ export function ensurePrivateDirectory(path, options = {}) {
88
+ try {
89
+ mkdirSync(path, { recursive: true, mode: 0o700 });
90
+ }
91
+ catch (error) {
92
+ if (errorCode(error) !== "EEXIST")
93
+ throw error;
94
+ }
95
+ if (process.platform === "win32")
96
+ return;
97
+ const before = validatePrivateDirectory(path, options);
98
+ const fd = openSync(path, constants.O_RDONLY | constants.O_DIRECTORY | constants.O_NOFOLLOW);
99
+ try {
100
+ fchmodSync(fd, 0o700);
101
+ const after = validatePrivateDirectory(path, options);
102
+ const openStats = fstatSync(fd);
103
+ if (!sameDirectory(before, after) || !sameDirectory(before, openStats)) {
104
+ throw new UnsafePrivateDirectoryError(path, "changed_during_chmod");
105
+ }
106
+ }
107
+ finally {
108
+ closeSync(fd);
109
+ }
110
+ }
111
+ export function setPrivateFileMode(path) {
112
+ if (process.platform !== "win32")
113
+ chmodSync(path, 0o600);
114
+ }
115
+ function createAuthToken(paths) {
116
+ ensurePrivateDirectory(dirname(paths.auth));
117
+ const token = randomBytes(AUTH_TOKEN_BYTES).toString("base64url");
118
+ let fd;
119
+ try {
120
+ fd = openSync(paths.auth, "wx", 0o600);
121
+ }
122
+ catch (error) {
123
+ if (errorCode(error) === "EEXIST") {
124
+ const existing = readAuthToken(paths);
125
+ if (existing)
126
+ return existing;
127
+ }
128
+ throw error;
129
+ }
130
+ try {
131
+ writeSync(fd, `${token}\n`);
132
+ }
133
+ finally {
134
+ closeSync(fd);
135
+ }
136
+ setPrivateFileMode(paths.auth);
137
+ return token;
138
+ }
139
+ function errorCode(error) {
140
+ if (!error || typeof error !== "object" || !("code" in error))
141
+ return undefined;
142
+ const code = Reflect.get(error, "code");
143
+ return typeof code === "string" ? code : undefined;
144
+ }
145
+ function validatePrivateDirectory(path, options) {
146
+ const stats = options.lstat ? options.lstat(path) : lstatPrivateDirectory(path);
147
+ if (stats.isSymbolicLink())
148
+ throw new UnsafePrivateDirectoryError(path, "symlink");
149
+ if (!stats.isDirectory())
150
+ throw new UnsafePrivateDirectoryError(path, "not_directory");
151
+ const currentUid = options.currentUid ? options.currentUid() : process.getuid?.();
152
+ if (currentUid !== undefined && stats.uid !== currentUid) {
153
+ throw new UnsafePrivateDirectoryError(path, "wrong_owner");
154
+ }
155
+ return stats;
156
+ }
157
+ function lstatPrivateDirectory(path) {
158
+ return lstatSync(path);
159
+ }
160
+ function sameDirectory(a, b) {
161
+ return a.dev === b.dev && a.ino === b.ino;
162
+ }
163
+ function tokenMatches(candidate, expected) {
164
+ const candidateBytes = Buffer.from(candidate);
165
+ const expectedBytes = Buffer.from(expected);
166
+ return candidateBytes.length === expectedBytes.length && timingSafeEqual(candidateBytes, expectedBytes);
167
+ }
168
+ function jsonRpcId(raw) {
169
+ if (!isPlainRecord(raw))
170
+ return null;
171
+ const id = raw["id"];
172
+ return typeof id === "string" || typeof id === "number" || id === null ? id : null;
173
+ }
174
+ function authError(id) {
175
+ return {
176
+ jsonrpc: "2.0",
177
+ id,
178
+ error: { code: AUTH_ERROR_CODE, message: "daemon authentication failed", data: { code: "daemon_authentication_failed" } },
179
+ };
180
+ }
181
+ function protocolError(id) {
182
+ return {
183
+ jsonrpc: "2.0",
184
+ id,
185
+ error: { code: PROTOCOL_ERROR_CODE, message: "daemon protocol mismatch", data: { code: "daemon_protocol_mismatch" } },
186
+ };
187
+ }
@@ -8,7 +8,9 @@ export function isProcessAlive(pid) {
8
8
  return true;
9
9
  }
10
10
  catch (error) {
11
- return error.code === "EPERM";
11
+ if (error instanceof Error)
12
+ return errorCode(error) === "EPERM";
13
+ throw error;
12
14
  }
13
15
  }
14
16
  export function readLockPid(lockPath) {
@@ -33,13 +35,13 @@ export function tryAcquireLock(lockPath, ownerPid = process.pid) {
33
35
  }
34
36
  function writeLockFile(lockPath, ownerPid) {
35
37
  try {
36
- const fd = openSync(lockPath, "wx");
38
+ const fd = openSync(lockPath, "wx", 0o600);
37
39
  writeSync(fd, `${ownerPid}\n`);
38
40
  closeSync(fd);
39
41
  return { release: () => unlinkQuietly(lockPath) };
40
42
  }
41
43
  catch (error) {
42
- if (error.code === "EEXIST")
44
+ if (errorCode(error) === "EEXIST")
43
45
  return null;
44
46
  throw error;
45
47
  }
@@ -56,6 +58,14 @@ export function unlinkQuietly(path) {
56
58
  unlinkSync(path);
57
59
  }
58
60
  catch (error) {
59
- void error;
61
+ if (error instanceof Error)
62
+ return;
63
+ throw error;
60
64
  }
61
65
  }
66
+ function errorCode(error) {
67
+ if (!error || typeof error !== "object" || !("code" in error))
68
+ return undefined;
69
+ const code = Reflect.get(error, "code");
70
+ return typeof code === "string" ? code : undefined;
71
+ }
@@ -0,0 +1,49 @@
1
+ import { type LockHandle } from "./lock.js";
2
+ import type { DaemonPaths } from "./paths.js";
3
+ export type EndpointIdentity = {
4
+ readonly kind: "unix";
5
+ readonly path: string;
6
+ readonly dev: number;
7
+ readonly ino: number;
8
+ } | {
9
+ readonly kind: "windows";
10
+ readonly path: string;
11
+ } | {
12
+ readonly kind: "missing";
13
+ readonly path: string;
14
+ };
15
+ export type DaemonOwner = {
16
+ readonly pid: number;
17
+ readonly nonce: string;
18
+ readonly startedAt: string;
19
+ readonly endpoint: EndpointIdentity;
20
+ };
21
+ export type StartupLease = {
22
+ readonly lock: LockHandle;
23
+ readonly token: string;
24
+ readonly owner: DaemonOwner;
25
+ };
26
+ export declare class DaemonAlreadyRunningError extends Error {
27
+ readonly name = "DaemonAlreadyRunningError";
28
+ readonly code = "daemon_already_running";
29
+ }
30
+ export declare class DaemonStartupDeferredError extends Error {
31
+ readonly reason: string;
32
+ readonly name = "DaemonStartupDeferredError";
33
+ readonly code = "daemon_startup_deferred";
34
+ constructor(reason: string);
35
+ }
36
+ export type OwnerPing = {
37
+ readonly pid: number;
38
+ readonly nonce: string;
39
+ readonly startedAt: string;
40
+ readonly endpoint: EndpointIdentity;
41
+ };
42
+ export declare function acquireStartupLease(paths: DaemonPaths, pingOwner: (token: string) => Promise<OwnerPing | null>): Promise<StartupLease>;
43
+ export declare function ensureDaemonDirectories(paths: DaemonPaths): void;
44
+ export declare function readDaemonOwner(paths: DaemonPaths): DaemonOwner | null;
45
+ export declare function writeDaemonOwner(paths: DaemonPaths, owner: DaemonOwner): void;
46
+ export declare function removeDaemonMetadataForOwner(paths: DaemonPaths, owner: DaemonOwner): void;
47
+ export declare function endpointIdentity(endpointPath: string): EndpointIdentity;
48
+ export declare function sameEndpoint(a: EndpointIdentity, b: EndpointIdentity): boolean;
49
+ export declare function sameOwner(a: DaemonOwner, b: DaemonOwner): boolean;
@@ -0,0 +1,168 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { existsSync, lstatSync, readFileSync, statSync, unlinkSync } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ import { tryAcquireLock, isProcessAlive, unlinkQuietly } from "./lock.js";
5
+ import { ensurePrivateDirectory, readAuthToken, readOrCreateAuthToken, rotateAuthToken, writePrivateFile, } from "./ipc-protocol.js";
6
+ export class DaemonAlreadyRunningError extends Error {
7
+ constructor() {
8
+ super(...arguments);
9
+ this.name = "DaemonAlreadyRunningError";
10
+ this.code = "daemon_already_running";
11
+ }
12
+ }
13
+ export class DaemonStartupDeferredError extends Error {
14
+ constructor(reason) {
15
+ super(`LSP daemon startup deferred: ${reason}`);
16
+ this.reason = reason;
17
+ this.name = "DaemonStartupDeferredError";
18
+ this.code = "daemon_startup_deferred";
19
+ }
20
+ }
21
+ export async function acquireStartupLease(paths, pingOwner) {
22
+ ensureDaemonDirectories(paths);
23
+ const lock = tryAcquireLock(paths.lock);
24
+ if (!lock) {
25
+ const token = readAuthToken(paths);
26
+ if (token && (await pingOwner(token)))
27
+ throw new DaemonAlreadyRunningError("LSP daemon already running");
28
+ throw new DaemonStartupDeferredError("startup_lock_busy");
29
+ }
30
+ try {
31
+ const token = await validateExistingOwner(paths, pingOwner);
32
+ return { lock, token, owner: newOwner(paths) };
33
+ }
34
+ catch (error) {
35
+ lock.release();
36
+ throw error;
37
+ }
38
+ }
39
+ export function ensureDaemonDirectories(paths) {
40
+ ensurePrivateDirectory(paths.dir);
41
+ if (process.platform !== "win32")
42
+ ensurePrivateDirectory(dirname(paths.socket));
43
+ }
44
+ export function readDaemonOwner(paths) {
45
+ try {
46
+ return parseOwner(JSON.parse(readFileSync(paths.owner, "utf8")));
47
+ }
48
+ catch (error) {
49
+ if (error instanceof Error)
50
+ return null;
51
+ throw error;
52
+ }
53
+ }
54
+ export function writeDaemonOwner(paths, owner) {
55
+ writePrivateFile(paths.pid, `${owner.pid}\n`);
56
+ writePrivateFile(paths.endpoint, owner.endpoint.path);
57
+ writePrivateFile(paths.owner, `${JSON.stringify(owner)}\n`);
58
+ }
59
+ export function removeDaemonMetadataForOwner(paths, owner) {
60
+ const current = readDaemonOwner(paths);
61
+ if (!current || !sameOwner(current, owner))
62
+ return;
63
+ unlinkQuietly(paths.socket);
64
+ unlinkQuietly(paths.pid);
65
+ unlinkQuietly(paths.endpoint);
66
+ unlinkQuietly(paths.owner);
67
+ }
68
+ export function endpointIdentity(endpointPath) {
69
+ if (process.platform === "win32")
70
+ return { kind: "windows", path: endpointPath };
71
+ try {
72
+ const stats = statSync(endpointPath);
73
+ return { kind: "unix", path: endpointPath, dev: stats.dev, ino: stats.ino };
74
+ }
75
+ catch (error) {
76
+ if (error instanceof Error)
77
+ return { kind: "missing", path: endpointPath };
78
+ throw error;
79
+ }
80
+ }
81
+ export function sameEndpoint(a, b) {
82
+ if (a.kind !== b.kind || a.path !== b.path)
83
+ return false;
84
+ switch (a.kind) {
85
+ case "unix":
86
+ return b.kind === "unix" && a.dev === b.dev && a.ino === b.ino;
87
+ case "windows":
88
+ return true;
89
+ case "missing":
90
+ return true;
91
+ }
92
+ }
93
+ export function sameOwner(a, b) {
94
+ return a.pid === b.pid && a.nonce === b.nonce && sameEndpoint(a.endpoint, b.endpoint);
95
+ }
96
+ async function validateExistingOwner(paths, pingOwner) {
97
+ let token = readOrCreateAuthToken(paths);
98
+ for (let attempt = 0; attempt < 2; attempt += 1) {
99
+ const owner = readDaemonOwner(paths);
100
+ if (!owner)
101
+ return token;
102
+ const ping = await pingOwner(token);
103
+ if (ping && owner.nonce === ping.nonce && sameEndpoint(owner.endpoint, ping.endpoint)) {
104
+ throw new DaemonAlreadyRunningError("LSP daemon already running");
105
+ }
106
+ if (ping)
107
+ continue;
108
+ if (isProcessAlive(owner.pid))
109
+ throw new DaemonStartupDeferredError("owner_pid_live_unreachable");
110
+ const reread = readDaemonOwner(paths);
111
+ const endpoint = endpointIdentity(owner.endpoint.path);
112
+ if (!reread || reread.nonce !== owner.nonce || !sameEndpoint(endpoint, owner.endpoint)) {
113
+ throw new DaemonStartupDeferredError("owner_changed_during_cleanup");
114
+ }
115
+ cleanupDeadOwner(paths, owner);
116
+ token = rotateAuthToken(paths);
117
+ return token;
118
+ }
119
+ throw new DaemonStartupDeferredError("reachable_owner_mismatch");
120
+ }
121
+ function cleanupDeadOwner(paths, owner) {
122
+ if (process.platform !== "win32" && existsSync(owner.endpoint.path)) {
123
+ const stat = lstatSync(owner.endpoint.path);
124
+ if (stat.isSocket())
125
+ unlinkSync(owner.endpoint.path);
126
+ }
127
+ unlinkQuietly(paths.pid);
128
+ unlinkQuietly(paths.endpoint);
129
+ unlinkQuietly(paths.owner);
130
+ }
131
+ function newOwner(paths) {
132
+ return {
133
+ pid: process.pid,
134
+ nonce: randomUUID(),
135
+ startedAt: new Date().toISOString(),
136
+ endpoint: endpointIdentity(paths.socket),
137
+ };
138
+ }
139
+ function parseOwner(value) {
140
+ if (!value || typeof value !== "object" || Array.isArray(value))
141
+ return null;
142
+ const pid = Reflect.get(value, "pid");
143
+ const nonce = Reflect.get(value, "nonce");
144
+ const startedAt = Reflect.get(value, "startedAt");
145
+ const endpoint = parseEndpoint(Reflect.get(value, "endpoint"));
146
+ if (typeof pid !== "number" || typeof nonce !== "string" || typeof startedAt !== "string" || !endpoint)
147
+ return null;
148
+ return { pid, nonce, startedAt, endpoint };
149
+ }
150
+ function parseEndpoint(value) {
151
+ if (!value || typeof value !== "object" || Array.isArray(value))
152
+ return null;
153
+ const kind = Reflect.get(value, "kind");
154
+ const path = Reflect.get(value, "path");
155
+ if (typeof path !== "string")
156
+ return null;
157
+ if (kind === undefined)
158
+ return endpointIdentity(path);
159
+ if (kind === "windows")
160
+ return { kind, path };
161
+ if (kind === "missing")
162
+ return { kind, path };
163
+ const dev = Reflect.get(value, "dev");
164
+ const ino = Reflect.get(value, "ino");
165
+ if (kind === "unix" && typeof dev === "number" && typeof ino === "number")
166
+ return { kind, path, dev, ino };
167
+ return null;
168
+ }
@@ -1,12 +1,36 @@
1
+ import { type DaemonRuntimeDefaults } from "./runtime-contract.js";
2
+ export { InvalidDaemonVersionError, OMO_LSP_DAEMON_DIR, OMO_LSP_DAEMON_VERSION, validateDaemonVersion, } from "./runtime-contract.js";
3
+ export interface DaemonPathOperations {
4
+ readonly isAbsolute: (value: string) => boolean;
5
+ readonly join: (...parts: string[]) => string;
6
+ readonly resolve: (...parts: string[]) => string;
7
+ }
8
+ export interface DaemonPlatform {
9
+ readonly platform: NodeJS.Platform;
10
+ readonly homedir: () => string;
11
+ readonly tmpdir: () => string;
12
+ readonly getuid: () => number | undefined;
13
+ readonly username: () => string;
14
+ readonly path: DaemonPathOperations;
15
+ }
16
+ export declare class InvalidDaemonDirectoryError extends Error {
17
+ readonly code = "invalid_daemon_directory";
18
+ readonly directory: string;
19
+ constructor(directory: string);
20
+ }
1
21
  export interface DaemonPaths {
2
- version: string;
3
- dir: string;
4
- socket: string;
5
- lock: string;
6
- pid: string;
7
- log: string;
22
+ readonly version: string;
23
+ readonly cliPath: string;
24
+ readonly dir: string;
25
+ readonly socket: string;
26
+ readonly lock: string;
27
+ readonly pid: string;
28
+ readonly auth: string;
29
+ readonly endpoint: string;
30
+ readonly owner: string;
31
+ readonly log: string;
8
32
  }
9
33
  export declare function resolveDaemonVersion(requireFn?: (id: string) => unknown): string;
10
- export declare function daemonBaseDir(env?: NodeJS.ProcessEnv): string;
11
- export declare function daemonPaths(env?: NodeJS.ProcessEnv, version?: string): DaemonPaths;
12
- export declare function resolveDaemonVersionFromEnv(env?: NodeJS.ProcessEnv): string | null;
34
+ export declare function packagedRuntimeDefaults(): DaemonRuntimeDefaults;
35
+ export declare function daemonBaseDir(env?: NodeJS.ProcessEnv, platform?: DaemonPlatform): string;
36
+ export declare function daemonPaths(env?: NodeJS.ProcessEnv, runtimeDefaults?: DaemonRuntimeDefaults, platform?: DaemonPlatform): DaemonPaths;
@@ -1,54 +1,93 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { createRequire } from "node:module";
3
- import { homedir, tmpdir } from "node:os";
4
- import { join } from "node:path";
3
+ import { homedir, tmpdir, userInfo } from "node:os";
4
+ import * as path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+ import { OMO_LSP_DAEMON_DIR, resolveDaemonRuntime, validateDaemonVersion, } from "./runtime-contract.js";
7
+ export { InvalidDaemonVersionError, OMO_LSP_DAEMON_DIR, OMO_LSP_DAEMON_VERSION, validateDaemonVersion, } from "./runtime-contract.js";
5
8
  const requireFromHere = createRequire(import.meta.url);
6
9
  const MAX_SOCKET_PATH_LENGTH = 100;
7
- const CODEX_LSP_DAEMON_VERSION_ENV = "CODEX_LSP_DAEMON_VERSION";
10
+ export class InvalidDaemonDirectoryError extends Error {
11
+ constructor(directory) {
12
+ super(`${OMO_LSP_DAEMON_DIR} must be an absolute path`);
13
+ this.code = "invalid_daemon_directory";
14
+ this.name = "InvalidDaemonDirectoryError";
15
+ this.directory = directory;
16
+ }
17
+ }
8
18
  export function resolveDaemonVersion(requireFn = requireFromHere) {
9
19
  for (const candidate of ["./package.json", "../package.json"]) {
20
+ let loaded;
10
21
  try {
11
- const pkg = requireFn(candidate);
12
- if (typeof pkg.version === "string" && pkg.version.length > 0)
13
- return pkg.version;
22
+ loaded = requireFn(candidate);
23
+ }
24
+ catch (error) {
25
+ if (!(error instanceof Error))
26
+ throw error;
27
+ continue;
28
+ }
29
+ if (typeof loaded === "object" && loaded !== null && "version" in loaded) {
30
+ const version = Reflect.get(loaded, "version");
31
+ if (typeof version === "string")
32
+ return validateDaemonVersion(version);
14
33
  }
15
- catch { }
16
34
  }
17
35
  return "0";
18
36
  }
19
- export function daemonBaseDir(env = process.env) {
20
- const explicit = env["CODEX_LSP_DAEMON_DIR"]?.trim();
21
- if (explicit)
22
- return explicit;
23
- const pluginData = env["PLUGIN_DATA"]?.trim();
24
- if (pluginData)
25
- return join(pluginData, "daemon");
26
- const codexHome = env["CODEX_HOME"]?.trim();
27
- const home = codexHome && codexHome.length > 0 ? codexHome : join(homedir(), ".codex");
28
- return join(home, "codex-lsp", "daemon");
29
- }
30
- export function daemonPaths(env = process.env, version = resolveDaemonVersionFromEnv(env) ?? resolveDaemonVersion()) {
31
- const dir = join(daemonBaseDir(env), `v${version}`);
37
+ export function packagedRuntimeDefaults() {
38
+ return {
39
+ cliPath: fileURLToPath(new URL("./cli.js", import.meta.url)),
40
+ version: resolveDaemonVersion(),
41
+ };
42
+ }
43
+ export function daemonBaseDir(env = process.env, platform = defaultDaemonPlatform()) {
44
+ const override = env[OMO_LSP_DAEMON_DIR];
45
+ if (override !== undefined) {
46
+ if (!platform.path.isAbsolute(override))
47
+ throw new InvalidDaemonDirectoryError(override);
48
+ return platform.path.resolve(override);
49
+ }
50
+ return platform.path.resolve(platform.path.join(platform.homedir(), ".omo", "lsp-daemon"));
51
+ }
52
+ export function daemonPaths(env = process.env, runtimeDefaults = packagedRuntimeDefaults(), platform = defaultDaemonPlatform()) {
53
+ const runtime = resolveDaemonRuntime(env, runtimeDefaults);
54
+ const baseDir = daemonBaseDir(env, platform);
55
+ const dir = platform.path.resolve(platform.path.join(baseDir, `v${runtime.version}`));
32
56
  return {
33
- version,
57
+ version: runtime.version,
58
+ cliPath: runtime.cliPath,
34
59
  dir,
35
- socket: resolveSocketPath(dir, version),
36
- lock: join(dir, "daemon.lock"),
37
- pid: join(dir, "daemon.pid"),
38
- log: join(dir, "daemon.log"),
60
+ socket: resolveSocketPath(dir, runtime.version, platform),
61
+ lock: platform.path.join(dir, "daemon.lock"),
62
+ pid: platform.path.join(dir, "daemon.pid"),
63
+ auth: platform.path.join(dir, "daemon.auth"),
64
+ endpoint: platform.path.join(dir, "daemon.endpoint"),
65
+ owner: platform.path.join(dir, "daemon.owner"),
66
+ log: platform.path.join(dir, "daemon.log"),
39
67
  };
40
68
  }
41
- export function resolveDaemonVersionFromEnv(env = process.env) {
42
- const version = env[CODEX_LSP_DAEMON_VERSION_ENV]?.trim();
43
- return version && version.length > 0 ? version : null;
69
+ function defaultDaemonPlatform() {
70
+ return {
71
+ platform: process.platform,
72
+ homedir,
73
+ tmpdir,
74
+ getuid: () => (typeof process.getuid === "function" ? process.getuid() : undefined),
75
+ username: () => userInfo().username,
76
+ path,
77
+ };
44
78
  }
45
- function resolveSocketPath(dir, version) {
46
- const digest = createHash("sha256").update(dir).digest("hex").slice(0, 16);
47
- if (process.platform === "win32") {
79
+ function resolveSocketPath(dir, version, platform) {
80
+ const canonicalVersionDir = platform.path.resolve(dir);
81
+ if (platform.platform === "win32") {
82
+ const currentUserDiscriminator = `${platform.getuid() ?? "win"}:${platform.username()}:${platform.path.resolve(platform.homedir())}`;
83
+ const digest = shortDigest(`${canonicalVersionDir}\0${currentUserDiscriminator}`);
48
84
  return `\\\\.\\pipe\\omo-lsp-${version}-${digest}`;
49
85
  }
50
- const natural = join(dir, "daemon.sock");
86
+ const natural = platform.path.join(canonicalVersionDir, "daemon.sock");
51
87
  if (natural.length < MAX_SOCKET_PATH_LENGTH)
52
88
  return natural;
53
- return join(tmpdir(), `omo-lsp-${version}-${digest}.sock`);
89
+ return platform.path.join(platform.tmpdir(), `omo-lsp-${version}-${shortDigest(canonicalVersionDir)}`, "daemon.sock");
90
+ }
91
+ function shortDigest(value) {
92
+ return createHash("sha256").update(value).digest("hex").slice(0, 16);
54
93
  }
@@ -4,8 +4,13 @@ import { type DaemonPaths } from "./paths.js";
4
4
  export interface ProxyOptions {
5
5
  input?: Readable;
6
6
  output?: Writable;
7
+ stderr?: Writable;
7
8
  paths?: DaemonPaths;
8
9
  context?: DaemonToolContext;
10
+ cwd?: string;
11
+ env?: Record<string, string | undefined>;
12
+ homeDir?: string;
9
13
  ensure?: CallToolOptions["ensure"];
14
+ startupTimeoutMs?: number;
10
15
  }
11
16
  export declare function runMcpStdioProxy(options?: ProxyOptions): Promise<void>;