@inspecto-dev/plugin 0.2.0-alpha.3 → 0.3.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/index.cjs +452 -195
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +452 -195
- package/dist/index.js.map +1 -1
- package/dist/legacy/rspack/index.cjs +454 -197
- package/dist/legacy/rspack/index.cjs.map +1 -1
- package/dist/legacy/rspack/index.js +454 -197
- package/dist/legacy/rspack/index.js.map +1 -1
- package/dist/legacy/webpack4/index.cjs +454 -197
- package/dist/legacy/webpack4/index.cjs.map +1 -1
- package/dist/legacy/webpack4/index.js +454 -197
- package/dist/legacy/webpack4/index.js.map +1 -1
- package/dist/rollup.cjs +452 -195
- package/dist/rollup.cjs.map +1 -1
- package/dist/rollup.js +452 -195
- package/dist/rollup.js.map +1 -1
- package/dist/rspack.cjs +452 -195
- package/dist/rspack.cjs.map +1 -1
- package/dist/rspack.js +452 -195
- package/dist/rspack.js.map +1 -1
- package/dist/vite.cjs +452 -195
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +452 -195
- package/dist/vite.js.map +1 -1
- package/dist/webpack.cjs +452 -195
- package/dist/webpack.cjs.map +1 -1
- package/dist/webpack.js +452 -195
- package/dist/webpack.js.map +1 -1
- package/package.json +2 -2
package/dist/rollup.cjs
CHANGED
|
@@ -309,13 +309,11 @@ function transformRouter(options) {
|
|
|
309
309
|
|
|
310
310
|
// src/server/index.ts
|
|
311
311
|
var import_node_http = __toESM(require("http"), 1);
|
|
312
|
-
var
|
|
313
|
-
var
|
|
312
|
+
var import_node_fs3 = __toESM(require("fs"), 1);
|
|
313
|
+
var import_node_path7 = __toESM(require("path"), 1);
|
|
314
314
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
315
|
-
var
|
|
316
|
-
var import_node_child_process = require("child_process");
|
|
315
|
+
var import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
317
316
|
var import_portfinder = __toESM(require("portfinder"), 1);
|
|
318
|
-
var import_launch_ide = require("launch-ide");
|
|
319
317
|
var import_types2 = require("@inspecto-dev/types");
|
|
320
318
|
|
|
321
319
|
// src/server/snippet.ts
|
|
@@ -649,9 +647,9 @@ function extractToolOverrides(ide, config) {
|
|
|
649
647
|
function resolveIntents(serverPrompts) {
|
|
650
648
|
const baseMap = /* @__PURE__ */ new Map();
|
|
651
649
|
for (const intent of import_types.DEFAULT_INTENTS) {
|
|
652
|
-
|
|
650
|
+
baseMap.set(intent.id, { ...intent });
|
|
653
651
|
}
|
|
654
|
-
const defaults = () =>
|
|
652
|
+
const defaults = () => Array.from(baseMap.values());
|
|
655
653
|
if (!serverPrompts) return defaults();
|
|
656
654
|
const isReplace = !Array.isArray(serverPrompts) && typeof serverPrompts === "object" && serverPrompts.$replace === true;
|
|
657
655
|
const promptsArray = Array.isArray(serverPrompts) ? serverPrompts : isReplace ? serverPrompts.items : [];
|
|
@@ -678,16 +676,18 @@ function resolveIntents(serverPrompts) {
|
|
|
678
676
|
);
|
|
679
677
|
continue;
|
|
680
678
|
}
|
|
681
|
-
if (item.
|
|
679
|
+
if (!item.aiIntent) {
|
|
682
680
|
configLogger.warn(
|
|
683
|
-
`
|
|
681
|
+
`Intent "${item.id}" is missing required "aiIntent".`
|
|
684
682
|
);
|
|
685
683
|
continue;
|
|
686
684
|
}
|
|
687
|
-
result.push(
|
|
685
|
+
result.push(
|
|
686
|
+
baseMap.has(item.id) ? { ...baseMap.get(item.id), ...item } : item
|
|
687
|
+
);
|
|
688
688
|
}
|
|
689
689
|
}
|
|
690
|
-
return
|
|
690
|
+
return result;
|
|
691
691
|
}
|
|
692
692
|
const merged = Array.from(baseMap.values());
|
|
693
693
|
for (const item of promptsArray) {
|
|
@@ -704,9 +704,9 @@ function resolveIntents(serverPrompts) {
|
|
|
704
704
|
configLogger.warn('Intent object missing required "id" field, skipping.');
|
|
705
705
|
continue;
|
|
706
706
|
}
|
|
707
|
-
if (item.
|
|
707
|
+
if (!item.aiIntent) {
|
|
708
708
|
configLogger.warn(
|
|
709
|
-
`
|
|
709
|
+
`Intent "${item.id}" is missing required "aiIntent".`
|
|
710
710
|
);
|
|
711
711
|
continue;
|
|
712
712
|
}
|
|
@@ -724,15 +724,7 @@ function resolveIntents(serverPrompts) {
|
|
|
724
724
|
}
|
|
725
725
|
}
|
|
726
726
|
}
|
|
727
|
-
return
|
|
728
|
-
}
|
|
729
|
-
function ensureOpenInEditorLast(intents) {
|
|
730
|
-
const idx = intents.findIndex((i) => i.id === "open-in-editor");
|
|
731
|
-
if (idx === -1 || idx === intents.length - 1) return intents;
|
|
732
|
-
const result = [...intents];
|
|
733
|
-
const item = result.splice(idx, 1)[0];
|
|
734
|
-
result.push(item);
|
|
735
|
-
return result;
|
|
727
|
+
return merged;
|
|
736
728
|
}
|
|
737
729
|
var watchers = [];
|
|
738
730
|
function watchConfig(onReload, cwd = process.cwd(), gitRoot) {
|
|
@@ -767,7 +759,10 @@ function watchConfig(onReload, cwd = process.cwd(), gitRoot) {
|
|
|
767
759
|
}
|
|
768
760
|
}
|
|
769
761
|
|
|
770
|
-
// src/server/
|
|
762
|
+
// src/server/dispatch-transport.ts
|
|
763
|
+
var import_node_crypto = __toESM(require("crypto"), 1);
|
|
764
|
+
var import_node_child_process = require("child_process");
|
|
765
|
+
var import_launch_ide = require("launch-ide");
|
|
771
766
|
var serverLogger = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
772
767
|
var payloadTickets = /* @__PURE__ */ new Map();
|
|
773
768
|
function createTicket(payload) {
|
|
@@ -781,32 +776,8 @@ function createTicket(payload) {
|
|
|
781
776
|
);
|
|
782
777
|
return ticketId;
|
|
783
778
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
running: false,
|
|
787
|
-
projectRoot: "",
|
|
788
|
-
configRoot: "",
|
|
789
|
-
cwd: process.cwd()
|
|
790
|
-
};
|
|
791
|
-
var serverInstance = null;
|
|
792
|
-
function resolveProjectRoot() {
|
|
793
|
-
let gitRoot;
|
|
794
|
-
try {
|
|
795
|
-
serverLogger.info("Resolving project root...");
|
|
796
|
-
gitRoot = (0, import_node_child_process.execSync)("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
797
|
-
serverLogger.info("Resolved project root: " + gitRoot);
|
|
798
|
-
} catch (e) {
|
|
799
|
-
serverLogger.error("Failed to resolve project root:", e);
|
|
800
|
-
gitRoot = process.cwd();
|
|
801
|
-
}
|
|
802
|
-
let current = gitRoot;
|
|
803
|
-
while (true) {
|
|
804
|
-
if (import_node_fs2.default.existsSync(import_node_path5.default.join(current, ".inspecto"))) return current;
|
|
805
|
-
const parent = import_node_path5.default.dirname(current);
|
|
806
|
-
if (parent === current) break;
|
|
807
|
-
current = parent;
|
|
808
|
-
}
|
|
809
|
-
return gitRoot;
|
|
779
|
+
function readTicket(ticketId) {
|
|
780
|
+
return payloadTickets.get(ticketId);
|
|
810
781
|
}
|
|
811
782
|
function launchURI(uri) {
|
|
812
783
|
try {
|
|
@@ -822,6 +793,378 @@ function launchURI(uri) {
|
|
|
822
793
|
(0, import_launch_ide.launchIDE)({ file: uri });
|
|
823
794
|
}
|
|
824
795
|
}
|
|
796
|
+
|
|
797
|
+
// src/server/dispatch-runtime.ts
|
|
798
|
+
function resolvePromptDispatchRuntime(state) {
|
|
799
|
+
const userConfig = loadUserConfigSync(false, state.cwd, state.projectRoot);
|
|
800
|
+
const resolvedTarget = resolveTargetTool(userConfig);
|
|
801
|
+
const finalIde = resolveFinalIde(userConfig.ide, state.ideInfo?.ide, state.ideInfo?.scheme);
|
|
802
|
+
const mode = resolveProviderMode(resolvedTarget, finalIde, userConfig);
|
|
803
|
+
const overrides = extractToolOverrides(finalIde, userConfig)[resolvedTarget] || void 0;
|
|
804
|
+
return {
|
|
805
|
+
resolvedTarget,
|
|
806
|
+
finalIde,
|
|
807
|
+
mode,
|
|
808
|
+
...hasOverrides(overrides) ? { overrides } : {},
|
|
809
|
+
...userConfig["prompt.autoSend"] !== void 0 ? { autoSend: Boolean(userConfig["prompt.autoSend"]) } : {}
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
function dispatchPromptThroughIde(runtime, payload) {
|
|
813
|
+
const ticketId = createTicket({
|
|
814
|
+
ide: runtime.finalIde,
|
|
815
|
+
target: runtime.resolvedTarget,
|
|
816
|
+
targetType: runtime.mode,
|
|
817
|
+
prompt: payload.prompt,
|
|
818
|
+
filePath: payload.filePath,
|
|
819
|
+
line: payload.line,
|
|
820
|
+
column: payload.column,
|
|
821
|
+
snippet: payload.snippet,
|
|
822
|
+
...payload.screenshotContext ? { screenshotContext: payload.screenshotContext } : {},
|
|
823
|
+
overrides: runtime.overrides,
|
|
824
|
+
autoSend: runtime.autoSend
|
|
825
|
+
});
|
|
826
|
+
const params = new URLSearchParams();
|
|
827
|
+
params.set("ticket", ticketId);
|
|
828
|
+
params.set("target", runtime.resolvedTarget);
|
|
829
|
+
launchURI(`${runtime.finalIde}://inspecto.inspecto/send?${params.toString()}`);
|
|
830
|
+
return {
|
|
831
|
+
success: true,
|
|
832
|
+
fallbackPayload: {
|
|
833
|
+
prompt: payload.prompt,
|
|
834
|
+
...payload.filePath ? { file: payload.filePath } : {}
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
function resolveFinalIde(configuredIde, activeIde, activeIdeScheme) {
|
|
839
|
+
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
840
|
+
return configuredIde;
|
|
841
|
+
}
|
|
842
|
+
return configuredIde || activeIdeScheme || activeIde || "vscode";
|
|
843
|
+
}
|
|
844
|
+
function hasOverrides(overrides) {
|
|
845
|
+
return Boolean(overrides && Object.keys(overrides).length > 0);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// src/server/path-guards.ts
|
|
849
|
+
var import_node_path5 = __toESM(require("path"), 1);
|
|
850
|
+
function isWindowsAbsolutePath(file) {
|
|
851
|
+
return /^[a-zA-Z]:[\\/]/.test(file) || /^\\\\[^\\]+\\[^\\]+/.test(file);
|
|
852
|
+
}
|
|
853
|
+
function resolveWorkspacePath(file, cwd) {
|
|
854
|
+
if (isWindowsAbsolutePath(file)) {
|
|
855
|
+
return import_node_path5.default.win32.normalize(file);
|
|
856
|
+
}
|
|
857
|
+
return import_node_path5.default.isAbsolute(file) ? import_node_path5.default.resolve(file) : import_node_path5.default.resolve(cwd, file);
|
|
858
|
+
}
|
|
859
|
+
function assertPathWithinProject(file, projectRoot) {
|
|
860
|
+
const relativeToRoot = isWindowsAbsolutePath(file) || isWindowsAbsolutePath(projectRoot) ? import_node_path5.default.win32.relative(import_node_path5.default.win32.normalize(projectRoot), import_node_path5.default.win32.normalize(file)) : import_node_path5.default.relative(projectRoot, file);
|
|
861
|
+
if (relativeToRoot.startsWith("..") || import_node_path5.default.isAbsolute(relativeToRoot)) {
|
|
862
|
+
throw new Error("Access denied: File is outside of project workspace");
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// src/server/annotation-dispatch.ts
|
|
867
|
+
var AnnotationDispatchError = class extends Error {
|
|
868
|
+
constructor(message, errorCode) {
|
|
869
|
+
super(message);
|
|
870
|
+
this.name = "AnnotationDispatchError";
|
|
871
|
+
this.errorCode = errorCode;
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
async function dispatchAnnotationsToAi(req, state) {
|
|
875
|
+
try {
|
|
876
|
+
validateAnnotationDispatchRequest(req, state);
|
|
877
|
+
const batch = normalizeAnnotationBatch(req);
|
|
878
|
+
const prompt = buildAnnotationBatchPrompt(batch);
|
|
879
|
+
const representativeTarget = batch.annotations[0]?.targets[0];
|
|
880
|
+
const runtime = resolvePromptDispatchRuntime(state);
|
|
881
|
+
return dispatchPromptThroughIde(runtime, {
|
|
882
|
+
prompt,
|
|
883
|
+
...representativeTarget?.file ? { filePath: representativeTarget.file } : {},
|
|
884
|
+
...representativeTarget?.line ? { line: representativeTarget.line } : {},
|
|
885
|
+
...representativeTarget?.column ? { column: representativeTarget.column } : {},
|
|
886
|
+
...batch.screenshotContext ? { screenshotContext: batch.screenshotContext } : {}
|
|
887
|
+
});
|
|
888
|
+
} catch (error) {
|
|
889
|
+
return {
|
|
890
|
+
success: false,
|
|
891
|
+
error: error instanceof Error ? error.message : String(error),
|
|
892
|
+
errorCode: getAnnotationDispatchErrorCode(error)
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
function validateAnnotationDispatchRequest(req, state) {
|
|
897
|
+
if (!req.annotations.length) {
|
|
898
|
+
throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
|
|
899
|
+
}
|
|
900
|
+
for (const annotation of req.annotations) {
|
|
901
|
+
if (!annotation.targets.length) {
|
|
902
|
+
throw new AnnotationDispatchError(
|
|
903
|
+
"Each annotation must include at least one target.",
|
|
904
|
+
"INVALID_REQUEST"
|
|
905
|
+
);
|
|
906
|
+
}
|
|
907
|
+
for (const target of annotation.targets) {
|
|
908
|
+
const absolutePath = resolveWorkspacePath(target.location.file, state.cwd);
|
|
909
|
+
assertPathWithinProject(absolutePath, state.projectRoot);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
function normalizeAnnotationBatch(req) {
|
|
914
|
+
return {
|
|
915
|
+
instruction: req.instruction?.trim() ?? "",
|
|
916
|
+
responseMode: req.responseMode ?? "unified",
|
|
917
|
+
...req.runtimeContext ? { runtimeContext: req.runtimeContext } : {},
|
|
918
|
+
...req.screenshotContext ? { screenshotContext: req.screenshotContext } : {},
|
|
919
|
+
...req.cssContextPrompt?.trim() ? { cssContextPrompt: req.cssContextPrompt.trim() } : {},
|
|
920
|
+
annotations: req.annotations.map((annotation, index) => ({
|
|
921
|
+
index: index + 1,
|
|
922
|
+
note: annotation.note.trim(),
|
|
923
|
+
intent: annotation.intent,
|
|
924
|
+
targets: annotation.targets.map((target) => ({
|
|
925
|
+
file: target.location.file,
|
|
926
|
+
line: target.location.line,
|
|
927
|
+
column: target.location.column,
|
|
928
|
+
...target.label ? { label: target.label } : {},
|
|
929
|
+
...target.selector ? { selector: target.selector } : {},
|
|
930
|
+
...target.snippet ? { snippet: target.snippet } : {}
|
|
931
|
+
}))
|
|
932
|
+
}))
|
|
933
|
+
};
|
|
934
|
+
}
|
|
935
|
+
function buildAnnotationBatchPrompt(batch) {
|
|
936
|
+
const body = buildSelectedElementsPrompt(batch.annotations);
|
|
937
|
+
const prompt = batch.instruction ? `${batch.instruction}
|
|
938
|
+
|
|
939
|
+
${body}` : body;
|
|
940
|
+
return appendScreenshotContextSection(
|
|
941
|
+
appendCssContextSection(
|
|
942
|
+
appendRuntimeContextSection(prompt, batch.runtimeContext),
|
|
943
|
+
batch.cssContextPrompt
|
|
944
|
+
),
|
|
945
|
+
batch.screenshotContext
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
function appendCssContextSection(prompt, cssContextPrompt) {
|
|
949
|
+
if (!cssContextPrompt) return prompt;
|
|
950
|
+
return `${prompt}
|
|
951
|
+
|
|
952
|
+
${cssContextPrompt}`;
|
|
953
|
+
}
|
|
954
|
+
function buildSelectedElementsPrompt(annotations) {
|
|
955
|
+
const lines = ["Selected elements:"];
|
|
956
|
+
for (const annotation of annotations) {
|
|
957
|
+
const trimmedNote = annotation.note.trim();
|
|
958
|
+
for (const target of annotation.targets) {
|
|
959
|
+
const targetLabel = (target.label || "Unknown target").trim() || "Unknown target";
|
|
960
|
+
lines.push(`- ${targetLabel}`);
|
|
961
|
+
lines.push(`file=${target.file}:${target.line}:${target.column}`);
|
|
962
|
+
if (trimmedNote) {
|
|
963
|
+
lines.push(`note=${trimmedNote}`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
if (lines.length === 1) {
|
|
968
|
+
lines.push("- None");
|
|
969
|
+
}
|
|
970
|
+
return lines.join("\n");
|
|
971
|
+
}
|
|
972
|
+
function appendScreenshotContextSection(prompt, screenshotContext) {
|
|
973
|
+
if (!screenshotContext || !screenshotContext.imageDataUrl && !screenshotContext.imageAssetId) {
|
|
974
|
+
return prompt;
|
|
975
|
+
}
|
|
976
|
+
const lines = [
|
|
977
|
+
"Visual screenshot context attached:",
|
|
978
|
+
`- capturedAt=${screenshotContext.capturedAt}`,
|
|
979
|
+
`- mimeType=${screenshotContext.mimeType}`,
|
|
980
|
+
...screenshotContext.imageAssetId ? [`- imageAssetId=${screenshotContext.imageAssetId}`] : []
|
|
981
|
+
];
|
|
982
|
+
return `${prompt}
|
|
983
|
+
|
|
984
|
+
${lines.join("\n")}`;
|
|
985
|
+
}
|
|
986
|
+
function appendRuntimeContextSection(prompt, runtimeContext) {
|
|
987
|
+
if (!runtimeContext?.records.length) {
|
|
988
|
+
return prompt;
|
|
989
|
+
}
|
|
990
|
+
return `${prompt}
|
|
991
|
+
|
|
992
|
+
${buildRuntimeContextSection(runtimeContext.records)}`;
|
|
993
|
+
}
|
|
994
|
+
function buildRuntimeContextSection(records) {
|
|
995
|
+
return ["Relevant runtime context:", ...records.map(formatRuntimeRecord)].join("\n");
|
|
996
|
+
}
|
|
997
|
+
function formatRuntimeRecord(record) {
|
|
998
|
+
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}`;
|
|
999
|
+
const reasonSummary = record.relevanceReasons.length ? record.relevanceReasons.join("; ") : "timing-based";
|
|
1000
|
+
const stackSummary = record.stack ? `
|
|
1001
|
+
stack=${record.stack.split("\n").slice(0, 5).join(" | ")}` : "";
|
|
1002
|
+
return [
|
|
1003
|
+
`- [${record.kind}] ${record.message}`,
|
|
1004
|
+
` relevance=${record.relevanceLevel} (${reasonSummary})`,
|
|
1005
|
+
` ${requestSummary}`,
|
|
1006
|
+
stackSummary
|
|
1007
|
+
].filter(Boolean).join("\n");
|
|
1008
|
+
}
|
|
1009
|
+
function getAnnotationDispatchErrorCode(error) {
|
|
1010
|
+
if (error instanceof AnnotationDispatchError) return error.errorCode;
|
|
1011
|
+
if (error instanceof Error && error.message.includes("outside of project workspace")) {
|
|
1012
|
+
return "FORBIDDEN_PATH";
|
|
1013
|
+
}
|
|
1014
|
+
return "UNKNOWN";
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// src/server/client-config.ts
|
|
1018
|
+
async function buildClientConfig(serverState2) {
|
|
1019
|
+
const userConfig = loadUserConfigSync(false, serverState2.cwd, serverState2.configRoot);
|
|
1020
|
+
const promptsConfig = await loadPromptsConfig(false, serverState2.cwd, serverState2.configRoot);
|
|
1021
|
+
const effectiveIde = userConfig.ide ?? "vscode";
|
|
1022
|
+
let info;
|
|
1023
|
+
if (!serverState2.ideInfo) {
|
|
1024
|
+
info = { ide: effectiveIde };
|
|
1025
|
+
} else {
|
|
1026
|
+
const { scheme: _scheme, ...rest } = serverState2.ideInfo;
|
|
1027
|
+
info = rest;
|
|
1028
|
+
}
|
|
1029
|
+
return {
|
|
1030
|
+
...info,
|
|
1031
|
+
prompts: resolveIntents(promptsConfig),
|
|
1032
|
+
hotKeys: userConfig["inspector.hotKey"] ?? "alt",
|
|
1033
|
+
theme: userConfig["inspector.theme"] ?? "auto",
|
|
1034
|
+
includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
|
|
1035
|
+
runtimeContext: {
|
|
1036
|
+
enabled: true,
|
|
1037
|
+
preview: true,
|
|
1038
|
+
maxRuntimeErrors: 3,
|
|
1039
|
+
maxFailedRequests: 2
|
|
1040
|
+
},
|
|
1041
|
+
screenshotContext: {
|
|
1042
|
+
enabled: false
|
|
1043
|
+
},
|
|
1044
|
+
annotationResponseMode: userConfig["prompt.annotationResponseMode"] ?? "unified",
|
|
1045
|
+
autoSend: userConfig["prompt.autoSend"] ?? false
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// src/server/open-file.ts
|
|
1050
|
+
var import_node_child_process2 = require("child_process");
|
|
1051
|
+
var import_launch_ide2 = require("launch-ide");
|
|
1052
|
+
var serverLogger2 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
1053
|
+
var VSCODE_FAMILY_SCHEMES = [
|
|
1054
|
+
"vscode",
|
|
1055
|
+
"vscode-insiders",
|
|
1056
|
+
"cursor",
|
|
1057
|
+
"windsurf",
|
|
1058
|
+
"trae",
|
|
1059
|
+
"trae-cn",
|
|
1060
|
+
"vscodium",
|
|
1061
|
+
"codebuddy",
|
|
1062
|
+
"codebuddy-cn",
|
|
1063
|
+
"antigravity"
|
|
1064
|
+
];
|
|
1065
|
+
function handleOpenFileRequest(body, serverState2) {
|
|
1066
|
+
const absolutePath = resolveWorkspacePath(body.file, serverState2.cwd);
|
|
1067
|
+
assertPathWithinProject(absolutePath, serverState2.projectRoot);
|
|
1068
|
+
const userConfig = loadUserConfigSync(false, serverState2.cwd, serverState2.configRoot);
|
|
1069
|
+
const configuredIde = userConfig.ide;
|
|
1070
|
+
const activeIde = serverState2.ideInfo?.ide;
|
|
1071
|
+
const activeIdeScheme = serverState2.ideInfo?.scheme;
|
|
1072
|
+
const rawEditorHint = configuredIde || activeIde || activeIdeScheme || "code";
|
|
1073
|
+
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
1074
|
+
serverLogger2.warn(
|
|
1075
|
+
`Active IDE is ${activeIdeScheme}, but config forces ${configuredIde}. Using configured IDE.`
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
let editorHint = rawEditorHint;
|
|
1079
|
+
if (rawEditorHint === "vscode") editorHint = "code";
|
|
1080
|
+
else if (rawEditorHint === "vscode-insiders") editorHint = "code-insiders";
|
|
1081
|
+
else if (rawEditorHint === "vscodium") editorHint = "codium";
|
|
1082
|
+
else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
|
|
1083
|
+
serverLogger2.debug(
|
|
1084
|
+
`IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
|
|
1085
|
+
);
|
|
1086
|
+
if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
|
|
1087
|
+
let normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
1088
|
+
if (!normalizedPath.startsWith("/")) {
|
|
1089
|
+
normalizedPath = "/" + normalizedPath;
|
|
1090
|
+
}
|
|
1091
|
+
const encodedPath = encodeURI(normalizedPath);
|
|
1092
|
+
const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
|
|
1093
|
+
serverLogger2.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
|
|
1094
|
+
try {
|
|
1095
|
+
if (process.platform === "darwin") {
|
|
1096
|
+
(0, import_node_child_process2.execFileSync)("open", [uri]);
|
|
1097
|
+
} else if (process.platform === "win32") {
|
|
1098
|
+
(0, import_node_child_process2.execFileSync)("cmd", ["/c", "start", '""', uri]);
|
|
1099
|
+
} else {
|
|
1100
|
+
(0, import_node_child_process2.execFileSync)("xdg-open", [uri]);
|
|
1101
|
+
}
|
|
1102
|
+
} catch (e) {
|
|
1103
|
+
serverLogger2.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
|
|
1104
|
+
(0, import_launch_ide2.launchIDE)({
|
|
1105
|
+
file: absolutePath,
|
|
1106
|
+
line: body.line,
|
|
1107
|
+
column: body.column,
|
|
1108
|
+
editor: editorHint,
|
|
1109
|
+
type: process.platform === "darwin" ? "open" : "exec"
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
} else {
|
|
1113
|
+
(0, import_launch_ide2.launchIDE)({
|
|
1114
|
+
file: absolutePath,
|
|
1115
|
+
line: body.line,
|
|
1116
|
+
column: body.column,
|
|
1117
|
+
editor: editorHint,
|
|
1118
|
+
type: process.platform === "darwin" ? "open" : "exec"
|
|
1119
|
+
});
|
|
1120
|
+
}
|
|
1121
|
+
return { success: true };
|
|
1122
|
+
}
|
|
1123
|
+
|
|
1124
|
+
// src/server/project-root.ts
|
|
1125
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
1126
|
+
var import_node_path6 = __toESM(require("path"), 1);
|
|
1127
|
+
var import_node_child_process3 = require("child_process");
|
|
1128
|
+
var serverLogger3 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
1129
|
+
function resolveProjectRoot() {
|
|
1130
|
+
const cwd = process.cwd();
|
|
1131
|
+
let gitRoot;
|
|
1132
|
+
try {
|
|
1133
|
+
gitRoot = (0, import_node_child_process3.execSync)("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
1134
|
+
} catch (e) {
|
|
1135
|
+
serverLogger3.warn("Failed to resolve git root via git rev-parse:", e);
|
|
1136
|
+
gitRoot = cwd;
|
|
1137
|
+
}
|
|
1138
|
+
const visited = /* @__PURE__ */ new Set();
|
|
1139
|
+
const search = (start, stop) => {
|
|
1140
|
+
let current = start;
|
|
1141
|
+
while (!visited.has(current)) {
|
|
1142
|
+
visited.add(current);
|
|
1143
|
+
if (import_node_fs2.default.existsSync(import_node_path6.default.join(current, ".inspecto"))) return current;
|
|
1144
|
+
if (current === stop) break;
|
|
1145
|
+
const parent = import_node_path6.default.dirname(current);
|
|
1146
|
+
if (parent === current) break;
|
|
1147
|
+
current = parent;
|
|
1148
|
+
}
|
|
1149
|
+
return null;
|
|
1150
|
+
};
|
|
1151
|
+
const cwdMatch = search(cwd, import_node_path6.default.parse(cwd).root);
|
|
1152
|
+
if (cwdMatch) return cwdMatch;
|
|
1153
|
+
const repoMatch = search(gitRoot, import_node_path6.default.parse(gitRoot).root);
|
|
1154
|
+
if (repoMatch) return repoMatch;
|
|
1155
|
+
return gitRoot;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
// src/server/index.ts
|
|
1159
|
+
var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
1160
|
+
var serverState = {
|
|
1161
|
+
port: null,
|
|
1162
|
+
running: false,
|
|
1163
|
+
projectRoot: "",
|
|
1164
|
+
configRoot: "",
|
|
1165
|
+
cwd: process.cwd()
|
|
1166
|
+
};
|
|
1167
|
+
var serverInstance = null;
|
|
825
1168
|
async function startServer() {
|
|
826
1169
|
if (serverState.running && serverState.port !== null) {
|
|
827
1170
|
return serverState.port;
|
|
@@ -833,7 +1176,7 @@ async function startServer() {
|
|
|
833
1176
|
const port = await import_portfinder.default.getPortPromise();
|
|
834
1177
|
watchConfig(
|
|
835
1178
|
() => {
|
|
836
|
-
|
|
1179
|
+
serverLogger4.info("user config reloaded.");
|
|
837
1180
|
},
|
|
838
1181
|
serverState.cwd,
|
|
839
1182
|
serverState.configRoot
|
|
@@ -849,7 +1192,7 @@ async function startServer() {
|
|
|
849
1192
|
}
|
|
850
1193
|
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
851
1194
|
handleRequest(url, req, res).catch((err) => {
|
|
852
|
-
|
|
1195
|
+
serverLogger4.error("server error:", err);
|
|
853
1196
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
854
1197
|
res.end(JSON.stringify({ success: false, error: String(err) }));
|
|
855
1198
|
});
|
|
@@ -862,41 +1205,41 @@ async function startServer() {
|
|
|
862
1205
|
serverInstance.once("error", reject);
|
|
863
1206
|
});
|
|
864
1207
|
serverInstance.on("error", (err) => {
|
|
865
|
-
|
|
1208
|
+
serverLogger4.error("persistent server error:", err);
|
|
866
1209
|
});
|
|
867
1210
|
serverState.port = port;
|
|
868
1211
|
serverState.running = true;
|
|
869
|
-
const portFile =
|
|
1212
|
+
const portFile = import_node_path7.default.join(import_node_os2.default.tmpdir(), "inspecto.port.json");
|
|
870
1213
|
try {
|
|
871
1214
|
let portData = {};
|
|
872
|
-
if (
|
|
1215
|
+
if (import_node_fs3.default.existsSync(portFile)) {
|
|
873
1216
|
try {
|
|
874
|
-
portData = JSON.parse(
|
|
1217
|
+
portData = JSON.parse(import_node_fs3.default.readFileSync(portFile, "utf-8"));
|
|
875
1218
|
} catch (e) {
|
|
876
1219
|
}
|
|
877
1220
|
}
|
|
878
|
-
const rootHash =
|
|
1221
|
+
const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
879
1222
|
portData[rootHash] = port;
|
|
880
|
-
|
|
1223
|
+
import_node_fs3.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
881
1224
|
} catch (e) {
|
|
882
|
-
|
|
1225
|
+
serverLogger4.warn("Failed to write port file:", e);
|
|
883
1226
|
}
|
|
884
1227
|
process.once("exit", () => {
|
|
885
1228
|
try {
|
|
886
|
-
if (
|
|
887
|
-
const portData = JSON.parse(
|
|
888
|
-
const rootHash =
|
|
1229
|
+
if (import_node_fs3.default.existsSync(portFile)) {
|
|
1230
|
+
const portData = JSON.parse(import_node_fs3.default.readFileSync(portFile, "utf-8"));
|
|
1231
|
+
const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
889
1232
|
delete portData[rootHash];
|
|
890
1233
|
if (Object.keys(portData).length === 0) {
|
|
891
|
-
|
|
1234
|
+
import_node_fs3.default.unlinkSync(portFile);
|
|
892
1235
|
} else {
|
|
893
|
-
|
|
1236
|
+
import_node_fs3.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
894
1237
|
}
|
|
895
1238
|
}
|
|
896
1239
|
} catch {
|
|
897
1240
|
}
|
|
898
1241
|
});
|
|
899
|
-
|
|
1242
|
+
serverLogger4.info(`server running at http://127.0.0.1:${port}`);
|
|
900
1243
|
return port;
|
|
901
1244
|
}
|
|
902
1245
|
async function readBody(req) {
|
|
@@ -915,26 +1258,7 @@ async function handleRequest(url, req, res) {
|
|
|
915
1258
|
return;
|
|
916
1259
|
}
|
|
917
1260
|
if (pathname === import_types2.INSPECTO_API_PATHS.CLIENT_CONFIG && req.method === "GET") {
|
|
918
|
-
const
|
|
919
|
-
const promptsConfig = await loadPromptsConfig(false, serverState.cwd, serverState.configRoot);
|
|
920
|
-
const effectiveIde = userConfig.ide ?? "vscode";
|
|
921
|
-
let info;
|
|
922
|
-
if (!serverState.ideInfo) {
|
|
923
|
-
info = {
|
|
924
|
-
ide: effectiveIde
|
|
925
|
-
};
|
|
926
|
-
} else {
|
|
927
|
-
const { scheme: _scheme, ...rest } = serverState.ideInfo;
|
|
928
|
-
info = rest;
|
|
929
|
-
}
|
|
930
|
-
const config = {
|
|
931
|
-
...info,
|
|
932
|
-
prompts: resolveIntents(promptsConfig),
|
|
933
|
-
hotKeys: userConfig["inspector.hotKey"] ?? "alt",
|
|
934
|
-
theme: userConfig["inspector.theme"] ?? "auto",
|
|
935
|
-
includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
|
|
936
|
-
autoSend: userConfig["prompt.autoSend"] ?? false
|
|
937
|
-
};
|
|
1261
|
+
const config = await buildClientConfig(serverState);
|
|
938
1262
|
delete config.providers;
|
|
939
1263
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
940
1264
|
res.end(JSON.stringify(config));
|
|
@@ -945,21 +1269,23 @@ async function handleRequest(url, req, res) {
|
|
|
945
1269
|
const body = JSON.parse(await readBody(req));
|
|
946
1270
|
const ideWorkspace = body.workspaceRoot || "";
|
|
947
1271
|
const serverProjectRoot = serverState.projectRoot || "";
|
|
948
|
-
const
|
|
1272
|
+
const normalizedIdeRoot = ideWorkspace ? import_node_path7.default.resolve(ideWorkspace) : "";
|
|
1273
|
+
const normalizedServerRoot = serverProjectRoot ? import_node_path7.default.resolve(serverProjectRoot) : "";
|
|
1274
|
+
const isSameProject = !normalizedIdeRoot || !normalizedServerRoot || normalizedIdeRoot === normalizedServerRoot || normalizedServerRoot.startsWith(normalizedIdeRoot + import_node_path7.default.sep) || normalizedIdeRoot.startsWith(normalizedServerRoot + import_node_path7.default.sep);
|
|
949
1275
|
if (isSameProject) {
|
|
950
1276
|
serverState.ideInfo = body;
|
|
951
|
-
|
|
1277
|
+
serverLogger4.debug(
|
|
952
1278
|
`Accepted IDE info from matched workspace (ide-${body.ide} / schema-${body.scheme})`
|
|
953
1279
|
);
|
|
954
1280
|
} else {
|
|
955
|
-
|
|
1281
|
+
serverLogger4.debug(
|
|
956
1282
|
`Ignored IDE info from unrelated workspace (IDE Workspace: ${ideWorkspace}, Server: ${serverProjectRoot}, Scheme: ${body.scheme}, IDE: ${body.ide})`
|
|
957
1283
|
);
|
|
958
1284
|
}
|
|
959
1285
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
960
1286
|
res.end(JSON.stringify({ success: true }));
|
|
961
1287
|
} catch (e) {
|
|
962
|
-
|
|
1288
|
+
serverLogger4.error(`Error parsing ${import_types2.INSPECTO_API_PATHS.IDE_INFO} POST request:`, e);
|
|
963
1289
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
964
1290
|
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
965
1291
|
}
|
|
@@ -974,74 +1300,14 @@ async function handleRequest(url, req, res) {
|
|
|
974
1300
|
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
975
1301
|
return;
|
|
976
1302
|
}
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1303
|
+
try {
|
|
1304
|
+
handleOpenFileRequest(body, serverState);
|
|
1305
|
+
} catch {
|
|
1306
|
+
serverLogger4.warn(`Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}`);
|
|
981
1307
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
982
1308
|
res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
|
|
983
1309
|
return;
|
|
984
1310
|
}
|
|
985
|
-
const userConfig = loadUserConfigSync(false, serverState.cwd, serverState.configRoot);
|
|
986
|
-
const configuredIde = userConfig.ide;
|
|
987
|
-
const activeIde = serverState.ideInfo?.ide;
|
|
988
|
-
const activeIdeScheme = serverState.ideInfo?.scheme;
|
|
989
|
-
const rawEditorHint = configuredIde || activeIde || activeIdeScheme || "code";
|
|
990
|
-
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
991
|
-
serverLogger.warn(
|
|
992
|
-
`Active IDE is ${activeIdeScheme}, but config forces ${configuredIde}. Using configured IDE.`
|
|
993
|
-
);
|
|
994
|
-
}
|
|
995
|
-
let editorHint = rawEditorHint;
|
|
996
|
-
if (rawEditorHint === "vscode") editorHint = "code";
|
|
997
|
-
else if (rawEditorHint === "vscode-insiders") editorHint = "code-insiders";
|
|
998
|
-
else if (rawEditorHint === "vscodium") editorHint = "codium";
|
|
999
|
-
else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
|
|
1000
|
-
serverLogger.debug(
|
|
1001
|
-
`IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
|
|
1002
|
-
);
|
|
1003
|
-
const VSCODE_FAMILY_SCHEMES = [
|
|
1004
|
-
"vscode",
|
|
1005
|
-
"vscode-insiders",
|
|
1006
|
-
"cursor",
|
|
1007
|
-
"windsurf",
|
|
1008
|
-
"trae",
|
|
1009
|
-
"trae-cn",
|
|
1010
|
-
"vscodium",
|
|
1011
|
-
"codebuddy",
|
|
1012
|
-
"codebuddy-cn",
|
|
1013
|
-
"antigravity"
|
|
1014
|
-
];
|
|
1015
|
-
if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
|
|
1016
|
-
const uri = `${rawEditorHint}://file${absolutePath}:${body.line}:${body.column}`;
|
|
1017
|
-
serverLogger.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
|
|
1018
|
-
try {
|
|
1019
|
-
if (process.platform === "darwin") {
|
|
1020
|
-
(0, import_node_child_process.execFileSync)("open", [uri]);
|
|
1021
|
-
} else if (process.platform === "win32") {
|
|
1022
|
-
(0, import_node_child_process.execFileSync)("cmd", ["/c", "start", '""', uri]);
|
|
1023
|
-
} else {
|
|
1024
|
-
(0, import_node_child_process.execFileSync)("xdg-open", [uri]);
|
|
1025
|
-
}
|
|
1026
|
-
} catch (e) {
|
|
1027
|
-
serverLogger.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
|
|
1028
|
-
(0, import_launch_ide.launchIDE)({
|
|
1029
|
-
file: absolutePath,
|
|
1030
|
-
line: body.line,
|
|
1031
|
-
column: body.column,
|
|
1032
|
-
editor: editorHint,
|
|
1033
|
-
type: process.platform === "darwin" ? "open" : "exec"
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
} else {
|
|
1037
|
-
(0, import_launch_ide.launchIDE)({
|
|
1038
|
-
file: absolutePath,
|
|
1039
|
-
line: body.line,
|
|
1040
|
-
column: body.column,
|
|
1041
|
-
editor: editorHint,
|
|
1042
|
-
type: process.platform === "darwin" ? "open" : "exec"
|
|
1043
|
-
});
|
|
1044
|
-
}
|
|
1045
1311
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1046
1312
|
res.end(JSON.stringify({ success: true }));
|
|
1047
1313
|
return;
|
|
@@ -1052,10 +1318,11 @@ async function handleRequest(url, req, res) {
|
|
|
1052
1318
|
const column = parseInt(url.searchParams.get("column") ?? "1", 10);
|
|
1053
1319
|
const maxLines = parseInt(url.searchParams.get("maxLines") ?? "100", 10);
|
|
1054
1320
|
try {
|
|
1055
|
-
const absolutePath =
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1321
|
+
const absolutePath = resolveWorkspacePath(file, serverState.cwd);
|
|
1322
|
+
try {
|
|
1323
|
+
assertPathWithinProject(absolutePath, serverState.projectRoot);
|
|
1324
|
+
} catch {
|
|
1325
|
+
serverLogger4.warn(`Security: Blocked path traversal attempt in PROJECT_SNIPPET: ${file}`);
|
|
1059
1326
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
1060
1327
|
res.end(
|
|
1061
1328
|
JSON.stringify({
|
|
@@ -1085,7 +1352,23 @@ async function handleRequest(url, req, res) {
|
|
|
1085
1352
|
res.writeHead(result.success ? 200 : 500, { "Content-Type": "application/json" });
|
|
1086
1353
|
res.end(JSON.stringify(result));
|
|
1087
1354
|
} catch (e) {
|
|
1088
|
-
|
|
1355
|
+
serverLogger4.error(`Error parsing ${import_types2.INSPECTO_API_PATHS.AI_DISPATCH} request:`, e);
|
|
1356
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1357
|
+
res.end(JSON.stringify({ success: false, error: String(e), errorCode: "INTERNAL_ERROR" }));
|
|
1358
|
+
}
|
|
1359
|
+
return;
|
|
1360
|
+
}
|
|
1361
|
+
if (pathname === import_types2.INSPECTO_API_PATHS.AI_BATCH_DISPATCH && req.method === "POST") {
|
|
1362
|
+
try {
|
|
1363
|
+
const rawBody = await readBody(req);
|
|
1364
|
+
const body = JSON.parse(rawBody);
|
|
1365
|
+
const result = await dispatchAnnotationsToAi(body, serverState);
|
|
1366
|
+
res.writeHead(getBatchDispatchStatusCode(result.errorCode, result.success), {
|
|
1367
|
+
"Content-Type": "application/json"
|
|
1368
|
+
});
|
|
1369
|
+
res.end(JSON.stringify(result));
|
|
1370
|
+
} catch (e) {
|
|
1371
|
+
serverLogger4.error(`Error parsing ${import_types2.INSPECTO_API_PATHS.AI_BATCH_DISPATCH} request:`, e);
|
|
1089
1372
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1090
1373
|
res.end(JSON.stringify({ success: false, error: String(e), errorCode: "INTERNAL_ERROR" }));
|
|
1091
1374
|
}
|
|
@@ -1093,7 +1376,7 @@ async function handleRequest(url, req, res) {
|
|
|
1093
1376
|
}
|
|
1094
1377
|
if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
|
|
1095
1378
|
const ticketId = pathname.substring(import_types2.INSPECTO_API_PATHS.AI_TICKET.length + 1);
|
|
1096
|
-
const payloadStr =
|
|
1379
|
+
const payloadStr = readTicket(ticketId);
|
|
1097
1380
|
if (!payloadStr) {
|
|
1098
1381
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1099
1382
|
res.end(JSON.stringify({ success: false, error: "Ticket not found or expired" }));
|
|
@@ -1107,54 +1390,28 @@ async function handleRequest(url, req, res) {
|
|
|
1107
1390
|
res.end(JSON.stringify({ error: "not found" }));
|
|
1108
1391
|
}
|
|
1109
1392
|
async function dispatchToAi(req) {
|
|
1110
|
-
const { location, snippet, prompt } = req;
|
|
1111
|
-
const userConfig = loadUserConfigSync(false, serverState.cwd, serverState.configRoot);
|
|
1112
|
-
const resolvedTarget = resolveTargetTool(userConfig);
|
|
1393
|
+
const { location, snippet, prompt, screenshotContext } = req;
|
|
1113
1394
|
const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
|
|
1114
1395
|
|
|
1115
1396
|
\`\`\`
|
|
1116
1397
|
${snippet}
|
|
1117
1398
|
\`\`\`
|
|
1118
1399
|
`;
|
|
1119
|
-
const
|
|
1120
|
-
|
|
1121
|
-
const activeIde = serverState.ideInfo?.ide;
|
|
1122
|
-
const activeIdeScheme = serverState.ideInfo?.scheme;
|
|
1123
|
-
const finalIde = configuredIde || activeIdeScheme || activeIde || "vscode";
|
|
1124
|
-
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
1125
|
-
serverLogger.warn(
|
|
1126
|
-
`dispatchToAi: Active IDE is ${activeIdeScheme}, but config forces ${configuredIde}. Using configured IDE.`
|
|
1127
|
-
);
|
|
1128
|
-
}
|
|
1129
|
-
const mode = resolveProviderMode(resolvedTarget, finalIde, userConfig);
|
|
1130
|
-
const overrides = extractToolOverrides(finalIde, userConfig)[resolvedTarget] || {};
|
|
1131
|
-
overrides.type = mode;
|
|
1132
|
-
const fullPayload = {
|
|
1133
|
-
ide: finalIde,
|
|
1134
|
-
target: resolvedTarget,
|
|
1135
|
-
targetType: mode,
|
|
1400
|
+
const runtime = resolvePromptDispatchRuntime(serverState);
|
|
1401
|
+
return dispatchPromptThroughIde(runtime, {
|
|
1136
1402
|
prompt: formattedPrompt,
|
|
1137
1403
|
filePath: location.file,
|
|
1138
1404
|
line: location.line,
|
|
1139
1405
|
column: location.column,
|
|
1140
1406
|
snippet,
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
serverLogger.debug(`dispatchToAi: Generated URI: ${uri}`);
|
|
1150
|
-
launchURI(uri);
|
|
1151
|
-
return {
|
|
1152
|
-
success: true,
|
|
1153
|
-
fallbackPayload: {
|
|
1154
|
-
prompt: formattedPrompt,
|
|
1155
|
-
file: location.file
|
|
1156
|
-
}
|
|
1157
|
-
};
|
|
1407
|
+
...screenshotContext ? { screenshotContext } : {}
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
function getBatchDispatchStatusCode(errorCode, success) {
|
|
1411
|
+
if (success) return 200;
|
|
1412
|
+
if (errorCode === "INVALID_REQUEST") return 400;
|
|
1413
|
+
if (errorCode === "FORBIDDEN_PATH") return 403;
|
|
1414
|
+
return 500;
|
|
1158
1415
|
}
|
|
1159
1416
|
|
|
1160
1417
|
// src/injectors/utils.ts
|