@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.cjs CHANGED
@@ -551,7 +551,6 @@ function dispatchPromptThroughIde(runtime, payload) {
551
551
  line: payload.line,
552
552
  column: payload.column,
553
553
  snippet: payload.snippet,
554
- ...payload.screenshotContext ? { screenshotContext: payload.screenshotContext } : {},
555
554
  overrides: runtime.overrides,
556
555
  autoSend: runtime.autoSend
557
556
  });
@@ -685,6 +684,188 @@ function assertPathWithinIdeOpenScope(file, projectRoot) {
685
684
  }
686
685
  }
687
686
 
687
+ // src/server/session-store.ts
688
+ var DEFAULT_STATUS = "pending";
689
+ function createAnnotationSessionStore(options = {}) {
690
+ const sessions = /* @__PURE__ */ new Map();
691
+ const listeners = /* @__PURE__ */ new Set();
692
+ const now = options.now ?? (() => Date.now());
693
+ const createId = options.createId ?? createRandomId;
694
+ function findNewestMatchingSession(statuses) {
695
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
696
+ }
697
+ function updateSessionStatus(id, status) {
698
+ const session = sessions.get(id);
699
+ if (!session) return null;
700
+ const timestamp = now();
701
+ session.status = status;
702
+ session.updatedAt = timestamp;
703
+ if (status === "acknowledged") {
704
+ session.acknowledgedAt = timestamp;
705
+ }
706
+ if (status === "resolved") {
707
+ session.resolvedAt = timestamp;
708
+ }
709
+ emit({ type: "session-status-updated", session });
710
+ return cloneSession(session);
711
+ }
712
+ function claimSession(id, statuses) {
713
+ const session = sessions.get(id);
714
+ if (!session || statuses && !statuses.has(session.status)) return null;
715
+ if (session.status === "acknowledged") return cloneSession(session);
716
+ return updateSessionStatus(id, "acknowledged");
717
+ }
718
+ function emit(event) {
719
+ const snapshot = cloneSession(event.session);
720
+ for (const listener of listeners) {
721
+ listener({ type: event.type, session: snapshot });
722
+ }
723
+ }
724
+ const store = {
725
+ createSession(input) {
726
+ const timestamp = now();
727
+ const session = {
728
+ id: createId(),
729
+ instruction: input.instruction?.trim() ?? "",
730
+ annotations: cloneArray(input.annotations),
731
+ ...input.deliveryMode ? { deliveryMode: input.deliveryMode } : {},
732
+ status: DEFAULT_STATUS,
733
+ messages: cloneArray(input.messages ?? []),
734
+ createdAt: timestamp,
735
+ updatedAt: timestamp,
736
+ ...input.runtimeContext ? { runtimeContext: cloneValue(input.runtimeContext) } : {},
737
+ ...input.cssContextPrompt?.trim() ? { cssContextPrompt: input.cssContextPrompt.trim() } : {},
738
+ ...input.pageUrl ? { pageUrl: input.pageUrl } : {},
739
+ ...input.route ? { route: input.route } : {}
740
+ };
741
+ sessions.set(session.id, session);
742
+ emit({ type: "session-created", session });
743
+ return cloneSession(session);
744
+ },
745
+ getSession(id) {
746
+ const session = sessions.get(id);
747
+ return session ? cloneSession(session) : null;
748
+ },
749
+ listSessions(options2 = {}) {
750
+ const statuses = normalizeStatuses(options2.status);
751
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt).map((session) => cloneSession(session));
752
+ },
753
+ async claimNextSession(options2 = {}) {
754
+ const statuses = normalizeStatuses(DEFAULT_STATUS);
755
+ const existingSession = findNewestMatchingSession(statuses);
756
+ if (existingSession) {
757
+ return {
758
+ session: claimSession(existingSession.id, statuses),
759
+ timedOut: false,
760
+ matchedExisting: true
761
+ };
762
+ }
763
+ const timeoutMs = normalizeTimeoutMs(options2.timeoutMs);
764
+ if (timeoutMs === 0) {
765
+ return {
766
+ session: null,
767
+ timedOut: true,
768
+ matchedExisting: false
769
+ };
770
+ }
771
+ return await new Promise((resolve2) => {
772
+ let settled = false;
773
+ let timeout = null;
774
+ const finish = (result) => {
775
+ if (settled) return;
776
+ settled = true;
777
+ unsubscribe();
778
+ if (timeout) {
779
+ clearTimeout(timeout);
780
+ }
781
+ resolve2(result);
782
+ };
783
+ const unsubscribe = this.subscribe((event) => {
784
+ const session = claimSession(event.session.id, statuses);
785
+ if (!session) return;
786
+ finish({
787
+ session,
788
+ timedOut: false,
789
+ matchedExisting: false,
790
+ event: event.type
791
+ });
792
+ });
793
+ if (timeoutMs !== null) {
794
+ timeout = setTimeout(() => {
795
+ finish({
796
+ session: null,
797
+ timedOut: true,
798
+ matchedExisting: false
799
+ });
800
+ }, timeoutMs);
801
+ }
802
+ });
803
+ },
804
+ appendMessage(id, input) {
805
+ const session = sessions.get(id);
806
+ if (!session) return null;
807
+ const timestamp = now();
808
+ session.messages.push({
809
+ id: createId(),
810
+ role: input.role,
811
+ text: input.text,
812
+ createdAt: timestamp
813
+ });
814
+ session.updatedAt = timestamp;
815
+ if (input.role === "agent" && isPendingLikeStatus(session.status)) {
816
+ session.status = "in_progress";
817
+ }
818
+ emit({ type: "session-message-appended", session });
819
+ return cloneSession(session);
820
+ },
821
+ updateStatus(id, status) {
822
+ return updateSessionStatus(id, status);
823
+ },
824
+ subscribe(listener) {
825
+ listeners.add(listener);
826
+ return () => {
827
+ listeners.delete(listener);
828
+ };
829
+ },
830
+ clear() {
831
+ sessions.clear();
832
+ listeners.clear();
833
+ }
834
+ };
835
+ return store;
836
+ }
837
+ var annotationSessionStore = createAnnotationSessionStore();
838
+ function normalizeStatuses(status) {
839
+ if (!status) return null;
840
+ return new Set(Array.isArray(status) ? status : [status]);
841
+ }
842
+ function normalizeTimeoutMs(value) {
843
+ if (value === void 0) return null;
844
+ if (!Number.isFinite(value)) return 0;
845
+ return Math.max(0, Math.floor(value));
846
+ }
847
+ function isPendingLikeStatus(status) {
848
+ return status === "pending" || status === "acknowledged";
849
+ }
850
+ function hasAgentReply(session) {
851
+ return session.messages.some((message) => message.role === "agent" && Boolean(message.text?.trim()));
852
+ }
853
+ function createRandomId() {
854
+ return `annotation-session-${Math.random().toString(36).slice(2, 10)}`;
855
+ }
856
+ function cloneSession(session) {
857
+ return cloneValue(session);
858
+ }
859
+ function cloneArray(value) {
860
+ return cloneValue(value);
861
+ }
862
+ function cloneValue(value) {
863
+ if (typeof structuredClone === "function") {
864
+ return structuredClone(value);
865
+ }
866
+ return JSON.parse(JSON.stringify(value));
867
+ }
868
+
688
869
  // src/server/annotation-dispatch.ts
