glimmervoid 0.0.0-stage → 0.29.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/LICENSE +48 -0
- package/README.md +197 -2
- package/dist/bin/glimmervoid.js +259 -0
- package/dist/chunks/adapters-DIavI9-i.js +2 -0
- package/dist/chunks/adapters-Dlvu1FVB.js +566 -0
- package/dist/chunks/agent-api-cli-ClOzm28U.js +71 -0
- package/dist/chunks/agent-api-core-D6LHrpSL.js +103 -0
- package/dist/chunks/agent-setup-cli-C1CQ8Nkk.js +137 -0
- package/dist/chunks/ascii-figure-core-pDAwtlKO.js +45 -0
- package/dist/chunks/child-process-safe-Cz22RwlG.js +69 -0
- package/dist/chunks/config-path-core-DFt-TemS.js +38 -0
- package/dist/chunks/config-store-DtPUFVaO.js +1400 -0
- package/dist/chunks/config-store-DtUGwi4-.js +2 -0
- package/dist/chunks/contracts-JKChPhZ0.js +2998 -0
- package/dist/chunks/grok-COekzawt.js +407 -0
- package/dist/chunks/hook-relay-core-BHfYFnYV.js +88 -0
- package/dist/chunks/ingest-core-DWI1Mbu4.js +342 -0
- package/dist/chunks/ingest-editor-core-BuI1gMF_.js +445 -0
- package/dist/chunks/loopback-post-C7C22eYi.js +79 -0
- package/dist/chunks/main-BVDjy7vH.js +42740 -0
- package/dist/chunks/node-pty-preflight-CK1knGIR.js +162 -0
- package/dist/chunks/pair-cli-BQQnhkkc.js +110 -0
- package/dist/chunks/path-doctor-C23VAqeu.js +63 -0
- package/dist/chunks/refocus-core-BKQKllq7.js +38 -0
- package/dist/chunks/relay-stdin-CpHvLu6M.js +20 -0
- package/dist/chunks/remote-config-BHoDFCpo.js +521 -0
- package/dist/chunks/rtk-hook-core-DRYrKwYP.js +26 -0
- package/dist/chunks/rtk-resolver-DDVVNRBT.js +2 -0
- package/dist/chunks/rtk-resolver-oGmO-ew7.js +58 -0
- package/dist/chunks/runtime-paths-CVvq20A4.js +51 -0
- package/dist/chunks/sandbox-deps-BwvbDBDl.js +67 -0
- package/dist/chunks/session-CJtOveHl.js +162 -0
- package/dist/chunks/spawn-command-Cc40FiZI.js +134 -0
- package/dist/chunks/visions-cli-DUZnopGY.js +98 -0
- package/dist/chunks/visions-setup-C_kJobsy.js +1753 -0
- package/dist/client/assets/favicon-CKygeLwW.svg +7 -0
- package/dist/client/assets/index-BTwYgSAH.js +248 -0
- package/dist/client/assets/index-DgA708PE.css +1 -0
- package/dist/client/index.html +108 -0
- package/dist/package.json +3 -0
- package/dist/scripts/postinstall-path-check.js +36 -0
- package/dist/server/index.js +92 -0
- package/dist/session/command-hook-relay.js +16 -0
- package/dist/session/hook-relay.js +24 -0
- package/dist/session/rtk-relay.js +90 -0
- package/dist/session/statusline-relay.js +82 -0
- package/dist/session/visions-relay.js +627 -0
- package/dist/tools/vscode-visions/extension.js +289 -0
- package/dist/tools/vscode-visions/lsp-convert.js +80 -0
- package/dist/tools/vscode-visions/package.json +32 -0
- package/dist/tools/vscode-visions/visions-lsp-core.js +112 -0
- package/package.json +95 -4
- package/scripts/postinstall.mjs +4 -0
- package/scripts/prepare-build.js +38 -0
- package/scripts/recover-handoff.mjs +82 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
//#region tools/vscode-visions/extension.ts
|
|
2
|
+
var fs = require("node:fs");
|
|
3
|
+
var path = require("node:path");
|
|
4
|
+
var { spawn } = require("node:child_process");
|
|
5
|
+
var vscode = require("vscode");
|
|
6
|
+
var { createParserState, feedFrameBytes, serializeFrame } = require("./visions-lsp-core.js");
|
|
7
|
+
var { decideEditFreshness, toCodeActions, toDiagnostics, toWorkspaceEdit } = require("./lsp-convert.js");
|
|
8
|
+
var LANGUAGE_ID = "markdown";
|
|
9
|
+
var ACTIVITY_METHOD = "visions/editorActivity";
|
|
10
|
+
var CODE_ACTION_TIMEOUT_MS = 2e3;
|
|
11
|
+
var INITIAL_RESTART_MS = 1e3;
|
|
12
|
+
var MAX_RESTART_MS = 15e3;
|
|
13
|
+
function stampedRelayPath() {
|
|
14
|
+
try {
|
|
15
|
+
const stamp = JSON.parse(fs.readFileSync(path.join(__dirname, "relay-path.json"), "utf8"));
|
|
16
|
+
if (typeof stamp !== "object" || stamp === null) return "";
|
|
17
|
+
if (!("relayPath" in stamp) || typeof stamp.relayPath !== "string") return "";
|
|
18
|
+
return stamp.relayPath;
|
|
19
|
+
} catch {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function resolveRelayPath(settings, env) {
|
|
24
|
+
const candidates = [
|
|
25
|
+
settings.get("relayPath", ""),
|
|
26
|
+
env.GLIMMERVOID_RELAY_PATH || "",
|
|
27
|
+
stampedRelayPath()
|
|
28
|
+
];
|
|
29
|
+
for (const candidate of candidates) if (candidate && fs.existsSync(candidate)) return candidate;
|
|
30
|
+
return "";
|
|
31
|
+
}
|
|
32
|
+
function relayArgs(relayPath, port) {
|
|
33
|
+
if (!Number.isInteger(port) || port <= 0) return [relayPath];
|
|
34
|
+
return [
|
|
35
|
+
relayPath,
|
|
36
|
+
"--port",
|
|
37
|
+
String(port)
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
function activate(context) {
|
|
41
|
+
const output = vscode.window.createOutputChannel("Glimmervoid Visions");
|
|
42
|
+
const diagnostics = vscode.languages.createDiagnosticCollection("glimmervoid-visions");
|
|
43
|
+
context.subscriptions.push(output, diagnostics);
|
|
44
|
+
const settings = vscode.workspace.getConfiguration("glimmervoidVisions");
|
|
45
|
+
const relayPath = resolveRelayPath(settings, process.env);
|
|
46
|
+
if (!relayPath) {
|
|
47
|
+
output.appendLine("no relay found: reinstall with `glimmervoid visions install`, or set glimmervoidVisions.relayPath");
|
|
48
|
+
vscode.window.showErrorMessage("Glimmervoid Visions found no relay. Run `glimmervoid visions install` again, or set glimmervoidVisions.relayPath.");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const client = createRelayClient({
|
|
52
|
+
relayPath,
|
|
53
|
+
port: settings.get("port", 0),
|
|
54
|
+
output,
|
|
55
|
+
diagnostics
|
|
56
|
+
});
|
|
57
|
+
context.subscriptions.push({ dispose: () => client.stop() });
|
|
58
|
+
client.start();
|
|
59
|
+
context.subscriptions.push(vscode.workspace.onDidOpenTextDocument((document) => client.didOpen(document)), vscode.workspace.onDidChangeTextDocument((event) => client.didChange(event.document)), vscode.workspace.onDidSaveTextDocument((document) => client.didSave(document)), vscode.workspace.onDidCloseTextDocument((document) => client.didClose(document)), vscode.languages.registerCodeActionsProvider({ language: LANGUAGE_ID }, { provideCodeActions: (document, range, actionContext) => client.codeActions(document, range, actionContext) }, { providedCodeActionKinds: [vscode.CodeActionKind.QuickFix] }));
|
|
60
|
+
}
|
|
61
|
+
function createRelayClient({ relayPath, port, output, diagnostics }) {
|
|
62
|
+
let child = null;
|
|
63
|
+
let parserState = createParserState();
|
|
64
|
+
let nextRequestId = 1;
|
|
65
|
+
let restartMs = INITIAL_RESTART_MS;
|
|
66
|
+
let restartTimer = null;
|
|
67
|
+
let isStopping = false;
|
|
68
|
+
const pendingById = /* @__PURE__ */ new Map();
|
|
69
|
+
const openVersionByUri = /* @__PURE__ */ new Map();
|
|
70
|
+
function send(message) {
|
|
71
|
+
if (!child || child.killed || !child.stdin.writable) return false;
|
|
72
|
+
child.stdin.write(serializeFrame(message));
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
function notify(method, params) {
|
|
76
|
+
return send({
|
|
77
|
+
jsonrpc: "2.0",
|
|
78
|
+
method,
|
|
79
|
+
params
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function request(method, params, timeoutMs) {
|
|
83
|
+
return new Promise((resolve) => {
|
|
84
|
+
const id = nextRequestId;
|
|
85
|
+
nextRequestId += 1;
|
|
86
|
+
const timer = setTimeout(() => settle(id, null), timeoutMs);
|
|
87
|
+
pendingById.set(id, {
|
|
88
|
+
resolve,
|
|
89
|
+
timer
|
|
90
|
+
});
|
|
91
|
+
if (!send({
|
|
92
|
+
jsonrpc: "2.0",
|
|
93
|
+
id,
|
|
94
|
+
method,
|
|
95
|
+
params
|
|
96
|
+
})) settle(id, null);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function settle(id, result) {
|
|
100
|
+
const pending = pendingById.get(id);
|
|
101
|
+
if (!pending) return;
|
|
102
|
+
pendingById.delete(id);
|
|
103
|
+
clearTimeout(pending.timer);
|
|
104
|
+
pending.resolve(result);
|
|
105
|
+
}
|
|
106
|
+
function failPending() {
|
|
107
|
+
for (const id of [...pendingById.keys()]) settle(id, null);
|
|
108
|
+
}
|
|
109
|
+
function isMirrored(document) {
|
|
110
|
+
return document.languageId === LANGUAGE_ID;
|
|
111
|
+
}
|
|
112
|
+
function reportActivity(document, method) {
|
|
113
|
+
if (document.uri.scheme !== "file") return;
|
|
114
|
+
notify(ACTIVITY_METHOD, {
|
|
115
|
+
uri: document.uri.toString(),
|
|
116
|
+
method
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function didOpen(document) {
|
|
120
|
+
if (!isMirrored(document)) {
|
|
121
|
+
reportActivity(document, "textDocument/didOpen");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
openVersionByUri.set(document.uri.toString(), document.version);
|
|
125
|
+
notify("textDocument/didOpen", { textDocument: {
|
|
126
|
+
uri: document.uri.toString(),
|
|
127
|
+
languageId: document.languageId,
|
|
128
|
+
version: document.version,
|
|
129
|
+
text: document.getText()
|
|
130
|
+
} });
|
|
131
|
+
}
|
|
132
|
+
function didChange(document) {
|
|
133
|
+
if (!isMirrored(document)) return;
|
|
134
|
+
if (!openVersionByUri.has(document.uri.toString())) {
|
|
135
|
+
didOpen(document);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
openVersionByUri.set(document.uri.toString(), document.version);
|
|
139
|
+
notify("textDocument/didChange", {
|
|
140
|
+
textDocument: {
|
|
141
|
+
uri: document.uri.toString(),
|
|
142
|
+
version: document.version
|
|
143
|
+
},
|
|
144
|
+
contentChanges: [{ text: document.getText() }]
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
function didSave(document) {
|
|
148
|
+
if (!isMirrored(document)) {
|
|
149
|
+
reportActivity(document, "textDocument/didSave");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
notify("textDocument/didSave", { textDocument: { uri: document.uri.toString() } });
|
|
153
|
+
}
|
|
154
|
+
function didClose(document) {
|
|
155
|
+
if (!isMirrored(document)) {
|
|
156
|
+
reportActivity(document, "textDocument/didClose");
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
openVersionByUri.delete(document.uri.toString());
|
|
160
|
+
diagnostics.delete(document.uri);
|
|
161
|
+
notify("textDocument/didClose", { textDocument: { uri: document.uri.toString() } });
|
|
162
|
+
}
|
|
163
|
+
async function codeActions(document, range, actionContext) {
|
|
164
|
+
if (!isMirrored(document)) return [];
|
|
165
|
+
return toCodeActions(vscode, await request("textDocument/codeAction", {
|
|
166
|
+
textDocument: { uri: document.uri.toString() },
|
|
167
|
+
range: {
|
|
168
|
+
start: range.start,
|
|
169
|
+
end: range.end
|
|
170
|
+
},
|
|
171
|
+
context: {
|
|
172
|
+
diagnostics: [],
|
|
173
|
+
triggerKind: actionContext?.triggerKind
|
|
174
|
+
}
|
|
175
|
+
}, CODE_ACTION_TIMEOUT_MS));
|
|
176
|
+
}
|
|
177
|
+
function versionOfUri(uri) {
|
|
178
|
+
if (!openVersionByUri.has(uri)) return null;
|
|
179
|
+
return openVersionByUri.get(uri) ?? null;
|
|
180
|
+
}
|
|
181
|
+
async function handleApplyEdit(id, params) {
|
|
182
|
+
const edit = params?.edit;
|
|
183
|
+
const freshness = decideEditFreshness(edit, versionOfUri);
|
|
184
|
+
if (!freshness.fresh) {
|
|
185
|
+
output.appendLine(`applyEdit refused: ${freshness.reason}`);
|
|
186
|
+
return send({
|
|
187
|
+
jsonrpc: "2.0",
|
|
188
|
+
id,
|
|
189
|
+
result: { applied: false }
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return send({
|
|
193
|
+
jsonrpc: "2.0",
|
|
194
|
+
id,
|
|
195
|
+
result: { applied: await vscode.workspace.applyEdit(toWorkspaceEdit(vscode, edit)) }
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
function handleMessage(message) {
|
|
199
|
+
if (message?.method === "textDocument/publishDiagnostics") {
|
|
200
|
+
const uri = message.params?.uri;
|
|
201
|
+
if (typeof uri !== "string") return;
|
|
202
|
+
diagnostics.set(vscode.Uri.parse(uri), toDiagnostics(vscode, message.params?.diagnostics));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (message?.method === "workspace/applyEdit" && message.id !== void 0) {
|
|
206
|
+
handleApplyEdit(message.id, message.params);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (message?.id !== void 0 && message.method === void 0) settle(message.id, message.result ?? null);
|
|
210
|
+
}
|
|
211
|
+
function openMirroredDocuments() {
|
|
212
|
+
for (const document of vscode.workspace.textDocuments) didOpen(document);
|
|
213
|
+
}
|
|
214
|
+
async function handshake() {
|
|
215
|
+
await request("initialize", {
|
|
216
|
+
processId: process.pid,
|
|
217
|
+
rootUri: null,
|
|
218
|
+
capabilities: {},
|
|
219
|
+
clientInfo: { name: "glimmervoid-visions-extension" }
|
|
220
|
+
}, CODE_ACTION_TIMEOUT_MS);
|
|
221
|
+
notify("initialized", {});
|
|
222
|
+
openMirroredDocuments();
|
|
223
|
+
}
|
|
224
|
+
function scheduleRestart() {
|
|
225
|
+
if (isStopping || restartTimer) return;
|
|
226
|
+
output.appendLine(`relay exited; restarting in ${restartMs}ms`);
|
|
227
|
+
restartTimer = setTimeout(() => {
|
|
228
|
+
restartTimer = null;
|
|
229
|
+
start();
|
|
230
|
+
}, restartMs);
|
|
231
|
+
restartMs = Math.min(restartMs * 2, MAX_RESTART_MS);
|
|
232
|
+
}
|
|
233
|
+
function start() {
|
|
234
|
+
if (isStopping) return;
|
|
235
|
+
parserState = createParserState();
|
|
236
|
+
openVersionByUri.clear();
|
|
237
|
+
const relay = spawn(process.execPath, relayArgs(relayPath, port), {
|
|
238
|
+
env: {
|
|
239
|
+
...process.env,
|
|
240
|
+
ELECTRON_RUN_AS_NODE: "1"
|
|
241
|
+
},
|
|
242
|
+
windowsHide: true
|
|
243
|
+
});
|
|
244
|
+
child = relay;
|
|
245
|
+
output.appendLine(`relay started: ${relayPath}`);
|
|
246
|
+
relay.stdout.on("data", (chunk) => {
|
|
247
|
+
const parsed = feedFrameBytes(parserState, Buffer.from(chunk));
|
|
248
|
+
parserState = parsed.state;
|
|
249
|
+
for (const message of parsed.messages) handleMessage(message);
|
|
250
|
+
});
|
|
251
|
+
relay.stderr.on("data", (chunk) => output.append(String(chunk)));
|
|
252
|
+
relay.on("error", (error) => output.appendLine(`relay failed to start: ${error.message}`));
|
|
253
|
+
relay.on("exit", (code) => {
|
|
254
|
+
failPending();
|
|
255
|
+
diagnostics.clear();
|
|
256
|
+
output.appendLine(`relay exit code ${code}`);
|
|
257
|
+
scheduleRestart();
|
|
258
|
+
});
|
|
259
|
+
handshake();
|
|
260
|
+
}
|
|
261
|
+
function stop() {
|
|
262
|
+
isStopping = true;
|
|
263
|
+
if (restartTimer) clearTimeout(restartTimer);
|
|
264
|
+
restartTimer = null;
|
|
265
|
+
failPending();
|
|
266
|
+
if (!child) return;
|
|
267
|
+
notify("shutdown", {});
|
|
268
|
+
notify("exit", {});
|
|
269
|
+
child.kill();
|
|
270
|
+
child = null;
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
codeActions,
|
|
274
|
+
didChange,
|
|
275
|
+
didClose,
|
|
276
|
+
didOpen,
|
|
277
|
+
didSave,
|
|
278
|
+
start,
|
|
279
|
+
stop
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function deactivate() {}
|
|
283
|
+
module.exports = {
|
|
284
|
+
activate,
|
|
285
|
+
deactivate,
|
|
286
|
+
resolveRelayPath,
|
|
287
|
+
relayArgs
|
|
288
|
+
};
|
|
289
|
+
//#endregion
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
//#region tools/vscode-visions/lsp-convert.ts
|
|
2
|
+
var SEVERITY_BY_LSP = {
|
|
3
|
+
1: "Error",
|
|
4
|
+
2: "Warning",
|
|
5
|
+
3: "Information",
|
|
6
|
+
4: "Hint"
|
|
7
|
+
};
|
|
8
|
+
function toPosition(vscodeApi, position) {
|
|
9
|
+
const line = Number.isInteger(position?.line) ? Number(position?.line) : 0;
|
|
10
|
+
const character = Number.isInteger(position?.character) ? Number(position?.character) : 0;
|
|
11
|
+
return new vscodeApi.Position(line, character);
|
|
12
|
+
}
|
|
13
|
+
function toRange(vscodeApi, range) {
|
|
14
|
+
return new vscodeApi.Range(toPosition(vscodeApi, range?.start), toPosition(vscodeApi, range?.end));
|
|
15
|
+
}
|
|
16
|
+
function toDiagnostic(vscodeApi, diagnostic) {
|
|
17
|
+
const converted = new vscodeApi.Diagnostic(toRange(vscodeApi, diagnostic?.range), typeof diagnostic?.message === "string" ? diagnostic.message : "", vscodeApi.DiagnosticSeverity[SEVERITY_BY_LSP[Number(diagnostic?.severity)] || "Warning"]);
|
|
18
|
+
if (diagnostic?.code !== void 0) converted.code = diagnostic.code;
|
|
19
|
+
if (typeof diagnostic?.source === "string") converted.source = diagnostic.source;
|
|
20
|
+
return converted;
|
|
21
|
+
}
|
|
22
|
+
function toDiagnostics(vscodeApi, diagnostics) {
|
|
23
|
+
if (!Array.isArray(diagnostics)) return [];
|
|
24
|
+
return diagnostics.map((diagnostic) => toDiagnostic(vscodeApi, diagnostic));
|
|
25
|
+
}
|
|
26
|
+
function documentChangesOf(edit) {
|
|
27
|
+
const changes = edit?.documentChanges;
|
|
28
|
+
if (!Array.isArray(changes)) return [];
|
|
29
|
+
return changes.filter((change) => typeof change?.textDocument?.uri === "string");
|
|
30
|
+
}
|
|
31
|
+
function toWorkspaceEdit(vscodeApi, edit) {
|
|
32
|
+
const workspaceEdit = new vscodeApi.WorkspaceEdit();
|
|
33
|
+
for (const change of documentChangesOf(edit)) {
|
|
34
|
+
const uri = vscodeApi.Uri.parse(change.textDocument.uri);
|
|
35
|
+
for (const textEdit of Array.isArray(change.edits) ? change.edits : []) workspaceEdit.replace(uri, toRange(vscodeApi, textEdit?.range), typeof textEdit?.newText === "string" ? textEdit.newText : "");
|
|
36
|
+
}
|
|
37
|
+
return workspaceEdit;
|
|
38
|
+
}
|
|
39
|
+
function toCodeAction(vscodeApi, action) {
|
|
40
|
+
const converted = new vscodeApi.CodeAction(typeof action?.title === "string" ? action.title : "Visions fix", vscodeApi.CodeActionKind.QuickFix);
|
|
41
|
+
if (action?.edit) converted.edit = toWorkspaceEdit(vscodeApi, action.edit);
|
|
42
|
+
const diagnostics = action?.diagnostics;
|
|
43
|
+
if (Array.isArray(diagnostics)) converted.diagnostics = toDiagnostics(vscodeApi, diagnostics);
|
|
44
|
+
return converted;
|
|
45
|
+
}
|
|
46
|
+
function toCodeActions(vscodeApi, actions) {
|
|
47
|
+
if (!Array.isArray(actions)) return [];
|
|
48
|
+
return actions.map((action) => toCodeAction(vscodeApi, action));
|
|
49
|
+
}
|
|
50
|
+
function decideEditFreshness(edit, versionOfUri) {
|
|
51
|
+
const changes = documentChangesOf(edit);
|
|
52
|
+
if (changes.length === 0) return {
|
|
53
|
+
fresh: false,
|
|
54
|
+
reason: "no-document-changes"
|
|
55
|
+
};
|
|
56
|
+
for (const change of changes) {
|
|
57
|
+
const expected = change.textDocument.version;
|
|
58
|
+
if (expected === null || expected === void 0) continue;
|
|
59
|
+
const current = versionOfUri(change.textDocument.uri);
|
|
60
|
+
if (current === null || current === void 0) continue;
|
|
61
|
+
if (current !== expected) return {
|
|
62
|
+
fresh: false,
|
|
63
|
+
reason: "stale-version"
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
fresh: true,
|
|
68
|
+
reason: "ok"
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
module.exports = {
|
|
72
|
+
decideEditFreshness,
|
|
73
|
+
toCodeAction,
|
|
74
|
+
toCodeActions,
|
|
75
|
+
toDiagnostic,
|
|
76
|
+
toDiagnostics,
|
|
77
|
+
toRange,
|
|
78
|
+
toWorkspaceEdit
|
|
79
|
+
};
|
|
80
|
+
//#endregion
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "glimmervoid-visions",
|
|
3
|
+
"displayName": "Glimmervoid Visions",
|
|
4
|
+
"publisher": "johnwaters",
|
|
5
|
+
"version": "0.3.0",
|
|
6
|
+
"private": true,
|
|
7
|
+
"engines": {
|
|
8
|
+
"vscode": "^1.85.0"
|
|
9
|
+
},
|
|
10
|
+
"main": "./extension.js",
|
|
11
|
+
"activationEvents": [
|
|
12
|
+
"onStartupFinished"
|
|
13
|
+
],
|
|
14
|
+
"contributes": {
|
|
15
|
+
"configuration": {
|
|
16
|
+
"title": "Glimmervoid Visions",
|
|
17
|
+
"properties": {
|
|
18
|
+
"glimmervoidVisions.relayPath": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"default": "",
|
|
21
|
+
"description": "Absolute path to the visions relay (dist/session/visions-relay.js in an installed package, session/visions-relay.ts in a source checkout). Empty uses the path stamped at install time."
|
|
22
|
+
},
|
|
23
|
+
"glimmervoidVisions.port": {
|
|
24
|
+
"type": "number",
|
|
25
|
+
"default": 0,
|
|
26
|
+
"description": "Relay port. Use 0 to let the relay choose its fallback."
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"description": "Mirrors markdown buffers to the Glimmervoid daemon and shows its findings and silent fixes."
|
|
32
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region server/core/visions-lsp-core.ts
|
|
3
|
+
var HEADER_END = "\r\n\r\n";
|
|
4
|
+
var CONTENT_LENGTH_RE = /^content-length:\s*(\d+)\s*$/i;
|
|
5
|
+
function createParserState() {
|
|
6
|
+
return {
|
|
7
|
+
buffer: Buffer.alloc(0),
|
|
8
|
+
neededBodyBytes: null
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function feedFrameBytes(state, chunk) {
|
|
12
|
+
const nextState = state || createParserState();
|
|
13
|
+
nextState.buffer = Buffer.concat([nextState.buffer, chunk]);
|
|
14
|
+
const messages = [];
|
|
15
|
+
while (true) {
|
|
16
|
+
const bodyBytes = readNeededBodyBytes(nextState);
|
|
17
|
+
if (bodyBytes === null) break;
|
|
18
|
+
if (typeof bodyBytes !== "number") {
|
|
19
|
+
messages.push(bodyBytes);
|
|
20
|
+
nextState.neededBodyBytes = null;
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (nextState.buffer.length < bodyBytes) break;
|
|
24
|
+
const body = nextState.buffer.subarray(0, bodyBytes);
|
|
25
|
+
nextState.buffer = nextState.buffer.subarray(bodyBytes);
|
|
26
|
+
nextState.neededBodyBytes = null;
|
|
27
|
+
messages.push(parseBody(body));
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
state: nextState,
|
|
31
|
+
messages
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function readNeededBodyBytes(state) {
|
|
35
|
+
if (state.neededBodyBytes !== null) return state.neededBodyBytes;
|
|
36
|
+
const headerEndAt = state.buffer.indexOf(HEADER_END);
|
|
37
|
+
if (headerEndAt === -1) return null;
|
|
38
|
+
const headerText = state.buffer.subarray(0, headerEndAt).toString("ascii");
|
|
39
|
+
state.buffer = state.buffer.subarray(headerEndAt + 4);
|
|
40
|
+
const contentLength = contentLengthFromHeader(headerText);
|
|
41
|
+
if (contentLength === null) {
|
|
42
|
+
state.buffer = Buffer.alloc(0);
|
|
43
|
+
return {
|
|
44
|
+
parseError: true,
|
|
45
|
+
reason: "missing-content-length",
|
|
46
|
+
raw: headerText
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
state.neededBodyBytes = contentLength;
|
|
50
|
+
return state.neededBodyBytes;
|
|
51
|
+
}
|
|
52
|
+
function contentLengthFromHeader(headerText) {
|
|
53
|
+
const lines = headerText.split("\r\n");
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
const match = line.match(CONTENT_LENGTH_RE);
|
|
56
|
+
if (!match) continue;
|
|
57
|
+
return Number(match[1]);
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function parseBody(body) {
|
|
62
|
+
const raw = body.toString("utf8");
|
|
63
|
+
try {
|
|
64
|
+
return JSON.parse(raw);
|
|
65
|
+
} catch {
|
|
66
|
+
return {
|
|
67
|
+
parseError: true,
|
|
68
|
+
raw
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function serializeFrame(messageObject) {
|
|
73
|
+
const body = JSON.stringify(messageObject);
|
|
74
|
+
const bodyBytes = Buffer.byteLength(body, "utf8");
|
|
75
|
+
return Buffer.from(`Content-Length: ${bodyBytes}\r\n\r\n${body}`, "utf8");
|
|
76
|
+
}
|
|
77
|
+
function classifyMessage(msg) {
|
|
78
|
+
if (!msg || typeof msg !== "object") return {
|
|
79
|
+
kind: "invalid",
|
|
80
|
+
method: void 0,
|
|
81
|
+
id: void 0
|
|
82
|
+
};
|
|
83
|
+
const fields = msg;
|
|
84
|
+
const hasMethod = typeof fields.method === "string";
|
|
85
|
+
const hasId = Object.hasOwn(msg, "id");
|
|
86
|
+
const method = typeof fields.method === "string" ? fields.method : void 0;
|
|
87
|
+
if (hasMethod && hasId) return {
|
|
88
|
+
kind: "request",
|
|
89
|
+
method,
|
|
90
|
+
id: fields.id
|
|
91
|
+
};
|
|
92
|
+
if (hasMethod) return {
|
|
93
|
+
kind: "notification",
|
|
94
|
+
method,
|
|
95
|
+
id: void 0
|
|
96
|
+
};
|
|
97
|
+
if (hasId) return {
|
|
98
|
+
kind: "response",
|
|
99
|
+
method: void 0,
|
|
100
|
+
id: fields.id
|
|
101
|
+
};
|
|
102
|
+
return {
|
|
103
|
+
kind: "invalid",
|
|
104
|
+
method: void 0,
|
|
105
|
+
id: void 0
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//#endregion
|
|
109
|
+
exports.classifyMessage = classifyMessage;
|
|
110
|
+
exports.createParserState = createParserState;
|
|
111
|
+
exports.feedFrameBytes = feedFrameBytes;
|
|
112
|
+
exports.serializeFrame = serializeFrame;
|
package/package.json
CHANGED
|
@@ -1,6 +1,97 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glimmervoid",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.29.1",
|
|
4
|
+
"description": "Mission control for coding agents: run and supervise Claude Code, Codex and Grok sessions in parallel git worktrees from one local browser dashboard",
|
|
5
|
+
"main": "dist/server/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"imports": {
|
|
8
|
+
"#shared/*": "./shared/*"
|
|
9
|
+
},
|
|
10
|
+
"bin": {
|
|
11
|
+
"glimmervoid": "dist/bin/glimmervoid.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist/",
|
|
15
|
+
"!dist/client/pictures/",
|
|
16
|
+
"!dist/client/AGENTS.md",
|
|
17
|
+
"scripts/postinstall.mjs",
|
|
18
|
+
"scripts/recover-handoff.mjs",
|
|
19
|
+
"scripts/prepare-build.js"
|
|
20
|
+
],
|
|
21
|
+
"engines": {
|
|
22
|
+
"node": ">=22.18.0"
|
|
23
|
+
},
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"author": "John C. Waters <john@johncwaters.com>",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/johncwaters/glimmervoid.git"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/johncwaters/glimmervoid#readme",
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/johncwaters/glimmervoid/issues"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"claude",
|
|
39
|
+
"claude-code",
|
|
40
|
+
"codex",
|
|
41
|
+
"coding-agents",
|
|
42
|
+
"ai-agents",
|
|
43
|
+
"agent-orchestrator",
|
|
44
|
+
"agent-manager",
|
|
45
|
+
"parallel-agents",
|
|
46
|
+
"git-worktree",
|
|
47
|
+
"terminal",
|
|
48
|
+
"dashboard",
|
|
49
|
+
"session-manager",
|
|
50
|
+
"xterm",
|
|
51
|
+
"windows",
|
|
52
|
+
"linux",
|
|
53
|
+
"macos"
|
|
54
|
+
],
|
|
55
|
+
"scripts": {
|
|
56
|
+
"start": "node dist/server/index.js",
|
|
57
|
+
"lint": "biome check .",
|
|
58
|
+
"test": "node --import ./tests/helpers/isolate-home.ts --test \"tests/**/*.test.ts\"",
|
|
59
|
+
"typecheck": "tsc --project tsconfig.json && tsc --project tsconfig.public.json && tsc --project tsconfig.browser-harness.json",
|
|
60
|
+
"test:browser": "node test/browser/harness.ts",
|
|
61
|
+
"site:capture": "node test/site-capture/capture.ts",
|
|
62
|
+
"site:record": "node test/site-capture/record.ts",
|
|
63
|
+
"site:dev": "npm --prefix site run dev",
|
|
64
|
+
"site:build": "npm --prefix site run build",
|
|
65
|
+
"test:container": "docker build -f test/container/Dockerfile -t glimmervoid-remote-test . && docker run --rm glimmervoid-remote-test",
|
|
66
|
+
"dev": "vite",
|
|
67
|
+
"build": "node scripts/build.mjs",
|
|
68
|
+
"postinstall": "node scripts/postinstall.mjs",
|
|
69
|
+
"prepare": "node scripts/prepare-build.js",
|
|
70
|
+
"release": "node scripts/release.ts",
|
|
71
|
+
"docs:config": "node scripts/generate-config-docs.ts"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@parcel/watcher": "^2.6.0",
|
|
75
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
76
|
+
"@xterm/addon-serialize": "^0.14.0",
|
|
77
|
+
"@xterm/addon-webgl": "^0.19.0",
|
|
78
|
+
"@xterm/headless": "^6.0.0",
|
|
79
|
+
"@xterm/xterm": "^6.0.0",
|
|
80
|
+
"express": "^5.2.1",
|
|
81
|
+
"node-pty": "^1.1.0",
|
|
82
|
+
"ws": "^8.22.0",
|
|
83
|
+
"zod": "^4.6.5"
|
|
84
|
+
},
|
|
85
|
+
"allowScripts": {
|
|
86
|
+
"node-pty": true
|
|
87
|
+
},
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"@biomejs/biome": "^2.5.14",
|
|
90
|
+
"@tailwindcss/vite": "^4.3.3",
|
|
91
|
+
"@types/node": "^26.6.3",
|
|
92
|
+
"playwright-core": "1.63.0",
|
|
93
|
+
"tailwindcss": "^4.3.3",
|
|
94
|
+
"typescript": "^7.0.2",
|
|
95
|
+
"vite": "^8.3.1"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
const viteBin = path.join(import.meta.dirname, '..', 'node_modules', '.bin', process.platform === 'win32' ? 'vite.cmd' : 'vite');
|
|
6
|
+
|
|
7
|
+
function npmInvocation(args) {
|
|
8
|
+
const npmExecPath = process.env.npm_execpath;
|
|
9
|
+
if (npmExecPath && /\.[cm]?js$/.test(npmExecPath)) {
|
|
10
|
+
return { command: process.execPath, args: [npmExecPath, ...args], shell: false };
|
|
11
|
+
}
|
|
12
|
+
if (process.platform === 'win32') {
|
|
13
|
+
return { command: 'npm', args, shell: true };
|
|
14
|
+
}
|
|
15
|
+
return { command: 'npm', args, shell: false };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function runNpm(args, env = {}) {
|
|
19
|
+
const invocation = npmInvocation(args);
|
|
20
|
+
const child = spawnSync(invocation.command, invocation.args, {
|
|
21
|
+
env: { ...process.env, ...env, npm_config_global: 'false' },
|
|
22
|
+
stdio: 'inherit',
|
|
23
|
+
shell: invocation.shell,
|
|
24
|
+
});
|
|
25
|
+
if (child.error) {
|
|
26
|
+
console.error(`prepare-build: npm failed to start: ${child.error.message}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
if (child.status === 0) return;
|
|
30
|
+
process.exit(child.status || 1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!fs.existsSync(viteBin)) {
|
|
34
|
+
runNpm(['ci', '--include=dev', '--ignore-scripts', '--no-audit', '--no-fund']);
|
|
35
|
+
}
|
|
36
|
+
runNpm(['run', 'build']);
|
|
37
|
+
|
|
38
|
+
spawnSync(process.execPath, [path.join(import.meta.dirname, 'postinstall.mjs')], { stdio: 'inherit' });
|