@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/vite.js CHANGED
@@ -694,7 +694,9 @@ function transformRouter(options) {
694
694
  return transformSvelte({
695
695
  filePath,
696
696
  source,
697
+ projectRoot,
697
698
  escapeTags: pluginOptions.escapeTags,
699
+ pathType: pluginOptions.pathType,
698
700
  attributeName: pluginOptions.attributeName
699
701
  });
700
702
  }
@@ -703,6 +705,7 @@ function transformRouter(options) {
703
705
  filePath,
704
706
  source,
705
707
  escapeTags: pluginOptions.escapeTags,
708
+ pathType: pluginOptions.pathType,
706
709
  attributeName: pluginOptions.attributeName
707
710
  });
708
711
  }
@@ -1224,7 +1227,6 @@ function dispatchPromptThroughIde(runtime, payload) {
1224
1227
  line: payload.line,
1225
1228
  column: payload.column,
1226
1229
  snippet: payload.snippet,
1227
- ...payload.screenshotContext ? { screenshotContext: payload.screenshotContext } : {},
1228
1230
  overrides: runtime.overrides,
1229
1231
  autoSend: runtime.autoSend
1230
1232
  });
@@ -1358,6 +1360,188 @@ function assertPathWithinIdeOpenScope(file, projectRoot) {
1358
1360
  }
1359
1361
  }
1360
1362
 