689
870
  var AnnotationDispatchError = class extends Error {
690
871
  constructor(message, errorCode) {
@@ -693,20 +874,30 @@ var AnnotationDispatchError = class extends Error {
693
874
  this.errorCode = errorCode;
694
875
  }
695
876
  };
696
- async function dispatchAnnotationsToAi(req, state) {
877
+ async function dispatchAnnotationsToAi(req, state, store = annotationSessionStore) {
697
878
  try {
698
879
  validateAnnotationDispatchRequest(req, state);
699
880
  const batch = normalizeAnnotationBatch(req);
700
881
  const prompt = buildAnnotationBatchPrompt(batch);
882
+ const deliveryMode = normalizeDeliveryMode(req.deliveryMode);
883
+ const session = store.createSession({
884
+ instruction: batch.instruction,
885
+ annotations: toSessionAnnotations(batch.annotations),
886
+ deliveryMode,
887
+ ...batch.runtimeContext ? { runtimeContext: batch.runtimeContext } : {},
888
+ ...batch.cssContextPrompt ? { cssContextPrompt: batch.cssContextPrompt } : {}
889
+ });
701
890
  const representativeTarget = batch.annotations[0]?.targets[0];
702
- const runtime = resolvePromptDispatchRuntime(state);
703
- return dispatchPromptThroughIde(runtime, {
891
+ const dispatchResult = deliveryMode === "ide" ? dispatchPromptThroughIde(resolvePromptDispatchRuntime(state), {
704
892
  prompt,
705
893
  ...representativeTarget?.file ? { filePath: representativeTarget.file } : {},
706
894
  ...representativeTarget?.line ? { line: representativeTarget.line } : {},
707
- ...representativeTarget?.column ? { column: representativeTarget.column } : {},
708
- ...batch.screenshotContext ? { screenshotContext: batch.screenshotContext } : {}
709
- });
895
+ ...representativeTarget?.column ? { column: representativeTarget.column } : {}
896
+ }) : { success: true };
897
+ return {
898
+ ...dispatchResult,
899
+ session: toSessionSummary(session)
900
+ };
710
901
  } catch (error) {
711
902
  return {
712
903
  success: false,
@@ -715,6 +906,41 @@ async function dispatchAnnotationsToAi(req, state) {
715
906
  };
716
907
  }
717
908
  }
909
+ function normalizeDeliveryMode(input) {
910
+ return input === "agent" ? "agent" : "ide";
911
+ }
912
+ function toSessionAnnotations(annotations) {
913
+ return annotations.map((annotation) => ({
914
+ id: `annotation-${annotation.index}`,
915
+ note: annotation.note,
916
+ intent: annotation.intent,
917
+ targets: annotation.targets.map((target, targetIndex) => ({
918
+ id: `annotation-${annotation.index}-target-${targetIndex + 1}`,
919
+ label: target.label ?? "Unknown target",
920
+ location: {
921
+ file: target.file,
922
+ line: target.line,
923
+ column: target.column
924
+ },
925
+ ...target.selector ? { selector: target.selector } : {},
926
+ ...target.snippet ? { snippet: target.snippet } : {},
927
+ rect: {
928
+ x: 0,
929
+ y: 0,
930
+ width: 0,
931
+ height: 0
932
+ }
933
+ }))
934
+ }));
935
+ }
936
+ function toSessionSummary(session) {
937
+ return {
938
+ id: session.id,
939
+ status: session.status,
940
+ createdAt: session.createdAt,
941
+ updatedAt: session.updatedAt
942
+ };
943
+ }
718
944
  function validateAnnotationDispatchRequest(req, state) {
719
945
  if (!req.annotations.length) {
720
946
  throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
@@ -735,9 +961,7 @@ function validateAnnotationDispatchRequest(req, state) {
735
961
  function normalizeAnnotationBatch(req) {
736
962
  return {
737
963
  instruction: req.instruction?.trim() ?? "",
738
- responseMode: req.responseMode ?? "unified",
739
964
  ...req.runtimeContext ? { runtimeContext: req.runtimeContext } : {},
740
- ...req.screenshotContext ? { screenshotContext: req.screenshotContext } : {},
741
965
  ...req.cssContextPrompt?.trim() ? { cssContextPrompt: req.cssContextPrompt.trim() } : {},
742
966
  annotations: req.annotations.map((annotation, index) => ({
743
967
  index: index + 1,
@@ -759,12 +983,9 @@ function buildAnnotationBatchPrompt(batch) {
759
983
  const prompt = batch.instruction ? `${batch.instruction}
760
984
 
761
985
  ${body}` : body;
762
- return appendScreenshotContextSection(
763
- appendCssContextSection(
764
- appendRuntimeContextSection(prompt, batch.runtimeContext),
765
- batch.cssContextPrompt
766
- ),
767
- batch.screenshotContext
986
+ return appendCssContextSection(
987
+ appendRuntimeContextSection(prompt, batch.runtimeContext),
988
+ batch.cssContextPrompt
768
989
  );
769
990
  }
770
991
  function appendCssContextSection(prompt, cssContextPrompt) {
@@ -791,20 +1012,6 @@ function buildSelectedElementsPrompt(annotations) {
791
1012
  }
792
1013
  return lines.join("\n");
793
1014
  }
794
- function appendScreenshotContextSection(prompt, screenshotContext) {
795
- if (!screenshotContext || !screenshotContext.imageDataUrl && !screenshotContext.imageAssetId) {
796
- return prompt;
797
- }
798
- const lines = [
799
- "Visual screenshot context attached:",
800
- `- capturedAt=${screenshotContext.capturedAt}`,
801
- `- mimeType=${screenshotContext.mimeType}`,
802
- ...screenshotContext.imageAssetId ? [`- imageAssetId=${screenshotContext.imageAssetId}`] : []
803
- ];
804
- return `${prompt}
805
-
806
- ${lines.join("\n")}`;
807
- }
808
1015
  function appendRuntimeContextSection(prompt, runtimeContext) {
809
1016
  if (!runtimeContext?.records.length) {
810
1017
  return prompt;
@@ -852,7 +1059,7 @@ async function buildClientConfig(serverState2) {
852
1059
  ...info,
853
1060
  prompts: resolveIntents(promptsConfig),
854
1061
  hotKeys: userConfig["inspector.hotKey"] ?? "alt",
855
- theme: userConfig["inspector.theme"] ?? "auto",
1062
+ annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "both",
856
1063
  includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
857
1064
  runtimeContext: {
858
1065
  enabled: true,
@@ -860,10 +1067,6 @@ async function buildClientConfig(serverState2) {
860
1067
  maxRuntimeErrors: 3,
861
1068
  maxFailedRequests: 2
862
1069
  },
863
- screenshotContext: {
864
- enabled: false
865
- },
866
- annotationResponseMode: userConfig["prompt.annotationResponseMode"] ?? "unified",
867
1070
  autoSend: userConfig["prompt.autoSend"] ?? false
868
1071
  };
869
1072
  }
@@ -908,7 +1111,7 @@ function handleOpenFileRequest(body, serverState2) {
908
1111
  else if (rawEditorHint === "vscodium") editorHint = "codium";
909
1112
  else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
910
1113
  serverLogger2.debug(
911
- `IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
1114
+ `SOURCE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
912
1115
  );
913
1116
  if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
914
1117
  let normalizedPath = absolutePath.replace(/\\/g, "/");
@@ -917,7 +1120,7 @@ function handleOpenFileRequest(body, serverState2) {
917
1120
  }
918
1121
  const encodedPath = encodeURI(normalizedPath);
919
1122
  const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
920
- serverLogger2.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
1123
+ serverLogger2.debug(`SOURCE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
921
1124
  try {
922
1125
  if (process.platform === "darwin") {
923
1126
  (0, import_node_child_process2.execFileSync)("open", [uri]);
@@ -927,7 +1130,7 @@ function handleOpenFileRequest(body, serverState2) {
927
1130
  (0, import_node_child_process2.execFileSync)("xdg-open", [uri]);
928
1131
  }
929
1132
  } catch (e) {
930
- serverLogger2.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
1133
+ serverLogger2.error(`Failed to launch URI for SOURCE_OPEN (${uri}):`, e);
931
1134
  (0, import_launch_ide2.launchIDE)({
932
1135
  file: absolutePath,
933
1136
  line: body.line,
@@ -984,6 +1187,7 @@ function resolveProjectRoot() {
984
1187
 
985
1188
  // src/server/index.ts
986
1189
  var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
1190
+ var PORT_FILE_NAME = "inspecto.port.json";
987
1191
  var serverState = {
988
1192
  port: null,
989
1193
  running: false,
@@ -992,6 +1196,42 @@ var serverState = {
992
1196
  cwd: process.cwd()
993
1197
  };
994
1198
  var serverInstance = null;
1199
+ function getPortFilePath() {
1200
+ return import_node_path4.default.join(import_node_os2.default.tmpdir(), PORT_FILE_NAME);
1201
+ }
1202
+ function getProjectRootHash() {
1203
+ if (!serverState.projectRoot) return null;
1204
+ return import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1205
+ }
1206
+ function readPortData(portFile) {
1207
+ if (!import_node_fs4.default.existsSync(portFile)) return {};
1208
+ try {
1209
+ return JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1210
+ } catch {
1211
+ return {};
1212
+ }
1213
+ }
1214
+ function writeProjectPort(port) {
1215
+ const rootHash = getProjectRootHash();
1216
+ if (!rootHash) return;
1217
+ const portFile = getPortFilePath();
1218
+ const portData = readPortData(portFile);
1219
+ portData[rootHash] = port;
1220
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1221
+ }
1222
+ function removeProjectPort() {
1223
+ const rootHash = getProjectRootHash();
1224
+ if (!rootHash) return;
1225
+ const portFile = getPortFilePath();
1226
+ if (!import_node_fs4.default.existsSync(portFile)) return;
1227
+ const portData = readPortData(portFile);
1228
+ delete portData[rootHash];
1229
+ if (Object.keys(portData).length === 0) {
1230
+ import_node_fs4.default.unlinkSync(portFile);
1231
+ } else {
1232
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1233
+ }
1234
+ }
995
1235
  async function startServer() {
996
1236
  if (serverState.running && serverState.port !== null) {
997
1237
  return serverState.port;
@@ -1025,7 +1265,7 @@ async function startServer() {
1025
1265
  });
1026
1266
  });
1027
1267
  await new Promise((resolve2, reject) => {
1028
- serverInstance.listen(port, "127.0.0.1", () => {
1268
+ serverInstance.listen(port, "0.0.0.0", () => {
1029
1269
  serverInstance.unref();
1030
1270
  resolve2();
1031
1271
  });
@@ -1036,37 +1276,18 @@ async function startServer() {
1036
1276
  });
1037
1277
  serverState.port = port;
1038
1278
  serverState.running = true;
1039
- const portFile = import_node_path4.default.join(import_node_os2.default.tmpdir(), "inspecto.port.json");
1040
1279
  try {
1041
- let portData = {};
1042
- if (import_node_fs4.default.existsSync(portFile)) {
1043
- try {
1044
- portData = JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1045
- } catch (_e) {
1046
- }
1047
- }
1048
- const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1049
- portData[rootHash] = port;
1050
- import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1280
+ writeProjectPort(port);
1051
1281
  } catch (_e) {
1052
1282
  serverLogger4.warn("Failed to write port file:", _e);
1053
1283
  }
1054
1284
  process.once("exit", () => {
1055
1285
  try {
1056
- if (import_node_fs4.default.existsSync(portFile)) {
1057
- const portData = JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1058
- const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1059
- delete portData[rootHash];
1060
- if (Object.keys(portData).length === 0) {
1061
- import_node_fs4.default.unlinkSync(portFile);
1062
- } else {
1063
- import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1064
- }
1065
- }
1286
+ removeProjectPort();
1066
1287
  } catch {
1067
1288
  }
1068
1289
  });
1069
- serverLogger4.info(`server running at http://127.0.0.1:${port}`);
1290
+ serverLogger4.info(`server running at http://0.0.0.0:${port}`);
1070
1291
  return port;
1071
1292
  }
1072
1293
  async function readBody(req) {
@@ -1118,7 +1339,7 @@ async function handleRequest(url, req, res) {
1118
1339
  }
1119
1340
  return;
1120
1341
  }
1121
- if (pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
1342
+ if ((pathname === import_types2.INSPECTO_API_PATHS.SOURCE_OPEN || pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN) && req.method === "POST") {
1122
1343
  let body;
1123
1344
  try {
1124
1345
  body = JSON.parse(await readBody(req));
@@ -1131,7 +1352,7 @@ async function handleRequest(url, req, res) {
1131
1352
  handleOpenFileRequest(body, serverState);
1132
1353
  } catch (err) {
1133
1354
  serverLogger4.warn(
1134
- `Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}. Reason: ${err.message}`
1355
+ `Security: Blocked path traversal attempt in SOURCE_OPEN: ${body.file}. Reason: ${err.message}`
1135
1356
  );
1136
1357
  res.writeHead(403, { "Content-Type": "application/json" });
1137
1358
  res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
@@ -1205,6 +1426,212 @@ async function handleRequest(url, req, res) {
1205
1426
  }
1206
1427
  return;
1207
1428
  }
1429
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_CLAIM_NEXT && req.method === "POST") {
1430
+ try {
1431
+ const rawBody = await readBody(req);
1432
+ const body = rawBody ? JSON.parse(rawBody) : {};
1433
+ const timeoutMs = normalizeSessionClaimTimeout(
1434
+ body.timeoutMs === void 0 ? null : String(body.timeoutMs)
1435
+ );
1436
+ const result = await annotationSessionStore.claimNextSession({
1437
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
1438
+ });
1439
+ res.writeHead(200, { "Content-Type": "application/json" });
1440
+ res.end(
1441
+ JSON.stringify({
1442
+ success: true,
1443
+ timedOut: result.timedOut,
1444
+ matchedExisting: result.matchedExisting,
1445
+ ...result.event ? { event: result.event } : {},
1446
+ ...result.session ? { session: result.session } : {}
1447
+ })
1448
+ );
1449
+ } catch (e) {
1450
+ serverLogger4.error(`Error parsing session claim request:`, e);
1451
+ res.writeHead(400, { "Content-Type": "application/json" });
1452
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1453
+ }
1454
+ return;
1455
+ }
1456
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_EVENTS && req.method === "GET") {
1457
+ const statusParam = url.searchParams.getAll("status");
1458
+ const statuses = statusParam.length ? new Set(statusParam) : null;
1459
+ const sessionId = url.searchParams.get("sessionId")?.trim() || null;
1460
+ res.writeHead(200, {
1461
+ "Content-Type": "text/event-stream",
1462
+ "Cache-Control": "no-cache",
1463
+ Connection: "keep-alive"
1464
+ });
1465
+ res.write(`event: ready
1466
+ data: ${JSON.stringify({ ok: true })}
1467
+
1468
+ `);
1469
+ const unsubscribe = annotationSessionStore.subscribe((event) => {
1470
+ if (sessionId && event.session.id !== sessionId) {
1471
+ return;
1472
+ }
1473
+ if (statuses && !statuses.has(event.session.status)) {
1474
+ return;
1475
+ }
1476
+ res.write(formatSessionSseEvent(event));
1477
+ });
1478
+ req.on("close", () => {
1479
+ unsubscribe();
1480
+ res.end();
1481
+ });
1482
+ return;
1483
+ }
1484
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSIONS && req.method === "GET") {
1485
+ const statusParam = url.searchParams.getAll("status");
1486
+ const sessions = annotationSessionStore.listSessions(
1487
+ statusParam.length ? {
1488
+ status: statusParam
1489
+ } : void 0
1490
+ );
1491
+ res.writeHead(200, { "Content-Type": "application/json" });
1492
+ res.end(JSON.stringify({ success: true, sessions }));
1493
+ return;
1494
+ }
1495
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && req.method === "GET") {
1496
+ const sessionId = pathname.substring(import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1);
1497
+ const session = annotationSessionStore.getSession(sessionId);
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
+ return;
1506
+ }
1507
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX) && req.method === "POST") {
1508
+ const sessionId = pathname.slice(
1509
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
1510
+ -import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX.length
1511
+ );
1512
+ try {
1513
+ const rawBody = await readBody(req);
1514
+ const body = JSON.parse(rawBody);
1515
+ if (!isAnnotationThreadRole(body.role)) {
1516
+ res.writeHead(400, { "Content-Type": "application/json" });
1517
+ res.end(JSON.stringify({ success: false, error: "Reply role is invalid." }));
1518
+ return;
1519
+ }
1520
+ if (!body.text?.trim()) {
1521
+ res.writeHead(400, { "Content-Type": "application/json" });
1522
+ res.end(JSON.stringify({ success: false, error: "Reply text is required." }));
1523
+ return;
1524
+ }
1525
+ const session = annotationSessionStore.appendMessage(sessionId, {
1526
+ role: body.role,
1527
+ text: body.text.trim()
1528
+ });
1529
+ if (!session) {
1530
+ res.writeHead(404, { "Content-Type": "application/json" });
1531
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1532
+ return;
1533
+ }
1534
+ res.writeHead(200, { "Content-Type": "application/json" });
1535
+ res.end(JSON.stringify({ success: true, session }));
1536
+ } catch (e) {
1537
+ serverLogger4.error(`Error parsing session reply request:`, e);
1538
+ res.writeHead(400, { "Content-Type": "application/json" });
1539
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1540
+ }
1541
+ return;
1542
+ }
1543
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX) && req.method === "POST") {
1544
+ const sessionId = pathname.slice(
1545
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
1546
+ -import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX.length
1547
+ );
1548
+ try {
1549
+ const rawBody = await readBody(req);
1550
+ const body = rawBody ? JSON.parse(rawBody) : {};
1551
+ const message = body.message?.trim();
1552
+ const existingSession = annotationSessionStore.getSession(sessionId);
1553
+ if (!existingSession) {
1554
+ res.writeHead(404, { "Content-Type": "application/json" });
1555
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1556
+ return;
1557
+ }
1558
+ if (!message && !hasAgentReply(existingSession)) {
1559
+ res.writeHead(400, { "Content-Type": "application/json" });
1560
+ res.end(
1561
+ JSON.stringify({
1562
+ success: false,
1563
+ error: "Resolve message is required until an agent reply is recorded."
1564
+ })
1565
+ );
1566
+ return;
1567
+ }
1568
+ if (message) {
1569
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
1570
+ role: "agent",
1571
+ text: message
1572
+ });
1573
+ if (!repliedSession) {
1574
+ res.writeHead(404, { "Content-Type": "application/json" });
1575
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1576
+ return;
1577
+ }
1578
+ }
1579
+ const session = annotationSessionStore.updateStatus(sessionId, "resolved");
1580
+ if (!session) {
1581
+ res.writeHead(404, { "Content-Type": "application/json" });
1582
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1583
+ return;
1584
+ }
1585
+ res.writeHead(200, { "Content-Type": "application/json" });
1586
+ res.end(JSON.stringify({ success: true, session }));
1587
+ } catch (e) {
1588
+ serverLogger4.error(`Error parsing session resolve request:`, e);
1589
+ res.writeHead(400, { "Content-Type": "application/json" });
1590
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1591
+ }
1592
+ return;
1593
+ }
1594
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX) && req.method === "POST") {
1595
+ const sessionId = pathname.slice(
1596
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
1597
+ -import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX.length
1598
+ );
1599
+ try {
1600
+ const rawBody = await readBody(req);
1601
+ const body = rawBody ? JSON.parse(rawBody) : {};
1602
+ const message = body.message?.trim();
1603
+ const existingSession = annotationSessionStore.getSession(sessionId);
1604
+ if (!existingSession) {
1605
+ res.writeHead(404, { "Content-Type": "application/json" });
1606
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1607
+ return;
1608
+ }
1609
+ if (message) {
1610
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
1611
+ role: "agent",
1612
+ text: message
1613
+ });
1614
+ if (!repliedSession) {
1615
+ res.writeHead(404, { "Content-Type": "application/json" });
1616
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1617
+ return;
1618
+ }
1619
+ }
1620
+ const session = annotationSessionStore.updateStatus(sessionId, "dismissed");
1621
+ if (!session) {
1622
+ res.writeHead(404, { "Content-Type": "application/json" });
1623
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
1624
+ return;
1625
+ }
1626
+ res.writeHead(200, { "Content-Type": "application/json" });
1627
+ res.end(JSON.stringify({ success: true, session }));
1628
+ } catch (e) {
1629
+ serverLogger4.error(`Error parsing session dismiss request:`, e);
1630
+ res.writeHead(400, { "Content-Type": "application/json" });
1631
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
1632
+ }
1633
+ return;
1634
+ }
1208
1635
  if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
1209
1636
  const ticketId = pathname.substring(import_types2.INSPECTO_API_PATHS.AI_TICKET.length + 1);
1210
1637
  const payloadStr = readTicket(ticketId);
@@ -1221,7 +1648,7 @@ async function handleRequest(url, req, res) {
1221
1648
  res.end(JSON.stringify({ error: "not found" }));
1222
1649
  }
1223
1650
  async function dispatchToAi(req) {
1224
- const { location, snippet, prompt, screenshotContext } = req;
1651
+ const { location, snippet, prompt } = req;
1225
1652
  const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
1226
1653
 
1227
1654
  \`\`\`
@@ -1234,8 +1661,7 @@ ${snippet}
1234
1661
  filePath: location.file,
1235
1662
  line: location.line,
1236
1663
  column: location.column,
1237
- snippet,
1238
- ...screenshotContext ? { screenshotContext } : {}
1664
+ snippet
1239
1665
  });
1240
1666
  }
1241
1667
  function getBatchDispatchStatusCode(errorCode, success) {
@@ -1244,6 +1670,21 @@ function getBatchDispatchStatusCode(errorCode, success) {
1244
1670
  if (errorCode === "FORBIDDEN_PATH") return 403;
1245
1671
  return 500;
1246
1672
  }
1673
+ function isAnnotationThreadRole(value) {
1674
+ return value === "user" || value === "agent" || value === "system";
1675
+ }
1676
+ function formatSessionSseEvent(event) {
1677
+ return `event: ${event.type}
1678
+ data: ${JSON.stringify(event)}
1679
+
1680
+ `;
1681
+ }
1682
+ function normalizeSessionClaimTimeout(value) {
1683
+ if (!value?.trim()) return 3e4;
1684
+ const parsed = Number.parseInt(value, 10);
1685
+ if (!Number.isFinite(parsed)) return 3e4;
1686
+ return Math.max(0, Math.min(parsed, 3e5));
1687
+ }
1247
1688
 
1248
1689
  // src/index.ts
1249
1690
  var import_unplugin = require("unplugin");
@@ -1934,7 +2375,9 @@ function transformRouter(options) {
1934
2375
  return transformSvelte({
1935
2376
  filePath,
1936
2377
  source,
2378
+ projectRoot,
1937
2379
  escapeTags: pluginOptions.escapeTags,
2380
+ pathType: pluginOptions.pathType,
1938
2381
  attributeName: pluginOptions.attributeName
1939
2382
  });
1940
2383
  }
@@ -1943,6 +2386,7 @@ function transformRouter(options) {
1943
2386
  filePath,
1944
2387
  source,
1945
2388
  escapeTags: pluginOptions.escapeTags,
2389
+ pathType: pluginOptions.pathType,
1946
2390
  attributeName: pluginOptions.attributeName
1947
2391
  });
1948
2392
  }
@@ -1970,13 +2414,13 @@ var resolveClientModule = () => {
1970
2414
  function getWebpackHtmlScript(serverPort) {
1971
2415
  return `
1972
2416
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
1973
- window.addEventListener('load', () => {
1974
- if (window.InspectoClient) {
1975
- window.InspectoClient.mountInspector({
1976
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
1977
- });
1978
- }
1979
- });
2417
+ window.addEventListener('load', () => {
2418
+ if (window.InspectoClient) {
2419
+ window.InspectoClient.mountInspector({
2420
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
2421
+ });
2422
+ }
2423
+ });
1980
2424
  `;
1981
2425
  }
1982
2426
  function getWebpackAssetScript(serverPort) {
@@ -1986,7 +2430,7 @@ if (typeof window !== 'undefined') {
1986
2430
  const _initInspecto = () => {
1987
2431
  if (window.InspectoClient) {
1988
2432
  window.InspectoClient.mountInspector({
1989
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
2433
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
1990
2434
  });
1991
2435
  } else {
1992
2436
  setTimeout(_initInspecto, 100);
@@ -2077,7 +2521,7 @@ function getViteVirtualModuleScript(serverPort) {
2077
2521
  import { mountInspector } from '@inspecto-dev/core';
2078
2522
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
2079
2523
  mountInspector({
2080
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
2524
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
2081
2525
  });
2082
2526
  `;
2083
2527
  }
@@ -2209,7 +2653,7 @@ var esbuildPlugin = InspectoPlugin.esbuild;
2209
2653
  function getAstroInjectedScript(serverPort) {
2210
2654
  return `
2211
2655
  import { mountInspector } from '@inspecto-dev/core';
2212
- window.__AI_INSPECTOR_SERVER_URL__ = 'http://127.0.0.1:${serverPort}';
2656
+ window.__AI_INSPECTOR_SERVER_URL__ = 'http://0.0.0.0:${serverPort}';
2213
2657
  mountInspector({
2214
2658
  serverUrl: window.__AI_INSPECTOR_SERVER_URL__,
2215
2659
  });