@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
package/dist/astro.js CHANGED
@@ -520,7 +520,6 @@ function dispatchPromptThroughIde(runtime, payload) {
520
520
  line: payload.line,
521
521
  column: payload.column,
522
522
  snippet: payload.snippet,
523
- ...payload.screenshotContext ? { screenshotContext: payload.screenshotContext } : {},
524
523
  overrides: runtime.overrides,
525
524
  autoSend: runtime.autoSend
526
525
  });
@@ -654,6 +653,188 @@ function assertPathWithinIdeOpenScope(file, projectRoot) {
654
653
  }
655
654
  }
656
655
 
656
+ // src/server/session-store.ts
657
+ var DEFAULT_STATUS = "pending";
658
+ function createAnnotationSessionStore(options = {}) {
659
+ const sessions = /* @__PURE__ */ new Map();
660
+ const listeners = /* @__PURE__ */ new Set();
661
+ const now = options.now ?? (() => Date.now());
662
+ const createId = options.createId ?? createRandomId;
663
+ function findNewestMatchingSession(statuses) {
664
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
665
+ }
666
+ function updateSessionStatus(id, status) {
667
+ const session = sessions.get(id);
668
+ if (!session) return null;
669
+ const timestamp = now();
670
+ session.status = status;
671
+ session.updatedAt = timestamp;
672
+ if (status === "acknowledged") {
673
+ session.acknowledgedAt = timestamp;
674
+ }
675
+ if (status === "resolved") {
676
+ session.resolvedAt = timestamp;
677
+ }
678
+ emit({ type: "session-status-updated", session });
679
+ return cloneSession(session);
680
+ }
681
+ function claimSession(id, statuses) {
682
+ const session = sessions.get(id);
683
+ if (!session || statuses && !statuses.has(session.status)) return null;
684
+ if (session.status === "acknowledged") return cloneSession(session);
685
+ return updateSessionStatus(id, "acknowledged");
686
+ }
687
+ function emit(event) {
688
+ const snapshot = cloneSession(event.session);
689
+ for (const listener of listeners) {
690
+ listener({ type: event.type, session: snapshot });
691
+ }
692
+ }
693
+ const store = {
694
+ createSession(input) {
695
+ const timestamp = now();
696
+ const session = {
697
+ id: createId(),
698
+ instruction: input.instruction?.trim() ?? "",
699
+ annotations: cloneArray(input.annotations),
700
+ ...input.deliveryMode ? { deliveryMode: input.deliveryMode } : {},
701
+ status: DEFAULT_STATUS,
702
+ messages: cloneArray(input.messages ?? []),
703
+ createdAt: timestamp,
704
+ updatedAt: timestamp,
705
+ ...input.runtimeContext ? { runtimeContext: cloneValue(input.runtimeContext) } : {},
706
+ ...input.cssContextPrompt?.trim() ? { cssContextPrompt: input.cssContextPrompt.trim() } : {},
707
+ ...input.pageUrl ? { pageUrl: input.pageUrl } : {},
708
+ ...input.route ? { route: input.route } : {}
709
+ };
710
+ sessions.set(session.id, session);
711
+ emit({ type: "session-created", session });
712
+ return cloneSession(session);
713
+ },
714
+ getSession(id) {
715
+ const session = sessions.get(id);
716
+ return session ? cloneSession(session) : null;
717
+ },
718
+ listSessions(options2 = {}) {
719
+ const statuses = normalizeStatuses(options2.status);
720
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt).map((session) => cloneSession(session));
721
+ },
722
+ async claimNextSession(options2 = {}) {
723
+ const statuses = normalizeStatuses(DEFAULT_STATUS);
724
+ const existingSession = findNewestMatchingSession(statuses);
725
+ if (existingSession) {
726
+ return {
727
+ session: claimSession(existingSession.id, statuses),
728
+ timedOut: false,
729
+ matchedExisting: true
730
+ };
731
+ }
732
+ const timeoutMs = normalizeTimeoutMs(options2.timeoutMs);
733
+ if (timeoutMs === 0) {
734
+ return {
735
+ session: null,
736
+ timedOut: true,
737
+ matchedExisting: false
738
+ };
739
+ }
740
+ return await new Promise((resolve2) => {
741
+ let settled = false;
742
+ let timeout = null;
743
+ const finish = (result) => {
744
+ if (settled) return;
745
+ settled = true;
746
+ unsubscribe();
747
+ if (timeout) {
748
+ clearTimeout(timeout);
749
+ }
750
+ resolve2(result);
751
+ };
752
+ const unsubscribe = this.subscribe((event) => {
753
+ const session = claimSession(event.session.id, statuses);
754
+ if (!session) return;
755
+ finish({
756
+ session,
757
+ timedOut: false,
758
+ matchedExisting: false,
759
+ event: event.type
760
+ });
761
+ });
762
+ if (timeoutMs !== null) {
763
+ timeout = setTimeout(() => {
764
+ finish({
765
+ session: null,
766
+ timedOut: true,
767
+ matchedExisting: false
768
+ });
769
+ }, timeoutMs);
770
+ }
771
+ });
772
+ },
773
+ appendMessage(id, input) {
774
+ const session = sessions.get(id);
775
+ if (!session) return null;
776
+ const timestamp = now();
777
+ session.messages.push({
778
+ id: createId(),
779
+ role: input.role,
780
+ text: input.text,
781
+ createdAt: timestamp
782
+ });
783
+ session.updatedAt = timestamp;
784
+ if (input.role === "agent" && isPendingLikeStatus(session.status)) {
785
+ session.status = "in_progress";
786
+ }
787
+ emit({ type: "session-message-appended", session });
788
+ return cloneSession(session);
789
+ },
790
+ updateStatus(id, status) {
791
+ return updateSessionStatus(id, status);
792
+ },
793
+ subscribe(listener) {
794
+ listeners.add(listener);
795
+ return () => {
796
+ listeners.delete(listener);
797
+ };
798
+ },
799
+ clear() {
800
+ sessions.clear();
801
+ listeners.clear();
802
+ }
803
+ };
804
+ return store;
805
+ }
806
+ var annotationSessionStore = createAnnotationSessionStore();
807
+ function normalizeStatuses(status) {
808
+ if (!status) return null;
809
+ return new Set(Array.isArray(status) ? status : [status]);
810
+ }
811
+ function normalizeTimeoutMs(value) {
812
+ if (value === void 0) return null;
813
+ if (!Number.isFinite(value)) return 0;
814
+ return Math.max(0, Math.floor(value));
815
+ }
816
+ function isPendingLikeStatus(status) {
817
+ return status === "pending" || status === "acknowledged";
818
+ }
819
+ function hasAgentReply(session) {
820
+ return session.messages.some((message) => message.role === "agent" && Boolean(message.text?.trim()));
821
+ }
822
+ function createRandomId() {
823
+ return `annotation-session-${Math.random().toString(36).slice(2, 10)}`;
824
+ }
825
+ function cloneSession(session) {
826
+ return cloneValue(session);
827
+ }
828
+ function cloneArray(value) {
829
+ return cloneValue(value);
830
+ }
831
+ function cloneValue(value) {
832
+ if (typeof structuredClone === "function") {
833
+ return structuredClone(value);
834
+ }
835
+ return JSON.parse(JSON.stringify(value));
836
+ }
837
+
657
838
  // src/server/annotation-dispatch.ts
