@inspecto-dev/plugin 0.3.8 → 0.3.10

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 (45) hide show
  1. package/dist/astro.cjs +572 -86
  2. package/dist/astro.cjs.map +1 -1
  3. package/dist/astro.d.cts +1 -1
  4. package/dist/astro.d.ts +1 -1
  5. package/dist/astro.js +571 -85
  6. package/dist/astro.js.map +1 -1
  7. package/dist/index.cjs +559 -83
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +1 -0
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.js +558 -82
  12. package/dist/index.js.map +1 -1
  13. package/dist/legacy/rspack/index.cjs +526 -74
  14. package/dist/legacy/rspack/index.cjs.map +1 -1
  15. package/dist/legacy/rspack/index.js +526 -74
  16. package/dist/legacy/rspack/index.js.map +1 -1
  17. package/dist/legacy/rspack/loader.cjs +4 -1
  18. package/dist/legacy/rspack/loader.cjs.map +1 -1
  19. package/dist/legacy/rspack/loader.js +3 -0
  20. package/dist/legacy/rspack/loader.js.map +1 -1
  21. package/dist/legacy/webpack4/index.cjs +526 -74
  22. package/dist/legacy/webpack4/index.cjs.map +1 -1
  23. package/dist/legacy/webpack4/index.js +526 -74
  24. package/dist/legacy/webpack4/index.js.map +1 -1
  25. package/dist/legacy/webpack4/loader.cjs +4 -1
  26. package/dist/legacy/webpack4/loader.cjs.map +1 -1
  27. package/dist/legacy/webpack4/loader.js +3 -0
  28. package/dist/legacy/webpack4/loader.js.map +1 -1
  29. package/dist/rollup.cjs +559 -83
  30. package/dist/rollup.cjs.map +1 -1
  31. package/dist/rollup.js +558 -82
  32. package/dist/rollup.js.map +1 -1
  33. package/dist/rspack.cjs +559 -83
  34. package/dist/rspack.cjs.map +1 -1
  35. package/dist/rspack.js +558 -82
  36. package/dist/rspack.js.map +1 -1
  37. package/dist/vite.cjs +559 -83
  38. package/dist/vite.cjs.map +1 -1
  39. package/dist/vite.js +558 -82
  40. package/dist/vite.js.map +1 -1
  41. package/dist/webpack.cjs +559 -83
  42. package/dist/webpack.cjs.map +1 -1
  43. package/dist/webpack.js +558 -82
  44. package/dist/webpack.js.map +1 -1
  45. package/package.json +8 -12
package/dist/index.cjs CHANGED
@@ -43,7 +43,7 @@ __export(src_exports, {
43
43
  });
44
44
  module.exports = __toCommonJS(src_exports);
45
45
 
46
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.10_typescript@5.9.3_yaml@2.8.3/node_modules/tsup/assets/cjs_shims.js
46
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.14_typescript@5.9.3_yaml@2.8.4/node_modules/tsup/assets/cjs_shims.js
47
47
  var getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
48
48
  var importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
49
49
 
@@ -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,
@@ -1693,8 +1899,27 @@ function resolveProjectRoot() {
1693
1899
  return gitRoot;
1694
1900
  }
1695
1901
 
1902
+ // src/server/server-url.ts
1903
+ function resolveServerHost(cwd, configRoot) {
1904
+ const userConfig = loadUserConfigSync(false, cwd, configRoot);
1905
+ const configuredHost = userConfig["server.host"]?.trim();
1906
+ if (configuredHost) return configuredHost;
1907
+ if (process.env["VITEST"]) return "127.0.0.1";
1908
+ return "127.0.0.1";
1909
+ }
1910
+ function resolvePublicServerUrl(args) {
1911
+ const userConfig = loadUserConfigSync(false, args.cwd, args.configRoot);
1912
+ const configuredPublicUrl = userConfig["server.publicUrl"]?.trim();
1913
+ if (configuredPublicUrl) {
1914
+ return configuredPublicUrl.replace(/\/$/, "");
1915
+ }
1916
+ const host = resolveServerHost(args.cwd, args.configRoot);
1917
+ return `http://${host}:${args.port}`;
1918
+ }
1919
+
1696
1920
  // src/server/index.ts
