@inspecto-dev/plugin 0.2.0-alpha.0 → 0.2.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -0
- package/dist/index.cjs +431 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +436 -103
- package/dist/index.js.map +1 -1
- package/dist/legacy/rspack/index.cjs +402 -98
- package/dist/legacy/rspack/index.cjs.map +1 -1
- package/dist/legacy/rspack/index.js +407 -99
- package/dist/legacy/rspack/index.js.map +1 -1
- package/dist/legacy/rspack/loader.cjs +8 -1
- package/dist/legacy/rspack/loader.cjs.map +1 -1
- package/dist/legacy/rspack/loader.js +8 -1
- package/dist/legacy/rspack/loader.js.map +1 -1
- package/dist/legacy/webpack4/index.cjs +977 -0
- package/dist/legacy/webpack4/index.cjs.map +1 -0
- package/dist/legacy/webpack4/index.d.cts +8 -0
- package/dist/legacy/webpack4/index.d.ts +8 -0
- package/dist/legacy/webpack4/index.js +953 -0
- package/dist/legacy/webpack4/index.js.map +1 -0
- package/dist/legacy/webpack4/loader.cjs +347 -0
- package/dist/legacy/webpack4/loader.cjs.map +1 -0
- package/dist/legacy/webpack4/loader.d.cts +3 -0
- package/dist/legacy/webpack4/loader.d.ts +3 -0
- package/dist/legacy/webpack4/loader.js +314 -0
- package/dist/legacy/webpack4/loader.js.map +1 -0
- package/dist/rollup.cjs +431 -102
- package/dist/rollup.cjs.map +1 -1
- package/dist/rollup.js +436 -103
- package/dist/rollup.js.map +1 -1
- package/dist/rspack.cjs +431 -102
- package/dist/rspack.cjs.map +1 -1
- package/dist/rspack.js +436 -103
- package/dist/rspack.js.map +1 -1
- package/dist/vite.cjs +431 -102
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +436 -103
- package/dist/vite.js.map +1 -1
- package/dist/webpack.cjs +431 -102
- package/dist/webpack.cjs.map +1 -1
- package/dist/webpack.js +436 -103
- package/dist/webpack.js.map +1 -1
- package/package.json +19 -3
|
@@ -0,0 +1,953 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/esm_shims.js
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
11
|
+
var getFilename = () => fileURLToPath(import.meta.url);
|
|
12
|
+
var getDirname = () => path.dirname(getFilename());
|
|
13
|
+
var __dirname = /* @__PURE__ */ getDirname();
|
|
14
|
+
|
|
15
|
+
// src/injectors/webpack.ts
|
|
16
|
+
function getWebpackHtmlScript(serverPort) {
|
|
17
|
+
return `
|
|
18
|
+
window.__AI_INSPECTOR_PORT__ = ${serverPort};
|
|
19
|
+
window.addEventListener('load', () => {
|
|
20
|
+
if (window.InspectoClient) {
|
|
21
|
+
window.InspectoClient.mountInspector({
|
|
22
|
+
serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/injectors/utils.ts
|
|
30
|
+
import { createRequire } from "module";
|
|
31
|
+
var resolveClientModule = () => {
|
|
32
|
+
try {
|
|
33
|
+
return createRequire(import.meta.url).resolve("@inspecto-dev/core");
|
|
34
|
+
} catch {
|
|
35
|
+
try {
|
|
36
|
+
return __require.resolve("@inspecto-dev/core");
|
|
37
|
+
} catch {
|
|
38
|
+
console.warn(
|
|
39
|
+
"[inspecto] Could not resolve @inspecto-dev/core \u2014 falling back to bare specifier"
|
|
40
|
+
);
|
|
41
|
+
return "@inspecto-dev/core";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/server/index.ts
|
|
47
|
+
import http from "http";
|
|
48
|
+
import fs3 from "fs";
|
|
49
|
+
import path4 from "path";
|
|
50
|
+
import os2 from "os";
|
|
51
|
+
import crypto from "crypto";
|
|
52
|
+
import { execSync, execFileSync } from "child_process";
|
|
53
|
+
import portfinder from "portfinder";
|
|
54
|
+
import { launchIDE } from "launch-ide";
|
|
55
|
+
import { INSPECTO_API_PATHS } from "@inspecto-dev/types";
|
|
56
|
+
|
|
57
|
+
// src/server/snippet.ts
|
|
58
|
+
import * as fs from "fs";
|
|
59
|
+
import * as path2 from "path";
|
|
60
|
+
import * as parser from "@babel/parser";
|
|
61
|
+
import traverse_ from "@babel/traverse";
|
|
62
|
+
var traverse = typeof traverse_ === "function" ? traverse_ : traverse_.default || traverse_;
|
|
63
|
+
var snippetCache = /* @__PURE__ */ new Map();
|
|
64
|
+
var DEFAULT_MAX_LINES = 100;
|
|
65
|
+
var DEFAULT_CONTEXT_LINES_BEFORE = 5;
|
|
66
|
+
async function extractSnippet(req) {
|
|
67
|
+
const { file, line, column, maxLines = DEFAULT_MAX_LINES } = req;
|
|
68
|
+
const absolutePath = path2.resolve(file);
|
|
69
|
+
let stat;
|
|
70
|
+
try {
|
|
71
|
+
stat = await fs.promises.stat(absolutePath);
|
|
72
|
+
} catch {
|
|
73
|
+
throw new Error(`FILE_NOT_FOUND: ${absolutePath}`);
|
|
74
|
+
}
|
|
75
|
+
const mtime = stat.mtimeMs;
|
|
76
|
+
let lines;
|
|
77
|
+
const cached = snippetCache.get(absolutePath);
|
|
78
|
+
if (cached && cached.mtime === mtime) {
|
|
79
|
+
lines = cached.lines;
|
|
80
|
+
} else {
|
|
81
|
+
const source = await fs.promises.readFile(absolutePath, "utf-8");
|
|
82
|
+
lines = source.split("\n");
|
|
83
|
+
snippetCache.set(absolutePath, { mtime, lines });
|
|
84
|
+
}
|
|
85
|
+
let snippetLines;
|
|
86
|
+
let startLine;
|
|
87
|
+
let componentName;
|
|
88
|
+
try {
|
|
89
|
+
const result = extractComponentBoundary(lines.join("\n"), line, column, maxLines);
|
|
90
|
+
snippetLines = result.lines;
|
|
91
|
+
startLine = result.startLine;
|
|
92
|
+
componentName = result.name;
|
|
93
|
+
} catch {
|
|
94
|
+
const before = Math.max(0, line - 1 - DEFAULT_CONTEXT_LINES_BEFORE);
|
|
95
|
+
const after = Math.min(lines.length, before + maxLines);
|
|
96
|
+
snippetLines = lines.slice(before, after);
|
|
97
|
+
startLine = before + 1;
|
|
98
|
+
}
|
|
99
|
+
if (snippetLines.length > maxLines) {
|
|
100
|
+
snippetLines = snippetLines.slice(0, maxLines);
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
snippet: snippetLines.join("\n"),
|
|
104
|
+
startLine,
|
|
105
|
+
file: absolutePath,
|
|
106
|
+
...componentName ? { name: componentName } : {}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
function extractComponentBoundary(source, targetLine, _targetColumn, maxLines) {
|
|
110
|
+
const ast = parser.parse(source, {
|
|
111
|
+
sourceType: "module",
|
|
112
|
+
plugins: ["jsx", "typescript", "decorators-legacy", "classProperties"],
|
|
113
|
+
errorRecovery: true
|
|
114
|
+
});
|
|
115
|
+
const allLines = source.split("\n");
|
|
116
|
+
let bestStart = 0;
|
|
117
|
+
let bestEnd = allLines.length - 1;
|
|
118
|
+
let bestName;
|
|
119
|
+
traverse(ast, {
|
|
120
|
+
"FunctionDeclaration|FunctionExpression|ArrowFunctionExpression|ClassMethod"(nodePath) {
|
|
121
|
+
const node = nodePath.node;
|
|
122
|
+
if (!node.loc) return;
|
|
123
|
+
const nodeStart = node.loc.start.line;
|
|
124
|
+
const nodeEnd = node.loc.end.line;
|
|
125
|
+
if (targetLine < nodeStart || targetLine > nodeEnd) return;
|
|
126
|
+
if (nodeEnd - nodeStart < bestEnd - bestStart) {
|
|
127
|
+
bestStart = nodeStart - 1;
|
|
128
|
+
bestEnd = nodeEnd - 1;
|
|
129
|
+
bestName = extractFunctionName(nodePath);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
let sliceStart = bestStart;
|
|
134
|
+
let sliceEnd = bestEnd + 1;
|
|
135
|
+
if (sliceEnd - sliceStart > maxLines) {
|
|
136
|
+
const targetIdx = targetLine - 1;
|
|
137
|
+
sliceStart = Math.max(bestStart, targetIdx - Math.floor(maxLines / 3));
|
|
138
|
+
sliceEnd = sliceStart + maxLines;
|
|
139
|
+
if (sliceEnd > bestEnd + 1) {
|
|
140
|
+
sliceEnd = bestEnd + 1;
|
|
141
|
+
sliceStart = Math.max(0, sliceEnd - maxLines);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
lines: allLines.slice(sliceStart, sliceEnd),
|
|
146
|
+
startLine: sliceStart + 1,
|
|
147
|
+
...bestName ? { name: bestName } : {}
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function extractFunctionName(nodePath) {
|
|
151
|
+
const node = nodePath.node;
|
|
152
|
+
if (node.type === "FunctionDeclaration" && node.id) {
|
|
153
|
+
return node.id.name;
|
|
154
|
+
}
|
|
155
|
+
const parent = nodePath.parent;
|
|
156
|
+
if ((node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && parent.type === "VariableDeclarator" && parent.id.type === "Identifier") {
|
|
157
|
+
return parent.id.name;
|
|
158
|
+
}
|
|
159
|
+
if (node.type === "ClassMethod" && node.key.type === "Identifier") {
|
|
160
|
+
return node.key.name;
|
|
161
|
+
}
|
|
162
|
+
return void 0;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// src/config.ts
|
|
166
|
+
import fs2 from "fs";
|
|
167
|
+
import path3 from "path";
|
|
168
|
+
import os from "os";
|
|
169
|
+
import { createDefu } from "defu";
|
|
170
|
+
import {
|
|
171
|
+
DEFAULT_PROVIDER_MODE,
|
|
172
|
+
VALID_MODES,
|
|
173
|
+
DEFAULT_INTENTS
|
|
174
|
+
} from "@inspecto-dev/types";
|
|
175
|
+
|
|
176
|
+
// src/utils/logger.ts
|
|
177
|
+
var LOG_LEVELS = {
|
|
178
|
+
silent: 0,
|
|
179
|
+
error: 1,
|
|
180
|
+
warn: 2,
|
|
181
|
+
info: 3
|
|
182
|
+
};
|
|
183
|
+
function isDebugEnabled(namespace) {
|
|
184
|
+
if (typeof process === "undefined" || !process.env) return false;
|
|
185
|
+
const debugEnv = process.env.DEBUG;
|
|
186
|
+
if (!debugEnv) return false;
|
|
187
|
+
const namespaces = debugEnv.split(",").map((s) => s.trim());
|
|
188
|
+
for (const ns of namespaces) {
|
|
189
|
+
if (ns === "*") return true;
|
|
190
|
+
if (ns.endsWith("*")) {
|
|
191
|
+
const prefix = ns.slice(0, -1);
|
|
192
|
+
if (namespace.startsWith(prefix)) return true;
|
|
193
|
+
} else if (ns === namespace) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
var globalLevel = "warn";
|
|
200
|
+
var registeredLoggers = /* @__PURE__ */ new Set();
|
|
201
|
+
function createLogger(namespace, options) {
|
|
202
|
+
let currentLevel = options?.logLevel ?? globalLevel;
|
|
203
|
+
let numericLevel = LOG_LEVELS[currentLevel] ?? 2;
|
|
204
|
+
const debugEnabled = isDebugEnabled(namespace);
|
|
205
|
+
const logger = {
|
|
206
|
+
setLevel(level) {
|
|
207
|
+
currentLevel = level;
|
|
208
|
+
numericLevel = LOG_LEVELS[level] ?? 2;
|
|
209
|
+
},
|
|
210
|
+
info(msg, ...args) {
|
|
211
|
+
if (numericLevel >= LOG_LEVELS.info) {
|
|
212
|
+
console.log(`\x1B[36m[inspecto]\x1B[0m ${msg}`, ...args);
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
warn(msg, ...args) {
|
|
216
|
+
if (numericLevel >= LOG_LEVELS.warn) {
|
|
217
|
+
console.warn(`\x1B[33m[inspecto] WARN:\x1B[0m ${msg}`, ...args);
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
error(msg, ...args) {
|
|
221
|
+
if (numericLevel >= LOG_LEVELS.error) {
|
|
222
|
+
console.error(`\x1B[31m[inspecto] ERROR:\x1B[0m ${msg}`, ...args);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
debug(msg, ...args) {
|
|
226
|
+
if (debugEnabled) {
|
|
227
|
+
console.log(`\x1B[90m[${namespace}]\x1B[0m ${msg}`, ...args);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
registeredLoggers.add(logger);
|
|
232
|
+
return logger;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/config.ts
|
|
236
|
+
var configLogger = createLogger("inspecto:config");
|
|
237
|
+
var loadedConfig = null;
|
|
238
|
+
var loadedPrompts = null;
|
|
239
|
+
var globalLogLevel = "warn";
|
|
240
|
+
var isWatching = false;
|
|
241
|
+
var arrayReplaceMerge = createDefu((obj, key, val) => {
|
|
242
|
+
if (Array.isArray(val)) {
|
|
243
|
+
obj[key] = val;
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
function getGlobalLogLevel() {
|
|
248
|
+
return globalLogLevel;
|
|
249
|
+
}
|
|
250
|
+
function resolveConfigRoots(cwd, gitRoot) {
|
|
251
|
+
const roots = [];
|
|
252
|
+
let current = cwd;
|
|
253
|
+
const isUnderOrEqual = current === gitRoot || current.startsWith(gitRoot + path3.sep);
|
|
254
|
+
if (!isUnderOrEqual) {
|
|
255
|
+
if (fs2.existsSync(path3.join(cwd, ".inspecto"))) roots.push(cwd);
|
|
256
|
+
return roots;
|
|
257
|
+
}
|
|
258
|
+
while (true) {
|
|
259
|
+
if (fs2.existsSync(path3.join(current, ".inspecto"))) {
|
|
260
|
+
roots.push(current);
|
|
261
|
+
}
|
|
262
|
+
if (current === gitRoot) break;
|
|
263
|
+
const parent = path3.dirname(current);
|
|
264
|
+
if (parent === current) break;
|
|
265
|
+
current = parent;
|
|
266
|
+
}
|
|
267
|
+
return roots;
|
|
268
|
+
}
|
|
269
|
+
function loadUserConfigSync(force = false, cwd = process.cwd(), gitRoot) {
|
|
270
|
+
if (loadedConfig && !force) return loadedConfig;
|
|
271
|
+
loadedConfig = null;
|
|
272
|
+
const layers = [];
|
|
273
|
+
const roots = resolveConfigRoots(cwd, gitRoot ?? cwd);
|
|
274
|
+
for (const root of roots) {
|
|
275
|
+
layers.push(readJsonSafely(path3.join(root, ".inspecto", "settings.local.json")));
|
|
276
|
+
layers.push(readJsonSafely(path3.join(root, ".inspecto", "settings.json")));
|
|
277
|
+
}
|
|
278
|
+
layers.push(readJsonSafely(path3.join(os.homedir(), ".inspecto", "settings.json")));
|
|
279
|
+
layers.push({});
|
|
280
|
+
const validLayers = layers.filter((l) => l !== null);
|
|
281
|
+
loadedConfig = arrayReplaceMerge(...validLayers);
|
|
282
|
+
return loadedConfig;
|
|
283
|
+
}
|
|
284
|
+
async function loadPromptsConfig(force = false, cwd = process.cwd(), gitRoot) {
|
|
285
|
+
if (loadedPrompts && !force) return loadedPrompts;
|
|
286
|
+
const layers = [];
|
|
287
|
+
const roots = resolveConfigRoots(cwd, gitRoot ?? cwd);
|
|
288
|
+
for (const root of roots) {
|
|
289
|
+
const localPath = path3.join(root, ".inspecto", "prompts.local.json");
|
|
290
|
+
const jsonPath = path3.join(root, ".inspecto", "prompts.json");
|
|
291
|
+
layers.push(readJsonSafely(localPath));
|
|
292
|
+
layers.push(readJsonSafely(jsonPath));
|
|
293
|
+
}
|
|
294
|
+
layers.push(readJsonSafely(path3.join(os.homedir(), ".inspecto", "prompts.json")));
|
|
295
|
+
let finalPrompts = [];
|
|
296
|
+
for (const layer of layers) {
|
|
297
|
+
if (Array.isArray(layer) && layer.length > 0) {
|
|
298
|
+
finalPrompts = layer;
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
if (layer && typeof layer === "object" && layer.$replace === true && Array.isArray(layer.items)) {
|
|
302
|
+
finalPrompts = layer;
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
loadedPrompts = finalPrompts;
|
|
307
|
+
return loadedPrompts;
|
|
308
|
+
}
|
|
309
|
+
function readJsonSafely(filePath) {
|
|
310
|
+
try {
|
|
311
|
+
if (fs2.existsSync(filePath)) {
|
|
312
|
+
const content = fs2.readFileSync(filePath, "utf-8").trim();
|
|
313
|
+
if (!content) return null;
|
|
314
|
+
const parsed = JSON.parse(content);
|
|
315
|
+
if (!Array.isArray(parsed) && parsed.prompts && Array.isArray(parsed.prompts)) {
|
|
316
|
+
return parsed.prompts;
|
|
317
|
+
}
|
|
318
|
+
return parsed;
|
|
319
|
+
}
|
|
320
|
+
} catch (e) {
|
|
321
|
+
if (e instanceof SyntaxError) {
|
|
322
|
+
configLogger.warn(`Failed to parse config at ${filePath}: Invalid JSON`);
|
|
323
|
+
} else {
|
|
324
|
+
configLogger.warn(`Failed to read config at ${filePath}:`, e);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
function resolveTargetTool(config, ide = "vscode") {
|
|
330
|
+
const defaultProvider = config["provider.default"];
|
|
331
|
+
if (defaultProvider) {
|
|
332
|
+
const tool = defaultProvider.split(".")[0];
|
|
333
|
+
return tool;
|
|
334
|
+
}
|
|
335
|
+
return "copilot";
|
|
336
|
+
}
|
|
337
|
+
function resolveProviderMode(tool, ide, config) {
|
|
338
|
+
let requestedType = void 0;
|
|
339
|
+
const defaultProvider = config["provider.default"];
|
|
340
|
+
if (defaultProvider && defaultProvider.startsWith(`${tool}.`)) {
|
|
341
|
+
const mode = defaultProvider.split(".")[1];
|
|
342
|
+
if (mode === "extension") requestedType = "extension";
|
|
343
|
+
if (mode === "cli") requestedType = "cli";
|
|
344
|
+
}
|
|
345
|
+
requestedType = requestedType ?? DEFAULT_PROVIDER_MODE[tool];
|
|
346
|
+
const valid = VALID_MODES[tool] || [DEFAULT_PROVIDER_MODE[tool]];
|
|
347
|
+
return requestedType && valid.includes(requestedType) ? requestedType : valid[0];
|
|
348
|
+
}
|
|
349
|
+
function extractToolOverrides(ide, config) {
|
|
350
|
+
const result = {};
|
|
351
|
+
if (!config) return result;
|
|
352
|
+
for (const [key, value] of Object.entries(config)) {
|
|
353
|
+
if (!key.startsWith("provider.")) continue;
|
|
354
|
+
if (key === "provider.default") continue;
|
|
355
|
+
const toolIndex = 1;
|
|
356
|
+
const modeIndex = 2;
|
|
357
|
+
const propIndex = 3;
|
|
358
|
+
const parts = key.split(".");
|
|
359
|
+
if (parts.length >= propIndex + 1) {
|
|
360
|
+
const tool = parts[toolIndex];
|
|
361
|
+
const mode = parts[modeIndex];
|
|
362
|
+
const prop = parts[propIndex];
|
|
363
|
+
if (!result[tool]) {
|
|
364
|
+
result[tool] = { type: mode };
|
|
365
|
+
}
|
|
366
|
+
const overrides = result[tool];
|
|
367
|
+
if (prop === "bin") overrides.binaryPath = value;
|
|
368
|
+
if (prop === "args") overrides.args = value;
|
|
369
|
+
if (prop === "cwd") overrides.cwd = value;
|
|
370
|
+
if (prop === "coldStartDelay") overrides.coldStartDelay = value;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return result;
|
|
374
|
+
}
|
|
375
|
+
function resolveIntents(serverPrompts) {
|
|
376
|
+
const baseMap = /* @__PURE__ */ new Map();
|
|
377
|
+
for (const intent of DEFAULT_INTENTS) {
|
|
378
|
+
if (intent.id) baseMap.set(intent.id, { ...intent });
|
|
379
|
+
}
|
|
380
|
+
const defaults = () => ensureOpenInEditorLast(Array.from(baseMap.values()));
|
|
381
|
+
if (!serverPrompts) return defaults();
|
|
382
|
+
const isReplace = !Array.isArray(serverPrompts) && typeof serverPrompts === "object" && serverPrompts.$replace === true;
|
|
383
|
+
const promptsArray = Array.isArray(serverPrompts) ? serverPrompts : isReplace ? serverPrompts.items : [];
|
|
384
|
+
if (!promptsArray || promptsArray.length === 0) return defaults();
|
|
385
|
+
if (isReplace) {
|
|
386
|
+
const result = [];
|
|
387
|
+
for (const item of promptsArray) {
|
|
388
|
+
if (typeof item === "string") {
|
|
389
|
+
if (baseMap.has(item)) {
|
|
390
|
+
result.push(baseMap.get(item));
|
|
391
|
+
} else {
|
|
392
|
+
configLogger.warn(
|
|
393
|
+
`Unknown built-in intent id: "${item}". Available: ${[...baseMap.keys()].join(", ")}`
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
} else if (typeof item === "object") {
|
|
397
|
+
if (!item.id) {
|
|
398
|
+
configLogger.warn('Intent object missing required "id" field, skipping.');
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (item.enabled === false) {
|
|
402
|
+
configLogger.warn(
|
|
403
|
+
`Intent "${item.id}" is listed in $replace but has enabled:false \u2014 it will be excluded.`
|
|
404
|
+
);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (item.isAction && item.id !== "open-in-editor") {
|
|
408
|
+
configLogger.warn(
|
|
409
|
+
`isAction is reserved for built-in actions. Ignoring intent "${item.id}".`
|
|
410
|
+
);
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
result.push(baseMap.has(item.id) ? { ...baseMap.get(item.id), ...item } : item);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return ensureOpenInEditorLast(result);
|
|
417
|
+
}
|
|
418
|
+
const merged = Array.from(baseMap.values());
|
|
419
|
+
for (const item of promptsArray) {
|
|
420
|
+
if (typeof item === "string") {
|
|
421
|
+
if (!baseMap.has(item)) {
|
|
422
|
+
configLogger.warn(
|
|
423
|
+
`Unknown built-in intent id: "${item}". In append mode, strings have no effect on ordering \u2014 use $replace to control order.`
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
if (typeof item === "object") {
|
|
429
|
+
if (!item.id) {
|
|
430
|
+
configLogger.warn('Intent object missing required "id" field, skipping.');
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (item.isAction && item.id !== "open-in-editor") {
|
|
434
|
+
configLogger.warn(
|
|
435
|
+
`isAction is reserved for built-in actions. Ignoring intent "${item.id}".`
|
|
436
|
+
);
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
const existingIdx = merged.findIndex((i) => i.id === item.id);
|
|
440
|
+
if (existingIdx !== -1) {
|
|
441
|
+
if (item.enabled === false) {
|
|
442
|
+
merged.splice(existingIdx, 1);
|
|
443
|
+
} else {
|
|
444
|
+
merged[existingIdx] = { ...merged[existingIdx], ...item };
|
|
445
|
+
}
|
|
446
|
+
} else {
|
|
447
|
+
if (item.enabled !== false) {
|
|
448
|
+
merged.push(item);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
return ensureOpenInEditorLast(merged);
|
|
454
|
+
}
|
|
455
|
+
function ensureOpenInEditorLast(intents) {
|
|
456
|
+
const idx = intents.findIndex((i) => i.id === "open-in-editor");
|
|
457
|
+
if (idx === -1 || idx === intents.length - 1) return intents;
|
|
458
|
+
const result = [...intents];
|
|
459
|
+
const item = result.splice(idx, 1)[0];
|
|
460
|
+
result.push(item);
|
|
461
|
+
return result;
|
|
462
|
+
}
|
|
463
|
+
var watchers = [];
|
|
464
|
+
function watchConfig(onReload, cwd = process.cwd(), gitRoot) {
|
|
465
|
+
if (isWatching) return;
|
|
466
|
+
isWatching = true;
|
|
467
|
+
const watchDirs = [path3.join(os.homedir(), ".inspecto")];
|
|
468
|
+
const roots = resolveConfigRoots(cwd, gitRoot ?? cwd);
|
|
469
|
+
for (const root of roots) {
|
|
470
|
+
watchDirs.push(path3.join(root, ".inspecto"));
|
|
471
|
+
}
|
|
472
|
+
const CONFIG_FILES = /* @__PURE__ */ new Set([
|
|
473
|
+
"settings.json",
|
|
474
|
+
"settings.local.json",
|
|
475
|
+
"prompts.json",
|
|
476
|
+
"prompts.local.json"
|
|
477
|
+
]);
|
|
478
|
+
for (const dir of watchDirs) {
|
|
479
|
+
if (!fs2.existsSync(dir)) continue;
|
|
480
|
+
try {
|
|
481
|
+
const watcher = fs2.watch(dir, async (eventType, filename) => {
|
|
482
|
+
if (!filename || !CONFIG_FILES.has(filename)) return;
|
|
483
|
+
loadedConfig = null;
|
|
484
|
+
loadedPrompts = null;
|
|
485
|
+
loadUserConfigSync(true, cwd, gitRoot);
|
|
486
|
+
await loadPromptsConfig(true, cwd, gitRoot);
|
|
487
|
+
onReload();
|
|
488
|
+
});
|
|
489
|
+
watcher.unref();
|
|
490
|
+
watchers.push(watcher);
|
|
491
|
+
} catch (e) {
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// src/server/index.ts
|
|
497
|
+
var serverLogger = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
498
|
+
var payloadTickets = /* @__PURE__ */ new Map();
|
|
499
|
+
function createTicket(payload) {
|
|
500
|
+
const ticketId = crypto.randomUUID();
|
|
501
|
+
payloadTickets.set(ticketId, JSON.stringify(payload));
|
|
502
|
+
setTimeout(
|
|
503
|
+
() => {
|
|
504
|
+
payloadTickets.delete(ticketId);
|
|
505
|
+
},
|
|
506
|
+
5 * 60 * 1e3
|
|
507
|
+
);
|
|
508
|
+
return ticketId;
|
|
509
|
+
}
|
|
510
|
+
var serverState = {
|
|
511
|
+
port: null,
|
|
512
|
+
running: false,
|
|
513
|
+
projectRoot: "",
|
|
514
|
+
configRoot: "",
|
|
515
|
+
cwd: process.cwd()
|
|
516
|
+
};
|
|
517
|
+
var serverInstance = null;
|
|
518
|
+
function resolveProjectRoot() {
|
|
519
|
+
let gitRoot;
|
|
520
|
+
try {
|
|
521
|
+
serverLogger.info("Resolving project root...");
|
|
522
|
+
gitRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
523
|
+
serverLogger.info("Resolved project root: " + gitRoot);
|
|
524
|
+
} catch (e) {
|
|
525
|
+
serverLogger.error("Failed to resolve project root:", e);
|
|
526
|
+
gitRoot = process.cwd();
|
|
527
|
+
}
|
|
528
|
+
let current = gitRoot;
|
|
529
|
+
while (true) {
|
|
530
|
+
if (fs3.existsSync(path4.join(current, ".inspecto"))) return current;
|
|
531
|
+
const parent = path4.dirname(current);
|
|
532
|
+
if (parent === current) break;
|
|
533
|
+
current = parent;
|
|
534
|
+
}
|
|
535
|
+
return gitRoot;
|
|
536
|
+
}
|
|
537
|
+
function launchURI(uri) {
|
|
538
|
+
try {
|
|
539
|
+
if (process.platform === "darwin") {
|
|
540
|
+
execFileSync("open", [uri]);
|
|
541
|
+
} else if (process.platform === "win32") {
|
|
542
|
+
execFileSync("cmd", ["/c", "start", '""', uri]);
|
|
543
|
+
} else {
|
|
544
|
+
execFileSync("xdg-open", [uri]);
|
|
545
|
+
}
|
|
546
|
+
} catch (e) {
|
|
547
|
+
serverLogger.error("Failed to launch URI via execFileSync, falling back to launchIDE:", e);
|
|
548
|
+
launchIDE({ file: uri });
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
async function startServer() {
|
|
552
|
+
if (serverState.running && serverState.port !== null) {
|
|
553
|
+
return serverState.port;
|
|
554
|
+
}
|
|
555
|
+
serverState.projectRoot = resolveProjectRoot();
|
|
556
|
+
serverState.configRoot = serverState.projectRoot;
|
|
557
|
+
serverState.cwd = process.cwd();
|
|
558
|
+
portfinder.basePort = 5678;
|
|
559
|
+
const port = await portfinder.getPortPromise();
|
|
560
|
+
watchConfig(
|
|
561
|
+
() => {
|
|
562
|
+
serverLogger.info("user config reloaded.");
|
|
563
|
+
},
|
|
564
|
+
serverState.cwd,
|
|
565
|
+
serverState.configRoot
|
|
566
|
+
);
|
|
567
|
+
serverInstance = http.createServer((req, res) => {
|
|
568
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
569
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
570
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
571
|
+
if (req.method === "OPTIONS") {
|
|
572
|
+
res.writeHead(204);
|
|
573
|
+
res.end();
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
577
|
+
handleRequest(url, req, res).catch((err) => {
|
|
578
|
+
serverLogger.error("server error:", err);
|
|
579
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
580
|
+
res.end(JSON.stringify({ success: false, error: String(err) }));
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
await new Promise((resolve2, reject) => {
|
|
584
|
+
serverInstance.listen(port, "127.0.0.1", () => {
|
|
585
|
+
serverInstance.unref();
|
|
586
|
+
resolve2();
|
|
587
|
+
});
|
|
588
|
+
serverInstance.once("error", reject);
|
|
589
|
+
});
|
|
590
|
+
serverInstance.on("error", (err) => {
|
|
591
|
+
serverLogger.error("persistent server error:", err);
|
|
592
|
+
});
|
|
593
|
+
serverState.port = port;
|
|
594
|
+
serverState.running = true;
|
|
595
|
+
const portFile = path4.join(os2.tmpdir(), "inspecto.port.json");
|
|
596
|
+
try {
|
|
597
|
+
let portData = {};
|
|
598
|
+
if (fs3.existsSync(portFile)) {
|
|
599
|
+
try {
|
|
600
|
+
portData = JSON.parse(fs3.readFileSync(portFile, "utf-8"));
|
|
601
|
+
} catch (e) {
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
const rootHash = crypto.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
605
|
+
portData[rootHash] = port;
|
|
606
|
+
fs3.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
607
|
+
} catch (e) {
|
|
608
|
+
serverLogger.warn("Failed to write port file:", e);
|
|
609
|
+
}
|
|
610
|
+
process.once("exit", () => {
|
|
611
|
+
try {
|
|
612
|
+
if (fs3.existsSync(portFile)) {
|
|
613
|
+
const portData = JSON.parse(fs3.readFileSync(portFile, "utf-8"));
|
|
614
|
+
const rootHash = crypto.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
615
|
+
delete portData[rootHash];
|
|
616
|
+
if (Object.keys(portData).length === 0) {
|
|
617
|
+
fs3.unlinkSync(portFile);
|
|
618
|
+
} else {
|
|
619
|
+
fs3.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
} catch {
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
serverLogger.info(`server running at http://127.0.0.1:${port}`);
|
|
626
|
+
return port;
|
|
627
|
+
}
|
|
628
|
+
async function readBody(req) {
|
|
629
|
+
return new Promise((resolve2, reject) => {
|
|
630
|
+
const chunks = [];
|
|
631
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
632
|
+
req.on("end", () => resolve2(Buffer.concat(chunks).toString("utf-8")));
|
|
633
|
+
req.on("error", reject);
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
async function handleRequest(url, req, res) {
|
|
637
|
+
const pathname = url.pathname;
|
|
638
|
+
if ((pathname === "/health" || pathname === INSPECTO_API_PATHS.HEALTH) && req.method === "GET") {
|
|
639
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
640
|
+
res.end(JSON.stringify({ ok: true, port: serverState.port }));
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
if (pathname === INSPECTO_API_PATHS.CLIENT_CONFIG && req.method === "GET") {
|
|
644
|
+
const userConfig = loadUserConfigSync(false, serverState.cwd, serverState.configRoot);
|
|
645
|
+
const promptsConfig = await loadPromptsConfig(false, serverState.cwd, serverState.configRoot);
|
|
646
|
+
const effectiveIde = userConfig.ide ?? "vscode";
|
|
647
|
+
let info;
|
|
648
|
+
if (!serverState.ideInfo) {
|
|
649
|
+
info = {
|
|
650
|
+
ide: effectiveIde
|
|
651
|
+
};
|
|
652
|
+
} else {
|
|
653
|
+
const { scheme: _scheme, ...rest } = serverState.ideInfo;
|
|
654
|
+
info = rest;
|
|
655
|
+
}
|
|
656
|
+
const config = {
|
|
657
|
+
...info,
|
|
658
|
+
prompts: resolveIntents(promptsConfig),
|
|
659
|
+
hotKeys: userConfig["inspector.hotKey"] ?? "alt",
|
|
660
|
+
theme: userConfig["inspector.theme"] ?? "auto",
|
|
661
|
+
includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
|
|
662
|
+
autoSend: userConfig["prompt.autoSend"] ?? false
|
|
663
|
+
};
|
|
664
|
+
delete config.providers;
|
|
665
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
666
|
+
res.end(JSON.stringify(config));
|
|
667
|
+
return;
|
|
668
|
+
}
|
|
669
|
+
if (pathname === INSPECTO_API_PATHS.IDE_INFO && req.method === "POST") {
|
|
670
|
+
try {
|
|
671
|
+
const body = JSON.parse(await readBody(req));
|
|
672
|
+
const ideWorkspace = body.workspaceRoot || "";
|
|
673
|
+
const serverProjectRoot = serverState.projectRoot || "";
|
|
674
|
+
const isSameProject = !ideWorkspace || !serverProjectRoot || ideWorkspace === serverProjectRoot || serverProjectRoot.startsWith(ideWorkspace);
|
|
675
|
+
if (isSameProject) {
|
|
676
|
+
serverState.ideInfo = body;
|
|
677
|
+
serverLogger.debug(
|
|
678
|
+
`Accepted IDE info from matched workspace (ide-${body.ide} / schema-${body.scheme})`
|
|
679
|
+
);
|
|
680
|
+
} else {
|
|
681
|
+
serverLogger.debug(
|
|
682
|
+
`Ignored IDE info from unrelated workspace (IDE Workspace: ${ideWorkspace}, Server: ${serverProjectRoot}, Scheme: ${body.scheme}, IDE: ${body.ide})`
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
686
|
+
res.end(JSON.stringify({ success: true }));
|
|
687
|
+
} catch (e) {
|
|
688
|
+
serverLogger.error(`Error parsing ${INSPECTO_API_PATHS.IDE_INFO} POST request:`, e);
|
|
689
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
690
|
+
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
691
|
+
}
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if (pathname === INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
|
|
695
|
+
let body;
|
|
696
|
+
try {
|
|
697
|
+
body = JSON.parse(await readBody(req));
|
|
698
|
+
} catch (e) {
|
|
699
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
700
|
+
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
const absolutePath = path4.isAbsolute(body.file) ? path4.resolve(body.file) : path4.resolve(serverState.cwd, body.file);
|
|
704
|
+
const relativeToRoot = path4.relative(serverState.projectRoot, absolutePath);
|
|
705
|
+
if (relativeToRoot.startsWith("..") || path4.isAbsolute(relativeToRoot)) {
|
|
706
|
+
serverLogger.warn(`Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}`);
|
|
707
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
708
|
+
res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
const userConfig = loadUserConfigSync(false, serverState.cwd, serverState.configRoot);
|
|
712
|
+
const configuredIde = userConfig.ide;
|
|
713
|
+
const activeIde = serverState.ideInfo?.ide;
|
|
714
|
+
const activeIdeScheme = serverState.ideInfo?.scheme;
|
|
715
|
+
const rawEditorHint = configuredIde || activeIde || activeIdeScheme || "code";
|
|
716
|
+
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
717
|
+
serverLogger.warn(
|
|
718
|
+
`Active IDE is ${activeIdeScheme}, but config forces ${configuredIde}. Using configured IDE.`
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
let editorHint = rawEditorHint;
|
|
722
|
+
if (rawEditorHint === "vscode") editorHint = "code";
|
|
723
|
+
else if (rawEditorHint === "vscode-insiders") editorHint = "code-insiders";
|
|
724
|
+
else if (rawEditorHint === "vscodium") editorHint = "codium";
|
|
725
|
+
else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
|
|
726
|
+
serverLogger.debug(
|
|
727
|
+
`IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
|
|
728
|
+
);
|
|
729
|
+
const VSCODE_FAMILY_SCHEMES = [
|
|
730
|
+
"vscode",
|
|
731
|
+
"vscode-insiders",
|
|
732
|
+
"cursor",
|
|
733
|
+
"windsurf",
|
|
734
|
+
"trae",
|
|
735
|
+
"trae-cn",
|
|
736
|
+
"vscodium",
|
|
737
|
+
"codebuddy",
|
|
738
|
+
"codebuddy-cn",
|
|
739
|
+
"antigravity"
|
|
740
|
+
];
|
|
741
|
+
if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
|
|
742
|
+
const uri = `${rawEditorHint}://file${absolutePath}:${body.line}:${body.column}`;
|
|
743
|
+
serverLogger.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
|
|
744
|
+
try {
|
|
745
|
+
if (process.platform === "darwin") {
|
|
746
|
+
execFileSync("open", [uri]);
|
|
747
|
+
} else if (process.platform === "win32") {
|
|
748
|
+
execFileSync("cmd", ["/c", "start", '""', uri]);
|
|
749
|
+
} else {
|
|
750
|
+
execFileSync("xdg-open", [uri]);
|
|
751
|
+
}
|
|
752
|
+
} catch (e) {
|
|
753
|
+
serverLogger.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
|
|
754
|
+
launchIDE({
|
|
755
|
+
file: absolutePath,
|
|
756
|
+
line: body.line,
|
|
757
|
+
column: body.column,
|
|
758
|
+
editor: editorHint,
|
|
759
|
+
type: process.platform === "darwin" ? "open" : "exec"
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
} else {
|
|
763
|
+
launchIDE({
|
|
764
|
+
file: absolutePath,
|
|
765
|
+
line: body.line,
|
|
766
|
+
column: body.column,
|
|
767
|
+
editor: editorHint,
|
|
768
|
+
type: process.platform === "darwin" ? "open" : "exec"
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
772
|
+
res.end(JSON.stringify({ success: true }));
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
if (pathname === INSPECTO_API_PATHS.PROJECT_SNIPPET && req.method === "GET") {
|
|
776
|
+
const file = url.searchParams.get("file") ?? "";
|
|
777
|
+
const line = parseInt(url.searchParams.get("line") ?? "1", 10);
|
|
778
|
+
const column = parseInt(url.searchParams.get("column") ?? "1", 10);
|
|
779
|
+
const maxLines = parseInt(url.searchParams.get("maxLines") ?? "100", 10);
|
|
780
|
+
try {
|
|
781
|
+
const absolutePath = path4.isAbsolute(file) ? path4.resolve(file) : path4.resolve(serverState.cwd, file);
|
|
782
|
+
const relativeToRoot = path4.relative(serverState.projectRoot, absolutePath);
|
|
783
|
+
if (relativeToRoot.startsWith("..") || path4.isAbsolute(relativeToRoot)) {
|
|
784
|
+
serverLogger.warn(`Security: Blocked path traversal attempt in PROJECT_SNIPPET: ${file}`);
|
|
785
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
786
|
+
res.end(
|
|
787
|
+
JSON.stringify({
|
|
788
|
+
success: false,
|
|
789
|
+
error: "Access denied: File is outside of project workspace",
|
|
790
|
+
errorCode: "FORBIDDEN"
|
|
791
|
+
})
|
|
792
|
+
);
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
const result = await extractSnippet({ file: absolutePath, line, column, maxLines });
|
|
796
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
797
|
+
res.end(JSON.stringify(result));
|
|
798
|
+
} catch (err) {
|
|
799
|
+
const message = String(err.message || err);
|
|
800
|
+
const errorCode = message.startsWith("FILE_NOT_FOUND") ? "FILE_NOT_FOUND" : "UNKNOWN";
|
|
801
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
802
|
+
res.end(JSON.stringify({ success: false, error: message, errorCode }));
|
|
803
|
+
}
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
if (pathname === INSPECTO_API_PATHS.AI_DISPATCH && req.method === "POST") {
|
|
807
|
+
try {
|
|
808
|
+
const rawBody = await readBody(req);
|
|
809
|
+
const body = JSON.parse(rawBody);
|
|
810
|
+
const result = await dispatchToAi(body);
|
|
811
|
+
res.writeHead(result.success ? 200 : 500, { "Content-Type": "application/json" });
|
|
812
|
+
res.end(JSON.stringify(result));
|
|
813
|
+
} catch (e) {
|
|
814
|
+
serverLogger.error(`Error parsing ${INSPECTO_API_PATHS.AI_DISPATCH} request:`, e);
|
|
815
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
816
|
+
res.end(JSON.stringify({ success: false, error: String(e), errorCode: "INTERNAL_ERROR" }));
|
|
817
|
+
}
|
|
818
|
+
return;
|
|
819
|
+
}
|
|
820
|
+
if (pathname.startsWith(`${INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
|
|
821
|
+
const ticketId = pathname.substring(INSPECTO_API_PATHS.AI_TICKET.length + 1);
|
|
822
|
+
const payloadStr = payloadTickets.get(ticketId);
|
|
823
|
+
if (!payloadStr) {
|
|
824
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
825
|
+
res.end(JSON.stringify({ success: false, error: "Ticket not found or expired" }));
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
829
|
+
res.end(payloadStr);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
833
|
+
res.end(JSON.stringify({ error: "not found" }));
|
|
834
|
+
}
|
|
835
|
+
async function dispatchToAi(req) {
|
|
836
|
+
const { location, snippet, prompt } = req;
|
|
837
|
+
const userConfig = loadUserConfigSync(false, serverState.cwd, serverState.configRoot);
|
|
838
|
+
const resolvedTarget = resolveTargetTool(userConfig);
|
|
839
|
+
const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
|
|
840
|
+
|
|
841
|
+
\`\`\`
|
|
842
|
+
${snippet}
|
|
843
|
+
\`\`\`
|
|
844
|
+
`;
|
|
845
|
+
const ideReportedMode = serverState.ideInfo?.providers[resolvedTarget]?.mode;
|
|
846
|
+
const configuredIde = userConfig.ide;
|
|
847
|
+
const activeIde = serverState.ideInfo?.ide;
|
|
848
|
+
const activeIdeScheme = serverState.ideInfo?.scheme;
|
|
849
|
+
const finalIde = configuredIde || activeIdeScheme || activeIde || "vscode";
|
|
850
|
+
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
851
|
+
serverLogger.warn(
|
|
852
|
+
`dispatchToAi: Active IDE is ${activeIdeScheme}, but config forces ${configuredIde}. Using configured IDE.`
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
const mode = resolveProviderMode(resolvedTarget, finalIde, userConfig);
|
|
856
|
+
const overrides = extractToolOverrides(finalIde, userConfig)[resolvedTarget] || {};
|
|
857
|
+
overrides.type = mode;
|
|
858
|
+
const fullPayload = {
|
|
859
|
+
ide: finalIde,
|
|
860
|
+
target: resolvedTarget,
|
|
861
|
+
targetType: mode,
|
|
862
|
+
prompt: formattedPrompt,
|
|
863
|
+
filePath: location.file,
|
|
864
|
+
line: location.line,
|
|
865
|
+
column: location.column,
|
|
866
|
+
snippet,
|
|
867
|
+
overrides: Object.keys(overrides).length > 0 ? overrides : void 0,
|
|
868
|
+
autoSend: userConfig["prompt.autoSend"] !== void 0 ? Boolean(userConfig["prompt.autoSend"]) : void 0
|
|
869
|
+
};
|
|
870
|
+
const ticketId = createTicket(fullPayload);
|
|
871
|
+
const params = new URLSearchParams();
|
|
872
|
+
params.set("ticket", ticketId);
|
|
873
|
+
params.set("target", resolvedTarget);
|
|
874
|
+
const uri = `${finalIde}://inspecto.inspecto/send?${params.toString()}`;
|
|
875
|
+
serverLogger.debug(`dispatchToAi: Generated URI: ${uri}`);
|
|
876
|
+
launchURI(uri);
|
|
877
|
+
return {
|
|
878
|
+
success: true,
|
|
879
|
+
fallbackPayload: {
|
|
880
|
+
prompt: formattedPrompt,
|
|
881
|
+
file: location.file
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// src/legacy/webpack4/index.ts
|
|
887
|
+
import path5 from "path";
|
|
888
|
+
var InspectoWebpack4Plugin = class {
|
|
889
|
+
constructor(options = {}) {
|
|
890
|
+
this.options = options;
|
|
891
|
+
}
|
|
892
|
+
apply(compiler) {
|
|
893
|
+
const clientPath = resolveClientModule();
|
|
894
|
+
compiler.hooks.afterEnvironment.tap("InspectoWebpack4Plugin", () => {
|
|
895
|
+
const inspectoLoader = path5.resolve(__dirname, "loader.cjs");
|
|
896
|
+
compiler.options.module.rules.push({
|
|
897
|
+
test: /\.[jt]sx?$/,
|
|
898
|
+
enforce: "pre",
|
|
899
|
+
exclude: /node_modules/,
|
|
900
|
+
use: [
|
|
901
|
+
{
|
|
902
|
+
loader: inspectoLoader,
|
|
903
|
+
options: {
|
|
904
|
+
...this.options,
|
|
905
|
+
clientPath
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
]
|
|
909
|
+
});
|
|
910
|
+
});
|
|
911
|
+
compiler.hooks.compilation.tap("InspectoWebpack4Plugin", (compilation) => {
|
|
912
|
+
const htmlPlugin = compiler.options.plugins.find(
|
|
913
|
+
(p) => p && p.constructor && p.constructor.name === "HtmlWebpackPlugin"
|
|
914
|
+
);
|
|
915
|
+
const HtmlWebpackPlugin = htmlPlugin ? htmlPlugin.constructor : null;
|
|
916
|
+
if (HtmlWebpackPlugin && HtmlWebpackPlugin.getHooks) {
|
|
917
|
+
HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync(
|
|
918
|
+
"InspectoWebpack4Plugin",
|
|
919
|
+
async (data, cb) => {
|
|
920
|
+
const port = await startServer();
|
|
921
|
+
data.headTags.unshift({
|
|
922
|
+
tagName: "script",
|
|
923
|
+
voidTag: false,
|
|
924
|
+
attributes: { "data-plugin": "inspecto-overlay" },
|
|
925
|
+
innerHTML: getWebpackHtmlScript(port)
|
|
926
|
+
});
|
|
927
|
+
cb(null, data);
|
|
928
|
+
}
|
|
929
|
+
);
|
|
930
|
+
} else if (compilation.hooks.htmlWebpackPluginAlterAssetTags) {
|
|
931
|
+
compilation.hooks.htmlWebpackPluginAlterAssetTags.tapAsync(
|
|
932
|
+
"InspectoWebpack4Plugin",
|
|
933
|
+
async (data, cb) => {
|
|
934
|
+
const port = await startServer();
|
|
935
|
+
data.head.unshift({
|
|
936
|
+
tagName: "script",
|
|
937
|
+
voidTag: false,
|
|
938
|
+
attributes: { "data-plugin": "inspecto-overlay" },
|
|
939
|
+
innerHTML: getWebpackHtmlScript(port)
|
|
940
|
+
});
|
|
941
|
+
cb(null, data);
|
|
942
|
+
}
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
var webpack4Plugin = (options) => new InspectoWebpack4Plugin(options);
|
|
949
|
+
export {
|
|
950
|
+
InspectoWebpack4Plugin,
|
|
951
|
+
webpack4Plugin
|
|
952
|
+
};
|
|
953
|
+
//# sourceMappingURL=index.js.map
|