@inspecto-dev/plugin 0.3.8 → 0.3.10

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 (45) hide show
  1. package/dist/astro.cjs +572 -86
  2. package/dist/astro.cjs.map +1 -1
  3. package/dist/astro.d.cts +1 -1
  4. package/dist/astro.d.ts +1 -1
  5. package/dist/astro.js +571 -85
  6. package/dist/astro.js.map +1 -1
  7. package/dist/index.cjs +559 -83
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +1 -0
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.js +558 -82
  12. package/dist/index.js.map +1 -1
  13. package/dist/legacy/rspack/index.cjs +526 -74
  14. package/dist/legacy/rspack/index.cjs.map +1 -1
  15. package/dist/legacy/rspack/index.js +526 -74
  16. package/dist/legacy/rspack/index.js.map +1 -1
  17. package/dist/legacy/rspack/loader.cjs +4 -1
  18. package/dist/legacy/rspack/loader.cjs.map +1 -1
  19. package/dist/legacy/rspack/loader.js +3 -0
  20. package/dist/legacy/rspack/loader.js.map +1 -1
  21. package/dist/legacy/webpack4/index.cjs +526 -74
  22. package/dist/legacy/webpack4/index.cjs.map +1 -1
  23. package/dist/legacy/webpack4/index.js +526 -74
  24. package/dist/legacy/webpack4/index.js.map +1 -1
  25. package/dist/legacy/webpack4/loader.cjs +4 -1
  26. package/dist/legacy/webpack4/loader.cjs.map +1 -1
  27. package/dist/legacy/webpack4/loader.js +3 -0
  28. package/dist/legacy/webpack4/loader.js.map +1 -1
  29. package/dist/rollup.cjs +559 -83
  30. package/dist/rollup.cjs.map +1 -1
  31. package/dist/rollup.js +558 -82
  32. package/dist/rollup.js.map +1 -1
  33. package/dist/rspack.cjs +559 -83
  34. package/dist/rspack.cjs.map +1 -1
  35. package/dist/rspack.js +558 -82
  36. package/dist/rspack.js.map +1 -1
  37. package/dist/vite.cjs +559 -83
  38. package/dist/vite.cjs.map +1 -1
  39. package/dist/vite.js +558 -82
  40. package/dist/vite.js.map +1 -1
  41. package/dist/webpack.cjs +559 -83
  42. package/dist/webpack.cjs.map +1 -1
  43. package/dist/webpack.js +558 -82
  44. package/dist/webpack.js.map +1 -1
  45. package/package.json +8 -12
@@ -36,7 +36,7 @@ __export(rspack_exports, {
36
36
  });
37
37
  module.exports = __toCommonJS(rspack_exports);
38
38
 
39
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.10_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/cjs_shims.js
39
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.14_typescript@5.9.3_yaml@2.8.4/node_modules/tsup/assets/cjs_shims.js
40
40
  var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
41
41
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
42
42
 
@@ -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,
@@ -968,8 +1171,18 @@ function resolveProjectRoot() {
968
1171
  return gitRoot;
969
1172
  }
970
1173
 
1174
+ // src/server/server-url.ts
1175
+ function resolveServerHost(cwd, configRoot) {
1176
+ const userConfig = loadUserConfigSync(false, cwd, configRoot);
1177
+ const configuredHost = userConfig["server.host"]?.trim();
1178
+ if (configuredHost) return configuredHost;
1179
+ if (process.env["VITEST"]) return "127.0.0.1";
1180
+ return "127.0.0.1";
1181
+ }
1182
+
971
1183
  // src/server/index.ts
972
1184
  var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