1697
1921
  var serverLogger4 = createLogger("inspecto:server", { logLevel: getGlobalLogLevel() });
1922
+ var PORT_FILE_NAME = "inspecto.port.json";
1698
1923
  var serverState = {
1699
1924
  port: null,
1700
1925
  running: false,
@@ -1703,6 +1928,42 @@ var serverState = {
1703
1928
  cwd: process.cwd()
1704
1929
  };
1705
1930
  var serverInstance = null;
1931
+ function getPortFilePath() {
1932
+ return import_node_path7.default.join(import_node_os2.default.tmpdir(), PORT_FILE_NAME);
1933
+ }
1934
+ function getProjectRootHash() {
1935
+ if (!serverState.projectRoot) return null;
1936
+ return import_node_crypto2.default.createHash("md5").update(serverState.projectRoot).digest("hex");
1937
+ }
1938
+ function readPortData(portFile) {
1939
+ if (!import_node_fs4.default.existsSync(portFile)) return {};
1940
+ try {
1941
+ return JSON.parse(import_node_fs4.default.readFileSync(portFile, "utf-8"));
1942
+ } catch {
1943
+ return {};
1944
+ }
1945
+ }
1946
+ function writeProjectPort(port) {
1947
+ const rootHash = getProjectRootHash();
1948
+ if (!rootHash) return;
1949
+ const portFile = getPortFilePath();
1950
+ const portData = readPortData(portFile);
1951
+ portData[rootHash] = port;
1952
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1953
+ }
1954
+ function removeProjectPort() {
1955
+ const rootHash = getProjectRootHash();
1956
+ if (!rootHash) return;
1957
+ const portFile = getPortFilePath();
1958
+ if (!import_node_fs4.default.existsSync(portFile)) return;
1959
+ const portData = readPortData(portFile);
1960
+ delete portData[rootHash];
1961
+ if (Object.keys(portData).length === 0) {
1962
+ import_node_fs4.default.unlinkSync(portFile);
1963
+ } else {
1964
+ import_node_fs4.default.writeFileSync(portFile, JSON.stringify(portData, null, 2), "utf-8");
1965
+ }
1966
+ }
1706
1967
  async function startServer() {
1707
1968
  if (serverState.running && serverState.port !== null) {
1708
1969
  return serverState.port;
@@ -1710,6 +1971,7 @@ async function startServer() {
1710
1971
  serverState.projectRoot = resolveProjectRoot();
1711
1972
  serverState.configRoot = serverState.projectRoot;
1712
1973
  serverState.cwd = process.cwd();
1974
+ const serverHost = resolveServerHost(serverState.cwd, serverState.configRoot);
1713
1975
  import_portfinder.default.basePort = 5678;
1714
1976
  const port = await import_portfinder.default.getPortPromise();
1715
1977
  watchConfig(
@@ -1736,7 +1998,7 @@ async function startServer() {
1736
1998
  });
1737
1999
  });
1738
2000
  await new Promise((resolve2, reject) => {
1739
- serverInstance.listen(port, "127.0.0.1", () => {
2001
+ serverInstance.listen(port, serverHost, () => {
1740
2002
  serverInstance.unref();
1741
2003
  resolve2();
1742
2004
  });
@@ -1747,37 +2009,18 @@ async function startServer() {
1747
2009
  });
1748
2010
  serverState.port = port;
1749
2011
  serverState.running = true;
1750
- const portFile = import_node_path7.default.join(import_node_os2.default.tmpdir(), "inspecto.port.json");
1751
2012
  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");
2013
+ writeProjectPort(port);
1762
2014
  } catch (_e) {
1763
2015
  serverLogger4.warn("Failed to write port file:", _e);
1764
2016
  }
1765
2017
  process.once("exit", () => {
1766
2018
  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
- }
2019
+ removeProjectPort();
1777
2020
  } catch {
1778
2021
  }
1779
2022
  });
1780
- serverLogger4.info(`server running at http://127.0.0.1:${port}`);
2023
+ serverLogger4.info(`server running at http://${serverHost}:${port}`);
1781
2024
  return port;
1782
2025
  }
1783
2026
  async function readBody(req) {
@@ -1829,7 +2072,7 @@ async function handleRequest(url, req, res) {
1829
2072
  }
1830
2073
  return;
1831
2074
  }
1832
- if (pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN && req.method === "POST") {
2075
+ if ((pathname === import_types2.INSPECTO_API_PATHS.SOURCE_OPEN || pathname === import_types2.INSPECTO_API_PATHS.IDE_OPEN) && req.method === "POST") {
1833
2076
  let body;
1834
2077
  try {
1835
2078
  body = JSON.parse(await readBody(req));
@@ -1842,7 +2085,7 @@ async function handleRequest(url, req, res) {
1842
2085
  handleOpenFileRequest(body, serverState);
1843
2086
  } catch (err) {
1844
2087
  serverLogger4.warn(
1845
- `Security: Blocked path traversal attempt in IDE_OPEN: ${body.file}. Reason: ${err.message}`
2088
+ `Security: Blocked path traversal attempt in SOURCE_OPEN: ${body.file}. Reason: ${err.message}`
1846
2089
  );
1847
2090
  res.writeHead(403, { "Content-Type": "application/json" });
1848
2091
  res.end(JSON.stringify({ error: "Access denied: File is outside of project workspace" }));
@@ -1916,6 +2159,212 @@ async function handleRequest(url, req, res) {
1916
2159
  }
1917
2160
  return;
1918
2161
  }
2162
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_CLAIM_NEXT && req.method === "POST") {
2163
+ try {
2164
+ const rawBody = await readBody(req);
2165
+ const body = rawBody ? JSON.parse(rawBody) : {};
2166
+ const timeoutMs = normalizeSessionClaimTimeout(
2167
+ body.timeoutMs === void 0 ? null : String(body.timeoutMs)
2168
+ );
2169
+ const result = await annotationSessionStore.claimNextSession({
2170
+ ...timeoutMs !== void 0 ? { timeoutMs } : {}
2171
+ });
2172
+ res.writeHead(200, { "Content-Type": "application/json" });
2173
+ res.end(
2174
+ JSON.stringify({
2175
+ success: true,
2176
+ timedOut: result.timedOut,
2177
+ matchedExisting: result.matchedExisting,
2178
+ ...result.event ? { event: result.event } : {},
2179
+ ...result.session ? { session: result.session } : {}
2180
+ })
2181
+ );
2182
+ } catch (e) {
2183
+ serverLogger4.error(`Error parsing session claim request:`, e);
2184
+ res.writeHead(400, { "Content-Type": "application/json" });
2185
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2186
+ }
2187
+ return;
2188
+ }
2189
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSION_EVENTS && req.method === "GET") {
2190
+ const statusParam = url.searchParams.getAll("status");
2191
+ const statuses = statusParam.length ? new Set(statusParam) : null;
2192
+ const sessionId = url.searchParams.get("sessionId")?.trim() || null;
2193
+ res.writeHead(200, {
2194
+ "Content-Type": "text/event-stream",
2195
+ "Cache-Control": "no-cache",
2196
+ Connection: "keep-alive"
2197
+ });
2198
+ res.write(`event: ready
2199
+ data: ${JSON.stringify({ ok: true })}
2200
+
2201
+ `);
2202
+ const unsubscribe = annotationSessionStore.subscribe((event) => {
2203
+ if (sessionId && event.session.id !== sessionId) {
2204
+ return;
2205
+ }
2206
+ if (statuses && !statuses.has(event.session.status)) {
2207
+ return;
2208
+ }
2209
+ res.write(formatSessionSseEvent(event));
2210
+ });
2211
+ req.on("close", () => {
2212
+ unsubscribe();
2213
+ res.end();
2214
+ });
2215
+ return;
2216
+ }
2217
+ if (pathname === import_types2.INSPECTO_API_PATHS.SESSIONS && req.method === "GET") {
2218
+ const statusParam = url.searchParams.getAll("status");
2219
+ const sessions = annotationSessionStore.listSessions(
2220
+ statusParam.length ? {
2221
+ status: statusParam
2222
+ } : void 0
2223
+ );
2224
+ res.writeHead(200, { "Content-Type": "application/json" });
2225
+ res.end(JSON.stringify({ success: true, sessions }));
2226
+ return;
2227
+ }
2228
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && req.method === "GET") {
2229
+ const sessionId = pathname.substring(import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1);
2230
+ const session = annotationSessionStore.getSession(sessionId);
2231
+ if (!session) {
2232
+ res.writeHead(404, { "Content-Type": "application/json" });
2233
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2234
+ return;
2235
+ }
2236
+ res.writeHead(200, { "Content-Type": "application/json" });
2237
+ res.end(JSON.stringify({ success: true, session }));
2238
+ return;
2239
+ }
2240
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX) && req.method === "POST") {
2241
+ const sessionId = pathname.slice(
2242
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
2243
+ -import_types2.INSPECTO_API_PATHS.SESSION_REPLY_SUFFIX.length
2244
+ );
2245
+ try {
2246
+ const rawBody = await readBody(req);
2247
+ const body = JSON.parse(rawBody);
2248
+ if (!isAnnotationThreadRole(body.role)) {
2249
+ res.writeHead(400, { "Content-Type": "application/json" });
2250
+ res.end(JSON.stringify({ success: false, error: "Reply role is invalid." }));
2251
+ return;
2252
+ }
2253
+ if (!body.text?.trim()) {
2254
+ res.writeHead(400, { "Content-Type": "application/json" });
2255
+ res.end(JSON.stringify({ success: false, error: "Reply text is required." }));
2256
+ return;
2257
+ }
2258
+ const session = annotationSessionStore.appendMessage(sessionId, {
2259
+ role: body.role,
2260
+ text: body.text.trim()
2261
+ });
2262
+ if (!session) {
2263
+ res.writeHead(404, { "Content-Type": "application/json" });
2264
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2265
+ return;
2266
+ }
2267
+ res.writeHead(200, { "Content-Type": "application/json" });
2268
+ res.end(JSON.stringify({ success: true, session }));
2269
+ } catch (e) {
2270
+ serverLogger4.error(`Error parsing session reply request:`, e);
2271
+ res.writeHead(400, { "Content-Type": "application/json" });
2272
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2273
+ }
2274
+ return;
2275
+ }
2276
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX) && req.method === "POST") {
2277
+ const sessionId = pathname.slice(
2278
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
2279
+ -import_types2.INSPECTO_API_PATHS.SESSION_RESOLVE_SUFFIX.length
2280
+ );
2281
+ try {
2282
+ const rawBody = await readBody(req);
2283
+ const body = rawBody ? JSON.parse(rawBody) : {};
2284
+ const message = body.message?.trim();
2285
+ const existingSession = annotationSessionStore.getSession(sessionId);
2286
+ if (!existingSession) {
2287
+ res.writeHead(404, { "Content-Type": "application/json" });
2288
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2289
+ return;
2290
+ }
2291
+ if (!message && !hasAgentReply(existingSession)) {
2292
+ res.writeHead(400, { "Content-Type": "application/json" });
2293
+ res.end(
2294
+ JSON.stringify({
2295
+ success: false,
2296
+ error: "Resolve message is required until an agent reply is recorded."
2297
+ })
2298
+ );
2299
+ return;
2300
+ }
2301
+ if (message) {
2302
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
2303
+ role: "agent",
2304
+ text: message
2305
+ });
2306
+ if (!repliedSession) {
2307
+ res.writeHead(404, { "Content-Type": "application/json" });
2308
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2309
+ return;
2310
+ }
2311
+ }
2312
+ const session = annotationSessionStore.updateStatus(sessionId, "resolved");
2313
+ if (!session) {
2314
+ res.writeHead(404, { "Content-Type": "application/json" });
2315
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2316
+ return;
2317
+ }
2318
+ res.writeHead(200, { "Content-Type": "application/json" });
2319
+ res.end(JSON.stringify({ success: true, session }));
2320
+ } catch (e) {
2321
+ serverLogger4.error(`Error parsing session resolve request:`, e);
2322
+ res.writeHead(400, { "Content-Type": "application/json" });
2323
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2324
+ }
2325
+ return;
2326
+ }
2327
+ if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.SESSIONS}/`) && pathname.endsWith(import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX) && req.method === "POST") {
2328
+ const sessionId = pathname.slice(
2329
+ import_types2.INSPECTO_API_PATHS.SESSIONS.length + 1,
2330
+ -import_types2.INSPECTO_API_PATHS.SESSION_DISMISS_SUFFIX.length
2331
+ );
2332
+ try {
2333
+ const rawBody = await readBody(req);
2334
+ const body = rawBody ? JSON.parse(rawBody) : {};
2335
+ const message = body.message?.trim();
2336
+ const existingSession = annotationSessionStore.getSession(sessionId);
2337
+ if (!existingSession) {
2338
+ res.writeHead(404, { "Content-Type": "application/json" });
2339
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2340
+ return;
2341
+ }
2342
+ if (message) {
2343
+ const repliedSession = annotationSessionStore.appendMessage(sessionId, {
2344
+ role: "agent",
2345
+ text: message
2346
+ });
2347
+ if (!repliedSession) {
2348
+ res.writeHead(404, { "Content-Type": "application/json" });
2349
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2350
+ return;
2351
+ }
2352
+ }
2353
+ const session = annotationSessionStore.updateStatus(sessionId, "dismissed");
2354
+ if (!session) {
2355
+ res.writeHead(404, { "Content-Type": "application/json" });
2356
+ res.end(JSON.stringify({ success: false, error: "Session not found" }));
2357
+ return;
2358
+ }
2359
+ res.writeHead(200, { "Content-Type": "application/json" });
2360
+ res.end(JSON.stringify({ success: true, session }));
2361
+ } catch (e) {
2362
+ serverLogger4.error(`Error parsing session dismiss request:`, e);
2363
+ res.writeHead(400, { "Content-Type": "application/json" });
2364
+ res.end(JSON.stringify({ success: false, error: "Invalid JSON body" }));
2365
+ }
2366
+ return;
2367
+ }
1919
2368
  if (pathname.startsWith(`${import_types2.INSPECTO_API_PATHS.AI_TICKET}/`) && req.method === "GET") {
1920
2369
  const ticketId = pathname.substring(import_types2.INSPECTO_API_PATHS.AI_TICKET.length + 1);
1921
2370
  const payloadStr = readTicket(ticketId);
@@ -1932,7 +2381,7 @@ async function handleRequest(url, req, res) {
1932
2381
  res.end(JSON.stringify({ error: "not found" }));
1933
2382
  }
1934
2383
  async function dispatchToAi(req) {
1935
- const { location, snippet, prompt, screenshotContext } = req;
2384
+ const { location, snippet, prompt } = req;
1936
2385
  const formattedPrompt = prompt ?? `Please help me with this code from \`${location.file}\` (line ${location.line}):
1937
2386
 
1938
2387
  \`\`\`
@@ -1945,8 +2394,7 @@ ${snippet}
1945
2394
  filePath: location.file,
1946
2395
  line: location.line,
1947
2396
  column: location.column,
1948
- snippet,
1949
- ...screenshotContext ? { screenshotContext } : {}
2397
+ snippet
1950
2398
  });
1951
2399
  }
1952
2400
  function getBatchDispatchStatusCode(errorCode, success) {
@@ -1955,6 +2403,21 @@ function getBatchDispatchStatusCode(errorCode, success) {
1955
2403
  if (errorCode === "FORBIDDEN_PATH") return 403;
1956
2404
  return 500;
1957
2405
  }
2406
+ function isAnnotationThreadRole(value) {
2407
+ return value === "user" || value === "agent" || value === "system";
2408
+ }
2409
+ function formatSessionSseEvent(event) {
2410
+ return `event: ${event.type}
2411
+ data: ${JSON.stringify(event)}
2412
+
2413
+ `;
2414
+ }
2415
+ function normalizeSessionClaimTimeout(value) {
2416
+ if (!value?.trim()) return 3e4;
2417
+ const parsed = Number.parseInt(value, 10);
2418
+ if (!Number.isFinite(parsed)) return 3e4;
2419
+ return Math.max(0, Math.min(parsed, 3e5));
2420
+ }
1958
2421
 
1959
2422
  // src/injectors/utils.ts
1960
2423
  var import_node_module = require("module");
@@ -1974,26 +2437,28 @@ var resolveClientModule = () => {
1974
2437
  };
1975
2438
 
1976
2439
  // src/injectors/webpack.ts
1977
- function getWebpackHtmlScript(serverPort) {
2440
+ function getWebpackHtmlScript(serverPort, publicServerUrl) {
1978
2441
  return `
