@inspecto-dev/plugin 0.3.6 → 0.3.8
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 +2 -1
- package/dist/astro.cjs +2243 -0
- package/dist/astro.cjs.map +1 -0
- package/dist/astro.d.cts +17 -0
- package/dist/astro.d.ts +17 -0
- package/dist/astro.js +2212 -0
- package/dist/astro.js.map +1 -0
- package/dist/index.cjs +430 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -1
- package/dist/index.d.ts +21 -1
- package/dist/index.js +428 -17
- package/dist/index.js.map +1 -1
- package/dist/legacy/rspack/index.cjs +33 -21
- package/dist/legacy/rspack/index.cjs.map +1 -1
- package/dist/legacy/rspack/index.js +33 -21
- package/dist/legacy/rspack/index.js.map +1 -1
- package/dist/legacy/rspack/loader.cjs +404 -3
- package/dist/legacy/rspack/loader.cjs.map +1 -1
- package/dist/legacy/rspack/loader.js +400 -3
- package/dist/legacy/rspack/loader.js.map +1 -1
- package/dist/legacy/webpack4/index.cjs +22 -10
- package/dist/legacy/webpack4/index.cjs.map +1 -1
- package/dist/legacy/webpack4/index.js +22 -10
- package/dist/legacy/webpack4/index.js.map +1 -1
- package/dist/legacy/webpack4/loader.cjs +404 -3
- package/dist/legacy/webpack4/loader.cjs.map +1 -1
- package/dist/legacy/webpack4/loader.js +400 -3
- package/dist/legacy/webpack4/loader.js.map +1 -1
- package/dist/rollup.cjs +430 -17
- package/dist/rollup.cjs.map +1 -1
- package/dist/rollup.d.cts +1 -1
- package/dist/rollup.d.ts +1 -1
- package/dist/rollup.js +428 -17
- package/dist/rollup.js.map +1 -1
- package/dist/rspack.cjs +430 -17
- package/dist/rspack.cjs.map +1 -1
- package/dist/rspack.d.cts +1 -1
- package/dist/rspack.d.ts +1 -1
- package/dist/rspack.js +428 -17
- package/dist/rspack.js.map +1 -1
- package/dist/vite.cjs +430 -17
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.d.cts +1 -1
- package/dist/vite.d.ts +1 -1
- package/dist/vite.js +428 -17
- package/dist/vite.js.map +1 -1
- package/dist/webpack.cjs +430 -17
- package/dist/webpack.cjs.map +1 -1
- package/dist/webpack.d.cts +1 -1
- package/dist/webpack.d.ts +1 -1
- package/dist/webpack.js +428 -17
- package/dist/webpack.js.map +1 -1
- package/package.json +10 -3
package/dist/astro.js
ADDED
|
@@ -0,0 +1,2212 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b2) => (typeof require !== "undefined" ? require : a)[b2]
|
|
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
|
+
// src/server/index.ts
|
|
9
|
+
import http from "http";
|
|
10
|
+
import fs5 from "fs";
|
|
11
|
+
import path5 from "path";
|
|
12
|
+
import os2 from "os";
|
|
13
|
+
import crypto2 from "crypto";
|
|
14
|
+
import portfinder from "portfinder";
|
|
15
|
+
import { INSPECTO_API_PATHS } from "@inspecto-dev/types";
|
|
16
|
+
|
|
17
|
+
// src/server/snippet.ts
|
|
18
|
+
import * as fs from "fs";
|
|
19
|
+
import * as path from "path";
|
|
20
|
+
import * as parser from "@babel/parser";
|
|
21
|
+
import traverse_ from "@babel/traverse";
|
|
22
|
+
var traverse = typeof traverse_ === "function" ? traverse_ : traverse_.default || traverse_;
|
|
23
|
+
var snippetCache = /* @__PURE__ */ new Map();
|
|
24
|
+
var DEFAULT_MAX_LINES = 100;
|
|
25
|
+
var DEFAULT_CONTEXT_LINES_BEFORE = 5;
|
|
26
|
+
async function extractSnippet(req) {
|
|
27
|
+
const { file, line, column, maxLines = DEFAULT_MAX_LINES } = req;
|
|
28
|
+
const absolutePath = path.resolve(file);
|
|
29
|
+
let stat;
|
|
30
|
+
try {
|
|
31
|
+
stat = await fs.promises.stat(absolutePath);
|
|
32
|
+
} catch {
|
|
33
|
+
throw new Error(`FILE_NOT_FOUND: ${absolutePath}`);
|
|
34
|
+
}
|
|
35
|
+
const mtime = stat.mtimeMs;
|
|
36
|
+
let lines;
|
|
37
|
+
const cached = snippetCache.get(absolutePath);
|
|
38
|
+
if (cached && cached.mtime === mtime) {
|
|
39
|
+
lines = cached.lines;
|
|
40
|
+
} else {
|
|
41
|
+
const source = await fs.promises.readFile(absolutePath, "utf-8");
|
|
42
|
+
lines = source.split("\n");
|
|
43
|
+
snippetCache.set(absolutePath, { mtime, lines });
|
|
44
|
+
}
|
|
45
|
+
let snippetLines;
|
|
46
|
+
let startLine;
|
|
47
|
+
let componentName;
|
|
48
|
+
try {
|
|
49
|
+
const result = extractComponentBoundary(lines.join("\n"), line, column, maxLines);
|
|
50
|
+
snippetLines = result.lines;
|
|
51
|
+
startLine = result.startLine;
|
|
52
|
+
componentName = result.name;
|
|
53
|
+
} catch {
|
|
54
|
+
const before = Math.max(0, line - 1 - DEFAULT_CONTEXT_LINES_BEFORE);
|
|
55
|
+
const after = Math.min(lines.length, before + maxLines);
|
|
56
|
+
snippetLines = lines.slice(before, after);
|
|
57
|
+
startLine = before + 1;
|
|
58
|
+
}
|
|
59
|
+
if (snippetLines.length > maxLines) {
|
|
60
|
+
snippetLines = snippetLines.slice(0, maxLines);
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
snippet: snippetLines.join("\n"),
|
|
64
|
+
startLine,
|
|
65
|
+
file: absolutePath,
|
|
66
|
+
...componentName ? { name: componentName } : {}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
function extractComponentBoundary(source, targetLine, _targetColumn, maxLines) {
|
|
70
|
+
const ast = parser.parse(source, {
|
|
71
|
+
sourceType: "module",
|
|
72
|
+
plugins: ["jsx", "typescript", "decorators-legacy", "classProperties"],
|
|
73
|
+
errorRecovery: true
|
|
74
|
+
});
|
|
75
|
+
const allLines = source.split("\n");
|
|
76
|
+
let bestStart = 0;
|
|
77
|
+
let bestEnd = allLines.length - 1;
|
|
78
|
+
let bestName;
|
|
79
|
+
traverse(ast, {
|
|
80
|
+
"FunctionDeclaration|FunctionExpression|ArrowFunctionExpression|ClassMethod"(nodePath) {
|
|
81
|
+
const node = nodePath.node;
|
|
82
|
+
if (!node.loc) return;
|
|
83
|
+
const nodeStart = node.loc.start.line;
|
|
84
|
+
const nodeEnd = node.loc.end.line;
|
|
85
|
+
if (targetLine < nodeStart || targetLine > nodeEnd) return;
|
|
86
|
+
if (nodeEnd - nodeStart < bestEnd - bestStart) {
|
|
87
|
+
bestStart = nodeStart - 1;
|
|
88
|
+
bestEnd = nodeEnd - 1;
|
|
89
|
+
bestName = extractFunctionName(nodePath);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
let sliceStart = bestStart;
|
|
94
|
+
let sliceEnd = bestEnd + 1;
|
|
95
|
+
if (sliceEnd - sliceStart > maxLines) {
|
|
96
|
+
const targetIdx = targetLine - 1;
|
|
97
|
+
sliceStart = Math.max(bestStart, targetIdx - Math.floor(maxLines / 3));
|
|
98
|
+
sliceEnd = sliceStart + maxLines;
|
|
99
|
+
if (sliceEnd > bestEnd + 1) {
|
|
100
|
+
sliceEnd = bestEnd + 1;
|
|
101
|
+
sliceStart = Math.max(0, sliceEnd - maxLines);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
lines: allLines.slice(sliceStart, sliceEnd),
|
|
106
|
+
startLine: sliceStart + 1,
|
|
107
|
+
...bestName ? { name: bestName } : {}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function extractFunctionName(nodePath) {
|
|
111
|
+
const node = nodePath.node;
|
|
112
|
+
if (node.type === "FunctionDeclaration" && node.id) {
|
|
113
|
+
return node.id.name;
|
|
114
|
+
}
|
|
115
|
+
const parent = nodePath.parent;
|
|
116
|
+
if ((node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && parent.type === "VariableDeclarator" && parent.id.type === "Identifier") {
|
|
117
|
+
return parent.id.name;
|
|
118
|
+
}
|
|
119
|
+
if (node.type === "ClassMethod" && node.key.type === "Identifier") {
|
|
120
|
+
return node.key.name;
|
|
121
|
+
}
|
|
122
|
+
return void 0;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/config.ts
|
|
126
|
+
import fs2 from "fs";
|
|
127
|
+
import path2 from "path";
|
|
128
|
+
import os from "os";
|
|
129
|
+
import { createDefu } from "defu";
|
|
130
|
+
import {
|
|
131
|
+
DEFAULT_PROVIDER_MODE,
|
|
132
|
+
VALID_MODES,
|
|
133
|
+
DEFAULT_INTENTS
|
|
134
|
+
} from "@inspecto-dev/types";
|
|
135
|
+
|
|
136
|
+
// src/utils/logger.ts
|
|
137
|
+
var LOG_LEVELS = {
|
|
138
|
+
silent: 0,
|
|
139
|
+
error: 1,
|
|
140
|
+
warn: 2,
|
|
141
|
+
info: 3
|
|
142
|
+
};
|
|
143
|
+
function isDebugEnabled(namespace) {
|
|
144
|
+
if (typeof process === "undefined" || !process.env) return false;
|
|
145
|
+
const debugEnv = process.env.DEBUG;
|
|
146
|
+
if (!debugEnv) return false;
|
|
147
|
+
const namespaces = debugEnv.split(",").map((s2) => s2.trim());
|
|
148
|
+
for (const ns of namespaces) {
|
|
149
|
+
if (ns === "*") return true;
|
|
150
|
+
if (ns.endsWith("*")) {
|
|
151
|
+
const prefix = ns.slice(0, -1);
|
|
152
|
+
if (namespace.startsWith(prefix)) return true;
|
|
153
|
+
} else if (ns === namespace) {
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
var globalLevel = "warn";
|
|
160
|
+
var registeredLoggers = /* @__PURE__ */ new Set();
|
|
161
|
+
function setLoggerGlobalLevel(level) {
|
|
162
|
+
globalLevel = level;
|
|
163
|
+
for (const logger of registeredLoggers) {
|
|
164
|
+
if (logger.setLevel) {
|
|
165
|
+
logger.setLevel(level);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function createLogger(namespace, options) {
|
|
170
|
+
let currentLevel = options?.logLevel ?? globalLevel;
|
|
171
|
+
let numericLevel = LOG_LEVELS[currentLevel] ?? 2;
|
|
172
|
+
const debugEnabled = isDebugEnabled(namespace);
|
|
173
|
+
const logger = {
|
|
174
|
+
setLevel(level) {
|
|
175
|
+
currentLevel = level;
|
|
176
|
+
numericLevel = LOG_LEVELS[level] ?? 2;
|
|
177
|
+
},
|
|
178
|
+
info(msg, ...args) {
|
|
179
|
+
if (numericLevel >= LOG_LEVELS.info) {
|
|
180
|
+
console.log(`\x1B[36m[inspecto]\x1B[0m ${msg}`, ...args);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
warn(msg, ...args) {
|
|
184
|
+
if (numericLevel >= LOG_LEVELS.warn) {
|
|
185
|
+
console.warn(`\x1B[33m[inspecto] WARN:\x1B[0m ${msg}`, ...args);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
error(msg, ...args) {
|
|
189
|
+
if (numericLevel >= LOG_LEVELS.error) {
|
|
190
|
+
console.error(`\x1B[31m[inspecto] ERROR:\x1B[0m ${msg}`, ...args);
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
debug(msg, ...args) {
|
|
194
|
+
if (debugEnabled) {
|
|
195
|
+
console.log(`\x1B[90m[${namespace}]\x1B[0m ${msg}`, ...args);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
registeredLoggers.add(logger);
|
|
200
|
+
return logger;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// src/config.ts
|
|
204
|
+
var configLogger = createLogger("inspecto:config");
|
|
205
|
+
var loadedConfig = null;
|
|
206
|
+
var loadedPrompts = null;
|
|
207
|
+
var globalLogLevel = "warn";
|
|
208
|
+
var isWatching = false;
|
|
209
|
+
var arrayReplaceMerge = createDefu((obj, key, val) => {
|
|
210
|
+
if (Array.isArray(val)) {
|
|
211
|
+
obj[key] = val;
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
function setGlobalLogLevel(level) {
|
|
216
|
+
if (level) {
|
|
217
|
+
globalLogLevel = level;
|
|
218
|
+
setLoggerGlobalLevel(level);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function getGlobalLogLevel() {
|
|
222
|
+
return globalLogLevel;
|
|
223
|
+
}
|
|
224
|
+
function resolveConfigRoots(cwd, gitRoot) {
|
|
225
|
+
const roots = [];
|
|
226
|
+
let current = cwd;
|
|
227
|
+
const isUnderOrEqual = current === gitRoot || current.startsWith(gitRoot + path2.sep);
|
|
228
|
+
if (!isUnderOrEqual) {
|
|
229
|
+
if (fs2.existsSync(path2.join(cwd, ".inspecto"))) roots.push(cwd);
|
|
230
|
+
return roots;
|
|
231
|
+
}
|
|
232
|
+
while (true) {
|
|
233
|
+
if (fs2.existsSync(path2.join(current, ".inspecto"))) {
|
|
234
|
+
roots.push(current);
|
|
235
|
+
}
|
|
236
|
+
if (current === gitRoot) break;
|
|
237
|
+
const parent = path2.dirname(current);
|
|
238
|
+
if (parent === current) break;
|
|
239
|
+
current = parent;
|
|
240
|
+
}
|
|
241
|
+
return roots;
|
|
242
|
+
}
|
|
243
|
+
function loadUserConfigSync(force = false, cwd = process.cwd(), gitRoot) {
|
|
244
|
+
if (loadedConfig && !force) return loadedConfig;
|
|
245
|
+
loadedConfig = null;
|
|
246
|
+
const layers = [];
|
|
247
|
+
const roots = resolveConfigRoots(cwd, gitRoot ?? cwd);
|
|
248
|
+
for (const root of roots) {
|
|
249
|
+
layers.push(readJsonSafely(path2.join(root, ".inspecto", "settings.local.json")));
|
|
250
|
+
layers.push(readJsonSafely(path2.join(root, ".inspecto", "settings.json")));
|
|
251
|
+
}
|
|
252
|
+
layers.push(readJsonSafely(path2.join(os.homedir(), ".inspecto", "settings.json")));
|
|
253
|
+
layers.push({});
|
|
254
|
+
const validLayers = layers.filter((l) => l !== null);
|
|
255
|
+
loadedConfig = arrayReplaceMerge(...validLayers);
|
|
256
|
+
return loadedConfig;
|
|
257
|
+
}
|
|
258
|
+
async function loadPromptsConfig(force = false, cwd = process.cwd(), gitRoot) {
|
|
259
|
+
if (loadedPrompts && !force) return loadedPrompts;
|
|
260
|
+
const layers = [];
|
|
261
|
+
const roots = resolveConfigRoots(cwd, gitRoot ?? cwd);
|
|
262
|
+
for (const root of roots) {
|
|
263
|
+
const localPath = path2.join(root, ".inspecto", "prompts.local.json");
|
|
264
|
+
const jsonPath = path2.join(root, ".inspecto", "prompts.json");
|
|
265
|
+
layers.push(readJsonSafely(localPath));
|
|
266
|
+
layers.push(readJsonSafely(jsonPath));
|
|
267
|
+
}
|
|
268
|
+
layers.push(readJsonSafely(path2.join(os.homedir(), ".inspecto", "prompts.json")));
|
|
269
|
+
let finalPrompts = [];
|
|
270
|
+
for (const layer of layers) {
|
|
271
|
+
if (Array.isArray(layer) && layer.length > 0) {
|
|
272
|
+
finalPrompts = layer;
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
if (layer && typeof layer === "object" && layer.$replace === true && Array.isArray(layer.items)) {
|
|
276
|
+
finalPrompts = layer;
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
loadedPrompts = finalPrompts;
|
|
281
|
+
return loadedPrompts;
|
|
282
|
+
}
|
|
283
|
+
function readJsonSafely(filePath) {
|
|
284
|
+
try {
|
|
285
|
+
if (fs2.existsSync(filePath)) {
|
|
286
|
+
const content = fs2.readFileSync(filePath, "utf-8").trim();
|
|
287
|
+
if (!content) return null;
|
|
288
|
+
const parsed = JSON.parse(content);
|
|
289
|
+
if (!Array.isArray(parsed) && parsed.prompts && Array.isArray(parsed.prompts)) {
|
|
290
|
+
return parsed.prompts;
|
|
291
|
+
}
|
|
292
|
+
return parsed;
|
|
293
|
+
}
|
|
294
|
+
} catch (e) {
|
|
295
|
+
if (e instanceof SyntaxError) {
|
|
296
|
+
configLogger.warn(`Failed to parse config at ${filePath}: Invalid JSON`);
|
|
297
|
+
} else {
|
|
298
|
+
configLogger.warn(`Failed to read config at ${filePath}:`, e);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
function resolveTargetTool(config, _ide = "vscode") {
|
|
304
|
+
const defaultProvider = config["provider.default"];
|
|
305
|
+
if (defaultProvider) {
|
|
306
|
+
const tool = defaultProvider.split(".")[0];
|
|
307
|
+
return tool;
|
|
308
|
+
}
|
|
309
|
+
return "copilot";
|
|
310
|
+
}
|
|
311
|
+
function resolveProviderMode(tool, ide, config) {
|
|
312
|
+
let requestedType = void 0;
|
|
313
|
+
const defaultProvider = config["provider.default"];
|
|
314
|
+
if (defaultProvider && defaultProvider.startsWith(`${tool}.`)) {
|
|
315
|
+
const mode = defaultProvider.split(".")[1];
|
|
316
|
+
if (mode === "extension") requestedType = "extension";
|
|
317
|
+
if (mode === "cli") requestedType = "cli";
|
|
318
|
+
}
|
|
319
|
+
requestedType = requestedType ?? DEFAULT_PROVIDER_MODE[tool];
|
|
320
|
+
const valid = VALID_MODES[tool] || [DEFAULT_PROVIDER_MODE[tool]];
|
|
321
|
+
return requestedType && valid.includes(requestedType) ? requestedType : valid[0];
|
|
322
|
+
}
|
|
323
|
+
function extractToolOverrides(ide, config) {
|
|
324
|
+
const result = {};
|
|
325
|
+
if (!config) return result;
|
|
326
|
+
for (const [key, value] of Object.entries(config)) {
|
|
327
|
+
if (!key.startsWith("provider.")) continue;
|
|
328
|
+
if (key === "provider.default") continue;
|
|
329
|
+
const toolIndex = 1;
|
|
330
|
+
const modeIndex = 2;
|
|
331
|
+
const propIndex = 3;
|
|
332
|
+
const parts = key.split(".");
|
|
333
|
+
if (parts.length >= propIndex + 1) {
|
|
334
|
+
const tool = parts[toolIndex];
|
|
335
|
+
const mode = parts[modeIndex];
|
|
336
|
+
const prop = parts[propIndex];
|
|
337
|
+
if (!result[tool]) {
|
|
338
|
+
result[tool] = { type: mode };
|
|
339
|
+
}
|
|
340
|
+
const overrides = result[tool];
|
|
341
|
+
if (prop === "bin") overrides.binaryPath = value;
|
|
342
|
+
if (prop === "args") overrides.args = value;
|
|
343
|
+
if (prop === "cwd") overrides.cwd = value;
|
|
344
|
+
if (prop === "coldStartDelay") overrides.coldStartDelay = value;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
return result;
|
|
348
|
+
}
|
|
349
|
+
function resolveIntents(serverPrompts) {
|
|
350
|
+
const baseMap = /* @__PURE__ */ new Map();
|
|
351
|
+
for (const intent of DEFAULT_INTENTS) {
|
|
352
|
+
baseMap.set(intent.id, { ...intent });
|
|
353
|
+
}
|
|
354
|
+
const defaults = () => Array.from(baseMap.values());
|
|
355
|
+
if (!serverPrompts) return defaults();
|
|
356
|
+
const isReplace = !Array.isArray(serverPrompts) && typeof serverPrompts === "object" && serverPrompts.$replace === true;
|
|
357
|
+
const promptsArray = Array.isArray(serverPrompts) ? serverPrompts : isReplace ? serverPrompts.items : [];
|
|
358
|
+
if (!promptsArray || promptsArray.length === 0) return defaults();
|
|
359
|
+
if (isReplace) {
|
|
360
|
+
const result = [];
|
|
361
|
+
for (const item of promptsArray) {
|
|
362
|
+
if (typeof item === "string") {
|
|
363
|
+
if (baseMap.has(item)) {
|
|
364
|
+
result.push(baseMap.get(item));
|
|
365
|
+
} else {
|
|
366
|
+
configLogger.warn(
|
|
367
|
+
`Unknown built-in intent id: "${item}". Available: ${[...baseMap.keys()].join(", ")}`
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
} else if (typeof item === "object") {
|
|
371
|
+
if (!item.id) {
|
|
372
|
+
configLogger.warn('Intent object missing required "id" field, skipping.');
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (item.enabled === false) {
|
|
376
|
+
configLogger.warn(
|
|
377
|
+
`Intent "${item.id}" is listed in $replace but has enabled:false \u2014 it will be excluded.`
|
|
378
|
+
);
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
if (!item.aiIntent) {
|
|
382
|
+
configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
result.push(
|
|
386
|
+
baseMap.has(item.id) ? { ...baseMap.get(item.id), ...item } : item
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return result;
|
|
391
|
+
}
|
|
392
|
+
const merged = Array.from(baseMap.values());
|
|
393
|
+
for (const item of promptsArray) {
|
|
394
|
+
if (typeof item === "string") {
|
|
395
|
+
if (!baseMap.has(item)) {
|
|
396
|
+
configLogger.warn(
|
|
397
|
+
`Unknown built-in intent id: "${item}". In append mode, strings have no effect on ordering \u2014 use $replace to control order.`
|
|
398
|
+
);
|
|
399
|
+
}
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
if (typeof item === "object") {
|
|
403
|
+
if (!item.id) {
|
|
404
|
+
configLogger.warn('Intent object missing required "id" field, skipping.');
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
if (!item.aiIntent) {
|
|
408
|
+
configLogger.warn(`Intent "${item.id}" is missing required "aiIntent".`);
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
const existingIdx = merged.findIndex((i2) => i2.id === item.id);
|
|
412
|
+
if (existingIdx !== -1) {
|
|
413
|
+
if (item.enabled === false) {
|
|
414
|
+
merged.splice(existingIdx, 1);
|
|
415
|
+
} else {
|
|
416
|
+
merged[existingIdx] = { ...merged[existingIdx], ...item };
|
|
417
|
+
}
|
|
418
|
+
} else {
|
|
419
|
+
if (item.enabled !== false) {
|
|
420
|
+
merged.push(item);
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return merged;
|
|
426
|
+
}
|
|
427
|
+
var watchers = [];
|
|
428
|
+
function watchConfig(onReload, cwd = process.cwd(), gitRoot) {
|
|
429
|
+
if (isWatching) return;
|
|
430
|
+
isWatching = true;
|
|
431
|
+
const watchDirs = [path2.join(os.homedir(), ".inspecto")];
|
|
432
|
+
const roots = resolveConfigRoots(cwd, gitRoot ?? cwd);
|
|
433
|
+
for (const root of roots) {
|
|
434
|
+
watchDirs.push(path2.join(root, ".inspecto"));
|
|
435
|
+
}
|
|
436
|
+
const CONFIG_FILES = /* @__PURE__ */ new Set([
|
|
437
|
+
"settings.json",
|
|
438
|
+
"settings.local.json",
|
|
439
|
+
"prompts.json",
|
|
440
|
+
"prompts.local.json"
|
|
441
|
+
]);
|
|
442
|
+
for (const dir of watchDirs) {
|
|
443
|
+
if (!fs2.existsSync(dir)) continue;
|
|
444
|
+
try {
|
|
445
|
+
const watcher = fs2.watch(dir, async (eventType, filename) => {
|
|
446
|
+
if (!filename || !CONFIG_FILES.has(filename)) return;
|
|
447
|
+
loadedConfig = null;
|
|
448
|
+
loadedPrompts = null;
|
|
449
|
+
loadUserConfigSync(true, cwd, gitRoot);
|
|
450
|
+
await loadPromptsConfig(true, cwd, gitRoot);
|
|
451
|
+
onReload();
|
|
452
|
+
});
|
|
453
|
+
watcher.unref();
|
|
454
|
+
watchers.push(watcher);
|
|
455
|
+
} catch (_e) {
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/server/dispatch-transport.ts
|
|
461
|
+
import crypto from "crypto";
|
|
462
|
+
import { execFileSync } from "child_process";
|
|
463
|
+
import { launchIDE } from "launch-ide";
|
|
464
|
+
var serverLogger = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
465
|
+
var payloadTickets = /* @__PURE__ */ new Map();
|
|
466
|
+
function createTicket(payload) {
|
|
467
|
+
const ticketId = crypto.randomUUID();
|
|
468
|
+
payloadTickets.set(ticketId, JSON.stringify(payload));
|
|
469
|
+
setTimeout(
|
|
470
|
+
() => {
|
|
471
|
+
payloadTickets.delete(ticketId);
|
|
472
|
+
},
|
|
473
|
+
5 * 60 * 1e3
|
|
474
|
+
);
|
|
475
|
+
return ticketId;
|
|
476
|
+
}
|
|
477
|
+
function readTicket(ticketId) {
|
|
478
|
+
return payloadTickets.get(ticketId);
|
|
479
|
+
}
|
|
480
|
+
function launchURI(uri) {
|
|
481
|
+
try {
|
|
482
|
+
if (process.platform === "darwin") {
|
|
483
|
+
execFileSync("open", [uri]);
|
|
484
|
+
} else if (process.platform === "win32") {
|
|
485
|
+
execFileSync("cmd", ["/c", "start", '""', uri]);
|
|
486
|
+
} else {
|
|
487
|
+
execFileSync("xdg-open", [uri]);
|
|
488
|
+
}
|
|
489
|
+
} catch (e) {
|
|
490
|
+
serverLogger.error("Failed to launch URI via execFileSync, falling back to launchIDE:", e);
|
|
491
|
+
launchIDE({ file: uri });
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// src/server/dispatch-runtime.ts
|
|
496
|
+
function normalizeIdeToken(value) {
|
|
497
|
+
return (value ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
498
|
+
}
|
|
499
|
+
function resolvePromptDispatchRuntime(state) {
|
|
500
|
+
const userConfig = loadUserConfigSync(false, state.cwd, state.projectRoot);
|
|
501
|
+
const resolvedTarget = resolveTargetTool(userConfig);
|
|
502
|
+
const finalIde = resolveFinalIde(userConfig.ide, state.ideInfo?.ide, state.ideInfo?.scheme);
|
|
503
|
+
const mode = resolveProviderMode(resolvedTarget, finalIde, userConfig);
|
|
504
|
+
const overrides = extractToolOverrides(finalIde, userConfig)[resolvedTarget] || void 0;
|
|
505
|
+
return {
|
|
506
|
+
resolvedTarget,
|
|
507
|
+
finalIde,
|
|
508
|
+
mode,
|
|
509
|
+
...hasOverrides(overrides) ? { overrides } : {},
|
|
510
|
+
...userConfig["prompt.autoSend"] !== void 0 ? { autoSend: Boolean(userConfig["prompt.autoSend"]) } : {}
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
function dispatchPromptThroughIde(runtime, payload) {
|
|
514
|
+
const ticketId = createTicket({
|
|
515
|
+
ide: runtime.finalIde,
|
|
516
|
+
target: runtime.resolvedTarget,
|
|
517
|
+
targetType: runtime.mode,
|
|
518
|
+
prompt: payload.prompt,
|
|
519
|
+
filePath: payload.filePath,
|
|
520
|
+
line: payload.line,
|
|
521
|
+
column: payload.column,
|
|
522
|
+
snippet: payload.snippet,
|
|
523
|
+
...payload.screenshotContext ? { screenshotContext: payload.screenshotContext } : {},
|
|
524
|
+
overrides: runtime.overrides,
|
|
525
|
+
autoSend: runtime.autoSend
|
|
526
|
+
});
|
|
527
|
+
const params = new URLSearchParams();
|
|
528
|
+
params.set("ticket", ticketId);
|
|
529
|
+
params.set("target", runtime.resolvedTarget);
|
|
530
|
+
launchURI(`${runtime.finalIde}://inspecto.inspecto/send?${params.toString()}`);
|
|
531
|
+
return {
|
|
532
|
+
success: true,
|
|
533
|
+
fallbackPayload: {
|
|
534
|
+
prompt: payload.prompt,
|
|
535
|
+
...payload.filePath ? { file: payload.filePath } : {}
|
|
536
|
+
}
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
function resolveFinalIde(configuredIde, activeIde, activeIdeScheme) {
|
|
540
|
+
const configuredIdeMatchesActiveScheme = Boolean(configuredIde) && Boolean(activeIdeScheme) && normalizeIdeToken(configuredIde) === normalizeIdeToken(activeIdeScheme);
|
|
541
|
+
if (configuredIdeMatchesActiveScheme) {
|
|
542
|
+
return activeIdeScheme;
|
|
543
|
+
}
|
|
544
|
+
if (configuredIde && activeIdeScheme && normalizeIdeToken(activeIdeScheme).includes(normalizeIdeToken(configuredIde)) === false) {
|
|
545
|
+
return configuredIde;
|
|
546
|
+
}
|
|
547
|
+
return configuredIde || activeIdeScheme || activeIde || "vscode";
|
|
548
|
+
}
|
|
549
|
+
function hasOverrides(overrides) {
|
|
550
|
+
return Boolean(overrides && Object.keys(overrides).length > 0);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// src/server/path-guards.ts
|
|
554
|
+
import path3 from "path";
|
|
555
|
+
import fs3 from "fs";
|
|
556
|
+
function isWindowsAbsolutePath(file) {
|
|
557
|
+
return /^[a-zA-Z]:[\\/]/.test(file) || /^\\\\[^\\]+\\[^\\]+/.test(file);
|
|
558
|
+
}
|
|
559
|
+
function resolveWorkspacePath(file, cwd) {
|
|
560
|
+
if (isWindowsAbsolutePath(file)) {
|
|
561
|
+
return path3.win32.normalize(file);
|
|
562
|
+
}
|
|
563
|
+
return path3.isAbsolute(file) ? path3.resolve(file) : path3.resolve(cwd, file);
|
|
564
|
+
}
|
|
565
|
+
function assertPathWithinProject(file, projectRoot) {
|
|
566
|
+
let realFile = file;
|
|
567
|
+
let realProjectRoot = projectRoot;
|
|
568
|
+
try {
|
|
569
|
+
if (fs3.existsSync(file)) {
|
|
570
|
+
realFile = fs3.realpathSync(file);
|
|
571
|
+
}
|
|
572
|
+
} catch {
|
|
573
|
+
}
|
|
574
|
+
try {
|
|
575
|
+
if (fs3.existsSync(projectRoot)) {
|
|
576
|
+
realProjectRoot = fs3.realpathSync(projectRoot);
|
|
577
|
+
}
|
|
578
|
+
} catch {
|
|
579
|
+
}
|
|
580
|
+
if (isWithinPath(file, projectRoot) || isWithinPath(realFile, realProjectRoot)) {
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
throw new Error(
|
|
584
|
+
`Access denied: File ${normalizeForComparison(realFile)} is outside of project workspace ${normalizeForComparison(realProjectRoot)}`
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
function tryReadPackageName(packageRoot) {
|
|
588
|
+
try {
|
|
589
|
+
const packageJsonPath = path3.join(packageRoot, "package.json");
|
|
590
|
+
if (!fs3.existsSync(packageJsonPath)) return void 0;
|
|
591
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf8"));
|
|
592
|
+
return typeof packageJson.name === "string" ? packageJson.name : void 0;
|
|
593
|
+
} catch {
|
|
594
|
+
return void 0;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function findNearestPackageRoot(file) {
|
|
598
|
+
let current = path3.dirname(file);
|
|
599
|
+
while (true) {
|
|
600
|
+
if (fs3.existsSync(path3.join(current, "package.json"))) {
|
|
601
|
+
return current;
|
|
602
|
+
}
|
|
603
|
+
const parent = path3.dirname(current);
|
|
604
|
+
if (parent === current) {
|
|
605
|
+
return void 0;
|
|
606
|
+
}
|
|
607
|
+
current = parent;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
function normalizeForComparison(file) {
|
|
611
|
+
return isWindowsAbsolutePath(file) ? path3.win32.normalize(file) : path3.normalize(file);
|
|
612
|
+
}
|
|
613
|
+
function pathSeparatorFor(file) {
|
|
614
|
+
return isWindowsAbsolutePath(file) ? path3.win32.sep : path3.sep;
|
|
615
|
+
}
|
|
616
|
+
function isWithinPath(file, root) {
|
|
617
|
+
const normalizedFile = normalizeForComparison(file);
|
|
618
|
+
const normalizedRoot = normalizeForComparison(root);
|
|
619
|
+
const separator = pathSeparatorFor(normalizedRoot);
|
|
620
|
+
const rootWithSep = normalizedRoot.endsWith(separator) ? normalizedRoot : normalizedRoot + separator;
|
|
621
|
+
return normalizedFile === normalizedRoot || normalizedFile.startsWith(rootWithSep);
|
|
622
|
+
}
|
|
623
|
+
function resolveLinkedDependencyEntry(projectRoot, packageName) {
|
|
624
|
+
const packageSegments = packageName.split("/");
|
|
625
|
+
const dependencyPath = path3.join(projectRoot, "node_modules", ...packageSegments);
|
|
626
|
+
if (!fs3.existsSync(dependencyPath)) return void 0;
|
|
627
|
+
try {
|
|
628
|
+
return fs3.realpathSync(dependencyPath);
|
|
629
|
+
} catch {
|
|
630
|
+
return dependencyPath;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function isLinkedDependencyPath(file, projectRoot, packageName) {
|
|
634
|
+
const linkedDependencyRoot = resolveLinkedDependencyEntry(projectRoot, packageName);
|
|
635
|
+
if (!linkedDependencyRoot) return false;
|
|
636
|
+
return isWithinPath(file, linkedDependencyRoot);
|
|
637
|
+
}
|
|
638
|
+
function isLinkedDependencySourcePath(file, projectRoot) {
|
|
639
|
+
const packageRoot = findNearestPackageRoot(file);
|
|
640
|
+
if (!packageRoot) return false;
|
|
641
|
+
const packageName = tryReadPackageName(packageRoot);
|
|
642
|
+
if (!packageName) return false;
|
|
643
|
+
return isLinkedDependencyPath(file, projectRoot, packageName);
|
|
644
|
+
}
|
|
645
|
+
function assertPathWithinIdeOpenScope(file, projectRoot) {
|
|
646
|
+
try {
|
|
647
|
+
assertPathWithinProject(file, projectRoot);
|
|
648
|
+
return;
|
|
649
|
+
} catch {
|
|
650
|
+
if (isLinkedDependencySourcePath(file, projectRoot)) {
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
throw new Error(`Access denied: File is outside of project workspace`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/server/annotation-dispatch.ts
|
|
658
|
+
var AnnotationDispatchError = class extends Error {
|
|
659
|
+
constructor(message, errorCode) {
|
|
660
|
+
super(message);
|
|
661
|
+
this.name = "AnnotationDispatchError";
|
|
662
|
+
this.errorCode = errorCode;
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
async function dispatchAnnotationsToAi(req, state) {
|
|
666
|
+
try {
|
|
667
|
+
validateAnnotationDispatchRequest(req, state);
|
|
668
|
+
const batch = normalizeAnnotationBatch(req);
|
|
669
|
+
const prompt = buildAnnotationBatchPrompt(batch);
|
|
670
|
+
const representativeTarget = batch.annotations[0]?.targets[0];
|
|
671
|
+
const runtime = resolvePromptDispatchRuntime(state);
|
|
672
|
+
return dispatchPromptThroughIde(runtime, {
|
|
673
|
+
prompt,
|
|
674
|
+
...representativeTarget?.file ? { filePath: representativeTarget.file } : {},
|
|
675
|
+
...representativeTarget?.line ? { line: representativeTarget.line } : {},
|
|
676
|
+
...representativeTarget?.column ? { column: representativeTarget.column } : {},
|
|
677
|
+
...batch.screenshotContext ? { screenshotContext: batch.screenshotContext } : {}
|
|
678
|
+
});
|
|
679
|
+
} catch (error) {
|
|
680
|
+
return {
|
|
681
|
+
success: false,
|
|
682
|
+
error: error instanceof Error ? error.message : String(error),
|
|
683
|
+
errorCode: getAnnotationDispatchErrorCode(error)
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
function validateAnnotationDispatchRequest(req, state) {
|
|
688
|
+
if (!req.annotations.length) {
|
|
689
|
+
throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
|
|
690
|
+
}
|
|
691
|
+
for (const annotation of req.annotations) {
|
|
692
|
+
if (!annotation.targets.length) {
|
|
693
|
+
throw new AnnotationDispatchError(
|
|
694
|
+
"Each annotation must include at least one target.",
|
|
695
|
+
"INVALID_REQUEST"
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
for (const target of annotation.targets) {
|
|
699
|
+
const absolutePath = resolveWorkspacePath(target.location.file, state.cwd);
|
|
700
|
+
assertPathWithinProject(absolutePath, state.projectRoot);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
function normalizeAnnotationBatch(req) {
|
|
705
|
+
return {
|
|
706
|
+
instruction: req.instruction?.trim() ?? "",
|
|
707
|
+
responseMode: req.responseMode ?? "unified",
|
|
708
|
+
...req.runtimeContext ? { runtimeContext: req.runtimeContext } : {},
|
|
709
|
+
...req.screenshotContext ? { screenshotContext: req.screenshotContext } : {},
|
|
710
|
+
...req.cssContextPrompt?.trim() ? { cssContextPrompt: req.cssContextPrompt.trim() } : {},
|
|
711
|
+
annotations: req.annotations.map((annotation, index) => ({
|
|
712
|
+
index: index + 1,
|
|
713
|
+
note: annotation.note.trim(),
|
|
714
|
+
intent: annotation.intent,
|
|
715
|
+
targets: annotation.targets.map((target) => ({
|
|
716
|
+
file: target.location.file,
|
|
717
|
+
line: target.location.line,
|
|
718
|
+
column: target.location.column,
|
|
719
|
+
...target.label ? { label: target.label } : {},
|
|
720
|
+
...target.selector ? { selector: target.selector } : {},
|
|
721
|
+
...target.snippet ? { snippet: target.snippet } : {}
|
|
722
|
+
}))
|
|
723
|
+
}))
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
function buildAnnotationBatchPrompt(batch) {
|
|
727
|
+
const body = buildSelectedElementsPrompt(batch.annotations);
|
|
728
|
+
const prompt = batch.instruction ? `${batch.instruction}
|
|
729
|
+
|
|
730
|
+
${body}` : body;
|
|
731
|
+
return appendScreenshotContextSection(
|
|
732
|
+
appendCssContextSection(
|
|
733
|
+
appendRuntimeContextSection(prompt, batch.runtimeContext),
|
|
734
|
+
batch.cssContextPrompt
|
|
735
|
+
),
|
|
736
|
+
batch.screenshotContext
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
function appendCssContextSection(prompt, cssContextPrompt) {
|
|
740
|
+
if (!cssContextPrompt) return prompt;
|
|
741
|
+
return `${prompt}
|
|
742
|
+
|
|
743
|
+
${cssContextPrompt}`;
|
|
744
|
+
}
|
|
745
|
+
function buildSelectedElementsPrompt(annotations) {
|
|
746
|
+
const lines = ["Selected elements:"];
|
|
747
|
+
for (const annotation of annotations) {
|
|
748
|
+
const trimmedNote = annotation.note.trim();
|
|
749
|
+
for (const target of annotation.targets) {
|
|
750
|
+
const targetLabel = (target.label || "Unknown target").trim() || "Unknown target";
|
|
751
|
+
lines.push(`- ${targetLabel}`);
|
|
752
|
+
lines.push(`file=${target.file}:${target.line}:${target.column}`);
|
|
753
|
+
if (trimmedNote) {
|
|
754
|
+
lines.push(`note=${trimmedNote}`);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
if (lines.length === 1) {
|
|
759
|
+
lines.push("- None");
|
|
760
|
+
}
|
|
761
|
+
return lines.join("\n");
|
|
762
|
+
}
|
|
763
|
+
function appendScreenshotContextSection(prompt, screenshotContext) {
|
|
764
|
+
if (!screenshotContext || !screenshotContext.imageDataUrl && !screenshotContext.imageAssetId) {
|
|
765
|
+
return prompt;
|
|
766
|
+
}
|
|
767
|
+
const lines = [
|
|
768
|
+
"Visual screenshot context attached:",
|
|
769
|
+
`- capturedAt=${screenshotContext.capturedAt}`,
|
|
770
|
+
`- mimeType=${screenshotContext.mimeType}`,
|
|
771
|
+
...screenshotContext.imageAssetId ? [`- imageAssetId=${screenshotContext.imageAssetId}`] : []
|
|
772
|
+
];
|
|
773
|
+
return `${prompt}
|
|
774
|
+
|
|
775
|
+
${lines.join("\n")}`;
|
|
776
|
+
}
|
|
777
|
+
function appendRuntimeContextSection(prompt, runtimeContext) {
|
|
778
|
+
if (!runtimeContext?.records.length) {
|
|
779
|
+
return prompt;
|
|
780
|
+
}
|
|
781
|
+
return `${prompt}
|
|
782
|
+
|
|
783
|
+
${buildRuntimeContextSection(runtimeContext.records)}`;
|
|
784
|
+
}
|
|
785
|
+
function buildRuntimeContextSection(records) {
|
|
786
|
+
return ["Relevant runtime context:", ...records.map(formatRuntimeRecord)].join("\n");
|
|
787
|
+
}
|
|
788
|
+
function formatRuntimeRecord(record) {
|
|
789
|
+
const requestSummary = record.kind === "failed-request" ? `request=${record.request?.method ?? "GET"} ${record.request?.pathname ?? record.request?.url ?? "unknown"} status=${record.request?.status ?? "unknown"}` : `occurrences=${record.occurrenceCount}`;
|
|
790
|
+
const reasonSummary = record.relevanceReasons.length ? record.relevanceReasons.join("; ") : "timing-based";
|
|
791
|
+
const stackSummary = record.stack ? `
|
|
792
|
+
stack=${record.stack.split("\n").slice(0, 5).join(" | ")}` : "";
|
|
793
|
+
return [
|
|
794
|
+
`- [${record.kind}] ${record.message}`,
|
|
795
|
+
` relevance=${record.relevanceLevel} (${reasonSummary})`,
|
|
796
|
+
` ${requestSummary}`,
|
|
797
|
+
stackSummary
|
|
798
|
+
].filter(Boolean).join("\n");
|
|
799
|
+
}
|
|
800
|
+
function getAnnotationDispatchErrorCode(error) {
|
|
801
|
+
if (error instanceof AnnotationDispatchError) return error.errorCode;
|
|
802
|
+
if (error instanceof Error && error.message.includes("outside of project workspace")) {
|
|
803
|
+
return "FORBIDDEN_PATH";
|
|
804
|
+
}
|
|
805
|
+
return "UNKNOWN";
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// src/server/client-config.ts
|
|
809
|
+
async function buildClientConfig(serverState2) {
|
|
810
|
+
const userConfig = loadUserConfigSync(false, serverState2.cwd, serverState2.configRoot);
|
|
811
|
+
const promptsConfig = await loadPromptsConfig(false, serverState2.cwd, serverState2.configRoot);
|
|
812
|
+
const effectiveIde = userConfig.ide ?? "vscode";
|
|
813
|
+
let info;
|
|
814
|
+
if (!serverState2.ideInfo) {
|
|
815
|
+
info = { ide: effectiveIde };
|
|
816
|
+
} else {
|
|
817
|
+
const { scheme: _scheme, ...rest } = serverState2.ideInfo;
|
|
818
|
+
info = rest;
|
|
819
|
+
}
|
|
820
|
+
return {
|
|
821
|
+
...info,
|
|
822
|
+
prompts: resolveIntents(promptsConfig),
|
|
823
|
+
hotKeys: userConfig["inspector.hotKey"] ?? "alt",
|
|
824
|
+
theme: userConfig["inspector.theme"] ?? "auto",
|
|
825
|
+
includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
|
|
826
|
+
runtimeContext: {
|
|
827
|
+
enabled: true,
|
|
828
|
+
preview: true,
|
|
829
|
+
maxRuntimeErrors: 3,
|
|
830
|
+
maxFailedRequests: 2
|
|
831
|
+
},
|
|
832
|
+
screenshotContext: {
|
|
833
|
+
enabled: false
|
|
834
|
+
},
|
|
835
|
+
annotationResponseMode: userConfig["prompt.annotationResponseMode"] ?? "unified",
|
|
836
|
+
autoSend: userConfig["prompt.autoSend"] ?? false
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// src/server/open-file.ts
|
|
841
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
842
|
+
import { launchIDE as launchIDE2 } from "launch-ide";
|
|
843
|
+
var serverLogger2 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
844
|
+
var VSCODE_FAMILY_SCHEMES = [
|
|
845
|
+
"vscode",
|
|
846
|
+
"vscode-insiders",
|
|
847
|
+
"cursor",
|
|
848
|
+
"windsurf",
|
|
849
|
+
"trae",
|
|
850
|
+
"trae-cn",
|
|
851
|
+
"vscodium",
|
|
852
|
+
"codebuddy",
|
|
853
|
+
"codebuddy-cn",
|
|
854
|
+
"codebuddycn",
|
|
855
|
+
"antigravity"
|
|
856
|
+
];
|
|
857
|
+
function normalizeIdeToken2(value) {
|
|
858
|
+
return (value ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
859
|
+
}
|
|
860
|
+
function handleOpenFileRequest(body, serverState2) {
|
|
861
|
+
const absolutePath = resolveWorkspacePath(body.file, serverState2.cwd);
|
|
862
|
+
assertPathWithinIdeOpenScope(absolutePath, serverState2.projectRoot);
|
|
863
|
+
const userConfig = loadUserConfigSync(false, serverState2.cwd, serverState2.configRoot);
|
|
864
|
+
const configuredIde = userConfig.ide;
|
|
865
|
+
const activeIde = serverState2.ideInfo?.ide;
|
|
866
|
+
const activeIdeScheme = serverState2.ideInfo?.scheme;
|
|
867
|
+
const configuredIdeMatchesActiveScheme = Boolean(configuredIde) && Boolean(activeIdeScheme) && normalizeIdeToken2(configuredIde) === normalizeIdeToken2(activeIdeScheme);
|
|
868
|
+
const rawEditorHint = configuredIdeMatchesActiveScheme ? activeIdeScheme : configuredIde || activeIde || activeIdeScheme || "code";
|
|
869
|
+
if (configuredIde && activeIdeScheme && normalizeIdeToken2(activeIdeScheme).includes(normalizeIdeToken2(configuredIde)) === false) {
|
|
870
|
+
serverLogger2.warn(
|
|
871
|
+
`Active IDE is ${activeIdeScheme}, but config forces ${configuredIde}. Using configured IDE.`
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
let editorHint = rawEditorHint;
|
|
875
|
+
if (rawEditorHint === "vscode") editorHint = "code";
|
|
876
|
+
else if (rawEditorHint === "vscode-insiders") editorHint = "code-insiders";
|
|
877
|
+
else if (rawEditorHint === "vscodium") editorHint = "codium";
|
|
878
|
+
else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
|
|
879
|
+
serverLogger2.debug(
|
|
880
|
+
`IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
|
|
881
|
+
);
|
|
882
|
+
if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
|
|
883
|
+
let normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
884
|
+
if (!normalizedPath.startsWith("/")) {
|
|
885
|
+
normalizedPath = "/" + normalizedPath;
|
|
886
|
+
}
|
|
887
|
+
const encodedPath = encodeURI(normalizedPath);
|
|
888
|
+
const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
|
|
889
|
+
serverLogger2.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
|
|
890
|
+
try {
|
|
891
|
+
if (process.platform === "darwin") {
|
|
892
|
+
execFileSync2("open", [uri]);
|
|
893
|
+
} else if (process.platform === "win32") {
|
|
894
|
+
execFileSync2("cmd", ["/c", "start", '""', uri]);
|
|
895
|
+
} else {
|
|
896
|
+
execFileSync2("xdg-open", [uri]);
|
|
897
|
+
}
|
|
898
|
+
} catch (e) {
|
|
899
|
+
serverLogger2.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
|
|
900
|
+
launchIDE2({
|
|
901
|
+
file: absolutePath,
|
|
902
|
+
line: body.line,
|
|
903
|
+
column: body.column,
|
|
904
|
+
editor: editorHint,
|
|
905
|
+
type: process.platform === "darwin" ? "open" : "exec"
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
} else {
|
|
909
|
+
launchIDE2({
|
|
910
|
+
file: absolutePath,
|
|
911
|
+
line: body.line,
|
|
912
|
+
column: body.column,
|
|
913
|
+
editor: editorHint,
|
|
914
|
+
type: process.platform === "darwin" ? "open" : "exec"
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
return { success: true };
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// src/server/project-root.ts
|
|
921
|
+
import fs4 from "fs";
|
|
922
|
+
import path4 from "path";
|
|
923
|
+
import { execSync } from "child_process";
|
|
924
|
+
var serverLogger3 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
925
|
+
function resolveProjectRoot() {
|
|
926
|
+
const cwd = process.cwd();
|
|
927
|
+
let gitRoot;
|
|
928
|
+
try {
|
|
929
|
+
gitRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
930
|
+
} catch (e) {
|
|
931
|
+
serverLogger3.warn("Failed to resolve git root via git rev-parse:", e);
|
|
932
|
+
gitRoot = cwd;
|
|
933
|
+
}
|
|
934
|
+
const visited = /* @__PURE__ */ new Set();
|
|
935
|
+
const search = (start, stop) => {
|
|
936
|
+
let current = start;
|
|
937
|
+
while (!visited.has(current)) {
|
|
938
|
+
visited.add(current);
|
|
939
|
+
if (fs4.existsSync(path4.join(current, ".inspecto"))) return current;
|
|
940
|
+
if (current === stop) break;
|
|
941
|
+
const parent = path4.dirname(current);
|
|
942
|
+
if (parent === current) break;
|
|
943
|
+
current = parent;
|
|
944
|
+
}
|
|
945
|
+
return null;
|
|
946
|
+
};
|
|
947
|
+
const cwdMatch = search(cwd, path4.parse(cwd).root);
|
|
948
|
+
if (cwdMatch) return cwdMatch;
|
|
949
|
+
const repoMatch = search(gitRoot, path4.parse(gitRoot).root);
|
|
950
|
+
if (repoMatch) return repoMatch;
|
|
951
|
+
return gitRoot;
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// src/server/index.ts
|
|
955
|
+
var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
956
|
+
var serverState = {
|
|
957
|
+
port: null,
|
|
958
|
+
running: false,
|
|
959
|
+
projectRoot: "",
|
|
960
|
+
configRoot: "",
|
|
961
|
+
cwd: process.cwd()
|
|
962
|
+
};
|
|
963
|
+
var serverInstance = null;
|
|
964
|
+
async function startServer() {
|
|
965
|
+
if (serverState.running && serverState.port !== null) {
|
|
966
|
+
return serverState.port;
|
|
967
|
+
}
|
|
968
|
+
serverState.projectRoot = resolveProjectRoot();
|
|
969
|
+
serverState.configRoot = serverState.projectRoot;
|
|
970
|
+
serverState.cwd = process.cwd();
|
|
971
|
+
portfinder.basePort = 5678;
|
|
972
|
+
const port = await portfinder.getPortPromise();
|
|
973
|
+
watchConfig(
|
|
974
|
+
() => {
|
|
975
|
+
serverLogger4.info("user config reloaded.");
|
|
976
|
+
},
|
|
977
|
+
serverState.cwd,
|
|
978
|
+
serverState.configRoot
|
|
979
|
+
);
|
|
980
|
+
serverInstance = http.createServer((req, res) => {
|
|
981
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
982
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
983
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
984
|
+
if (req.method === "OPTIONS") {
|
|
985
|
+
res.writeHead(204);
|
|
986
|
+
res.end();
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
990
|
+
handleRequest(url, req, res).catch((err) => {
|
|
991
|
+
serverLogger4.error("server error:", err);
|
|
992
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
993
|
+
res.end(JSON.stringify({ success: false, error: String(err) }));
|
|
994
|
+
});
|
|
995
|
+
});
|
|
996
|
+
await new Promise((resolve2, reject) => {
|
|
997
|
+
serverInstance.listen(port, "127.0.0.1", () => {
|
|
998
|
+
serverInstance.unref();
|
|
999
|
+
resolve2();
|
|
1000
|
+
});
|
|
1001
|
+
serverInstance.once("error", reject);
|
|
1002
|
+
});
|
|
1003
|
+
serverInstance.on("error", (err) => {
|
|
1004
|
+
serverLogger4.error("persistent server error:", err);
|
|
1005
|
+
});
|
|
1006
|
+
serverState.port = port;
|
|
1007
|
+
serverState.running = true;
|
|
1008
|
+
const portFile = path5.join(os2.tmpdir(), "inspecto.port.json");
|
|
1009
|
+
try {
|
|
1010
|
+
let portData = {};
|
|
1011
|
+
if (fs5.existsSync(portFile)) {
|
|
1012
|
+
try {
|
|
1013
|
+
portData = JSON.parse(fs5.readFileSync(portFile, "utf-8"));
|
|
1014
|
+
} catch (_e) {
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
const rootHash = crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
1018
|
+
portData[rootHash] = port;
|
|
1019
|
+
fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
1020
|
+
} catch (_e) {
|
|
1021
|
+
serverLogger4.warn("Failed to write port file:", _e);
|
|
1022
|
+
}
|
|
1023
|
+
process.once("exit", () => {
|
|
1024
|
+
try {
|
|
1025
|
+
if (fs5.existsSync(portFile)) {
|
|
1026
|
+
const portData = JSON.parse(fs5.readFileSync(portFile, "utf-8"));
|
|
1027
|
+
const rootHash = crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
1028
|
+
delete portData[rootHash];
|
|
1029
|
+
if (Object.keys(portData).length === 0) {
|
|
1030
|
+
fs5.unlinkSync(portFile);
|
|
1031
|
+
} else {
|
|
1032
|
+
fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
} catch {
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
serverLogger4.info(`server running at http://127.0.0.1:${port}`);
|
|
1039
|
+
return port;
|
|
1040
|
+
}
|
|
1041
|
+
async function readBody(req) {
|
|
1042
|
+
return new Promise((resolve2, reject) => {
|
|
1043
|
+
const chunks = [];
|
|
1044
|
+
req.on("data", (chunk) => chunks.push(chunk));
|
|
1045
|
+
req.on("end", () => resolve2(Buffer.concat(chunks).toString("utf-8")));
|
|
1046
|
+
req.on("error", reject);
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
async function handleRequest(url, req, res) {
|
|
1050
|
+
const pathname = url.pathname;
|
|
1051
|
+
if ((pathname === "/health" || pathname === INSPECTO_API_PATHS.HEALTH) && req.method === "GET") {
|
|
1052
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1053
|
+
res.end(JSON.stringify({ ok: true, port: serverState.port }));
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
if (pathname === INSPECTO_API_PATHS.CLIENT_CONFIG && req.method === "GET") {
|
|
1057
|
+
const config = await buildClientConfig(serverState);
|
|
1058
|
+
delete config.providers;
|
|
1059
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1060
|
+
res.end(JSON.stringify(config));
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
if (pathname === INSPECTO_API_PATHS.IDE_INFO && req.method === "POST") {
|
|
1064
|
+
try {
|
|
1065
|
+
const body = JSON.parse(await readBody(req));
|
|
1066
|
+
const ideWorkspace = body.workspaceRoot || "";
|
|
1067
|
+
const serverProjectRoot = serverState.projectRoot || "";
|
|
1068
|
+
const normalizedIdeRoot = ideWorkspace ? path5.resolve(ideWorkspace) : "";
|
|
1069
|
+
const normalizedServerRoot = serverProjectRoot ? path5.resolve(serverProjectRoot) : "";
|
|
1070
|
+
const isSameProject = !normalizedIdeRoot || !normalizedServerRoot || normalizedIdeRoot === normalizedServerRoot || normalizedServerRoot.startsWith(normalizedIdeRoot + path5.sep) || normalizedIdeRoot.startsWith(normalizedServerRoot + path5.sep);
|
|
1071
|
+
if (isSameProject) {
|
|
1072
|
+
serverState.ideInfo = body;
|
|
1073
|
+
serverLogger4.debug(
|
|
1074
|
+
`Accepted IDE info from matched workspace (ide-${body.ide} / schema-${body.scheme})`
|
|
1075
|
+
);
|
|
1076
|
+
} else {
|
|
1077
|
+
serverLogger4.debug(
|
|
1078
|
+
`Ignored IDE info from unrelated workspace (IDE Workspace: ${ideWorkspace}, Server: ${serverProjectRoot}, Scheme: ${body.scheme}, IDE: ${body.ide})`
|
|
1079
|
+
);
|
|
1080
|
+
}
|
|
1081
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1082
|
+
res.end(JSON.stringify({ success: true }));
|
|
1083
|
+
} catch (e) {
|
|
1084
|
+
serverLogger4.error(`Error parsing ${INSPECTO_API_PATHS.IDE_INFO} POST request:`, e);
|
|
1085
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1086
|
+
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
1087
|
+
}
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
if (pathname === INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
|
|
1091
|
+
let body;
|
|
1092
|
+
try {
|
|
1093
|
+
body = JSON.parse(await readBody(req));
|
|
1094
|
+
} catch (_e) {
|
|
1095
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1096
|
+
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
try {
|
|
1100
|
+
handleOpenFileRequest(body, serverState);
|
|
1101
|
+
} catch (err) {
|
|
1102
|
+
serverLogger4.warn(
|
|
1103
|
+
`Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}. Reason: ${err.message}`
|
|
1104
|
+
);
|
|
1105
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
1106
|
+
res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1110
|
+
res.end(JSON.stringify({ success: true }));
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
if (pathname === INSPECTO_API_PATHS.PROJECT_SNIPPET && req.method === "GET") {
|
|
1114
|
+
const file = url.searchParams.get("file") ?? "";
|
|
1115
|
+
const line = parseInt(url.searchParams.get("line") ?? "1", 10);
|
|
1116
|
+
const column = parseInt(url.searchParams.get("column") ?? "1", 10);
|
|
1117
|
+
const maxLines = parseInt(url.searchParams.get("maxLines") ?? "100", 10);
|
|
1118
|
+
try {
|
|
1119
|
+
const absolutePath = resolveWorkspacePath(file, serverState.cwd);
|
|
1120
|
+
try {
|
|
1121
|
+
assertPathWithinProject(absolutePath, serverState.projectRoot);
|
|
1122
|
+
} catch (err) {
|
|
1123
|
+
serverLogger4.warn(
|
|
1124
|
+
`Security: Blocked path traversal attempt in PROJECT_SNIPPET: ${file}. Reason: ${err.message}`
|
|
1125
|
+
);
|
|
1126
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
1127
|
+
res.end(
|
|
1128
|
+
JSON.stringify({
|
|
1129
|
+
success: false,
|
|
1130
|
+
error: "Access denied: File is outside of project workspace",
|
|
1131
|
+
errorCode: "FORBIDDEN"
|
|
1132
|
+
})
|
|
1133
|
+
);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
const result = await extractSnippet({ file: absolutePath, line, column, maxLines });
|
|
1137
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1138
|
+
res.end(JSON.stringify(result));
|
|
1139
|
+
} catch (err) {
|
|
1140
|
+
const message = String(err.message || err);
|
|
1141
|
+
const errorCode = message.startsWith("FILE_NOT_FOUND") ? "FILE_NOT_FOUND" : "UNKNOWN";
|
|
1142
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1143
|
+
res.end(JSON.stringify({ success: false, error: message, errorCode }));
|
|
1144
|
+
}
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
if (pathname === INSPECTO_API_PATHS.AI_DISPATCH && req.method === "POST") {
|
|
1148
|
+
try {
|
|
1149
|
+
const rawBody = await readBody(req);
|
|
1150
|
+
const body = JSON.parse(rawBody);
|
|
1151
|
+
const result = await dispatchToAi(body);
|
|
1152
|
+
res.writeHead(result.success ? 200 : 500, { "Content-Type": "application/json" });
|
|
1153
|
+
res.end(JSON.stringify(result));
|
|
1154
|
+
} catch (e) {
|
|
1155
|
+
serverLogger4.error(`Error parsing ${INSPECTO_API_PATHS.AI_DISPATCH} request:`, e);
|
|
1156
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1157
|
+
res.end(JSON.stringify({ success: false, error: String(e), errorCode: "INTERNAL_ERROR" }));
|
|
1158
|
+
}
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
if (pathname === INSPECTO_API_PATHS.AI_BATCH_DISPATCH && req.method === "POST") {
|
|
1162
|
+
try {
|
|
1163
|
+
const rawBody = await readBody(req);
|
|
1164
|
+
const body = JSON.parse(rawBody);
|
|
1165
|
+
const result = await dispatchAnnotationsToAi(body, serverState);
|
|
1166
|
+
res.writeHead(getBatchDispatchStatusCode(result.errorCode, result.success), {
|
|
1167
|
+
"Content-Type": "application/json"
|
|
1168
|
+
});
|
|
1169
|
+
res.end(JSON.stringify(result));
|
|
1170
|
+
} catch (e) {
|
|
1171
|
+
serverLogger4.error(`Error parsing ${INSPECTO_API_PATHS.AI_BATCH_DISPATCH} request:`, e);
|
|
1172
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1173
|
+
res.end(JSON.stringify({ success: false, error: String(e), errorCode: "INTERNAL_ERROR" }));
|
|
1174
|
+
}
|
|
1175
|
+
return;
|
|
1176
|
+
}
|
|
1177
|
+
if (pathname.startsWith(`${INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
|
|
1178
|
+
const ticketId = pathname.substring(INSPECTO_API_PATHS.AI_TICKET.length + 1);
|
|
1179
|
+
const payloadStr = readTicket(ticketId);
|
|
1180
|
+
if (!payloadStr) {
|
|
1181
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1182
|
+
res.end(JSON.stringify({ success: false, error: "Ticket not found or expired" }));
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1186
|
+
res.end(payloadStr);
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1190
|
+
res.end(JSON.stringify({ error: "not found" }));
|
|
1191
|
+
}
|
|
1192
|
+
async function dispatchToAi(req) {
|
|
1193
|
+
const { location, snippet, prompt, screenshotContext } = req;
|
|
1194
|
+
const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
|
|
1195
|
+
|
|
1196
|
+
\`\`\`
|
|
1197
|
+
${snippet}
|
|
1198
|
+
\`\`\`
|
|
1199
|
+
`;
|
|
1200
|
+
const runtime = resolvePromptDispatchRuntime(serverState);
|
|
1201
|
+
return dispatchPromptThroughIde(runtime, {
|
|
1202
|
+
prompt: formattedPrompt,
|
|
1203
|
+
filePath: location.file,
|
|
1204
|
+
line: location.line,
|
|
1205
|
+
column: location.column,
|
|
1206
|
+
snippet,
|
|
1207
|
+
...screenshotContext ? { screenshotContext } : {}
|
|
1208
|
+
});
|
|
1209
|
+
}
|
|
1210
|
+
function getBatchDispatchStatusCode(errorCode, success) {
|
|
1211
|
+
if (success) return 200;
|
|
1212
|
+
if (errorCode === "INVALID_REQUEST") return 400;
|
|
1213
|
+
if (errorCode === "FORBIDDEN_PATH") return 403;
|
|
1214
|
+
return 500;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// src/index.ts
|
|
1218
|
+
import { createUnplugin } from "unplugin";
|
|
1219
|
+
|
|
1220
|
+
// src/transform/utils.ts
|
|
1221
|
+
var DEFAULT_ESCAPE_TAGS = /* @__PURE__ */ new Set([
|
|
1222
|
+
"template",
|
|
1223
|
+
"script",
|
|
1224
|
+
"style",
|
|
1225
|
+
// React special elements
|
|
1226
|
+
"Fragment",
|
|
1227
|
+
"React.Fragment",
|
|
1228
|
+
"StrictMode",
|
|
1229
|
+
"React.StrictMode",
|
|
1230
|
+
"Suspense",
|
|
1231
|
+
"React.Suspense",
|
|
1232
|
+
"Profiler",
|
|
1233
|
+
"React.Profiler",
|
|
1234
|
+
// React transitions
|
|
1235
|
+
"Transition",
|
|
1236
|
+
"TransitionGroup",
|
|
1237
|
+
// Vue built-in components
|
|
1238
|
+
"KeepAlive",
|
|
1239
|
+
"Teleport",
|
|
1240
|
+
"Suspense",
|
|
1241
|
+
// Vue router built-ins
|
|
1242
|
+
"RouterView",
|
|
1243
|
+
"RouterLink",
|
|
1244
|
+
"NuxtPage",
|
|
1245
|
+
"NuxtLink"
|
|
1246
|
+
]);
|
|
1247
|
+
var JSX_EXTENSIONS = /* @__PURE__ */ new Set([".jsx", ".tsx", ".js", ".ts", ".mjs", ".mts"]);
|
|
1248
|
+
function normalizeWebpackModuleRequest(id) {
|
|
1249
|
+
return id.replace(/!+$/, "").replace(/^\((?:app-pages-browser|rsc|ssr)\)\/\.\//, "");
|
|
1250
|
+
}
|
|
1251
|
+
function extractNextModuleRequest(id) {
|
|
1252
|
+
if (!id.includes("next-flight-client-entry-loader.js?")) {
|
|
1253
|
+
return void 0;
|
|
1254
|
+
}
|
|
1255
|
+
const queryIndex = id.indexOf("?");
|
|
1256
|
+
if (queryIndex === -1) {
|
|
1257
|
+
return void 0;
|
|
1258
|
+
}
|
|
1259
|
+
const params = new URLSearchParams(id.slice(queryIndex + 1).replace(/!+$/, ""));
|
|
1260
|
+
for (const entry of params.getAll("modules")) {
|
|
1261
|
+
try {
|
|
1262
|
+
const parsed = JSON.parse(entry);
|
|
1263
|
+
if (typeof parsed.request === "string" && parsed.request.length > 0) {
|
|
1264
|
+
return parsed.request;
|
|
1265
|
+
}
|
|
1266
|
+
} catch {
|
|
1267
|
+
continue;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
return void 0;
|
|
1271
|
+
}
|
|
1272
|
+
function extractTransformFilePath(requestId) {
|
|
1273
|
+
const normalizedRequestId = normalizeWebpackModuleRequest(requestId);
|
|
1274
|
+
const nextModuleRequest = extractNextModuleRequest(normalizedRequestId);
|
|
1275
|
+
if (nextModuleRequest) {
|
|
1276
|
+
return {
|
|
1277
|
+
requestId,
|
|
1278
|
+
filePath: nextModuleRequest,
|
|
1279
|
+
wrapped: true
|
|
1280
|
+
};
|
|
1281
|
+
}
|
|
1282
|
+
const lastLoaderSeparator = normalizedRequestId.lastIndexOf("!");
|
|
1283
|
+
const resourceRequest = lastLoaderSeparator >= 0 ? normalizedRequestId.slice(lastLoaderSeparator + 1) : normalizedRequestId;
|
|
1284
|
+
const queryIndex = resourceRequest.indexOf("?");
|
|
1285
|
+
const filePath = queryIndex >= 0 ? resourceRequest.slice(0, queryIndex) : resourceRequest;
|
|
1286
|
+
return {
|
|
1287
|
+
requestId,
|
|
1288
|
+
filePath,
|
|
1289
|
+
wrapped: filePath !== requestId
|
|
1290
|
+
};
|
|
1291
|
+
}
|
|
1292
|
+
function shouldTransform(filePath, _options) {
|
|
1293
|
+
const resolvedFilePath = extractTransformFilePath(filePath).filePath;
|
|
1294
|
+
if (process.env["NODE_ENV"] === "production") return false;
|
|
1295
|
+
if (resolvedFilePath.includes("node_modules")) return false;
|
|
1296
|
+
if (resolvedFilePath.startsWith("\0")) return false;
|
|
1297
|
+
if (/[/\\](dist|build|\.next|\.nuxt)[/\\]/.test(resolvedFilePath)) return false;
|
|
1298
|
+
const ext = resolvedFilePath.split(".").pop()?.toLowerCase();
|
|
1299
|
+
if (ext && !["js", "jsx", "ts", "tsx", "mjs", "mts", "vue", "svelte", "astro"].includes(ext)) {
|
|
1300
|
+
return false;
|
|
1301
|
+
}
|
|
1302
|
+
return true;
|
|
1303
|
+
}
|
|
1304
|
+
function buildEscapeTagsSet(escapeTags) {
|
|
1305
|
+
const merged = new Set(DEFAULT_ESCAPE_TAGS);
|
|
1306
|
+
if (escapeTags) {
|
|
1307
|
+
for (const tag of escapeTags) {
|
|
1308
|
+
merged.add(tag);
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return merged;
|
|
1312
|
+
}
|
|
1313
|
+
function formatAttrValue(file, line, column) {
|
|
1314
|
+
return `${file}:${line}:${column}`;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// src/transform/index.ts
|
|
1318
|
+
import path8 from "path";
|
|
1319
|
+
|
|
1320
|
+
// src/transform/transform-jsx.ts
|
|
1321
|
+
import * as parser2 from "@babel/parser";
|
|
1322
|
+
import traverse_2 from "@babel/traverse";
|
|
1323
|
+
import MagicString from "magic-string";
|
|
1324
|
+
import path6 from "path";
|
|
1325
|
+
var traverse2 = typeof traverse_2 === "function" ? traverse_2 : traverse_2.default || traverse_2;
|
|
1326
|
+
function transformJsx(options) {
|
|
1327
|
+
const {
|
|
1328
|
+
filePath,
|
|
1329
|
+
source,
|
|
1330
|
+
projectRoot,
|
|
1331
|
+
escapeTags,
|
|
1332
|
+
pathType = "absolute",
|
|
1333
|
+
attributeName = "data-inspecto"
|
|
1334
|
+
} = options;
|
|
1335
|
+
const escapeTagsSet = buildEscapeTagsSet(escapeTags);
|
|
1336
|
+
const resolvedPath = pathType === "absolute" ? path6.resolve(filePath) : path6.relative(projectRoot, path6.resolve(filePath));
|
|
1337
|
+
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
1338
|
+
let ast;
|
|
1339
|
+
try {
|
|
1340
|
+
ast = parser2.parse(source, {
|
|
1341
|
+
sourceType: "module",
|
|
1342
|
+
plugins: [
|
|
1343
|
+
"jsx",
|
|
1344
|
+
"typescript",
|
|
1345
|
+
"decorators-legacy",
|
|
1346
|
+
"classProperties",
|
|
1347
|
+
"optionalChaining",
|
|
1348
|
+
"nullishCoalescingOperator",
|
|
1349
|
+
"importMeta"
|
|
1350
|
+
],
|
|
1351
|
+
errorRecovery: true
|
|
1352
|
+
});
|
|
1353
|
+
} catch {
|
|
1354
|
+
return { code: source, map: null, changed: false };
|
|
1355
|
+
}
|
|
1356
|
+
const ms = new MagicString(source);
|
|
1357
|
+
let changed = false;
|
|
1358
|
+
traverse2(ast, {
|
|
1359
|
+
JSXOpeningElement(nodePath) {
|
|
1360
|
+
const node = nodePath.node;
|
|
1361
|
+
const alreadyHasAttr = node.attributes.some(
|
|
1362
|
+
(attr) => attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === attributeName
|
|
1363
|
+
);
|
|
1364
|
+
if (alreadyHasAttr) return;
|
|
1365
|
+
const nameNode = node.name;
|
|
1366
|
+
let tagName;
|
|
1367
|
+
if (nameNode.type === "JSXIdentifier") {
|
|
1368
|
+
tagName = nameNode.name;
|
|
1369
|
+
} else if (nameNode.type === "JSXMemberExpression") {
|
|
1370
|
+
const objName = nameNode.object.type === "JSXIdentifier" ? nameNode.object.name : "";
|
|
1371
|
+
const propName = nameNode.property.type === "JSXIdentifier" ? nameNode.property.name : "";
|
|
1372
|
+
tagName = objName && propName ? `${objName}.${propName}` : objName;
|
|
1373
|
+
} else {
|
|
1374
|
+
tagName = "";
|
|
1375
|
+
}
|
|
1376
|
+
if (escapeTagsSet.has(tagName)) return;
|
|
1377
|
+
const loc = node.loc;
|
|
1378
|
+
if (!loc) return;
|
|
1379
|
+
const { line, column } = loc.start;
|
|
1380
|
+
const attrValue = formatAttrValue(normalizedPath, line, column + 1);
|
|
1381
|
+
let insertPos = null;
|
|
1382
|
+
if (node.attributes && node.attributes.length > 0) {
|
|
1383
|
+
const firstAttr = node.attributes[0];
|
|
1384
|
+
if (firstAttr && firstAttr.start != null) {
|
|
1385
|
+
insertPos = firstAttr.start;
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
if (insertPos == null) {
|
|
1389
|
+
if (node.typeParameters && node.typeParameters.end != null) {
|
|
1390
|
+
insertPos = node.typeParameters.end;
|
|
1391
|
+
} else if (node.name.end != null) {
|
|
1392
|
+
insertPos = node.name.end;
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
if (insertPos == null) return;
|
|
1396
|
+
ms.appendLeft(
|
|
1397
|
+
insertPos,
|
|
1398
|
+
` ${attributeName}="${attrValue}"${node.attributes && node.attributes.length > 0 ? "" : " "}`
|
|
1399
|
+
);
|
|
1400
|
+
changed = true;
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
if (!changed) {
|
|
1404
|
+
return { code: source, map: null, changed: false };
|
|
1405
|
+
}
|
|
1406
|
+
return {
|
|
1407
|
+
code: ms.toString(),
|
|
1408
|
+
map: ms.generateMap({ hires: true, source: filePath }),
|
|
1409
|
+
changed: true
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
// src/transform/transform-vue.ts
|
|
1414
|
+
import * as vueCompiler from "@vue/compiler-dom";
|
|
1415
|
+
import { parse as parseSFC } from "@vue/compiler-sfc";
|
|
1416
|
+
import { NodeTypes } from "@vue/compiler-core";
|
|
1417
|
+
import MagicString2 from "magic-string";
|
|
1418
|
+
import path7 from "path";
|
|
1419
|
+
function transformVue(options) {
|
|
1420
|
+
const {
|
|
1421
|
+
filePath,
|
|
1422
|
+
source,
|
|
1423
|
+
projectRoot,
|
|
1424
|
+
escapeTags,
|
|
1425
|
+
pathType = "absolute",
|
|
1426
|
+
attributeName = "data-inspecto"
|
|
1427
|
+
} = options;
|
|
1428
|
+
const escapeTagsSet = buildEscapeTagsSet(escapeTags);
|
|
1429
|
+
const resolvedPath = pathType === "absolute" ? path7.resolve(filePath) : path7.relative(projectRoot, path7.resolve(filePath));
|
|
1430
|
+
const normalizedPath = resolvedPath.replace(/\\/g, "/");
|
|
1431
|
+
const { descriptor, errors } = parseSFC(source, {
|
|
1432
|
+
filename: filePath,
|
|
1433
|
+
sourceMap: false,
|
|
1434
|
+
ignoreEmpty: true
|
|
1435
|
+
});
|
|
1436
|
+
if (errors.length > 0 || !descriptor.template) {
|
|
1437
|
+
return { code: source, map: null, changed: false };
|
|
1438
|
+
}
|
|
1439
|
+
const templateContent = descriptor.template.content;
|
|
1440
|
+
const templateBlockStart = descriptor.template.loc.start.offset;
|
|
1441
|
+
let ast;
|
|
1442
|
+
try {
|
|
1443
|
+
ast = vueCompiler.parse(templateContent, {
|
|
1444
|
+
parseMode: "html",
|
|
1445
|
+
// Preserve source locations relative to templateContent
|
|
1446
|
+
onError: () => {
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
} catch {
|
|
1450
|
+
return { code: source, map: null, changed: false };
|
|
1451
|
+
}
|
|
1452
|
+
const ms = new MagicString2(source);
|
|
1453
|
+
let changed = false;
|
|
1454
|
+
walkElement(ast, (node) => {
|
|
1455
|
+
if (node.type !== NodeTypes.ELEMENT) return;
|
|
1456
|
+
const tagName = node.tag;
|
|
1457
|
+
if (escapeTagsSet.has(tagName)) return;
|
|
1458
|
+
if (tagName === "template" && node === ast.children[0]) return;
|
|
1459
|
+
const alreadyHasAttr = node.props.some(
|
|
1460
|
+
(p2) => p2.type === NodeTypes.ATTRIBUTE && p2.name === attributeName
|
|
1461
|
+
);
|
|
1462
|
+
if (alreadyHasAttr) return;
|
|
1463
|
+
const loc = node.loc;
|
|
1464
|
+
if (!loc) return;
|
|
1465
|
+
const { line, column } = loc.start;
|
|
1466
|
+
const templateStartLoc = descriptor.template.loc.start;
|
|
1467
|
+
const absoluteLine = templateStartLoc.line + line - 1;
|
|
1468
|
+
const absoluteColumn = line === 1 ? templateStartLoc.column + column - 1 : column;
|
|
1469
|
+
const attrValue = formatAttrValue(normalizedPath, absoluteLine, absoluteColumn);
|
|
1470
|
+
const tagNameEnd = loc.start.offset + tagName.length + 1;
|
|
1471
|
+
const absoluteOffset = templateBlockStart + tagNameEnd;
|
|
1472
|
+
ms.appendLeft(absoluteOffset, ` ${attributeName}="${attrValue}"`);
|
|
1473
|
+
changed = true;
|
|
1474
|
+
});
|
|
1475
|
+
if (!changed) {
|
|
1476
|
+
return { code: source, map: null, changed: false };
|
|
1477
|
+
}
|
|
1478
|
+
return {
|
|
1479
|
+
code: ms.toString(),
|
|
1480
|
+
map: ms.generateMap({ hires: true, source: filePath }),
|
|
1481
|
+
changed: true
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
function walkElement(node, visitor) {
|
|
1485
|
+
if (node.type === NodeTypes.ELEMENT) {
|
|
1486
|
+
visitor(node);
|
|
1487
|
+
for (const child of node.children) {
|
|
1488
|
+
walkElement(child, visitor);
|
|
1489
|
+
}
|
|
1490
|
+
} else if ("children" in node && Array.isArray(node.children)) {
|
|
1491
|
+
for (const child of node.children) {
|
|
1492
|
+
walkElement(child, visitor);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
|
|
1497
|
+
// src/transform/transform-svelte.ts
|
|
1498
|
+
import MagicString3 from "magic-string";
|
|
1499
|
+
import { parse as parseSvelte } from "svelte/compiler";
|
|
1500
|
+
function walk(node, visitor) {
|
|
1501
|
+
if (!node || typeof node !== "object") return;
|
|
1502
|
+
visitor.enter(node);
|
|
1503
|
+
for (const key in node) {
|
|
1504
|
+
if (key === "parent" || key === "prev" || key === "next") continue;
|
|
1505
|
+
const value = node[key];
|
|
1506
|
+
if (Array.isArray(value)) {
|
|
1507
|
+
value.forEach((child) => {
|
|
1508
|
+
if (child && typeof child === "object") walk(child, visitor);
|
|
1509
|
+
});
|
|
1510
|
+
} else if (value && typeof value === "object") {
|
|
1511
|
+
walk(value, visitor);
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
function transformSvelte(options) {
|
|
1516
|
+
const { filePath, source, escapeTags, attributeName = "data-inspecto" } = options;
|
|
1517
|
+
const escapeTagsSet = buildEscapeTagsSet(escapeTags);
|
|
1518
|
+
let replacedContent = source;
|
|
1519
|
+
const scriptRegex = /<script(?:\s+[a-zA-Z-]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]*))?)?>[\s\S]*?<\/script>/gi;
|
|
1520
|
+
const styleRegex = /<style(?:\s+[a-zA-Z-]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]*))?)?>[\s\S]*?<\/style>/gi;
|
|
1521
|
+
const scriptMatches = source.match(scriptRegex) || [];
|
|
1522
|
+
const styleMatches = source.match(styleRegex) || [];
|
|
1523
|
+
[...scriptMatches, ...styleMatches].forEach((match) => {
|
|
1524
|
+
replacedContent = replacedContent.replace(match, " ".repeat(match.length));
|
|
1525
|
+
});
|
|
1526
|
+
let ast;
|
|
1527
|
+
try {
|
|
1528
|
+
ast = parseSvelte(replacedContent);
|
|
1529
|
+
} catch {
|
|
1530
|
+
return { code: source, map: null, changed: false };
|
|
1531
|
+
}
|
|
1532
|
+
const s2 = new MagicString3(source);
|
|
1533
|
+
let changed = false;
|
|
1534
|
+
function countLines(text, position) {
|
|
1535
|
+
let lines = 0;
|
|
1536
|
+
for (let i2 = 0; i2 < position; i2++) {
|
|
1537
|
+
if (text[i2] === "\n") lines++;
|
|
1538
|
+
}
|
|
1539
|
+
return lines;
|
|
1540
|
+
}
|
|
1541
|
+
const root = ast.html || ast.fragment || ast;
|
|
1542
|
+
walk(root, {
|
|
1543
|
+
enter(node) {
|
|
1544
|
+
if (node.type === "Element" || node.type === "RegularElement" || node.type === "InlineComponent" || node.type === "Component") {
|
|
1545
|
+
const tagName = node.name || "";
|
|
1546
|
+
if (tagName && !escapeTagsSet.has(tagName.toLowerCase()) && !node.attributes?.some((attr) => attr.name === attributeName)) {
|
|
1547
|
+
const insertPosition = node.start + tagName.length + 1;
|
|
1548
|
+
const line = countLines(source, node.start) + 1;
|
|
1549
|
+
const lastNewLine = source.lastIndexOf("\n", node.start - 1);
|
|
1550
|
+
const column = lastNewLine === -1 ? node.start + 1 : node.start - lastNewLine;
|
|
1551
|
+
const attrValue = formatAttrValue(filePath, line, column);
|
|
1552
|
+
const addition = ` ${attributeName}="${attrValue}"`;
|
|
1553
|
+
s2.appendLeft(insertPosition, addition);
|
|
1554
|
+
changed = true;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
});
|
|
1559
|
+
return {
|
|
1560
|
+
code: s2.toString(),
|
|
1561
|
+
map: changed ? s2.generateMap({ source: filePath, includeContent: true }) : null,
|
|
1562
|
+
changed
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
// src/transform/transform-astro.ts
|
|
1567
|
+
import MagicString4 from "magic-string";
|
|
1568
|
+
|
|
1569
|
+
// ../../node_modules/.pnpm/@astrojs+compiler@3.0.1/node_modules/@astrojs/compiler/dist/chunk-W5DTLHV4.js
|
|
1570
|
+
import g from "crypto";
|
|
1571
|
+
import _ from "fs";
|
|
1572
|
+
import { TextDecoder as b, TextEncoder as v } from "util";
|
|
1573
|
+
globalThis.fs || Object.defineProperty(globalThis, "fs", { value: _ });
|
|
1574
|
+
globalThis.process || Object.defineProperties(globalThis, "process", { value: process });
|
|
1575
|
+
globalThis.crypto || Object.defineProperty(globalThis, "crypto", { value: g.webcrypto ? g.webcrypto : { getRandomValues(m2) {
|
|
1576
|
+
return g.randomFillSync(m2);
|
|
1577
|
+
} } });
|
|
1578
|
+
globalThis.performance || Object.defineProperty(globalThis, "performance", { value: { now() {
|
|
1579
|
+
let [m2, o] = process.hrtime();
|
|
1580
|
+
return m2 * 1e3 + o / 1e6;
|
|
1581
|
+
} } });
|
|
1582
|
+
var y = new v("utf-8");
|
|
1583
|
+
var w = new b("utf-8");
|
|
1584
|
+
var d = class {
|
|
1585
|
+
constructor() {
|
|
1586
|
+
this.argv = ["js"], this.env = {}, this.exit = (t) => {
|
|
1587
|
+
t !== 0 && console.warn("exit code:", t);
|
|
1588
|
+
}, this._exitPromise = new Promise((t) => {
|
|
1589
|
+
this._resolveExitPromise = t;
|
|
1590
|
+
}), this._pendingEvent = null, this._scheduledTimeouts = /* @__PURE__ */ new Map(), this._nextCallbackTimeoutID = 1;
|
|
1591
|
+
let o = (t, e) => {
|
|
1592
|
+
this.mem.setUint32(t + 0, e, true), this.mem.setUint32(t + 4, Math.floor(e / 4294967296), true);
|
|
1593
|
+
}, n = (t) => {
|
|
1594
|
+
let e = this.mem.getUint32(t + 0, true), s2 = this.mem.getInt32(t + 4, true);
|
|
1595
|
+
return e + s2 * 4294967296;
|
|
1596
|
+
}, r = (t) => {
|
|
1597
|
+
let e = this.mem.getFloat64(t, true);
|
|
1598
|
+
if (e === 0) return;
|
|
1599
|
+
if (!isNaN(e)) return e;
|
|
1600
|
+
let s2 = this.mem.getUint32(t, true);
|
|
1601
|
+
return this._values[s2];
|
|
1602
|
+
}, l = (t, e) => {
|
|
1603
|
+
if (typeof e == "number" && e !== 0) {
|
|
1604
|
+
if (isNaN(e)) {
|
|
1605
|
+
this.mem.setUint32(t + 4, 2146959360, true), this.mem.setUint32(t, 0, true);
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
this.mem.setFloat64(t, e, true);
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
if (e === void 0) {
|
|
1612
|
+
this.mem.setFloat64(t, 0, true);
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
let i2 = this._ids.get(e);
|
|
1616
|
+
i2 === void 0 && (i2 = this._idPool.pop(), i2 === void 0 && (i2 = this._values.length), this._values[i2] = e, this._goRefCounts[i2] = 0, this._ids.set(e, i2)), this._goRefCounts[i2]++;
|
|
1617
|
+
let a = 0;
|
|
1618
|
+
switch (typeof e) {
|
|
1619
|
+
case "object":
|
|
1620
|
+
e !== null && (a = 1);
|
|
1621
|
+
break;
|
|
1622
|
+
case "string":
|
|
1623
|
+
a = 2;
|
|
1624
|
+
break;
|
|
1625
|
+
case "symbol":
|
|
1626
|
+
a = 3;
|
|
1627
|
+
break;
|
|
1628
|
+
case "function":
|
|
1629
|
+
a = 4;
|
|
1630
|
+
break;
|
|
1631
|
+
}
|
|
1632
|
+
this.mem.setUint32(t + 4, 2146959360 | a, true), this.mem.setUint32(t, i2, true);
|
|
1633
|
+
}, c = (t) => {
|
|
1634
|
+
let e = n(t + 0), s2 = n(t + 8);
|
|
1635
|
+
return new Uint8Array(this._inst.exports.mem.buffer, e, s2);
|
|
1636
|
+
}, f2 = (t) => {
|
|
1637
|
+
let e = n(t + 0), s2 = n(t + 8), i2 = new Array(s2);
|
|
1638
|
+
for (let a = 0; a < s2; a++) i2[a] = r(e + a * 8);
|
|
1639
|
+
return i2;
|
|
1640
|
+
}, u = (t) => {
|
|
1641
|
+
let e = n(t + 0), s2 = n(t + 8);
|
|
1642
|
+
return w.decode(new DataView(this._inst.exports.mem.buffer, e, s2));
|
|
1643
|
+
}, h = Date.now() - performance.now();
|
|
1644
|
+
this.importObject = { gojs: { "runtime.wasmExit": (t) => {
|
|
1645
|
+
t >>>= 0;
|
|
1646
|
+
let e = this.mem.getInt32(t + 8, true);
|
|
1647
|
+
this.exited = true, delete this._inst, delete this._values, delete this._goRefCounts, delete this._ids, delete this._idPool, this.exit(e);
|
|
1648
|
+
}, "runtime.wasmWrite": (t) => {
|
|
1649
|
+
t >>>= 0;
|
|
1650
|
+
let e = n(t + 8), s2 = n(t + 16), i2 = this.mem.getInt32(t + 24, true);
|
|
1651
|
+
_.writeSync(e, new Uint8Array(this._inst.exports.mem.buffer, s2, i2));
|
|
1652
|
+
}, "runtime.resetMemoryDataView": (t) => {
|
|
1653
|
+
t >>>= 0, this.mem = new DataView(this._inst.exports.mem.buffer);
|
|
1654
|
+
}, "runtime.nanotime1": (t) => {
|
|
1655
|
+
t >>>= 0, o(t + 8, (h + performance.now()) * 1e6);
|
|
1656
|
+
}, "runtime.walltime": (t) => {
|
|
1657
|
+
t >>>= 0;
|
|
1658
|
+
let e = (/* @__PURE__ */ new Date()).getTime();
|
|
1659
|
+
o(t + 8, e / 1e3), this.mem.setInt32(t + 16, e % 1e3 * 1e6, true);
|
|
1660
|
+
}, "runtime.scheduleTimeoutEvent": (t) => {
|
|
1661
|
+
t >>>= 0;
|
|
1662
|
+
let e = this._nextCallbackTimeoutID;
|
|
1663
|
+
this._nextCallbackTimeoutID++, this._scheduledTimeouts.set(e, setTimeout(() => {
|
|
1664
|
+
for (this._resume(); this._scheduledTimeouts.has(e); ) console.warn("scheduleTimeoutEvent: missed timeout event"), this._resume();
|
|
1665
|
+
}, n(t + 8) + 1)), this.mem.setInt32(t + 16, e, true);
|
|
1666
|
+
}, "runtime.clearTimeoutEvent": (t) => {
|
|
1667
|
+
t >>>= 0;
|
|
1668
|
+
let e = this.mem.getInt32(t + 8, true);
|
|
1669
|
+
clearTimeout(this._scheduledTimeouts.get(e)), this._scheduledTimeouts.delete(e);
|
|
1670
|
+
}, "runtime.getRandomData": (t) => {
|
|
1671
|
+
t >>>= 0, globalThis.crypto.getRandomValues(c(t + 8));
|
|
1672
|
+
}, "syscall/js.finalizeRef": (t) => {
|
|
1673
|
+
t >>>= 0;
|
|
1674
|
+
let e = this.mem.getUint32(t + 8, true);
|
|
1675
|
+
if (this._goRefCounts[e]--, this._goRefCounts[e] === 0) {
|
|
1676
|
+
let s2 = this._values[e];
|
|
1677
|
+
this._values[e] = null, this._ids.delete(s2), this._idPool.push(e);
|
|
1678
|
+
}
|
|
1679
|
+
}, "syscall/js.stringVal": (t) => {
|
|
1680
|
+
t >>>= 0, l(t + 24, u(t + 8));
|
|
1681
|
+
}, "syscall/js.valueGet": (t) => {
|
|
1682
|
+
t >>>= 0;
|
|
1683
|
+
let e = Reflect.get(r(t + 8), u(t + 16));
|
|
1684
|
+
t = this._inst.exports.getsp() >>> 0, l(t + 32, e);
|
|
1685
|
+
}, "syscall/js.valueSet": (t) => {
|
|
1686
|
+
t >>>= 0, Reflect.set(r(t + 8), u(t + 16), r(t + 32));
|
|
1687
|
+
}, "syscall/js.valueDelete": (t) => {
|
|
1688
|
+
t >>>= 0, Reflect.deleteProperty(r(t + 8), u(t + 16));
|
|
1689
|
+
}, "syscall/js.valueIndex": (t) => {
|
|
1690
|
+
t >>>= 0, l(t + 24, Reflect.get(r(t + 8), n(t + 16)));
|
|
1691
|
+
}, "syscall/js.valueSetIndex": (t) => {
|
|
1692
|
+
t >>>= 0, Reflect.set(r(t + 8), n(t + 16), r(t + 24));
|
|
1693
|
+
}, "syscall/js.valueCall": (t) => {
|
|
1694
|
+
t >>>= 0;
|
|
1695
|
+
try {
|
|
1696
|
+
let e = r(t + 8), s2 = Reflect.get(e, u(t + 16)), i2 = f2(t + 32), a = Reflect.apply(s2, e, i2);
|
|
1697
|
+
t = this._inst.exports.getsp() >>> 0, l(t + 56, a), this.mem.setUint8(t + 64, 1);
|
|
1698
|
+
} catch (e) {
|
|
1699
|
+
t = this._inst.exports.getsp() >>> 0, l(t + 56, e), this.mem.setUint8(t + 64, 0);
|
|
1700
|
+
}
|
|
1701
|
+
}, "syscall/js.valueInvoke": (t) => {
|
|
1702
|
+
t >>>= 0;
|
|
1703
|
+
try {
|
|
1704
|
+
let e = r(t + 8), s2 = f2(t + 16), i2 = Reflect.apply(e, void 0, s2);
|
|
1705
|
+
t = this._inst.exports.getsp() >>> 0, l(t + 40, i2), this.mem.setUint8(t + 48, 1);
|
|
1706
|
+
} catch (e) {
|
|
1707
|
+
t = this._inst.exports.getsp() >>> 0, l(t + 40, e), this.mem.setUint8(t + 48, 0);
|
|
1708
|
+
}
|
|
1709
|
+
}, "syscall/js.valueNew": (t) => {
|
|
1710
|
+
t >>>= 0;
|
|
1711
|
+
try {
|
|
1712
|
+
let e = r(t + 8), s2 = f2(t + 16), i2 = Reflect.construct(e, s2);
|
|
1713
|
+
t = this._inst.exports.getsp() >>> 0, l(t + 40, i2), this.mem.setUint8(t + 48, 1);
|
|
1714
|
+
} catch (e) {
|
|
1715
|
+
t = this._inst.exports.getsp() >>> 0, l(t + 40, e), this.mem.setUint8(t + 48, 0);
|
|
1716
|
+
}
|
|
1717
|
+
}, "syscall/js.valueLength": (t) => {
|
|
1718
|
+
t >>>= 0, o(t + 16, Number.parseInt(r(t + 8).length));
|
|
1719
|
+
}, "syscall/js.valuePrepareString": (t) => {
|
|
1720
|
+
t >>>= 0;
|
|
1721
|
+
let e = y.encode(String(r(t + 8)));
|
|
1722
|
+
l(t + 16, e), o(t + 24, e.length);
|
|
1723
|
+
}, "syscall/js.valueLoadString": (t) => {
|
|
1724
|
+
t >>>= 0;
|
|
1725
|
+
let e = r(t + 8);
|
|
1726
|
+
c(t + 16).set(e);
|
|
1727
|
+
}, "syscall/js.valueInstanceOf": (t) => {
|
|
1728
|
+
t >>>= 0, this.mem.setUint8(t + 24, r(t + 8) instanceof r(t + 16) ? 1 : 0);
|
|
1729
|
+
}, "syscall/js.copyBytesToGo": (t) => {
|
|
1730
|
+
t >>>= 0;
|
|
1731
|
+
let e = c(t + 8), s2 = r(t + 32);
|
|
1732
|
+
if (!(s2 instanceof Uint8Array || s2 instanceof Uint8ClampedArray)) {
|
|
1733
|
+
this.mem.setUint8(t + 48, 0);
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
let i2 = s2.subarray(0, e.length);
|
|
1737
|
+
e.set(i2), o(t + 40, i2.length), this.mem.setUint8(t + 48, 1);
|
|
1738
|
+
}, "syscall/js.copyBytesToJS": (t) => {
|
|
1739
|
+
t >>>= 0;
|
|
1740
|
+
let e = r(t + 8), s2 = c(t + 16);
|
|
1741
|
+
if (!(e instanceof Uint8Array || e instanceof Uint8ClampedArray)) {
|
|
1742
|
+
this.mem.setUint8(t + 48, 0);
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
let i2 = s2.subarray(0, e.length);
|
|
1746
|
+
e.set(i2), o(t + 40, i2.length), this.mem.setUint8(t + 48, 1);
|
|
1747
|
+
}, debug: (t) => {
|
|
1748
|
+
console.log(t);
|
|
1749
|
+
} } };
|
|
1750
|
+
}
|
|
1751
|
+
async run(o) {
|
|
1752
|
+
if (!(o instanceof WebAssembly.Instance)) throw new Error("Go.run: WebAssembly.Instance expected");
|
|
1753
|
+
this._inst = o, this.mem = new DataView(this._inst.exports.mem.buffer), this._values = [Number.NaN, 0, null, true, false, globalThis, this], this._goRefCounts = new Array(this._values.length).fill(Number.POSITIVE_INFINITY), this._ids = /* @__PURE__ */ new Map([[0, 1], [null, 2], [true, 3], [false, 4], [globalThis, 5], [this, 6]]), this._idPool = [], this.exited = false;
|
|
1754
|
+
let n = 4096, r = (h) => {
|
|
1755
|
+
let t = n, e = y.encode(`${h}\0`);
|
|
1756
|
+
return new Uint8Array(this.mem.buffer, n, e.length).set(e), n += e.length, n % 8 !== 0 && (n += 8 - n % 8), t;
|
|
1757
|
+
}, l = this.argv.length, c = [];
|
|
1758
|
+
this.argv.forEach((h) => {
|
|
1759
|
+
c.push(r(h));
|
|
1760
|
+
}), c.push(0), Object.keys(this.env).sort().forEach((h) => {
|
|
1761
|
+
c.push(r(`${h}=${this.env[h]}`));
|
|
1762
|
+
}), c.push(0);
|
|
1763
|
+
let u = n;
|
|
1764
|
+
c.forEach((h) => {
|
|
1765
|
+
this.mem.setUint32(n, h, true), this.mem.setUint32(n + 4, 0, true), n += 8;
|
|
1766
|
+
}), this._inst.exports.run(l, u), this.exited && this._resolveExitPromise(), await this._exitPromise;
|
|
1767
|
+
}
|
|
1768
|
+
_resume() {
|
|
1769
|
+
if (this.exited) throw new Error("Go program has already exited");
|
|
1770
|
+
this._inst.exports.resume(), this.exited && this._resolveExitPromise();
|
|
1771
|
+
}
|
|
1772
|
+
_makeFuncWrapper(o) {
|
|
1773
|
+
let n = this;
|
|
1774
|
+
return function() {
|
|
1775
|
+
let r = { id: o, this: this, args: arguments };
|
|
1776
|
+
return n._pendingEvent = r, n._resume(), r.result;
|
|
1777
|
+
};
|
|
1778
|
+
}
|
|
1779
|
+
};
|
|
1780
|
+
|
|
1781
|
+
// ../../node_modules/.pnpm/@astrojs+compiler@3.0.1/node_modules/@astrojs/compiler/dist/node/sync.js
|
|
1782
|
+
import { readFileSync as p } from "fs";
|
|
1783
|
+
import { fileURLToPath as m } from "url";
|
|
1784
|
+
function i() {
|
|
1785
|
+
return s || (s = f()), s;
|
|
1786
|
+
}
|
|
1787
|
+
var s;
|
|
1788
|
+
var w2 = (e, t) => i().parse(e, t);
|
|
1789
|
+
function f() {
|
|
1790
|
+
let e = new d(), t = v2(m(new URL("../astro.wasm", import.meta.url)), e.importObject);
|
|
1791
|
+
e.run(t);
|
|
1792
|
+
let o = globalThis["@astrojs/compiler"];
|
|
1793
|
+
return { transform: (n, a) => {
|
|
1794
|
+
try {
|
|
1795
|
+
return o.transform(n, a || {});
|
|
1796
|
+
} catch (r) {
|
|
1797
|
+
throw s = void 0, r;
|
|
1798
|
+
}
|
|
1799
|
+
}, parse: (n, a) => {
|
|
1800
|
+
try {
|
|
1801
|
+
let r = o.parse(n, a || {});
|
|
1802
|
+
return { ...r, ast: JSON.parse(r.ast) };
|
|
1803
|
+
} catch (r) {
|
|
1804
|
+
throw s = void 0, r;
|
|
1805
|
+
}
|
|
1806
|
+
}, convertToTSX: (n, a) => {
|
|
1807
|
+
try {
|
|
1808
|
+
let r = o.convertToTSX(n, a || {});
|
|
1809
|
+
return { ...r, map: JSON.parse(r.map) };
|
|
1810
|
+
} catch (r) {
|
|
1811
|
+
throw s = void 0, r;
|
|
1812
|
+
}
|
|
1813
|
+
} };
|
|
1814
|
+
}
|
|
1815
|
+
function v2(e, t) {
|
|
1816
|
+
let o = p(e);
|
|
1817
|
+
return new WebAssembly.Instance(new WebAssembly.Module(o), t);
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
// src/transform/transform-astro.ts
|
|
1821
|
+
function walk2(node, visitor) {
|
|
1822
|
+
if (!node || typeof node !== "object") return;
|
|
1823
|
+
visitor.enter(node);
|
|
1824
|
+
if (Array.isArray(node.children)) {
|
|
1825
|
+
for (const child of node.children) {
|
|
1826
|
+
walk2(child, visitor);
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
function transformAstro(options) {
|
|
1831
|
+
const { filePath, source, escapeTags, attributeName = "data-inspecto" } = options;
|
|
1832
|
+
const escapeTagsSet = buildEscapeTagsSet(escapeTags);
|
|
1833
|
+
let ast;
|
|
1834
|
+
try {
|
|
1835
|
+
ast = w2(source, { position: true }).ast;
|
|
1836
|
+
} catch (_err) {
|
|
1837
|
+
return { code: source, map: null, changed: false };
|
|
1838
|
+
}
|
|
1839
|
+
const s2 = new MagicString4(source);
|
|
1840
|
+
let changed = false;
|
|
1841
|
+
walk2(ast, {
|
|
1842
|
+
enter(node) {
|
|
1843
|
+
if (node.type === "element" || node.type === "component") {
|
|
1844
|
+
const tagName = node.name;
|
|
1845
|
+
if (tagName && !escapeTagsSet.has(tagName) && !node.attributes?.some((attr) => attr.name === attributeName)) {
|
|
1846
|
+
const startOffset = node.position?.start?.offset ?? -1;
|
|
1847
|
+
if (startOffset === -1) return;
|
|
1848
|
+
let tagStartIndex = startOffset;
|
|
1849
|
+
while (tagStartIndex >= 0 && source[tagStartIndex] !== "<") {
|
|
1850
|
+
tagStartIndex--;
|
|
1851
|
+
}
|
|
1852
|
+
if (tagStartIndex >= 0) {
|
|
1853
|
+
const substringAfterTag = source.substring(tagStartIndex);
|
|
1854
|
+
const escapedTagName = tagName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1855
|
+
const strictRegex = new RegExp(`^<\\s*${escapedTagName}(?=\\s|/|>)`, "i");
|
|
1856
|
+
const strictMatch = substringAfterTag.match(strictRegex);
|
|
1857
|
+
if (strictMatch) {
|
|
1858
|
+
const insertPosition = tagStartIndex + strictMatch[0].length;
|
|
1859
|
+
const line = node.position.start.line;
|
|
1860
|
+
const column = node.position.start.column;
|
|
1861
|
+
const attrValue = formatAttrValue(filePath, line, column);
|
|
1862
|
+
const addition = ` ${attributeName}="${attrValue}"`;
|
|
1863
|
+
s2.appendLeft(insertPosition, addition);
|
|
1864
|
+
changed = true;
|
|
1865
|
+
}
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
});
|
|
1871
|
+
return {
|
|
1872
|
+
code: s2.toString(),
|
|
1873
|
+
map: changed ? s2.generateMap({ source: filePath, includeContent: true }) : null,
|
|
1874
|
+
changed
|
|
1875
|
+
};
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
// src/transform/index.ts
|
|
1879
|
+
function transformRouter(options) {
|
|
1880
|
+
const { filePath, source, projectRoot, pluginOptions } = options;
|
|
1881
|
+
const ext = path8.extname(filePath).toLowerCase();
|
|
1882
|
+
if (JSX_EXTENSIONS.has(ext)) {
|
|
1883
|
+
return transformJsx({
|
|
1884
|
+
filePath,
|
|
1885
|
+
source,
|
|
1886
|
+
projectRoot,
|
|
1887
|
+
escapeTags: pluginOptions.escapeTags,
|
|
1888
|
+
pathType: pluginOptions.pathType,
|
|
1889
|
+
attributeName: pluginOptions.attributeName
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
if (ext === ".vue") {
|
|
1893
|
+
return transformVue({
|
|
1894
|
+
filePath,
|
|
1895
|
+
source,
|
|
1896
|
+
projectRoot,
|
|
1897
|
+
escapeTags: pluginOptions.escapeTags,
|
|
1898
|
+
pathType: pluginOptions.pathType,
|
|
1899
|
+
attributeName: pluginOptions.attributeName
|
|
1900
|
+
});
|
|
1901
|
+
}
|
|
1902
|
+
if (ext === ".svelte") {
|
|
1903
|
+
return transformSvelte({
|
|
1904
|
+
filePath,
|
|
1905
|
+
source,
|
|
1906
|
+
escapeTags: pluginOptions.escapeTags,
|
|
1907
|
+
attributeName: pluginOptions.attributeName
|
|
1908
|
+
});
|
|
1909
|
+
}
|
|
1910
|
+
if (ext === ".astro") {
|
|
1911
|
+
return transformAstro({
|
|
1912
|
+
filePath,
|
|
1913
|
+
source,
|
|
1914
|
+
escapeTags: pluginOptions.escapeTags,
|
|
1915
|
+
attributeName: pluginOptions.attributeName
|
|
1916
|
+
});
|
|
1917
|
+
}
|
|
1918
|
+
return null;
|
|
1919
|
+
}
|
|
1920
|
+
|
|
1921
|
+
// src/injectors/utils.ts
|
|
1922
|
+
import { createRequire } from "module";
|
|
1923
|
+
var resolveClientModule = () => {
|
|
1924
|
+
try {
|
|
1925
|
+
return createRequire(import.meta.url).resolve("@inspecto-dev/core");
|
|
1926
|
+
} catch {
|
|
1927
|
+
try {
|
|
1928
|
+
return __require.resolve("@inspecto-dev/core");
|
|
1929
|
+
} catch {
|
|
1930
|
+
console.warn(
|
|
1931
|
+
"[inspecto] Could not resolve @inspecto-dev/core \u2014 falling back to bare specifier"
|
|
1932
|
+
);
|
|
1933
|
+
return "@inspecto-dev/core";
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
};
|
|
1937
|
+
|
|
1938
|
+
// src/injectors/webpack.ts
|
|
1939
|
+
function getWebpackHtmlScript(serverPort) {
|
|
1940
|
+
return `
|
|
1941
|
+
window.__AI_INSPECTOR_PORT__ = ${serverPort};
|
|
1942
|
+
window.addEventListener('load', () => {
|
|
1943
|
+
if (window.InspectoClient) {
|
|
1944
|
+
window.InspectoClient.mountInspector({
|
|
1945
|
+
serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
});
|
|
1949
|
+
`;
|
|
1950
|
+
}
|
|
1951
|
+
function getWebpackAssetScript(serverPort) {
|
|
1952
|
+
return `
|
|
1953
|
+
if (typeof window !== 'undefined') {
|
|
1954
|
+
window.__AI_INSPECTOR_PORT__ = ${serverPort};
|
|
1955
|
+
const _initInspecto = () => {
|
|
1956
|
+
if (window.InspectoClient) {
|
|
1957
|
+
window.InspectoClient.mountInspector({
|
|
1958
|
+
serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
|
|
1959
|
+
});
|
|
1960
|
+
} else {
|
|
1961
|
+
setTimeout(_initInspecto, 100);
|
|
1962
|
+
}
|
|
1963
|
+
};
|
|
1964
|
+
if (document.readyState === 'complete') {
|
|
1965
|
+
_initInspecto();
|
|
1966
|
+
} else {
|
|
1967
|
+
window.addEventListener('load', _initInspecto);
|
|
1968
|
+
}
|
|
1969
|
+
}
|
|
1970
|
+
`;
|
|
1971
|
+
}
|
|
1972
|
+
function injectWebpack(compiler, serverPortFn, resolveClientModule2) {
|
|
1973
|
+
const inspectoClientPath = resolveClientModule2();
|
|
1974
|
+
if (compiler.webpack && compiler.webpack.EntryPlugin) {
|
|
1975
|
+
new compiler.webpack.EntryPlugin(compiler.context, inspectoClientPath, {
|
|
1976
|
+
name: void 0
|
|
1977
|
+
}).apply(compiler);
|
|
1978
|
+
}
|
|
1979
|
+
compiler.hooks.compilation.tap("inspecto-overlay", (compilation) => {
|
|
1980
|
+
const HtmlWebpackPlugin = compiler.options.plugins.find(
|
|
1981
|
+
(p2) => p2 && p2.constructor && p2.constructor.name === "HtmlWebpackPlugin"
|
|
1982
|
+
);
|
|
1983
|
+
if (HtmlWebpackPlugin) {
|
|
1984
|
+
const hooks = HtmlWebpackPlugin.constructor.getHooks(compilation);
|
|
1985
|
+
hooks.alterAssetTagGroups.tapPromise("inspecto-overlay", async (data) => {
|
|
1986
|
+
const port = await serverPortFn();
|
|
1987
|
+
data.headTags.unshift({
|
|
1988
|
+
tagName: "script",
|
|
1989
|
+
voidTag: false,
|
|
1990
|
+
meta: { plugin: "inspecto-overlay" },
|
|
1991
|
+
innerHTML: getWebpackHtmlScript(port)
|
|
1992
|
+
});
|
|
1993
|
+
return data;
|
|
1994
|
+
});
|
|
1995
|
+
} else {
|
|
1996
|
+
if (compilation.hooks.processAssets) {
|
|
1997
|
+
compilation.hooks.processAssets.tapPromise(
|
|
1998
|
+
{
|
|
1999
|
+
name: "inspecto-overlay",
|
|
2000
|
+
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
|
|
2001
|
+
},
|
|
2002
|
+
async (assets) => {
|
|
2003
|
+
const port = await serverPortFn();
|
|
2004
|
+
const mainAssetKey = Object.keys(assets).find(
|
|
2005
|
+
(key) => key.endsWith(".js") && (key.includes("main") || key.includes("app") || key.includes("umi"))
|
|
2006
|
+
);
|
|
2007
|
+
if (!mainAssetKey) return;
|
|
2008
|
+
const originalSource = assets[mainAssetKey].source();
|
|
2009
|
+
assets[mainAssetKey] = new compiler.webpack.sources.RawSource(
|
|
2010
|
+
getWebpackAssetScript(port) + "\n" + originalSource
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
2013
|
+
);
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
|
|
2019
|
+
// src/injectors/rspack.ts
|
|
2020
|
+
function injectRspack(compiler, serverPortFn, resolveClientModule2) {
|
|
2021
|
+
const inspectoClientPath = resolveClientModule2();
|
|
2022
|
+
new compiler.webpack.EntryPlugin(compiler.context, inspectoClientPath, {}).apply(compiler);
|
|
2023
|
+
compiler.hooks.compilation.tap("inspecto-overlay", (compilation) => {
|
|
2024
|
+
const HtmlRspackPlugin = compiler.options.plugins.find(
|
|
2025
|
+
(p2) => p2 && p2.constructor && p2.constructor.name === "HtmlRspackPlugin"
|
|
2026
|
+
);
|
|
2027
|
+
if (HtmlRspackPlugin) {
|
|
2028
|
+
const hooks = HtmlRspackPlugin.constructor.getHooks(compilation);
|
|
2029
|
+
hooks.alterAssetTagGroups.tapPromise("inspecto-overlay", async (data) => {
|
|
2030
|
+
const port = await serverPortFn();
|
|
2031
|
+
data.headTags.unshift({
|
|
2032
|
+
tagName: "script",
|
|
2033
|
+
voidTag: false,
|
|
2034
|
+
meta: { plugin: "inspecto-overlay" },
|
|
2035
|
+
innerHTML: getWebpackHtmlScript(port)
|
|
2036
|
+
});
|
|
2037
|
+
return data;
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
});
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// src/injectors/vite.ts
|
|
2044
|
+
function getViteVirtualModuleScript(serverPort) {
|
|
2045
|
+
return `
|
|
2046
|
+
import { mountInspector } from '@inspecto-dev/core';
|
|
2047
|
+
window.__AI_INSPECTOR_PORT__ = ${serverPort};
|
|
2048
|
+
mountInspector({
|
|
2049
|
+
serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
|
|
2050
|
+
});
|
|
2051
|
+
`;
|
|
2052
|
+
}
|
|
2053
|
+
var VITE_VIRTUAL_MODULE_ID = "\0virtual:inspecto-client";
|
|
2054
|
+
var VITE_VIRTUAL_IMPORT_ID = "/@id/__x00__virtual:inspecto-client";
|
|
2055
|
+
|
|
2056
|
+
// src/index.ts
|
|
2057
|
+
var DEFAULT_OPTIONS = {
|
|
2058
|
+
include: [],
|
|
2059
|
+
exclude: [],
|
|
2060
|
+
escapeTags: [],
|
|
2061
|
+
pathType: "absolute",
|
|
2062
|
+
attributeName: "data-inspecto",
|
|
2063
|
+
logLevel: "warn"
|
|
2064
|
+
};
|
|
2065
|
+
var DEFAULT_PORT = 5678;
|
|
2066
|
+
var InspectoPlugin = createUnplugin((userOptions = {}) => {
|
|
2067
|
+
const options = {
|
|
2068
|
+
...DEFAULT_OPTIONS,
|
|
2069
|
+
...userOptions
|
|
2070
|
+
};
|
|
2071
|
+
setGlobalLogLevel(options.logLevel);
|
|
2072
|
+
const pluginLogger = createLogger("inspecto:plugin", { logLevel: options.logLevel });
|
|
2073
|
+
const isProduction = process.env["NODE_ENV"] === "production";
|
|
2074
|
+
let projectRoot = process.cwd();
|
|
2075
|
+
let serverPort = null;
|
|
2076
|
+
const ensureServer = async () => {
|
|
2077
|
+
if (serverPort === null) {
|
|
2078
|
+
serverPort = await startServer();
|
|
2079
|
+
}
|
|
2080
|
+
return serverPort;
|
|
2081
|
+
};
|
|
2082
|
+
return {
|
|
2083
|
+
name: "inspecto-overlay",
|
|
2084
|
+
enforce: "pre",
|
|
2085
|
+
buildStart() {
|
|
2086
|
+
if (isProduction) return;
|
|
2087
|
+
projectRoot = serverState.cwd || process.cwd();
|
|
2088
|
+
ensureServer().catch((err) => pluginLogger.error("Failed to start server:", err));
|
|
2089
|
+
},
|
|
2090
|
+
buildEnd() {
|
|
2091
|
+
},
|
|
2092
|
+
webpack: (compiler) => {
|
|
2093
|
+
if (isProduction) return;
|
|
2094
|
+
injectWebpack(compiler, ensureServer, resolveClientModule);
|
|
2095
|
+
},
|
|
2096
|
+
rspack: (compiler) => {
|
|
2097
|
+
if (isProduction) return;
|
|
2098
|
+
injectRspack(compiler, ensureServer, resolveClientModule);
|
|
2099
|
+
},
|
|
2100
|
+
vite: {
|
|
2101
|
+
config(config) {
|
|
2102
|
+
if (isProduction) return config;
|
|
2103
|
+
return {
|
|
2104
|
+
...config,
|
|
2105
|
+
define: {
|
|
2106
|
+
...config.define,
|
|
2107
|
+
__AI_INSPECTOR_PORT__: JSON.stringify(DEFAULT_PORT)
|
|
2108
|
+
// Placeholder, rewritten in configureServer
|
|
2109
|
+
}
|
|
2110
|
+
};
|
|
2111
|
+
},
|
|
2112
|
+
resolveId(id) {
|
|
2113
|
+
if (id === "virtual:inspecto-client") {
|
|
2114
|
+
return VITE_VIRTUAL_MODULE_ID;
|
|
2115
|
+
}
|
|
2116
|
+
return null;
|
|
2117
|
+
},
|
|
2118
|
+
load(id) {
|
|
2119
|
+
if (id === VITE_VIRTUAL_MODULE_ID) {
|
|
2120
|
+
return getViteVirtualModuleScript(serverPort ?? DEFAULT_PORT);
|
|
2121
|
+
}
|
|
2122
|
+
return null;
|
|
2123
|
+
},
|
|
2124
|
+
async configureServer(server) {
|
|
2125
|
+
if (isProduction) return;
|
|
2126
|
+
const port = await ensureServer();
|
|
2127
|
+
if (!server.config.define) {
|
|
2128
|
+
;
|
|
2129
|
+
server.config.define = {};
|
|
2130
|
+
}
|
|
2131
|
+
;
|
|
2132
|
+
server.config.define["__AI_INSPECTOR_PORT__"] = JSON.stringify(port);
|
|
2133
|
+
const mod = server.moduleGraph.getModuleById(VITE_VIRTUAL_MODULE_ID);
|
|
2134
|
+
if (mod) server.moduleGraph.invalidateModule(mod);
|
|
2135
|
+
},
|
|
2136
|
+
transformIndexHtml(html) {
|
|
2137
|
+
if (isProduction || !serverPort) return html;
|
|
2138
|
+
return {
|
|
2139
|
+
html,
|
|
2140
|
+
tags: [
|
|
2141
|
+
{
|
|
2142
|
+
tag: "script",
|
|
2143
|
+
attrs: { type: "module" },
|
|
2144
|
+
children: `import '${VITE_VIRTUAL_IMPORT_ID}';`
|
|
2145
|
+
}
|
|
2146
|
+
]
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
},
|
|
2150
|
+
transformInclude(id) {
|
|
2151
|
+
if (isProduction || !id) return false;
|
|
2152
|
+
return shouldTransform(id, options);
|
|
2153
|
+
},
|
|
2154
|
+
transform(code, id) {
|
|
2155
|
+
if (isProduction || !id) return null;
|
|
2156
|
+
const { filePath } = extractTransformFilePath(id);
|
|
2157
|
+
const result = transformRouter({
|
|
2158
|
+
filePath,
|
|
2159
|
+
source: code,
|
|
2160
|
+
projectRoot,
|
|
2161
|
+
pluginOptions: options
|
|
2162
|
+
});
|
|
2163
|
+
if (!result || !result.changed) return null;
|
|
2164
|
+
return {
|
|
2165
|
+
code: result.code,
|
|
2166
|
+
map: result.map
|
|
2167
|
+
};
|
|
2168
|
+
}
|
|
2169
|
+
};
|
|
2170
|
+
});
|
|
2171
|
+
var vitePlugin = InspectoPlugin.vite;
|
|
2172
|
+
var webpackPlugin = InspectoPlugin.webpack;
|
|
2173
|
+
var rspackPlugin = InspectoPlugin.rspack;
|
|
2174
|
+
var rollupPlugin = InspectoPlugin.rollup;
|
|
2175
|
+
var esbuildPlugin = InspectoPlugin.esbuild;
|
|
2176
|
+
|
|
2177
|
+
// src/astro.ts
|
|
2178
|
+
function getAstroInjectedScript(serverPort) {
|
|
2179
|
+
return `
|
|
2180
|
+
import { mountInspector } from '@inspecto-dev/core';
|
|
2181
|
+
window.__AI_INSPECTOR_SERVER_URL__ = 'http://127.0.0.1:${serverPort}';
|
|
2182
|
+
mountInspector({
|
|
2183
|
+
serverUrl: window.__AI_INSPECTOR_SERVER_URL__,
|
|
2184
|
+
});
|
|
2185
|
+
`;
|
|
2186
|
+
}
|
|
2187
|
+
function astroIntegration(options) {
|
|
2188
|
+
return {
|
|
2189
|
+
name: "inspecto-astro",
|
|
2190
|
+
hooks: {
|
|
2191
|
+
async "astro:config:setup"({ command, injectScript, updateConfig }) {
|
|
2192
|
+
updateConfig({
|
|
2193
|
+
vite: {
|
|
2194
|
+
plugins: [vitePlugin(options)]
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
if (command !== "dev") {
|
|
2198
|
+
return;
|
|
2199
|
+
}
|
|
2200
|
+
const serverPort = await startServer();
|
|
2201
|
+
injectScript("page", getAstroInjectedScript(serverPort));
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
var astro_default = astroIntegration;
|
|
2207
|
+
export {
|
|
2208
|
+
astroIntegration,
|
|
2209
|
+
astro_default as default,
|
|
2210
|
+
getAstroInjectedScript
|
|
2211
|
+
};
|
|
2212
|
+
//# sourceMappingURL=astro.js.map
|