oh-my-opencode 4.18.1 → 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 +39 -16
- package/.agents/skills/opencode-qa/scripts/lsp-e2e.sh +111 -28
- package/bin/AGENTS.md +33 -0
- package/dist/cli/index.js +57 -18
- package/dist/cli-node/index.js +57 -18
- 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 +560 -333
- 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 +13 -13
- package/packages/git-bash-mcp/dist/cli.js +81 -19
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +4 -4
- package/packages/lsp-core/src/lsp/directory-diagnostics.test.ts +118 -1
- package/packages/lsp-core/src/lsp/directory-diagnostics.ts +1 -1
- package/packages/lsp-daemon/dist/cli.js +262 -63
- package/packages/lsp-daemon/dist/client.js +194 -50
- package/packages/lsp-daemon/dist/daemon-client.d.ts +2 -2
- package/packages/lsp-daemon/dist/daemon-client.js +26 -2
- package/packages/lsp-daemon/dist/ensure-daemon.d.ts +5 -4
- package/packages/lsp-daemon/dist/ensure-daemon.js +79 -18
- package/packages/lsp-daemon/dist/index.js +262 -63
- package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
- package/packages/lsp-daemon/dist/proxy.js +79 -23
- package/packages/lsp-tools-mcp/dist/cli.js +82 -20
- package/packages/lsp-tools-mcp/dist/mcp.js +82 -20
- package/packages/lsp-tools-mcp/dist/tools.js +1 -1
- 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 +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +113 -31
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +1 -0
- 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/references/full-workflow.md +1 -1
- 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 +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +10 -2
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +7 -0
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +3 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +50 -33
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
- 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 +35 -4
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/daemon-client.ts
|
|
2
|
-
import { connect
|
|
2
|
+
import { connect } from "node:net";
|
|
3
3
|
import { homedir as homedir3 } from "node:os";
|
|
4
4
|
import { join as join7 } from "node:path";
|
|
5
5
|
|
|
@@ -180,7 +180,7 @@ function isPlainRecord(value) {
|
|
|
180
180
|
// src/ensure-daemon.ts
|
|
181
181
|
import { spawn } from "node:child_process";
|
|
182
182
|
import { closeSync as closeSync2, mkdirSync as mkdirSync2, openSync as openSync2 } from "node:fs";
|
|
183
|
-
import {
|
|
183
|
+
import { Socket } from "node:net";
|
|
184
184
|
import { dirname as dirname3 } from "node:path";
|
|
185
185
|
import { execPath } from "node:process";
|
|
186
186
|
|
|
@@ -587,42 +587,48 @@ class DaemonUnreachableError extends Error {
|
|
|
587
587
|
async function ensureDaemonRunning(paths, deps = defaultEnsureDaemonDeps(), options = {}) {
|
|
588
588
|
const readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
|
|
589
589
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
590
|
-
|
|
590
|
+
const signal = options.signal;
|
|
591
|
+
throwIfAborted(signal);
|
|
592
|
+
if (await awaitWithSignal(deps.probe(paths, signal), signal))
|
|
591
593
|
return;
|
|
594
|
+
throwIfAborted(signal);
|
|
592
595
|
deps.spawnDaemon(paths);
|
|
593
|
-
await waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs);
|
|
596
|
+
await waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs, signal);
|
|
594
597
|
}
|
|
595
|
-
async function waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs) {
|
|
598
|
+
async function waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs, signal) {
|
|
596
599
|
const deadline = deps.now() + readyTimeoutMs;
|
|
597
600
|
for (;; ) {
|
|
598
|
-
|
|
601
|
+
throwIfAborted(signal);
|
|
602
|
+
if (await awaitWithSignal(deps.probe(paths, signal), signal))
|
|
599
603
|
return;
|
|
600
604
|
if (deps.now() >= deadline)
|
|
601
605
|
throw new DaemonUnreachableError(paths.socket);
|
|
602
|
-
await deps.sleep(pollIntervalMs);
|
|
606
|
+
await awaitWithSignal(deps.sleep(pollIntervalMs, signal), signal);
|
|
603
607
|
}
|
|
604
608
|
}
|
|
605
|
-
async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
609
|
+
async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
606
610
|
const token = readAuthToken(paths);
|
|
607
611
|
if (!token)
|
|
608
612
|
return false;
|
|
609
|
-
return await pingDaemon(paths, token, timeoutMs) !== null;
|
|
613
|
+
return await pingDaemon(paths, token, timeoutMs, signal) !== null;
|
|
610
614
|
}
|
|
611
|
-
function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
615
|
+
function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
612
616
|
return new Promise((resolve2) => {
|
|
613
|
-
const socket =
|
|
617
|
+
const socket = new Socket;
|
|
614
618
|
let settled = false;
|
|
619
|
+
let timer;
|
|
615
620
|
const finish = (value) => {
|
|
616
621
|
if (settled)
|
|
617
622
|
return;
|
|
618
623
|
settled = true;
|
|
624
|
+
if (timer !== undefined)
|
|
625
|
+
clearTimeout(timer);
|
|
626
|
+
signal?.removeEventListener("abort", onAbort);
|
|
619
627
|
socket.destroy();
|
|
620
628
|
resolve2(value);
|
|
621
629
|
};
|
|
622
|
-
const
|
|
623
|
-
timer.unref?.();
|
|
630
|
+
const onAbort = () => finish(null);
|
|
624
631
|
const decoder = createLineDecoder((message) => {
|
|
625
|
-
clearTimeout(timer);
|
|
626
632
|
finish(parsePingResponse(message));
|
|
627
633
|
});
|
|
628
634
|
socket.once("connect", () => {
|
|
@@ -630,9 +636,16 @@ function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
|
630
636
|
});
|
|
631
637
|
socket.on("data", (chunk) => decoder.push(chunk));
|
|
632
638
|
socket.once("error", () => {
|
|
633
|
-
clearTimeout(timer);
|
|
634
639
|
finish(null);
|
|
635
640
|
});
|
|
641
|
+
timer = setTimeout(() => finish(null), timeoutMs);
|
|
642
|
+
timer.unref?.();
|
|
643
|
+
if (signal?.aborted) {
|
|
644
|
+
onAbort();
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
648
|
+
socket.connect(paths.socket);
|
|
636
649
|
});
|
|
637
650
|
}
|
|
638
651
|
function spawnDaemonProcess(paths) {
|
|
@@ -650,14 +663,62 @@ function spawnDaemonProcess(paths) {
|
|
|
650
663
|
}
|
|
651
664
|
function defaultEnsureDaemonDeps() {
|
|
652
665
|
return {
|
|
653
|
-
probe: (paths) => probeDaemon(paths),
|
|
666
|
+
probe: (paths, signal) => probeDaemon(paths, PROBE_TIMEOUT_MS, signal),
|
|
654
667
|
spawnDaemon: (paths) => spawnDaemonProcess(paths),
|
|
655
|
-
sleep: (ms) =>
|
|
656
|
-
setTimeout(resolve2, ms);
|
|
657
|
-
}),
|
|
668
|
+
sleep: (ms, signal) => sleepWithSignal(ms, signal),
|
|
658
669
|
now: () => Date.now()
|
|
659
670
|
};
|
|
660
671
|
}
|
|
672
|
+
function sleepWithSignal(ms, signal) {
|
|
673
|
+
return new Promise((resolve2) => {
|
|
674
|
+
let settled = false;
|
|
675
|
+
const finish = () => {
|
|
676
|
+
if (settled)
|
|
677
|
+
return;
|
|
678
|
+
settled = true;
|
|
679
|
+
clearTimeout(timer);
|
|
680
|
+
signal?.removeEventListener("abort", finish);
|
|
681
|
+
resolve2();
|
|
682
|
+
};
|
|
683
|
+
const timer = setTimeout(finish, ms);
|
|
684
|
+
if (signal?.aborted) {
|
|
685
|
+
finish();
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
function awaitWithSignal(promise, signal) {
|
|
692
|
+
if (!signal)
|
|
693
|
+
return promise;
|
|
694
|
+
if (signal.aborted)
|
|
695
|
+
return Promise.reject(abortError(signal));
|
|
696
|
+
return new Promise((resolve2, reject) => {
|
|
697
|
+
let settled = false;
|
|
698
|
+
const finish = (run) => {
|
|
699
|
+
if (settled)
|
|
700
|
+
return;
|
|
701
|
+
settled = true;
|
|
702
|
+
signal.removeEventListener("abort", onAbort);
|
|
703
|
+
run();
|
|
704
|
+
};
|
|
705
|
+
const onAbort = () => finish(() => reject(abortError(signal)));
|
|
706
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
707
|
+
promise.then((value) => finish(() => resolve2(value)), (error) => finish(() => reject(error)));
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
function throwIfAborted(signal) {
|
|
711
|
+
if (signal?.aborted)
|
|
712
|
+
throw abortError(signal);
|
|
713
|
+
}
|
|
714
|
+
function abortError(signal) {
|
|
715
|
+
const reason = signal.reason;
|
|
716
|
+
if (reason instanceof Error)
|
|
717
|
+
return reason;
|
|
718
|
+
const error = new Error(typeof reason === "string" ? reason : "daemon startup cancelled");
|
|
719
|
+
error.name = "AbortError";
|
|
720
|
+
return error;
|
|
721
|
+
}
|
|
661
722
|
function parsePingResponse(message) {
|
|
662
723
|
if (!message || typeof message !== "object" || Array.isArray(message))
|
|
663
724
|
return null;
|
|
@@ -697,6 +758,9 @@ function errorResponse(id, code, message, data) {
|
|
|
697
758
|
function jsonRpcId2(value) {
|
|
698
759
|
return typeof value === "string" || typeof value === "number" || value === null ? value : null;
|
|
699
760
|
}
|
|
761
|
+
function messageFromError(error) {
|
|
762
|
+
return error instanceof Error ? error.message : String(error);
|
|
763
|
+
}
|
|
700
764
|
// ../mcp-stdio-core/src/transport.ts
|
|
701
765
|
var HEADER_SEPARATOR = Buffer.from(`\r
|
|
702
766
|
\r
|
|
@@ -719,16 +783,45 @@ async function* readStdioJsonRpcMessages(input) {
|
|
|
719
783
|
yield parseJsonPayload(trailing, "line");
|
|
720
784
|
}
|
|
721
785
|
}
|
|
722
|
-
function writeStdioJsonRpcResponse(output, response, responseMode) {
|
|
786
|
+
async function writeStdioJsonRpcResponse(output, response, responseMode) {
|
|
723
787
|
const body = JSON.stringify(response);
|
|
724
|
-
|
|
725
|
-
output.write(`Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
788
|
+
const payload = responseMode === "framed" ? `Content-Length: ${Buffer.byteLength(body, "utf8")}\r
|
|
726
789
|
\r
|
|
727
|
-
${body}`
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
790
|
+
${body}` : `${body}
|
|
791
|
+
`;
|
|
792
|
+
await writeChunk(output, payload);
|
|
793
|
+
}
|
|
794
|
+
function writeChunk(output, chunk) {
|
|
795
|
+
return new Promise((resolve2, reject) => {
|
|
796
|
+
let settled = false;
|
|
797
|
+
const onError = (error) => {
|
|
798
|
+
if (settled)
|
|
799
|
+
return;
|
|
800
|
+
settled = true;
|
|
801
|
+
reject(error);
|
|
802
|
+
};
|
|
803
|
+
output.once("error", onError);
|
|
804
|
+
try {
|
|
805
|
+
output.write(chunk, (error) => {
|
|
806
|
+
if (settled)
|
|
807
|
+
return;
|
|
808
|
+
settled = true;
|
|
809
|
+
if (error) {
|
|
810
|
+
queueMicrotask(() => output.removeListener("error", onError));
|
|
811
|
+
reject(error);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
output.removeListener("error", onError);
|
|
815
|
+
resolve2();
|
|
816
|
+
});
|
|
817
|
+
} catch (error) {
|
|
818
|
+
output.removeListener("error", onError);
|
|
819
|
+
if (settled)
|
|
820
|
+
return;
|
|
821
|
+
settled = true;
|
|
822
|
+
reject(error);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
732
825
|
}
|
|
733
826
|
function readNextMessage(buffer) {
|
|
734
827
|
if (buffer.length === 0)
|
|
@@ -824,39 +917,69 @@ async function runJsonRpcStdioServer(config) {
|
|
|
824
917
|
break;
|
|
825
918
|
idleTimer.arm();
|
|
826
919
|
if (message.kind === "parse_error") {
|
|
827
|
-
handleParseError(message, config, log)
|
|
920
|
+
if (!await handleParseError(message, config, log))
|
|
921
|
+
break;
|
|
828
922
|
continue;
|
|
829
923
|
}
|
|
830
|
-
await handleRequest(message, config, log)
|
|
924
|
+
if (!await handleRequest(message, config, log))
|
|
925
|
+
break;
|
|
831
926
|
}
|
|
832
927
|
} finally {
|
|
833
928
|
idleTimer.clear();
|
|
834
929
|
log("stdio_stopped");
|
|
835
930
|
}
|
|
836
931
|
}
|
|
837
|
-
function handleParseError(message, config, log) {
|
|
932
|
+
async function handleParseError(message, config, log) {
|
|
838
933
|
log("parse_error", { message: message.message });
|
|
839
934
|
const response = config.parseErrorResponse?.(message.message) ?? errorResponse(null, -32700, "Parse error", message.message);
|
|
840
|
-
if (response
|
|
841
|
-
|
|
842
|
-
|
|
935
|
+
if (response === undefined)
|
|
936
|
+
return true;
|
|
937
|
+
return writeResponse(response, {
|
|
938
|
+
output: config.output,
|
|
939
|
+
responseMode: message.responseMode,
|
|
940
|
+
log
|
|
941
|
+
});
|
|
843
942
|
}
|
|
844
943
|
async function handleRequest(message, config, log) {
|
|
845
944
|
const parsed = message.payload;
|
|
846
945
|
const id = isPlainRecord(parsed) ? jsonRpcId2(parsed["id"]) : null;
|
|
847
946
|
const method = isPlainRecord(parsed) && typeof parsed["method"] === "string" ? parsed["method"] : null;
|
|
848
947
|
log("request", { id: id === null ? null : String(id), method });
|
|
948
|
+
let response;
|
|
849
949
|
try {
|
|
850
|
-
|
|
851
|
-
if (response === undefined)
|
|
852
|
-
return;
|
|
853
|
-
writeStdioJsonRpcResponse(config.output, response, message.responseMode);
|
|
854
|
-
log("response", { id: String(response.id), method, is_error: response.error !== undefined });
|
|
950
|
+
response = await config.handler(parsed, config.handlerOptions);
|
|
855
951
|
} catch (error) {
|
|
856
952
|
if (config.onHandlerError === undefined)
|
|
857
953
|
throw error;
|
|
858
954
|
config.onHandlerError(error);
|
|
955
|
+
return true;
|
|
859
956
|
}
|
|
957
|
+
if (response === undefined)
|
|
958
|
+
return true;
|
|
959
|
+
if (!await writeResponse(response, {
|
|
960
|
+
output: config.output,
|
|
961
|
+
responseMode: message.responseMode,
|
|
962
|
+
log
|
|
963
|
+
}))
|
|
964
|
+
return false;
|
|
965
|
+
log("response", { id: String(response.id), method, is_error: response.error !== undefined });
|
|
966
|
+
return true;
|
|
967
|
+
}
|
|
968
|
+
async function writeResponse(response, context) {
|
|
969
|
+
try {
|
|
970
|
+
await writeStdioJsonRpcResponse(context.output, response, context.responseMode);
|
|
971
|
+
return true;
|
|
972
|
+
} catch (error) {
|
|
973
|
+
if (!isTerminalOutputError(error))
|
|
974
|
+
throw error;
|
|
975
|
+
context.log("output_error", { message: messageFromError(error) });
|
|
976
|
+
return false;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
function isTerminalOutputError(error) {
|
|
980
|
+
if (!(error instanceof Error) || !("code" in error))
|
|
981
|
+
return false;
|
|
982
|
+
return error.code === "EPIPE" || error.code === "ERR_STREAM_DESTROYED" || error.code === "ERR_STREAM_WRITE_AFTER_END";
|
|
860
983
|
}
|
|
861
984
|
function createIdleTimer(idleTimeoutMs, log, onIdleTimeout) {
|
|
862
985
|
let timer = null;
|
|
@@ -1065,7 +1188,7 @@ class JsonRpcConnection {
|
|
|
1065
1188
|
settled = true;
|
|
1066
1189
|
this.pendingRequests.delete(key);
|
|
1067
1190
|
cleanup();
|
|
1068
|
-
const rejectCancelled = () => reject(
|
|
1191
|
+
const rejectCancelled = () => reject(abortError2(options.signal));
|
|
1069
1192
|
if (!requestWritten) {
|
|
1070
1193
|
cancelAfterWrite = true;
|
|
1071
1194
|
rejectCancelled();
|
|
@@ -1269,7 +1392,7 @@ ${body}`;
|
|
|
1269
1392
|
}
|
|
1270
1393
|
}
|
|
1271
1394
|
}
|
|
1272
|
-
function
|
|
1395
|
+
function abortError2(signal) {
|
|
1273
1396
|
const reason = signal?.reason;
|
|
1274
1397
|
if (reason instanceof Error)
|
|
1275
1398
|
return reason;
|
|
@@ -3547,11 +3670,11 @@ function waitForDiagnosticsActivity(wait, signal) {
|
|
|
3547
3670
|
if (!signal)
|
|
3548
3671
|
return wait;
|
|
3549
3672
|
if (signal.aborted)
|
|
3550
|
-
return Promise.reject(
|
|
3673
|
+
return Promise.reject(abortError3(signal));
|
|
3551
3674
|
return new Promise((resolve7, reject) => {
|
|
3552
3675
|
const onAbort = () => {
|
|
3553
3676
|
signal.removeEventListener("abort", onAbort);
|
|
3554
|
-
reject(
|
|
3677
|
+
reject(abortError3(signal));
|
|
3555
3678
|
};
|
|
3556
3679
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
3557
3680
|
wait.then(() => {
|
|
@@ -3586,7 +3709,7 @@ function preCommitAbortReason(source) {
|
|
|
3586
3709
|
return reason;
|
|
3587
3710
|
return new Error("LSP request cancelled before workspace edit commit");
|
|
3588
3711
|
}
|
|
3589
|
-
function
|
|
3712
|
+
function abortError3(signal) {
|
|
3590
3713
|
const reason = signal.reason;
|
|
3591
3714
|
if (reason instanceof Error)
|
|
3592
3715
|
return reason;
|
|
@@ -3621,7 +3744,7 @@ async function stopClientBestEffort(client) {
|
|
|
3621
3744
|
reportBestEffortCleanupError("client stop", error);
|
|
3622
3745
|
}
|
|
3623
3746
|
}
|
|
3624
|
-
function
|
|
3747
|
+
function awaitWithSignal2(promise, signal) {
|
|
3625
3748
|
if (!signal)
|
|
3626
3749
|
return promise;
|
|
3627
3750
|
return new Promise((resolve7, reject) => {
|
|
@@ -3724,7 +3847,7 @@ class LspManager {
|
|
|
3724
3847
|
if (managed.initPromise) {
|
|
3725
3848
|
managed.pendingWaiters++;
|
|
3726
3849
|
try {
|
|
3727
|
-
await
|
|
3850
|
+
await awaitWithSignal2(managed.initPromise, signal);
|
|
3728
3851
|
} catch (err) {
|
|
3729
3852
|
managed.pendingWaiters--;
|
|
3730
3853
|
await this.tryDeleteIfOrphaned(key, managed);
|
|
@@ -3762,7 +3885,7 @@ class LspManager {
|
|
|
3762
3885
|
};
|
|
3763
3886
|
this.clients.set(key, newManaged);
|
|
3764
3887
|
try {
|
|
3765
|
-
await
|
|
3888
|
+
await awaitWithSignal2(initPromise, signal);
|
|
3766
3889
|
} catch (err) {
|
|
3767
3890
|
newManaged.pendingWaiters--;
|
|
3768
3891
|
if (this.clients.get(key) === newManaged) {
|
|
@@ -4748,7 +4871,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
4748
4871
|
const fileErrors = [];
|
|
4749
4872
|
const maxConcurrency = Math.max(1, options.maxConcurrency ?? DIRECTORY_DIAGNOSTICS_MAX_CONCURRENCY);
|
|
4750
4873
|
options.signal?.throwIfAborted();
|
|
4751
|
-
const client = await manager.getClient(root, server2);
|
|
4874
|
+
const client = await manager.getClient(root, server2, options.signal);
|
|
4752
4875
|
try {
|
|
4753
4876
|
let nextIndex = 0;
|
|
4754
4877
|
const workers = Array.from({ length: Math.min(maxConcurrency, filesToProcess.length) }, async () => {
|
|
@@ -5618,14 +5741,14 @@ class DaemonRequestCancelledError extends DaemonRequestError {
|
|
|
5618
5741
|
async function callToolViaDaemon(name, args, options) {
|
|
5619
5742
|
const context = requireContext(options.context);
|
|
5620
5743
|
const paths = options.paths ?? daemonPaths();
|
|
5621
|
-
const ensure = options.ensure ?? ensureDaemonRunning;
|
|
5744
|
+
const ensure = options.ensure ?? ((ensurePaths, signal) => ensureDaemonRunning(ensurePaths, undefined, signal === undefined ? {} : { signal }));
|
|
5622
5745
|
const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
5623
5746
|
const requestArgs = withContext(args, context);
|
|
5624
5747
|
let lastError;
|
|
5625
5748
|
let authRefreshUsed = false;
|
|
5626
5749
|
for (let attempt = 0;attempt < 3; attempt += 1) {
|
|
5627
5750
|
try {
|
|
5628
|
-
await
|
|
5751
|
+
await ensureDaemonAvailable(paths, ensure, options.signal);
|
|
5629
5752
|
const token = readAuthToken(paths);
|
|
5630
5753
|
if (!token)
|
|
5631
5754
|
throw new DaemonRequestError("daemon auth token missing", false);
|
|
@@ -5667,6 +5790,27 @@ function requireContext(context) {
|
|
|
5667
5790
|
function withContext(args, context) {
|
|
5668
5791
|
return { ...args, [CONTEXT_KEY]: context };
|
|
5669
5792
|
}
|
|
5793
|
+
function ensureDaemonAvailable(paths, ensure, signal) {
|
|
5794
|
+
if (!signal)
|
|
5795
|
+
return ensure(paths);
|
|
5796
|
+
return new Promise((resolve9, reject) => {
|
|
5797
|
+
let settled = false;
|
|
5798
|
+
const finish = (run) => {
|
|
5799
|
+
if (settled)
|
|
5800
|
+
return;
|
|
5801
|
+
settled = true;
|
|
5802
|
+
signal.removeEventListener("abort", onAbort);
|
|
5803
|
+
run();
|
|
5804
|
+
};
|
|
5805
|
+
const onAbort = () => finish(() => reject(new DaemonRequestCancelledError(false)));
|
|
5806
|
+
if (signal.aborted) {
|
|
5807
|
+
onAbort();
|
|
5808
|
+
return;
|
|
5809
|
+
}
|
|
5810
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
5811
|
+
Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() => resolve9()), (error) => finish(() => reject(signal.aborted ? new DaemonRequestCancelledError(false) : error)));
|
|
5812
|
+
});
|
|
5813
|
+
}
|
|
5670
5814
|
function daemonUnreachableResult(paths, error) {
|
|
5671
5815
|
const text2 = [
|
|
5672
5816
|
`LSP daemon unreachable: ${errorText(error)}.`,
|
|
@@ -5680,7 +5824,7 @@ function daemonUnreachableResult(paths, error) {
|
|
|
5680
5824
|
}
|
|
5681
5825
|
function sendToolCall(paths, token, name, args, options) {
|
|
5682
5826
|
return new Promise((resolve9, reject) => {
|
|
5683
|
-
const socket =
|
|
5827
|
+
const socket = connect(paths.socket);
|
|
5684
5828
|
const requestId = allocateProxyRequestId();
|
|
5685
5829
|
let settled = false;
|
|
5686
5830
|
let requestWritten = false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ToolExecutionResult } from "@oh-my-opencode/lsp-core/tools";
|
|
2
1
|
import { type LspRequestContext } from "@oh-my-opencode/lsp-core/request-context";
|
|
2
|
+
import type { ToolExecutionResult } from "@oh-my-opencode/lsp-core/tools";
|
|
3
3
|
import { type DaemonPaths } from "./paths.js";
|
|
4
4
|
export declare class DaemonRequestError extends Error {
|
|
5
5
|
readonly requestWritten: boolean;
|
|
@@ -17,7 +17,7 @@ export interface CallToolOptions {
|
|
|
17
17
|
paths?: DaemonPaths;
|
|
18
18
|
requestTimeoutMs?: number;
|
|
19
19
|
signal?: AbortSignal;
|
|
20
|
-
ensure?: (paths: DaemonPaths) => Promise<void>;
|
|
20
|
+
ensure?: (paths: DaemonPaths, signal?: AbortSignal) => Promise<void>;
|
|
21
21
|
}
|
|
22
22
|
export declare function callToolViaDaemon(name: string, args: Record<string, unknown>, options: CallToolOptions): Promise<ToolExecutionResult>;
|
|
23
23
|
export declare function callDiagnosticsViaDaemon(filePath: string, options: CallToolOptions): Promise<ToolExecutionResult>;
|
|
@@ -32,14 +32,15 @@ export class DaemonRequestCancelledError extends DaemonRequestError {
|
|
|
32
32
|
export async function callToolViaDaemon(name, args, options) {
|
|
33
33
|
const context = requireContext(options.context);
|
|
34
34
|
const paths = options.paths ?? daemonPaths();
|
|
35
|
-
const ensure = options.ensure ??
|
|
35
|
+
const ensure = options.ensure ??
|
|
36
|
+
((ensurePaths, signal) => ensureDaemonRunning(ensurePaths, undefined, signal === undefined ? {} : { signal }));
|
|
36
37
|
const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
37
38
|
const requestArgs = withContext(args, context);
|
|
38
39
|
let lastError;
|
|
39
40
|
let authRefreshUsed = false;
|
|
40
41
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
41
42
|
try {
|
|
42
|
-
await
|
|
43
|
+
await ensureDaemonAvailable(paths, ensure, options.signal);
|
|
43
44
|
const token = readAuthToken(paths);
|
|
44
45
|
if (!token)
|
|
45
46
|
throw new DaemonRequestError("daemon auth token missing", false);
|
|
@@ -82,6 +83,29 @@ function requireContext(context) {
|
|
|
82
83
|
function withContext(args, context) {
|
|
83
84
|
return { ...args, [CONTEXT_KEY]: context };
|
|
84
85
|
}
|
|
86
|
+
function ensureDaemonAvailable(paths, ensure, signal) {
|
|
87
|
+
if (!signal)
|
|
88
|
+
return ensure(paths);
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
let settled = false;
|
|
91
|
+
const finish = (run) => {
|
|
92
|
+
if (settled)
|
|
93
|
+
return;
|
|
94
|
+
settled = true;
|
|
95
|
+
signal.removeEventListener("abort", onAbort);
|
|
96
|
+
run();
|
|
97
|
+
};
|
|
98
|
+
const onAbort = () => finish(() => reject(new DaemonRequestCancelledError(false)));
|
|
99
|
+
if (signal.aborted) {
|
|
100
|
+
onAbort();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
104
|
+
Promise.resolve()
|
|
105
|
+
.then(() => ensure(paths, signal))
|
|
106
|
+
.then(() => finish(() => resolve()), (error) => finish(() => reject(signal.aborted ? new DaemonRequestCancelledError(false) : error)));
|
|
107
|
+
});
|
|
108
|
+
}
|
|
85
109
|
function daemonUnreachableResult(paths, error) {
|
|
86
110
|
const text = [
|
|
87
111
|
`LSP daemon unreachable: ${errorText(error)}.`,
|
|
@@ -6,18 +6,19 @@ export declare class DaemonUnreachableError extends Error {
|
|
|
6
6
|
constructor(socketPath: string);
|
|
7
7
|
}
|
|
8
8
|
export interface EnsureDaemonDeps {
|
|
9
|
-
probe(paths: DaemonPaths): Promise<boolean>;
|
|
9
|
+
probe(paths: DaemonPaths, signal?: AbortSignal): Promise<boolean>;
|
|
10
10
|
spawnDaemon(paths: DaemonPaths): void;
|
|
11
|
-
sleep(ms: number): Promise<void>;
|
|
11
|
+
sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
12
12
|
now(): number;
|
|
13
13
|
}
|
|
14
14
|
export interface EnsureDaemonOptions {
|
|
15
15
|
readyTimeoutMs?: number;
|
|
16
16
|
pollIntervalMs?: number;
|
|
17
|
+
readonly signal?: AbortSignal;
|
|
17
18
|
}
|
|
18
19
|
export declare function ensureDaemonRunning(paths: DaemonPaths, deps?: EnsureDaemonDeps, options?: EnsureDaemonOptions): Promise<void>;
|
|
19
|
-
export declare function probeDaemon(paths: DaemonPaths, timeoutMs?: number): Promise<boolean>;
|
|
20
|
-
export declare function pingDaemon(paths: DaemonPaths, token: string, timeoutMs?: number): Promise<OwnerPing | null>;
|
|
20
|
+
export declare function probeDaemon(paths: DaemonPaths, timeoutMs?: number, signal?: AbortSignal): Promise<boolean>;
|
|
21
|
+
export declare function pingDaemon(paths: DaemonPaths, token: string, timeoutMs?: number, signal?: AbortSignal): Promise<OwnerPing | null>;
|
|
21
22
|
export declare function spawnDaemonProcess(paths: DaemonPaths): void;
|
|
22
23
|
export declare function resolveDaemonCliPath(env?: NodeJS.ProcessEnv, defaults?: DaemonRuntimeDefaults): string;
|
|
23
24
|
export declare function defaultEnsureDaemonDeps(): EnsureDaemonDeps;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { closeSync, mkdirSync, openSync } from "node:fs";
|
|
3
|
-
import {
|
|
3
|
+
import { Socket } from "node:net";
|
|
4
4
|
import { dirname } from "node:path";
|
|
5
5
|
import { execPath } from "node:process";
|
|
6
6
|
import { authEnvelope, readAuthToken } from "./ipc-protocol.js";
|
|
@@ -20,42 +20,48 @@ export class DaemonUnreachableError extends Error {
|
|
|
20
20
|
export async function ensureDaemonRunning(paths, deps = defaultEnsureDaemonDeps(), options = {}) {
|
|
21
21
|
const readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
|
|
22
22
|
const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
23
|
-
|
|
23
|
+
const signal = options.signal;
|
|
24
|
+
throwIfAborted(signal);
|
|
25
|
+
if (await awaitWithSignal(deps.probe(paths, signal), signal))
|
|
24
26
|
return;
|
|
27
|
+
throwIfAborted(signal);
|
|
25
28
|
deps.spawnDaemon(paths);
|
|
26
|
-
await waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs);
|
|
29
|
+
await waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs, signal);
|
|
27
30
|
}
|
|
28
|
-
async function waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs) {
|
|
31
|
+
async function waitUntilReachable(paths, deps, readyTimeoutMs, pollIntervalMs, signal) {
|
|
29
32
|
const deadline = deps.now() + readyTimeoutMs;
|
|
30
33
|
for (;;) {
|
|
31
|
-
|
|
34
|
+
throwIfAborted(signal);
|
|
35
|
+
if (await awaitWithSignal(deps.probe(paths, signal), signal))
|
|
32
36
|
return;
|
|
33
37
|
if (deps.now() >= deadline)
|
|
34
38
|
throw new DaemonUnreachableError(paths.socket);
|
|
35
|
-
await deps.sleep(pollIntervalMs);
|
|
39
|
+
await awaitWithSignal(deps.sleep(pollIntervalMs, signal), signal);
|
|
36
40
|
}
|
|
37
41
|
}
|
|
38
|
-
export async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
42
|
+
export async function probeDaemon(paths, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
39
43
|
const token = readAuthToken(paths);
|
|
40
44
|
if (!token)
|
|
41
45
|
return false;
|
|
42
|
-
return (await pingDaemon(paths, token, timeoutMs)) !== null;
|
|
46
|
+
return (await pingDaemon(paths, token, timeoutMs, signal)) !== null;
|
|
43
47
|
}
|
|
44
|
-
export function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
48
|
+
export function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS, signal) {
|
|
45
49
|
return new Promise((resolve) => {
|
|
46
|
-
const socket =
|
|
50
|
+
const socket = new Socket();
|
|
47
51
|
let settled = false;
|
|
52
|
+
let timer;
|
|
48
53
|
const finish = (value) => {
|
|
49
54
|
if (settled)
|
|
50
55
|
return;
|
|
51
56
|
settled = true;
|
|
57
|
+
if (timer !== undefined)
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
signal?.removeEventListener("abort", onAbort);
|
|
52
60
|
socket.destroy();
|
|
53
61
|
resolve(value);
|
|
54
62
|
};
|
|
55
|
-
const
|
|
56
|
-
timer.unref?.();
|
|
63
|
+
const onAbort = () => finish(null);
|
|
57
64
|
const decoder = createLineDecoder((message) => {
|
|
58
|
-
clearTimeout(timer);
|
|
59
65
|
finish(parsePingResponse(message));
|
|
60
66
|
});
|
|
61
67
|
socket.once("connect", () => {
|
|
@@ -63,9 +69,16 @@ export function pingDaemon(paths, token, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
|
63
69
|
});
|
|
64
70
|
socket.on("data", (chunk) => decoder.push(chunk));
|
|
65
71
|
socket.once("error", () => {
|
|
66
|
-
clearTimeout(timer);
|
|
67
72
|
finish(null);
|
|
68
73
|
});
|
|
74
|
+
timer = setTimeout(() => finish(null), timeoutMs);
|
|
75
|
+
timer.unref?.();
|
|
76
|
+
if (signal?.aborted) {
|
|
77
|
+
onAbort();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
81
|
+
socket.connect(paths.socket);
|
|
69
82
|
});
|
|
70
83
|
}
|
|
71
84
|
export function spawnDaemonProcess(paths) {
|
|
@@ -87,14 +100,62 @@ export function resolveDaemonCliPath(env = process.env, defaults = packagedRunti
|
|
|
87
100
|
}
|
|
88
101
|
export function defaultEnsureDaemonDeps() {
|
|
89
102
|
return {
|
|
90
|
-
probe: (paths) => probeDaemon(paths),
|
|
103
|
+
probe: (paths, signal) => probeDaemon(paths, PROBE_TIMEOUT_MS, signal),
|
|
91
104
|
spawnDaemon: (paths) => spawnDaemonProcess(paths),
|
|
92
|
-
sleep: (ms) =>
|
|
93
|
-
setTimeout(resolve, ms);
|
|
94
|
-
}),
|
|
105
|
+
sleep: (ms, signal) => sleepWithSignal(ms, signal),
|
|
95
106
|
now: () => Date.now(),
|
|
96
107
|
};
|
|
97
108
|
}
|
|
109
|
+
function sleepWithSignal(ms, signal) {
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
let settled = false;
|
|
112
|
+
const finish = () => {
|
|
113
|
+
if (settled)
|
|
114
|
+
return;
|
|
115
|
+
settled = true;
|
|
116
|
+
clearTimeout(timer);
|
|
117
|
+
signal?.removeEventListener("abort", finish);
|
|
118
|
+
resolve();
|
|
119
|
+
};
|
|
120
|
+
const timer = setTimeout(finish, ms);
|
|
121
|
+
if (signal?.aborted) {
|
|
122
|
+
finish();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
signal?.addEventListener("abort", finish, { once: true });
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function awaitWithSignal(promise, signal) {
|
|
129
|
+
if (!signal)
|
|
130
|
+
return promise;
|
|
131
|
+
if (signal.aborted)
|
|
132
|
+
return Promise.reject(abortError(signal));
|
|
133
|
+
return new Promise((resolve, reject) => {
|
|
134
|
+
let settled = false;
|
|
135
|
+
const finish = (run) => {
|
|
136
|
+
if (settled)
|
|
137
|
+
return;
|
|
138
|
+
settled = true;
|
|
139
|
+
signal.removeEventListener("abort", onAbort);
|
|
140
|
+
run();
|
|
141
|
+
};
|
|
142
|
+
const onAbort = () => finish(() => reject(abortError(signal)));
|
|
143
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
144
|
+
promise.then((value) => finish(() => resolve(value)), (error) => finish(() => reject(error)));
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function throwIfAborted(signal) {
|
|
148
|
+
if (signal?.aborted)
|
|
149
|
+
throw abortError(signal);
|
|
150
|
+
}
|
|
151
|
+
function abortError(signal) {
|
|
152
|
+
const reason = signal.reason;
|
|
153
|
+
if (reason instanceof Error)
|
|
154
|
+
return reason;
|
|
155
|
+
const error = new Error(typeof reason === "string" ? reason : "daemon startup cancelled");
|
|
156
|
+
error.name = "AbortError";
|
|
157
|
+
return error;
|
|
158
|
+
}
|
|
98
159
|
function parsePingResponse(message) {
|
|
99
160
|
if (!message || typeof message !== "object" || Array.isArray(message))
|
|
100
161
|
return null;
|