1979
2442
  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
- });
2443
+ window.__AI_INSPECTOR_SERVER_URL__ = '${publicServerUrl ?? `http://127.0.0.1:${serverPort}`}';
2444
+ window.addEventListener('load', () => {
2445
+ if (window.InspectoClient) {
2446
+ window.InspectoClient.mountInspector({
2447
+ serverUrl: window.__AI_INSPECTOR_SERVER_URL__,
2448
+ });
2449
+ }
2450
+ });
1987
2451
  `;
1988
2452
  }
1989
- function getWebpackAssetScript(serverPort) {
2453
+ function getWebpackAssetScript(serverPort, publicServerUrl) {
1990
2454
  return `
1991
2455
  if (typeof window !== 'undefined') {
1992
2456
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
2457
+ window.__AI_INSPECTOR_SERVER_URL__ = '${publicServerUrl ?? `http://127.0.0.1:${serverPort}`}';
1993
2458
  const _initInspecto = () => {
1994
2459
  if (window.InspectoClient) {
1995
2460
  window.InspectoClient.mountInspector({
1996
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
2461
+ serverUrl: window.__AI_INSPECTOR_SERVER_URL__,
1997
2462
  });
1998
2463
  } else {
1999
2464
  setTimeout(_initInspecto, 100);
@@ -2007,7 +2472,7 @@ if (typeof window !== 'undefined') {
2007
2472
  }
2008
2473
  `;
2009
2474
  }
2010
- function injectWebpack(compiler, serverPortFn, resolveClientModule2) {
2475
+ function injectWebpack(compiler, serverPortFn, publicServerUrlFn, resolveClientModule2) {
2011
2476
  const inspectoClientPath = resolveClientModule2();
2012
2477
  if (compiler.webpack && compiler.webpack.EntryPlugin) {
2013
2478
  new compiler.webpack.EntryPlugin(compiler.context, inspectoClientPath, {
@@ -2022,11 +2487,12 @@ function injectWebpack(compiler, serverPortFn, resolveClientModule2) {
2022
2487
  const hooks = HtmlWebpackPlugin.constructor.getHooks(compilation);
2023
2488
  hooks.alterAssetTagGroups.tapPromise("inspecto-overlay", async (data) => {
2024
2489
  const port = await serverPortFn();
2490
+ const publicServerUrl = publicServerUrlFn(port);
2025
2491
  data.headTags.unshift({
2026
2492
  tagName: "script",
2027
2493
  voidTag: false,
2028
2494
  meta: { plugin: "inspecto-overlay" },
2029
- innerHTML: getWebpackHtmlScript(port)
2495
+ innerHTML: getWebpackHtmlScript(port, publicServerUrl)
2030
2496
  });
2031
2497
  return data;
2032
2498
  });
@@ -2039,13 +2505,14 @@ function injectWebpack(compiler, serverPortFn, resolveClientModule2) {
2039
2505
  },
2040
2506
  async (assets) => {
2041
2507
  const port = await serverPortFn();
2508
+ const publicServerUrl = publicServerUrlFn(port);
2042
2509
  const mainAssetKey = Object.keys(assets).find(
2043
2510
  (key) => key.endsWith(".js") && (key.includes("main") || key.includes("app") || key.includes("umi"))
2044
2511
  );
2045
2512
  if (!mainAssetKey) return;
2046
2513
  const originalSource = assets[mainAssetKey].source();
2047
2514
  assets[mainAssetKey] = new compiler.webpack.sources.RawSource(
2048
- getWebpackAssetScript(port) + "\n" + originalSource
2515
+ getWebpackAssetScript(port, publicServerUrl) + "\n" + originalSource
2049
2516
  );
2050
2517
  }
2051
2518
  );
@@ -2079,12 +2546,13 @@ function injectRspack(compiler, serverPortFn, resolveClientModule2) {
2079
2546
  }
2080
2547
 
2081
2548
  // src/injectors/vite.ts
2082
- function getViteVirtualModuleScript(serverPort) {
2549
+ function getViteVirtualModuleScript(serverPort, publicServerUrl) {
2083
2550
  return `
2084
2551
  import { mountInspector } from '@inspecto-dev/core';
2085
2552
  window.__AI_INSPECTOR_PORT__ = ${serverPort};
2553
+ window.__AI_INSPECTOR_SERVER_URL__ = '${publicServerUrl ?? `http://127.0.0.1:${serverPort}`}';
2086
2554
  mountInspector({
2087
- serverUrl: 'http://127.0.0.1:' + window.__AI_INSPECTOR_PORT__,
2555
+ serverUrl: window.__AI_INSPECTOR_SERVER_URL__,
2088
2556
  });
2089
2557
  `;
2090
2558
  }
@@ -2117,6 +2585,11 @@ var InspectoPlugin = (0, import_unplugin.createUnplugin)((userOptions = {}) => {
2117
2585
  }
2118
2586
  return serverPort;
2119
2587
  };
2588
+ const getPublicServerUrl = (port) => resolvePublicServerUrl({
2589
+ cwd: serverState.cwd || process.cwd(),
2590
+ configRoot: serverState.configRoot || projectRoot,
2591
+ port
2592
+ });
2120
2593
  return {
2121
2594
  name: "inspecto-overlay",
2122
2595
  enforce: "pre",
@@ -2129,7 +2602,7 @@ var InspectoPlugin = (0, import_unplugin.createUnplugin)((userOptions = {}) => {
2129
2602
  },
2130
2603
  webpack: (compiler) => {
2131
2604
  if (isProduction) return;
2132
- injectWebpack(compiler, ensureServer, resolveClientModule);
2605
+ injectWebpack(compiler, ensureServer, getPublicServerUrl, resolveClientModule);
2133
2606
  },
2134
2607
  rspack: (compiler) => {
2135
2608
  if (isProduction) return;
@@ -2155,7 +2628,10 @@ var InspectoPlugin = (0, import_unplugin.createUnplugin)((userOptions = {}) => {
2155
2628
  },
2156
2629
  load(id) {
2157
2630
  if (id === VITE_VIRTUAL_MODULE_ID) {
2158
- return getViteVirtualModuleScript(serverPort ?? DEFAULT_PORT);
2631
+ return getViteVirtualModuleScript(
2632
+ serverPort ?? DEFAULT_PORT,
2633
+ getPublicServerUrl(serverPort ?? DEFAULT_PORT)
2634
+ );
2159
2635
  }
2160
2636
  return null;
2161
2637
  },