@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.
Files changed (43) hide show
  1. package/dist/astro.cjs +519 -75
  2. package/dist/astro.cjs.map +1 -1
  3. package/dist/astro.js +519 -75
  4. package/dist/astro.js.map +1 -1
  5. package/dist/index.cjs +518 -74
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.cts +1 -0
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.js +518 -74
  10. package/dist/index.js.map +1 -1
  11. package/dist/legacy/rspack/index.cjs +513 -72
  12. package/dist/legacy/rspack/index.cjs.map +1 -1
  13. package/dist/legacy/rspack/index.js +513 -72
  14. package/dist/legacy/rspack/index.js.map +1 -1
  15. package/dist/legacy/rspack/loader.cjs +3 -0
  16. package/dist/legacy/rspack/loader.cjs.map +1 -1
  17. package/dist/legacy/rspack/loader.js +3 -0
  18. package/dist/legacy/rspack/loader.js.map +1 -1
  19. package/dist/legacy/webpack4/index.cjs +513 -72
  20. package/dist/legacy/webpack4/index.cjs.map +1 -1
  21. package/dist/legacy/webpack4/index.js +513 -72
  22. package/dist/legacy/webpack4/index.js.map +1 -1
  23. package/dist/legacy/webpack4/loader.cjs +3 -0
  24. package/dist/legacy/webpack4/loader.cjs.map +1 -1
  25. package/dist/legacy/webpack4/loader.js +3 -0
  26. package/dist/legacy/webpack4/loader.js.map +1 -1
  27. package/dist/rollup.cjs +518 -74
  28. package/dist/rollup.cjs.map +1 -1
  29. package/dist/rollup.js +518 -74
  30. package/dist/rollup.js.map +1 -1
  31. package/dist/rspack.cjs +518 -74
  32. package/dist/rspack.cjs.map +1 -1
  33. package/dist/rspack.js +518 -74
  34. package/dist/rspack.js.map +1 -1
  35. package/dist/vite.cjs +518 -74
  36. package/dist/vite.cjs.map +1 -1
  37. package/dist/vite.js +518 -74
  38. package/dist/vite.js.map +1 -1
  39. package/dist/webpack.cjs +518 -74
  40. package/dist/webpack.cjs.map +1 -1
  41. package/dist/webpack.js +518 -74
  42. package/dist/webpack.js.map +1 -1
  43. 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 runtime = resolvePromptDispatchRuntime(state);
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
- ...batch.screenshotContext ? { screenshotContext: batch.screenshotContext } : {}
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 appendScreenshotContextSection(
749
- appendCssContextSection(
750
- appendRuntimeContextSection(prompt, batch.runtimeContext),
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
- theme: userConfig["inspector.theme"] ?? "auto",
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
- `IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
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(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
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 IDE_OPEN (${uri}):`, e);
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, "127.0.0.1", () => {
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
- let portData = {};
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
- if (import_node_fs4.default.existsSync(portFile)) {
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://127.0.0.1:${port}`);
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 IDE_OPEN: ${body.file}. Reason: ${err.message}`
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, screenshotContext } = req;
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
- if (window.InspectoClient) {
1257
- window.InspectoClient.mountInspector({
1258
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
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