658
839
  var AnnotationDispatchError = class extends Error {
659
840
  constructor(message, errorCode) {
@@ -662,20 +843,30 @@ var AnnotationDispatchError = class extends Error {
662
843
  this.errorCode = errorCode;
663
844
  }
664
845
  };
665
- async function dispatchAnnotationsToAi(req, state) {
846
+ async function dispatchAnnotationsToAi(req, state, store = annotationSessionStore) {
666
847
  try {
667
848
  validateAnnotationDispatchRequest(req, state);
668
849
  const batch = normalizeAnnotationBatch(req);
669
850
  const prompt = buildAnnotationBatchPrompt(batch);
851
+ const deliveryMode = normalizeDeliveryMode(req.deliveryMode);
852
+ const session = store.createSession({
853
+ instruction: batch.instruction,
854
+ annotations: toSessionAnnotations(batch.annotations),
855
+ deliveryMode,
856
+ ...batch.runtimeContext ? { runtimeContext: batch.runtimeContext } : {},
857
+ ...batch.cssContextPrompt ? { cssContextPrompt: batch.cssContextPrompt } : {}
858
+ });
670
859
  const representativeTarget = batch.annotations[0]?.targets[0];
671
- const runtime = resolvePromptDispatchRuntime(state);
672
- return dispatchPromptThroughIde(runtime, {
860
+ const dispatchResult = deliveryMode === "ide" ? dispatchPromptThroughIde(resolvePromptDispatchRuntime(state), {
673
861
  prompt,
674
862
  ...representativeTarget?.file ? { filePath: representativeTarget.file } : {},
675
863
  ...representativeTarget?.line ? { line: representativeTarget.line } : {},
676
- ...representativeTarget?.column ? { column: representativeTarget.column } : {},
677
- ...batch.screenshotContext ? { screenshotContext: batch.screenshotContext } : {}
678
- });
864
+ ...representativeTarget?.column ? { column: representativeTarget.column } : {}
865
+ }) : { success: true };
866
+ return {
867
+ ...dispatchResult,
868
+ session: toSessionSummary(session)
869
+ };
679
870
  } catch (error) {
680
871
  return {
681
872
  success: false,
@@ -684,6 +875,41 @@ async function dispatchAnnotationsToAi(req, state) {
684
875
  };
685
876
  }
686
877
  }
878
+ function normalizeDeliveryMode(input) {
879
+ return input === "agent" ? "agent" : "ide";
880
+ }
881
+ function toSessionAnnotations(annotations) {
882
+ return annotations.map((annotation) => ({
883
+ id: `annotation-${annotation.index}`,
884
+ note: annotation.note,
885
+ intent: annotation.intent,
886
+ targets: annotation.targets.map((target, targetIndex) => ({
887
+ id: `annotation-${annotation.index}-target-${targetIndex + 1}`,
888
+ label: target.label ?? "Unknown target",
889
+ location: {
890
+ file: target.file,
891
+ line: target.line,
892
+ column: target.column
893
+ },
894
+ ...target.selector ? { selector: target.selector } : {},
895
+ ...target.snippet ? { snippet: target.snippet } : {},
896
+ rect: {
897
+ x: 0,
898
+ y: 0,
899
+ width: 0,
900
+ height: 0
901
+ }
902
+ }))
903
+ }));
904
+ }
905
+ function toSessionSummary(session) {
906
+ return {
907
+ id: session.id,
908
+ status: session.status,
909
+ createdAt: session.createdAt,
910
+ updatedAt: session.updatedAt
911
+ };
912
+ }
687
913
  function validateAnnotationDispatchRequest(req, state) {
688
914
  if (!req.annotations.length) {
689
915
  throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
@@ -704,9 +930,7 @@ function validateAnnotationDispatchRequest(req, state) {
704
930
  function normalizeAnnotationBatch(req) {
705
931
  return {
706
932
  instruction: req.instruction?.trim() ?? "",
707
- responseMode: req.responseMode ?? "unified",
708
933
  ...req.runtimeContext ? { runtimeContext: req.runtimeContext } : {},
709
- ...req.screenshotContext ? { screenshotContext: req.screenshotContext } : {},
710
934
  ...req.cssContextPrompt?.trim() ? { cssContextPrompt: req.cssContextPrompt.trim() } : {},
711
935
  annotations: req.annotations.map((annotation, index) => ({
712
936
  index: index + 1,
@@ -728,12 +952,9 @@ function buildAnnotationBatchPrompt(batch) {
728
952
  const prompt = batch.instruction ? `${batch.instruction}
729
953
 
730
954
  ${body}` : body;
731
- return appendScreenshotContextSection(
732
- appendCssContextSection(
733
- appendRuntimeContextSection(prompt, batch.runtimeContext),
734
- batch.cssContextPrompt
735
- ),
736
- batch.screenshotContext
955
+ return appendCssContextSection(
956
+ appendRuntimeContextSection(prompt, batch.runtimeContext),
957
+ batch.cssContextPrompt
737
958
  );
738
959
  }
739
960
  function appendCssContextSection(prompt, cssContextPrompt) {
@@ -760,20 +981,6 @@ function buildSelectedElementsPrompt(annotations) {
760
981
  }
761
982
  return lines.join("\n");
762
983
  }
763
- function appendScreenshotContextSection(prompt, screenshotContext) {
764
- if (!screenshotContext || !screenshotContext.imageDataUrl && !screenshotContext.imageAssetId) {
765
- return prompt;
766
- }
767
- const lines = [
768
- "Visual screenshot context attached:",
769
- `- capturedAt=${screenshotContext.capturedAt}`,
770
- `- mimeType=${screenshotContext.mimeType}`,
771
- ...screenshotContext.imageAssetId ? [`- imageAssetId=${screenshotContext.imageAssetId}`] : []
772
- ];
773
- return `${prompt}
774
-
775
- ${lines.join("\n")}`;
776
- }
777
984
  function appendRuntimeContextSection(prompt, runtimeContext) {
778
985
  if (!runtimeContext?.records.length) {
779
986
  return prompt;
@@ -821,7 +1028,7 @@ async function buildClientConfig(serverState2) {
821
1028
  ...info,
822
1029
  prompts: resolveIntents(promptsConfig),
823
1030
  hotKeys: userConfig["inspector.hotKey"] ?? "alt",
824
- theme: userConfig["inspector.theme"] ?? "auto",
1031
+ annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "both",
825
1032
  includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
826
1033
  runtimeContext: {
827
1034
  enabled: true,
@@ -829,10 +1036,6 @@ async function buildClientConfig(serverState2) {
829
1036
  maxRuntimeErrors: 3,
830
1037
  maxFailedRequests: 2
831
1038
  },
832
- screenshotContext: {
833
- enabled: false
834
- },
835
- annotationResponseMode: userConfig["prompt.annotationResponseMode"] ?? "unified",
836
1039
  autoSend: userConfig["prompt.autoSend"] ?? false
837
1040
  };
838
1041
  }
@@ -877,7 +1080,7 @@ function handleOpenFileRequest(body, serverState2) {
877
1080
  else if (rawEditorHint === "vscodium") editorHint = "codium";
878
1081
  else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
879
1082
  serverLogger2.debug(
880
- `IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
1083
+ `SOURCE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
881
1084
  );
882
1085
  if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
883
1086
  let normalizedPath = absolutePath.replace(/\\/g, "/");
@@ -886,7 +1089,7 @@ function handleOpenFileRequest(body, serverState2) {
886
1089
  }
887
1090
  const encodedPath = encodeURI(normalizedPath);
888
1091
  const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
889
- serverLogger2.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
1092
+ serverLogger2.debug(`SOURCE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
890
1093
  try {
891
1094
  if (process.platform === "darwin") {
892
1095
  execFileSync2("open", [uri]);
@@ -896,7 +1099,7 @@ function handleOpenFileRequest(body, serverState2) {
896
1099
  execFileSync2("xdg-open", [uri]);
897
1100
  }
898
1101
  } catch (e) {
899
- serverLogger2.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
1102
+ serverLogger2.error(`Failed to launch URI for SOURCE_OPEN (${uri}):`, e);
900
1103
  launchIDE2({
901
1104
  file: absolutePath,
902
1105
  line: body.line,
@@ -953,6 +1156,7 @@ function resolveProjectRoot() {
953
1156
 
954
1157
  // src/server/index.ts
955
1158
  var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
1159
+ var PORT_FILE_NAME = "inspecto.port.json";
956
1160
  var serverState = {
957
1161
  port: null,
958
1162
  running: false,
@@ -961,6 +1165,42 @@ var serverState = {
961
1165
  cwd: process.cwd()
962
1166
  };
963
1167
  var serverInstance = null;
1168
+ function getPortFilePath() {
1169
+ return path5.join(os2.tmpdir(), PORT_FILE_NAME);
1170
+ }
1171
+ function getProjectRootHash() {
1172
+ if (!serverState.projectRoot) return null;
1173
+ return crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
1174
+ }
1175
+ function readPortData(portFile) {
1176
+ if (!fs5.existsSync(portFile)) return {};
1177
+ try {
1178
+ return JSON.parse(fs5.readFileSync(portFile, "utf-8"));
1179
+ } catch {
1180
+ return {};
1181
+ }
1182
+ }
1183
+ function writeProjectPort(port) {
1184
+ const rootHash = getProjectRootHash();
1185
+ if (!rootHash) return;
1186
+ const portFile = getPortFilePath();
1187
+ const portData = readPortData(portFile);
1188
+ portData[rootHash] = port;
1189
+ fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1190
+ }
1191
+ function removeProjectPort() {
1192
+ const rootHash = getProjectRootHash();
1193
+ if (!rootHash) return;
1194
+ const portFile = getPortFilePath();
1195
+ if (!fs5.existsSync(portFile)) return;
1196
+ const portData = readPortData(portFile);
1197
+ delete portData[rootHash];
1198
+ if (Object.keys(portData).length === 0) {
1199
+ fs5.unlinkSync(portFile);
1200
+ } else {
1201
+ fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1202
+ }
1203
+ }
964
1204
  async function startServer() {
965
1205
  if (serverState.running && serverState.port !== null) {
966
1206
  return serverState.port;
@@ -994,7 +1234,7 @@ async function startServer() {
994
1234
  });
995
1235
  });
996
1236
  await new Promise((resolve2, reject) => {
997
- serverInstance.listen(port, "127.0.0.1", () => {
1237
+ serverInstance.listen(port, "0.0.0.0", () => {
998
1238
  serverInstance.unref();
999
1239
  resolve2();
1000
1240
  });
@@ -1005,37 +1245,18 @@ async function startServer() {
1005
1245
  });
1006
1246
  serverState.port = port;
1007
1247
  serverState.running = true;
1008
- const portFile = path5.join(os2.tmpdir(), "inspecto.port.json");
1009
1248
  try {
1010
- let portData = {};
1011
- if (fs5.existsSync(portFile)) {
1012
- try {
1013
- portData = JSON.parse(fs5.readFileSync(portFile, "utf-8"));
1014
- } catch (_e) {
1015
- }
1016
- }
1017
- const rootHash = crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
1018
- portData[rootHash] = port;
1019
- fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1249
+ writeProjectPort(port);
1020
1250
  } catch (_e) {
1021
1251
  serverLogger4.warn("Failed to write port file:", _e);
1022
1252
  }
1023
1253
  process.once("exit", () => {
1024
1254
  try {
1025
- if (fs5.existsSync(portFile)) {
1026
- const portData = JSON.parse(fs5.readFileSync(portFile, "utf-8"));
1027
- const rootHash = crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
1028
- delete portData[rootHash];
1029
- if (Object.keys(portData).length === 0) {
1030
- fs5.unlinkSync(portFile);
1031
- } else {
1032
- fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1033
- }
1034
- }
1255
+ removeProjectPort();
1035
1256
  } catch {
1036
1257
  }
1037
1258
  });
1038
- serverLogger4.info(`server running at http://127.0.0.1:${port}`);
1259
+ serverLogger4.info(`server running at http://0.0.0.0:${port}`);
1039
1260
  return port;
1040
1261
  }
1041
1262
  async function readBody(req) {
@@ -1087,7 +1308,7 @@ async function handleRequest(url, req, res) {
1087
1308
  }
1088
1309
  return;
1089
1310
  }
1090
- if (pathname === INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
1311
+ if ((pathname === INSPECTO_API_PATHS.SOURCE_OPEN || pathname === INSPECTO_API_PATHS.IDE_OPEN) && req.method === "POST") {
1091
1312
  let body;
1092
1313
  try {
1093
1314
  body = JSON.parse(await readBody(req));
@@ -1100,7 +1321,7 @@ async function handleRequest(url, req, res) {
1100
1321
  handleOpenFileRequest(body, serverState);
1101
1322
  } catch (err) {
1102
1323
  serverLogger4.warn(
1103
- `Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}. Reason: ${err.message}`
1324
+ `Security: Blocked path traversal attempt in SOURCE_OPEN: ${body.file}. Reason: ${err.message}`
1104
1325
  );
1105
1326
  res.writeHead(403, { "Content-Type": "application/json" });
1106
1327
  res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
@@ -1174,6 +1395,212 @@ async function handleRequest(url, req, res) {
1174
1395
  }
1175
1396
  return;
1176
1397
  }
1398
+ if (pathname === INSPECTO_API_PATHS.SESSION_CLAIM_NEXT && req.method === "POST") {
1399
+ try {
1400
+ const rawBody = await readBody(req);
1401
+ const body = rawBody ? JSON.parse(rawBody) : {};
1402
+ const timeoutMs = normalizeSessionClaimTimeout(
1403
+ body.timeoutMs === void 0 ? null : String(body.timeoutMs)
1404
+ );
1405
+ const result = await annotationSessionStore.claimNextSession({
1406
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
1407
+ });
1408
+ res.writeHead(200, { "Content-Type": "application/json" });
1409
+ res.end(
1410
+ JSON.stringify({
1411
+ success: true,
1412
+ timedOut: result.timedOut,
1413
+ matchedExisting: result.matchedExisting,
1414
+ ...result.event ? { event: result.event } : {},
1415
+ ...result.session ? { session: result.session } : {}
1416
+ })
1417
+ );
1418
+ } catch (e) {
1419
+ serverLogger4.error(`Error parsing session claim request:`, e);
1420
+ res.writeHead(400, { "Content-Type": "application/json" });
1421
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1422
+ }
1423
+ return;
1424
+ }
1425
+ if (pathname === INSPECTO_API_PATHS.SESSION_EVENTS && req.method === "GET") {
1426
+ const statusParam = url.searchParams.getAll("status");
1427
+ const statuses = statusParam.length ? new Set(statusParam) : null;
1428
+ const sessionId = url.searchParams.get("sessionId")?.trim() || null;
1429
+ res.writeHead(200, {
1430
+ "Content-Type": "text/event-stream",
1431
+ "Cache-Control": "no-cache",
1432
+ Connection: "keep-alive"
1433
+ });
1434
+ res.write(`event: ready
1435
+ data: ${JSON.stringify({ ok: true })}
1436
+
1437
+ `);
1438
+ const unsubscribe = annotationSessionStore.subscribe((event) => {
1439
+ if (sessionId && event.session.id !== sessionId) {
1440
+ return;
1441
+ }
1442
+ if (statuses && !statuses.has(event.session.status)) {
1443
+ return;
1444
+ }
1445
+ res.write(formatSessionSseEvent(event));
1446
+ });
1447
+ req.on("close", () => {
1448
+ unsubscribe();
1449
+ res.end();
1450
+ });
1451
+ return;
1452
+ }
1453
+ if (pathname === INSPECTO_API_PATHS.SESSIONS && req.method === "GET") {
1454
+ const statusParam = url.searchParams.getAll("status");
1455
+ const sessions = annotationSessionStore.listSessions(
1456
+ statusParam.length ? {
1457
+ status: statusParam
1458
+ } : void 0
1459
+ );
1460
+ res.writeHead(200, { "Content-Type": "application/json" });
1461
+ res.end(JSON.stringify({ success: true, sessions }));
1462
+ return;
1463
+ }
1464
+ if (pathname.startsWith(`${INSPECTO_API_PATHS.SESSIONS}/`) && req.method === "GET") {
1465
+ const sessionId = pathname.substring(INSPECTO_API_PATHS.SESSIONS.length + 1);
1466
+ const session = annotationSessionStore.getSession(sessionId);
1467
+ if (!session) {
1468
+ res.writeHead(404, { "Content-Type": "application/json" });
1469
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1470
+ return;
1471
+ }
1472
+ res.writeHead(200, { "Content-Type": "application/json" });
1473
+ res.end(JSON.stringify({ success: true, session }));
1474
+ return;
1475
+ }
1476
+ if (pathname.startsWith(`${INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX) && req.method === "POST") {
1477
+ const sessionId = pathname.slice(
1478
+ INSPECTO_API_PATHS.SESSIONS.length + 1,
1479
+ -INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX.length
1480
+ );
1481
+ try {
1482
+ const rawBody = await readBody(req);
1483
+ const body = JSON.parse(rawBody);
1484
+ if (!isAnnotationThreadRole(body.role)) {
1485
+ res.writeHead(400, { "Content-Type": "application/json" });
1486
+ res.end(JSON.stringify({ success: false, error: "Reply role is invalid." }));
1487
+ return;
1488
+ }
1489
+ if (!body.text?.trim()) {
1490
+ res.writeHead(400, { "Content-Type": "application/json" });
1491
+ res.end(JSON.stringify({ success: false, error: "Reply text is required." }));
1492
+ return;
1493
+ }
1494
+ const session = annotationSessionStore.appendMessage(sessionId, {
1495
+ role: body.role,
1496
+ text: body.text.trim()
1497
+ });
1498
+ if (!session) {
1499
+ res.writeHead(404, { "Content-Type": "application/json" });
1500
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1501
+ return;
1502
+ }
1503
+ res.writeHead(200, { "Content-Type": "application/json" });
1504
+ res.end(JSON.stringify({ success: true, session }));
1505
+ } catch (e) {
1506
+ serverLogger4.error(`Error parsing session reply request:`, e);
1507
+ res.writeHead(400, { "Content-Type": "application/json" });
1508
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1509
+ }
1510
+ return;
1511
+ }
1512
+ if (pathname.startsWith(`${INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX) && req.method === "POST") {
1513
+ const sessionId = pathname.slice(
1514
+ INSPECTO_API_PATHS.SESSIONS.length + 1,
1515
+ -INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX.length
1516
+ );
1517
+ try {
1518
+ const rawBody = await readBody(req);
1519
+ const body = rawBody ? JSON.parse(rawBody) : {};
1520
+ const message = body.message?.trim();
1521
+ const existingSession = annotationSessionStore.getSession(sessionId);
1522
+ if (!existingSession) {
1523
+ res.writeHead(404, { "Content-Type": "application/json" });
1524
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1525
+ return;
1526
+ }
1527
+ if (!message && !hasAgentReply(existingSession)) {
1528
+ res.writeHead(400, { "Content-Type": "application/json" });
1529
+ res.end(
1530
+ JSON.stringify({
1531
+ success: false,
1532
+ error: "Resolve message is required until an agent reply is recorded."
1533
+ })
1534
+ );
1535
+ return;
1536
+ }
1537
+ if (message) {
1538
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
1539
+ role: "agent",
1540
+ text: message
1541
+ });
1542
+ if (!repliedSession) {
1543
+ res.writeHead(404, { "Content-Type": "application/json" });
1544
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1545
+ return;
1546
+ }
1547
+ }
1548
+ const session = annotationSessionStore.updateStatus(sessionId, "resolved");
1549
+ if (!session) {
1550
+ res.writeHead(404, { "Content-Type": "application/json" });
1551
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1552
+ return;
1553
+ }
1554
+ res.writeHead(200, { "Content-Type": "application/json" });
1555
+ res.end(JSON.stringify({ success: true, session }));
1556
+ } catch (e) {
1557
+ serverLogger4.error(`Error parsing session resolve request:`, e);
1558
+ res.writeHead(400, { "Content-Type": "application/json" });
1559
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1560
+ }
1561
+ return;
1562
+ }
1563
+ if (pathname.startsWith(`${INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX) && req.method === "POST") {
1564
+ const sessionId = pathname.slice(
1565
+ INSPECTO_API_PATHS.SESSIONS.length + 1,
1566
+ -INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX.length
1567
+ );
1568
+ try {
1569
+ const rawBody = await readBody(req);
1570
+ const body = rawBody ? JSON.parse(rawBody) : {};
1571
+ const message = body.message?.trim();
1572
+ const existingSession = annotationSessionStore.getSession(sessionId);
1573
+ if (!existingSession) {
1574
+ res.writeHead(404, { "Content-Type": "application/json" });
1575
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1576
+ return;
1577
+ }
1578
+ if (message) {
1579
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
1580
+ role: "agent",
1581
+ text: message
1582
+ });
1583
+ if (!repliedSession) {
1584
+ res.writeHead(404, { "Content-Type": "application/json" });
1585
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1586
+ return;
1587
+ }
1588
+ }
1589
+ const session = annotationSessionStore.updateStatus(sessionId, "dismissed");
1590
+ if (!session) {
1591
+ res.writeHead(404, { "Content-Type": "application/json" });
1592
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1593
+ return;
1594
+ }
1595
+ res.writeHead(200, { "Content-Type": "application/json" });
1596
+ res.end(JSON.stringify({ success: true, session }));
1597
+ } catch (e) {
1598
+ serverLogger4.error(`Error parsing session dismiss request:`, e);
1599
+ res.writeHead(400, { "Content-Type": "application/json" });
1600
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1601
+ }
1602
+ return;
1603
+ }
1177
1604
  if (pathname.startsWith(`${INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
1178
1605
  const ticketId = pathname.substring(INSPECTO_API_PATHS.AI_TICKET.length + 1);
1179
1606
  const payloadStr = readTicket(ticketId);
@@ -1190,7 +1617,7 @@ async function handleRequest(url, req, res) {
1190
1617
  res.end(JSON.stringify({ error: "not found" }));
1191
1618
  }
1192
1619
  async function dispatchToAi(req) {
1193
- const { location, snippet, prompt, screenshotContext } = req;
1620
+ const { location, snippet, prompt } = req;
1194
1621
  const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
1195
1622
 
1196
1623
  \`\`\`
@@ -1203,8 +1630,7 @@ ${snippet}
1203
1630
  filePath: location.file,
1204
1631
  line: location.line,
1205
1632
  column: location.column,
1206
- snippet,
1207
- ...screenshotContext ? { screenshotContext } : {}
1633
+ snippet
1208
1634
  });
1209
1635
  }
1210
1636
  function getBatchDispatchStatusCode(errorCode, success) {
@@ -1213,6 +1639,21 @@ function getBatchDispatchStatusCode(errorCode, success) {
1213
1639
  if (errorCode === "FORBIDDEN_PATH") return 403;
1214
1640
  return 500;
1215
1641
  }
1642
+ function isAnnotationThreadRole(value) {
1643
+ return value === "user" || value === "agent" || value === "system";
1644
+ }
1645
+ function formatSessionSseEvent(event) {
1646
+ return `event: ${event.type}
1647
+ data: ${JSON.stringify(event)}
1648
+
1649
+ `;
1650
+ }
1651
+ function normalizeSessionClaimTimeout(value) {
1652
+ if (!value?.trim()) return 3e4;
1653
+ const parsed = Number.parseInt(value, 10);
1654
+ if (!Number.isFinite(parsed)) return 3e4;
1655
+ return Math.max(0, Math.min(parsed, 3e5));
1656
+ }
1216
1657
 
1217
1658
  // src/index.ts
1218
1659
  import { createUnplugin } from "unplugin";
@@ -1903,7 +2344,9 @@ function transformRouter(options) {
1903
2344
  return transformSvelte({
1904
2345
  filePath,
1905
2346
  source,
2347
+ projectRoot,
1906
2348
  escapeTags: pluginOptions.escapeTags,
2349
+ pathType: pluginOptions.pathType,
1907
2350
  attributeName: pluginOptions.attributeName
1908
2351
  });
1909
2352
  }
@@ -1912,6 +2355,7 @@ function transformRouter(options) {
1912
2355
  filePath,
1913
2356
  source,
1914
2357
  escapeTags: pluginOptions.escapeTags,
2358
+ pathType: pluginOptions.pathType,
1915
2359
  attributeName: pluginOptions.attributeName
1916
2360
  });
1917
2361
  }
@@ -1939,13 +2383,13 @@ var resolveClientModule = () => {
1939
2383
  function getWebpackHtmlScript(serverPort) {
1940
2384
  return `
1941
2385
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
1942
- window.addEventListener('load', () => {
1943
- if (window.InspectoClient) {
1944
- window.InspectoClient.mountInspector({
1945
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
1946
- });
1947
- }
1948
- });
2386
+ window.addEventListener('load', () => {
2387
+ if (window.InspectoClient) {
2388
+ window.InspectoClient.mountInspector({
2389
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
2390
+ });
2391
+ }
2392
+ });
1949
2393
  `;
1950
2394
  }
1951
2395
  function getWebpackAssetScript(serverPort) {
@@ -1955,7 +2399,7 @@ if (typeof window !== 'undefined') {
1955
2399
  const _initInspecto = () => {
1956
2400
  if (window.InspectoClient) {
1957
2401
  window.InspectoClient.mountInspector({
1958
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
2402
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
1959
2403
  });
1960
2404
  } else {
1961
2405
  setTimeout(_initInspecto, 100);
@@ -2046,7 +2490,7 @@ function getViteVirtualModuleScript(serverPort) {
2046
2490
  import { mountInspector } from '@inspecto-dev/core';
2047
2491
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
2048
2492
  mountInspector({
2049
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
2493
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
2050
2494
  });
2051
2495
  `;
2052
2496
  }
@@ -2178,7 +2622,7 @@ var esbuildPlugin = InspectoPlugin.esbuild;
2178
2622
  function getAstroInjectedScript(serverPort) {
2179
2623
  return `
2180
2624
  import { mountInspector } from '@inspecto-dev/core';
2181
- window.__AI_INSPECTOR_SERVER_URL__ = 'http://127.0.0.1:${serverPort}';
2625
+ window.__AI_INSPECTOR_SERVER_URL__ = 'http://0.0.0.0:${serverPort}';
2182
2626
  mountInspector({
2183
2627
  serverUrl: window.__AI_INSPECTOR_SERVER_URL__,
2184
2628
  });