@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/rollup.cjs CHANGED
@@ -736,7 +736,9 @@ function transformRouter(options) {
736
736
  return transformSvelte({
737
737
  filePath,
738
738
  source,
739
+ projectRoot,
739
740
  escapeTags: pluginOptions.escapeTags,
741
+ pathType: pluginOptions.pathType,
740
742
  attributeName: pluginOptions.attributeName
741
743
  });
742
744
  }
@@ -745,6 +747,7 @@ function transformRouter(options) {
745
747
  filePath,
746
748
  source,
747
749
  escapeTags: pluginOptions.escapeTags,
750
+ pathType: pluginOptions.pathType,
748
751
  attributeName: pluginOptions.attributeName
749
752
  });
750
753
  }
@@ -1262,7 +1265,6 @@ function dispatchPromptThroughIde(runtime, payload) {
1262
1265
  line: payload.line,
1263
1266
  column: payload.column,
1264
1267
  snippet: payload.snippet,
1265
- ...payload.screenshotContext ? { screenshotContext: payload.screenshotContext } : {},
1266
1268
  overrides: runtime.overrides,
1267
1269
  autoSend: runtime.autoSend
1268
1270
  });
@@ -1396,6 +1398,188 @@ function assertPathWithinIdeOpenScope(file, projectRoot) {
1396
1398
  }
1397
1399
  }
1398
1400
 
