@oscharko-dev/keiko-server 0.2.8 → 0.2.9
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/dist/.tsbuildinfo +1 -1
- package/dist/chat-handlers.d.ts +18 -2
- package/dist/chat-handlers.d.ts.map +1 -1
- package/dist/chat-handlers.js +185 -3
- package/dist/command-runner-errors.d.ts +17 -0
- package/dist/command-runner-errors.d.ts.map +1 -0
- package/dist/command-runner-errors.js +37 -0
- package/dist/command-runner-evidence.d.ts +23 -0
- package/dist/command-runner-evidence.d.ts.map +1 -0
- package/dist/command-runner-evidence.js +69 -0
- package/dist/command-runner-routes.d.ts +7 -0
- package/dist/command-runner-routes.d.ts.map +1 -0
- package/dist/command-runner-routes.js +175 -0
- package/dist/command-runner.d.ts +29 -0
- package/dist/command-runner.d.ts.map +1 -0
- package/dist/command-runner.js +348 -0
- package/dist/conversation-prompt.d.ts +2 -2
- package/dist/conversation-prompt.d.ts.map +1 -1
- package/dist/conversation-prompt.js +17 -1
- package/dist/csp.d.ts.map +1 -1
- package/dist/csp.js +3 -0
- package/dist/deps.d.ts +27 -1
- package/dist/deps.d.ts.map +1 -1
- package/dist/deps.js +288 -13
- package/dist/discussion-prompt.d.ts +4 -0
- package/dist/discussion-prompt.d.ts.map +1 -0
- package/dist/discussion-prompt.js +19 -0
- package/dist/editor/agentActionAudit.d.ts +18 -0
- package/dist/editor/agentActionAudit.d.ts.map +1 -0
- package/dist/editor/agentActionAudit.js +80 -0
- package/dist/editor/agentRoutes.d.ts +1 -0
- package/dist/editor/agentRoutes.d.ts.map +1 -1
- package/dist/editor/agentRoutes.js +292 -55
- package/dist/editor/agentSessionRegistry.d.ts +35 -0
- package/dist/editor/agentSessionRegistry.d.ts.map +1 -0
- package/dist/editor/agentSessionRegistry.js +243 -0
- package/dist/editor/completionRoutes.d.ts.map +1 -1
- package/dist/editor/completionRoutes.js +5 -10
- package/dist/editor/languageRoutes.d.ts +12 -1
- package/dist/editor/languageRoutes.d.ts.map +1 -1
- package/dist/editor/languageRoutes.js +71 -8
- package/dist/editor/languageService.d.ts +3 -2
- package/dist/editor/languageService.d.ts.map +1 -1
- package/dist/editor/languageService.js +41 -3
- package/dist/editor/languageServiceHost.d.ts.map +1 -1
- package/dist/editor/languageServiceHost.js +2 -2
- package/dist/editor/lsp/hostLanguageOperation.d.ts +17 -0
- package/dist/editor/lsp/hostLanguageOperation.d.ts.map +1 -0
- package/dist/editor/lsp/hostLanguageOperation.js +436 -0
- package/dist/editor/lsp/hostLanguageProviders.d.ts +26 -0
- package/dist/editor/lsp/hostLanguageProviders.d.ts.map +1 -0
- package/dist/editor/lsp/hostLanguageProviders.js +161 -0
- package/dist/editor/lsp/lspFrameCodec.d.ts +13 -0
- package/dist/editor/lsp/lspFrameCodec.d.ts.map +1 -0
- package/dist/editor/lsp/lspFrameCodec.js +164 -0
- package/dist/editor/lsp/lspJsonRpcClient.d.ts +34 -0
- package/dist/editor/lsp/lspJsonRpcClient.d.ts.map +1 -0
- package/dist/editor/lsp/lspJsonRpcClient.js +173 -0
- package/dist/editor/lsp/lspLanguageProvider.d.ts +7 -0
- package/dist/editor/lsp/lspLanguageProvider.d.ts.map +1 -0
- package/dist/editor/lsp/lspLanguageProvider.js +29 -0
- package/dist/editor/lsp/lspLifecycleLedger.d.ts +5 -0
- package/dist/editor/lsp/lspLifecycleLedger.d.ts.map +1 -0
- package/dist/editor/lsp/lspLifecycleLedger.js +37 -0
- package/dist/editor/lsp/lspNodeAdapter.d.ts +31 -0
- package/dist/editor/lsp/lspNodeAdapter.d.ts.map +1 -0
- package/dist/editor/lsp/lspNodeAdapter.js +230 -0
- package/dist/editor/lsp/lspProcessManager.d.ts +24 -0
- package/dist/editor/lsp/lspProcessManager.d.ts.map +1 -0
- package/dist/editor/lsp/lspProcessManager.js +255 -0
- package/dist/editor/lsp/lspRestartThrottle.d.ts +6 -0
- package/dist/editor/lsp/lspRestartThrottle.d.ts.map +1 -0
- package/dist/editor/lsp/lspRestartThrottle.js +24 -0
- package/dist/editor/lsp/lspStatusRoute.d.ts +8 -0
- package/dist/editor/lsp/lspStatusRoute.d.ts.map +1 -0
- package/dist/editor/lsp/lspStatusRoute.js +22 -0
- package/dist/editor/lsp/lspTransport.d.ts +19 -0
- package/dist/editor/lsp/lspTransport.d.ts.map +1 -0
- package/dist/editor/lsp/lspTransport.js +55 -0
- package/dist/editor/lsp/testing/fakeLspProcess.d.ts +23 -0
- package/dist/editor/lsp/testing/fakeLspProcess.d.ts.map +1 -0
- package/dist/editor/lsp/testing/fakeLspProcess.js +132 -0
- package/dist/files.d.ts +45 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +631 -7
- package/dist/gateway-readiness.js +3 -3
- package/dist/gateway-setup.d.ts +2 -0
- package/dist/gateway-setup.d.ts.map +1 -1
- package/dist/gateway-setup.js +275 -11
- package/dist/gitDelivery/actionSheetProjection.d.ts +30 -0
- package/dist/gitDelivery/actionSheetProjection.d.ts.map +1 -0
- package/dist/gitDelivery/actionSheetProjection.js +206 -0
- package/dist/gitDelivery/actionSheetRoutes.d.ts +29 -0
- package/dist/gitDelivery/actionSheetRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/actionSheetRoutes.js +293 -0
- package/dist/gitDelivery/agentOperationsRoutes.d.ts +33 -0
- package/dist/gitDelivery/agentOperationsRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/agentOperationsRoutes.js +405 -0
- package/dist/gitDelivery/commitRoutes.d.ts +23 -0
- package/dist/gitDelivery/commitRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/commitRoutes.js +204 -0
- package/dist/gitDelivery/evidenceRoutes.d.ts +9 -0
- package/dist/gitDelivery/evidenceRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/evidenceRoutes.js +101 -0
- package/dist/gitDelivery/execution.d.ts +38 -0
- package/dist/gitDelivery/execution.d.ts.map +1 -0
- package/dist/gitDelivery/execution.js +117 -0
- package/dist/gitDelivery/localMutationRoutes.d.ts +30 -0
- package/dist/gitDelivery/localMutationRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/localMutationRoutes.js +165 -0
- package/dist/gitDelivery/mergeExecution.d.ts +63 -0
- package/dist/gitDelivery/mergeExecution.d.ts.map +1 -0
- package/dist/gitDelivery/mergeExecution.js +168 -0
- package/dist/gitDelivery/mergeRoutes.d.ts +12 -0
- package/dist/gitDelivery/mergeRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/mergeRoutes.js +218 -0
- package/dist/gitDelivery/mutationEvidenceLedger.d.ts +23 -0
- package/dist/gitDelivery/mutationEvidenceLedger.d.ts.map +1 -0
- package/dist/gitDelivery/mutationEvidenceLedger.js +87 -0
- package/dist/gitDelivery/prExecution.d.ts +54 -0
- package/dist/gitDelivery/prExecution.d.ts.map +1 -0
- package/dist/gitDelivery/prExecution.js +192 -0
- package/dist/gitDelivery/prRoutes.d.ts +12 -0
- package/dist/gitDelivery/prRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/prRoutes.js +256 -0
- package/dist/gitDelivery/pushExecution.d.ts +43 -0
- package/dist/gitDelivery/pushExecution.d.ts.map +1 -0
- package/dist/gitDelivery/pushExecution.js +124 -0
- package/dist/gitDelivery/pushRoutes.d.ts +12 -0
- package/dist/gitDelivery/pushRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/pushRoutes.js +200 -0
- package/dist/gitDelivery/requestGuards.d.ts +15 -0
- package/dist/gitDelivery/requestGuards.d.ts.map +1 -0
- package/dist/gitDelivery/requestGuards.js +97 -0
- package/dist/gitDelivery/syncEvidence.d.ts +37 -0
- package/dist/gitDelivery/syncEvidence.d.ts.map +1 -0
- package/dist/gitDelivery/syncEvidence.js +85 -0
- package/dist/gitDelivery/syncExecution.d.ts +30 -0
- package/dist/gitDelivery/syncExecution.d.ts.map +1 -0
- package/dist/gitDelivery/syncExecution.js +266 -0
- package/dist/gitDelivery/syncRoutes.d.ts +13 -0
- package/dist/gitDelivery/syncRoutes.d.ts.map +1 -0
- package/dist/gitDelivery/syncRoutes.js +200 -0
- package/dist/gitPorcelainStatus.d.ts +15 -0
- package/dist/gitPorcelainStatus.d.ts.map +1 -0
- package/dist/gitPorcelainStatus.js +104 -0
- package/dist/gitRepositoryReads.d.ts +10 -0
- package/dist/gitRepositoryReads.d.ts.map +1 -0
- package/dist/gitRepositoryReads.js +314 -0
- package/dist/gitRepositoryRoutes.d.ts +7 -0
- package/dist/gitRepositoryRoutes.d.ts.map +1 -0
- package/dist/gitRepositoryRoutes.js +221 -0
- package/dist/gitRoutes.d.ts +66 -0
- package/dist/gitRoutes.d.ts.map +1 -0
- package/dist/gitRoutes.js +543 -0
- package/dist/governed-workflow.d.ts +2 -0
- package/dist/governed-workflow.d.ts.map +1 -1
- package/dist/governed-workflow.js +4 -0
- package/dist/grounded-qa.d.ts +11 -0
- package/dist/grounded-qa.d.ts.map +1 -1
- package/dist/grounded-qa.js +13 -4
- package/dist/headers.d.ts +4 -1
- package/dist/headers.d.ts.map +1 -1
- package/dist/headers.js +11 -4
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts +1 -1
- package/dist/qualityIntelligence/figmaSnapshotRoutes.d.ts.map +1 -1
- package/dist/qualityIntelligence/figmaSnapshotRoutes.js +1 -1
- package/dist/read-handlers.d.ts +5 -0
- package/dist/read-handlers.d.ts.map +1 -1
- package/dist/read-handlers.js +57 -1
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +259 -6
- package/dist/run-engine.d.ts.map +1 -1
- package/dist/run-engine.js +3 -0
- package/dist/run-handlers.d.ts.map +1 -1
- package/dist/run-handlers.js +74 -4
- package/dist/run-request.d.ts +11 -0
- package/dist/run-request.d.ts.map +1 -1
- package/dist/run-request.js +158 -10
- package/dist/runtime/capabilityDetector.d.ts +38 -0
- package/dist/runtime/capabilityDetector.d.ts.map +1 -0
- package/dist/runtime/capabilityDetector.js +443 -0
- package/dist/runtime/capabilityRoutes.d.ts +9 -0
- package/dist/runtime/capabilityRoutes.d.ts.map +1 -0
- package/dist/runtime/capabilityRoutes.js +45 -0
- package/dist/runtime/containerEngineDetector.d.ts +17 -0
- package/dist/runtime/containerEngineDetector.d.ts.map +1 -0
- package/dist/runtime/containerEngineDetector.js +222 -0
- package/dist/runtime/containerRoutes.d.ts +8 -0
- package/dist/runtime/containerRoutes.d.ts.map +1 -0
- package/dist/runtime/containerRoutes.js +207 -0
- package/dist/runtime/containerRunner-errors.d.ts +18 -0
- package/dist/runtime/containerRunner-errors.d.ts.map +1 -0
- package/dist/runtime/containerRunner-errors.js +42 -0
- package/dist/runtime/containerRunner-evidence.d.ts +24 -0
- package/dist/runtime/containerRunner-evidence.d.ts.map +1 -0
- package/dist/runtime/containerRunner-evidence.js +74 -0
- package/dist/runtime/containerRunner.d.ts +37 -0
- package/dist/runtime/containerRunner.d.ts.map +1 -0
- package/dist/runtime/containerRunner.js +443 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +24 -4
- package/dist/store/schema.d.ts +1 -1
- package/dist/store/schema.d.ts.map +1 -1
- package/dist/store/schema.js +62 -1
- package/dist/task-workspace/active-store.d.ts +21 -0
- package/dist/task-workspace/active-store.d.ts.map +1 -0
- package/dist/task-workspace/active-store.js +55 -0
- package/dist/task-workspace/authorization.d.ts +7 -0
- package/dist/task-workspace/authorization.d.ts.map +1 -0
- package/dist/task-workspace/authorization.js +54 -0
- package/dist/task-workspace/binding.d.ts +3 -0
- package/dist/task-workspace/binding.d.ts.map +1 -0
- package/dist/task-workspace/binding.js +22 -0
- package/dist/task-workspace/cleanup.d.ts +4 -0
- package/dist/task-workspace/cleanup.d.ts.map +1 -0
- package/dist/task-workspace/cleanup.js +428 -0
- package/dist/task-workspace/errors.d.ts +14 -0
- package/dist/task-workspace/errors.d.ts.map +1 -0
- package/dist/task-workspace/errors.js +81 -0
- package/dist/task-workspace/evidence.d.ts +32 -0
- package/dist/task-workspace/evidence.d.ts.map +1 -0
- package/dist/task-workspace/evidence.js +52 -0
- package/dist/task-workspace/field-safety.d.ts +3 -0
- package/dist/task-workspace/field-safety.d.ts.map +1 -0
- package/dist/task-workspace/field-safety.js +42 -0
- package/dist/task-workspace/health.d.ts +4 -0
- package/dist/task-workspace/health.d.ts.map +1 -0
- package/dist/task-workspace/health.js +163 -0
- package/dist/task-workspace/lifecycle.d.ts +3 -0
- package/dist/task-workspace/lifecycle.d.ts.map +1 -0
- package/dist/task-workspace/lifecycle.js +248 -0
- package/dist/task-workspace/locks.d.ts +13 -0
- package/dist/task-workspace/locks.d.ts.map +1 -0
- package/dist/task-workspace/locks.js +44 -0
- package/dist/task-workspace/managed-root.d.ts +7 -0
- package/dist/task-workspace/managed-root.d.ts.map +1 -0
- package/dist/task-workspace/managed-root.js +98 -0
- package/dist/task-workspace/mutex.d.ts +8 -0
- package/dist/task-workspace/mutex.d.ts.map +1 -0
- package/dist/task-workspace/mutex.js +82 -0
- package/dist/task-workspace/naming.d.ts +15 -0
- package/dist/task-workspace/naming.d.ts.map +1 -0
- package/dist/task-workspace/naming.js +0 -0
- package/dist/task-workspace/provisioning.d.ts +3 -0
- package/dist/task-workspace/provisioning.d.ts.map +1 -0
- package/dist/task-workspace/provisioning.js +528 -0
- package/dist/task-workspace/reconciliation.d.ts +15 -0
- package/dist/task-workspace/reconciliation.d.ts.map +1 -0
- package/dist/task-workspace/reconciliation.js +274 -0
- package/dist/task-workspace/repair.d.ts +3 -0
- package/dist/task-workspace/repair.d.ts.map +1 -0
- package/dist/task-workspace/repair.js +286 -0
- package/dist/task-workspace/routes.d.ts +19 -0
- package/dist/task-workspace/routes.d.ts.map +1 -0
- package/dist/task-workspace/routes.js +481 -0
- package/dist/task-workspace/store.d.ts +12 -0
- package/dist/task-workspace/store.d.ts.map +1 -0
- package/dist/task-workspace/store.js +128 -0
- package/dist/task-workspace/types.d.ts +170 -0
- package/dist/task-workspace/types.d.ts.map +1 -0
- package/dist/task-workspace/types.js +5 -0
- package/dist/voice-action-governance.d.ts +23 -0
- package/dist/voice-action-governance.d.ts.map +1 -0
- package/dist/voice-action-governance.js +126 -0
- package/dist/voice-handlers.d.ts +6 -0
- package/dist/voice-handlers.d.ts.map +1 -0
- package/dist/voice-handlers.js +570 -0
- package/dist/voice-realtime-grounded-tool.d.ts +31 -0
- package/dist/voice-realtime-grounded-tool.d.ts.map +1 -0
- package/dist/voice-realtime-grounded-tool.js +322 -0
- package/dist/voice-realtime.d.ts +69 -0
- package/dist/voice-realtime.d.ts.map +1 -0
- package/dist/voice-realtime.js +787 -0
- package/dist/workspace-state-handlers.d.ts +5 -0
- package/dist/workspace-state-handlers.d.ts.map +1 -0
- package/dist/workspace-state-handlers.js +106 -0
- package/package.json +20 -19
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
// Byte-level LSP base-protocol codec (Issue #1381, Epic #1491, ADR-0069 D3/I3). The reader consumes
|
|
2
|
+
// raw stdout chunks, splits `Content-Length: N\r\n\r\n` + exactly N bytes, and yields frame body
|
|
3
|
+
// buffers. It works purely at the Buffer level: headers are ASCII, but bodies are decoded as JSON
|
|
4
|
+
// only by the caller after exactly N bytes are accumulated, so a multi-byte UTF-8 sequence that
|
|
5
|
+
// straddles a chunk boundary is never split mid-character.
|
|
6
|
+
//
|
|
7
|
+
// ADR-0069 I3 (stream boundaries never buffer an oversized body): when the declared Content-Length
|
|
8
|
+
// exceeds `maxFrameBytes` the reader rejects the frame BEFORE reading the body and never accumulates
|
|
9
|
+
// the oversized payload. A missing or garbled header is rejected as MALFORMED_HEADER. The pre-header
|
|
10
|
+
// region is itself capped at `MAX_HEADER_BYTES` so a server that floods bytes WITHOUT ever sending a
|
|
11
|
+
// `\r\n\r\n` terminator cannot grow the accumulation buffer unbounded (OOM); such a stream is rejected
|
|
12
|
+
// as MALFORMED_HEADER. Incoming chunks accumulate in a list and are concatenated at most once per
|
|
13
|
+
// yielded frame, so a large body arriving in many small chunks costs O(n) total copy, not O(n^2).
|
|
14
|
+
import { parseLspFrameHeader } from "@oscharko-dev/keiko-contracts";
|
|
15
|
+
const HEADER_DELIMITER = Buffer.from("\r\n\r\n", "ascii");
|
|
16
|
+
const HEADER_LINE_DELIMITER = "\r\n";
|
|
17
|
+
// Per LSP-norm header bound: a well-formed `Content-Length` (+ optional `Content-Type`) header block
|
|
18
|
+
// is far under 8 KiB. Capping the pre-terminator region at this constant (independent of the body cap
|
|
19
|
+
// `maxFrameBytes`, which governs the declared body) means a delimiter-less flood is rejected promptly
|
|
20
|
+
// instead of accumulating without limit. 8192 is the conventional LSP base-protocol header ceiling.
|
|
21
|
+
const MAX_HEADER_BYTES = 8192;
|
|
22
|
+
// Thrown by the frame reader when a frame cannot be read safely. The `reason` is a content-free enum
|
|
23
|
+
// member; no header text or body bytes are carried on the error (ADR-0069 D6).
|
|
24
|
+
export class LspFrameRejectError extends Error {
|
|
25
|
+
reason;
|
|
26
|
+
constructor(reason) {
|
|
27
|
+
super(reason);
|
|
28
|
+
this.name = "LspFrameRejectError";
|
|
29
|
+
this.reason = reason;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Locates the `\r\n\r\n` header terminator in the accumulated buffer and parses the Content-Length.
|
|
33
|
+
// Returns null when the terminator is not yet present (more chunks needed). Throws LspFrameRejectError
|
|
34
|
+
// when a header is present but malformed, so the caller surfaces MALFORMED_HEADER deterministically.
|
|
35
|
+
function parseHeader(buffer) {
|
|
36
|
+
const delimiterIndex = buffer.indexOf(HEADER_DELIMITER);
|
|
37
|
+
if (delimiterIndex === -1) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const headerBlock = buffer.subarray(0, delimiterIndex).toString("ascii");
|
|
41
|
+
const contentLength = readContentLength(headerBlock);
|
|
42
|
+
if (contentLength === null) {
|
|
43
|
+
throw new LspFrameRejectError("MALFORMED_HEADER");
|
|
44
|
+
}
|
|
45
|
+
return { contentLength, headerEndIndex: delimiterIndex + HEADER_DELIMITER.length };
|
|
46
|
+
}
|
|
47
|
+
// Scans the header block lines for a parseable Content-Length. Other header lines (LSP allows an
|
|
48
|
+
// optional Content-Type) are ignored; absence of a valid Content-Length is malformed.
|
|
49
|
+
function readContentLength(headerBlock) {
|
|
50
|
+
const lines = headerBlock.split(HEADER_LINE_DELIMITER);
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
const parsed = parseLspFrameHeader(line);
|
|
53
|
+
if (parsed !== null) {
|
|
54
|
+
return parsed.contentLength;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
// Reads framed bodies from a byte source, yielding one Buffer per frame. Async-generator semantics
|
|
60
|
+
// give natural backpressure: a frame is yielded only once its full body is buffered, and the next
|
|
61
|
+
// source pull happens when the consumer requests the next frame.
|
|
62
|
+
//
|
|
63
|
+
// Accumulation is a list of chunks plus a running byte total; the list is concatenated into a single
|
|
64
|
+
// contiguous buffer only when a header terminator is present (so a complete header+body frame can be
|
|
65
|
+
// sliced). A large body delivered in many small chunks therefore incurs one O(n) concat at yield time
|
|
66
|
+
// rather than an O(n^2) concat-on-every-chunk. Before any terminator arrives, the accumulated size is
|
|
67
|
+
// bounded by `MAX_HEADER_BYTES`; exceeding it without a terminator is a MALFORMED_HEADER reject.
|
|
68
|
+
export async function* createLspFrameReader(source, maxFrameBytes) {
|
|
69
|
+
const pending = createPendingBuffer();
|
|
70
|
+
for await (const chunk of source) {
|
|
71
|
+
pending.push(chunk);
|
|
72
|
+
yield* drainFrames(pending, maxFrameBytes);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const EMPTY = Buffer.alloc(0);
|
|
76
|
+
const DELIMITER_OVERLAP = HEADER_DELIMITER.length - 1;
|
|
77
|
+
function createPendingBuffer() {
|
|
78
|
+
let chunks = [];
|
|
79
|
+
let totalBytes = 0;
|
|
80
|
+
let delimiterFound = false;
|
|
81
|
+
// The last `DELIMITER_OVERLAP` bytes of already-scanned data, retained so a terminator straddling a
|
|
82
|
+
// chunk boundary is detected by scanning only `tail + newChunk` — never a full re-concat (O(n)).
|
|
83
|
+
let tail = EMPTY;
|
|
84
|
+
const scanIncoming = (chunk) => {
|
|
85
|
+
const window = tail.length === 0 ? chunk : Buffer.concat([tail, chunk]);
|
|
86
|
+
if (window.indexOf(HEADER_DELIMITER) !== -1) {
|
|
87
|
+
delimiterFound = true;
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
tail = window.subarray(Math.max(0, window.length - DELIMITER_OVERLAP));
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
push: (chunk) => {
|
|
94
|
+
if (chunk.length === 0)
|
|
95
|
+
return;
|
|
96
|
+
chunks.push(chunk);
|
|
97
|
+
totalBytes += chunk.length;
|
|
98
|
+
if (!delimiterFound) {
|
|
99
|
+
scanIncoming(chunk);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
byteLength: () => totalBytes,
|
|
103
|
+
hasHeaderDelimiter: () => delimiterFound,
|
|
104
|
+
coalesce: () => (chunks.length === 1 ? (chunks[0] ?? EMPTY) : Buffer.concat(chunks)),
|
|
105
|
+
reset: (rest) => {
|
|
106
|
+
chunks = rest.length === 0 ? [] : [rest];
|
|
107
|
+
totalBytes = rest.length;
|
|
108
|
+
tail = EMPTY;
|
|
109
|
+
delimiterFound = rest.indexOf(HEADER_DELIMITER) !== -1;
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// Rejects a delimiter-less accumulation that has grown past the header cap (ADR-0069 I3): a server
|
|
114
|
+
// that floods bytes without ever sending `\r\n\r\n` would otherwise grow the buffer unbounded.
|
|
115
|
+
function guardHeaderBound(pending) {
|
|
116
|
+
if (!pending.hasHeaderDelimiter() && pending.byteLength() > MAX_HEADER_BYTES) {
|
|
117
|
+
throw new LspFrameRejectError("MALFORMED_HEADER");
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Attempts to slice one complete frame from the head of the buffer. Returns null when the full
|
|
121
|
+
// declared body is not yet present. Enforces the oversized cap at the header boundary, before any
|
|
122
|
+
// body byte is required (ADR-0069 I3). The caller only invokes this once a terminator is present, so
|
|
123
|
+
// `parseHeader` never returns null here.
|
|
124
|
+
function takeFrame(buffer, maxFrameBytes) {
|
|
125
|
+
const header = parseHeader(buffer);
|
|
126
|
+
if (header === null) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
if (header.contentLength > maxFrameBytes) {
|
|
130
|
+
throw new LspFrameRejectError("RESPONSE_TOO_LARGE");
|
|
131
|
+
}
|
|
132
|
+
const bodyEnd = header.headerEndIndex + header.contentLength;
|
|
133
|
+
if (buffer.length < bodyEnd) {
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
body: buffer.subarray(header.headerEndIndex, bodyEnd),
|
|
138
|
+
rest: buffer.subarray(bodyEnd),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// Slices every complete frame currently buffered, yielding each body. Enforces the header cap on the
|
|
142
|
+
// no-terminator path before coalescing, so a delimiter-less flood is rejected without an unbounded
|
|
143
|
+
// concat. Stops when neither a full header nor a full body is yet available (awaiting more chunks).
|
|
144
|
+
function* drainFrames(pending, maxFrameBytes) {
|
|
145
|
+
for (;;) {
|
|
146
|
+
if (!pending.hasHeaderDelimiter()) {
|
|
147
|
+
guardHeaderBound(pending);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
const frame = takeFrame(pending.coalesce(), maxFrameBytes);
|
|
151
|
+
if (frame === null) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
pending.reset(frame.rest);
|
|
155
|
+
yield frame.body;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Serializes a JSON-RPC body string as one LSP base-protocol frame: an ASCII Content-Length header
|
|
159
|
+
// (byte length of the UTF-8 body, not the string length) followed by `\r\n\r\n` and the body bytes.
|
|
160
|
+
export function writeLspFrame(sink, body) {
|
|
161
|
+
const bodyBytes = Buffer.from(body, "utf8");
|
|
162
|
+
const header = Buffer.from(`Content-Length: ${String(bodyBytes.length)}\r\n\r\n`, "ascii");
|
|
163
|
+
sink.write(Buffer.concat([header, bodyBytes]));
|
|
164
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { LspByteSource } from "./lspFrameCodec.js";
|
|
2
|
+
export declare class LspRpcTimeoutError extends Error {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
5
|
+
export declare class LspRpcCancelledError extends Error {
|
|
6
|
+
constructor();
|
|
7
|
+
}
|
|
8
|
+
export declare class LspRpcDisposedError extends Error {
|
|
9
|
+
constructor();
|
|
10
|
+
}
|
|
11
|
+
export type LspNotificationHandler = (method: string, params: unknown) => void;
|
|
12
|
+
export interface LspRequestOptions {
|
|
13
|
+
readonly signal?: AbortSignal | undefined;
|
|
14
|
+
readonly deadlineMs: number;
|
|
15
|
+
readonly now?: (() => number) | undefined;
|
|
16
|
+
}
|
|
17
|
+
export interface LspJsonRpcClient {
|
|
18
|
+
request<T>(method: string, params: unknown, options: LspRequestOptions): Promise<T>;
|
|
19
|
+
notify(method: string, params: unknown): void;
|
|
20
|
+
onNotification(handler: LspNotificationHandler): void;
|
|
21
|
+
pendingCount(): number;
|
|
22
|
+
dispose(): void;
|
|
23
|
+
}
|
|
24
|
+
export interface LspRpcScheduler {
|
|
25
|
+
setTimer(callback: () => void, delayMs: number): unknown;
|
|
26
|
+
clearTimer(handle: unknown): void;
|
|
27
|
+
}
|
|
28
|
+
export interface LspJsonRpcClientDeps {
|
|
29
|
+
readonly source: LspByteSource;
|
|
30
|
+
readonly sendFrame: (body: string) => void;
|
|
31
|
+
readonly scheduler?: LspRpcScheduler | undefined;
|
|
32
|
+
}
|
|
33
|
+
export declare function createLspJsonRpcClient(deps: LspJsonRpcClientDeps): LspJsonRpcClient;
|
|
34
|
+
//# sourceMappingURL=lspJsonRpcClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lspJsonRpcClient.d.ts","sourceRoot":"","sources":["../../../src/editor/lsp/lspJsonRpcClient.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,oBAAoB,CAAC;AAElE,qBAAa,kBAAmB,SAAQ,KAAK;;CAK5C;AAED,qBAAa,oBAAqB,SAAQ,KAAK;;CAK9C;AAED,qBAAa,mBAAoB,SAAQ,KAAK;;CAK7C;AAED,MAAM,MAAM,sBAAsB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AAE/E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,MAAM,CAAC,GAAG,SAAS,CAAC;CAC3C;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9C,cAAc,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtD,YAAY,IAAI,MAAM,CAAC;IACvB,OAAO,IAAI,IAAI,CAAC;CACjB;AAID,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IACzD,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC;CAClD;AAaD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,oBAAoB,GAAG,gBAAgB,CA4CnF"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Minimal JSON-RPC 2.0 client over the LSP base protocol (Issue #1381, Epic #1491, ADR-0069 D3/D4).
|
|
2
|
+
// It correlates outbound requests to inbound responses by numeric id, dispatches server-initiated
|
|
3
|
+
// notifications to a registered handler, and enforces a per-request deadline plus AbortSignal
|
|
4
|
+
// cancellation by mirroring the `createDeadlineCancellation` clock+signal model natively. We do NOT
|
|
5
|
+
// import `languageCancellation` here: it pulls in the TypeScript compiler for its HostCancellationToken,
|
|
6
|
+
// which this transport layer must not depend on.
|
|
7
|
+
//
|
|
8
|
+
// On deadline expiry the client sends `$/cancelRequest` and rejects with LspRpcTimeoutError (the
|
|
9
|
+
// process is not killed — a slow request is not a crash, ADR-0069 D4). On abort it sends
|
|
10
|
+
// `$/cancelRequest` and rejects with LspRpcCancelledError. On dispose every pending request rejects
|
|
11
|
+
// with LspRpcDisposedError. All three carry only a class name, never request/response content.
|
|
12
|
+
export class LspRpcTimeoutError extends Error {
|
|
13
|
+
constructor() {
|
|
14
|
+
super("LSP request timed out");
|
|
15
|
+
this.name = "LspRpcTimeoutError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export class LspRpcCancelledError extends Error {
|
|
19
|
+
constructor() {
|
|
20
|
+
super("LSP request cancelled");
|
|
21
|
+
this.name = "LspRpcCancelledError";
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export class LspRpcDisposedError extends Error {
|
|
25
|
+
constructor() {
|
|
26
|
+
super("LSP client disposed");
|
|
27
|
+
this.name = "LspRpcDisposedError";
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const defaultScheduler = {
|
|
31
|
+
setTimer: (callback, delayMs) => setTimeout(callback, delayMs),
|
|
32
|
+
clearTimer: (handle) => { clearTimeout(handle); },
|
|
33
|
+
};
|
|
34
|
+
export function createLspJsonRpcClient(deps) {
|
|
35
|
+
const scheduler = deps.scheduler ?? defaultScheduler;
|
|
36
|
+
const pending = new Map();
|
|
37
|
+
let notificationHandler;
|
|
38
|
+
let nextId = 1;
|
|
39
|
+
let disposed = false;
|
|
40
|
+
const send = (payload) => {
|
|
41
|
+
deps.sendFrame(JSON.stringify(payload));
|
|
42
|
+
};
|
|
43
|
+
void consume(deps.source, (body) => { dispatch(body, pending, (m, p) => notificationHandler?.(m, p)); });
|
|
44
|
+
const request = (method, params, options) => {
|
|
45
|
+
if (disposed) {
|
|
46
|
+
return Promise.reject(new LspRpcDisposedError());
|
|
47
|
+
}
|
|
48
|
+
const id = nextId++;
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
registerPending(id, pending, scheduler, send, options, {
|
|
51
|
+
resolve: resolve,
|
|
52
|
+
reject,
|
|
53
|
+
});
|
|
54
|
+
send({ jsonrpc: "2.0", id, method, params });
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
request,
|
|
59
|
+
notify: (method, params) => {
|
|
60
|
+
if (!disposed)
|
|
61
|
+
send({ jsonrpc: "2.0", method, params });
|
|
62
|
+
},
|
|
63
|
+
onNotification: (handler) => {
|
|
64
|
+
notificationHandler = handler;
|
|
65
|
+
},
|
|
66
|
+
pendingCount: () => pending.size,
|
|
67
|
+
dispose: () => {
|
|
68
|
+
if (disposed)
|
|
69
|
+
return;
|
|
70
|
+
disposed = true;
|
|
71
|
+
rejectAll(pending, new LspRpcDisposedError());
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Wires the deadline timer and abort listener for one in-flight request and records it in the pending
|
|
76
|
+
// map. The shared `settle()` tears down both the timer and the abort listener exactly once, on any
|
|
77
|
+
// terminal outcome (response, timeout, abort, dispose), so no handle leaks.
|
|
78
|
+
function registerPending(id, pending, scheduler, send, options, handlers) {
|
|
79
|
+
const cancel = () => { send({ jsonrpc: "2.0", method: "$/cancelRequest", params: { id } }); };
|
|
80
|
+
const timer = scheduler.setTimer(() => {
|
|
81
|
+
if (!pending.has(id))
|
|
82
|
+
return;
|
|
83
|
+
pending.delete(id);
|
|
84
|
+
settle();
|
|
85
|
+
cancel();
|
|
86
|
+
handlers.reject(new LspRpcTimeoutError());
|
|
87
|
+
}, options.deadlineMs);
|
|
88
|
+
const onAbort = () => {
|
|
89
|
+
if (!pending.has(id))
|
|
90
|
+
return;
|
|
91
|
+
pending.delete(id);
|
|
92
|
+
settle();
|
|
93
|
+
cancel();
|
|
94
|
+
handlers.reject(new LspRpcCancelledError());
|
|
95
|
+
};
|
|
96
|
+
const settle = () => {
|
|
97
|
+
scheduler.clearTimer(timer);
|
|
98
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
99
|
+
};
|
|
100
|
+
if (options.signal?.aborted === true) {
|
|
101
|
+
pending.delete(id);
|
|
102
|
+
settle();
|
|
103
|
+
cancel();
|
|
104
|
+
handlers.reject(new LspRpcCancelledError());
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
options.signal?.addEventListener("abort", onAbort);
|
|
108
|
+
pending.set(id, { resolve: handlers.resolve, reject: handlers.reject, settle });
|
|
109
|
+
}
|
|
110
|
+
// Parses one frame body and routes it: a response (has numeric `id`) resolves or rejects its pending
|
|
111
|
+
// entry; anything else is treated as a server-initiated notification. Malformed JSON or an unknown id
|
|
112
|
+
// is ignored — a misbehaving server cannot crash the client (fail-safe, ADR-0069 D4).
|
|
113
|
+
function dispatch(body, pending, notify) {
|
|
114
|
+
const message = parseMessage(body);
|
|
115
|
+
if (message === null)
|
|
116
|
+
return;
|
|
117
|
+
const id = message.id;
|
|
118
|
+
if (typeof id === "number") {
|
|
119
|
+
settleResponse(id, message, pending);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (typeof message.method === "string") {
|
|
123
|
+
notify(message.method, message.params);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function settleResponse(id, message, pending) {
|
|
127
|
+
const entry = pending.get(id);
|
|
128
|
+
if (entry === undefined)
|
|
129
|
+
return;
|
|
130
|
+
pending.delete(id);
|
|
131
|
+
entry.settle();
|
|
132
|
+
const error = message.error;
|
|
133
|
+
if (isRpcError(error)) {
|
|
134
|
+
entry.reject(new Error(typeof error.message === "string" ? error.message : "LSP error"));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
entry.resolve(message.result);
|
|
138
|
+
}
|
|
139
|
+
function isRpcError(value) {
|
|
140
|
+
return typeof value === "object" && value !== null;
|
|
141
|
+
}
|
|
142
|
+
function parseMessage(body) {
|
|
143
|
+
try {
|
|
144
|
+
const parsed = JSON.parse(body.toString("utf8"));
|
|
145
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
146
|
+
return null;
|
|
147
|
+
return parsed;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function rejectAll(pending, error) {
|
|
154
|
+
for (const entry of pending.values()) {
|
|
155
|
+
entry.settle();
|
|
156
|
+
entry.reject(error);
|
|
157
|
+
}
|
|
158
|
+
pending.clear();
|
|
159
|
+
}
|
|
160
|
+
// Drains the frame source until it ends. Reader-loop errors (e.g. a frame-reject thrown by the codec)
|
|
161
|
+
// terminate the loop; the transport owns surfacing that as a process-level failure, so it is swallowed
|
|
162
|
+
// here to avoid an unhandled rejection on the detached consume promise.
|
|
163
|
+
async function consume(source, onBody) {
|
|
164
|
+
try {
|
|
165
|
+
for await (const body of source) {
|
|
166
|
+
onBody(body);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Intentionally ignored: frame-level failures are surfaced by the transport via the reader, not
|
|
171
|
+
// by this detached drain loop. See lspTransport.ts.
|
|
172
|
+
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LanguageProviderDescriptor, LanguageServiceOperation, LspProcessStatus } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
import type { LanguageProvider } from "../languageProvider.js";
|
|
3
|
+
export interface LspManagerLanguageProvider extends LanguageProvider {
|
|
4
|
+
readonly descriptor: LanguageProviderDescriptor;
|
|
5
|
+
}
|
|
6
|
+
export declare function buildLanguageProvider(managerId: string, languages: readonly string[], operations: readonly LanguageServiceOperation[], getStatus: () => LspProcessStatus): LspManagerLanguageProvider;
|
|
7
|
+
//# sourceMappingURL=lspLanguageProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lspLanguageProvider.d.ts","sourceRoot":"","sources":["../../../src/editor/lsp/lspLanguageProvider.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,0BAA0B,EAC1B,wBAAwB,EACxB,gBAAgB,EACjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAGV,gBAAgB,EAEjB,MAAM,wBAAwB,CAAC;AAGhC,MAAM,WAAW,0BAA2B,SAAQ,gBAAgB;IAClE,QAAQ,CAAC,UAAU,EAAE,0BAA0B,CAAC;CACjD;AAYD,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,UAAU,EAAE,SAAS,wBAAwB,EAAE,EAC/C,SAAS,EAAE,MAAM,gBAAgB,GAChC,0BAA0B,CAa5B"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Adapts a managed LSP process into a `LanguageProvider` whose descriptor reflects the manager's live
|
|
2
|
+
// status (Issue #1381, ADR-0069 D5). This foundation ships no real provider: the language-intelligence
|
|
3
|
+
// operations return empty, non-truncated results and the descriptor is the sole governed surface —
|
|
4
|
+
// `describeLanguageCapabilities()` consumes `descriptor.availability` unchanged. A future per-language
|
|
5
|
+
// issue replaces these stubs with real LSP request mappings.
|
|
6
|
+
import { lspStatusToProviderDescriptor } from "@oscharko-dev/keiko-contracts";
|
|
7
|
+
const EMPTY_DIAGNOSTICS = { diagnostics: [], truncated: false };
|
|
8
|
+
const EMPTY_SYMBOLS = { symbols: [], truncated: false };
|
|
9
|
+
const EMPTY_FORMATTING = { edits: [], truncated: false };
|
|
10
|
+
const EMPTY_COMPLETION = {
|
|
11
|
+
items: [],
|
|
12
|
+
isIncomplete: false,
|
|
13
|
+
truncated: false,
|
|
14
|
+
};
|
|
15
|
+
const EMPTY_HOVER = { contents: null };
|
|
16
|
+
export function buildLanguageProvider(managerId, languages, operations, getStatus) {
|
|
17
|
+
const supported = new Set(languages);
|
|
18
|
+
return {
|
|
19
|
+
get descriptor() {
|
|
20
|
+
return lspStatusToProviderDescriptor(managerId, languages, operations, getStatus());
|
|
21
|
+
},
|
|
22
|
+
supports: (languageId) => supported.has(languageId),
|
|
23
|
+
getDiagnostics: () => EMPTY_DIAGNOSTICS,
|
|
24
|
+
getCompletions: () => EMPTY_COMPLETION,
|
|
25
|
+
getHover: () => EMPTY_HOVER,
|
|
26
|
+
getSymbols: () => EMPTY_SYMBOLS,
|
|
27
|
+
getFormatting: () => EMPTY_FORMATTING,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { LspLifecycleEvent } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
export declare function recordLspLifecycleEvent(event: LspLifecycleEvent): LspLifecycleEvent | null;
|
|
3
|
+
export declare function listLspLifecycleEvents(): readonly LspLifecycleEvent[];
|
|
4
|
+
export declare function _resetLspLifecycleLedgerForTests(): void;
|
|
5
|
+
//# sourceMappingURL=lspLifecycleLedger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lspLifecycleLedger.d.ts","sourceRoot":"","sources":["../../../src/editor/lsp/lspLifecycleLedger.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAmBvE,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,IAAI,CAW1F;AAGD,wBAAgB,sBAAsB,IAAI,SAAS,iBAAiB,EAAE,CAErE;AAED,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Issue #1381, ADR-0069 D6/I4 — bounded, content-free in-memory ledger of LSP process lifecycle
|
|
2
|
+
// events. Mirrors agentActionAudit.ts: a module-level append-only FIFO surfaces "recent LSP process
|
|
3
|
+
// activity" to the read-only status route. The record is content-free by construction (the contract
|
|
4
|
+
// `LspLifecycleEvent` carries only an opaque managerId, an enum status/error code, counts, and a
|
|
5
|
+
// timestamp — no source text, paths, method names, or raw stderr) and is run through the
|
|
6
|
+
// keiko-security redactor as defense in depth before it is stored or served, so a secret-shaped
|
|
7
|
+
// substring that ever slipped into an id cannot reach the ledger.
|
|
8
|
+
import { deepRedactStrings, redact } from "@oscharko-dev/keiko-security";
|
|
9
|
+
// Bounded like the agent-audit ledger: a long-lived server must not grow this without limit.
|
|
10
|
+
const MAX_EVENTS = 200;
|
|
11
|
+
const state = { events: [] };
|
|
12
|
+
function redactEvent(event) {
|
|
13
|
+
return deepRedactStrings(event, redact);
|
|
14
|
+
}
|
|
15
|
+
// Record one content-free lifecycle event. Best-effort and throw-free: a ledger failure must never
|
|
16
|
+
// break the manager's lifecycle path. Returns the stored (redacted) event, or null when recording
|
|
17
|
+
// failed. The manager's `onLifecycleEvent` callback feeds this on every state transition.
|
|
18
|
+
export function recordLspLifecycleEvent(event) {
|
|
19
|
+
try {
|
|
20
|
+
const redacted = redactEvent(event);
|
|
21
|
+
state.events.push(redacted);
|
|
22
|
+
if (state.events.length > MAX_EVENTS) {
|
|
23
|
+
state.events.splice(0, state.events.length - MAX_EVENTS);
|
|
24
|
+
}
|
|
25
|
+
return redacted;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Recent lifecycle events, newest last, bounded to the FIFO cap. Already redacted at record time.
|
|
32
|
+
export function listLspLifecycleEvents() {
|
|
33
|
+
return state.events;
|
|
34
|
+
}
|
|
35
|
+
export function _resetLspLifecycleLedgerForTests() {
|
|
36
|
+
state.events.length = 0;
|
|
37
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { CommandRule } from "@oscharko-dev/keiko-tools";
|
|
2
|
+
import type { WorkspaceInfo } from "@oscharko-dev/keiko-workspace";
|
|
3
|
+
import type { LspProcessErrorCode } from "@oscharko-dev/keiko-contracts";
|
|
4
|
+
import type { LspSpawnHandle } from "./lspTransport.js";
|
|
5
|
+
export declare class LspProcessError extends Error {
|
|
6
|
+
readonly code: LspProcessErrorCode;
|
|
7
|
+
constructor(code: LspProcessErrorCode);
|
|
8
|
+
}
|
|
9
|
+
export type LspSpawnFn = (executable: string, args: readonly string[], env: Record<string, string>, cwd: string) => LspSpawnHandle & {
|
|
10
|
+
kill(signal: NodeJS.Signals): void;
|
|
11
|
+
onExit(callback: (code: number | null) => void): void;
|
|
12
|
+
onError(callback: (error: Error) => void): void;
|
|
13
|
+
};
|
|
14
|
+
export interface EphemeralHome {
|
|
15
|
+
readonly path: string;
|
|
16
|
+
cleanup(): void;
|
|
17
|
+
}
|
|
18
|
+
export interface KillableChild {
|
|
19
|
+
readonly pid?: number | undefined;
|
|
20
|
+
kill(signal: NodeJS.Signals): void;
|
|
21
|
+
}
|
|
22
|
+
export declare function resolveExecutableOutsideWorkspace(name: string, workspace: WorkspaceInfo, processEnv: NodeJS.ProcessEnv): string;
|
|
23
|
+
export declare function createEphemeralHome(): EphemeralHome;
|
|
24
|
+
export type ChildExitRegistration = (onExit: () => void) => void;
|
|
25
|
+
export declare function escalateKill(child: KillableChild, gracePeriodMs: number, exited: () => boolean, scheduler?: KillScheduler, whenExited?: ChildExitRegistration): Promise<void>;
|
|
26
|
+
export interface KillScheduler {
|
|
27
|
+
setTimer(callback: () => void, delayMs: number): unknown;
|
|
28
|
+
}
|
|
29
|
+
export declare const defaultLspSpawnFn: LspSpawnFn;
|
|
30
|
+
export declare function preflightSpawnEnv(rules: readonly CommandRule[], executable: string, args: readonly string[], processEnv: NodeJS.ProcessEnv, envAllowlist: readonly string[]): Record<string, string>;
|
|
31
|
+
//# sourceMappingURL=lspNodeAdapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lspNodeAdapter.d.ts","sourceRoot":"","sources":["../../../src/editor/lsp/lspNodeAdapter.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAIxD,qBAAa,eAAgB,SAAQ,KAAK;IACxC,SAAgB,IAAI,EAAE,mBAAmB,CAAC;gBAEvB,IAAI,EAAE,mBAAmB;CAK7C;AAID,MAAM,MAAM,UAAU,GAAG,CACvB,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,GAAG,EAAE,MAAM,KACR,cAAc,GAAG;IACpB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;IACnC,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC;IACtD,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;CACjD,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,IAAI,IAAI,CAAC;CACjB;AAID,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;CACpC;AA2DD,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,aAAa,EACxB,UAAU,EAAE,MAAM,CAAC,UAAU,GAC5B,MAAM,CAmBR;AAID,wBAAgB,mBAAmB,IAAI,aAAa,CAYnD;AAgCD,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,MAAM,IAAI,KAAK,IAAI,CAAC;AAQjE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,aAAa,EACpB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,OAAO,EACrB,SAAS,GAAE,aAAoC,EAC/C,UAAU,CAAC,EAAE,qBAAqB,GACjC,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CAC1D;AA0CD,eAAO,MAAM,iBAAiB,EAAE,UAkB/B,CAAC;AAKF,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,UAAU,EAAE,MAAM,CAAC,UAAU,EAC7B,YAAY,EAAE,SAAS,MAAM,EAAE,GAC9B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB"}
|