@inspecto-dev/plugin 0.3.7 → 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 (54) hide show
  1. package/README.md +2 -1
  2. package/dist/astro.cjs +2687 -0
  3. package/dist/astro.cjs.map +1 -0
  4. package/dist/astro.d.cts +17 -0
  5. package/dist/astro.d.ts +17 -0
  6. package/dist/astro.js +2656 -0
  7. package/dist/astro.js.map +1 -0
  8. package/dist/index.cjs +932 -87
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +22 -1
  11. package/dist/index.d.ts +22 -1
  12. package/dist/index.js +930 -87
  13. package/dist/index.js.map +1 -1
  14. package/dist/legacy/rspack/index.cjs +530 -89
  15. package/dist/legacy/rspack/index.cjs.map +1 -1
  16. package/dist/legacy/rspack/index.js +530 -89
  17. package/dist/legacy/rspack/index.js.map +1 -1
  18. package/dist/legacy/rspack/loader.cjs +407 -3
  19. package/dist/legacy/rspack/loader.cjs.map +1 -1
  20. package/dist/legacy/rspack/loader.js +403 -3
  21. package/dist/legacy/rspack/loader.js.map +1 -1
  22. package/dist/legacy/webpack4/index.cjs +519 -78
  23. package/dist/legacy/webpack4/index.cjs.map +1 -1
  24. package/dist/legacy/webpack4/index.js +519 -78
  25. package/dist/legacy/webpack4/index.js.map +1 -1
  26. package/dist/legacy/webpack4/loader.cjs +407 -3
  27. package/dist/legacy/webpack4/loader.cjs.map +1 -1
  28. package/dist/legacy/webpack4/loader.js +403 -3
  29. package/dist/legacy/webpack4/loader.js.map +1 -1
  30. package/dist/rollup.cjs +932 -87
  31. package/dist/rollup.cjs.map +1 -1
  32. package/dist/rollup.d.cts +1 -1
  33. package/dist/rollup.d.ts +1 -1
  34. package/dist/rollup.js +930 -87
  35. package/dist/rollup.js.map +1 -1
  36. package/dist/rspack.cjs +932 -87
  37. package/dist/rspack.cjs.map +1 -1
  38. package/dist/rspack.d.cts +1 -1
  39. package/dist/rspack.d.ts +1 -1
  40. package/dist/rspack.js +930 -87
  41. package/dist/rspack.js.map +1 -1
  42. package/dist/vite.cjs +932 -87
  43. package/dist/vite.cjs.map +1 -1
  44. package/dist/vite.d.cts +1 -1
  45. package/dist/vite.d.ts +1 -1
  46. package/dist/vite.js +930 -87
  47. package/dist/vite.js.map +1 -1
  48. package/dist/webpack.cjs +932 -87
  49. package/dist/webpack.cjs.map +1 -1
  50. package/dist/webpack.d.cts +1 -1
  51. package/dist/webpack.d.ts +1 -1
  52. package/dist/webpack.js +930 -87
  53. package/dist/webpack.js.map +1 -1
  54. package/package.json +13 -10
@@ -35,7 +35,7 @@ __export(webpack4_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(webpack4_exports);
37
37
 
38
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.9_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/cjs_shims.js
38
+ // ../../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
39
  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;
40
40
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
41
41
 
@@ -43,13 +43,13 @@ var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
43
43
  function getWebpackHtmlScript(serverPort) {
44
44
  return `
45
45
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
46
- window.addEventListener('load', () => {
47
- if (window.InspectoClient) {
48
- window.InspectoClient.mountInspector({
49
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
50
- });
51
- }
52
- });
46
+ window.addEventListener('load', () => {
47
+ if (window.InspectoClient) {
48
+ window.InspectoClient.mountInspector({
49
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
50
+ });
51
+ }
52
+ });
53
53
  `;
54
54
  }
55
55
 
@@ -347,7 +347,7 @@ function readJsonSafely(filePath) {
347
347
  }
348
348
  return null;
349
349
  }
350
- function resolveTargetTool(config, ide = "vscode") {
350
+ function resolveTargetTool(config, _ide = "vscode") {
351
351
  const defaultProvider = config["provider.default"];
352
352
  if (defaultProvider) {
353
353
  const tool = defaultProvider.split(".")[0];
@@ -499,7 +499,7 @@ function watchConfig(onReload, cwd = process.cwd(), gitRoot) {
499
499
  });
500
500
  watcher.unref();
501
501
  watchers.push(watcher);
502
- } catch (e) {
502
+ } catch (_e) {
503
503
  }
504
504
  }