1185
+ var PORT_FILE_NAME = "inspecto.port.json";
973
1186
  var serverState = {
974
1187
  port: null,
975
1188
  running: false,
@@ -978,6 +1191,42 @@ var serverState = {
978
1191
  cwd: process.cwd()
979
1192
  };
980
1193
  var serverInstance = null;
1194
+ function getPortFilePath() {
1195
+ return import_node_path4.default.join(import_node_os2.default.tmpdir(), PORT_FILE_NAME);
1196
+ }
1197
+ function getProjectRootHash() {
1198
+ if (!serverState.projectRoot) return null;
1199
+ return import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1200
+ }
1201
+ function readPortData(portFile) {
1202
+ if (!import_node_fs4.default.existsSync(portFile)) return {};
1203
+ try {
1204
+ return JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1205
+ } catch {
1206
+ return {};
1207
+ }
1208
+ }
1209
+ function writeProjectPort(port) {
1210
+ const rootHash = getProjectRootHash();
1211
+ if (!rootHash) return;
1212
+ const portFile = getPortFilePath();
1213
+ const portData = readPortData(portFile);
1214
+ portData[rootHash] = port;
1215
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1216
+ }
1217
+ function removeProjectPort() {
1218
+ const rootHash = getProjectRootHash();
1219
+ if (!rootHash) return;
1220
+ const portFile = getPortFilePath();
1221
+ if (!import_node_fs4.default.existsSync(portFile)) return;
1222
+ const portData = readPortData(portFile);
1223
+ delete portData[rootHash];
1224
+ if (Object.keys(portData).length === 0) {
1225
+ import_node_fs4.default.unlinkSync(portFile);
1226
+ } else {
1227
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1228
+ }
1229
+ }
981
1230
  async function startServer() {
982
1231
  if (serverState.running && serverState.port !== null) {
983
1232
  return serverState.port;
@@ -985,6 +1234,7 @@ async function startServer() {
985
1234
  serverState.projectRoot = resolveProjectRoot();
986
1235
  serverState.configRoot = serverState.projectRoot;
987
1236
  serverState.cwd = process.cwd();
1237
+ const serverHost = resolveServerHost(serverState.cwd, serverState.configRoot);
988
1238
  import_portfinder.default.basePort = 5678;
989
1239
  const port = await import_portfinder.default.getPortPromise();
990
1240
  watchConfig(
@@ -1011,7 +1261,7 @@ async function startServer() {
1011
1261
  });
1012
1262
  });
1013
1263
  await new Promise((resolve2, reject) => {
1014
- serverInstance.listen(port, "127.0.0.1", () => {
1264
+ serverInstance.listen(port, serverHost, () => {
1015
1265
  serverInstance.unref();
1016
1266
  resolve2();
1017
1267
  });
@@ -1022,37 +1272,18 @@ async function startServer() {
1022
1272
  });
1023
1273
  serverState.port = port;
1024
1274
  serverState.running = true;
1025
- const portFile = import_node_path4.default.join(import_node_os2.default.tmpdir(), "inspecto.port.json");
1026
1275
  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");
1276
+ writeProjectPort(port);
1037
1277
  } catch (_e) {
1038
1278
  serverLogger4.warn("Failed to write port file:", _e);
1039
1279
  }
1040
1280
  process.once("exit", () => {
1041
1281
  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
- }
1282
+ removeProjectPort();
1052
1283
  } catch {
1053
1284
  }
1054
1285
  });
1055
- serverLogger4.info(`server running at http://127.0.0.1:${port}`);
1286
+ serverLogger4.info(`server running at http://${serverHost}:${port}`);
1056
1287
  return port;
1057
1288
  }
1058
1289
  async function readBody(req) {
@@ -1104,7 +1335,7 @@ async function handleRequest(url, req, res) {
1104
1335
  }
1105
1336
  return;
1106
1337
  }
1107
- if (pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
1338
+ if ((pathname === import_types2.INSPECTO_API_PATHS.SOURCE_OPEN || pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN) && req.method === "POST") {
1108
1339
  let body;
1109
1340
  try {
1110
1341
  body = JSON.parse(await readBody(req));
@@ -1117,7 +1348,7 @@ async function handleRequest(url, req, res) {
1117
1348
  handleOpenFileRequest(body, serverState);
1118
1349
  } catch (err) {
1119
1350
  serverLogger4.warn(
1120
- `Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}. Reason: ${err.message}`
1351
+ `Security: Blocked path traversal attempt in SOURCE_OPEN: ${body.file}. Reason: ${err.message}`
1121
1352
  );
1122
1353
  res.writeHead(403, { "Content-Type": "application/json" });
1123
1354
  res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
@@ -1191,6 +1422,212 @@ async function handleRequest(url, req, res) {
1191
1422
  }
1192
1423
  return;
1193
1424
  }
1425
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_CLAIM_NEXT && req.method === "POST") {
1426
+ try {
1427
+ const rawBody = await readBody(req);
1428
+ const body = rawBody ? JSON.parse(rawBody) : {};
1429
+ const timeoutMs = normalizeSessionClaimTimeout(
1430
+ body.timeoutMs === void 0 ? null : String(body.timeoutMs)
1431
+ );
1432
+ const result = await annotationSessionStore.claimNextSession({
1433
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
1434
+ });
1435
+ res.writeHead(200, { "Content-Type": "application/json" });
1436
+ res.end(
1437
+ JSON.stringify({
1438
+ success: true,
1439
+ timedOut: result.timedOut,
1440
+ matchedExisting: result.matchedExisting,
1441
+ ...result.event ? { event: result.event } : {},
1442
+ ...result.session ? { session: result.session } : {}
1443
+ })
1444
+ );
1445
+ } catch (e) {
1446
+ serverLogger4.error(`Error parsing session claim request:`, e);
1447
+ res.writeHead(400, { "Content-Type": "application/json" });
1448
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1449
+ }
1450
+ return;
1451
+ }
1452
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_EVENTS && req.method === "GET") {
1453
+ const statusParam = url.searchParams.getAll("status");
1454
+ const statuses = statusParam.length ? new Set(statusParam) : null;
1455
+ const sessionId = url.searchParams.get("sessionId")?.trim() || null;
1456
+ res.writeHead(200, {
1457
+ "Content-Type": "text/event-stream",
1458
+ "Cache-Control": "no-cache",
1459
+ Connection: "keep-alive"
1460
+ });
1461
+ res.write(`event: ready
1462
+ data: ${JSON.stringify({ ok: true })}
1463
+
1464
+ `);
1465
+ const unsubscribe = annotationSessionStore.subscribe((event) => {
1466
+ if (sessionId && event.session.id !== sessionId) {
1467
+ return;
1468
+ }
1469
+ if (statuses && !statuses.has(event.session.status)) {
1470
+ return;
1471
+ }
1472
+ res.write(formatSessionSseEvent(event));
1473
+ });
1474
+ req.on("close", () => {
1475
+ unsubscribe();
1476
+ res.end();
1477
+ });
1478
+ return;
1479
+ }
1480
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSIONS && req.method === "GET") {
1481
+ const statusParam = url.searchParams.getAll("status");
1482
+ const sessions = annotationSessionStore.listSessions(
1483
+ statusParam.length ? {
1484
+ status: statusParam
1485
+ } : void 0
1486
+ );
1487
+ res.writeHead(200, { "Content-Type": "application/json" });
1488
+ res.end(JSON.stringify({ success: true, sessions }));
1489
+ return;
1490
+ }
1491
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && req.method === "GET") {
1492
+ const sessionId = pathname.substring(import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1);
1493
+ const session = annotationSessionStore.getSession(sessionId);
1494
+ if (!session) {
1495
+ res.writeHead(404, { "Content-Type": "application/json" });
1496
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1497
+ return;
1498
+ }
1499
+ res.writeHead(200, { "Content-Type": "application/json" });
1500
+ res.end(JSON.stringify({ success: true, session }));
1501
+ return;
1502
+ }
1503
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX) && req.method === "POST") {
1504
+ const sessionId = pathname.slice(
1505
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
1506
+ -import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX.length
1507
+ );
1508
+ try {
1509
+ const rawBody = await readBody(req);
1510
+ const body = JSON.parse(rawBody);
1511
+ if (!isAnnotationThreadRole(body.role)) {
1512
+ res.writeHead(400, { "Content-Type": "application/json" });
1513
+ res.end(JSON.stringify({ success: false, error: "Reply role is invalid." }));
1514
+ return;
1515
+ }
1516
+ if (!body.text?.trim()) {
1517
+ res.writeHead(400, { "Content-Type": "application/json" });
1518
+ res.end(JSON.stringify({ success: false, error: "Reply text is required." }));
1519
+ return;
1520
+ }
1521
+ const session = annotationSessionStore.appendMessage(sessionId, {
1522
+ role: body.role,
1523
+ text: body.text.trim()
1524
+ });
1525
+ if (!session) {
1526
+ res.writeHead(404, { "Content-Type": "application/json" });
1527
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1528
+ return;
1529
+ }
1530
+ res.writeHead(200, { "Content-Type": "application/json" });
1531
+ res.end(JSON.stringify({ success: true, session }));
1532
+ } catch (e) {
1533
+ serverLogger4.error(`Error parsing session reply request:`, e);
1534
+ res.writeHead(400, { "Content-Type": "application/json" });
1535
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1536
+ }
1537
+ return;
1538
+ }
1539
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX) && req.method === "POST") {
1540
+ const sessionId = pathname.slice(
1541
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
1542
+ -import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX.length
1543
+ );
1544
+ try {
1545
+ const rawBody = await readBody(req);
1546
+ const body = rawBody ? JSON.parse(rawBody) : {};
1547
+ const message = body.message?.trim();
1548
+ const existingSession = annotationSessionStore.getSession(sessionId);
1549
+ if (!existingSession) {
1550
+ res.writeHead(404, { "Content-Type": "application/json" });
1551
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1552
+ return;
1553
+ }
1554
+ if (!message && !hasAgentReply(existingSession)) {
1555
+ res.writeHead(400, { "Content-Type": "application/json" });
1556
+ res.end(
1557
+ JSON.stringify({
1558
+ success: false,
1559
+ error: "Resolve message is required until an agent reply is recorded."
1560
+ })
1561
+ );
1562
+ return;
1563
+ }
1564
+ if (message) {
1565
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
1566
+ role: "agent",
1567
+ text: message
1568
+ });
1569
+ if (!repliedSession) {
1570
+ res.writeHead(404, { "Content-Type": "application/json" });
1571
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1572
+ return;
1573
+ }
1574
+ }
1575
+ const session = annotationSessionStore.updateStatus(sessionId, "resolved");
1576
+ if (!session) {
1577
+ res.writeHead(404, { "Content-Type": "application/json" });
1578
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1579
+ return;
1580
+ }
1581
+ res.writeHead(200, { "Content-Type": "application/json" });
1582
+ res.end(JSON.stringify({ success: true, session }));
1583
+ } catch (e) {
1584
+ serverLogger4.error(`Error parsing session resolve request:`, e);
1585
+ res.writeHead(400, { "Content-Type": "application/json" });
1586
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1587
+ }
1588
+ return;
1589
+ }
1590
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX) && req.method === "POST") {
1591
+ const sessionId = pathname.slice(
1592
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
1593
+ -import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX.length
1594
+ );
1595
+ try {
1596
+ const rawBody = await readBody(req);
1597
+ const body = rawBody ? JSON.parse(rawBody) : {};
1598
+ const message = body.message?.trim();
1599
+ const existingSession = annotationSessionStore.getSession(sessionId);
1600
+ if (!existingSession) {
1601
+ res.writeHead(404, { "Content-Type": "application/json" });
1602
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1603
+ return;
1604
+ }
1605
+ if (message) {
1606
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
1607
+ role: "agent",
1608
+ text: message
1609
+ });
1610
+ if (!repliedSession) {
1611
+ res.writeHead(404, { "Content-Type": "application/json" });
1612
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1613
+ return;
1614
+ }
1615
+ }
1616
+ const session = annotationSessionStore.updateStatus(sessionId, "dismissed");
1617
+ if (!session) {
1618
+ res.writeHead(404, { "Content-Type": "application/json" });
1619
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1620
+ return;
1621
+ }
1622
+ res.writeHead(200, { "Content-Type": "application/json" });
1623
+ res.end(JSON.stringify({ success: true, session }));
1624
+ } catch (e) {
1625
+ serverLogger4.error(`Error parsing session dismiss request:`, e);
1626
+ res.writeHead(400, { "Content-Type": "application/json" });
1627
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1628
+ }
1629
+ return;
1630
+ }
1194
1631
  if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
