@inspecto-dev/plugin 0.2.0-alpha.4 → 0.3.0
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 +444 -203
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +443 -202
- package/dist/index.js.map +1 -1
- package/dist/legacy/rspack/index.cjs +446 -205
- package/dist/legacy/rspack/index.cjs.map +1 -1
- package/dist/legacy/rspack/index.js +445 -204
- package/dist/legacy/rspack/index.js.map +1 -1
- package/dist/legacy/webpack4/index.cjs +446 -205
- package/dist/legacy/webpack4/index.cjs.map +1 -1
- package/dist/legacy/webpack4/index.js +445 -204
- package/dist/legacy/webpack4/index.js.map +1 -1
- package/dist/rollup.cjs +444 -203
- package/dist/rollup.cjs.map +1 -1
- package/dist/rollup.js +443 -202
- package/dist/rollup.js.map +1 -1
- package/dist/rspack.cjs +444 -203
- package/dist/rspack.cjs.map +1 -1
- package/dist/rspack.js +443 -202
- package/dist/rspack.js.map +1 -1
- package/dist/vite.cjs +444 -203
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +443 -202
- package/dist/vite.js.map +1 -1
- package/dist/webpack.cjs +444 -203
- package/dist/webpack.cjs.map +1 -1
- package/dist/webpack.js +443 -202
- package/dist/webpack.js.map +1 -1
- package/package.json +2 -2
package/dist/vite.js
CHANGED
|
@@ -269,13 +269,11 @@ function transformRouter(options) {
|
|
|
269
269
|
|
|
270
270
|
// src/server/index.ts
|
|
271
271
|
import http from "http";
|
|
272
|
-
import
|
|
273
|
-
import
|
|
272
|
+
import fs4 from "fs";
|
|
273
|
+
import path8 from "path";
|
|
274
274
|
import os2 from "os";
|
|
275
|
-
import
|
|
276
|
-
import { execSync, execFileSync } from "child_process";
|
|
275
|
+
import crypto2 from "crypto";
|
|
277
276
|
import portfinder from "portfinder";
|
|
278
|
-
import { launchIDE } from "launch-ide";
|
|
279
277
|
import { INSPECTO_API_PATHS } from "@inspecto-dev/types";
|
|
280
278
|
|
|
281
279
|
// src/server/snippet.ts
|
|
@@ -613,9 +611,9 @@ function extractToolOverrides(ide, config) {
|
|
|
613
611
|
function resolveIntents(serverPrompts) {
|
|
614
612
|
const baseMap = /* @__PURE__ */ new Map();
|
|
615
613
|
for (const intent of DEFAULT_INTENTS) {
|
|
616
|
-
|
|
614
|
+
baseMap.set(intent.id, { ...intent });
|
|
617
615
|
}
|
|
618
|
-
const defaults = () =>
|
|
616
|
+
const defaults = () => Array.from(baseMap.values());
|
|
619
617
|
if (!serverPrompts) return defaults();
|
|
620
618
|
const isReplace = !Array.isArray(serverPrompts) && typeof serverPrompts === "object" && serverPrompts.$replace === true;
|
|
621
619
|
const promptsArray = Array.isArray(serverPrompts) ? serverPrompts : isReplace ? serverPrompts.items : [];
|
|
@@ -642,16 +640,18 @@ function resolveIntents(serverPrompts) {
|
|
|
642
640
|
);
|
|
643
641
|
continue;
|
|
644
642
|
}
|
|
645
|
-
if (item.
|
|
643
|
+
if (!item.aiIntent) {
|
|
646
644
|
configLogger.warn(
|
|
647
|
-
`
|
|
645
|
+
`Intent "${item.id}" is missing required "aiIntent".`
|
|
648
646
|
);
|
|
649
647
|
continue;
|
|
650
648
|
}
|
|
651
|
-
result.push(
|
|
649
|
+
result.push(
|
|
650
|
+
baseMap.has(item.id) ? { ...baseMap.get(item.id), ...item } : item
|
|
651
|
+
);
|
|
652
652
|
}
|
|
653
653
|
}
|
|
654
|
-
return
|
|
654
|
+
return result;
|
|
655
655
|
}
|
|
656
656
|
const merged = Array.from(baseMap.values());
|
|
657
657
|
for (const item of promptsArray) {
|
|
@@ -668,9 +668,9 @@ function resolveIntents(serverPrompts) {
|
|
|
668
668
|
configLogger.warn('Intent object missing required "id" field, skipping.');
|
|
669
669
|
continue;
|
|
670
670
|
}
|
|
671
|
-
if (item.
|
|
671
|
+
if (!item.aiIntent) {
|
|
672
672
|
configLogger.warn(
|
|
673
|
-
`
|
|
673
|
+
`Intent "${item.id}" is missing required "aiIntent".`
|
|
674
674
|
);
|
|
675
675
|
continue;
|
|
676
676
|
}
|
|
@@ -688,15 +688,7 @@ function resolveIntents(serverPrompts) {
|
|
|
688
688
|
}
|
|
689
689
|
}
|
|
690
690
|
}
|
|
691
|
-
return
|
|
692
|
-
}
|
|
693
|
-
function ensureOpenInEditorLast(intents) {
|
|
694
|
-
const idx = intents.findIndex((i) => i.id === "open-in-editor");
|
|
695
|
-
if (idx === -1 || idx === intents.length - 1) return intents;
|
|
696
|
-
const result = [...intents];
|
|
697
|
-
const item = result.splice(idx, 1)[0];
|
|
698
|
-
result.push(item);
|
|
699
|
-
return result;
|
|
691
|
+
return merged;
|
|
700
692
|
}
|
|
701
693
|
var watchers = [];
|
|
702
694
|
function watchConfig(onReload, cwd = process.cwd(), gitRoot) {
|
|
@@ -731,7 +723,10 @@ function watchConfig(onReload, cwd = process.cwd(), gitRoot) {
|
|
|
731
723
|
}
|
|
732
724
|
}
|
|
733
725
|
|
|
734
|
-
// src/server/
|
|
726
|
+
// src/server/dispatch-transport.ts
|
|
727
|
+
import crypto from "crypto";
|
|
728
|
+
import { execFileSync } from "child_process";
|
|
729
|
+
import { launchIDE } from "launch-ide";
|
|
735
730
|
var serverLogger = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
736
731
|
var payloadTickets = /* @__PURE__ */ new Map();
|
|
737
732
|
function createTicket(payload) {
|
|
@@ -745,21 +740,363 @@ function createTicket(payload) {
|
|
|
745
740
|
);
|
|
746
741
|
return ticketId;
|
|
747
742
|
}
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
743
|
+
function readTicket(ticketId) {
|
|
744
|
+
return payloadTickets.get(ticketId);
|
|
745
|
+
}
|
|
746
|
+
function launchURI(uri) {
|
|
747
|
+
try {
|
|
748
|
+
if (process.platform === "darwin") {
|
|
749
|
+
execFileSync("open", [uri]);
|
|
750
|
+
} else if (process.platform === "win32") {
|
|
751
|
+
execFileSync("cmd", ["/c", "start", '""', uri]);
|
|
752
|
+
} else {
|
|
753
|
+
execFileSync("xdg-open", [uri]);
|
|
754
|
+
}
|
|
755
|
+
} catch (e) {
|
|
756
|
+
serverLogger.error("Failed to launch URI via execFileSync, falling back to launchIDE:", e);
|
|
757
|
+
launchIDE({ file: uri });
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// src/server/dispatch-runtime.ts
|
|
762
|
+
function resolvePromptDispatchRuntime(state) {
|
|
763
|
+
const userConfig = loadUserConfigSync(false, state.cwd, state.projectRoot);
|
|
764
|
+
const resolvedTarget = resolveTargetTool(userConfig);
|
|
765
|
+
const finalIde = resolveFinalIde(userConfig.ide, state.ideInfo?.ide, state.ideInfo?.scheme);
|
|
766
|
+
const mode = resolveProviderMode(resolvedTarget, finalIde, userConfig);
|
|
767
|
+
const overrides = extractToolOverrides(finalIde, userConfig)[resolvedTarget] || void 0;
|
|
768
|
+
return {
|
|
769
|
+
resolvedTarget,
|
|
770
|
+
finalIde,
|
|
771
|
+
mode,
|
|
772
|
+
...hasOverrides(overrides) ? { overrides } : {},
|
|
773
|
+
...userConfig["prompt.autoSend"] !== void 0 ? { autoSend: Boolean(userConfig["prompt.autoSend"]) } : {}
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
function dispatchPromptThroughIde(runtime, payload) {
|
|
777
|
+
const ticketId = createTicket({
|
|
778
|
+
ide: runtime.finalIde,
|
|
779
|
+
target: runtime.resolvedTarget,
|
|
780
|
+
targetType: runtime.mode,
|
|
781
|
+
prompt: payload.prompt,
|
|
782
|
+
filePath: payload.filePath,
|
|
783
|
+
line: payload.line,
|
|
784
|
+
column: payload.column,
|
|
785
|
+
snippet: payload.snippet,
|
|
786
|
+
...payload.screenshotContext ? { screenshotContext: payload.screenshotContext } : {},
|
|
787
|
+
overrides: runtime.overrides,
|
|
788
|
+
autoSend: runtime.autoSend
|
|
789
|
+
});
|
|
790
|
+
const params = new URLSearchParams();
|
|
791
|
+
params.set("ticket", ticketId);
|
|
792
|
+
params.set("target", runtime.resolvedTarget);
|
|
793
|
+
launchURI(`${runtime.finalIde}://inspecto.inspecto/send?${params.toString()}`);
|
|
794
|
+
return {
|
|
795
|
+
success: true,
|
|
796
|
+
fallbackPayload: {
|
|
797
|
+
prompt: payload.prompt,
|
|
798
|
+
...payload.filePath ? { file: payload.filePath } : {}
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
function resolveFinalIde(configuredIde, activeIde, activeIdeScheme) {
|
|
803
|
+
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
804
|
+
return configuredIde;
|
|
805
|
+
}
|
|
806
|
+
return configuredIde || activeIdeScheme || activeIde || "vscode";
|
|
807
|
+
}
|
|
808
|
+
function hasOverrides(overrides) {
|
|
809
|
+
return Boolean(overrides && Object.keys(overrides).length > 0);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
// src/server/path-guards.ts
|
|
813
|
+
import path6 from "path";
|
|
814
|
+
function isWindowsAbsolutePath(file) {
|
|
815
|
+
return /^[a-zA-Z]:[\\/]/.test(file) || /^\\\\[^\\]+\\[^\\]+/.test(file);
|
|
816
|
+
}
|
|
817
|
+
function resolveWorkspacePath(file, cwd) {
|
|
818
|
+
if (isWindowsAbsolutePath(file)) {
|
|
819
|
+
return path6.win32.normalize(file);
|
|
820
|
+
}
|
|
821
|
+
return path6.isAbsolute(file) ? path6.resolve(file) : path6.resolve(cwd, file);
|
|
822
|
+
}
|
|
823
|
+
function assertPathWithinProject(file, projectRoot) {
|
|
824
|
+
const relativeToRoot = isWindowsAbsolutePath(file) || isWindowsAbsolutePath(projectRoot) ? path6.win32.relative(path6.win32.normalize(projectRoot), path6.win32.normalize(file)) : path6.relative(projectRoot, file);
|
|
825
|
+
if (relativeToRoot.startsWith("..") || path6.isAbsolute(relativeToRoot)) {
|
|
826
|
+
throw new Error("Access denied: File is outside of project workspace");
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// src/server/annotation-dispatch.ts
|
|
831
|
+
var AnnotationDispatchError = class extends Error {
|
|
832
|
+
constructor(message, errorCode) {
|
|
833
|
+
super(message);
|
|
834
|
+
this.name = "AnnotationDispatchError";
|
|
835
|
+
this.errorCode = errorCode;
|
|
836
|
+
}
|
|
754
837
|
};
|
|
755
|
-
|
|
838
|
+
async function dispatchAnnotationsToAi(req, state) {
|
|
839
|
+
try {
|
|
840
|
+
validateAnnotationDispatchRequest(req, state);
|
|
841
|
+
const batch = normalizeAnnotationBatch(req);
|
|
842
|
+
const prompt = buildAnnotationBatchPrompt(batch);
|
|
843
|
+
const representativeTarget = batch.annotations[0]?.targets[0];
|
|
844
|
+
const runtime = resolvePromptDispatchRuntime(state);
|
|
845
|
+
return dispatchPromptThroughIde(runtime, {
|
|
846
|
+
prompt,
|
|
847
|
+
...representativeTarget?.file ? { filePath: representativeTarget.file } : {},
|
|
848
|
+
...representativeTarget?.line ? { line: representativeTarget.line } : {},
|
|
849
|
+
...representativeTarget?.column ? { column: representativeTarget.column } : {},
|
|
850
|
+
...batch.screenshotContext ? { screenshotContext: batch.screenshotContext } : {}
|
|
851
|
+
});
|
|
852
|
+
} catch (error) {
|
|
853
|
+
return {
|
|
854
|
+
success: false,
|
|
855
|
+
error: error instanceof Error ? error.message : String(error),
|
|
856
|
+
errorCode: getAnnotationDispatchErrorCode(error)
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
function validateAnnotationDispatchRequest(req, state) {
|
|
861
|
+
if (!req.annotations.length) {
|
|
862
|
+
throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
|
|
863
|
+
}
|
|
864
|
+
for (const annotation of req.annotations) {
|
|
865
|
+
if (!annotation.targets.length) {
|
|
866
|
+
throw new AnnotationDispatchError(
|
|
867
|
+
"Each annotation must include at least one target.",
|
|
868
|
+
"INVALID_REQUEST"
|
|
869
|
+
);
|
|
870
|
+
}
|
|
871
|
+
for (const target of annotation.targets) {
|
|
872
|
+
const absolutePath = resolveWorkspacePath(target.location.file, state.cwd);
|
|
873
|
+
assertPathWithinProject(absolutePath, state.projectRoot);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
function normalizeAnnotationBatch(req) {
|
|
878
|
+
return {
|
|
879
|
+
instruction: req.instruction?.trim() ?? "",
|
|
880
|
+
responseMode: req.responseMode ?? "unified",
|
|
881
|
+
...req.runtimeContext ? { runtimeContext: req.runtimeContext } : {},
|
|
882
|
+
...req.screenshotContext ? { screenshotContext: req.screenshotContext } : {},
|
|
883
|
+
...req.cssContextPrompt?.trim() ? { cssContextPrompt: req.cssContextPrompt.trim() } : {},
|
|
884
|
+
annotations: req.annotations.map((annotation, index) => ({
|
|
885
|
+
index: index + 1,
|
|
886
|
+
note: annotation.note.trim(),
|
|
887
|
+
intent: annotation.intent,
|
|
888
|
+
targets: annotation.targets.map((target) => ({
|
|
889
|
+
file: target.location.file,
|
|
890
|
+
line: target.location.line,
|
|
891
|
+
column: target.location.column,
|
|
892
|
+
...target.label ? { label: target.label } : {},
|
|
893
|
+
...target.selector ? { selector: target.selector } : {},
|
|
894
|
+
...target.snippet ? { snippet: target.snippet } : {}
|
|
895
|
+
}))
|
|
896
|
+
}))
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function buildAnnotationBatchPrompt(batch) {
|
|
900
|
+
const body = buildSelectedElementsPrompt(batch.annotations);
|
|
901
|
+
const prompt = batch.instruction ? `${batch.instruction}
|
|
902
|
+
|
|
903
|
+
${body}` : body;
|
|
904
|
+
return appendScreenshotContextSection(
|
|
905
|
+
appendCssContextSection(
|
|
906
|
+
appendRuntimeContextSection(prompt, batch.runtimeContext),
|
|
907
|
+
batch.cssContextPrompt
|
|
908
|
+
),
|
|
909
|
+
batch.screenshotContext
|
|
910
|
+
);
|
|
911
|
+
}
|
|
912
|
+
function appendCssContextSection(prompt, cssContextPrompt) {
|
|
913
|
+
if (!cssContextPrompt) return prompt;
|
|
914
|
+
return `${prompt}
|
|
915
|
+
|
|
916
|
+
${cssContextPrompt}`;
|
|
917
|
+
}
|
|
918
|
+
function buildSelectedElementsPrompt(annotations) {
|
|
919
|
+
const lines = ["Selected elements:"];
|
|
920
|
+
for (const annotation of annotations) {
|
|
921
|
+
const trimmedNote = annotation.note.trim();
|
|
922
|
+
for (const target of annotation.targets) {
|
|
923
|
+
const targetLabel = (target.label || "Unknown target").trim() || "Unknown target";
|
|
924
|
+
lines.push(`- ${targetLabel}`);
|
|
925
|
+
lines.push(`file=${target.file}:${target.line}:${target.column}`);
|
|
926
|
+
if (trimmedNote) {
|
|
927
|
+
lines.push(`note=${trimmedNote}`);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
if (lines.length === 1) {
|
|
932
|
+
lines.push("- None");
|
|
933
|
+
}
|
|
934
|
+
return lines.join("\n");
|
|
935
|
+
}
|
|
936
|
+
function appendScreenshotContextSection(prompt, screenshotContext) {
|
|
937
|
+
if (!screenshotContext || !screenshotContext.imageDataUrl && !screenshotContext.imageAssetId) {
|
|
938
|
+
return prompt;
|
|
939
|
+
}
|
|
940
|
+
const lines = [
|
|
941
|
+
"Visual screenshot context attached:",
|
|
942
|
+
`- capturedAt=${screenshotContext.capturedAt}`,
|
|
943
|
+
`- mimeType=${screenshotContext.mimeType}`,
|
|
944
|
+
...screenshotContext.imageAssetId ? [`- imageAssetId=${screenshotContext.imageAssetId}`] : []
|
|
945
|
+
];
|
|
946
|
+
return `${prompt}
|
|
947
|
+
|
|
948
|
+
${lines.join("\n")}`;
|
|
949
|
+
}
|
|
950
|
+
function appendRuntimeContextSection(prompt, runtimeContext) {
|
|
951
|
+
if (!runtimeContext?.records.length) {
|
|
952
|
+
return prompt;
|
|
953
|
+
}
|
|
954
|
+
return `${prompt}
|
|
955
|
+
|
|
956
|
+
${buildRuntimeContextSection(runtimeContext.records)}`;
|
|
957
|
+
}
|
|
958
|
+
function buildRuntimeContextSection(records) {
|
|
959
|
+
return ["Relevant runtime context:", ...records.map(formatRuntimeRecord)].join("\n");
|
|
960
|
+
}
|
|
961
|
+
function formatRuntimeRecord(record) {
|
|
962
|
+
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}`;
|
|
963
|
+
const reasonSummary = record.relevanceReasons.length ? record.relevanceReasons.join("; ") : "timing-based";
|
|
964
|
+
const stackSummary = record.stack ? `
|
|
965
|
+
stack=${record.stack.split("\n").slice(0, 5).join(" | ")}` : "";
|
|
966
|
+
return [
|
|
967
|
+
`- [${record.kind}] ${record.message}`,
|
|
968
|
+
` relevance=${record.relevanceLevel} (${reasonSummary})`,
|
|
969
|
+
` ${requestSummary}`,
|
|
970
|
+
stackSummary
|
|
971
|
+
].filter(Boolean).join("\n");
|
|
972
|
+
}
|
|
973
|
+
function getAnnotationDispatchErrorCode(error) {
|
|
974
|
+
if (error instanceof AnnotationDispatchError) return error.errorCode;
|
|
975
|
+
if (error instanceof Error && error.message.includes("outside of project workspace")) {
|
|
976
|
+
return "FORBIDDEN_PATH";
|
|
977
|
+
}
|
|
978
|
+
return "UNKNOWN";
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// src/server/client-config.ts
|
|
982
|
+
async function buildClientConfig(serverState2) {
|
|
983
|
+
const userConfig = loadUserConfigSync(false, serverState2.cwd, serverState2.configRoot);
|
|
984
|
+
const promptsConfig = await loadPromptsConfig(false, serverState2.cwd, serverState2.configRoot);
|
|
985
|
+
const effectiveIde = userConfig.ide ?? "vscode";
|
|
986
|
+
let info;
|
|
987
|
+
if (!serverState2.ideInfo) {
|
|
988
|
+
info = { ide: effectiveIde };
|
|
989
|
+
} else {
|
|
990
|
+
const { scheme: _scheme, ...rest } = serverState2.ideInfo;
|
|
991
|
+
info = rest;
|
|
992
|
+
}
|
|
993
|
+
return {
|
|
994
|
+
...info,
|
|
995
|
+
prompts: resolveIntents(promptsConfig),
|
|
996
|
+
hotKeys: userConfig["inspector.hotKey"] ?? "alt",
|
|
997
|
+
theme: userConfig["inspector.theme"] ?? "auto",
|
|
998
|
+
includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
|
|
999
|
+
runtimeContext: {
|
|
1000
|
+
enabled: true,
|
|
1001
|
+
preview: true,
|
|
1002
|
+
maxRuntimeErrors: 3,
|
|
1003
|
+
maxFailedRequests: 2
|
|
1004
|
+
},
|
|
1005
|
+
screenshotContext: {
|
|
1006
|
+
enabled: false
|
|
1007
|
+
},
|
|
1008
|
+
annotationResponseMode: userConfig["prompt.annotationResponseMode"] ?? "unified",
|
|
1009
|
+
autoSend: userConfig["prompt.autoSend"] ?? false
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// src/server/open-file.ts
|
|
1014
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
1015
|
+
import { launchIDE as launchIDE2 } from "launch-ide";
|
|
1016
|
+
var serverLogger2 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
1017
|
+
var VSCODE_FAMILY_SCHEMES = [
|
|
1018
|
+
"vscode",
|
|
1019
|
+
"vscode-insiders",
|
|
1020
|
+
"cursor",
|
|
1021
|
+
"windsurf",
|
|
1022
|
+
"trae",
|
|
1023
|
+
"trae-cn",
|
|
1024
|
+
"vscodium",
|
|
1025
|
+
"codebuddy",
|
|
1026
|
+
"codebuddy-cn",
|
|
1027
|
+
"antigravity"
|
|
1028
|
+
];
|
|
1029
|
+
function handleOpenFileRequest(body, serverState2) {
|
|
1030
|
+
const absolutePath = resolveWorkspacePath(body.file, serverState2.cwd);
|
|
1031
|
+
assertPathWithinProject(absolutePath, serverState2.projectRoot);
|
|
1032
|
+
const userConfig = loadUserConfigSync(false, serverState2.cwd, serverState2.configRoot);
|
|
1033
|
+
const configuredIde = userConfig.ide;
|
|
1034
|
+
const activeIde = serverState2.ideInfo?.ide;
|
|
1035
|
+
const activeIdeScheme = serverState2.ideInfo?.scheme;
|
|
1036
|
+
const rawEditorHint = configuredIde || activeIde || activeIdeScheme || "code";
|
|
1037
|
+
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
1038
|
+
serverLogger2.warn(
|
|
1039
|
+
`Active IDE is ${activeIdeScheme}, but config forces ${configuredIde}. Using configured IDE.`
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
let editorHint = rawEditorHint;
|
|
1043
|
+
if (rawEditorHint === "vscode") editorHint = "code";
|
|
1044
|
+
else if (rawEditorHint === "vscode-insiders") editorHint = "code-insiders";
|
|
1045
|
+
else if (rawEditorHint === "vscodium") editorHint = "codium";
|
|
1046
|
+
else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
|
|
1047
|
+
serverLogger2.debug(
|
|
1048
|
+
`IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
|
|
1049
|
+
);
|
|
1050
|
+
if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
|
|
1051
|
+
let normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
1052
|
+
if (!normalizedPath.startsWith("/")) {
|
|
1053
|
+
normalizedPath = "/" + normalizedPath;
|
|
1054
|
+
}
|
|
1055
|
+
const encodedPath = encodeURI(normalizedPath);
|
|
1056
|
+
const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
|
|
1057
|
+
serverLogger2.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
|
|
1058
|
+
try {
|
|
1059
|
+
if (process.platform === "darwin") {
|
|
1060
|
+
execFileSync2("open", [uri]);
|
|
1061
|
+
} else if (process.platform === "win32") {
|
|
1062
|
+
execFileSync2("cmd", ["/c", "start", '""', uri]);
|
|
1063
|
+
} else {
|
|
1064
|
+
execFileSync2("xdg-open", [uri]);
|
|
1065
|
+
}
|
|
1066
|
+
} catch (e) {
|
|
1067
|
+
serverLogger2.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
|
|
1068
|
+
launchIDE2({
|
|
1069
|
+
file: absolutePath,
|
|
1070
|
+
line: body.line,
|
|
1071
|
+
column: body.column,
|
|
1072
|
+
editor: editorHint,
|
|
1073
|
+
type: process.platform === "darwin" ? "open" : "exec"
|
|
1074
|
+
});
|
|
1075
|
+
}
|
|
1076
|
+
} else {
|
|
1077
|
+
launchIDE2({
|
|
1078
|
+
file: absolutePath,
|
|
1079
|
+
line: body.line,
|
|
1080
|
+
column: body.column,
|
|
1081
|
+
editor: editorHint,
|
|
1082
|
+
type: process.platform === "darwin" ? "open" : "exec"
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
return { success: true };
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// src/server/project-root.ts
|
|
1089
|
+
import fs3 from "fs";
|
|
1090
|
+
import path7 from "path";
|
|
1091
|
+
import { execSync } from "child_process";
|
|
1092
|
+
var serverLogger3 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
756
1093
|
function resolveProjectRoot() {
|
|
757
1094
|
const cwd = process.cwd();
|
|
758
1095
|
let gitRoot;
|
|
759
1096
|
try {
|
|
760
1097
|
gitRoot = execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
761
1098
|
} catch (e) {
|
|
762
|
-
|
|
1099
|
+
serverLogger3.warn("Failed to resolve git root via git rev-parse:", e);
|
|
763
1100
|
gitRoot = cwd;
|
|
764
1101
|
}
|
|
765
1102
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -767,34 +1104,31 @@ function resolveProjectRoot() {
|
|
|
767
1104
|
let current = start;
|
|
768
1105
|
while (!visited.has(current)) {
|
|
769
1106
|
visited.add(current);
|
|
770
|
-
if (fs3.existsSync(
|
|
1107
|
+
if (fs3.existsSync(path7.join(current, ".inspecto"))) return current;
|
|
771
1108
|
if (current === stop) break;
|
|
772
|
-
const parent =
|
|
1109
|
+
const parent = path7.dirname(current);
|
|
773
1110
|
if (parent === current) break;
|
|
774
1111
|
current = parent;
|
|
775
1112
|
}
|
|
776
1113
|
return null;
|
|
777
1114
|
};
|
|
778
|
-
const cwdMatch = search(cwd,
|
|
1115
|
+
const cwdMatch = search(cwd, path7.parse(cwd).root);
|
|
779
1116
|
if (cwdMatch) return cwdMatch;
|
|
780
|
-
const repoMatch = search(gitRoot,
|
|
1117
|
+
const repoMatch = search(gitRoot, path7.parse(gitRoot).root);
|
|
781
1118
|
if (repoMatch) return repoMatch;
|
|
782
1119
|
return gitRoot;
|
|
783
1120
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
launchIDE({ file: uri });
|
|
796
|
-
}
|
|
797
|
-
}
|
|
1121
|
+
|
|
1122
|
+
// src/server/index.ts
|
|
1123
|
+
var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
1124
|
+
var serverState = {
|
|
1125
|
+
port: null,
|
|
1126
|
+
running: false,
|
|
1127
|
+
projectRoot: "",
|
|
1128
|
+
configRoot: "",
|
|
1129
|
+
cwd: process.cwd()
|
|
1130
|
+
};
|
|
1131
|
+
var serverInstance = null;
|
|
798
1132
|
async function startServer() {
|
|
799
1133
|
if (serverState.running && serverState.port !== null) {
|
|
800
1134
|
return serverState.port;
|
|
@@ -806,7 +1140,7 @@ async function startServer() {
|
|
|
806
1140
|
const port = await portfinder.getPortPromise();
|
|
807
1141
|
watchConfig(
|
|
808
1142
|
() => {
|
|
809
|
-
|
|
1143
|
+
serverLogger4.info("user config reloaded.");
|
|
810
1144
|
},
|
|
811
1145
|
serverState.cwd,
|
|
812
1146
|
serverState.configRoot
|
|
@@ -822,7 +1156,7 @@ async function startServer() {
|
|
|
822
1156
|
}
|
|
823
1157
|
const url = new URL(req.url ?? "/", `http://localhost:${port}`);
|
|
824
1158
|
handleRequest(url, req, res).catch((err) => {
|
|
825
|
-
|
|
1159
|
+
serverLogger4.error("server error:", err);
|
|
826
1160
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
827
1161
|
res.end(JSON.stringify({ success: false, error: String(err) }));
|
|
828
1162
|
});
|
|
@@ -835,41 +1169,41 @@ async function startServer() {
|
|
|
835
1169
|
serverInstance.once("error", reject);
|
|
836
1170
|
});
|
|
837
1171
|
serverInstance.on("error", (err) => {
|
|
838
|
-
|
|
1172
|
+
serverLogger4.error("persistent server error:", err);
|
|
839
1173
|
});
|
|
840
1174
|
serverState.port = port;
|
|
841
1175
|
serverState.running = true;
|
|
842
|
-
const portFile =
|
|
1176
|
+
const portFile = path8.join(os2.tmpdir(), "inspecto.port.json");
|
|
843
1177
|
try {
|
|
844
1178
|
let portData = {};
|
|
845
|
-
if (
|
|
1179
|
+
if (fs4.existsSync(portFile)) {
|
|
846
1180
|
try {
|
|
847
|
-
portData = JSON.parse(
|
|
1181
|
+
portData = JSON.parse(fs4.readFileSync(portFile, "utf-8"));
|
|
848
1182
|
} catch (e) {
|
|
849
1183
|
}
|
|
850
1184
|
}
|
|
851
|
-
const rootHash =
|
|
1185
|
+
const rootHash = crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
852
1186
|
portData[rootHash] = port;
|
|
853
|
-
|
|
1187
|
+
fs4.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
854
1188
|
} catch (e) {
|
|
855
|
-
|
|
1189
|
+
serverLogger4.warn("Failed to write port file:", e);
|
|
856
1190
|
}
|
|
857
1191
|
process.once("exit", () => {
|
|
858
1192
|
try {
|
|
859
|
-
if (
|
|
860
|
-
const portData = JSON.parse(
|
|
861
|
-
const rootHash =
|
|
1193
|
+
if (fs4.existsSync(portFile)) {
|
|
1194
|
+
const portData = JSON.parse(fs4.readFileSync(portFile, "utf-8"));
|
|
1195
|
+
const rootHash = crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
862
1196
|
delete portData[rootHash];
|
|
863
1197
|
if (Object.keys(portData).length === 0) {
|
|
864
|
-
|
|
1198
|
+
fs4.unlinkSync(portFile);
|
|
865
1199
|
} else {
|
|
866
|
-
|
|
1200
|
+
fs4.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
867
1201
|
}
|
|
868
1202
|
}
|
|
869
1203
|
} catch {
|
|
870
1204
|
}
|
|
871
1205
|
});
|
|
872
|
-
|
|
1206
|
+
serverLogger4.info(`server running at http://127.0.0.1:${port}`);
|
|
873
1207
|
return port;
|
|
874
1208
|
}
|
|
875
1209
|
async function readBody(req) {
|
|
@@ -888,26 +1222,7 @@ async function handleRequest(url, req, res) {
|
|
|
888
1222
|
return;
|
|
889
1223
|
}
|
|
890
1224
|
if (pathname === INSPECTO_API_PATHS.CLIENT_CONFIG && req.method === "GET") {
|
|
891
|
-
const
|
|
892
|
-
const promptsConfig = await loadPromptsConfig(false, serverState.cwd, serverState.configRoot);
|
|
893
|
-
const effectiveIde = userConfig.ide ?? "vscode";
|
|
894
|
-
let info;
|
|
895
|
-
if (!serverState.ideInfo) {
|
|
896
|
-
info = {
|
|
897
|
-
ide: effectiveIde
|
|
898
|
-
};
|
|
899
|
-
} else {
|
|
900
|
-
const { scheme: _scheme, ...rest } = serverState.ideInfo;
|
|
901
|
-
info = rest;
|
|
902
|
-
}
|
|
903
|
-
const config = {
|
|
904
|
-
...info,
|
|
905
|
-
prompts: resolveIntents(promptsConfig),
|
|
906
|
-
hotKeys: userConfig["inspector.hotKey"] ?? "alt",
|
|
907
|
-
theme: userConfig["inspector.theme"] ?? "auto",
|
|
908
|
-
includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
|
|
909
|
-
autoSend: userConfig["prompt.autoSend"] ?? false
|
|
910
|
-
};
|
|
1225
|
+
const config = await buildClientConfig(serverState);
|
|
911
1226
|
delete config.providers;
|
|
912
1227
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
913
1228
|
res.end(JSON.stringify(config));
|
|
@@ -918,23 +1233,23 @@ async function handleRequest(url, req, res) {
|
|
|
918
1233
|
const body = JSON.parse(await readBody(req));
|
|
919
1234
|
const ideWorkspace = body.workspaceRoot || "";
|
|
920
1235
|
const serverProjectRoot = serverState.projectRoot || "";
|
|
921
|
-
const normalizedIdeRoot = ideWorkspace ?
|
|
922
|
-
const normalizedServerRoot = serverProjectRoot ?
|
|
923
|
-
const isSameProject = !normalizedIdeRoot || !normalizedServerRoot || normalizedIdeRoot === normalizedServerRoot || normalizedServerRoot.startsWith(normalizedIdeRoot +
|
|
1236
|
+
const normalizedIdeRoot = ideWorkspace ? path8.resolve(ideWorkspace) : "";
|
|
1237
|
+
const normalizedServerRoot = serverProjectRoot ? path8.resolve(serverProjectRoot) : "";
|
|
1238
|
+
const isSameProject = !normalizedIdeRoot || !normalizedServerRoot || normalizedIdeRoot === normalizedServerRoot || normalizedServerRoot.startsWith(normalizedIdeRoot + path8.sep) || normalizedIdeRoot.startsWith(normalizedServerRoot + path8.sep);
|
|
924
1239
|
if (isSameProject) {
|
|
925
1240
|
serverState.ideInfo = body;
|
|
926
|
-
|
|
1241
|
+
serverLogger4.debug(
|
|
927
1242
|
`Accepted IDE info from matched workspace (ide-${body.ide} / schema-${body.scheme})`
|
|
928
1243
|
);
|
|
929
1244
|
} else {
|
|
930
|
-
|
|
1245
|
+
serverLogger4.debug(
|
|
931
1246
|
`Ignored IDE info from unrelated workspace (IDE Workspace: ${ideWorkspace}, Server: ${serverProjectRoot}, Scheme: ${body.scheme}, IDE: ${body.ide})`
|
|
932
1247
|
);
|
|
933
1248
|
}
|
|
934
1249
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
935
1250
|
res.end(JSON.stringify({ success: true }));
|
|
936
1251
|
} catch (e) {
|
|
937
|
-
|
|
1252
|
+
serverLogger4.error(`Error parsing ${INSPECTO_API_PATHS.IDE_INFO} POST request:`, e);
|
|
938
1253
|
res.writeHead(400, { "Content-Type": "application/json" });
|
|
939
1254
|
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
940
1255
|
}
|
|
@@ -949,79 +1264,14 @@ async function handleRequest(url, req, res) {
|
|
|
949
1264
|
res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
950
1265
|
return;
|
|
951
1266
|
}
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1267
|
+
try {
|
|
1268
|
+
handleOpenFileRequest(body, serverState);
|
|
1269
|
+
} catch {
|
|
1270
|
+
serverLogger4.warn(`Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}`);
|
|
956
1271
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
957
1272
|
res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
|
|
958
1273
|
return;
|
|
959
1274
|
}
|
|
960
|
-
const userConfig = loadUserConfigSync(false, serverState.cwd, serverState.configRoot);
|
|
961
|
-
const configuredIde = userConfig.ide;
|
|
962
|
-
const activeIde = serverState.ideInfo?.ide;
|
|
963
|
-
const activeIdeScheme = serverState.ideInfo?.scheme;
|
|
964
|
-
const rawEditorHint = configuredIde || activeIde || activeIdeScheme || "code";
|
|
965
|
-
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
966
|
-
serverLogger.warn(
|
|
967
|
-
`Active IDE is ${activeIdeScheme}, but config forces ${configuredIde}. Using configured IDE.`
|
|
968
|
-
);
|
|
969
|
-
}
|
|
970
|
-
let editorHint = rawEditorHint;
|
|
971
|
-
if (rawEditorHint === "vscode") editorHint = "code";
|
|
972
|
-
else if (rawEditorHint === "vscode-insiders") editorHint = "code-insiders";
|
|
973
|
-
else if (rawEditorHint === "vscodium") editorHint = "codium";
|
|
974
|
-
else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
|
|
975
|
-
serverLogger.debug(
|
|
976
|
-
`IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
|
|
977
|
-
);
|
|
978
|
-
const VSCODE_FAMILY_SCHEMES = [
|
|
979
|
-
"vscode",
|
|
980
|
-
"vscode-insiders",
|
|
981
|
-
"cursor",
|
|
982
|
-
"windsurf",
|
|
983
|
-
"trae",
|
|
984
|
-
"trae-cn",
|
|
985
|
-
"vscodium",
|
|
986
|
-
"codebuddy",
|
|
987
|
-
"codebuddy-cn",
|
|
988
|
-
"antigravity"
|
|
989
|
-
];
|
|
990
|
-
if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
|
|
991
|
-
let normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
992
|
-
if (!normalizedPath.startsWith("/")) {
|
|
993
|
-
normalizedPath = "/" + normalizedPath;
|
|
994
|
-
}
|
|
995
|
-
const encodedPath = encodeURI(normalizedPath);
|
|
996
|
-
const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
|
|
997
|
-
serverLogger.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
|
|
998
|
-
try {
|
|
999
|
-
if (process.platform === "darwin") {
|
|
1000
|
-
execFileSync("open", [uri]);
|
|
1001
|
-
} else if (process.platform === "win32") {
|
|
1002
|
-
execFileSync("cmd", ["/c", "start", '""', uri]);
|
|
1003
|
-
} else {
|
|
1004
|
-
execFileSync("xdg-open", [uri]);
|
|
1005
|
-
}
|
|
1006
|
-
} catch (e) {
|
|
1007
|
-
serverLogger.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
|
|
1008
|
-
launchIDE({
|
|
1009
|
-
file: absolutePath,
|
|
1010
|
-
line: body.line,
|
|
1011
|
-
column: body.column,
|
|
1012
|
-
editor: editorHint,
|
|
1013
|
-
type: process.platform === "darwin" ? "open" : "exec"
|
|
1014
|
-
});
|
|
1015
|
-
}
|
|
1016
|
-
} else {
|
|
1017
|
-
launchIDE({
|
|
1018
|
-
file: absolutePath,
|
|
1019
|
-
line: body.line,
|
|
1020
|
-
column: body.column,
|
|
1021
|
-
editor: editorHint,
|
|
1022
|
-
type: process.platform === "darwin" ? "open" : "exec"
|
|
1023
|
-
});
|
|
1024
|
-
}
|
|
1025
1275
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1026
1276
|
res.end(JSON.stringify({ success: true }));
|
|
1027
1277
|
return;
|
|
@@ -1032,10 +1282,11 @@ async function handleRequest(url, req, res) {
|
|
|
1032
1282
|
const column = parseInt(url.searchParams.get("column") ?? "1", 10);
|
|
1033
1283
|
const maxLines = parseInt(url.searchParams.get("maxLines") ?? "100", 10);
|
|
1034
1284
|
try {
|
|
1035
|
-
const absolutePath =
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1285
|
+
const absolutePath = resolveWorkspacePath(file, serverState.cwd);
|
|
1286
|
+
try {
|
|
1287
|
+
assertPathWithinProject(absolutePath, serverState.projectRoot);
|
|
1288
|
+
} catch {
|
|
1289
|
+
serverLogger4.warn(`Security: Blocked path traversal attempt in PROJECT_SNIPPET: ${file}`);
|
|
1039
1290
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
1040
1291
|
res.end(
|
|
1041
1292
|
JSON.stringify({
|
|
@@ -1065,7 +1316,23 @@ async function handleRequest(url, req, res) {
|
|
|
1065
1316
|
res.writeHead(result.success ? 200 : 500, { "Content-Type": "application/json" });
|
|
1066
1317
|
res.end(JSON.stringify(result));
|
|
1067
1318
|
} catch (e) {
|
|
1068
|
-
|
|
1319
|
+
serverLogger4.error(`Error parsing ${INSPECTO_API_PATHS.AI_DISPATCH} request:`, e);
|
|
1320
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1321
|
+
res.end(JSON.stringify({ success: false, error: String(e), errorCode: "INTERNAL_ERROR" }));
|
|
1322
|
+
}
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
if (pathname === INSPECTO_API_PATHS.AI_BATCH_DISPATCH && req.method === "POST") {
|
|
1326
|
+
try {
|
|
1327
|
+
const rawBody = await readBody(req);
|
|
1328
|
+
const body = JSON.parse(rawBody);
|
|
1329
|
+
const result = await dispatchAnnotationsToAi(body, serverState);
|
|
1330
|
+
res.writeHead(getBatchDispatchStatusCode(result.errorCode, result.success), {
|
|
1331
|
+
"Content-Type": "application/json"
|
|
1332
|
+
});
|
|
1333
|
+
res.end(JSON.stringify(result));
|
|
1334
|
+
} catch (e) {
|
|
1335
|
+
serverLogger4.error(`Error parsing ${INSPECTO_API_PATHS.AI_BATCH_DISPATCH} request:`, e);
|
|
1069
1336
|
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1070
1337
|
res.end(JSON.stringify({ success: false, error: String(e), errorCode: "INTERNAL_ERROR" }));
|
|
1071
1338
|
}
|
|
@@ -1073,7 +1340,7 @@ async function handleRequest(url, req, res) {
|
|
|
1073
1340
|
}
|
|
1074
1341
|
if (pathname.startsWith(`${INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
|
|
1075
1342
|
const ticketId = pathname.substring(INSPECTO_API_PATHS.AI_TICKET.length + 1);
|
|
1076
|
-
const payloadStr =
|
|
1343
|
+
const payloadStr = readTicket(ticketId);
|
|
1077
1344
|
if (!payloadStr) {
|
|
1078
1345
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1079
1346
|
res.end(JSON.stringify({ success: false, error: "Ticket not found or expired" }));
|
|
@@ -1087,54 +1354,28 @@ async function handleRequest(url, req, res) {
|
|
|
1087
1354
|
res.end(JSON.stringify({ error: "not found" }));
|
|
1088
1355
|
}
|
|
1089
1356
|
async function dispatchToAi(req) {
|
|
1090
|
-
const { location, snippet, prompt } = req;
|
|
1091
|
-
const userConfig = loadUserConfigSync(false, serverState.cwd, serverState.configRoot);
|
|
1092
|
-
const resolvedTarget = resolveTargetTool(userConfig);
|
|
1357
|
+
const { location, snippet, prompt, screenshotContext } = req;
|
|
1093
1358
|
const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
|
|
1094
1359
|
|
|
1095
1360
|
\`\`\`
|
|
1096
1361
|
${snippet}
|
|
1097
1362
|
\`\`\`
|
|
1098
1363
|
`;
|
|
1099
|
-
const
|
|
1100
|
-
|
|
1101
|
-
const activeIde = serverState.ideInfo?.ide;
|
|
1102
|
-
const activeIdeScheme = serverState.ideInfo?.scheme;
|
|
1103
|
-
const finalIde = configuredIde || activeIdeScheme || activeIde || "vscode";
|
|
1104
|
-
if (configuredIde && activeIdeScheme && !activeIdeScheme.includes(configuredIde)) {
|
|
1105
|
-
serverLogger.warn(
|
|
1106
|
-
`dispatchToAi: Active IDE is ${activeIdeScheme}, but config forces ${configuredIde}. Using configured IDE.`
|
|
1107
|
-
);
|
|
1108
|
-
}
|
|
1109
|
-
const mode = resolveProviderMode(resolvedTarget, finalIde, userConfig);
|
|
1110
|
-
const overrides = extractToolOverrides(finalIde, userConfig)[resolvedTarget] || {};
|
|
1111
|
-
overrides.type = mode;
|
|
1112
|
-
const fullPayload = {
|
|
1113
|
-
ide: finalIde,
|
|
1114
|
-
target: resolvedTarget,
|
|
1115
|
-
targetType: mode,
|
|
1364
|
+
const runtime = resolvePromptDispatchRuntime(serverState);
|
|
1365
|
+
return dispatchPromptThroughIde(runtime, {
|
|
1116
1366
|
prompt: formattedPrompt,
|
|
1117
1367
|
filePath: location.file,
|
|
1118
1368
|
line: location.line,
|
|
1119
1369
|
column: location.column,
|
|
1120
1370
|
snippet,
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
serverLogger.debug(`dispatchToAi: Generated URI: ${uri}`);
|
|
1130
|
-
launchURI(uri);
|
|
1131
|
-
return {
|
|
1132
|
-
success: true,
|
|
1133
|
-
fallbackPayload: {
|
|
1134
|
-
prompt: formattedPrompt,
|
|
1135
|
-
file: location.file
|
|
1136
|
-
}
|
|
1137
|
-
};
|
|
1371
|
+
...screenshotContext ? { screenshotContext } : {}
|
|
1372
|
+
});
|
|
1373
|
+
}
|
|
1374
|
+
function getBatchDispatchStatusCode(errorCode, success) {
|
|
1375
|
+
if (success) return 200;
|
|
1376
|
+
if (errorCode === "INVALID_REQUEST") return 400;
|
|
1377
|
+
if (errorCode === "FORBIDDEN_PATH") return 403;
|
|
1378
|
+
return 500;
|
|
1138
1379
|
}
|
|
1139
1380
|
|
|
1140
1381
|
// src/injectors/utils.ts
|