1401
+ // src/server/session-store.ts
1402
+ var DEFAULT_STATUS = "pending";
1403
+ function createAnnotationSessionStore(options = {}) {
1404
+ const sessions = /* @__PURE__ */ new Map();
1405
+ const listeners = /* @__PURE__ */ new Set();
1406
+ const now = options.now ?? (() => Date.now());
1407
+ const createId = options.createId ?? createRandomId;
1408
+ function findNewestMatchingSession(statuses) {
1409
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
1410
+ }
1411
+ function updateSessionStatus(id, status) {
1412
+ const session = sessions.get(id);
1413
+ if (!session) return null;
1414
+ const timestamp = now();
1415
+ session.status = status;
1416
+ session.updatedAt = timestamp;
1417
+ if (status === "acknowledged") {
1418
+ session.acknowledgedAt = timestamp;
1419
+ }
1420
+ if (status === "resolved") {
1421
+ session.resolvedAt = timestamp;
1422
+ }
1423
+ emit({ type: "session-status-updated", session });
1424
+ return cloneSession(session);
1425
+ }
1426
+ function claimSession(id, statuses) {
1427
+ const session = sessions.get(id);
1428
+ if (!session || statuses && !statuses.has(session.status)) return null;
1429
+ if (session.status === "acknowledged") return cloneSession(session);
1430
+ return updateSessionStatus(id, "acknowledged");
1431
+ }
1432
+ function emit(event) {
1433
+ const snapshot = cloneSession(event.session);
1434
+ for (const listener of listeners) {
1435
+ listener({ type: event.type, session: snapshot });
1436
+ }
1437
+ }
1438
+ const store = {
1439
+ createSession(input) {
1440
+ const timestamp = now();
1441
+ const session = {
1442
+ id: createId(),
1443
+ instruction: input.instruction?.trim() ?? "",
1444
+ annotations: cloneArray(input.annotations),
1445
+ ...input.deliveryMode ? { deliveryMode: input.deliveryMode } : {},
1446
+ status: DEFAULT_STATUS,
1447
+ messages: cloneArray(input.messages ?? []),
1448
+ createdAt: timestamp,
1449
+ updatedAt: timestamp,
1450
+ ...input.runtimeContext ? { runtimeContext: cloneValue(input.runtimeContext) } : {},
1451
+ ...input.cssContextPrompt?.trim() ? { cssContextPrompt: input.cssContextPrompt.trim() } : {},
1452
+ ...input.pageUrl ? { pageUrl: input.pageUrl } : {},
1453
+ ...input.route ? { route: input.route } : {}
1454
+ };
1455
+ sessions.set(session.id, session);
1456
+ emit({ type: "session-created", session });
1457
+ return cloneSession(session);
1458
+ },
1459
+ getSession(id) {
1460
+ const session = sessions.get(id);
1461
+ return session ? cloneSession(session) : null;
1462
+ },
1463
+ listSessions(options2 = {}) {
1464
+ const statuses = normalizeStatuses(options2.status);
1465
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt).map((session) => cloneSession(session));
1466
+ },
1467
+ async claimNextSession(options2 = {}) {
1468
+ const statuses = normalizeStatuses(DEFAULT_STATUS);
1469
+ const existingSession = findNewestMatchingSession(statuses);
1470
+ if (existingSession) {
1471
+ return {
1472
+ session: claimSession(existingSession.id, statuses),
1473
+ timedOut: false,
1474
+ matchedExisting: true
1475
+ };
1476
+ }
1477
+ const timeoutMs = normalizeTimeoutMs(options2.timeoutMs);
1478
+ if (timeoutMs === 0) {
1479
+ return {
1480
+ session: null,
1481
+ timedOut: true,
1482
+ matchedExisting: false
1483
+ };
1484
+ }
1485
+ return await new Promise((resolve2) => {
1486
+ let settled = false;
1487
+ let timeout = null;
1488
+ const finish = (result) => {
1489
+ if (settled) return;
1490
+ settled = true;
1491
+ unsubscribe();
1492
+ if (timeout) {
1493
+ clearTimeout(timeout);
1494
+ }
1495
+ resolve2(result);
1496
+ };
1497
+ const unsubscribe = this.subscribe((event) => {
1498
+ const session = claimSession(event.session.id, statuses);
1499
+ if (!session) return;
1500
+ finish({
1501
+ session,
1502
+ timedOut: false,
1503
+ matchedExisting: false,
1504
+ event: event.type
1505
+ });
1506
+ });
1507
+ if (timeoutMs !== null) {
1508
+ timeout = setTimeout(() => {
1509
+ finish({
1510
+ session: null,
1511
+ timedOut: true,
1512
+ matchedExisting: false
1513
+ });
1514
+ }, timeoutMs);
1515
+ }
1516
+ });
1517
+ },
1518
+ appendMessage(id, input) {
1519
+ const session = sessions.get(id);
1520
+ if (!session) return null;
1521
+ const timestamp = now();
1522
+ session.messages.push({
1523
+ id: createId(),
1524
+ role: input.role,
1525
+ text: input.text,
1526
+ createdAt: timestamp
1527
+ });
1528
+ session.updatedAt = timestamp;
1529
+ if (input.role === "agent" && isPendingLikeStatus(session.status)) {
1530
+ session.status = "in_progress";
1531
+ }
1532
+ emit({ type: "session-message-appended", session });
1533
+ return cloneSession(session);
1534
+ },
1535
+ updateStatus(id, status) {
1536
+ return updateSessionStatus(id, status);
1537
+ },
1538
+ subscribe(listener) {
1539
+ listeners.add(listener);
1540
+ return () => {
1541
+ listeners.delete(listener);
1542
+ };
1543
+ },
1544
+ clear() {
1545
+ sessions.clear();
1546
+ listeners.clear();
1547
+ }
1548
+ };
1549
+ return store;
1550
+ }
1551
+ var annotationSessionStore = createAnnotationSessionStore();
1552
+ function normalizeStatuses(status) {
1553
+ if (!status) return null;
1554
+ return new Set(Array.isArray(status) ? status : [status]);
1555
+ }
1556
+ function normalizeTimeoutMs(value) {
1557
+ if (value === void 0) return null;
1558
+ if (!Number.isFinite(value)) return 0;
1559
+ return Math.max(0, Math.floor(value));
1560
+ }
1561
+ function isPendingLikeStatus(status) {
1562
+ return status === "pending" || status === "acknowledged";
1563
+ }
1564
+ function hasAgentReply(session) {
1565
+ return session.messages.some((message) => message.role === "agent" && Boolean(message.text?.trim()));
1566
+ }
1567
+ function createRandomId() {
1568
+ return `annotation-session-${Math.random().toString(36).slice(2, 10)}`;
1569
+ }
1570
+ function cloneSession(session) {
1571
+ return cloneValue(session);
1572
+ }
1573
+ function cloneArray(value) {
1574
+ return cloneValue(value);
1575
+ }
1576
+ function cloneValue(value) {
1577
+ if (typeof structuredClone === "function") {
1578
+ return structuredClone(value);
1579
+ }
1580
+ return JSON.parse(JSON.stringify(value));
1581
+ }
1582
+
1399
1583
  // src/server/annotation-dispatch.ts