1195
1632
  const ticketId = pathname.substring(import_types2.INSPECTO_API_PATHS.AI_TICKET.length + 1);
1196
1633
  const payloadStr = readTicket(ticketId);
@@ -1207,7 +1644,7 @@ async function handleRequest(url, req, res) {
1207
1644
  res.end(JSON.stringify({ error: "not found" }));
1208
1645
  }
1209
1646
  async function dispatchToAi(req) {
1210
- const { location, snippet, prompt, screenshotContext } = req;
1647
+ const { location, snippet, prompt } = req;
1211
1648
  const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
1212
1649
 
1213
1650
  \`\`\`
@@ -1220,8 +1657,7 @@ ${snippet}
1220
1657
  filePath: location.file,
1221
1658
  line: location.line,
1222
1659
  column: location.column,
1223
- snippet,
1224
- ...screenshotContext ? { screenshotContext } : {}
1660
+ snippet
1225
1661
  });
1226
1662
  }
1227
1663
  function getBatchDispatchStatusCode(errorCode, success) {
@@ -1230,6 +1666,21 @@ function getBatchDispatchStatusCode(errorCode, success) {
1230
1666
  if (errorCode === "FORBIDDEN_PATH") return 403;
1231
1667
  return 500;
1232
1668
  }
1669
+ function isAnnotationThreadRole(value) {
1670
+ return value === "user" || value === "agent" || value === "system";
1671
+ }
1672
+ function formatSessionSseEvent(event) {
1673
+ return `event: ${event.type}
1674
+ data: ${JSON.stringify(event)}
1675
+
1676
+ `;
1677
+ }
1678
+ function normalizeSessionClaimTimeout(value) {
1679
+ if (!value?.trim()) return 3e4;
1680
+ const parsed = Number.parseInt(value, 10);
1681
+ if (!Number.isFinite(parsed)) return 3e4;
1682
+ return Math.max(0, Math.min(parsed, 3e5));
1683
+ }
1233
1684
 
1234
1685
  // src/injectors/utils.ts
1235
1686
  var import_node_module = require("module");
@@ -1249,16 +1700,17 @@ var resolveClientModule = () => {
1249
1700
  };
1250
1701
 
1251
1702
  // src/injectors/webpack.ts
1252
- function getWebpackHtmlScript(serverPort2) {
1703
+ function getWebpackHtmlScript(serverPort2, publicServerUrl) {
1253
1704
  return `
1254
1705
  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
- });
1706
+ window.__AI_INSPECTOR_SERVER_URL__ = '${publicServerUrl ?? `http://127.0.0.1:${serverPort2}`}';
1707
+ window.addEventListener('load', () => {
1708
+ if (window.InspectoClient) {
1709
+ window.InspectoClient.mountInspector({
1710
+ serverUrl: window.__AI_INSPECTOR_SERVER_URL__,
1711
+ });
1712
+ }
1713
+ });
1262
1714
  `;
1263
1715
  }
1264
1716