@inspecto-dev/plugin 0.3.8 → 0.3.9
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/dist/astro.cjs +519 -75
- package/dist/astro.cjs.map +1 -1
- package/dist/astro.js +519 -75
- package/dist/astro.js.map +1 -1
- package/dist/index.cjs +518 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +518 -74
- package/dist/index.js.map +1 -1
- package/dist/legacy/rspack/index.cjs +513 -72
- package/dist/legacy/rspack/index.cjs.map +1 -1
- package/dist/legacy/rspack/index.js +513 -72
- package/dist/legacy/rspack/index.js.map +1 -1
- package/dist/legacy/rspack/loader.cjs +3 -0
- package/dist/legacy/rspack/loader.cjs.map +1 -1
- package/dist/legacy/rspack/loader.js +3 -0
- package/dist/legacy/rspack/loader.js.map +1 -1
- package/dist/legacy/webpack4/index.cjs +513 -72
- package/dist/legacy/webpack4/index.cjs.map +1 -1
- package/dist/legacy/webpack4/index.js +513 -72
- package/dist/legacy/webpack4/index.js.map +1 -1
- package/dist/legacy/webpack4/loader.cjs +3 -0
- package/dist/legacy/webpack4/loader.cjs.map +1 -1
- package/dist/legacy/webpack4/loader.js +3 -0
- package/dist/legacy/webpack4/loader.js.map +1 -1
- package/dist/rollup.cjs +518 -74
- package/dist/rollup.cjs.map +1 -1
- package/dist/rollup.js +518 -74
- package/dist/rollup.js.map +1 -1
- package/dist/rspack.cjs +518 -74
- package/dist/rspack.cjs.map +1 -1
- package/dist/rspack.js +518 -74
- package/dist/rspack.js.map +1 -1
- package/dist/vite.cjs +518 -74
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.js +518 -74
- package/dist/vite.js.map +1 -1
- package/dist/webpack.cjs +518 -74
- package/dist/webpack.cjs.map +1 -1
- package/dist/webpack.js +518 -74
- package/dist/webpack.js.map +1 -1
- package/package.json +6 -10
|
@@ -537,7 +537,6 @@ function dispatchPromptThroughIde(runtime, payload) {
|
|
|
537
537
|
line: payload.line,
|
|
538
538
|
column: payload.column,
|
|
539
539
|
snippet: payload.snippet,
|
|
540
|
-
...payload.screenshotContext ? { screenshotContext: payload.screenshotContext } : {},
|
|
541
540
|
overrides: runtime.overrides,
|
|
542
541
|
autoSend: runtime.autoSend
|
|
543
542
|
});
|
|
@@ -671,6 +670,188 @@ function assertPathWithinIdeOpenScope(file, projectRoot) {
|
|
|
671
670
|
}
|
|
672
671
|
}
|
|
673
672
|
|
|
673
|
+
// src/server/session-store.ts
|
|
674
|
+
var DEFAULT_STATUS = "pending";
|
|
675
|
+
function createAnnotationSessionStore(options = {}) {
|
|
676
|
+
const sessions = /* @__PURE__ */ new Map();
|
|
677
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
678
|
+
const now = options.now ?? (() => Date.now());
|
|
679
|
+
const createId = options.createId ?? createRandomId;
|
|
680
|
+
function findNewestMatchingSession(statuses) {
|
|
681
|
+
return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
|
|
682
|
+
}
|
|
683
|
+
function updateSessionStatus(id, status) {
|
|
684
|
+
const session = sessions.get(id);
|
|
685
|
+
if (!session) return null;
|
|
686
|
+
const timestamp = now();
|
|
687
|
+
session.status = status;
|
|
688
|
+
session.updatedAt = timestamp;
|
|
689
|
+
if (status === "acknowledged") {
|
|
690
|
+
session.acknowledgedAt = timestamp;
|
|
691
|
+
}
|
|
692
|
+
if (status === "resolved") {
|
|
693
|
+
session.resolvedAt = timestamp;
|
|
694
|
+
}
|
|
695
|
+
emit({ type: "session-status-updated", session });
|
|
696
|
+
return cloneSession(session);
|
|
697
|
+
}
|
|
698
|
+
function claimSession(id, statuses) {
|
|
699
|
+
const session = sessions.get(id);
|
|
700
|
+
if (!session || statuses && !statuses.has(session.status)) return null;
|
|
701
|
+
if (session.status === "acknowledged") return cloneSession(session);
|
|
702
|
+
return updateSessionStatus(id, "acknowledged");
|
|
703
|
+
}
|
|
704
|
+
function emit(event) {
|
|
705
|
+
const snapshot = cloneSession(event.session);
|
|
706
|
+
for (const listener of listeners) {
|
|
707
|
+
listener({ type: event.type, session: snapshot });
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
const store = {
|
|
711
|
+
createSession(input) {
|
|
712
|
+
const timestamp = now();
|
|
713
|
+
const session = {
|
|
714
|
+
id: createId(),
|
|
715
|
+
instruction: input.instruction?.trim() ?? "",
|
|
716
|
+
annotations: cloneArray(input.annotations),
|
|
717
|
+
...input.deliveryMode ? { deliveryMode: input.deliveryMode } : {},
|
|
718
|
+
status: DEFAULT_STATUS,
|
|
719
|
+
messages: cloneArray(input.messages ?? []),
|
|
720
|
+
createdAt: timestamp,
|
|
721
|
+
updatedAt: timestamp,
|
|
722
|
+
...input.runtimeContext ? { runtimeContext: cloneValue(input.runtimeContext) } : {},
|
|
723
|
+
...input.cssContextPrompt?.trim() ? { cssContextPrompt: input.cssContextPrompt.trim() } : {},
|
|
724
|
+
...input.pageUrl ? { pageUrl: input.pageUrl } : {},
|
|
725
|
+
...input.route ? { route: input.route } : {}
|
|
726
|
+
};
|
|
727
|
+
sessions.set(session.id, session);
|
|
728
|
+
emit({ type: "session-created", session });
|
|
729
|
+
return cloneSession(session);
|
|
730
|
+
},
|
|
731
|
+
getSession(id) {
|
|
732
|
+
const session = sessions.get(id);
|
|
733
|
+
return session ? cloneSession(session) : null;
|
|
734
|
+
},
|
|
735
|
+
listSessions(options2 = {}) {
|
|
736
|
+
const statuses = normalizeStatuses(options2.status);
|
|
737
|
+
return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt).map((session) => cloneSession(session));
|
|
738
|
+
},
|
|
739
|
+
async claimNextSession(options2 = {}) {
|
|
740
|
+
const statuses = normalizeStatuses(DEFAULT_STATUS);
|
|
741
|
+
const existingSession = findNewestMatchingSession(statuses);
|
|
742
|
+
if (existingSession) {
|
|
743
|
+
return {
|
|
744
|
+
session: claimSession(existingSession.id, statuses),
|
|
745
|
+
timedOut: false,
|
|
746
|
+
matchedExisting: true
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
const timeoutMs = normalizeTimeoutMs(options2.timeoutMs);
|
|
750
|
+
if (timeoutMs === 0) {
|
|
751
|
+
return {
|
|
752
|
+
session: null,
|
|
753
|
+
timedOut: true,
|
|
754
|
+
matchedExisting: false
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
return await new Promise((resolve2) => {
|
|
758
|
+
let settled = false;
|
|
759
|
+
let timeout = null;
|
|
760
|
+
const finish = (result) => {
|
|
761
|
+
if (settled) return;
|
|
762
|
+
settled = true;
|
|
763
|
+
unsubscribe();
|
|
764
|
+
if (timeout) {
|
|
765
|
+
clearTimeout(timeout);
|
|
766
|
+
}
|
|
767
|
+
resolve2(result);
|
|
768
|
+
};
|
|
769
|
+
const unsubscribe = this.subscribe((event) => {
|
|
770
|
+
const session = claimSession(event.session.id, statuses);
|
|
771
|
+
if (!session) return;
|
|
772
|
+
finish({
|
|
773
|
+
session,
|
|
774
|
+
timedOut: false,
|
|
775
|
+
matchedExisting: false,
|
|
776
|
+
event: event.type
|
|
777
|
+
});
|
|
778
|
+
});
|
|
779
|
+
if (timeoutMs !== null) {
|
|
780
|
+
timeout = setTimeout(() => {
|
|
781
|
+
finish({
|
|
782
|
+
session: null,
|
|
783
|
+
timedOut: true,
|
|
784
|
+
matchedExisting: false
|
|
785
|
+
});
|
|
786
|
+
}, timeoutMs);
|
|
787
|
+
}
|
|
788
|
+
});
|
|
789
|
+
},
|
|
790
|
+
appendMessage(id, input) {
|
|
791
|
+
const session = sessions.get(id);
|
|
792
|
+
if (!session) return null;
|
|
793
|
+
const timestamp = now();
|
|
794
|
+
session.messages.push({
|
|
795
|
+
id: createId(),
|
|
796
|
+
role: input.role,
|
|
797
|
+
text: input.text,
|
|
798
|
+
createdAt: timestamp
|
|
799
|
+
});
|
|
800
|
+
session.updatedAt = timestamp;
|
|
801
|
+
if (input.role === "agent" && isPendingLikeStatus(session.status)) {
|
|
802
|
+
session.status = "in_progress";
|
|
803
|
+
}
|
|
804
|
+
emit({ type: "session-message-appended", session });
|
|
805
|
+
return cloneSession(session);
|
|
806
|
+
},
|
|
807
|
+
updateStatus(id, status) {
|
|
808
|
+
return updateSessionStatus(id, status);
|
|
809
|
+
},
|
|
810
|
+
subscribe(listener) {
|
|
811
|
+
listeners.add(listener);
|
|
812
|
+
return () => {
|
|
813
|
+
listeners.delete(listener);
|
|
814
|
+
};
|
|
815
|
+
},
|
|
816
|
+
clear() {
|
|
817
|
+
sessions.clear();
|
|
818
|
+
listeners.clear();
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
return store;
|
|
822
|
+
}
|
|
823
|
+
var annotationSessionStore = createAnnotationSessionStore();
|
|
824
|
+
function normalizeStatuses(status) {
|
|
825
|
+
if (!status) return null;
|
|
826
|
+
return new Set(Array.isArray(status) ? status : [status]);
|
|
827
|
+
}
|
|
828
|
+
function normalizeTimeoutMs(value) {
|
|
829
|
+
if (value === void 0) return null;
|
|
830
|
+
if (!Number.isFinite(value)) return 0;
|
|
831
|
+
return Math.max(0, Math.floor(value));
|
|
832
|
+
}
|
|
833
|
+
function isPendingLikeStatus(status) {
|
|
834
|
+
return status === "pending" || status === "acknowledged";
|
|
835
|
+
}
|
|
836
|
+
function hasAgentReply(session) {
|
|
837
|
+
return session.messages.some((message) => message.role === "agent" && Boolean(message.text?.trim()));
|
|
838
|
+
}
|
|
839
|
+
function createRandomId() {
|
|
840
|
+
return `annotation-session-${Math.random().toString(36).slice(2, 10)}`;
|
|
841
|
+
}
|
|
842
|
+
function cloneSession(session) {
|
|
843
|
+
return cloneValue(session);
|
|
844
|
+
}
|
|
845
|
+
function cloneArray(value) {
|
|
846
|
+
return cloneValue(value);
|
|
847
|
+
}
|
|
848
|
+
function cloneValue(value) {
|
|
849
|
+
if (typeof structuredClone === "function") {
|
|
850
|
+
return structuredClone(value);
|
|
851
|
+
}
|
|
852
|
+
return JSON.parse(JSON.stringify(value));
|
|
853
|
+
}
|
|
854
|
+
|
|
674
855
|
// src/server/annotation-dispatch.ts
|
|
675
856
|
var AnnotationDispatchError = class extends Error {
|
|
676
857
|
constructor(message, errorCode) {
|
|
@@ -679,20 +860,30 @@ var AnnotationDispatchError = class extends Error {
|
|
|
679
860
|
this.errorCode = errorCode;
|
|
680
861
|
}
|
|
681
862
|
};
|
|
682
|
-
async function dispatchAnnotationsToAi(req, state) {
|
|
863
|
+
async function dispatchAnnotationsToAi(req, state, store = annotationSessionStore) {
|
|
683
864
|
try {
|
|
684
865
|
validateAnnotationDispatchRequest(req, state);
|
|
685
866
|
const batch = normalizeAnnotationBatch(req);
|
|
686
867
|
const prompt = buildAnnotationBatchPrompt(batch);
|
|
868
|
+
const deliveryMode = normalizeDeliveryMode(req.deliveryMode);
|
|
869
|
+
const session = store.createSession({
|
|
870
|
+
instruction: batch.instruction,
|
|
871
|
+
annotations: toSessionAnnotations(batch.annotations),
|
|
872
|
+
deliveryMode,
|
|
873
|
+
...batch.runtimeContext ? { runtimeContext: batch.runtimeContext } : {},
|
|
874
|
+
...batch.cssContextPrompt ? { cssContextPrompt: batch.cssContextPrompt } : {}
|
|
875
|
+
});
|
|
687
876
|
const representativeTarget = batch.annotations[0]?.targets[0];
|
|
688
|
-
const
|
|
689
|
-
return dispatchPromptThroughIde(runtime, {
|
|
877
|
+
const dispatchResult = deliveryMode === "ide" ? dispatchPromptThroughIde(resolvePromptDispatchRuntime(state), {
|
|
690
878
|
prompt,
|
|
691
879
|
...representativeTarget?.file ? { filePath: representativeTarget.file } : {},
|
|
692
880
|
...representativeTarget?.line ? { line: representativeTarget.line } : {},
|
|
693
|
-
...representativeTarget?.column ? { column: representativeTarget.column } : {}
|
|
694
|
-
|
|
695
|
-
|
|
881
|
+
...representativeTarget?.column ? { column: representativeTarget.column } : {}
|
|
882
|
+
}) : { success: true };
|
|
883
|
+
return {
|
|
884
|
+
...dispatchResult,
|
|
885
|
+
session: toSessionSummary(session)
|
|
886
|
+
};
|
|
696
887
|
} catch (error) {
|
|
697
888
|
return {
|
|
698
889
|
success: false,
|
|
@@ -701,6 +892,41 @@ async function dispatchAnnotationsToAi(req, state) {
|
|
|
701
892
|
};
|
|
702
893
|
}
|
|
703
894
|
}
|
|
895
|
+
function normalizeDeliveryMode(input) {
|
|
896
|
+
return input === "agent" ? "agent" : "ide";
|
|
897
|
+
}
|
|
898
|
+
function toSessionAnnotations(annotations) {
|
|
899
|
+
return annotations.map((annotation) => ({
|
|
900
|
+
id: `annotation-${annotation.index}`,
|
|
901
|
+
note: annotation.note,
|
|
902
|
+
intent: annotation.intent,
|
|
903
|
+
targets: annotation.targets.map((target, targetIndex) => ({
|
|
904
|
+
id: `annotation-${annotation.index}-target-${targetIndex + 1}`,
|
|
905
|
+
label: target.label ?? "Unknown target",
|
|
906
|
+
location: {
|
|
907
|
+
file: target.file,
|
|
908
|
+
line: target.line,
|
|
909
|
+
column: target.column
|
|
910
|
+
},
|
|
911
|
+
...target.selector ? { selector: target.selector } : {},
|
|
912
|
+
...target.snippet ? { snippet: target.snippet } : {},
|
|
913
|
+
rect: {
|
|
914
|
+
x: 0,
|
|
915
|
+
y: 0,
|
|
916
|
+
width: 0,
|
|
917
|
+
height: 0
|
|
918
|
+
}
|
|
919
|
+
}))
|
|
920
|
+
}));
|
|
921
|
+
}
|
|
922
|
+
function toSessionSummary(session) {
|
|
923
|
+
return {
|
|
924
|
+
id: session.id,
|
|
925
|
+
status: session.status,
|
|
926
|
+
createdAt: session.createdAt,
|
|
927
|
+
updatedAt: session.updatedAt
|
|
928
|
+
};
|
|
929
|
+
}
|
|
704
930
|
function validateAnnotationDispatchRequest(req, state) {
|
|
705
931
|
if (!req.annotations.length) {
|
|
706
932
|
throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
|
|
@@ -721,9 +947,7 @@ function validateAnnotationDispatchRequest(req, state) {
|
|
|
721
947
|
function normalizeAnnotationBatch(req) {
|
|
722
948
|
return {
|
|
723
949
|
instruction: req.instruction?.trim() ?? "",
|
|
724
|
-
responseMode: req.responseMode ?? "unified",
|
|
725
950
|
...req.runtimeContext ? { runtimeContext: req.runtimeContext } : {},
|
|
726
|
-
...req.screenshotContext ? { screenshotContext: req.screenshotContext } : {},
|
|
727
951
|
...req.cssContextPrompt?.trim() ? { cssContextPrompt: req.cssContextPrompt.trim() } : {},
|
|
728
952
|
annotations: req.annotations.map((annotation, index) => ({
|
|
729
953
|
index: index + 1,
|
|
@@ -745,12 +969,9 @@ function buildAnnotationBatchPrompt(batch) {
|
|
|
745
969
|
const prompt = batch.instruction ? `${batch.instruction}
|
|
746
970
|
|
|
747
971
|
${body}` : body;
|
|
748
|
-
return
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
batch.cssContextPrompt
|
|
752
|
-
),
|
|
753
|
-
batch.screenshotContext
|
|
972
|
+
return appendCssContextSection(
|
|
973
|
+
appendRuntimeContextSection(prompt, batch.runtimeContext),
|
|
974
|
+
batch.cssContextPrompt
|
|
754
975
|
);
|
|
755
976
|
}
|
|
756
977
|
function appendCssContextSection(prompt, cssContextPrompt) {
|
|
@@ -777,20 +998,6 @@ function buildSelectedElementsPrompt(annotations) {
|
|
|
777
998
|
}
|
|
778
999
|
return lines.join("\n");
|
|
779
1000
|
}
|
|
780
|
-
function appendScreenshotContextSection(prompt, screenshotContext) {
|
|
781
|
-
if (!screenshotContext || !screenshotContext.imageDataUrl && !screenshotContext.imageAssetId) {
|
|
782
|
-
return prompt;
|
|
783
|
-
}
|
|
784
|
-
const lines = [
|
|
785
|
-
"Visual screenshot context attached:",
|
|
786
|
-
`- capturedAt=${screenshotContext.capturedAt}`,
|
|
787
|
-
`- mimeType=${screenshotContext.mimeType}`,
|
|
788
|
-
...screenshotContext.imageAssetId ? [`- imageAssetId=${screenshotContext.imageAssetId}`] : []
|
|
789
|
-
];
|
|
790
|
-
return `${prompt}
|
|
791
|
-
|
|
792
|
-
${lines.join("\n")}`;
|
|
793
|
-
}
|
|
794
1001
|
function appendRuntimeContextSection(prompt, runtimeContext) {
|
|
795
1002
|
if (!runtimeContext?.records.length) {
|
|
796
1003
|
return prompt;
|
|
@@ -838,7 +1045,7 @@ async function buildClientConfig(serverState2) {
|
|
|
838
1045
|
...info,
|
|
839
1046
|
prompts: resolveIntents(promptsConfig),
|
|
840
1047
|
hotKeys: userConfig["inspector.hotKey"] ?? "alt",
|
|
841
|
-
|
|
1048
|
+
annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "both",
|
|
842
1049
|
includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
|
|
843
1050
|
runtimeContext: {
|
|
844
1051
|
enabled: true,
|
|
@@ -846,10 +1053,6 @@ async function buildClientConfig(serverState2) {
|
|
|
846
1053
|
maxRuntimeErrors: 3,
|
|
847
1054
|
maxFailedRequests: 2
|
|
848
1055
|
},
|
|
849
|
-
screenshotContext: {
|
|
850
|
-
enabled: false
|
|
851
|
-
},
|
|
852
|
-
annotationResponseMode: userConfig["prompt.annotationResponseMode"] ?? "unified",
|
|
853
1056
|
autoSend: userConfig["prompt.autoSend"] ?? false
|
|
854
1057
|
};
|
|
855
1058
|
}
|
|
@@ -894,7 +1097,7 @@ function handleOpenFileRequest(body, serverState2) {
|
|
|
894
1097
|
else if (rawEditorHint === "vscodium") editorHint = "codium";
|
|
895
1098
|
else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
|
|
896
1099
|
serverLogger2.debug(
|
|
897
|
-
`
|
|
1100
|
+
`SOURCE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
|
|
898
1101
|
);
|
|
899
1102
|
if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
|
|
900
1103
|
let normalizedPath = absolutePath.replace(/\\/g, "/");
|
|
@@ -903,7 +1106,7 @@ function handleOpenFileRequest(body, serverState2) {
|
|
|
903
1106
|
}
|
|
904
1107
|
const encodedPath = encodeURI(normalizedPath);
|
|
905
1108
|
const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
|
|
906
|
-
serverLogger2.debug(`
|
|
1109
|
+
serverLogger2.debug(`SOURCE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
|
|
907
1110
|
try {
|
|
908
1111
|
if (process.platform === "darwin") {
|
|
909
1112
|
(0, import_node_child_process2.execFileSync)("open", [uri]);
|
|
@@ -913,7 +1116,7 @@ function handleOpenFileRequest(body, serverState2) {
|
|
|
913
1116
|
(0, import_node_child_process2.execFileSync)("xdg-open", [uri]);
|
|
914
1117
|
}
|
|
915
1118
|
} catch (e) {
|
|
916
|
-
serverLogger2.error(`Failed to launch URI for
|
|
1119
|
+
serverLogger2.error(`Failed to launch URI for SOURCE_OPEN (${uri}):`, e);
|
|
917
1120
|
(0, import_launch_ide2.launchIDE)({
|
|
918
1121
|
file: absolutePath,
|
|
919
1122
|
line: body.line,
|
|
@@ -970,6 +1173,7 @@ function resolveProjectRoot() {
|
|
|
970
1173
|
|
|
971
1174
|
// src/server/index.ts
|
|
972
1175
|
var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
|
|
1176
|
+
var PORT_FILE_NAME = "inspecto.port.json";
|
|
973
1177
|
var serverState = {
|
|
974
1178
|
port: null,
|
|
975
1179
|
running: false,
|
|
@@ -978,6 +1182,42 @@ var serverState = {
|
|
|
978
1182
|
cwd: process.cwd()
|
|
979
1183
|
};
|
|
980
1184
|
var serverInstance = null;
|
|
1185
|
+
function getPortFilePath() {
|
|
1186
|
+
return import_node_path4.default.join(import_node_os2.default.tmpdir(), PORT_FILE_NAME);
|
|
1187
|
+
}
|
|
1188
|
+
function getProjectRootHash() {
|
|
1189
|
+
if (!serverState.projectRoot) return null;
|
|
1190
|
+
return import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
1191
|
+
}
|
|
1192
|
+
function readPortData(portFile) {
|
|
1193
|
+
if (!import_node_fs4.default.existsSync(portFile)) return {};
|
|
1194
|
+
try {
|
|
1195
|
+
return JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
|
|
1196
|
+
} catch {
|
|
1197
|
+
return {};
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
function writeProjectPort(port) {
|
|
1201
|
+
const rootHash = getProjectRootHash();
|
|
1202
|
+
if (!rootHash) return;
|
|
1203
|
+
const portFile = getPortFilePath();
|
|
1204
|
+
const portData = readPortData(portFile);
|
|
1205
|
+
portData[rootHash] = port;
|
|
1206
|
+
import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
1207
|
+
}
|
|
1208
|
+
function removeProjectPort() {
|
|
1209
|
+
const rootHash = getProjectRootHash();
|
|
1210
|
+
if (!rootHash) return;
|
|
1211
|
+
const portFile = getPortFilePath();
|
|
1212
|
+
if (!import_node_fs4.default.existsSync(portFile)) return;
|
|
1213
|
+
const portData = readPortData(portFile);
|
|
1214
|
+
delete portData[rootHash];
|
|
1215
|
+
if (Object.keys(portData).length === 0) {
|
|
1216
|
+
import_node_fs4.default.unlinkSync(portFile);
|
|
1217
|
+
} else {
|
|
1218
|
+
import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
981
1221
|
async function startServer() {
|
|
982
1222
|
if (serverState.running && serverState.port !== null) {
|
|
983
1223
|
return serverState.port;
|
|
@@ -1011,7 +1251,7 @@ async function startServer() {
|
|
|
1011
1251
|
});
|
|
1012
1252
|
});
|
|
1013
1253
|
await new Promise((resolve2, reject) => {
|
|
1014
|
-
serverInstance.listen(port, "
|
|
1254
|
+
serverInstance.listen(port, "0.0.0.0", () => {
|
|
1015
1255
|
serverInstance.unref();
|
|
1016
1256
|
resolve2();
|
|
1017
1257
|
});
|
|
@@ -1022,37 +1262,18 @@ async function startServer() {
|
|
|
1022
1262
|
});
|
|
1023
1263
|
serverState.port = port;
|
|
1024
1264
|
serverState.running = true;
|
|
1025
|
-
const portFile = import_node_path4.default.join(import_node_os2.default.tmpdir(), "inspecto.port.json");
|
|
1026
1265
|
try {
|
|
1027
|
-
|
|
1028
|
-
if (import_node_fs4.default.existsSync(portFile)) {
|
|
1029
|
-
try {
|
|
1030
|
-
portData = JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
|
|
1031
|
-
} catch (_e) {
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
1035
|
-
portData[rootHash] = port;
|
|
1036
|
-
import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
1266
|
+
writeProjectPort(port);
|
|
1037
1267
|
} catch (_e) {
|
|
1038
1268
|
serverLogger4.warn("Failed to write port file:", _e);
|
|
1039
1269
|
}
|
|
1040
1270
|
process.once("exit", () => {
|
|
1041
1271
|
try {
|
|
1042
|
-
|
|
1043
|
-
const portData = JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
|
|
1044
|
-
const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
|
|
1045
|
-
delete portData[rootHash];
|
|
1046
|
-
if (Object.keys(portData).length === 0) {
|
|
1047
|
-
import_node_fs4.default.unlinkSync(portFile);
|
|
1048
|
-
} else {
|
|
1049
|
-
import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1272
|
+
removeProjectPort();
|
|
1052
1273
|
} catch {
|
|
1053
1274
|
}
|
|
1054
1275
|
});
|
|
1055
|
-
serverLogger4.info(`server running at http://
|
|
1276
|
+
serverLogger4.info(`server running at http://0.0.0.0:${port}`);
|
|
1056
1277
|
return port;
|
|
1057
1278
|
}
|
|
1058
1279
|
async function readBody(req) {
|
|
@@ -1104,7 +1325,7 @@ async function handleRequest(url, req, res) {
|
|
|
1104
1325
|
}
|
|
1105
1326
|
return;
|
|
1106
1327
|
}
|
|
1107
|
-
if (pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
|
|
1328
|
+
if ((pathname === import_types2.INSPECTO_API_PATHS.SOURCE_OPEN || pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN) && req.method === "POST") {
|
|
1108
1329
|
let body;
|
|
1109
1330
|
try {
|
|
1110
1331
|
body = JSON.parse(await readBody(req));
|
|
@@ -1117,7 +1338,7 @@ async function handleRequest(url, req, res) {
|
|
|
1117
1338
|
handleOpenFileRequest(body, serverState);
|
|
1118
1339
|
} catch (err) {
|
|
1119
1340
|
serverLogger4.warn(
|
|
1120
|
-
`Security: Blocked path traversal attempt in
|
|
1341
|
+
`Security: Blocked path traversal attempt in SOURCE_OPEN: ${body.file}. Reason: ${err.message}`
|
|
1121
1342
|
);
|
|
1122
1343
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
1123
1344
|
res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
|
|
@@ -1191,6 +1412,212 @@ async function handleRequest(url, req, res) {
|
|
|
1191
1412
|
}
|
|
1192
1413
|
return;
|
|
1193
1414
|
}
|
|
1415
|
+
if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_CLAIM_NEXT && req.method === "POST") {
|
|
1416
|
+
try {
|
|
1417
|
+
const rawBody = await readBody(req);
|
|
1418
|
+
const body = rawBody ? JSON.parse(rawBody) : {};
|
|
1419
|
+
const timeoutMs = normalizeSessionClaimTimeout(
|
|
1420
|
+
body.timeoutMs === void 0 ? null : String(body.timeoutMs)
|
|
1421
|
+
);
|
|
1422
|
+
const result = await annotationSessionStore.claimNextSession({
|
|
1423
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
1424
|
+
});
|
|
1425
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1426
|
+
res.end(
|
|
1427
|
+
JSON.stringify({
|
|
1428
|
+
success: true,
|
|
1429
|
+
timedOut: result.timedOut,
|
|
1430
|
+
matchedExisting: result.matchedExisting,
|
|
1431
|
+
...result.event ? { event: result.event } : {},
|
|
1432
|
+
...result.session ? { session: result.session } : {}
|
|
1433
|
+
})
|
|
1434
|
+
);
|
|
1435
|
+
} catch (e) {
|
|
1436
|
+
serverLogger4.error(`Error parsing session claim request:`, e);
|
|
1437
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1438
|
+
res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
|
|
1439
|
+
}
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_EVENTS && req.method === "GET") {
|
|
1443
|
+
const statusParam = url.searchParams.getAll("status");
|
|
1444
|
+
const statuses = statusParam.length ? new Set(statusParam) : null;
|
|
1445
|
+
const sessionId = url.searchParams.get("sessionId")?.trim() || null;
|
|
1446
|
+
res.writeHead(200, {
|
|
1447
|
+
"Content-Type": "text/event-stream",
|
|
1448
|
+
"Cache-Control": "no-cache",
|
|
1449
|
+
Connection: "keep-alive"
|
|
1450
|
+
});
|
|
1451
|
+
res.write(`event: ready
|
|
1452
|
+
data: ${JSON.stringify({ ok: true })}
|
|
1453
|
+
|
|
1454
|
+
`);
|
|
1455
|
+
const unsubscribe = annotationSessionStore.subscribe((event) => {
|
|
1456
|
+
if (sessionId && event.session.id !== sessionId) {
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
if (statuses && !statuses.has(event.session.status)) {
|
|
1460
|
+
return;
|
|
1461
|
+
}
|
|
1462
|
+
res.write(formatSessionSseEvent(event));
|
|
1463
|
+
});
|
|
1464
|
+
req.on("close", () => {
|
|
1465
|
+
unsubscribe();
|
|
1466
|
+
res.end();
|
|
1467
|
+
});
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
if (pathname === import_types2.INSPECTO_API_PATHS.SESSIONS && req.method === "GET") {
|
|
1471
|
+
const statusParam = url.searchParams.getAll("status");
|
|
1472
|
+
const sessions = annotationSessionStore.listSessions(
|
|
1473
|
+
statusParam.length ? {
|
|
1474
|
+
status: statusParam
|
|
1475
|
+
} : void 0
|
|
1476
|
+
);
|
|
1477
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1478
|
+
res.end(JSON.stringify({ success: true, sessions }));
|
|
1479
|
+
return;
|
|
1480
|
+
}
|
|
1481
|
+
if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && req.method === "GET") {
|
|
1482
|
+
const sessionId = pathname.substring(import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1);
|
|
1483
|
+
const session = annotationSessionStore.getSession(sessionId);
|
|
1484
|
+
if (!session) {
|
|
1485
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1486
|
+
res.end(JSON.stringify({ success: false, error: "Session not found" }));
|
|
1487
|
+
return;
|
|
1488
|
+
}
|
|
1489
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1490
|
+
res.end(JSON.stringify({ success: true, session }));
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX) && req.method === "POST") {
|
|
1494
|
+
const sessionId = pathname.slice(
|
|
1495
|
+
import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
|
|
1496
|
+
-import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX.length
|
|
1497
|
+
);
|
|
1498
|
+
try {
|
|
1499
|
+
const rawBody = await readBody(req);
|
|
1500
|
+
const body = JSON.parse(rawBody);
|
|
1501
|
+
if (!isAnnotationThreadRole(body.role)) {
|
|
1502
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1503
|
+
res.end(JSON.stringify({ success: false, error: "Reply role is invalid." }));
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
if (!body.text?.trim()) {
|
|
1507
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1508
|
+
res.end(JSON.stringify({ success: false, error: "Reply text is required." }));
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1511
|
+
const session = annotationSessionStore.appendMessage(sessionId, {
|
|
1512
|
+
role: body.role,
|
|
1513
|
+
text: body.text.trim()
|
|
1514
|
+
});
|
|
1515
|
+
if (!session) {
|
|
1516
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1517
|
+
res.end(JSON.stringify({ success: false, error: "Session not found" }));
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1521
|
+
res.end(JSON.stringify({ success: true, session }));
|
|
1522
|
+
} catch (e) {
|
|
1523
|
+
serverLogger4.error(`Error parsing session reply request:`, e);
|
|
1524
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1525
|
+
res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
|
|
1526
|
+
}
|
|
1527
|
+
return;
|
|
1528
|
+
}
|
|
1529
|
+
if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX) && req.method === "POST") {
|
|
1530
|
+
const sessionId = pathname.slice(
|
|
1531
|
+
import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
|
|
1532
|
+
-import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX.length
|
|
1533
|
+
);
|
|
1534
|
+
try {
|
|
1535
|
+
const rawBody = await readBody(req);
|
|
1536
|
+
const body = rawBody ? JSON.parse(rawBody) : {};
|
|
1537
|
+
const message = body.message?.trim();
|
|
1538
|
+
const existingSession = annotationSessionStore.getSession(sessionId);
|
|
1539
|
+
if (!existingSession) {
|
|
1540
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1541
|
+
res.end(JSON.stringify({ success: false, error: "Session not found" }));
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
if (!message && !hasAgentReply(existingSession)) {
|
|
1545
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1546
|
+
res.end(
|
|
1547
|
+
JSON.stringify({
|
|
1548
|
+
success: false,
|
|
1549
|
+
error: "Resolve message is required until an agent reply is recorded."
|
|
1550
|
+
})
|
|
1551
|
+
);
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
if (message) {
|
|
1555
|
+
const repliedSession = annotationSessionStore.appendMessage(sessionId, {
|
|
1556
|
+
role: "agent",
|
|
1557
|
+
text: message
|
|
1558
|
+
});
|
|
1559
|
+
if (!repliedSession) {
|
|
1560
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1561
|
+
res.end(JSON.stringify({ success: false, error: "Session not found" }));
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
const session = annotationSessionStore.updateStatus(sessionId, "resolved");
|
|
1566
|
+
if (!session) {
|
|
1567
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1568
|
+
res.end(JSON.stringify({ success: false, error: "Session not found" }));
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1572
|
+
res.end(JSON.stringify({ success: true, session }));
|
|
1573
|
+
} catch (e) {
|
|
1574
|
+
serverLogger4.error(`Error parsing session resolve request:`, e);
|
|
1575
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1576
|
+
res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
|
|
1577
|
+
}
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX) && req.method === "POST") {
|
|
1581
|
+
const sessionId = pathname.slice(
|
|
1582
|
+
import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
|
|
1583
|
+
-import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX.length
|
|
1584
|
+
);
|
|
1585
|
+
try {
|
|
1586
|
+
const rawBody = await readBody(req);
|
|
1587
|
+
const body = rawBody ? JSON.parse(rawBody) : {};
|
|
1588
|
+
const message = body.message?.trim();
|
|
1589
|
+
const existingSession = annotationSessionStore.getSession(sessionId);
|
|
1590
|
+
if (!existingSession) {
|
|
1591
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1592
|
+
res.end(JSON.stringify({ success: false, error: "Session not found" }));
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
if (message) {
|
|
1596
|
+
const repliedSession = annotationSessionStore.appendMessage(sessionId, {
|
|
1597
|
+
role: "agent",
|
|
1598
|
+
text: message
|
|
1599
|
+
});
|
|
1600
|
+
if (!repliedSession) {
|
|
1601
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1602
|
+
res.end(JSON.stringify({ success: false, error: "Session not found" }));
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
const session = annotationSessionStore.updateStatus(sessionId, "dismissed");
|
|
1607
|
+
if (!session) {
|
|
1608
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1609
|
+
res.end(JSON.stringify({ success: false, error: "Session not found" }));
|
|
1610
|
+
return;
|
|
1611
|
+
}
|
|
1612
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1613
|
+
res.end(JSON.stringify({ success: true, session }));
|
|
1614
|
+
} catch (e) {
|
|
1615
|
+
serverLogger4.error(`Error parsing session dismiss request:`, e);
|
|
1616
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1617
|
+
res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
|
|
1618
|
+
}
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1194
1621
|
if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
|
|
1195
1622
|
const ticketId = pathname.substring(import_types2.INSPECTO_API_PATHS.AI_TICKET.length + 1);
|
|
1196
1623
|
const payloadStr = readTicket(ticketId);
|
|
@@ -1207,7 +1634,7 @@ async function handleRequest(url, req, res) {
|
|
|
1207
1634
|
res.end(JSON.stringify({ error: "not found" }));
|
|
1208
1635
|
}
|
|
1209
1636
|
async function dispatchToAi(req) {
|
|
1210
|
-
const { location, snippet, prompt
|
|
1637
|
+
const { location, snippet, prompt } = req;
|
|
1211
1638
|
const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
|
|
1212
1639
|
|
|
1213
1640
|
\`\`\`
|
|
@@ -1220,8 +1647,7 @@ ${snippet}
|
|
|
1220
1647
|
filePath: location.file,
|
|
1221
1648
|
line: location.line,
|
|
1222
1649
|
column: location.column,
|
|
1223
|
-
snippet
|
|
1224
|
-
...screenshotContext ? { screenshotContext } : {}
|
|
1650
|
+
snippet
|
|
1225
1651
|
});
|
|
1226
1652
|
}
|
|
1227
1653
|
function getBatchDispatchStatusCode(errorCode, success) {
|
|
@@ -1230,6 +1656,21 @@ function getBatchDispatchStatusCode(errorCode, success) {
|
|
|
1230
1656
|
if (errorCode === "FORBIDDEN_PATH") return 403;
|
|
1231
1657
|
return 500;
|
|
1232
1658
|
}
|
|
1659
|
+
function isAnnotationThreadRole(value) {
|
|
1660
|
+
return value === "user" || value === "agent" || value === "system";
|
|
1661
|
+
}
|
|
1662
|
+
function formatSessionSseEvent(event) {
|
|
1663
|
+
return `event: ${event.type}
|
|
1664
|
+
data: ${JSON.stringify(event)}
|
|
1665
|
+
|
|
1666
|
+
`;
|
|
1667
|
+
}
|
|
1668
|
+
function normalizeSessionClaimTimeout(value) {
|
|
1669
|
+
if (!value?.trim()) return 3e4;
|
|
1670
|
+
const parsed = Number.parseInt(value, 10);
|
|
1671
|
+
if (!Number.isFinite(parsed)) return 3e4;
|
|
1672
|
+
return Math.max(0, Math.min(parsed, 3e5));
|
|
1673
|
+
}
|
|
1233
1674
|
|
|
1234
1675
|
// src/injectors/utils.ts
|
|
1235
1676
|
var import_node_module = require("module");
|
|
@@ -1252,13 +1693,13 @@ var resolveClientModule = () => {
|
|
|
1252
1693
|
function getWebpackHtmlScript(serverPort2) {
|
|
1253
1694
|
return `
|
|
1254
1695
|
window.__AI_INSPECTOR_PORT__ = ${serverPort2};
|
|
1255
|
-
window.addEventListener('load', () => {
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
});
|
|
1696
|
+
window.addEventListener('load', () => {
|
|
1697
|
+
if (window.InspectoClient) {
|
|
1698
|
+
window.InspectoClient.mountInspector({
|
|
1699
|
+
serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
});
|
|
1262
1703
|
`;
|
|
1263
1704
|
}
|
|
1264
1705
|
|