1400
1584
  var AnnotationDispatchError = class extends Error {
1401
1585
  constructor(message, errorCode) {
@@ -1404,20 +1588,30 @@ var AnnotationDispatchError = class extends Error {
1404
1588
  this.errorCode = errorCode;
1405
1589
  }
1406
1590
  };
1407
- async function dispatchAnnotationsToAi(req, state) {
1591
+ async function dispatchAnnotationsToAi(req, state, store = annotationSessionStore) {
1408
1592
  try {
1409
1593
  validateAnnotationDispatchRequest(req, state);
1410
1594
  const batch = normalizeAnnotationBatch(req);
1411
1595
  const prompt = buildAnnotationBatchPrompt(batch);
1596
+ const deliveryMode = normalizeDeliveryMode(req.deliveryMode);
1597
+ const session = store.createSession({
1598
+ instruction: batch.instruction,
1599
+ annotations: toSessionAnnotations(batch.annotations),
1600
+ deliveryMode,
1601
+ ...batch.runtimeContext ? { runtimeContext: batch.runtimeContext } : {},
1602
+ ...batch.cssContextPrompt ? { cssContextPrompt: batch.cssContextPrompt } : {}
1603
+ });
1412
1604
  const representativeTarget = batch.annotations[0]?.targets[0];
1413
- const runtime = resolvePromptDispatchRuntime(state);
1414
- return dispatchPromptThroughIde(runtime, {
1605
+ const dispatchResult = deliveryMode === "ide" ? dispatchPromptThroughIde(resolvePromptDispatchRuntime(state), {
1415
1606
  prompt,
1416
1607
  ...representativeTarget?.file ? { filePath: representativeTarget.file } : {},
1417
1608
  ...representativeTarget?.line ? { line: representativeTarget.line } : {},
1418
- ...representativeTarget?.column ? { column: representativeTarget.column } : {},
1419
- ...batch.screenshotContext ? { screenshotContext: batch.screenshotContext } : {}
1420
- });
1609
+ ...representativeTarget?.column ? { column: representativeTarget.column } : {}
1610
+ }) : { success: true };
1611
+ return {
1612
+ ...dispatchResult,
1613
+ session: toSessionSummary(session)
1614
+ };
1421
1615
  } catch (error) {
1422
1616
  return {
1423
1617
  success: false,
@@ -1426,6 +1620,41 @@ async function dispatchAnnotationsToAi(req, state) {
1426
1620
  };
1427
1621
  }
1428
1622
  }
1623
+ function normalizeDeliveryMode(input) {
1624
+ return input === "agent" ? "agent" : "ide";
1625
+ }
1626
+ function toSessionAnnotations(annotations) {
1627
+ return annotations.map((annotation) => ({
1628
+ id: `annotation-${annotation.index}`,
1629
+ note: annotation.note,
1630
+ intent: annotation.intent,
1631
+ targets: annotation.targets.map((target, targetIndex) => ({
1632
+ id: `annotation-${annotation.index}-target-${targetIndex + 1}`,
1633
+ label: target.label ?? "Unknown target",
1634
+ location: {
1635
+ file: target.file,
1636
+ line: target.line,
1637
+ column: target.column
1638
+ },
1639
+ ...target.selector ? { selector: target.selector } : {},
1640
+ ...target.snippet ? { snippet: target.snippet } : {},
1641
+ rect: {
1642
+ x: 0,
1643
+ y: 0,
1644
+ width: 0,
1645
+ height: 0
1646
+ }
1647
+ }))
1648
+ }));
1649
+ }
1650
+ function toSessionSummary(session) {
1651
+ return {
1652
+ id: session.id,
1653
+ status: session.status,
1654
+ createdAt: session.createdAt,
1655
+ updatedAt: session.updatedAt
1656
+ };
1657
+ }
1429
1658
  function validateAnnotationDispatchRequest(req, state) {
1430
1659
  if (!req.annotations.length) {
1431
1660
  throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
@@ -1446,9 +1675,7 @@ function validateAnnotationDispatchRequest(req, state) {
1446
1675
  function normalizeAnnotationBatch(req) {
1447
1676
  return {
1448
1677
  instruction: req.instruction?.trim() ?? "",
1449
- responseMode: req.responseMode ?? "unified",
1450
1678
  ...req.runtimeContext ? { runtimeContext: req.runtimeContext } : {},
1451
- ...req.screenshotContext ? { screenshotContext: req.screenshotContext } : {},
1452
1679
  ...req.cssContextPrompt?.trim() ? { cssContextPrompt: req.cssContextPrompt.trim() } : {},
1453
1680
  annotations: req.annotations.map((annotation, index) => ({
1454
1681
  index: index + 1,
@@ -1470,12 +1697,9 @@ function buildAnnotationBatchPrompt(batch) {
1470
1697
  const prompt = batch.instruction ? `${batch.instruction}
1471
1698
 
1472
1699
  ${body}` : body;
1473
- return appendScreenshotContextSection(
1474
- appendCssContextSection(
1475
- appendRuntimeContextSection(prompt, batch.runtimeContext),
1476
- batch.cssContextPrompt
1477
- ),
1478
- batch.screenshotContext
1700
+ return appendCssContextSection(
1701
+ appendRuntimeContextSection(prompt, batch.runtimeContext),
1702
+ batch.cssContextPrompt
1479
1703
  );
1480
1704
  }
1481
1705
  function appendCssContextSection(prompt, cssContextPrompt) {
@@ -1502,20 +1726,6 @@ function buildSelectedElementsPrompt(annotations) {
1502
1726
  }
1503
1727
  return lines.join("\n");
1504
1728
  }
1505
- function appendScreenshotContextSection(prompt, screenshotContext) {
1506
- if (!screenshotContext || !screenshotContext.imageDataUrl && !screenshotContext.imageAssetId) {
1507
- return prompt;
1508
- }
1509
- const lines = [
1510
- "Visual screenshot context attached:",
1511
- `- capturedAt=${screenshotContext.capturedAt}`,
1512
- `- mimeType=${screenshotContext.mimeType}`,
1513
- ...screenshotContext.imageAssetId ? [`- imageAssetId=${screenshotContext.imageAssetId}`] : []
1514
- ];
1515
- return `${prompt}
1516
-
1517
- ${lines.join("\n")}`;
1518
- }
1519
1729
  function appendRuntimeContextSection(prompt, runtimeContext) {
1520
1730
  if (!runtimeContext?.records.length) {
1521
1731
  return prompt;
@@ -1563,7 +1773,7 @@ async function buildClientConfig(serverState2) {
1563
1773
  ...info,
1564
1774
  prompts: resolveIntents(promptsConfig),
1565
1775
  hotKeys: userConfig["inspector.hotKey"] ?? "alt",
1566
- theme: userConfig["inspector.theme"] ?? "auto",
1776
+ annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "both",
1567
1777
  includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
1568
1778
  runtimeContext: {
1569
1779
  enabled: true,
@@ -1571,10 +1781,6 @@ async function buildClientConfig(serverState2) {
1571
1781
  maxRuntimeErrors: 3,
1572
1782
  maxFailedRequests: 2
1573
1783
  },
1574
- screenshotContext: {
1575
- enabled: false
1576
- },
1577
- annotationResponseMode: userConfig["prompt.annotationResponseMode"] ?? "unified",
1578
1784
  autoSend: userConfig["prompt.autoSend"] ?? false
1579
1785
  };
1580
1786
  }
@@ -1619,7 +1825,7 @@ function handleOpenFileRequest(body, serverState2) {
1619
1825
  else if (rawEditorHint === "vscodium") editorHint = "codium";
1620
1826
  else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
1621
1827
  serverLogger2.debug(
1622
- `IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
1828
+ `SOURCE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
1623
1829
  );
1624
1830
  if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
1625
1831
  let normalizedPath = absolutePath.replace(/\\/g, "/");
@@ -1628,7 +1834,7 @@ function handleOpenFileRequest(body, serverState2) {
1628
1834
  }
1629
1835
  const encodedPath = encodeURI(normalizedPath);
1630
1836
  const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
1631
- serverLogger2.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
1837
+ serverLogger2.debug(`SOURCE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
1632
1838
  try {
1633
1839
  if (process.platform === "darwin") {
1634
1840
  (0, import_node_child_process2.execFileSync)("open", [uri]);
@@ -1638,7 +1844,7 @@ function handleOpenFileRequest(body, serverState2) {
1638
1844
  (0, import_node_child_process2.execFileSync)("xdg-open", [uri]);
1639
1845
  }
1640
1846
  } catch (e) {
1641
- serverLogger2.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
1847
+ serverLogger2.error(`Failed to launch URI for SOURCE_OPEN (${uri}):`, e);
1642
1848
  (0, import_launch_ide2.launchIDE)({
1643
1849
  file: absolutePath,
1644
1850
  line: body.line,
@@ -1695,6 +1901,7 @@ function resolveProjectRoot() {
1695
1901
 
1696
1902
  // src/server/index.ts
1697
1903
  var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
1904
+ var PORT_FILE_NAME = "inspecto.port.json";
1698
1905
  var serverState = {
1699
1906
  port: null,
1700
1907
  running: false,
@@ -1703,6 +1910,42 @@ var serverState = {
1703
1910
  cwd: process.cwd()
1704
1911
  };
1705
1912
  var serverInstance = null;
1913
+ function getPortFilePath() {
1914
+ return import_node_path7.default.join(import_node_os2.default.tmpdir(), PORT_FILE_NAME);
1915
+ }
1916
+ function getProjectRootHash() {
1917
+ if (!serverState.projectRoot) return null;
1918
+ return import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1919
+ }
1920
+ function readPortData(portFile) {
1921
+ if (!import_node_fs4.default.existsSync(portFile)) return {};
1922
+ try {
1923
+ return JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1924
+ } catch {
1925
+ return {};
1926
+ }
1927
+ }
1928
+ function writeProjectPort(port) {
1929
+ const rootHash = getProjectRootHash();
1930
+ if (!rootHash) return;
1931
+ const portFile = getPortFilePath();
1932
+ const portData = readPortData(portFile);
1933
+ portData[rootHash] = port;
1934
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1935
+ }
1936
+ function removeProjectPort() {
1937
+ const rootHash = getProjectRootHash();
1938
+ if (!rootHash) return;
1939
+ const portFile = getPortFilePath();
1940
+ if (!import_node_fs4.default.existsSync(portFile)) return;
1941
+ const portData = readPortData(portFile);
1942
+ delete portData[rootHash];
1943
+ if (Object.keys(portData).length === 0) {
1944
+ import_node_fs4.default.unlinkSync(portFile);
1945
+ } else {
1946
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1947
+ }
1948
+ }
1706
1949
  async function startServer() {
1707
1950
  if (serverState.running && serverState.port !== null) {
1708
1951
  return serverState.port;
@@ -1736,7 +1979,7 @@ async function startServer() {
1736
1979
  });
1737
1980
  });
1738
1981
  await new Promise((resolve2, reject) => {
1739
- serverInstance.listen(port, "127.0.0.1", () => {
1982
+ serverInstance.listen(port, "0.0.0.0", () => {
1740
1983
  serverInstance.unref();
1741
1984
  resolve2();
1742
1985
  });
@@ -1747,37 +1990,18 @@ async function startServer() {
1747
1990
  });
1748
1991
  serverState.port = port;
1749
1992
  serverState.running = true;
1750
- const portFile = import_node_path7.default.join(import_node_os2.default.tmpdir(), "inspecto.port.json");
1751
1993
  try {
1752
- let portData = {};
1753
- if (import_node_fs4.default.existsSync(portFile)) {
1754
- try {
1755
- portData = JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1756
- } catch (_e) {
1757
- }
1758
- }
1759
- const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1760
- portData[rootHash] = port;
1761
- import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1994
+ writeProjectPort(port);
1762
1995
  } catch (_e) {
1763
1996
  serverLogger4.warn("Failed to write port file:", _e);
1764
1997
  }
1765
1998
  process.once("exit", () => {
1766
1999
  try {
1767
- if (import_node_fs4.default.existsSync(portFile)) {
1768
- const portData = JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1769
- const rootHash = import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1770
- delete portData[rootHash];
1771
- if (Object.keys(portData).length === 0) {
1772
- import_node_fs4.default.unlinkSync(portFile);
1773
- } else {
1774
- import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1775
- }
1776
- }
2000
+ removeProjectPort();
1777
2001
  } catch {
1778
2002
  }
1779
2003
  });
1780
- serverLogger4.info(`server running at http://127.0.0.1:${port}`);
2004
+ serverLogger4.info(`server running at http://0.0.0.0:${port}`);
1781
2005
  return port;
1782
2006
  }
1783
2007
  async function readBody(req) {
@@ -1829,7 +2053,7 @@ async function handleRequest(url, req, res) {
1829
2053
  }
1830
2054
  return;
1831
2055
  }
1832
- if (pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
2056
+ if ((pathname === import_types2.INSPECTO_API_PATHS.SOURCE_OPEN || pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN) && req.method === "POST") {
1833
2057
  let body;
1834
2058
  try {
1835
2059
  body = JSON.parse(await readBody(req));
@@ -1842,7 +2066,7 @@ async function handleRequest(url, req, res) {
1842
2066
  handleOpenFileRequest(body, serverState);
1843
2067
  } catch (err) {
1844
2068
  serverLogger4.warn(
1845
- `Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}. Reason: ${err.message}`
2069
+ `Security: Blocked path traversal attempt in SOURCE_OPEN: ${body.file}. Reason: ${err.message}`
1846
2070
  );
1847
2071
  res.writeHead(403, { "Content-Type": "application/json" });
1848
2072
  res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
@@ -1916,6 +2140,212 @@ async function handleRequest(url, req, res) {
1916
2140
  }
1917
2141
  return;
1918
2142
  }
2143
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_CLAIM_NEXT && req.method === "POST") {
2144
+ try {
2145
+ const rawBody = await readBody(req);
2146
+ const body = rawBody ? JSON.parse(rawBody) : {};
2147
+ const timeoutMs = normalizeSessionClaimTimeout(
2148
+ body.timeoutMs === void 0 ? null : String(body.timeoutMs)
2149
+ );
2150
+ const result = await annotationSessionStore.claimNextSession({
2151
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
2152
+ });
2153
+ res.writeHead(200, { "Content-Type": "application/json" });
2154
+ res.end(
2155
+ JSON.stringify({
2156
+ success: true,
2157
+ timedOut: result.timedOut,
2158
+ matchedExisting: result.matchedExisting,
2159
+ ...result.event ? { event: result.event } : {},
2160
+ ...result.session ? { session: result.session } : {}
2161
+ })
2162
+ );
2163
+ } catch (e) {
2164
+ serverLogger4.error(`Error parsing session claim request:`, e);
2165
+ res.writeHead(400, { "Content-Type": "application/json" });
2166
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2167
+ }
2168
+ return;
2169
+ }
2170
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_EVENTS && req.method === "GET") {
2171
+ const statusParam = url.searchParams.getAll("status");
2172
+ const statuses = statusParam.length ? new Set(statusParam) : null;
2173
+ const sessionId = url.searchParams.get("sessionId")?.trim() || null;
2174
+ res.writeHead(200, {
2175
+ "Content-Type": "text/event-stream",
2176
+ "Cache-Control": "no-cache",
2177
+ Connection: "keep-alive"
2178
+ });
2179
+ res.write(`event: ready
2180
+ data: ${JSON.stringify({ ok: true })}
2181
+
2182
+ `);
2183
+ const unsubscribe = annotationSessionStore.subscribe((event) => {
2184
+ if (sessionId && event.session.id !== sessionId) {
2185
+ return;
2186
+ }
2187
+ if (statuses && !statuses.has(event.session.status)) {
2188
+ return;
2189
+ }
2190
+ res.write(formatSessionSseEvent(event));
2191
+ });
2192
+ req.on("close", () => {
2193
+ unsubscribe();
2194
+ res.end();
2195
+ });
2196
+ return;
2197
+ }
2198
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSIONS && req.method === "GET") {
2199
+ const statusParam = url.searchParams.getAll("status");
2200
+ const sessions = annotationSessionStore.listSessions(
2201
+ statusParam.length ? {
2202
+ status: statusParam
2203
+ } : void 0
2204
+ );
2205
+ res.writeHead(200, { "Content-Type": "application/json" });
2206
+ res.end(JSON.stringify({ success: true, sessions }));
2207
+ return;
2208
+ }
2209
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && req.method === "GET") {
2210
+ const sessionId = pathname.substring(import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1);
2211
+ const session = annotationSessionStore.getSession(sessionId);
2212
+ if (!session) {
2213
+ res.writeHead(404, { "Content-Type": "application/json" });
2214
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2215
+ return;
2216
+ }
2217
+ res.writeHead(200, { "Content-Type": "application/json" });
2218
+ res.end(JSON.stringify({ success: true, session }));
2219
+ return;
2220
+ }
2221
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX) && req.method === "POST") {
2222
+ const sessionId = pathname.slice(
2223
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
2224
+ -import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX.length
2225
+ );
2226
+ try {
2227
+ const rawBody = await readBody(req);
2228
+ const body = JSON.parse(rawBody);
2229
+ if (!isAnnotationThreadRole(body.role)) {
2230
+ res.writeHead(400, { "Content-Type": "application/json" });
2231
+ res.end(JSON.stringify({ success: false, error: "Reply role is invalid." }));
2232
+ return;
2233
+ }
2234
+ if (!body.text?.trim()) {
2235
+ res.writeHead(400, { "Content-Type": "application/json" });
2236
+ res.end(JSON.stringify({ success: false, error: "Reply text is required." }));
2237
+ return;
2238
+ }
2239
+ const session = annotationSessionStore.appendMessage(sessionId, {
2240
+ role: body.role,
2241
+ text: body.text.trim()
2242
+ });
2243
+ if (!session) {
2244
+ res.writeHead(404, { "Content-Type": "application/json" });
2245
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2246
+ return;
2247
+ }
2248
+ res.writeHead(200, { "Content-Type": "application/json" });
2249
+ res.end(JSON.stringify({ success: true, session }));
2250
+ } catch (e) {
2251
+ serverLogger4.error(`Error parsing session reply request:`, e);
2252
+ res.writeHead(400, { "Content-Type": "application/json" });
2253
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2254
+ }
2255
+ return;
2256
+ }
2257
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX) && req.method === "POST") {
2258
+ const sessionId = pathname.slice(
2259
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
2260
+ -import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX.length
2261
+ );
2262
+ try {
2263
+ const rawBody = await readBody(req);
2264
+ const body = rawBody ? JSON.parse(rawBody) : {};
2265
+ const message = body.message?.trim();
2266
+ const existingSession = annotationSessionStore.getSession(sessionId);
2267
+ if (!existingSession) {
2268
+ res.writeHead(404, { "Content-Type": "application/json" });
2269
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2270
+ return;
2271
+ }
2272
+ if (!message && !hasAgentReply(existingSession)) {
2273
+ res.writeHead(400, { "Content-Type": "application/json" });
2274
+ res.end(
2275
+ JSON.stringify({
2276
+ success: false,
2277
+ error: "Resolve message is required until an agent reply is recorded."
2278
+ })
2279
+ );
2280
+ return;
2281
+ }
2282
+ if (message) {
2283
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
2284
+ role: "agent",
2285
+ text: message
2286
+ });
2287
+ if (!repliedSession) {
2288
+ res.writeHead(404, { "Content-Type": "application/json" });
2289
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2290
+ return;
2291
+ }
2292
+ }
2293
+ const session = annotationSessionStore.updateStatus(sessionId, "resolved");
2294
+ if (!session) {
2295
+ res.writeHead(404, { "Content-Type": "application/json" });
2296
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2297
+ return;
2298
+ }
2299
+ res.writeHead(200, { "Content-Type": "application/json" });
2300
+ res.end(JSON.stringify({ success: true, session }));
2301
+ } catch (e) {
2302
+ serverLogger4.error(`Error parsing session resolve request:`, e);
2303
+ res.writeHead(400, { "Content-Type": "application/json" });
2304
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2305
+ }
2306
+ return;
2307
+ }
2308
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX) && req.method === "POST") {
2309
+ const sessionId = pathname.slice(
2310
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
2311
+ -import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX.length
2312
+ );
2313
+ try {
2314
+ const rawBody = await readBody(req);
2315
+ const body = rawBody ? JSON.parse(rawBody) : {};
2316
+ const message = body.message?.trim();
2317
+ const existingSession = annotationSessionStore.getSession(sessionId);
2318
+ if (!existingSession) {
2319
+ res.writeHead(404, { "Content-Type": "application/json" });
2320
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2321
+ return;
2322
+ }
2323
+ if (message) {
2324
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
2325
+ role: "agent",
2326
+ text: message
2327
+ });
2328
+ if (!repliedSession) {
2329
+ res.writeHead(404, { "Content-Type": "application/json" });
2330
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2331
+ return;
2332
+ }
2333
+ }
2334
+ const session = annotationSessionStore.updateStatus(sessionId, "dismissed");
2335
+ if (!session) {
2336
+ res.writeHead(404, { "Content-Type": "application/json" });
2337
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2338
+ return;
2339
+ }
2340
+ res.writeHead(200, { "Content-Type": "application/json" });
2341
+ res.end(JSON.stringify({ success: true, session }));
2342
+ } catch (e) {
2343
+ serverLogger4.error(`Error parsing session dismiss request:`, e);
2344
+ res.writeHead(400, { "Content-Type": "application/json" });
2345
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2346
+ }
2347
+ return;
2348
+ }
1919
2349
  if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
1920
2350
  const ticketId = pathname.substring(import_types2.INSPECTO_API_PATHS.AI_TICKET.length + 1);
1921
2351
  const payloadStr = readTicket(ticketId);
@@ -1932,7 +2362,7 @@ async function handleRequest(url, req, res) {
1932
2362
  res.end(JSON.stringify({ error: "not found" }));
1933
2363
  }
1934
2364
  async function dispatchToAi(req) {
1935
- const { location, snippet, prompt, screenshotContext } = req;
2365
+ const { location, snippet, prompt } = req;
1936
2366
  const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
1937
2367
 
1938
2368
  \`\`\`
@@ -1945,8 +2375,7 @@ ${snippet}
1945
2375
  filePath: location.file,
1946
2376
  line: location.line,
1947
2377
  column: location.column,
1948
- snippet,
1949
- ...screenshotContext ? { screenshotContext } : {}
2378
+ snippet
1950
2379
  });
1951
2380
  }
1952
2381
  function getBatchDispatchStatusCode(errorCode, success) {
@@ -1955,6 +2384,21 @@ function getBatchDispatchStatusCode(errorCode, success) {
1955
2384
  if (errorCode === "FORBIDDEN_PATH") return 403;
1956
2385
  return 500;
1957
2386
  }
2387
+ function isAnnotationThreadRole(value) {
2388
+ return value === "user" || value === "agent" || value === "system";
2389
+ }
2390
+ function formatSessionSseEvent(event) {
2391
+ return `event: ${event.type}
2392
+ data: ${JSON.stringify(event)}
2393
+
2394
+ `;
2395
+ }
2396
+ function normalizeSessionClaimTimeout(value) {
2397
+ if (!value?.trim()) return 3e4;
2398
+ const parsed = Number.parseInt(value, 10);
2399
+ if (!Number.isFinite(parsed)) return 3e4;
2400
+ return Math.max(0, Math.min(parsed, 3e5));
2401
+ }
1958
2402
 
1959
2403
  // src/injectors/utils.ts
1960
2404
  var import_node_module = require("module");
@@ -1977,13 +2421,13 @@ var resolveClientModule = () => {
1977
2421
  function getWebpackHtmlScript(serverPort) {
1978
2422
  return `
1979
2423
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
1980
- window.addEventListener('load', () => {
1981
- if (window.InspectoClient) {
1982
- window.InspectoClient.mountInspector({
1983
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
1984
- });
1985
- }
1986
- });
2424
+ window.addEventListener('load', () => {
2425
+ if (window.InspectoClient) {
2426
+ window.InspectoClient.mountInspector({
2427
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
2428
+ });
2429
+ }
2430
+ });
1987
2431
  `;
1988
2432
  }
1989
2433
  function getWebpackAssetScript(serverPort) {
@@ -1993,7 +2437,7 @@ if (typeof window !== 'undefined') {
1993
2437
  const _initInspecto = () => {
1994
2438
  if (window.InspectoClient) {
1995
2439
  window.InspectoClient.mountInspector({
1996
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
2440
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
1997
2441
  });
1998
2442
  } else {
1999
2443
  setTimeout(_initInspecto, 100);
@@ -2084,7 +2528,7 @@ function getViteVirtualModuleScript(serverPort) {
2084
2528
  import { mountInspector } from '@inspecto-dev/core';
2085
2529
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
2086
2530
  mountInspector({
2087
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
2531
+ serverUrl: 'http://0.0.0.0:' + window.__AI_INSPECTOR_PORT__,
2088
2532
  });
2089
2533
  `;
2090
2534
  }