dsh-lsp-actions 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/README.es.md +176 -129
- package/README.hi.md +176 -129
- package/README.md +170 -129
- package/README.pt.md +176 -129
- package/README.zh.md +224 -0
- package/cordis.patch.yml +6 -0
- package/docs/editor-protocol.md +159 -0
- package/docs/editor-protocol.zh-CN.md +159 -0
- package/docs/seam-extension-notes.md +76 -0
- package/examples/vscode/README.md +72 -0
- package/examples/vscode/README.zh-CN.md +72 -0
- package/examples/vscode/backend/bin.mjs +44 -0
- package/examples/vscode/backend/cordis.yml +53 -0
- package/examples/vscode/backend/package.json +22 -0
- package/examples/vscode/extension/package.json +96 -0
- package/examples/vscode/extension/src/client.js +100 -0
- package/examples/vscode/extension/src/extension.js +187 -0
- package/examples/vscode/extension/src/views.js +127 -0
- package/lib/client.d.ts +3 -3
- package/lib/editor/actions.d.ts +44 -0
- package/lib/editor/actions.d.ts.map +1 -0
- package/lib/editor/actions.js +370 -0
- package/lib/editor/actions.js.map +1 -0
- package/lib/editor/cache.d.ts +51 -0
- package/lib/editor/cache.d.ts.map +1 -0
- package/lib/editor/cache.js +75 -0
- package/lib/editor/cache.js.map +1 -0
- package/lib/editor/server.d.ts +61 -0
- package/lib/editor/server.d.ts.map +1 -0
- package/lib/editor/server.js +97 -0
- package/lib/editor/server.js.map +1 -0
- package/lib/editor/service.d.ts +67 -0
- package/lib/editor/service.d.ts.map +1 -0
- package/lib/editor/service.js +182 -0
- package/lib/editor/service.js.map +1 -0
- package/lib/editor/transport.d.ts +53 -0
- package/lib/editor/transport.d.ts.map +1 -0
- package/lib/editor/transport.js +143 -0
- package/lib/editor/transport.js.map +1 -0
- package/lib/editor/types.d.ts +185 -0
- package/lib/editor/types.d.ts.map +1 -0
- package/lib/editor/types.js +13 -0
- package/lib/editor/types.js.map +1 -0
- package/lib/edits.d.ts +1 -1
- package/lib/extra-tools.d.ts +3 -3
- package/lib/index.d.ts +17 -11
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +26 -1
- package/lib/index.js.map +1 -1
- package/lib/runner.d.ts +5 -5
- package/lib/seam.d.ts +1 -1
- package/lib/servers.d.ts +20 -1
- package/lib/servers.d.ts.map +1 -1
- package/lib/servers.js +6 -0
- package/lib/servers.js.map +1 -1
- package/lib/tools.d.ts +18 -5
- package/lib/tools.d.ts.map +1 -1
- package/lib/tools.js +2 -2
- package/lib/tools.js.map +1 -1
- package/lib/translate.d.ts +1 -1
- package/lib/vocabulary.d.ts +3 -1
- package/lib/vocabulary.d.ts.map +1 -1
- package/lib/vocabulary.js.map +1 -1
- package/package.json +61 -8
- package/README.zh-CN.md +0 -181
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor-protocol JSON-RPC server: serves `lsp.actions.list` / `lsp.actions.run` and the
|
|
3
|
+
* client-controlled `lsp.events` subscription over a newline-delimited JSON-RPC stdio channel.
|
|
4
|
+
* This is a thin transport consumer of {@link EditorActionService} — all protocol and permission
|
|
5
|
+
* logic lives there, so any other transport (HTTP, WS, an upstream extension point) can serve the
|
|
6
|
+
* same surface unchanged.
|
|
7
|
+
*
|
|
8
|
+
* Stdout is reserved for protocol frames: enable this only in a dedicated headless composition
|
|
9
|
+
* whose stdout nothing else claims (the plugin refuses nothing at load — the deployment owns that
|
|
10
|
+
* contract, exactly like the official SDK JSON-RPC server).
|
|
11
|
+
* @module dsh-lsp-actions/editor/server
|
|
12
|
+
*/
|
|
13
|
+
import { LruDiagnosticsCache } from "./cache.js";
|
|
14
|
+
import { EditorActionService } from "./service.js";
|
|
15
|
+
import { EditorJsonRpcTransport, EditorProtocolError } from "./transport.js";
|
|
16
|
+
/** The three protocol methods, by wire name. */
|
|
17
|
+
export const EDITOR_METHOD_LIST = 'lsp.actions.list';
|
|
18
|
+
export const EDITOR_METHOD_RUN = 'lsp.actions.run';
|
|
19
|
+
export const EDITOR_METHOD_EVENTS = 'lsp.events';
|
|
20
|
+
/**
|
|
21
|
+
* Start the editor protocol for one plugin instance: the bounded diagnostics cache, the
|
|
22
|
+
* transport-agnostic service (listeners attached), and the JSON-RPC stdio server. Every side
|
|
23
|
+
* effect is owned by the returned {@link EditorProtocolRuntime.dispose}, so the plugin's effect
|
|
24
|
+
* scope can reverse the whole surface.
|
|
25
|
+
* @param ctx - the plugin context.
|
|
26
|
+
* @param runner - the seam-first action runner shared with the model tools.
|
|
27
|
+
* @param sandbox - the escalation controller shared with the mutation tools.
|
|
28
|
+
* @param config - the resolved plugin configuration (editor bounds validated by the caller).
|
|
29
|
+
* @returns the runtime to dispose with the plugin.
|
|
30
|
+
*/
|
|
31
|
+
export function startEditorProtocol(ctx, runner, sandbox, config) {
|
|
32
|
+
const cache = new LruDiagnosticsCache(config.editor.diagnosticsCacheMaxFiles);
|
|
33
|
+
const service = new EditorActionService(ctx, runner, sandbox, config, cache);
|
|
34
|
+
const stopListeners = service.start();
|
|
35
|
+
// Runtime-only transport hooks (tests): production configs go through the schema, which strips
|
|
36
|
+
// them, so the server claims process stdio — mirroring the official SDK JSON-RPC plugin.
|
|
37
|
+
const hooks = config.editor;
|
|
38
|
+
const server = new EditorJsonRpcServer(service, { input: hooks.input, output: hooks.output });
|
|
39
|
+
server.start();
|
|
40
|
+
return {
|
|
41
|
+
service,
|
|
42
|
+
server,
|
|
43
|
+
dispose: async () => {
|
|
44
|
+
server.close();
|
|
45
|
+
stopListeners();
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Serve the editor protocol over one transport. Construction subscribes to the service's event
|
|
51
|
+
* stream only while a client has subscribed via the `lsp.events` notification, so idle
|
|
52
|
+
* connections never accumulate listeners.
|
|
53
|
+
*/
|
|
54
|
+
export class EditorJsonRpcServer {
|
|
55
|
+
service;
|
|
56
|
+
transport;
|
|
57
|
+
eventsDisposer;
|
|
58
|
+
constructor(service, options = {}) {
|
|
59
|
+
this.service = service;
|
|
60
|
+
this.transport = new EditorJsonRpcTransport(options.input ?? process.stdin, options.output ?? process.stdout);
|
|
61
|
+
this.transport.onRequest(async (method, params) => {
|
|
62
|
+
switch (method) {
|
|
63
|
+
case EDITOR_METHOD_LIST:
|
|
64
|
+
return this.service.list();
|
|
65
|
+
case EDITOR_METHOD_RUN:
|
|
66
|
+
return await this.service.run(params);
|
|
67
|
+
default:
|
|
68
|
+
throw new EditorProtocolError(-32601, `method not found: ${method}`);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
this.transport.onNotification((method, params) => {
|
|
72
|
+
if (method !== EDITOR_METHOD_EVENTS)
|
|
73
|
+
return;
|
|
74
|
+
const subscribe = typeof params.subscribe === 'boolean' ? params.subscribe : true;
|
|
75
|
+
if (subscribe) {
|
|
76
|
+
if (this.eventsDisposer === undefined) {
|
|
77
|
+
this.eventsDisposer = this.service.subscribe(event => this.transport.notify('lsp.event', event));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
this.eventsDisposer?.();
|
|
82
|
+
this.eventsDisposer = undefined;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/** Attach the transport to its streams. */
|
|
87
|
+
start() {
|
|
88
|
+
this.transport.start();
|
|
89
|
+
}
|
|
90
|
+
/** Detach the transport and drop the event subscription. */
|
|
91
|
+
close() {
|
|
92
|
+
this.eventsDisposer?.();
|
|
93
|
+
this.eventsDisposer = undefined;
|
|
94
|
+
this.transport.close();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/editor/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor-protocol service: the transport-agnostic `lsp.actions.list` / `lsp.actions.run` /
|
|
3
|
+
* `lsp.events` surface editors consume. It owns the action dispatch, the per-run timeout, the
|
|
4
|
+
* version check, the bounded diagnostics cache (invalidated by filesystem observations and by the
|
|
5
|
+
* plugin's own writes), and the event stream; the JSON-RPC transport in `server.ts` is one thin
|
|
6
|
+
* consumer of it. Everything the service registers (listeners, cache, event subscriptions) is
|
|
7
|
+
* effect-scoped through the plugin's `ctx.effect`, so stopping the plugin reverses it all.
|
|
8
|
+
* @module dsh-lsp-actions/editor/service
|
|
9
|
+
*/
|
|
10
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
11
|
+
import type { ActionRunner } from '../runner.js';
|
|
12
|
+
import type { FormatSandboxController } from '../sandbox.js';
|
|
13
|
+
import type { ResolvedConfig } from '../servers.js';
|
|
14
|
+
import { LruDiagnosticsCache } from './cache.js';
|
|
15
|
+
import type { EditorEvent, EditorListResult, EditorRunRequest, EditorRunResult } from './types.js';
|
|
16
|
+
/** The listener contract of the event stream. */
|
|
17
|
+
export type EditorEventListener = (event: EditorEvent) => void;
|
|
18
|
+
/**
|
|
19
|
+
* The editor action surface for one plugin instance. Construction registers nothing; call
|
|
20
|
+
* {@link EditorActionService.start} (inside the plugin's effect) to attach listeners.
|
|
21
|
+
*/
|
|
22
|
+
export declare class EditorActionService {
|
|
23
|
+
private readonly ctx;
|
|
24
|
+
private readonly runner;
|
|
25
|
+
private readonly sandbox;
|
|
26
|
+
private readonly config;
|
|
27
|
+
private readonly cache;
|
|
28
|
+
private readonly listeners;
|
|
29
|
+
private started;
|
|
30
|
+
/** Serial source for request ids of runs that did not supply one (instance-owned, so a plugin reload never shares it). */
|
|
31
|
+
private serial;
|
|
32
|
+
constructor(ctx: Context, runner: ActionRunner, sandbox: FormatSandboxController, config: ResolvedConfig, cache: LruDiagnosticsCache);
|
|
33
|
+
/**
|
|
34
|
+
* Attach the reversible listeners: filesystem observations invalidate cached diagnostics (and
|
|
35
|
+
* announce `file.changed`), and session creation/disposal re-announce the session list. Safe to
|
|
36
|
+
* call once; the returned disposer detaches everything.
|
|
37
|
+
* @returns the disposal function for this service's listeners.
|
|
38
|
+
*/
|
|
39
|
+
start(): () => void;
|
|
40
|
+
/**
|
|
41
|
+
* The `lsp.actions.list` result: the protocol identity, the v1 action catalog, and the
|
|
42
|
+
* addressable DSH sessions (live sessions from the optional `sessions` service).
|
|
43
|
+
* @returns the list payload.
|
|
44
|
+
*/
|
|
45
|
+
list(): EditorListResult;
|
|
46
|
+
/**
|
|
47
|
+
* Run one editor action: version-check, validate, bind the addressed session's live agent (for
|
|
48
|
+
* the official permission-preset and approval choreography), enforce the configured per-run
|
|
49
|
+
* timeout, emit `action.status` lifecycle events, and always answer with the structured success
|
|
50
|
+
* or failure envelope.
|
|
51
|
+
* @param request - the raw `lsp.actions.run` parameters.
|
|
52
|
+
* @param signal - optional caller cancellation, raced with the configured timeout.
|
|
53
|
+
* @returns the unified run envelope.
|
|
54
|
+
*/
|
|
55
|
+
run(request: EditorRunRequest, signal?: AbortSignal): Promise<EditorRunResult>;
|
|
56
|
+
/** Subscribe to the event stream. Returns a disposer for the subscription. */
|
|
57
|
+
subscribe(listener: EditorEventListener): () => void;
|
|
58
|
+
/** The current live-session snapshot. */
|
|
59
|
+
private sessionSnapshot;
|
|
60
|
+
/** The live agent of the addressed session, when one exists. */
|
|
61
|
+
private liveAgentOf;
|
|
62
|
+
private sessionsService;
|
|
63
|
+
private announce;
|
|
64
|
+
/** A per-service-instance unique request id for runs that did not supply one. */
|
|
65
|
+
private counter;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/editor/service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAChD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAInD,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAqB,MAAM,YAAY,CAAA;AAYrH,iDAAiD;AACjD,MAAM,MAAM,mBAAmB,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;AAE9D;;;GAGG;AACH,qBAAa,mBAAmB;IAO5B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAVxB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAC3D,OAAO,CAAC,OAAO,CAAQ;IACvB,0HAA0H;IAC1H,OAAO,CAAC,MAAM,CAAI;gBAGC,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,uBAAuB,EAChC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,mBAAmB;IAG7C;;;;;OAKG;IACH,KAAK,IAAI,MAAM,IAAI;IA2BnB;;;;OAIG;IACH,IAAI,IAAI,gBAAgB;IASxB;;;;;;;;OAQG;IACG,GAAG,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC;IA6CpF,8EAA8E;IAC9E,SAAS,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,IAAI;IAKpD,yCAAyC;IACzC,OAAO,CAAC,eAAe;IAWvB,gEAAgE;IAChE,OAAO,CAAC,WAAW;IAUnB,OAAO,CAAC,eAAe;IAIvB,OAAO,CAAC,QAAQ;IAIhB,iFAAiF;IACjF,OAAO,CAAC,OAAO;CAIhB"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor-protocol service: the transport-agnostic `lsp.actions.list` / `lsp.actions.run` /
|
|
3
|
+
* `lsp.events` surface editors consume. It owns the action dispatch, the per-run timeout, the
|
|
4
|
+
* version check, the bounded diagnostics cache (invalidated by filesystem observations and by the
|
|
5
|
+
* plugin's own writes), and the event stream; the JSON-RPC transport in `server.ts` is one thin
|
|
6
|
+
* consumer of it. Everything the service registers (listeners, cache, event subscriptions) is
|
|
7
|
+
* effect-scoped through the plugin's `ctx.effect`, so stopping the plugin reverses it all.
|
|
8
|
+
* @module dsh-lsp-actions/editor/service
|
|
9
|
+
*/
|
|
10
|
+
import { LspActionError } from "../vocabulary.js";
|
|
11
|
+
import { EDITOR_ACTIONS, runEditorAction } from "./actions.js";
|
|
12
|
+
import { EDITOR_PROTOCOL } from "./types.js";
|
|
13
|
+
/**
|
|
14
|
+
* The editor action surface for one plugin instance. Construction registers nothing; call
|
|
15
|
+
* {@link EditorActionService.start} (inside the plugin's effect) to attach listeners.
|
|
16
|
+
*/
|
|
17
|
+
export class EditorActionService {
|
|
18
|
+
ctx;
|
|
19
|
+
runner;
|
|
20
|
+
sandbox;
|
|
21
|
+
config;
|
|
22
|
+
cache;
|
|
23
|
+
listeners = new Set();
|
|
24
|
+
started = false;
|
|
25
|
+
/** Serial source for request ids of runs that did not supply one (instance-owned, so a plugin reload never shares it). */
|
|
26
|
+
serial = 0;
|
|
27
|
+
constructor(ctx, runner, sandbox, config, cache) {
|
|
28
|
+
this.ctx = ctx;
|
|
29
|
+
this.runner = runner;
|
|
30
|
+
this.sandbox = sandbox;
|
|
31
|
+
this.config = config;
|
|
32
|
+
this.cache = cache;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Attach the reversible listeners: filesystem observations invalidate cached diagnostics (and
|
|
36
|
+
* announce `file.changed`), and session creation/disposal re-announce the session list. Safe to
|
|
37
|
+
* call once; the returned disposer detaches everything.
|
|
38
|
+
* @returns the disposal function for this service's listeners.
|
|
39
|
+
*/
|
|
40
|
+
start() {
|
|
41
|
+
if (this.started)
|
|
42
|
+
throw new Error('lsp-actions editor service already started');
|
|
43
|
+
this.started = true;
|
|
44
|
+
const offObserved = this.ctx.on('fs/observed', (...args) => {
|
|
45
|
+
const target = args[0];
|
|
46
|
+
const observation = args[1];
|
|
47
|
+
if (target === undefined || observation?.kind !== 'present')
|
|
48
|
+
return;
|
|
49
|
+
const key = target.displayPath ?? target.targetKey;
|
|
50
|
+
if (key === undefined)
|
|
51
|
+
return;
|
|
52
|
+
if (this.cache.delete(key))
|
|
53
|
+
this.announce({ kind: 'file.changed', filePath: key });
|
|
54
|
+
});
|
|
55
|
+
const announceSessions = () => {
|
|
56
|
+
this.announce({ kind: 'sessions.changed', sessions: this.sessionSnapshot() });
|
|
57
|
+
};
|
|
58
|
+
const sessions = this.sessionsService();
|
|
59
|
+
const offCreated = sessions === undefined ? undefined : this.ctx.on('session/created', () => { announceSessions(); });
|
|
60
|
+
const offDisposed = sessions === undefined ? undefined : this.ctx.on('session/disposed', () => { announceSessions(); });
|
|
61
|
+
return () => {
|
|
62
|
+
offObserved();
|
|
63
|
+
offCreated?.();
|
|
64
|
+
offDisposed?.();
|
|
65
|
+
this.listeners.clear();
|
|
66
|
+
this.cache.clear();
|
|
67
|
+
this.started = false;
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The `lsp.actions.list` result: the protocol identity, the v1 action catalog, and the
|
|
72
|
+
* addressable DSH sessions (live sessions from the optional `sessions` service).
|
|
73
|
+
* @returns the list payload.
|
|
74
|
+
*/
|
|
75
|
+
list() {
|
|
76
|
+
return {
|
|
77
|
+
protocol: EDITOR_PROTOCOL,
|
|
78
|
+
version: 1,
|
|
79
|
+
actions: EDITOR_ACTIONS,
|
|
80
|
+
sessions: this.sessionSnapshot(),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Run one editor action: version-check, validate, bind the addressed session's live agent (for
|
|
85
|
+
* the official permission-preset and approval choreography), enforce the configured per-run
|
|
86
|
+
* timeout, emit `action.status` lifecycle events, and always answer with the structured success
|
|
87
|
+
* or failure envelope.
|
|
88
|
+
* @param request - the raw `lsp.actions.run` parameters.
|
|
89
|
+
* @param signal - optional caller cancellation, raced with the configured timeout.
|
|
90
|
+
* @returns the unified run envelope.
|
|
91
|
+
*/
|
|
92
|
+
async run(request, signal) {
|
|
93
|
+
const requestId = typeof request?.requestId === 'string' && request.requestId !== '' ? request.requestId : `editor-${this.counter()}`;
|
|
94
|
+
// Every failure — including protocol-level validation — answers through the same structured
|
|
95
|
+
// envelope, so clients route on `status`/`error.code` and never parse JSON-RPC error text.
|
|
96
|
+
const refused = (code, message) => {
|
|
97
|
+
const error = { code, message };
|
|
98
|
+
if (typeof request?.action === 'string' && request.action !== '') {
|
|
99
|
+
this.announce({ kind: 'action.status', requestId, action: request.action, status: 'failed', error });
|
|
100
|
+
}
|
|
101
|
+
return { requestId, action: typeof request?.action === 'string' ? request.action : '', status: 'failed', error };
|
|
102
|
+
};
|
|
103
|
+
if (request?.protocol !== undefined && request.protocol !== EDITOR_PROTOCOL) {
|
|
104
|
+
return refused('LSP_PROTOCOL_VERSION_UNSUPPORTED', `unsupported editor protocol "${String(request.protocol)}" — this server speaks ${EDITOR_PROTOCOL}`);
|
|
105
|
+
}
|
|
106
|
+
if (typeof request?.action !== 'string' || request.action.trim() === '') {
|
|
107
|
+
return refused('LSP_ACTION_INVALID_ARGS', 'lsp.actions.run requires a non-empty string action');
|
|
108
|
+
}
|
|
109
|
+
const action = request.action;
|
|
110
|
+
const agent = this.liveAgentOf(request.sessionId);
|
|
111
|
+
const controller = new AbortController();
|
|
112
|
+
const timeout = setTimeout(() => { controller.abort(new LspActionError(`the editor action timed out after ${this.config.editor.requestTimeoutMs} ms`, 'LSP_ACTION_UNAVAILABLE')); }, this.config.editor.requestTimeoutMs);
|
|
113
|
+
const run = {
|
|
114
|
+
requestId,
|
|
115
|
+
signal: signal === undefined ? controller.signal : AbortSignal.any([signal, controller.signal]),
|
|
116
|
+
agent,
|
|
117
|
+
};
|
|
118
|
+
this.announce({ kind: 'action.status', requestId, action, status: 'started' });
|
|
119
|
+
try {
|
|
120
|
+
const result = await runEditorAction({ ctx: this.ctx, runner: this.runner, sandbox: this.sandbox, config: this.config, cache: this.cache, onEvent: event => this.announce(event) }, action, request.params === null || typeof request.params !== 'object' ? undefined : request.params, run);
|
|
121
|
+
this.announce({ kind: 'action.status', requestId, action, status: 'succeeded' });
|
|
122
|
+
return { requestId, action, status: 'succeeded', result };
|
|
123
|
+
}
|
|
124
|
+
catch (error) {
|
|
125
|
+
const info = structuredError(error);
|
|
126
|
+
this.announce({ kind: 'action.status', requestId, action, status: 'failed', error: info });
|
|
127
|
+
return { requestId, action, status: 'failed', error: info };
|
|
128
|
+
}
|
|
129
|
+
finally {
|
|
130
|
+
clearTimeout(timeout);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Subscribe to the event stream. Returns a disposer for the subscription. */
|
|
134
|
+
subscribe(listener) {
|
|
135
|
+
this.listeners.add(listener);
|
|
136
|
+
return () => { this.listeners.delete(listener); };
|
|
137
|
+
}
|
|
138
|
+
/** The current live-session snapshot. */
|
|
139
|
+
sessionSnapshot() {
|
|
140
|
+
const sessions = this.sessionsService();
|
|
141
|
+
if (sessions === undefined)
|
|
142
|
+
return [];
|
|
143
|
+
const agents = this.ctx.get('agents');
|
|
144
|
+
return sessions.list().map(session => ({
|
|
145
|
+
sessionId: String(session.id),
|
|
146
|
+
cwd: session.header.cwd ?? '',
|
|
147
|
+
live: agents?.get(session.id) !== undefined,
|
|
148
|
+
}));
|
|
149
|
+
}
|
|
150
|
+
/** The live agent of the addressed session, when one exists. */
|
|
151
|
+
liveAgentOf(sessionId) {
|
|
152
|
+
if (sessionId === undefined)
|
|
153
|
+
return undefined;
|
|
154
|
+
const sessions = this.sessionsService();
|
|
155
|
+
if (sessions === undefined)
|
|
156
|
+
return undefined;
|
|
157
|
+
const session = sessions.list().find(candidate => String(candidate.id) === sessionId);
|
|
158
|
+
if (session === undefined)
|
|
159
|
+
return undefined;
|
|
160
|
+
const agents = this.ctx.get('agents');
|
|
161
|
+
return agents?.get(session.id);
|
|
162
|
+
}
|
|
163
|
+
sessionsService() {
|
|
164
|
+
return this.ctx.get('sessions');
|
|
165
|
+
}
|
|
166
|
+
announce(event) {
|
|
167
|
+
for (const listener of this.listeners)
|
|
168
|
+
listener(event);
|
|
169
|
+
}
|
|
170
|
+
/** A per-service-instance unique request id for runs that did not supply one. */
|
|
171
|
+
counter() {
|
|
172
|
+
this.serial += 1;
|
|
173
|
+
return `editor-${this.serial}`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/** Map one thrown failure to the stable, plain-JSON error shape of a failed envelope. */
|
|
177
|
+
function structuredError(error) {
|
|
178
|
+
if (error instanceof LspActionError)
|
|
179
|
+
return { code: error.code, message: error.message };
|
|
180
|
+
return { code: 'LSP_ACTION_UNAVAILABLE', message: error instanceof Error ? error.message : String(error) };
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/editor/service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor protocol's JSON-RPC transport: newline-delimited JSON-RPC 2.0 over byte streams,
|
|
3
|
+
* wire-compatible with the official DeepSeek Harness SDK/ACP transports (one JSON object per line,
|
|
4
|
+
* the same request/response/notification frame rules and error codes). A client that can speak the
|
|
5
|
+
* official SDK wire can speak this wire unchanged — only the method vocabulary differs.
|
|
6
|
+
* @module dsh-lsp-actions/editor/transport
|
|
7
|
+
*/
|
|
8
|
+
import type { Readable, Writable } from 'node:stream';
|
|
9
|
+
/** A request handler: resolves to the response `result`, rejects into a `-32603` error frame. */
|
|
10
|
+
export type EditorRequestHandler = (method: string, params: Record<string, unknown>) => Promise<unknown>;
|
|
11
|
+
/**
|
|
12
|
+
* A minimal JSON-RPC 2.0 line endpoint over caller-owned streams. Malformed lines are ignored;
|
|
13
|
+
* unknown methods answer `-32601`; handler failures answer `-32603`; notifications without a
|
|
14
|
+
* handler are dropped. {@link start} attaches listeners and {@link close} detaches them.
|
|
15
|
+
*/
|
|
16
|
+
export declare class EditorJsonRpcTransport {
|
|
17
|
+
private readonly input;
|
|
18
|
+
private readonly output;
|
|
19
|
+
private buffer;
|
|
20
|
+
private readonly decoder;
|
|
21
|
+
private started;
|
|
22
|
+
private requestHandler;
|
|
23
|
+
private notificationHandler;
|
|
24
|
+
constructor(input: Readable, output: Writable);
|
|
25
|
+
/** Attach the input listeners and begin reading frames. Idempotent. */
|
|
26
|
+
start(): void;
|
|
27
|
+
/** Detach listeners. Safe before {@link start}. */
|
|
28
|
+
close(): void;
|
|
29
|
+
/** Install the request handler, replacing any prior handler. */
|
|
30
|
+
onRequest(handler: EditorRequestHandler): void;
|
|
31
|
+
/** Install the notification handler, replacing any prior handler. */
|
|
32
|
+
onNotification(handler: (method: string, params: Record<string, unknown>) => void): void;
|
|
33
|
+
/** Send one server→client notification. */
|
|
34
|
+
notify(method: string, params: object): void;
|
|
35
|
+
/** Wait for the output stream to flush prior writes. */
|
|
36
|
+
flush(): Promise<void>;
|
|
37
|
+
private readonly onData;
|
|
38
|
+
private drainLines;
|
|
39
|
+
private readonly onInputError;
|
|
40
|
+
private readonly onInputEnd;
|
|
41
|
+
private handleLine;
|
|
42
|
+
private handleRequest;
|
|
43
|
+
private writeError;
|
|
44
|
+
private write;
|
|
45
|
+
}
|
|
46
|
+
/** A protocol-level failure that maps to a specific JSON-RPC error code. */
|
|
47
|
+
export declare class EditorProtocolError extends Error {
|
|
48
|
+
readonly jsonRpcCode: number;
|
|
49
|
+
constructor(jsonRpcCode: number, message: string);
|
|
50
|
+
}
|
|
51
|
+
/** A fresh, transport-unique request id (used when a peer frame carries none). */
|
|
52
|
+
export declare function freshRequestId(): string;
|
|
53
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/editor/transport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAKrD,iGAAiG;AACjG,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;AAExG;;;;GAIG;AACH,qBAAa,sBAAsB;IAQ/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA4B;IACpD,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,cAAc,CAAkC;IACxD,OAAO,CAAC,mBAAmB,CAAyE;gBAGjF,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,QAAQ;IAGnC,uEAAuE;IACvE,KAAK,IAAI,IAAI;IAQb,mDAAmD;IACnD,KAAK,IAAI,IAAI;IAMb,gEAAgE;IAChE,SAAS,CAAC,OAAO,EAAE,oBAAoB,GAAG,IAAI;IAI9C,qEAAqE;IACrE,cAAc,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAAG,IAAI;IAIxF,2CAA2C;IAC3C,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAI5C,wDAAwD;IACxD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAMtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGtB;IAED,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAiB;IAE9C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAG1B;YAEa,UAAU;YAoBV,aAAa;IAe3B,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,KAAK;CAGd;AAED,4EAA4E;AAC5E,qBAAa,mBAAoB,SAAQ,KAAK;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM;gBAAnB,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAI1D;AASD,kFAAkF;AAClF,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The editor protocol's JSON-RPC transport: newline-delimited JSON-RPC 2.0 over byte streams,
|
|
3
|
+
* wire-compatible with the official DeepSeek Harness SDK/ACP transports (one JSON object per line,
|
|
4
|
+
* the same request/response/notification frame rules and error codes). A client that can speak the
|
|
5
|
+
* official SDK wire can speak this wire unchanged — only the method vocabulary differs.
|
|
6
|
+
* @module dsh-lsp-actions/editor/transport
|
|
7
|
+
*/
|
|
8
|
+
import { randomUUID } from 'node:crypto';
|
|
9
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
10
|
+
/**
|
|
11
|
+
* A minimal JSON-RPC 2.0 line endpoint over caller-owned streams. Malformed lines are ignored;
|
|
12
|
+
* unknown methods answer `-32601`; handler failures answer `-32603`; notifications without a
|
|
13
|
+
* handler are dropped. {@link start} attaches listeners and {@link close} detaches them.
|
|
14
|
+
*/
|
|
15
|
+
export class EditorJsonRpcTransport {
|
|
16
|
+
input;
|
|
17
|
+
output;
|
|
18
|
+
buffer = '';
|
|
19
|
+
decoder = new StringDecoder('utf8');
|
|
20
|
+
started = false;
|
|
21
|
+
requestHandler;
|
|
22
|
+
notificationHandler;
|
|
23
|
+
constructor(input, output) {
|
|
24
|
+
this.input = input;
|
|
25
|
+
this.output = output;
|
|
26
|
+
}
|
|
27
|
+
/** Attach the input listeners and begin reading frames. Idempotent. */
|
|
28
|
+
start() {
|
|
29
|
+
if (this.started)
|
|
30
|
+
return;
|
|
31
|
+
this.started = true;
|
|
32
|
+
this.input.on('data', this.onData);
|
|
33
|
+
this.input.on('error', this.onInputError);
|
|
34
|
+
this.input.on('end', this.onInputEnd);
|
|
35
|
+
}
|
|
36
|
+
/** Detach listeners. Safe before {@link start}. */
|
|
37
|
+
close() {
|
|
38
|
+
this.input.off('data', this.onData);
|
|
39
|
+
this.input.off('error', this.onInputError);
|
|
40
|
+
this.input.off('end', this.onInputEnd);
|
|
41
|
+
}
|
|
42
|
+
/** Install the request handler, replacing any prior handler. */
|
|
43
|
+
onRequest(handler) {
|
|
44
|
+
this.requestHandler = handler;
|
|
45
|
+
}
|
|
46
|
+
/** Install the notification handler, replacing any prior handler. */
|
|
47
|
+
onNotification(handler) {
|
|
48
|
+
this.notificationHandler = handler;
|
|
49
|
+
}
|
|
50
|
+
/** Send one server→client notification. */
|
|
51
|
+
notify(method, params) {
|
|
52
|
+
this.write({ jsonrpc: '2.0', method, params });
|
|
53
|
+
}
|
|
54
|
+
/** Wait for the output stream to flush prior writes. */
|
|
55
|
+
flush() {
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
this.output.write('', error => error === null || error === undefined ? resolve() : reject(error));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
onData = (chunk) => {
|
|
61
|
+
this.buffer += typeof chunk === 'string' ? chunk : this.decoder.write(chunk);
|
|
62
|
+
this.drainLines();
|
|
63
|
+
};
|
|
64
|
+
drainLines() {
|
|
65
|
+
for (;;) {
|
|
66
|
+
const newline = this.buffer.indexOf('\n');
|
|
67
|
+
if (newline < 0)
|
|
68
|
+
break;
|
|
69
|
+
const line = this.buffer.slice(0, newline).trim();
|
|
70
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
71
|
+
if (!line)
|
|
72
|
+
continue;
|
|
73
|
+
void this.handleLine(line);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
onInputError = () => { };
|
|
77
|
+
onInputEnd = () => {
|
|
78
|
+
this.buffer += this.decoder.end();
|
|
79
|
+
this.drainLines();
|
|
80
|
+
};
|
|
81
|
+
async handleLine(line) {
|
|
82
|
+
let message;
|
|
83
|
+
try {
|
|
84
|
+
message = JSON.parse(line);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (message === null || typeof message !== 'object' || Array.isArray(message))
|
|
90
|
+
return;
|
|
91
|
+
const frame = message;
|
|
92
|
+
const id = frame.id;
|
|
93
|
+
const method = frame.method;
|
|
94
|
+
if ((typeof id === 'string' || typeof id === 'number') && typeof method === 'string') {
|
|
95
|
+
await this.handleRequest(id, method, normalizeParams(frame.params));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (typeof method === 'string') {
|
|
99
|
+
this.notificationHandler?.(method, normalizeParams(frame.params));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async handleRequest(id, method, params) {
|
|
103
|
+
const handler = this.requestHandler;
|
|
104
|
+
if (handler === undefined) {
|
|
105
|
+
this.writeError(id, -32601, `method not found: ${method}`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
const result = await handler(method, params);
|
|
110
|
+
this.write({ jsonrpc: '2.0', id, result });
|
|
111
|
+
}
|
|
112
|
+
catch (error) {
|
|
113
|
+
const code = error instanceof EditorProtocolError ? error.jsonRpcCode : -32603;
|
|
114
|
+
this.writeError(id, code, error instanceof Error ? error.message : String(error));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
writeError(id, code, message) {
|
|
118
|
+
this.write({ jsonrpc: '2.0', id, error: { code, message } });
|
|
119
|
+
}
|
|
120
|
+
write(message) {
|
|
121
|
+
this.output.write(`${JSON.stringify(message)}\n`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/** A protocol-level failure that maps to a specific JSON-RPC error code. */
|
|
125
|
+
export class EditorProtocolError extends Error {
|
|
126
|
+
jsonRpcCode;
|
|
127
|
+
constructor(jsonRpcCode, message) {
|
|
128
|
+
super(message);
|
|
129
|
+
this.jsonRpcCode = jsonRpcCode;
|
|
130
|
+
this.name = 'EditorProtocolError';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Normalize JSON-RPC params to a plain object (arrays and scalars collapse to `{}`). */
|
|
134
|
+
function normalizeParams(params) {
|
|
135
|
+
return params !== null && typeof params === 'object' && !Array.isArray(params)
|
|
136
|
+
? params
|
|
137
|
+
: {};
|
|
138
|
+
}
|
|
139
|
+
/** A fresh, transport-unique request id (used when a peer frame carries none). */
|
|
140
|
+
export function freshRequestId() {
|
|
141
|
+
return randomUUID();
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../../src/editor/transport.ts"],"names":[],"mappings":"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"}
|