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.
- package/.agents/skills/codex-qa/scripts/lsp-e2e.sh +3677 -0
- package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +3154 -0
- package/.agents/skills/work-with-pr/SKILL.md +16 -37
- package/.agents/skills/work-with-pr-workspace/evals/evals.json +3 -3
- package/.opencode/skills/work-with-pr/SKILL.md +16 -37
- package/.opencode/skills/work-with-pr-workspace/evals/evals.json +3 -3
- package/bin/AGENTS.md +33 -0
- package/dist/cli/index.js +500 -158
- package/dist/cli-node/index.js +500 -158
- package/dist/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.d.ts +6 -0
- package/dist/hooks/category-skill-reminder/hook.d.ts +9 -1
- package/dist/hooks/comment-checker/hook.d.ts +8 -1
- package/dist/hooks/todo-continuation-enforcer/types.d.ts +3 -0
- package/dist/index.js +956 -702
- package/dist/plugin/messages-transform.d.ts +1 -0
- package/dist/plugin-handlers/prometheus-agent-config-builder.d.ts +2 -0
- package/dist/tui.js +43 -4
- package/package.json +16 -16
- package/packages/git-bash-mcp/dist/cli.js +81 -19
- package/packages/lsp-core/package.json +4 -0
- package/packages/lsp-core/src/index.ts +1 -0
- package/packages/lsp-core/src/lsp/cleanup-errors.test.ts +18 -0
- package/packages/lsp-core/src/lsp/cleanup-errors.ts +12 -3
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +261 -0
- package/packages/lsp-core/src/lsp/client-wrapper.test.ts +63 -0
- package/packages/lsp-core/src/lsp/client-wrapper.ts +35 -5
- package/packages/lsp-core/src/lsp/client.ts +262 -80
- package/packages/lsp-core/src/lsp/config-loader.ts +5 -17
- package/packages/lsp-core/src/lsp/connection.ts +12 -6
- package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +221 -0
- package/packages/lsp-core/src/lsp/directory-diagnostics.ts +61 -28
- package/packages/lsp-core/src/lsp/errors.ts +11 -0
- package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +283 -0
- package/packages/lsp-core/src/lsp/fixtures/workspace-edit-contract-probe.ts +196 -0
- package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +215 -0
- package/packages/lsp-core/src/lsp/formatters.ts +3 -0
- package/packages/lsp-core/src/lsp/json-rpc-connection-cancellation.test.ts +97 -0
- package/packages/lsp-core/src/lsp/json-rpc-connection.ts +73 -5
- package/packages/lsp-core/src/lsp/server-install-state.ts +3 -6
- package/packages/lsp-core/src/lsp/transport-protocol.ts +52 -0
- package/packages/lsp-core/src/lsp/transport.ts +96 -70
- package/packages/lsp-core/src/lsp/workspace-apply-edit-failure.ts +19 -0
- package/packages/lsp-core/src/lsp/workspace-apply-edit-lease.integration.test.ts +214 -0
- package/packages/lsp-core/src/lsp/workspace-apply-edit-sync.integration.test.ts +113 -0
- package/packages/lsp-core/src/lsp/workspace-apply-edit-test-support.ts +163 -0
- package/packages/lsp-core/src/lsp/workspace-apply-edit.integration.test.ts +163 -0
- package/packages/lsp-core/src/lsp/workspace-document-state.test.ts +67 -0
- package/packages/lsp-core/src/lsp/workspace-document-state.ts +368 -0
- package/packages/lsp-core/src/lsp/workspace-edit-adversarial.test.ts +113 -0
- package/packages/lsp-core/src/lsp/workspace-edit-commit.test.ts +140 -0
- package/packages/lsp-core/src/lsp/workspace-edit-commit.ts +220 -0
- package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.test.ts +56 -0
- package/packages/lsp-core/src/lsp/workspace-edit-contract-evidence.ts +30 -0
- package/packages/lsp-core/src/lsp/workspace-edit-fingerprint.ts +44 -0
- package/packages/lsp-core/src/lsp/workspace-edit-options.test.ts +147 -0
- package/packages/lsp-core/src/lsp/workspace-edit-parse-helpers.ts +59 -0
- package/packages/lsp-core/src/lsp/workspace-edit-parser.ts +130 -0
- package/packages/lsp-core/src/lsp/workspace-edit-path.ts +98 -0
- package/packages/lsp-core/src/lsp/workspace-edit-plan-types.ts +60 -0
- package/packages/lsp-core/src/lsp/workspace-edit-plan.ts +73 -0
- package/packages/lsp-core/src/lsp/workspace-edit-prevalidation.test.ts +174 -0
- package/packages/lsp-core/src/lsp/workspace-edit-resource-parser.ts +89 -0
- package/packages/lsp-core/src/lsp/workspace-edit-simulation.ts +183 -0
- package/packages/lsp-core/src/lsp/workspace-edit-snapshot.ts +53 -0
- package/packages/lsp-core/src/lsp/workspace-edit-text.ts +125 -0
- package/packages/lsp-core/src/lsp/workspace-edit-types.ts +121 -0
- package/packages/lsp-core/src/lsp/workspace-edit.characterization.test.ts +95 -0
- package/packages/lsp-core/src/lsp/workspace-edit.ts +49 -200
- package/packages/lsp-core/src/lsp/workspace-mutation-controller.ts +182 -0
- package/packages/lsp-core/src/mcp.ts +18 -7
- package/packages/lsp-core/src/missing-dependency-result.test.ts +105 -0
- package/packages/lsp-core/src/missing-dependency-result.ts +57 -0
- package/packages/lsp-core/src/post-edit/index.ts +1 -0
- package/packages/lsp-core/src/post-edit/orchestration.test.ts +157 -0
- package/packages/lsp-core/src/post-edit/orchestration.ts +178 -0
- package/packages/lsp-core/src/request-context.test.ts +171 -0
- package/packages/lsp-core/src/request-context.ts +222 -9
- package/packages/lsp-core/src/tool-surface.test.ts +4 -1
- package/packages/lsp-core/src/tools/diagnostics.ts +32 -13
- package/packages/lsp-core/src/tools/navigation.ts +12 -12
- package/packages/lsp-core/src/tools/rename.ts +10 -15
- package/packages/lsp-core/src/tools/symbols.ts +11 -11
- package/packages/lsp-core/src/tools/types.ts +2 -1
- package/packages/lsp-daemon/dist/cli.js +3330 -764
- package/packages/lsp-daemon/dist/client.d.ts +105 -0
- package/packages/lsp-daemon/dist/client.js +5995 -0
- package/packages/lsp-daemon/dist/daemon-client.d.ts +12 -7
- package/packages/lsp-daemon/dist/daemon-client.js +139 -32
- package/packages/lsp-daemon/dist/daemon-server.d.ts +1 -0
- package/packages/lsp-daemon/dist/daemon-server.js +40 -15
- package/packages/lsp-daemon/dist/ensure-daemon.d.ts +10 -8
- package/packages/lsp-daemon/dist/ensure-daemon.js +135 -51
- package/packages/lsp-daemon/dist/index.d.ts +2 -2
- package/packages/lsp-daemon/dist/index.js +3093 -786
- package/packages/lsp-daemon/dist/ipc-protocol.d.ts +46 -0
- package/packages/lsp-daemon/dist/ipc-protocol.js +187 -0
- package/packages/lsp-daemon/dist/lock.js +14 -4
- package/packages/lsp-daemon/dist/ownership.d.ts +49 -0
- package/packages/lsp-daemon/dist/ownership.js +168 -0
- package/packages/lsp-daemon/dist/paths.d.ts +33 -9
- package/packages/lsp-daemon/dist/paths.js +72 -33
- package/packages/lsp-daemon/dist/proxy.d.ts +5 -0
- package/packages/lsp-daemon/dist/proxy.js +123 -16
- package/packages/lsp-daemon/dist/request-routing.d.ts +7 -2
- package/packages/lsp-daemon/dist/request-routing.js +71 -22
- package/packages/lsp-daemon/dist/run-daemon.js +9 -2
- package/packages/lsp-daemon/dist/runtime-contract.d.ts +21 -0
- package/packages/lsp-daemon/dist/runtime-contract.js +58 -0
- package/packages/lsp-daemon/dist/socket-jsonrpc.js +6 -1
- package/packages/lsp-daemon/package.json +12 -3
- package/packages/lsp-tools-mcp/dist/cli.js +2189 -454
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +1741 -148
- package/packages/lsp-tools-mcp/dist/mcp.js +2206 -471
- package/packages/lsp-tools-mcp/dist/request-context.js +176 -6
- package/packages/lsp-tools-mcp/dist/tools.js +2119 -447
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +100 -28
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +100 -28
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +21 -9
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +35 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +69 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +57 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/.mcp.json +2 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +55 -0
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +3033 -936
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook-cli.js +0 -4
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.d.ts +5 -2
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +41 -62
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.d.ts +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.js +24 -15
- package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.d.ts +3 -7
- package/packages/omo-codex/plugin/components/lsp/dist/lsp-session-state.js +23 -49
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +3 -2
- package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-daemon.mjs +31 -1
- package/packages/omo-codex/plugin/components/lsp/scripts/build-lsp-daemon.test.mjs +76 -0
- package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.mjs +201 -0
- package/packages/omo-codex/plugin/components/lsp/scripts/build-runtime.test.mjs +55 -0
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook-cli.ts +0 -4
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +49 -71
- package/packages/omo-codex/plugin/components/lsp/src/daemon-cli-path.ts +26 -15
- package/packages/omo-codex/plugin/components/lsp/src/lsp-session-state.ts +26 -64
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook-unavailable.test.ts +16 -17
- package/packages/omo-codex/plugin/components/lsp/test/codex-hook.test.ts +30 -4
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +20 -5
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.5.md +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +5 -3
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/README.md +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +3 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/boulder-reader.ts +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/src/codex-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +15 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +20 -0
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +50 -33
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +6 -5
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +5 -6
- package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +2 -2
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +26 -14
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/build-bundled-mcp-runtimes.mjs +2 -3
- package/packages/omo-codex/plugin/scripts/build-components.mjs +13 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +9 -1
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +2 -1
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +6 -5
- package/packages/omo-codex/plugin/test/aggregate-build.test.mjs +8 -0
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +128 -15
- package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +10 -0
- package/packages/omo-codex/plugin/test/lsp-prebuild-layouts.test.mjs +2 -0
- package/packages/omo-codex/plugin/test/mcp-research-servers.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +7 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +34 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +328 -63
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/components/bootstrap/dist/cli.js\" hook session-start",
|
|
9
9
|
"commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -File \"${PLUGIN_ROOT}\\components\\bootstrap\\scripts\\bootstrap.ps1\"",
|
|
10
10
|
"timeout": 30,
|
|
11
|
-
"statusMessage": "(OmO 4.18.
|
|
11
|
+
"statusMessage": "(OmO 4.18.2) Checking Bootstrap Provisioning"
|
|
12
12
|
}
|
|
13
13
|
]
|
|
14
14
|
}
|
|
@@ -2989,6 +2989,9 @@ function errorResponse(id, code, message, data) {
|
|
|
2989
2989
|
function jsonRpcId(value) {
|
|
2990
2990
|
return typeof value === "string" || typeof value === "number" || value === null ? value : null;
|
|
2991
2991
|
}
|
|
2992
|
+
function messageFromError(error) {
|
|
2993
|
+
return error instanceof Error ? error.message : String(error);
|
|
2994
|
+
}
|
|
2992
2995
|
// ../../mcp-stdio-core/src/transport.ts
|
|
2993
2996
|
var HEADER_SEPARATOR = Buffer.from(`\r
|
|
2994
2997
|
\r
|
|
@@ -3011,16 +3014,45 @@ async function* readStdioJsonRpcMessages(input) {
|
|
|
3011
3014
|
yield parseJsonPayload(trailing, "line");
|
|
3012
3015
|
}
|
|
3013
3016
|
}
|
|
3014
|
-
function writeStdioJsonRpcResponse(output, response, responseMode) {
|
|
3017
|
+
async function writeStdioJsonRpcResponse(output, response, responseMode) {
|
|
3015
3018
|
const body = JSON.stringify(response);
|
|
3016
|
-
|
|
3017
|
-
output.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
3019
|
+
const payload = responseMode === "framed" ? `Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
3018
3020
|
\r
|
|
3019
|
-
${body}`
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3021
|
+
${body}` : `${body}
|
|
3022
|
+
`;
|
|
3023
|
+
await writeChunk(output, payload);
|
|
3024
|
+
}
|
|
3025
|
+
function writeChunk(output, chunk) {
|
|
3026
|
+
return new Promise((resolve5, reject) => {
|
|
3027
|
+
let settled = false;
|
|
3028
|
+
const onError = (error) => {
|
|
3029
|
+
if (settled)
|
|
3030
|
+
return;
|
|
3031
|
+
settled = true;
|
|
3032
|
+
reject(error);
|
|
3033
|
+
};
|
|
3034
|
+
output.once("error", onError);
|
|
3035
|
+
try {
|
|
3036
|
+
output.write(chunk, (error) => {
|
|
3037
|
+
if (settled)
|
|
3038
|
+
return;
|
|
3039
|
+
settled = true;
|
|
3040
|
+
if (error) {
|
|
3041
|
+
queueMicrotask(() => output.removeListener("error", onError));
|
|
3042
|
+
reject(error);
|
|
3043
|
+
return;
|
|
3044
|
+
}
|
|
3045
|
+
output.removeListener("error", onError);
|
|
3046
|
+
resolve5();
|
|
3047
|
+
});
|
|
3048
|
+
} catch (error) {
|
|
3049
|
+
output.removeListener("error", onError);
|
|
3050
|
+
if (settled)
|
|
3051
|
+
return;
|
|
3052
|
+
settled = true;
|
|
3053
|
+
reject(error);
|
|
3054
|
+
}
|
|
3055
|
+
});
|
|
3024
3056
|
}
|
|
3025
3057
|
function readNextMessage(buffer) {
|
|
3026
3058
|
if (buffer.length === 0)
|
|
@@ -3116,40 +3148,70 @@ async function runJsonRpcStdioServer(config) {
|
|
|
3116
3148
|
break;
|
|
3117
3149
|
idleTimer.arm();
|
|
3118
3150
|
if (message.kind === "parse_error") {
|
|
3119
|
-
handleParseError(message, config, log)
|
|
3151
|
+
if (!await handleParseError(message, config, log))
|
|
3152
|
+
break;
|
|
3120
3153
|
continue;
|
|
3121
3154
|
}
|
|
3122
|
-
await handleRequest(message, config, log)
|
|
3155
|
+
if (!await handleRequest(message, config, log))
|
|
3156
|
+
break;
|
|
3123
3157
|
}
|
|
3124
3158
|
} finally {
|
|
3125
3159
|
idleTimer.clear();
|
|
3126
3160
|
log("stdio_stopped");
|
|
3127
3161
|
}
|
|
3128
3162
|
}
|
|
3129
|
-
function handleParseError(message, config, log) {
|
|
3163
|
+
async function handleParseError(message, config, log) {
|
|
3130
3164
|
log("parse_error", { message: message.message });
|
|
3131
3165
|
const response = config.parseErrorResponse?.(message.message) ?? errorResponse(null, -32700, "Parse error", message.message);
|
|
3132
|
-
if (response
|
|
3133
|
-
|
|
3134
|
-
|
|
3166
|
+
if (response === undefined)
|
|
3167
|
+
return true;
|
|
3168
|
+
return writeResponse(response, {
|
|
3169
|
+
output: config.output,
|
|
3170
|
+
responseMode: message.responseMode,
|
|
3171
|
+
log
|
|
3172
|
+
});
|
|
3135
3173
|
}
|
|
3136
3174
|
async function handleRequest(message, config, log) {
|
|
3137
3175
|
const parsed = message.payload;
|
|
3138
3176
|
const id = isPlainRecord(parsed) ? jsonRpcId(parsed["id"]) : null;
|
|
3139
3177
|
const method = isPlainRecord(parsed) && typeof parsed["method"] === "string" ? parsed["method"] : null;
|
|
3140
3178
|
log("request", { id: id === null ? null : String(id), method });
|
|
3179
|
+
let response;
|
|
3141
3180
|
try {
|
|
3142
|
-
|
|
3143
|
-
if (response === undefined)
|
|
3144
|
-
return;
|
|
3145
|
-
writeStdioJsonRpcResponse(config.output, response, message.responseMode);
|
|
3146
|
-
log("response", { id: String(response.id), method, is_error: response.error !== undefined });
|
|
3181
|
+
response = await config.handler(parsed, config.handlerOptions);
|
|
3147
3182
|
} catch (error) {
|
|
3148
3183
|
if (config.onHandlerError === undefined)
|
|
3149
3184
|
throw error;
|
|
3150
3185
|
config.onHandlerError(error);
|
|
3186
|
+
return true;
|
|
3187
|
+
}
|
|
3188
|
+
if (response === undefined)
|
|
3189
|
+
return true;
|
|
3190
|
+
if (!await writeResponse(response, {
|
|
3191
|
+
output: config.output,
|
|
3192
|
+
responseMode: message.responseMode,
|
|
3193
|
+
log
|
|
3194
|
+
}))
|
|
3195
|
+
return false;
|
|
3196
|
+
log("response", { id: String(response.id), method, is_error: response.error !== undefined });
|
|
3197
|
+
return true;
|
|
3198
|
+
}
|
|
3199
|
+
async function writeResponse(response, context) {
|
|
3200
|
+
try {
|
|
3201
|
+
await writeStdioJsonRpcResponse(context.output, response, context.responseMode);
|
|
3202
|
+
return true;
|
|
3203
|
+
} catch (error) {
|
|
3204
|
+
if (!isTerminalOutputError(error))
|
|
3205
|
+
throw error;
|
|
3206
|
+
context.log("output_error", { message: messageFromError(error) });
|
|
3207
|
+
return false;
|
|
3151
3208
|
}
|
|
3152
3209
|
}
|
|
3210
|
+
function isTerminalOutputError(error) {
|
|
3211
|
+
if (!(error instanceof Error) || !("code" in error))
|
|
3212
|
+
return false;
|
|
3213
|
+
return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
|
|
3214
|
+
}
|
|
3153
3215
|
function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
|
|
3154
3216
|
let timer = null;
|
|
3155
3217
|
let isClosed = false;
|
|
@@ -3230,18 +3292,28 @@ async function runBridgedCodegraphProcess(command, args, options) {
|
|
|
3230
3292
|
resolveExit(signal === null ? 0 : 1);
|
|
3231
3293
|
});
|
|
3232
3294
|
});
|
|
3233
|
-
const
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
]);
|
|
3295
|
+
const clientForwardingDone = forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
|
|
3296
|
+
defaultResponseMode = mode;
|
|
3297
|
+
});
|
|
3298
|
+
const responseForwardingDone = forwardCodegraphToClient(childOutput, options.output, pendingResponses, () => defaultResponseMode);
|
|
3299
|
+
const bridgeDone = Promise.all([clientForwardingDone, responseForwardingDone]);
|
|
3300
|
+
const childAndResponsesDone = Promise.all([childExit, responseForwardingDone]).then(([exitCode]) => exitCode);
|
|
3239
3301
|
const destroyChildPipes = () => {
|
|
3240
3302
|
childInput.destroy();
|
|
3241
3303
|
childOutput.destroy();
|
|
3242
3304
|
};
|
|
3243
3305
|
childExit.then(destroyChildPipes, destroyChildPipes);
|
|
3244
|
-
|
|
3306
|
+
try {
|
|
3307
|
+
return await Promise.race([childAndResponsesDone, bridgeDone.then(() => childExit)]);
|
|
3308
|
+
} catch (error) {
|
|
3309
|
+
destroyChildPipes();
|
|
3310
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
3311
|
+
child.kill("SIGKILL");
|
|
3312
|
+
await childExit.catch(() => {
|
|
3313
|
+
return;
|
|
3314
|
+
});
|
|
3315
|
+
throw error;
|
|
3316
|
+
}
|
|
3245
3317
|
}
|
|
3246
3318
|
async function forwardClientToCodegraph(input, childInput, pendingResponses, setDefaultResponseMode) {
|
|
3247
3319
|
for await (const message of readStdioJsonRpcMessages(input)) {
|
|
@@ -3265,7 +3337,7 @@ async function forwardClientToCodegraph(input, childInput, pendingResponses, set
|
|
|
3265
3337
|
async function forwardCodegraphToClient(childOutput, output, pendingResponses, defaultResponseMode) {
|
|
3266
3338
|
for await (const message of readStdioJsonRpcMessages(childOutput)) {
|
|
3267
3339
|
if (message.kind === "parse_error") {
|
|
3268
|
-
writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
|
|
3340
|
+
await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
|
|
3269
3341
|
continue;
|
|
3270
3342
|
}
|
|
3271
3343
|
const key = responseModeKey(message.payload);
|
|
@@ -3273,7 +3345,7 @@ async function forwardCodegraphToClient(childOutput, output, pendingResponses, d
|
|
|
3273
3345
|
const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
|
|
3274
3346
|
if (key !== null)
|
|
3275
3347
|
pendingResponses.delete(key);
|
|
3276
|
-
writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
|
|
3348
|
+
await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
|
|
3277
3349
|
}
|
|
3278
3350
|
}
|
|
3279
3351
|
function responseModeKey(payload) {
|
|
@@ -1897,6 +1897,9 @@ function errorResponse(id, code, message, data) {
|
|
|
1897
1897
|
function jsonRpcId(value) {
|
|
1898
1898
|
return typeof value === "string" || typeof value === "number" || value === null ? value : null;
|
|
1899
1899
|
}
|
|
1900
|
+
function messageFromError(error) {
|
|
1901
|
+
return error instanceof Error ? error.message : String(error);
|
|
1902
|
+
}
|
|
1900
1903
|
// ../../../../mcp-stdio-core/src/transport.ts
|
|
1901
1904
|
var HEADER_SEPARATOR = Buffer.from(`\r
|
|
1902
1905
|
\r
|
|
@@ -1919,16 +1922,45 @@ async function* readStdioJsonRpcMessages(input) {
|
|
|
1919
1922
|
yield parseJsonPayload(trailing, "line");
|
|
1920
1923
|
}
|
|
1921
1924
|
}
|
|
1922
|
-
function writeStdioJsonRpcResponse(output, response, responseMode) {
|
|
1925
|
+
async function writeStdioJsonRpcResponse(output, response, responseMode) {
|
|
1923
1926
|
const body = JSON.stringify(response);
|
|
1924
|
-
|
|
1925
|
-
output.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
1927
|
+
const payload = responseMode === "framed" ? `Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
1926
1928
|
\r
|
|
1927
|
-
${body}`
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1929
|
+
${body}` : `${body}
|
|
1930
|
+
`;
|
|
1931
|
+
await writeChunk(output, payload);
|
|
1932
|
+
}
|
|
1933
|
+
function writeChunk(output, chunk) {
|
|
1934
|
+
return new Promise((resolve3, reject) => {
|
|
1935
|
+
let settled = false;
|
|
1936
|
+
const onError = (error) => {
|
|
1937
|
+
if (settled)
|
|
1938
|
+
return;
|
|
1939
|
+
settled = true;
|
|
1940
|
+
reject(error);
|
|
1941
|
+
};
|
|
1942
|
+
output.once("error", onError);
|
|
1943
|
+
try {
|
|
1944
|
+
output.write(chunk, (error) => {
|
|
1945
|
+
if (settled)
|
|
1946
|
+
return;
|
|
1947
|
+
settled = true;
|
|
1948
|
+
if (error) {
|
|
1949
|
+
queueMicrotask(() => output.removeListener("error", onError));
|
|
1950
|
+
reject(error);
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
output.removeListener("error", onError);
|
|
1954
|
+
resolve3();
|
|
1955
|
+
});
|
|
1956
|
+
} catch (error) {
|
|
1957
|
+
output.removeListener("error", onError);
|
|
1958
|
+
if (settled)
|
|
1959
|
+
return;
|
|
1960
|
+
settled = true;
|
|
1961
|
+
reject(error);
|
|
1962
|
+
}
|
|
1963
|
+
});
|
|
1932
1964
|
}
|
|
1933
1965
|
function readNextMessage(buffer) {
|
|
1934
1966
|
if (buffer.length === 0)
|
|
@@ -2024,40 +2056,70 @@ async function runJsonRpcStdioServer(config) {
|
|
|
2024
2056
|
break;
|
|
2025
2057
|
idleTimer.arm();
|
|
2026
2058
|
if (message.kind === "parse_error") {
|
|
2027
|
-
handleParseError(message, config, log)
|
|
2059
|
+
if (!await handleParseError(message, config, log))
|
|
2060
|
+
break;
|
|
2028
2061
|
continue;
|
|
2029
2062
|
}
|
|
2030
|
-
await handleRequest(message, config, log)
|
|
2063
|
+
if (!await handleRequest(message, config, log))
|
|
2064
|
+
break;
|
|
2031
2065
|
}
|
|
2032
2066
|
} finally {
|
|
2033
2067
|
idleTimer.clear();
|
|
2034
2068
|
log("stdio_stopped");
|
|
2035
2069
|
}
|
|
2036
2070
|
}
|
|
2037
|
-
function handleParseError(message, config, log) {
|
|
2071
|
+
async function handleParseError(message, config, log) {
|
|
2038
2072
|
log("parse_error", { message: message.message });
|
|
2039
2073
|
const response = config.parseErrorResponse?.(message.message) ?? errorResponse(null, -32700, "Parse error", message.message);
|
|
2040
|
-
if (response
|
|
2041
|
-
|
|
2042
|
-
|
|
2074
|
+
if (response === undefined)
|
|
2075
|
+
return true;
|
|
2076
|
+
return writeResponse(response, {
|
|
2077
|
+
output: config.output,
|
|
2078
|
+
responseMode: message.responseMode,
|
|
2079
|
+
log
|
|
2080
|
+
});
|
|
2043
2081
|
}
|
|
2044
2082
|
async function handleRequest(message, config, log) {
|
|
2045
2083
|
const parsed = message.payload;
|
|
2046
2084
|
const id = isPlainRecord(parsed) ? jsonRpcId(parsed["id"]) : null;
|
|
2047
2085
|
const method = isPlainRecord(parsed) && typeof parsed["method"] === "string" ? parsed["method"] : null;
|
|
2048
2086
|
log("request", { id: id === null ? null : String(id), method });
|
|
2087
|
+
let response;
|
|
2049
2088
|
try {
|
|
2050
|
-
|
|
2051
|
-
if (response === undefined)
|
|
2052
|
-
return;
|
|
2053
|
-
writeStdioJsonRpcResponse(config.output, response, message.responseMode);
|
|
2054
|
-
log("response", { id: String(response.id), method, is_error: response.error !== undefined });
|
|
2089
|
+
response = await config.handler(parsed, config.handlerOptions);
|
|
2055
2090
|
} catch (error) {
|
|
2056
2091
|
if (config.onHandlerError === undefined)
|
|
2057
2092
|
throw error;
|
|
2058
2093
|
config.onHandlerError(error);
|
|
2094
|
+
return true;
|
|
2095
|
+
}
|
|
2096
|
+
if (response === undefined)
|
|
2097
|
+
return true;
|
|
2098
|
+
if (!await writeResponse(response, {
|
|
2099
|
+
output: config.output,
|
|
2100
|
+
responseMode: message.responseMode,
|
|
2101
|
+
log
|
|
2102
|
+
}))
|
|
2103
|
+
return false;
|
|
2104
|
+
log("response", { id: String(response.id), method, is_error: response.error !== undefined });
|
|
2105
|
+
return true;
|
|
2106
|
+
}
|
|
2107
|
+
async function writeResponse(response, context) {
|
|
2108
|
+
try {
|
|
2109
|
+
await writeStdioJsonRpcResponse(context.output, response, context.responseMode);
|
|
2110
|
+
return true;
|
|
2111
|
+
} catch (error) {
|
|
2112
|
+
if (!isTerminalOutputError(error))
|
|
2113
|
+
throw error;
|
|
2114
|
+
context.log("output_error", { message: messageFromError(error) });
|
|
2115
|
+
return false;
|
|
2059
2116
|
}
|
|
2060
2117
|
}
|
|
2118
|
+
function isTerminalOutputError(error) {
|
|
2119
|
+
if (!(error instanceof Error) || !("code" in error))
|
|
2120
|
+
return false;
|
|
2121
|
+
return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
|
|
2122
|
+
}
|
|
2061
2123
|
function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
|
|
2062
2124
|
let timer = null;
|
|
2063
2125
|
let isClosed = false;
|
|
@@ -2138,18 +2200,28 @@ async function runBridgedCodegraphProcess(command, args, options) {
|
|
|
2138
2200
|
resolveExit(signal === null ? 0 : 1);
|
|
2139
2201
|
});
|
|
2140
2202
|
});
|
|
2141
|
-
const
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
]);
|
|
2203
|
+
const clientForwardingDone = forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
|
|
2204
|
+
defaultResponseMode = mode;
|
|
2205
|
+
});
|
|
2206
|
+
const responseForwardingDone = forwardCodegraphToClient(childOutput, options.output, pendingResponses, () => defaultResponseMode);
|
|
2207
|
+
const bridgeDone = Promise.all([clientForwardingDone, responseForwardingDone]);
|
|
2208
|
+
const childAndResponsesDone = Promise.all([childExit, responseForwardingDone]).then(([exitCode]) => exitCode);
|
|
2147
2209
|
const destroyChildPipes = () => {
|
|
2148
2210
|
childInput.destroy();
|
|
2149
2211
|
childOutput.destroy();
|
|
2150
2212
|
};
|
|
2151
2213
|
childExit.then(destroyChildPipes, destroyChildPipes);
|
|
2152
|
-
|
|
2214
|
+
try {
|
|
2215
|
+
return await Promise.race([childAndResponsesDone, bridgeDone.then(() => childExit)]);
|
|
2216
|
+
} catch (error) {
|
|
2217
|
+
destroyChildPipes();
|
|
2218
|
+
if (child.exitCode === null && child.signalCode === null)
|
|
2219
|
+
child.kill("SIGKILL");
|
|
2220
|
+
await childExit.catch(() => {
|
|
2221
|
+
return;
|
|
2222
|
+
});
|
|
2223
|
+
throw error;
|
|
2224
|
+
}
|
|
2153
2225
|
}
|
|
2154
2226
|
async function forwardClientToCodegraph(input, childInput, pendingResponses, setDefaultResponseMode) {
|
|
2155
2227
|
for await (const message of readStdioJsonRpcMessages(input)) {
|
|
@@ -2173,7 +2245,7 @@ async function forwardClientToCodegraph(input, childInput, pendingResponses, set
|
|
|
2173
2245
|
async function forwardCodegraphToClient(childOutput, output, pendingResponses, defaultResponseMode) {
|
|
2174
2246
|
for await (const message of readStdioJsonRpcMessages(childOutput)) {
|
|
2175
2247
|
if (message.kind === "parse_error") {
|
|
2176
|
-
writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
|
|
2248
|
+
await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
|
|
2177
2249
|
continue;
|
|
2178
2250
|
}
|
|
2179
2251
|
const key = responseModeKey(message.payload);
|
|
@@ -2181,7 +2253,7 @@ async function forwardCodegraphToClient(childOutput, output, pendingResponses, d
|
|
|
2181
2253
|
const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
|
|
2182
2254
|
if (key !== null)
|
|
2183
2255
|
pendingResponses.delete(key);
|
|
2184
|
-
writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
|
|
2256
|
+
await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
|
|
2185
2257
|
}
|
|
2186
2258
|
}
|
|
2187
2259
|
function responseModeKey(payload) {
|
|
@@ -63,18 +63,30 @@ export async function runBridgedCodegraphProcess(
|
|
|
63
63
|
resolveExit(signal === null ? 0 : 1);
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
66
|
+
const clientForwardingDone = forwardClientToCodegraph(options.input, childInput, pendingResponses, (mode) => {
|
|
67
|
+
defaultResponseMode = mode;
|
|
68
|
+
});
|
|
69
|
+
const responseForwardingDone = forwardCodegraphToClient(
|
|
70
|
+
childOutput,
|
|
71
|
+
options.output,
|
|
72
|
+
pendingResponses,
|
|
73
|
+
() => defaultResponseMode,
|
|
74
|
+
);
|
|
75
|
+
const bridgeDone = Promise.all([clientForwardingDone, responseForwardingDone]);
|
|
76
|
+
const childAndResponsesDone = Promise.all([childExit, responseForwardingDone]).then(([exitCode]) => exitCode);
|
|
72
77
|
const destroyChildPipes = (): void => {
|
|
73
78
|
childInput.destroy();
|
|
74
79
|
childOutput.destroy();
|
|
75
80
|
};
|
|
76
81
|
void childExit.then(destroyChildPipes, destroyChildPipes);
|
|
77
|
-
|
|
82
|
+
try {
|
|
83
|
+
return await Promise.race([childAndResponsesDone, bridgeDone.then(() => childExit)]);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
destroyChildPipes();
|
|
86
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
87
|
+
await childExit.catch(() => undefined);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
78
90
|
}
|
|
79
91
|
|
|
80
92
|
async function forwardClientToCodegraph(
|
|
@@ -110,14 +122,14 @@ async function forwardCodegraphToClient(
|
|
|
110
122
|
): Promise<void> {
|
|
111
123
|
for await (const message of readStdioJsonRpcMessages(childOutput)) {
|
|
112
124
|
if (message.kind === "parse_error") {
|
|
113
|
-
writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
|
|
125
|
+
await writeStdioJsonRpcResponse(output, errorResponse(null, -32700, "Parse error", message.message), defaultResponseMode());
|
|
114
126
|
continue;
|
|
115
127
|
}
|
|
116
128
|
const key = responseModeKey(message.payload);
|
|
117
129
|
const pendingResponse = key === null ? undefined : pendingResponses.get(key);
|
|
118
130
|
const responseMode = pendingResponse?.responseMode ?? defaultResponseMode();
|
|
119
131
|
if (key !== null) pendingResponses.delete(key);
|
|
120
|
-
writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
|
|
132
|
+
await writeStdioJsonRpcResponse(output, clarifyCodegraphResponse(message.payload, pendingResponse), responseMode);
|
|
121
133
|
}
|
|
122
134
|
}
|
|
123
135
|
|
|
@@ -49,6 +49,41 @@ export function writeFakeContractCodegraph(filePath: string): void {
|
|
|
49
49
|
chmodSync(filePath, 0o755);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
export function writeFakeHeldOpenCodegraph(filePath: string): void {
|
|
53
|
+
writeFileSync(
|
|
54
|
+
filePath,
|
|
55
|
+
[
|
|
56
|
+
"#!/usr/bin/env node",
|
|
57
|
+
"const fs = require('node:fs');",
|
|
58
|
+
"const readline = require('node:readline');",
|
|
59
|
+
"fs.writeFileSync(process.env.CODEGRAPH_FAKE_LOG, String(process.pid));",
|
|
60
|
+
"const rl = readline.createInterface({ input: process.stdin });",
|
|
61
|
+
"rl.once('line', (line) => {",
|
|
62
|
+
" const request = JSON.parse(line);",
|
|
63
|
+
" process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { ok: true } }) + '\\n');",
|
|
64
|
+
"});",
|
|
65
|
+
"setInterval(() => {}, 1000);",
|
|
66
|
+
].join("\n"),
|
|
67
|
+
);
|
|
68
|
+
chmodSync(filePath, 0o755);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function writeFakeExitingCodegraph(filePath: string): void {
|
|
72
|
+
writeFileSync(
|
|
73
|
+
filePath,
|
|
74
|
+
[
|
|
75
|
+
"#!/usr/bin/env node",
|
|
76
|
+
"const readline = require('node:readline');",
|
|
77
|
+
"const rl = readline.createInterface({ input: process.stdin });",
|
|
78
|
+
"rl.once('line', (line) => {",
|
|
79
|
+
" const request = JSON.parse(line);",
|
|
80
|
+
" process.stdout.write(JSON.stringify({ jsonrpc: '2.0', id: request.id, result: { ok: true } }) + '\\n', () => process.exit(0));",
|
|
81
|
+
"});",
|
|
82
|
+
].join("\n"),
|
|
83
|
+
);
|
|
84
|
+
chmodSync(filePath, 0o755);
|
|
85
|
+
}
|
|
86
|
+
|
|
52
87
|
export function frameMcpRequest(request: {
|
|
53
88
|
readonly id: number;
|
|
54
89
|
readonly method: string;
|
package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { mkdirSync, mkdtempSync, realpathSync, rmSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { PassThrough, Writable } from "node:stream";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
import { runCodegraphServe } from "../src/serve.ts";
|
|
8
|
+
import {
|
|
9
|
+
frameMcpRequest,
|
|
10
|
+
writeFakeExitingCodegraph,
|
|
11
|
+
} from "./mcp-bridge-fixtures.ts";
|
|
12
|
+
|
|
13
|
+
const componentRoot = realpathSync(
|
|
14
|
+
fileURLToPath(new URL("..", import.meta.url)),
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
describe("runCodegraphServe MCP bridge lifecycle", () => {
|
|
18
|
+
it("#given an exiting Codegraph child and delayed closed parent output #when the final response write fails #then the output error is preserved", async () => {
|
|
19
|
+
const tempRoot = mkdtempSync(
|
|
20
|
+
join(componentRoot, ".tmp-codegraph-exit-output-failure-"),
|
|
21
|
+
);
|
|
22
|
+
const projectRoot = join(tempRoot, "project");
|
|
23
|
+
const fakeCodegraph = join(tempRoot, "codegraph-exiting.cjs");
|
|
24
|
+
const input = new PassThrough();
|
|
25
|
+
const outputError = Object.assign(
|
|
26
|
+
new Error("parent output closed after child exit"),
|
|
27
|
+
{ code: "EPIPE" },
|
|
28
|
+
);
|
|
29
|
+
const writeStarted = Promise.withResolvers<void>();
|
|
30
|
+
const output = new Writable({
|
|
31
|
+
write(_chunk, _encoding, callback) {
|
|
32
|
+
writeStarted.resolve();
|
|
33
|
+
setTimeout(() => callback(outputError), 150);
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
mkdirSync(projectRoot, { recursive: true });
|
|
39
|
+
writeFakeExitingCodegraph(fakeCodegraph);
|
|
40
|
+
const run = runCodegraphServe({
|
|
41
|
+
cwd: tempRoot,
|
|
42
|
+
env: {
|
|
43
|
+
CODEGRAPH_ALLOW_UNSAFE_NODE: "1",
|
|
44
|
+
OMO_CODEGRAPH_BIN: fakeCodegraph,
|
|
45
|
+
OMO_CODEGRAPH_PROJECT_CWD: projectRoot,
|
|
46
|
+
PATH: process.env["PATH"],
|
|
47
|
+
},
|
|
48
|
+
nodeVersion: "22.14.0",
|
|
49
|
+
buildEnv: () => ({}),
|
|
50
|
+
resolve: () => ({
|
|
51
|
+
argsPrefix: [],
|
|
52
|
+
command: fakeCodegraph,
|
|
53
|
+
exists: true,
|
|
54
|
+
source: "env",
|
|
55
|
+
}),
|
|
56
|
+
stderr: { write: () => undefined },
|
|
57
|
+
stdin: input,
|
|
58
|
+
stdout: output,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
input.end(frameMcpRequest({ id: 1, method: "tools/list", params: {} }));
|
|
62
|
+
|
|
63
|
+
await writeStarted.promise;
|
|
64
|
+
await expect(run).rejects.toBe(outputError);
|
|
65
|
+
} finally {
|
|
66
|
+
rmSync(tempRoot, { recursive: true, force: true });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|