505
505
  }
@@ -567,7 +567,6 @@ function dispatchPromptThroughIde(runtime, payload) {
567
567
  line: payload.line,
568
568
  column: payload.column,
569
569
  snippet: payload.snippet,
570
- ...payload.screenshotContext ? { screenshotContext: payload.screenshotContext } : {},
571
570
  overrides: runtime.overrides,
572
571
  autoSend: runtime.autoSend
573
572
  });
@@ -701,6 +700,188 @@ function assertPathWithinIdeOpenScope(file, projectRoot) {
701
700
  }
702
701
  }
703
702
 
703
+ // src/server/session-store.ts
704
+ var DEFAULT_STATUS = "pending";
705
+ function createAnnotationSessionStore(options = {}) {
706
+ const sessions = /* @__PURE__ */ new Map();
707
+ const listeners = /* @__PURE__ */ new Set();
708
+ const now = options.now ?? (() => Date.now());
709
+ const createId = options.createId ?? createRandomId;
710
+ function findNewestMatchingSession(statuses) {
711
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
712
+ }
713
+ function updateSessionStatus(id, status) {
714
+ const session = sessions.get(id);
715
+ if (!session) return null;
716
+ const timestamp = now();
717
+ session.status = status;
718
+ session.updatedAt = timestamp;
719
+ if (status === "acknowledged") {
720
+ session.acknowledgedAt = timestamp;
721
+ }
722
+ if (status === "resolved") {
723
+ session.resolvedAt = timestamp;
724
+ }
725
+ emit({ type: "session-status-updated", session });
726
+ return cloneSession(session);
727
+ }
728
+ function claimSession(id, statuses) {
729
+ const session = sessions.get(id);
730
+ if (!session || statuses && !statuses.has(session.status)) return null;
731
+ if (session.status === "acknowledged") return cloneSession(session);
732
+ return updateSessionStatus(id, "acknowledged");
733
+ }
734
+ function emit(event) {
735
+ const snapshot = cloneSession(event.session);
736
+ for (const listener of listeners) {
737
+ listener({ type: event.type, session: snapshot });
738
+ }
739
+ }
740
+ const store = {
741
+ createSession(input) {
742
+ const timestamp = now();
743
+ const session = {
744
+ id: createId(),
745
+ instruction: input.instruction?.trim() ?? "",
746
+ annotations: cloneArray(input.annotations),
747
+ ...input.deliveryMode ? { deliveryMode: input.deliveryMode } : {},
748
+ status: DEFAULT_STATUS,
749
+ messages: cloneArray(input.messages ?? []),
750
+ createdAt: timestamp,
751
+ updatedAt: timestamp,
752
+ ...input.runtimeContext ? { runtimeContext: cloneValue(input.runtimeContext) } : {},
753
+ ...input.cssContextPrompt?.trim() ? { cssContextPrompt: input.cssContextPrompt.trim() } : {},
754
+ ...input.pageUrl ? { pageUrl: input.pageUrl } : {},
755
+ ...input.route ? { route: input.route } : {}
756
+ };
757
+ sessions.set(session.id, session);
758
+ emit({ type: "session-created", session });
759
+ return cloneSession(session);
760
+ },
761
+ getSession(id) {
762
+ const session = sessions.get(id);
763
+ return session ? cloneSession(session) : null;
764
+ },
765
+ listSessions(options2 = {}) {
766
+ const statuses = normalizeStatuses(options2.status);
767
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt).map((session) => cloneSession(session));
768
+ },
769
+ async claimNextSession(options2 = {}) {
770
+ const statuses = normalizeStatuses(DEFAULT_STATUS);
771
+ const existingSession = findNewestMatchingSession(statuses);
772
+ if (existingSession) {
773
+ return {
774
+ session: claimSession(existingSession.id, statuses),
775
+ timedOut: false,
776
+ matchedExisting: true
777
+ };
778
+ }
779
+ const timeoutMs = normalizeTimeoutMs(options2.timeoutMs);
780
+ if (timeoutMs === 0) {
781
+ return {
782
+ session: null,
783
+ timedOut: true,
784
+ matchedExisting: false
785
+ };
786
+ }
787
+ return await new Promise((resolve2) => {
788
+ let settled = false;
789
+ let timeout = null;
790
+ const finish = (result) => {
791
+ if (settled) return;
792
+ settled = true;
793
+ unsubscribe();
794
+ if (timeout) {
795
+ clearTimeout(timeout);
796
+ }
797
+ resolve2(result);
798
+ };
799
+ const unsubscribe = this.subscribe((event) => {
800
+ const session = claimSession(event.session.id, statuses);
801
+ if (!session) return;
802
+ finish({
803
+ session,
804
+ timedOut: false,
805
+ matchedExisting: false,
806
+ event: event.type
807
+ });
808
+ });
809
+ if (timeoutMs !== null) {
810
+ timeout = setTimeout(() => {
811
+ finish({
812
+ session: null,
813
+ timedOut: true,
814
+ matchedExisting: false
815
+ });
816
+ }, timeoutMs);
817
+ }
818
+ });
819
+ },
820
+ appendMessage(id, input) {
821
+ const session = sessions.get(id);
822
+ if (!session) return null;
823
+ const timestamp = now();
824
+ session.messages.push({
825
+ id: createId(),
826
+ role: input.role,
827
+ text: input.text,
828
+ createdAt: timestamp
829
+ });
830
+ session.updatedAt = timestamp;
831
+ if (input.role === "agent" && isPendingLikeStatus(session.status)) {
832
+ session.status = "in_progress";
833
+ }
834
+ emit({ type: "session-message-appended", session });
835
+ return cloneSession(session);
836
+ },
837
+ updateStatus(id, status) {
838
+ return updateSessionStatus(id, status);
839
+ },
840
+ subscribe(listener) {
841
+ listeners.add(listener);
842
+ return () => {
843
+ listeners.delete(listener);
844
+ };
845
+ },
846
+ clear() {
847
+ sessions.clear();
848
+ listeners.clear();
849
+ }
850
+ };
851
+ return store;
852
+ }
853
+ var annotationSessionStore = createAnnotationSessionStore();
854
+ function normalizeStatuses(status) {
855
+ if (!status) return null;
856
+ return new Set(Array.isArray(status) ? status : [status]);
857
+ }
858
+ function normalizeTimeoutMs(value) {
859
+ if (value === void 0) return null;
860
+ if (!Number.isFinite(value)) return 0;
861
+ return Math.max(0, Math.floor(value));
862
+ }
863
+ function isPendingLikeStatus(status) {
864
+ return status === "pending" || status === "acknowledged";
865
+ }
866
+ function hasAgentReply(session) {
867
+ return session.messages.some((message) => message.role === "agent" && Boolean(message.text?.trim()));
868
+ }
869
+ function createRandomId() {
870
+ return `annotation-session-${Math.random().toString(36).slice(2, 10)}`;
871
+ }
872
+ function cloneSession(session) {
873
+ return cloneValue(session);
874
+ }
875
+ function cloneArray(value) {
876
+ return cloneValue(value);
877
+ }
878
+ function cloneValue(value) {
879
+ if (typeof structuredClone === "function") {
880
+ return structuredClone(value);
881
+ }
882
+ return JSON.parse(JSON.stringify(value));
883
+ }
884
+
704
885
  // src/server/annotation-dispatch.ts