1363
+ // src/server/session-store.ts
1364
+ var DEFAULT_STATUS = "pending";
1365
+ function createAnnotationSessionStore(options = {}) {
1366
+ const sessions = /* @__PURE__ */ new Map();
1367
+ const listeners = /* @__PURE__ */ new Set();
1368
+ const now = options.now ?? (() => Date.now());
1369
+ const createId = options.createId ?? createRandomId;
1370
+ function findNewestMatchingSession(statuses) {
1371
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt)[0] ?? null;
1372
+ }
1373
+ function updateSessionStatus(id, status) {
1374
+ const session = sessions.get(id);
1375
+ if (!session) return null;
1376
+ const timestamp = now();
1377
+ session.status = status;
1378
+ session.updatedAt = timestamp;
1379
+ if (status === "acknowledged") {
1380
+ session.acknowledgedAt = timestamp;
1381
+ }
1382
+ if (status === "resolved") {
1383
+ session.resolvedAt = timestamp;
1384
+ }
1385
+ emit({ type: "session-status-updated", session });
1386
+ return cloneSession(session);
1387
+ }
1388
+ function claimSession(id, statuses) {
1389
+ const session = sessions.get(id);
1390
+ if (!session || statuses && !statuses.has(session.status)) return null;
1391
+ if (session.status === "acknowledged") return cloneSession(session);
1392
+ return updateSessionStatus(id, "acknowledged");
1393
+ }
1394
+ function emit(event) {
1395
+ const snapshot = cloneSession(event.session);
1396
+ for (const listener of listeners) {
1397
+ listener({ type: event.type, session: snapshot });
1398
+ }
1399
+ }
1400
+ const store = {
1401
+ createSession(input) {
1402
+ const timestamp = now();
1403
+ const session = {
1404
+ id: createId(),
1405
+ instruction: input.instruction?.trim() ?? "",
1406
+ annotations: cloneArray(input.annotations),
1407
+ ...input.deliveryMode ? { deliveryMode: input.deliveryMode } : {},
1408
+ status: DEFAULT_STATUS,
1409
+ messages: cloneArray(input.messages ?? []),
1410
+ createdAt: timestamp,
1411
+ updatedAt: timestamp,
1412
+ ...input.runtimeContext ? { runtimeContext: cloneValue(input.runtimeContext) } : {},
1413
+ ...input.cssContextPrompt?.trim() ? { cssContextPrompt: input.cssContextPrompt.trim() } : {},
1414
+ ...input.pageUrl ? { pageUrl: input.pageUrl } : {},
1415
+ ...input.route ? { route: input.route } : {}
1416
+ };
1417
+ sessions.set(session.id, session);
1418
+ emit({ type: "session-created", session });
1419
+ return cloneSession(session);
1420
+ },
1421
+ getSession(id) {
1422
+ const session = sessions.get(id);
1423
+ return session ? cloneSession(session) : null;
1424
+ },
1425
+ listSessions(options2 = {}) {
1426
+ const statuses = normalizeStatuses(options2.status);
1427
+ return [...sessions.values()].filter((session) => statuses ? statuses.has(session.status) : true).sort((left, right) => right.updatedAt - left.updatedAt).map((session) => cloneSession(session));
1428
+ },
1429
+ async claimNextSession(options2 = {}) {
1430
+ const statuses = normalizeStatuses(DEFAULT_STATUS);
1431
+ const existingSession = findNewestMatchingSession(statuses);
1432
+ if (existingSession) {
1433
+ return {
1434
+ session: claimSession(existingSession.id, statuses),
1435
+ timedOut: false,
1436
+ matchedExisting: true
1437
+ };
1438
+ }
1439
+ const timeoutMs = normalizeTimeoutMs(options2.timeoutMs);
1440
+ if (timeoutMs === 0) {
1441
+ return {
1442
+ session: null,
1443
+ timedOut: true,
1444
+ matchedExisting: false
1445
+ };
1446
+ }
1447
+ return await new Promise((resolve2) => {
1448
+ let settled = false;
1449
+ let timeout = null;
1450
+ const finish = (result) => {
1451
+ if (settled) return;
1452
+ settled = true;
1453
+ unsubscribe();
1454
+ if (timeout) {
1455
+ clearTimeout(timeout);
1456
+ }
1457
+ resolve2(result);
1458
+ };
1459
+ const unsubscribe = this.subscribe((event) => {
1460
+ const session = claimSession(event.session.id, statuses);
1461
+ if (!session) return;
1462
+ finish({
1463
+ session,
1464
+ timedOut: false,
1465
+ matchedExisting: false,
1466
+ event: event.type
1467
+ });
1468
+ });
1469
+ if (timeoutMs !== null) {
1470
+ timeout = setTimeout(() => {
1471
+ finish({
1472
+ session: null,
1473
+ timedOut: true,
1474
+ matchedExisting: false
1475
+ });
1476
+ }, timeoutMs);
1477
+ }
1478
+ });
1479
+ },
1480
+ appendMessage(id, input) {
1481
+ const session = sessions.get(id);
1482
+ if (!session) return null;
1483
+ const timestamp = now();
1484
+ session.messages.push({
1485
+ id: createId(),
1486
+ role: input.role,
1487
+ text: input.text,
1488
+ createdAt: timestamp
1489
+ });
1490
+ session.updatedAt = timestamp;
1491
+ if (input.role === "agent" && isPendingLikeStatus(session.status)) {
1492
+ session.status = "in_progress";
1493
+ }
1494
+ emit({ type: "session-message-appended", session });
1495
+ return cloneSession(session);
1496
+ },
1497
+ updateStatus(id, status) {
1498
+ return updateSessionStatus(id, status);
1499
+ },
1500
+ subscribe(listener) {
1501
+ listeners.add(listener);
1502
+ return () => {
1503
+ listeners.delete(listener);
1504
+ };
1505
+ },
1506
+ clear() {
1507
+ sessions.clear();
1508
+ listeners.clear();
1509
+ }
1510
+ };
1511
+ return store;
1512
+ }
1513
+ var annotationSessionStore = createAnnotationSessionStore();
1514
+ function normalizeStatuses(status) {
1515
+ if (!status) return null;
1516
+ return new Set(Array.isArray(status) ? status : [status]);
1517
+ }
1518
+ function normalizeTimeoutMs(value) {
1519
+ if (value === void 0) return null;
1520
+ if (!Number.isFinite(value)) return 0;
1521
+ return Math.max(0, Math.floor(value));
1522
+ }
1523
+ function isPendingLikeStatus(status) {
1524
+ return status === "pending" || status === "acknowledged";
1525
+ }
1526
+ function hasAgentReply(session) {
1527
+ return session.messages.some((message) => message.role === "agent" && Boolean(message.text?.trim()));
1528
+ }
1529
+ function createRandomId() {
1530
+ return `annotation-session-${Math.random().toString(36).slice(2, 10)}`;
1531
+ }
1532
+ function cloneSession(session) {
1533
+ return cloneValue(session);
1534
+ }
1535
+ function cloneArray(value) {
1536
+ return cloneValue(value);
1537
+ }
1538
+ function cloneValue(value) {
1539
+ if (typeof structuredClone === "function") {
1540
+ return structuredClone(value);
1541
+ }
1542
+ return JSON.parse(JSON.stringify(value));
1543
+ }
1544
+
1361
1545
  // src/server/annotation-dispatch.ts
