@aria-cli/tools 1.0.1 → 1.0.3
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/.aria-build-stamp.json +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/definitions/code-intelligence.d.ts +9 -0
- package/dist/definitions/code-intelligence.d.ts.map +1 -0
- package/dist/definitions/code-intelligence.js +471 -0
- package/dist/definitions/code-intelligence.js.map +1 -0
- package/dist/definitions/core.d.ts +3 -0
- package/dist/definitions/core.d.ts.map +1 -1
- package/dist/definitions/core.js +13 -1
- package/dist/definitions/core.js.map +1 -1
- package/dist/definitions/filesystem.d.ts +3 -2
- package/dist/definitions/filesystem.d.ts.map +1 -1
- package/dist/definitions/filesystem.js +4 -38
- package/dist/definitions/filesystem.js.map +1 -1
- package/dist/definitions/frg.d.ts +4 -0
- package/dist/definitions/frg.d.ts.map +1 -0
- package/dist/definitions/frg.js +64 -0
- package/dist/definitions/frg.js.map +1 -0
- package/dist/definitions/index.d.ts +3 -0
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/index.js +3 -0
- package/dist/definitions/index.js.map +1 -1
- package/dist/definitions/search.d.ts +10 -0
- package/dist/definitions/search.d.ts.map +1 -0
- package/dist/definitions/search.js +61 -0
- package/dist/definitions/search.js.map +1 -0
- package/dist/executors/apply-patch.d.ts.map +1 -1
- package/dist/executors/apply-patch.js +18 -0
- package/dist/executors/apply-patch.js.map +1 -1
- package/dist/executors/code-intelligence.d.ts +139 -0
- package/dist/executors/code-intelligence.d.ts.map +1 -0
- package/dist/executors/code-intelligence.js +883 -0
- package/dist/executors/code-intelligence.js.map +1 -0
- package/dist/executors/filesystem.d.ts.map +1 -1
- package/dist/executors/filesystem.js +14 -8
- package/dist/executors/filesystem.js.map +1 -1
- package/dist/executors/frg-freshness.d.ts +94 -0
- package/dist/executors/frg-freshness.d.ts.map +1 -0
- package/dist/executors/frg-freshness.js +577 -0
- package/dist/executors/frg-freshness.js.map +1 -0
- package/dist/executors/frg.d.ts +28 -0
- package/dist/executors/frg.d.ts.map +1 -0
- package/dist/executors/frg.js +299 -0
- package/dist/executors/frg.js.map +1 -0
- package/dist/executors/index.d.ts +6 -0
- package/dist/executors/index.d.ts.map +1 -1
- package/dist/executors/index.js +5 -0
- package/dist/executors/index.js.map +1 -1
- package/dist/executors/lsp-client.d.ts +39 -0
- package/dist/executors/lsp-client.d.ts.map +1 -0
- package/dist/executors/lsp-client.js +297 -0
- package/dist/executors/lsp-client.js.map +1 -0
- package/dist/executors/restart.d.ts +4 -9
- package/dist/executors/restart.d.ts.map +1 -1
- package/dist/executors/restart.js +20 -51
- package/dist/executors/restart.js.map +1 -1
- package/dist/executors/search-freshness.d.ts +51 -0
- package/dist/executors/search-freshness.d.ts.map +1 -0
- package/dist/executors/search-freshness.js +196 -0
- package/dist/executors/search-freshness.js.map +1 -0
- package/dist/executors/search.d.ts +12 -0
- package/dist/executors/search.d.ts.map +1 -0
- package/dist/executors/search.js +67 -0
- package/dist/executors/search.js.map +1 -0
- package/dist/headless-control-contract.d.ts +4 -0
- package/dist/headless-control-contract.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/network-runtime/local-control-contract.d.ts +2 -0
- package/dist/network-runtime/local-control-contract.d.ts.map +1 -1
- package/dist/network-runtime/local-control-contract.js +2 -0
- package/dist/network-runtime/local-control-contract.js.map +1 -1
- package/dist-cjs/.tsbuildinfo +1 -1
- package/dist-cjs/definitions/code-intelligence.d.ts +8 -0
- package/dist-cjs/definitions/code-intelligence.js +474 -0
- package/dist-cjs/definitions/code-intelligence.js.map +1 -0
- package/dist-cjs/definitions/core.d.ts +3 -0
- package/dist-cjs/definitions/core.js +17 -2
- package/dist-cjs/definitions/core.js.map +1 -1
- package/dist-cjs/definitions/filesystem.d.ts +3 -2
- package/dist-cjs/definitions/filesystem.js +3 -37
- package/dist-cjs/definitions/filesystem.js.map +1 -1
- package/dist-cjs/definitions/frg.d.ts +3 -0
- package/dist-cjs/definitions/frg.js +67 -0
- package/dist-cjs/definitions/frg.js.map +1 -0
- package/dist-cjs/definitions/index.d.ts +3 -0
- package/dist-cjs/definitions/index.js +7 -1
- package/dist-cjs/definitions/index.js.map +1 -1
- package/dist-cjs/definitions/search.d.ts +9 -0
- package/dist-cjs/definitions/search.js +64 -0
- package/dist-cjs/definitions/search.js.map +1 -0
- package/dist-cjs/executors/apply-patch.js +18 -0
- package/dist-cjs/executors/apply-patch.js.map +1 -1
- package/dist-cjs/executors/code-intelligence.d.ts +138 -0
- package/dist-cjs/executors/code-intelligence.js +926 -0
- package/dist-cjs/executors/code-intelligence.js.map +1 -0
- package/dist-cjs/executors/filesystem.js +17 -8
- package/dist-cjs/executors/filesystem.js.map +1 -1
- package/dist-cjs/executors/frg-freshness.d.ts +93 -0
- package/dist-cjs/executors/frg-freshness.js +628 -0
- package/dist-cjs/executors/frg-freshness.js.map +1 -0
- package/dist-cjs/executors/frg.d.ts +27 -0
- package/dist-cjs/executors/frg.js +335 -0
- package/dist-cjs/executors/frg.js.map +1 -0
- package/dist-cjs/executors/index.d.ts +6 -0
- package/dist-cjs/executors/index.js +34 -2
- package/dist-cjs/executors/index.js.map +1 -1
- package/dist-cjs/executors/lsp-client.d.ts +38 -0
- package/dist-cjs/executors/lsp-client.js +311 -0
- package/dist-cjs/executors/lsp-client.js.map +1 -0
- package/dist-cjs/executors/restart.d.ts +4 -9
- package/dist-cjs/executors/restart.js +19 -50
- package/dist-cjs/executors/restart.js.map +1 -1
- package/dist-cjs/executors/search-freshness.d.ts +50 -0
- package/dist-cjs/executors/search-freshness.js +235 -0
- package/dist-cjs/executors/search-freshness.js.map +1 -0
- package/dist-cjs/executors/search.d.ts +11 -0
- package/dist-cjs/executors/search.js +103 -0
- package/dist-cjs/executors/search.js.map +1 -0
- package/dist-cjs/headless-control-contract.d.ts +15 -11
- package/dist-cjs/index.d.ts +2 -2
- package/dist-cjs/index.js +22 -2
- package/dist-cjs/index.js.map +1 -1
- package/dist-cjs/network-runtime/local-control-contract.d.ts +2 -0
- package/dist-cjs/network-runtime/local-control-contract.js +2 -0
- package/dist-cjs/network-runtime/local-control-contract.js.map +1 -1
- package/package.json +22 -18
- package/src/definitions/code-intelligence.ts +526 -0
- package/src/definitions/core.ts +13 -1
- package/src/definitions/filesystem.ts +3 -39
- package/src/definitions/frg.ts +67 -0
- package/src/definitions/index.ts +3 -0
- package/src/definitions/search.ts +67 -0
- package/src/executors/apply-patch.ts +20 -0
- package/src/executors/code-intelligence.ts +1179 -0
- package/src/executors/filesystem.ts +15 -8
- package/src/executors/frg-freshness.ts +743 -0
- package/src/executors/frg.ts +394 -0
- package/src/executors/index.ts +58 -0
- package/src/executors/lsp-client.ts +355 -0
- package/src/executors/restart.ts +21 -56
- package/src/executors/search-freshness.ts +249 -0
- package/src/executors/search.ts +89 -0
- package/src/index.ts +25 -0
- package/src/network-runtime/local-control-contract.ts +2 -0
- package/tests/definitions/tool-inventory.test.ts +17 -6
- package/tests/executors/frg-freshness.test.ts +136 -0
- package/tests/executors/frg-merge.test.ts +70 -0
- package/tests/executors/frg-session-content.test.ts +40 -0
- package/tests/executors/frg.test.ts +56 -0
- package/tests/integration/headless-control-contract.integration.test.ts +2 -0
- package/tests/loading-tier.test.ts +6 -6
- package/tests/test-lane-manifest.ts +4 -0
- package/tsconfig.cjs.json +9 -1
- package/tsconfig.json +1 -1
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native LSP Client — uses vscode-jsonrpc for proper message framing.
|
|
3
|
+
*
|
|
4
|
+
* Same approach as Claude Code: createMessageConnection over stdio.
|
|
5
|
+
* Same configuration as Claude Code's LSP plugins.
|
|
6
|
+
* Supports: TypeScript, Python, Go, Rust, C/C++, Swift, Java, Kotlin, Lua, PHP, Ruby, C#.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { spawn, type ChildProcess } from "node:child_process";
|
|
10
|
+
import { readFileSync } from "node:fs";
|
|
11
|
+
import { resolve } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { createRequire } from "node:module";
|
|
14
|
+
|
|
15
|
+
// vscode-jsonrpc: same library Claude Code uses for LSP message framing.
|
|
16
|
+
// Direct import via createRequire (package lacks ESM exports field).
|
|
17
|
+
const require_ = createRequire(fileURLToPath(import.meta.url));
|
|
18
|
+
const { createMessageConnection, StreamMessageReader, StreamMessageWriter } = require_(
|
|
19
|
+
"vscode-jsonrpc/node",
|
|
20
|
+
) as {
|
|
21
|
+
createMessageConnection: (reader: unknown, writer: unknown) => VscodeConnection;
|
|
22
|
+
StreamMessageReader: new (stream: NodeJS.ReadableStream) => unknown;
|
|
23
|
+
StreamMessageWriter: new (stream: NodeJS.WritableStream) => unknown;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
interface VscodeConnection {
|
|
27
|
+
sendRequest(method: string, params?: unknown): Promise<unknown>;
|
|
28
|
+
sendNotification(method: string, params?: unknown): void;
|
|
29
|
+
onNotification(method: string, handler: (params: unknown) => void): void;
|
|
30
|
+
listen(): void;
|
|
31
|
+
dispose(): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
// Language server registry — same config as Claude Code LSP plugins
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
|
|
38
|
+
export interface LspServerConfig {
|
|
39
|
+
command: string;
|
|
40
|
+
args: string[];
|
|
41
|
+
extensionToLanguage: Record<string, string>;
|
|
42
|
+
startupTimeout: number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const LSP_SERVERS: Record<string, LspServerConfig> = {
|
|
46
|
+
typescript: {
|
|
47
|
+
command: "typescript-language-server",
|
|
48
|
+
args: ["--stdio"],
|
|
49
|
+
extensionToLanguage: {
|
|
50
|
+
".ts": "typescript",
|
|
51
|
+
".tsx": "typescriptreact",
|
|
52
|
+
".js": "javascript",
|
|
53
|
+
".jsx": "javascriptreact",
|
|
54
|
+
".mts": "typescript",
|
|
55
|
+
".cts": "typescript",
|
|
56
|
+
".mjs": "javascript",
|
|
57
|
+
".cjs": "javascript",
|
|
58
|
+
},
|
|
59
|
+
startupTimeout: 30_000,
|
|
60
|
+
},
|
|
61
|
+
python: {
|
|
62
|
+
command: "pyright-langserver",
|
|
63
|
+
args: ["--stdio"],
|
|
64
|
+
extensionToLanguage: { ".py": "python", ".pyi": "python" },
|
|
65
|
+
startupTimeout: 60_000,
|
|
66
|
+
},
|
|
67
|
+
go: {
|
|
68
|
+
command: "gopls",
|
|
69
|
+
args: [],
|
|
70
|
+
extensionToLanguage: { ".go": "go" },
|
|
71
|
+
startupTimeout: 30_000,
|
|
72
|
+
},
|
|
73
|
+
rust: {
|
|
74
|
+
command: "rust-analyzer",
|
|
75
|
+
args: [],
|
|
76
|
+
extensionToLanguage: { ".rs": "rust" },
|
|
77
|
+
startupTimeout: 60_000,
|
|
78
|
+
},
|
|
79
|
+
"c/cpp": {
|
|
80
|
+
command: "clangd",
|
|
81
|
+
args: ["--background-index"],
|
|
82
|
+
extensionToLanguage: {
|
|
83
|
+
".c": "c",
|
|
84
|
+
".h": "c",
|
|
85
|
+
".cpp": "cpp",
|
|
86
|
+
".cc": "cpp",
|
|
87
|
+
".cxx": "cpp",
|
|
88
|
+
".hpp": "cpp",
|
|
89
|
+
".hxx": "cpp",
|
|
90
|
+
},
|
|
91
|
+
startupTimeout: 30_000,
|
|
92
|
+
},
|
|
93
|
+
swift: {
|
|
94
|
+
command: "sourcekit-lsp",
|
|
95
|
+
args: [],
|
|
96
|
+
extensionToLanguage: { ".swift": "swift" },
|
|
97
|
+
startupTimeout: 30_000,
|
|
98
|
+
},
|
|
99
|
+
java: {
|
|
100
|
+
command: "jdtls",
|
|
101
|
+
args: [],
|
|
102
|
+
extensionToLanguage: { ".java": "java" },
|
|
103
|
+
startupTimeout: 120_000,
|
|
104
|
+
},
|
|
105
|
+
kotlin: {
|
|
106
|
+
command: "kotlin-language-server",
|
|
107
|
+
args: ["--stdio"],
|
|
108
|
+
extensionToLanguage: { ".kt": "kotlin", ".kts": "kotlin" },
|
|
109
|
+
startupTimeout: 120_000,
|
|
110
|
+
},
|
|
111
|
+
lua: {
|
|
112
|
+
command: "lua-language-server",
|
|
113
|
+
args: [],
|
|
114
|
+
extensionToLanguage: { ".lua": "lua" },
|
|
115
|
+
startupTimeout: 30_000,
|
|
116
|
+
},
|
|
117
|
+
php: {
|
|
118
|
+
command: "intelephense",
|
|
119
|
+
args: ["--stdio"],
|
|
120
|
+
extensionToLanguage: { ".php": "php" },
|
|
121
|
+
startupTimeout: 30_000,
|
|
122
|
+
},
|
|
123
|
+
ruby: {
|
|
124
|
+
command: "ruby-lsp",
|
|
125
|
+
args: [],
|
|
126
|
+
extensionToLanguage: { ".rb": "ruby", ".rake": "ruby", ".gemspec": "ruby" },
|
|
127
|
+
startupTimeout: 30_000,
|
|
128
|
+
},
|
|
129
|
+
csharp: {
|
|
130
|
+
command: "csharp-ls",
|
|
131
|
+
args: [],
|
|
132
|
+
extensionToLanguage: { ".cs": "csharp" },
|
|
133
|
+
startupTimeout: 30_000,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Build reverse lookup: file extension → server name
|
|
138
|
+
const EXT_TO_SERVER = new Map<string, string>();
|
|
139
|
+
for (const [serverName, config] of Object.entries(LSP_SERVERS)) {
|
|
140
|
+
for (const ext of Object.keys(config.extensionToLanguage)) {
|
|
141
|
+
EXT_TO_SERVER.set(ext, serverName);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function getServerForFile(filePath: string): string | undefined {
|
|
146
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
147
|
+
return EXT_TO_SERVER.get(ext);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function getLanguageId(filePath: string): string | undefined {
|
|
151
|
+
const ext = filePath.slice(filePath.lastIndexOf("."));
|
|
152
|
+
const serverName = EXT_TO_SERVER.get(ext);
|
|
153
|
+
if (!serverName) return undefined;
|
|
154
|
+
return LSP_SERVERS[serverName]?.extensionToLanguage[ext];
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// LSP Server Instance — uses vscode-jsonrpc createMessageConnection
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
export class LspServer {
|
|
162
|
+
private process: ChildProcess | null = null;
|
|
163
|
+
private connection: VscodeConnection | null = null;
|
|
164
|
+
private initialized = false;
|
|
165
|
+
private openFiles = new Set<string>();
|
|
166
|
+
private diagnosticsReceived = new Set<string>();
|
|
167
|
+
readonly serverName: string;
|
|
168
|
+
readonly config: LspServerConfig;
|
|
169
|
+
private rootUri: string;
|
|
170
|
+
|
|
171
|
+
constructor(serverName: string, rootPath: string) {
|
|
172
|
+
this.serverName = serverName;
|
|
173
|
+
const cfg = LSP_SERVERS[serverName];
|
|
174
|
+
if (!cfg) throw new Error(`Unknown LSP server: ${serverName}`);
|
|
175
|
+
this.config = cfg;
|
|
176
|
+
this.rootUri = `file://${resolve(rootPath)}`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async start(): Promise<void> {
|
|
180
|
+
if (this.connection) return;
|
|
181
|
+
|
|
182
|
+
this.process = spawn(this.config.command, this.config.args, {
|
|
183
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
this.process.stderr?.on("data", () => {
|
|
187
|
+
// Swallow stderr — language servers are noisy
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
this.process.on("exit", () => {
|
|
191
|
+
this.connection?.dispose();
|
|
192
|
+
this.connection = null;
|
|
193
|
+
this.process = null;
|
|
194
|
+
this.initialized = false;
|
|
195
|
+
this.openFiles.clear();
|
|
196
|
+
this.diagnosticsReceived.clear();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Create vscode-jsonrpc connection — handles Content-Length framing,
|
|
200
|
+
// request/response matching, notification routing. Same as Claude Code.
|
|
201
|
+
this.connection = createMessageConnection(
|
|
202
|
+
new StreamMessageReader(this.process.stdout!),
|
|
203
|
+
new StreamMessageWriter(this.process.stdin!),
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
// Listen for diagnostics notifications (signals file analysis complete)
|
|
207
|
+
this.connection.onNotification("textDocument/publishDiagnostics", (params: unknown) => {
|
|
208
|
+
const p = params as { uri?: string };
|
|
209
|
+
if (p.uri) this.diagnosticsReceived.add(p.uri);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
this.connection.listen();
|
|
213
|
+
|
|
214
|
+
// Initialize handshake
|
|
215
|
+
await this.connection.sendRequest("initialize", {
|
|
216
|
+
processId: globalThis.process.pid,
|
|
217
|
+
rootUri: this.rootUri,
|
|
218
|
+
capabilities: {
|
|
219
|
+
textDocument: {
|
|
220
|
+
definition: { dynamicRegistration: false },
|
|
221
|
+
references: { dynamicRegistration: false },
|
|
222
|
+
hover: { dynamicRegistration: false, contentFormat: ["plaintext", "markdown"] },
|
|
223
|
+
documentSymbol: { dynamicRegistration: false },
|
|
224
|
+
rename: { dynamicRegistration: false, prepareSupport: false },
|
|
225
|
+
publishDiagnostics: { relatedInformation: true },
|
|
226
|
+
},
|
|
227
|
+
workspace: {
|
|
228
|
+
workspaceFolders: true,
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
workspaceFolders: [{ uri: this.rootUri, name: "root" }],
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
this.connection.sendNotification("initialized", {});
|
|
235
|
+
this.initialized = true;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async stop(): Promise<void> {
|
|
239
|
+
if (!this.connection) return;
|
|
240
|
+
try {
|
|
241
|
+
await this.connection.sendRequest("shutdown");
|
|
242
|
+
this.connection.sendNotification("exit");
|
|
243
|
+
} catch {
|
|
244
|
+
// Force kill if graceful shutdown fails
|
|
245
|
+
}
|
|
246
|
+
this.connection.dispose();
|
|
247
|
+
this.connection = null;
|
|
248
|
+
this.process?.kill("SIGTERM");
|
|
249
|
+
this.process = null;
|
|
250
|
+
this.initialized = false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Ensure a file is opened in the language server
|
|
254
|
+
async openFile(filePath: string): Promise<void> {
|
|
255
|
+
const uri = `file://${resolve(filePath)}`;
|
|
256
|
+
if (this.openFiles.has(uri)) return;
|
|
257
|
+
|
|
258
|
+
const languageId = getLanguageId(filePath);
|
|
259
|
+
if (!languageId) return;
|
|
260
|
+
|
|
261
|
+
let text: string;
|
|
262
|
+
try {
|
|
263
|
+
text = readFileSync(resolve(filePath), "utf-8");
|
|
264
|
+
} catch {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
this.connection?.sendNotification("textDocument/didOpen", {
|
|
269
|
+
textDocument: { uri, languageId, version: 1, text },
|
|
270
|
+
});
|
|
271
|
+
this.openFiles.add(uri);
|
|
272
|
+
|
|
273
|
+
// No wait needed. LSP servers queue requests after didOpen and respond
|
|
274
|
+
// when ready. Tested: TypeScript responds to hover in 239ms with zero
|
|
275
|
+
// wait (before diagnostics arrive). Pyright responds after its analysis.
|
|
276
|
+
// The sendRequest in the caller will block until the server responds.
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// --- LSP Operations ---
|
|
280
|
+
|
|
281
|
+
async definition(filePath: string, line: number, character: number): Promise<unknown> {
|
|
282
|
+
await this.ensureReady(filePath);
|
|
283
|
+
return this.connection!.sendRequest("textDocument/definition", {
|
|
284
|
+
textDocument: { uri: `file://${resolve(filePath)}` },
|
|
285
|
+
position: { line, character },
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async references(filePath: string, line: number, character: number): Promise<unknown> {
|
|
290
|
+
await this.ensureReady(filePath);
|
|
291
|
+
return this.connection!.sendRequest("textDocument/references", {
|
|
292
|
+
textDocument: { uri: `file://${resolve(filePath)}` },
|
|
293
|
+
position: { line, character },
|
|
294
|
+
context: { includeDeclaration: true },
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
async hover(filePath: string, line: number, character: number): Promise<unknown> {
|
|
299
|
+
await this.ensureReady(filePath);
|
|
300
|
+
return this.connection!.sendRequest("textDocument/hover", {
|
|
301
|
+
textDocument: { uri: `file://${resolve(filePath)}` },
|
|
302
|
+
position: { line, character },
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async documentSymbols(filePath: string): Promise<unknown> {
|
|
307
|
+
await this.ensureReady(filePath);
|
|
308
|
+
return this.connection!.sendRequest("textDocument/documentSymbol", {
|
|
309
|
+
textDocument: { uri: `file://${resolve(filePath)}` },
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async rename(
|
|
314
|
+
filePath: string,
|
|
315
|
+
line: number,
|
|
316
|
+
character: number,
|
|
317
|
+
newName: string,
|
|
318
|
+
): Promise<unknown> {
|
|
319
|
+
await this.ensureReady(filePath);
|
|
320
|
+
return this.connection!.sendRequest("textDocument/rename", {
|
|
321
|
+
textDocument: { uri: `file://${resolve(filePath)}` },
|
|
322
|
+
position: { line, character },
|
|
323
|
+
newName,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// --- Internals ---
|
|
328
|
+
|
|
329
|
+
private async ensureReady(filePath: string): Promise<void> {
|
|
330
|
+
if (!this.initialized) await this.start();
|
|
331
|
+
await this.openFile(filePath);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// ---------------------------------------------------------------------------
|
|
336
|
+
// Server Pool — one server per language, shared across queries
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
|
|
339
|
+
const serverPool = new Map<string, LspServer>();
|
|
340
|
+
|
|
341
|
+
export function getOrCreateServer(serverName: string, rootPath: string): LspServer {
|
|
342
|
+
const key = `${serverName}:${rootPath}`;
|
|
343
|
+
let server = serverPool.get(key);
|
|
344
|
+
if (!server) {
|
|
345
|
+
server = new LspServer(serverName, rootPath);
|
|
346
|
+
serverPool.set(key, server);
|
|
347
|
+
}
|
|
348
|
+
return server;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export async function shutdownAll(): Promise<void> {
|
|
352
|
+
const stops = [...serverPool.values()].map((s) => s.stop());
|
|
353
|
+
await Promise.allSettled(stops);
|
|
354
|
+
serverPool.clear();
|
|
355
|
+
}
|
package/src/executors/restart.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @aria/tools - Restart executor
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* respawns automatically.
|
|
9
|
-
*
|
|
10
|
-
* 2. Daemon (daemon-launcher): No supervisor loop. The executor spawns a
|
|
11
|
-
* replacement daemon on the process 'exit' event (after the singleton lock
|
|
12
|
-
* is released), then sends SIGINT to trigger graceful shutdown.
|
|
4
|
+
* Single restart path for both TUI and daemon:
|
|
5
|
+
* Register an 'exit' handler to spawn a replacement process, then SIGINT
|
|
6
|
+
* the current process for graceful shutdown. The replacement starts fresh
|
|
7
|
+
* and goes through normal startup (heap check, createCliContext, REPL/daemon).
|
|
13
8
|
*/
|
|
14
9
|
|
|
15
10
|
import type { ToolContext, ToolResult } from "../types.js";
|
|
@@ -18,13 +13,7 @@ import { spawn } from "node:child_process";
|
|
|
18
13
|
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
19
14
|
import { dirname, join, resolve } from "node:path";
|
|
20
15
|
import * as os from "node:os";
|
|
21
|
-
import {
|
|
22
|
-
NO_RELAUNCH_ENV,
|
|
23
|
-
RELAUNCH_EXIT_CODE,
|
|
24
|
-
RESUME_ARION_ENV,
|
|
25
|
-
RESUME_SESSION_ENV,
|
|
26
|
-
writeRelaunchMarker,
|
|
27
|
-
} from "@aria-cli/types";
|
|
16
|
+
import { RESUME_ARION_ENV, RESUME_SESSION_ENV, writeRelaunchMarker } from "@aria-cli/types";
|
|
28
17
|
|
|
29
18
|
export interface RestartInput {
|
|
30
19
|
reason?: string;
|
|
@@ -94,7 +83,6 @@ function findDaemonPid(): number | null {
|
|
|
94
83
|
export async function executeRestart(input: RestartInput, ctx: ToolContext): Promise<ToolResult> {
|
|
95
84
|
if (ctx.abortSignal?.aborted) return fail("Operation cancelled");
|
|
96
85
|
|
|
97
|
-
const supervised = process.env[NO_RELAUNCH_ENV] === "true";
|
|
98
86
|
const reason = (input.reason ?? "").trim() || "Restart requested";
|
|
99
87
|
|
|
100
88
|
// Note: tool definition has requiresConfirmation: true, so the framework
|
|
@@ -115,7 +103,7 @@ export async function executeRestart(input: RestartInput, ctx: ToolContext): Pro
|
|
|
115
103
|
const sessionId = process.env[RESUME_SESSION_ENV] ?? null;
|
|
116
104
|
const arionName = process.env[RESUME_ARION_ENV] || "ARIA";
|
|
117
105
|
|
|
118
|
-
// Write disk-backed marker
|
|
106
|
+
// Write disk-backed marker so the replacement process can resume the session.
|
|
119
107
|
writeRelaunchMarker({
|
|
120
108
|
sessionId,
|
|
121
109
|
arionName,
|
|
@@ -123,46 +111,23 @@ export async function executeRestart(input: RestartInput, ctx: ToolContext): Pro
|
|
|
123
111
|
timestamp: new Date().toISOString(),
|
|
124
112
|
});
|
|
125
113
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
try {
|
|
130
|
-
const send = (process as NodeJS.Process & { send?: (msg: unknown) => void }).send;
|
|
131
|
-
if (typeof send === "function") {
|
|
132
|
-
send({
|
|
133
|
-
type: "aria-relaunch",
|
|
134
|
-
resumeSessionId: sessionId,
|
|
135
|
-
arionName,
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
} catch {
|
|
139
|
-
// non-fatal — disk marker is the fallback
|
|
140
|
-
}
|
|
141
|
-
process.exit(RELAUNCH_EXIT_CODE);
|
|
142
|
-
} else {
|
|
143
|
-
// Path 2: Daemon mode — no supervisor loop.
|
|
144
|
-
// Register an 'exit' handler to spawn the replacement AFTER the singleton
|
|
145
|
-
// lock is released (beforeExit releases it). Then SIGINT ourselves to
|
|
146
|
-
// trigger graceful daemon shutdown. The SIGINT handler fires synchronously
|
|
147
|
-
// (just sets controller.abort()), so the tool can still return success.
|
|
148
|
-
const daemonPid = findDaemonPid() ?? process.pid;
|
|
149
|
-
|
|
150
|
-
process.once("exit", () => {
|
|
151
|
-
spawn(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
|
|
152
|
-
detached: true,
|
|
153
|
-
stdio: "ignore",
|
|
154
|
-
}).unref();
|
|
155
|
-
});
|
|
114
|
+
// Spawn replacement on exit, then SIGINT for graceful shutdown.
|
|
115
|
+
// Works for both TUI and daemon — no supervisor needed.
|
|
116
|
+
const targetPid = findDaemonPid() ?? process.pid;
|
|
156
117
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
118
|
+
process.once("exit", () => {
|
|
119
|
+
spawn(process.execPath, [...process.execArgv, ...process.argv.slice(1)], {
|
|
120
|
+
detached: true,
|
|
121
|
+
stdio: "ignore",
|
|
122
|
+
}).unref();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
process.stderr.write(`[aria] Sending SIGINT to pid ${targetPid} for graceful restart\n`);
|
|
126
|
+
try {
|
|
127
|
+
process.kill(targetPid, "SIGINT");
|
|
128
|
+
} catch {
|
|
129
|
+
process.kill(process.pid, "SIGINT");
|
|
164
130
|
}
|
|
165
131
|
|
|
166
|
-
// Unreachable in supervised path (process.exit above), but returned in daemon path
|
|
167
132
|
return success("Restarting...");
|
|
168
133
|
}
|