705
886
  var AnnotationDispatchError = class extends Error {
706
887
  constructor(message, errorCode) {
@@ -709,20 +890,30 @@ var AnnotationDispatchError = class extends Error {
709
890
  this.errorCode = errorCode;
710
891
  }
711
892
  };
712
- async function dispatchAnnotationsToAi(req, state) {
893
+ async function dispatchAnnotationsToAi(req, state, store = annotationSessionStore) {
713
894
  try {
714
895
  validateAnnotationDispatchRequest(req, state);
715
896
  const batch = normalizeAnnotationBatch(req);
716
897
  const prompt = buildAnnotationBatchPrompt(batch);
898
+ const deliveryMode = normalizeDeliveryMode(req.deliveryMode);
899
+ const session = store.createSession({
900
+ instruction: batch.instruction,
901
+ annotations: toSessionAnnotations(batch.annotations),
902
+ deliveryMode,
903
+ ...batch.runtimeContext ? { runtimeContext: batch.runtimeContext } : {},
904
+ ...batch.cssContextPrompt ? { cssContextPrompt: batch.cssContextPrompt } : {}
905
+ });
717
906
  const representativeTarget = batch.annotations[0]?.targets[0];
718
- const runtime = resolvePromptDispatchRuntime(state);
719
- return dispatchPromptThroughIde(runtime, {
907
+ const dispatchResult = deliveryMode === "ide" ? dispatchPromptThroughIde(resolvePromptDispatchRuntime(state), {
720
908
  prompt,
721
909
  ...representativeTarget?.file ? { filePath: representativeTarget.file } : {},
722
910
  ...representativeTarget?.line ? { line: representativeTarget.line } : {},
723
- ...representativeTarget?.column ? { column: representativeTarget.column } : {},
724
- ...batch.screenshotContext ? { screenshotContext: batch.screenshotContext } : {}
725
- });
911
+ ...representativeTarget?.column ? { column: representativeTarget.column } : {}
912
+ }) : { success: true };
913
+ return {
914
+ ...dispatchResult,
915
+ session: toSessionSummary(session)
916
+ };
726
917
  } catch (error) {
727
918
  return {
728
919
  success: false,
@@ -731,6 +922,41 @@ async function dispatchAnnotationsToAi(req, state) {
731
922
  };
732
923
  }
733
924
  }
925
+ function normalizeDeliveryMode(input) {
926
+ return input === "agent" ? "agent" : "ide";
927
+ }
928
+ function toSessionAnnotations(annotations) {
929
+ return annotations.map((annotation) => ({
930
+ id: `annotation-${annotation.index}`,
931
+ note: annotation.note,
932
+ intent: annotation.intent,
933
+ targets: annotation.targets.map((target, targetIndex) => ({
934
+ id: `annotation-${annotation.index}-target-${targetIndex + 1}`,
935
+ label: target.label ?? "Unknown target",
936
+ location: {
937
+ file: target.file,
938
+ line: target.line,
939
+ column: target.column
940
+ },
941
+ ...target.selector ? { selector: target.selector } : {},
942
+ ...target.snippet ? { snippet: target.snippet } : {},
943
+ rect: {
944
+ x: 0,
945
+ y: 0,
946
+ width: 0,
947
+ height: 0
948
+ }
949
+ }))
950
+ }));
951
+ }
952
+ function toSessionSummary(session) {
953
+ return {
954
+ id: session.id,
955
+ status: session.status,
956
+ createdAt: session.createdAt,
957
+ updatedAt: session.updatedAt
958
+ };
959
+ }
734
960
  function validateAnnotationDispatchRequest(req, state) {
735
961
  if (!req.annotations.length) {
736
962
  throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
@@ -751,9 +977,7 @@ function validateAnnotationDispatchRequest(req, state) {
751
977
  function normalizeAnnotationBatch(req) {
752
978
  return {
753
979
  instruction: req.instruction?.trim() ?? "",
754
- responseMode: req.responseMode ?? "unified",
755
980
  ...req.runtimeContext ? { runtimeContext: req.runtimeContext } : {},
756
- ...req.screenshotContext ? { screenshotContext: req.screenshotContext } : {},
757
981
  ...req.cssContextPrompt?.trim() ? { cssContextPrompt: req.cssContextPrompt.trim() } : {},
758
982
  annotations: req.annotations.map((annotation, index) => ({
759
983
  index: index + 1,
@@ -775,12 +999,9 @@ function buildAnnotationBatchPrompt(batch) {
775
999
  const prompt = batch.instruction ? `${batch.instruction}
776
1000
 
777
1001
  ${body}` : body;
778
- return appendScreenshotContextSection(
779
- appendCssContextSection(
780
- appendRuntimeContextSection(prompt, batch.runtimeContext),
781
- batch.cssContextPrompt
782
- ),
783
- batch.screenshotContext
1002
+ return appendCssContextSection(
1003
+ appendRuntimeContextSection(prompt, batch.runtimeContext),
1004
+ batch.cssContextPrompt
784
1005
  );
785
1006
  }
786
1007
  function appendCssContextSection(prompt, cssContextPrompt) {
@@ -807,20 +1028,6 @@ function buildSelectedElementsPrompt(annotations) {
807
1028
  }
808
1029
  return lines.join("\n");
809
1030
  }
810
- function appendScreenshotContextSection(prompt, screenshotContext) {
811
- if (!screenshotContext || !screenshotContext.imageDataUrl && !screenshotContext.imageAssetId) {
812
- return prompt;
813
- }
814
- const lines = [
815
- "Visual screenshot context attached:",
816
- `- capturedAt=${screenshotContext.capturedAt}`,
817
- `- mimeType=${screenshotContext.mimeType}`,
818
- ...screenshotContext.imageAssetId ? [`- imageAssetId=${screenshotContext.imageAssetId}`] : []
819
- ];
820
- return `${prompt}
821
-
822
- ${lines.join("\n")}`;
823
- }
824
1031
  function appendRuntimeContextSection(prompt, runtimeContext) {
825
1032
  if (!runtimeContext?.records.length) {
826
1033
  return prompt;
@@ -868,7 +1075,7 @@ async function buildClientConfig(serverState2) {
868
1075
  ...info,
869
1076
  prompts: resolveIntents(promptsConfig),
870
1077
  hotKeys: userConfig["inspector.hotKey"] ?? "alt",
871
- theme: userConfig["inspector.theme"] ?? "auto",
1078
+ annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "both",
872
1079
  includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
873
1080
  runtimeContext: {
874
1081
  enabled: true,
@@ -876,10 +1083,6 @@ async function buildClientConfig(serverState2) {
876
1083
  maxRuntimeErrors: 3,
877
1084
  maxFailedRequests: 2
878
1085
  },
879
- screenshotContext: {
880
- enabled: false
881
- },
882
- annotationResponseMode: userConfig["prompt.annotationResponseMode"] ?? "unified",
883
1086
  autoSend: userConfig["prompt.autoSend"] ?? false
884
1087
  };
885
1088
  }
@@ -924,7 +1127,7 @@ function handleOpenFileRequest(body, serverState2) {
924
1127
  else if (rawEditorHint === "vscodium") editorHint = "codium";
925
1128
  else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
926
1129
  serverLogger2.debug(
927
- `IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
1130
+ `SOURCE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
928
1131
  );
929
1132
  if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
930
1133
  let normalizedPath = absolutePath.replace(/\\/g, "/");
@@ -933,7 +1136,7 @@ function handleOpenFileRequest(body, serverState2) {
933
1136
  }
934
1137
  const encodedPath = encodeURI(normalizedPath);
935
1138
  const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
936
- serverLogger2.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
1139
+ serverLogger2.debug(`SOURCE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
937
1140
  try {
938
1141
  if (process.platform === "darwin") {
939
1142
  (0, import_node_child_process2.execFileSync)("open", [uri]);
@@ -943,7 +1146,7 @@ function handleOpenFileRequest(body, serverState2) {
943
1146
  (0, import_node_child_process2.execFileSync)("xdg-open", [uri]);
944
1147
  }
945
1148
  } catch (e) {
946
- serverLogger2.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
1149
+ serverLogger2.error(`Failed to launch URI for SOURCE_OPEN (${uri}):`, e);
947
1150
  (0, import_launch_ide2.launchIDE)({
948
1151
  file: absolutePath,
949
1152
  line: body.line,
@@ -1000,6 +1203,7 @@ function resolveProjectRoot() {
1000
1203
 
1001
1204
  // src/server/index.ts
1002
1205
  var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
1206
+ var PORT_FILE_NAME = "inspecto.port.json";
1003
1207
  var serverState = {
1004
1208
  port: null,
1005
1209
  running: false,
@@ -1008,6 +1212,42 @@ var serverState = {
1008
1212
  cwd: process.cwd()
1009
1213
  };
1010
1214
  var serverInstance = null;
1215
+ function getPortFilePath() {
1216
+ return import_node_path4.default.join(import_node_os2.default.tmpdir(), PORT_FILE_NAME);
1217
+ }
1218
+ function getProjectRootHash() {
1219
+ if (!serverState.projectRoot) return null;
1220
+ return import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1221
+ }
1222
+ function readPortData(portFile) {
1223
+ if (!import_node_fs4.default.existsSync(portFile)) return {};
1224
+ try {
1225
+ return JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1226
+ } catch {
1227
+ return {};
1228
+ }
1229
+ }
1230
+ function writeProjectPort(port) {
1231
+ const rootHash = getProjectRootHash();
1232
+ if (!rootHash) return;
1233
+ const portFile = getPortFilePath();
1234
+ const portData = readPortData(portFile);
1235
+ portData[rootHash] = port;
1236
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1237
+ }
1238
+ function removeProjectPort() {
1239
+ const rootHash = getProjectRootHash();
1240
+ if (!rootHash) return;
1241
+ const portFile = getPortFilePath();
1242
+ if (!import_node_fs4.default.existsSync(portFile)) return;
1243
+ const portData = readPortData(portFile);
1244
+ delete portData[rootHash];
1245
+ if (Object.keys(portData).length === 0) {
1246
+ import_node_fs4.default.unlinkSync(portFile);
1247
+ } else {
1248
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1249
+ }
1250
+ }
1011
1251
  async function startServer() {
1012
1252
  if (serverState.running && serverState.port !== null) {
1013
1253
  return serverState.port;
@@ -1041,7 +1281,7 @@ async function startServer() {
1041
1281
  });
1042
1282
  });
1043
1283
  await new Promise((resolve2, reject) => {
1044
- serverInstance.listen(port, "127.0.0.1", () => {
1284
+ serverInstance.listen(port, "0.0.0.0", () => {
1045
1285
  serverInstance.unref();
1046
1286
  resolve2();
1047
1287
  });
@@ -1052,37 +1292,18 @@ async function startServer() {
1052
1292
  });
1053
1293
  serverState.port = port;
1054
1294
  serverState.running = true;
1055
- const portFile = import_node_path4.default.join(import_node_os2.default.tmpdir(), "inspecto.port.json");
1056
1295
  try {
1057
- let portData = {};
1058
- if (import_node_fs4.default.existsSync(portFile)) {
1059
- try {
1060
- portData = JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1061
- } catch (e) {
1062
- }
1063
- }
1064
- const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1065
- portData[rootHash] = port;
1066
- import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1067
- } catch (e) {
1068
- serverLogger4.warn("Failed to write port file:", e);
1296
+ writeProjectPort(port);
1297
+ } catch (_e) {
1298
+ serverLogger4.warn("Failed to write port file:", _e);
1069
1299
  }
1070
1300
  process.once("exit", () => {
1071
1301
  try {
1072
- if (import_node_fs4.default.existsSync(portFile)) {
1073
- const portData = JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1074
- const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1075
- delete portData[rootHash];
1076
- if (Object.keys(portData).length === 0) {
1077
- import_node_fs4.default.unlinkSync(portFile);
1078
- } else {
1079
- import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1080
- }
1081
- }
1302
+ removeProjectPort();
1082
1303
  } catch {
1083
1304
  }
1084
1305
  });
1085
- serverLogger4.info(`server running at http://127.0.0.1:${port}`);
1306
+ serverLogger4.info(`server running at http://0.0.0.0:${port}`);
1086
1307
  return port;
1087
1308
  }
1088
1309
  async function readBody(req) {
@@ -1134,11 +1355,11 @@ async function handleRequest(url, req, res) {
1134
1355
  }
1135
1356
  return;
1136
1357
  }
1137
- if (pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
1358
+ if ((pathname === import_types2.INSPECTO_API_PATHS.SOURCE_OPEN || pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN) && req.method === "POST") {
1138
1359
  let body;
1139
1360
  try {
1140
1361
  body = JSON.parse(await readBody(req));
1141
- } catch (e) {
1362
+ } catch (_e) {
1142
1363
  res.writeHead(400, { "Content-Type": "application/json" });
1143
1364
  res.end(JSON.stringify({ error: "Invalid JSON body" }));
1144
1365
  return;
@@ -1147,7 +1368,7 @@ async function handleRequest(url, req, res) {
1147
1368
  handleOpenFileRequest(body, serverState);
1148
1369
  } catch (err) {
1149
1370
  serverLogger4.warn(
1150
- `Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}. Reason: ${err.message}`
1371
+ `Security: Blocked path traversal attempt in SOURCE_OPEN: ${body.file}. Reason: ${err.message}`
1151
1372
  );
1152
1373
  res.writeHead(403, { "Content-Type": "application/json" });
1153
1374
  res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
@@ -1221,6 +1442,212 @@ async function handleRequest(url, req, res) {
1221
1442
  }
1222
1443
  return;
1223
1444
  }
1445
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_CLAIM_NEXT && req.method === "POST") {
1446
+ try {
1447
+ const rawBody = await readBody(req);
1448
+ const body = rawBody ? JSON.parse(rawBody) : {};
1449
+ const timeoutMs = normalizeSessionClaimTimeout(
1450
+ body.timeoutMs === void 0 ? null : String(body.timeoutMs)
1451
+ );
1452
+ const result = await annotationSessionStore.claimNextSession({
1453
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
1454
+ });
1455
+ res.writeHead(200, { "Content-Type": "application/json" });
1456
+ res.end(
1457
+ JSON.stringify({
1458
+ success: true,
1459
+ timedOut: result.timedOut,
1460
+ matchedExisting: result.matchedExisting,
1461
+ ...result.event ? { event: result.event } : {},
1462
+ ...result.session ? { session: result.session } : {}
1463
+ })
1464
+ );
1465
+ } catch (e) {
1466
+ serverLogger4.error(`Error parsing session claim request:`, e);
1467
+ res.writeHead(400, { "Content-Type": "application/json" });
1468
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1469
+ }
1470
+ return;
1471
+ }
1472
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_EVENTS && req.method === "GET") {
1473
+ const statusParam = url.searchParams.getAll("status");
1474
+ const statuses = statusParam.length ? new Set(statusParam) : null;
1475
+ const sessionId = url.searchParams.get("sessionId")?.trim() || null;
1476
+ res.writeHead(200, {
1477
+ "Content-Type": "text/event-stream",
1478
+ "Cache-Control": "no-cache",
1479
+ Connection: "keep-alive"
1480
+ });
1481
+ res.write(`event: ready
1482
+ data: ${JSON.stringify({ ok: true })}
1483
+
1484
+ `);
1485
+ const unsubscribe = annotationSessionStore.subscribe((event) => {
1486
+ if (sessionId && event.session.id !== sessionId) {
1487
+ return;
1488
+ }
1489
+ if (statuses && !statuses.has(event.session.status)) {
1490
+ return;
1491
+ }
1492
+ res.write(formatSessionSseEvent(event));
1493
+ });
1494
+ req.on("close", () => {
1495
+ unsubscribe();
1496
+ res.end();
1497
+ });
1498
+ return;
1499
+ }
1500
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSIONS && req.method === "GET") {
1501
+ const statusParam = url.searchParams.getAll("status");
1502
+ const sessions = annotationSessionStore.listSessions(
1503
+ statusParam.length ? {
1504
+ status: statusParam
1505
+ } : void 0
1506
+ );
1507
+ res.writeHead(200, { "Content-Type": "application/json" });
1508
+ res.end(JSON.stringify({ success: true, sessions }));
1509
+ return;
1510
+ }
1511
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && req.method === "GET") {
1512
+ const sessionId = pathname.substring(import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1);
1513
+ const session = annotationSessionStore.getSession(sessionId);
1514
+ if (!session) {
1515
+ res.writeHead(404, { "Content-Type": "application/json" });
1516
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1517
+ return;
1518
+ }
1519
+ res.writeHead(200, { "Content-Type": "application/json" });
1520
+ res.end(JSON.stringify({ success: true, session }));
1521
+ return;
1522
+ }
1523
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX) && req.method === "POST") {
1524
+ const sessionId = pathname.slice(
1525
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
1526
+ -import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX.length
1527
+ );
1528
+ try {
1529
+ const rawBody = await readBody(req);
1530
+ const body = JSON.parse(rawBody);
1531
+ if (!isAnnotationThreadRole(body.role)) {
1532
+ res.writeHead(400, { "Content-Type": "application/json" });
1533
+ res.end(JSON.stringify({ success: false, error: "Reply role is invalid." }));
1534
+ return;
1535
+ }
1536
+ if (!body.text?.trim()) {
1537
+ res.writeHead(400, { "Content-Type": "application/json" });
1538
+ res.end(JSON.stringify({ success: false, error: "Reply text is required." }));
1539
+ return;
1540
+ }
1541
+ const session = annotationSessionStore.appendMessage(sessionId, {
1542
+ role: body.role,
1543
+ text: body.text.trim()
1544
+ });
1545
+ if (!session) {
1546
+ res.writeHead(404, { "Content-Type": "application/json" });
1547
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1548
+ return;
1549
+ }
1550
+ res.writeHead(200, { "Content-Type": "application/json" });
1551
+ res.end(JSON.stringify({ success: true, session }));
1552
+ } catch (e) {
1553
+ serverLogger4.error(`Error parsing session reply request:`, e);
1554
+ res.writeHead(400, { "Content-Type": "application/json" });
1555
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1556
+ }
1557
+ return;
1558
+ }
1559
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX) && req.method === "POST") {
1560
+ const sessionId = pathname.slice(
1561
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
1562
+ -import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX.length
1563
+ );
1564
+ try {
1565
+ const rawBody = await readBody(req);
1566
+ const body = rawBody ? JSON.parse(rawBody) : {};
1567
+ const message = body.message?.trim();
1568
+ const existingSession = annotationSessionStore.getSession(sessionId);
1569
+ if (!existingSession) {
1570
+ res.writeHead(404, { "Content-Type": "application/json" });
1571
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1572
+ return;
1573
+ }
1574
+ if (!message && !hasAgentReply(existingSession)) {
1575
+ res.writeHead(400, { "Content-Type": "application/json" });
1576
+ res.end(
1577
+ JSON.stringify({
1578
+ success: false,
1579
+ error: "Resolve message is required until an agent reply is recorded."
1580
+ })
1581
+ );
1582
+ return;
1583
+ }
1584
+ if (message) {
1585
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
1586
+ role: "agent",
1587
+ text: message
1588
+ });
1589
+ if (!repliedSession) {
1590
+ res.writeHead(404, { "Content-Type": "application/json" });
1591
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1592
+ return;
1593
+ }
1594
+ }
1595
+ const session = annotationSessionStore.updateStatus(sessionId, "resolved");
1596
+ if (!session) {
1597
+ res.writeHead(404, { "Content-Type": "application/json" });
1598
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1599
+ return;
1600
+ }
1601
+ res.writeHead(200, { "Content-Type": "application/json" });
1602
+ res.end(JSON.stringify({ success: true, session }));
1603
+ } catch (e) {
1604
+ serverLogger4.error(`Error parsing session resolve request:`, e);
1605
+ res.writeHead(400, { "Content-Type": "application/json" });
1606
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1607
+ }
1608
+ return;
1609
+ }
1610
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX) && req.method === "POST") {
1611
+ const sessionId = pathname.slice(
1612
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
1613
+ -import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX.length
1614
+ );
1615
+ try {
1616
+ const rawBody = await readBody(req);
1617
+ const body = rawBody ? JSON.parse(rawBody) : {};
1618
+ const message = body.message?.trim();
1619
+ const existingSession = annotationSessionStore.getSession(sessionId);
1620
+ if (!existingSession) {
1621
+ res.writeHead(404, { "Content-Type": "application/json" });
1622
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1623
+ return;
1624
+ }
1625
+ if (message) {
1626
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
1627
+ role: "agent",
1628
+ text: message
1629
+ });
1630
+ if (!repliedSession) {
1631
+ res.writeHead(404, { "Content-Type": "application/json" });
1632
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1633
+ return;
1634
+ }
1635
+ }
1636
+ const session = annotationSessionStore.updateStatus(sessionId, "dismissed");
1637
+ if (!session) {
1638
+ res.writeHead(404, { "Content-Type": "application/json" });
1639
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1640
+ return;
1641
+ }
1642
+ res.writeHead(200, { "Content-Type": "application/json" });
1643
+ res.end(JSON.stringify({ success: true, session }));
1644
+ } catch (e) {
1645
+ serverLogger4.error(`Error parsing session dismiss request:`, e);
1646
+ res.writeHead(400, { "Content-Type": "application/json" });
1647
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1648
+ }
1649
+ return;
1650
+ }
1224
1651
  if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
1225
1652
  const ticketId = pathname.substring(import_types2.INSPECTO_API_PATHS.AI_TICKET.length + 1);
1226
1653
  const payloadStr = readTicket(ticketId);
@@ -1237,7 +1664,7 @@ async function handleRequest(url, req, res) {
1237
1664
  res.end(JSON.stringify({ error: "not found" }));
1238
1665
  }
1239
1666
  async function dispatchToAi(req) {
1240
- const { location, snippet, prompt, screenshotContext } = req;
1667
+ const { location, snippet, prompt } = req;
1241
1668
  const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
1242
1669
 
1243
1670
  \`\`\`
@@ -1250,8 +1677,7 @@ ${snippet}
1250
1677
  filePath: location.file,
1251
1678
  line: location.line,
1252
1679
  column: location.column,
1253
- snippet,
1254
- ...screenshotContext ? { screenshotContext } : {}
1680
+ snippet
1255
1681
  });
1256
1682
  }
1257
1683
  function getBatchDispatchStatusCode(errorCode, success) {
@@ -1260,6 +1686,21 @@ function getBatchDispatchStatusCode(errorCode, success) {
1260
1686
  if (errorCode === "FORBIDDEN_PATH") return 403;
1261
1687
  return 500;
1262
1688
  }
1689
+ function isAnnotationThreadRole(value) {
1690
+ return value === "user" || value === "agent" || value === "system";
1691
+ }
1692
+ function formatSessionSseEvent(event) {
1693
+ return `event: ${event.type}
1694
+ data: ${JSON.stringify(event)}
1695
+
1696
+ `;
1697
+ }
1698
+ function normalizeSessionClaimTimeout(value) {
1699
+ if (!value?.trim()) return 3e4;
1700
+ const parsed = Number.parseInt(value, 10);
1701
+ if (!Number.isFinite(parsed)) return 3e4;
1702
+ return Math.max(0, Math.min(parsed, 3e5));
1703
+ }
1263
1704
 
1264
1705
  // src/legacy/webpack4/index.ts
1265
1706
  var import_node_path5 = __toESM(require("path"), 1);