1362
1546
  var AnnotationDispatchError = class extends Error {
1363
1547
  constructor(message, errorCode) {
@@ -1366,20 +1550,30 @@ var AnnotationDispatchError = class extends Error {
1366
1550
  this.errorCode = errorCode;
1367
1551
  }
1368
1552
  };
1369
- async function dispatchAnnotationsToAi(req, state) {
1553
+ async function dispatchAnnotationsToAi(req, state, store = annotationSessionStore) {
1370
1554
  try {
1371
1555
  validateAnnotationDispatchRequest(req, state);
1372
1556
  const batch = normalizeAnnotationBatch(req);
1373
1557
  const prompt = buildAnnotationBatchPrompt(batch);
1558
+ const deliveryMode = normalizeDeliveryMode(req.deliveryMode);
1559
+ const session = store.createSession({
1560
+ instruction: batch.instruction,
1561
+ annotations: toSessionAnnotations(batch.annotations),
1562
+ deliveryMode,
1563
+ ...batch.runtimeContext ? { runtimeContext: batch.runtimeContext } : {},
1564
+ ...batch.cssContextPrompt ? { cssContextPrompt: batch.cssContextPrompt } : {}
1565
+ });
1374
1566
  const representativeTarget = batch.annotations[0]?.targets[0];
1375
- const runtime = resolvePromptDispatchRuntime(state);
1376
- return dispatchPromptThroughIde(runtime, {
1567
+ const dispatchResult = deliveryMode === "ide" ? dispatchPromptThroughIde(resolvePromptDispatchRuntime(state), {
1377
1568
  prompt,
1378
1569
  ...representativeTarget?.file ? { filePath: representativeTarget.file } : {},
1379
1570
  ...representativeTarget?.line ? { line: representativeTarget.line } : {},
1380
- ...representativeTarget?.column ? { column: representativeTarget.column } : {},
1381
- ...batch.screenshotContext ? { screenshotContext: batch.screenshotContext } : {}
1382
- });
1571
+ ...representativeTarget?.column ? { column: representativeTarget.column } : {}
1572
+ }) : { success: true };
1573
+ return {
1574
+ ...dispatchResult,
1575
+ session: toSessionSummary(session)
1576
+ };
1383
1577
  } catch (error) {
1384
1578
  return {
1385
1579
  success: false,
@@ -1388,6 +1582,41 @@ async function dispatchAnnotationsToAi(req, state) {
1388
1582
  };
1389
1583
  }
1390
1584
  }
1585
+ function normalizeDeliveryMode(input) {
1586
+ return input === "agent" ? "agent" : "ide";
1587
+ }
1588
+ function toSessionAnnotations(annotations) {
1589
+ return annotations.map((annotation) => ({
1590
+ id: `annotation-${annotation.index}`,
1591
+ note: annotation.note,
1592
+ intent: annotation.intent,
1593
+ targets: annotation.targets.map((target, targetIndex) => ({
1594
+ id: `annotation-${annotation.index}-target-${targetIndex + 1}`,
1595
+ label: target.label ?? "Unknown target",
1596
+ location: {
1597
+ file: target.file,
1598
+ line: target.line,
1599
+ column: target.column
1600
+ },
1601
+ ...target.selector ? { selector: target.selector } : {},
1602
+ ...target.snippet ? { snippet: target.snippet } : {},
1603
+ rect: {
1604
+ x: 0,
1605
+ y: 0,
1606
+ width: 0,
1607
+ height: 0
1608
+ }
1609
+ }))
1610
+ }));
1611
+ }
1612
+ function toSessionSummary(session) {
1613
+ return {
1614
+ id: session.id,
1615
+ status: session.status,
1616
+ createdAt: session.createdAt,
1617
+ updatedAt: session.updatedAt
1618
+ };
1619
+ }
1391
1620
  function validateAnnotationDispatchRequest(req, state) {
1392
1621
  if (!req.annotations.length) {
1393
1622
  throw new AnnotationDispatchError("At least one annotation is required.", "INVALID_REQUEST");
@@ -1408,9 +1637,7 @@ function validateAnnotationDispatchRequest(req, state) {
1408
1637
  function normalizeAnnotationBatch(req) {
1409
1638
  return {
1410
1639
  instruction: req.instruction?.trim() ?? "",
1411
- responseMode: req.responseMode ?? "unified",
1412
1640
  ...req.runtimeContext ? { runtimeContext: req.runtimeContext } : {},
1413
- ...req.screenshotContext ? { screenshotContext: req.screenshotContext } : {},
1414
1641
  ...req.cssContextPrompt?.trim() ? { cssContextPrompt: req.cssContextPrompt.trim() } : {},
1415
1642
  annotations: req.annotations.map((annotation, index) => ({
1416
1643
  index: index + 1,
@@ -1432,12 +1659,9 @@ function buildAnnotationBatchPrompt(batch) {
1432
1659
  const prompt = batch.instruction ? `${batch.instruction}
1433
1660
 
1434
1661
  ${body}` : body;
1435
- return appendScreenshotContextSection(
1436
- appendCssContextSection(
1437
- appendRuntimeContextSection(prompt, batch.runtimeContext),
1438
- batch.cssContextPrompt
1439
- ),
1440
- batch.screenshotContext
1662
+ return appendCssContextSection(
1663
+ appendRuntimeContextSection(prompt, batch.runtimeContext),
1664
+ batch.cssContextPrompt
1441
1665
  );
1442
1666
  }
1443
1667
  function appendCssContextSection(prompt, cssContextPrompt) {
@@ -1464,20 +1688,6 @@ function buildSelectedElementsPrompt(annotations) {
1464
1688
  }
1465
1689
  return lines.join("\n");
1466
1690
  }
1467
- function appendScreenshotContextSection(prompt, screenshotContext) {
1468
- if (!screenshotContext || !screenshotContext.imageDataUrl && !screenshotContext.imageAssetId) {
1469
- return prompt;
1470
- }
1471
- const lines = [
1472
- "Visual screenshot context attached:",
1473
- `- capturedAt=${screenshotContext.capturedAt}`,
1474
- `- mimeType=${screenshotContext.mimeType}`,
1475
- ...screenshotContext.imageAssetId ? [`- imageAssetId=${screenshotContext.imageAssetId}`] : []
1476
- ];
1477
- return `${prompt}
1478
-
1479
- ${lines.join("\n")}`;
1480
- }
1481
1691
  function appendRuntimeContextSection(prompt, runtimeContext) {
1482
1692
  if (!runtimeContext?.records.length) {
1483
1693
  return prompt;
@@ -1525,7 +1735,7 @@ async function buildClientConfig(serverState2) {
1525
1735
  ...info,
1526
1736
  prompts: resolveIntents(promptsConfig),
1527
1737
  hotKeys: userConfig["inspector.hotKey"] ?? "alt",
1528
- theme: userConfig["inspector.theme"] ?? "auto",
1738
+ annotateDeliveryMode: userConfig["annotate.deliveryMode"] ?? "both",
1529
1739
  includeSnippet: userConfig["prompt.includeSnippet"] ?? false,
1530
1740
  runtimeContext: {
1531
1741
  enabled: true,
@@ -1533,10 +1743,6 @@ async function buildClientConfig(serverState2) {
1533
1743
  maxRuntimeErrors: 3,
1534
1744
  maxFailedRequests: 2
1535
1745
  },
1536
- screenshotContext: {
1537
- enabled: false
1538
- },
1539
- annotationResponseMode: userConfig["prompt.annotationResponseMode"] ?? "unified",
1540
1746
  autoSend: userConfig["prompt.autoSend"] ?? false
1541
1747
  };
1542
1748
  }
@@ -1581,7 +1787,7 @@ function handleOpenFileRequest(body, serverState2) {
1581
1787
  else if (rawEditorHint === "vscodium") editorHint = "codium";
1582
1788
  else if (rawEditorHint === "trae-cn" || rawEditorHint === "trae") editorHint = "trae";
1583
1789
  serverLogger2.debug(
1584
- `IDE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
1790
+ `SOURCE_OPEN: activeIde=${activeIde}, activeIdeScheme=${activeIdeScheme}, configuredIde=${configuredIde} -> rawEditorHint=${rawEditorHint}, finalEditorHint=${editorHint}`
1585
1791
  );
1586
1792
  if (VSCODE_FAMILY_SCHEMES.includes(rawEditorHint)) {
1587
1793
  let normalizedPath = absolutePath.replace(/\\/g, "/");
@@ -1590,7 +1796,7 @@ function handleOpenFileRequest(body, serverState2) {
1590
1796
  }
1591
1797
  const encodedPath = encodeURI(normalizedPath);
1592
1798
  const uri = `${rawEditorHint}://file${encodedPath}:${body.line}:${body.column}`;
1593
- serverLogger2.debug(`IDE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
1799
+ serverLogger2.debug(`SOURCE_OPEN: Bypassing launchIDE, using URI scheme directly: ${uri}`);
1594
1800
  try {
1595
1801
  if (process.platform === "darwin") {
1596
1802
  execFileSync2("open", [uri]);
@@ -1600,7 +1806,7 @@ function handleOpenFileRequest(body, serverState2) {
1600
1806
  execFileSync2("xdg-open", [uri]);
1601
1807
  }
1602
1808
  } catch (e) {
1603
- serverLogger2.error(`Failed to launch URI for IDE_OPEN (${uri}):`, e);
1809
+ serverLogger2.error(`Failed to launch URI for SOURCE_OPEN (${uri}):`, e);
1604
1810
  launchIDE2({
1605
1811
  file: absolutePath,
1606
1812
  line: body.line,
@@ -1657,6 +1863,7 @@ function resolveProjectRoot() {
1657
1863
 
1658
1864
  // src/server/index.ts
1659
1865
  var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
1866
+ var PORT_FILE_NAME = "inspecto.port.json";
1660
1867
  var serverState = {
1661
1868
  port: null,
1662
1869
  running: false,
@@ -1665,6 +1872,42 @@ var serverState = {
1665
1872
  cwd: process.cwd()
1666
1873
  };
1667
1874
  var serverInstance = null;
1875
+ function getPortFilePath() {
1876
+ return path8.join(os2.tmpdir(), PORT_FILE_NAME);
1877
+ }
1878
+ function getProjectRootHash() {
1879
+ if (!serverState.projectRoot) return null;
1880
+ return crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
1881
+ }
1882
+ function readPortData(portFile) {
1883
+ if (!fs5.existsSync(portFile)) return {};
1884
+ try {
1885
+ return JSON.parse(fs5.readFileSync(portFile, "utf-8"));
1886
+ } catch {
1887
+ return {};
1888
+ }
1889
+ }
1890
+ function writeProjectPort(port) {
1891
+ const rootHash = getProjectRootHash();
1892
+ if (!rootHash) return;
1893
+ const portFile = getPortFilePath();
1894
+ const portData = readPortData(portFile);
1895
+ portData[rootHash] = port;
1896
+ fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1897
+ }
1898
+ function removeProjectPort() {
1899
+ const rootHash = getProjectRootHash();
1900
+ if (!rootHash) return;
1901
+ const portFile = getPortFilePath();
1902
+ if (!fs5.existsSync(portFile)) return;
1903
+ const portData = readPortData(portFile);
1904
+ delete portData[rootHash];
1905
+ if (Object.keys(portData).length === 0) {
1906
+ fs5.unlinkSync(portFile);
1907
+ } else {
1908
+ fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1909
+ }
1910
+ }
1668
1911
  async function startServer() {
1669
1912
  if (serverState.running && serverState.port !== null) {
1670
1913
  return serverState.port;
@@ -1698,7 +1941,7 @@ async function startServer() {
1698
1941
  });
1699
1942
  });
1700
1943
  await new Promise((resolve2, reject) => {
1701
- serverInstance.listen(port, "127.0.0.1", () => {
1944
+ serverInstance.listen(port, "0.0.0.0", () => {
1702
1945
  serverInstance.unref();
1703
1946
  resolve2();
1704
1947
  });
@@ -1709,37 +1952,18 @@ async function startServer() {
1709
1952
  });
1710
1953
  serverState.port = port;
1711
1954
  serverState.running = true;
1712
- const portFile = path8.join(os2.tmpdir(), "inspecto.port.json");
1713
1955
  try {
1714
- let portData = {};
1715
- if (fs5.existsSync(portFile)) {
1716
- try {
1717
- portData = JSON.parse(fs5.readFileSync(portFile, "utf-8"));
1718
- } catch (_e) {
1719
- }
1720
- }
1721
- const rootHash = crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
1722
- portData[rootHash] = port;
1723
- fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1956
+ writeProjectPort(port);
1724
1957
  } catch (_e) {
1725
1958
  serverLogger4.warn("Failed to write port file:", _e);
1726
1959
  }
1727
1960
  process.once("exit", () => {
1728
1961
  try {
1729
- if (fs5.existsSync(portFile)) {
1730
- const portData = JSON.parse(fs5.readFileSync(portFile, "utf-8"));
1731
- const rootHash = crypto2.createHash("md5").update(serverState.projectRoot).digest("hex");
1732
- delete portData[rootHash];
1733
- if (Object.keys(portData).length === 0) {
1734
- fs5.unlinkSync(portFile);
1735
- } else {
1736
- fs5.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1737
- }
1738
- }
1962
+ removeProjectPort();
1739
1963
  } catch {
1740
1964
  }
1741
1965
  });
1742
- serverLogger4.info(`server running at http://127.0.0.1:${port}`);
1966
+ serverLogger4.info(`server running at http://0.0.0.0:${port}`);
1743
1967
  return port;
1744
1968
  }
1745
1969
  async function readBody(req) {
@@ -1791,7 +2015,7 @@ async function handleRequest(url, req, res) {
1791
2015
  }
1792
2016
  return;
1793
2017
  }
1794
- if (pathname === INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
2018
+ if ((pathname === INSPECTO_API_PATHS.SOURCE_OPEN || pathname === INSPECTO_API_PATHS.IDE_OPEN) && req.method === "POST") {
1795
2019
  let body;
1796
2020
  try {
1797
2021
  body = JSON.parse(await readBody(req));
@@ -1804,7 +2028,7 @@ async function handleRequest(url, req, res) {
1804
2028
  handleOpenFileRequest(body, serverState);
1805
2029
  } catch (err) {
1806
2030
  serverLogger4.warn(
1807
- `Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}. Reason: ${err.message}`
2031
+ `Security: Blocked path traversal attempt in SOURCE_OPEN: ${body.file}. Reason: ${err.message}`
1808
2032
  );
1809
2033
  res.writeHead(403, { "Content-Type": "application/json" });
1810
2034
  res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
@@ -1878,6 +2102,212 @@ async function handleRequest(url, req, res) {
1878
2102
  }
1879
2103
  return;
1880
2104
  }
2105
+ if (pathname === INSPECTO_API_PATHS.SESSION_CLAIM_NEXT && req.method === "POST") {
2106
+ try {
2107
+ const rawBody = await readBody(req);
2108
+ const body = rawBody ? JSON.parse(rawBody) : {};
2109
+ const timeoutMs = normalizeSessionClaimTimeout(
2110
+ body.timeoutMs === void 0 ? null : String(body.timeoutMs)
2111
+ );
2112
+ const result = await annotationSessionStore.claimNextSession({
2113
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
2114
+ });
2115
+ res.writeHead(200, { "Content-Type": "application/json" });
2116
+ res.end(
2117
+ JSON.stringify({
2118
+ success: true,
2119
+ timedOut: result.timedOut,
2120
+ matchedExisting: result.matchedExisting,
2121
+ ...result.event ? { event: result.event } : {},
2122
+ ...result.session ? { session: result.session } : {}
2123
+ })
2124
+ );
2125
+ } catch (e) {
2126
+ serverLogger4.error(`Error parsing session claim request:`, e);
2127
+ res.writeHead(400, { "Content-Type": "application/json" });
2128
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2129
+ }
2130
+ return;
2131
+ }
2132
+ if (pathname === INSPECTO_API_PATHS.SESSION_EVENTS && req.method === "GET") {
2133
+ const statusParam = url.searchParams.getAll("status");
2134
+ const statuses = statusParam.length ? new Set(statusParam) : null;
2135
+ const sessionId = url.searchParams.get("sessionId")?.trim() || null;
2136
+ res.writeHead(200, {
2137
+ "Content-Type": "text/event-stream",
2138
+ "Cache-Control": "no-cache",
2139
+ Connection: "keep-alive"
2140
+ });
2141
+ res.write(`event: ready
2142
+ data: ${JSON.stringify({ ok: true })}
2143
+
2144
+ `);
2145
+ const unsubscribe = annotationSessionStore.subscribe((event) => {
2146
+ if (sessionId && event.session.id !== sessionId) {
2147
+ return;
2148
+ }
2149
+ if (statuses && !statuses.has(event.session.status)) {
2150
+ return;
2151
+ }
2152
+ res.write(formatSessionSseEvent(event));
2153
+ });
2154
+ req.on("close", () => {
2155
+ unsubscribe();
2156
+ res.end();
2157
+ });
2158
+ return;
2159
+ }
2160
+ if (pathname === INSPECTO_API_PATHS.SESSIONS && req.method === "GET") {
2161
+ const statusParam = url.searchParams.getAll("status");
2162
+ const sessions = annotationSessionStore.listSessions(
2163
+ statusParam.length ? {
2164
+ status: statusParam
2165
+ } : void 0
2166
+ );
2167
+ res.writeHead(200, { "Content-Type": "application/json" });
2168
+ res.end(JSON.stringify({ success: true, sessions }));
2169
+ return;
2170
+ }
2171
+ if (pathname.startsWith(`${INSPECTO_API_PATHS.SESSIONS}/`) && req.method === "GET") {
2172
+ const sessionId = pathname.substring(INSPECTO_API_PATHS.SESSIONS.length + 1);
2173
+ const session = annotationSessionStore.getSession(sessionId);
2174
+ if (!session) {
2175
+ res.writeHead(404, { "Content-Type": "application/json" });
2176
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2177
+ return;
2178
+ }
2179
+ res.writeHead(200, { "Content-Type": "application/json" });
2180
+ res.end(JSON.stringify({ success: true, session }));
2181
+ return;
2182
+ }
2183
+ if (pathname.startsWith(`${INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX) && req.method === "POST") {
2184
+ const sessionId = pathname.slice(
2185
+ INSPECTO_API_PATHS.SESSIONS.length + 1,
2186
+ -INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX.length
2187
+ );
2188
+ try {
2189
+ const rawBody = await readBody(req);
2190
+ const body = JSON.parse(rawBody);
2191
+ if (!isAnnotationThreadRole(body.role)) {
2192
+ res.writeHead(400, { "Content-Type": "application/json" });
2193
+ res.end(JSON.stringify({ success: false, error: "Reply role is invalid." }));
2194
+ return;
2195
+ }
2196
+ if (!body.text?.trim()) {
2197
+ res.writeHead(400, { "Content-Type": "application/json" });
2198
+ res.end(JSON.stringify({ success: false, error: "Reply text is required." }));
2199
+ return;
2200
+ }
2201
+ const session = annotationSessionStore.appendMessage(sessionId, {
2202
+ role: body.role,
2203
+ text: body.text.trim()
2204
+ });
2205
+ if (!session) {
2206
+ res.writeHead(404, { "Content-Type": "application/json" });
2207
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2208
+ return;
2209
+ }
2210
+ res.writeHead(200, { "Content-Type": "application/json" });
2211
+ res.end(JSON.stringify({ success: true, session }));
2212
+ } catch (e) {
2213
+ serverLogger4.error(`Error parsing session reply request:`, e);
2214
+ res.writeHead(400, { "Content-Type": "application/json" });
2215
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2216
+ }
2217
+ return;
2218
+ }
2219
+ if (pathname.startsWith(`${INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX) && req.method === "POST") {
2220
+ const sessionId = pathname.slice(
2221
+ INSPECTO_API_PATHS.SESSIONS.length + 1,
2222
+ -INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX.length
2223
+ );
2224
+ try {
2225
+ const rawBody = await readBody(req);
2226
+ const body = rawBody ? JSON.parse(rawBody) : {};
2227
+ const message = body.message?.trim();
2228
+ const existingSession = annotationSessionStore.getSession(sessionId);
2229
+ if (!existingSession) {
2230
+ res.writeHead(404, { "Content-Type": "application/json" });
2231
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2232
+ return;
2233
+ }
2234
+ if (!message && !hasAgentReply(existingSession)) {
2235
+ res.writeHead(400, { "Content-Type": "application/json" });
2236
+ res.end(
2237
+ JSON.stringify({
2238
+ success: false,
2239
+ error: "Resolve message is required until an agent reply is recorded."
2240
+ })
2241
+ );
2242
+ return;
2243
+ }
2244
+ if (message) {
2245
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
2246
+ role: "agent",
2247
+ text: message
2248
+ });
2249
+ if (!repliedSession) {
2250
+ res.writeHead(404, { "Content-Type": "application/json" });
2251
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2252
+ return;
2253
+ }
2254
+ }
2255
+ const session = annotationSessionStore.updateStatus(sessionId, "resolved");
2256
+ if (!session) {
2257
+ res.writeHead(404, { "Content-Type": "application/json" });
2258
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2259
+ return;
2260
+ }
2261
+ res.writeHead(200, { "Content-Type": "application/json" });
2262
+ res.end(JSON.stringify({ success: true, session }));
2263
+ } catch (e) {
2264
+ serverLogger4.error(`Error parsing session resolve request:`, e);
2265
+ res.writeHead(400, { "Content-Type": "application/json" });
2266
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2267
+ }
2268
+ return;
2269
+ }
2270
+ if (pathname.startsWith(`${INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX) && req.method === "POST") {
2271
+ const sessionId = pathname.slice(
2272
+ INSPECTO_API_PATHS.SESSIONS.length + 1,
2273
+ -INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX.length
2274
+ );
2275
+ try {
2276
+ const rawBody = await readBody(req);
2277
+ const body = rawBody ? JSON.parse(rawBody) : {};
2278
+ const message = body.message?.trim();
2279
+ const existingSession = annotationSessionStore.getSession(sessionId);
2280
+ if (!existingSession) {
2281
+ res.writeHead(404, { "Content-Type": "application/json" });
2282
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2283
+ return;
2284
+ }
2285
+ if (message) {
2286
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
2287
+ role: "agent",
2288
+ text: message
2289
+ });
2290
+ if (!repliedSession) {
2291
+ res.writeHead(404, { "Content-Type": "application/json" });
2292
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2293
+ return;
2294
+ }
2295
+ }
2296
+ const session = annotationSessionStore.updateStatus(sessionId, "dismissed");
2297
+ if (!session) {
2298
+ res.writeHead(404, { "Content-Type": "application/json" });
2299
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2300
+ return;
2301
+ }
2302
+ res.writeHead(200, { "Content-Type": "application/json" });
2303
+ res.end(JSON.stringify({ success: true, session }));
2304
+ } catch (e) {
2305
+ serverLogger4.error(`Error parsing session dismiss request:`, e);
2306
+ res.writeHead(400, { "Content-Type": "application/json" });
2307
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2308
+ }
2309
+ return;
2310
+ }
1881
2311
  if (pathname.startsWith(`${INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
1882
2312
  const ticketId = pathname.substring(INSPECTO_API_PATHS.AI_TICKET.length + 1);
1883
2313
  const payloadStr = readTicket(ticketId);
@@ -1894,7 +2324,7 @@ async function handleRequest(url, req, res) {
1894
2324
  res.end(JSON.stringify({ error: "not found" }));
1895
2325
  }
1896
2326
  async function dispatchToAi(req) {
1897
- const { location, snippet, prompt, screenshotContext } = req;
2327
+ const { location, snippet, prompt } = req;
1898
2328
  const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
1899
2329
 
1900
2330
  \`\`\`
@@ -1907,8 +2337,7 @@ ${snippet}
1907
2337
  filePath: location.file,
1908
2338
  line: location.line,
1909
2339
  column: location.column,
1910
- snippet,
1911
- ...screenshotContext ? { screenshotContext } : {}
2340
+ snippet
1912
2341
  });
1913
2342
  }
1914
2343
  function getBatchDispatchStatusCode(errorCode, success) {
@@ -1917,6 +2346,21 @@ function getBatchDispatchStatusCode(errorCode, success) {
1917
2346
  if (errorCode === "FORBIDDEN_PATH") return 403;
1918
2347
  return 500;
1919
2348
  }
2349
+ function isAnnotationThreadRole(value) {
2350
+ return value === "user" || value === "agent" || value === "system";
2351
+ }
2352
+ function formatSessionSseEvent(event) {
2353
+ return `event: ${event.type}
2354
+ data: ${JSON.stringify(event)}
2355
+
2356
+ `;
2357
+ }
2358
+ function normalizeSessionClaimTimeout(value) {
2359
+ if (!value?.trim()) return 3e4;
2360
+ const parsed = Number.parseInt(value, 10);
2361
+ if (!Number.isFinite(parsed)) return 3e4;
2362
+ return Math.max(0, Math.min(parsed, 3e5));
2363
+ }
1920
2364
 
1921
2365
  // src/injectors/utils.ts
1922
2366
  import { createRequire } from "module";
@@ -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
  }