@aipanel/core 1.3.8 → 1.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.
@@ -56,7 +56,7 @@ export declare const NOTIFICATION_DURATION = 3000;
56
56
  export declare const INIT_MARKER = "__AIPANEL_INITIALIZED__";
57
57
  /** 选中元素存储键 */
58
58
  export declare const SELECTED_ELEMENTS_KEY = "__aipanel_selected_elements__";
59
- /** 页面会话标识键(sessionStorage,跨导航标识同一 Tab 的页面上下文) */
59
+ /** 页内会话标识键(sessionStorage,跨导航标识同一 Tab 的页面上下文) */
60
60
  export declare const SESSION_ID_KEY = "_aipanel_pk";
61
61
  /** ==================== 缓存目录 ==================== */
62
62
  /**
@@ -64,6 +64,18 @@ export declare const SESSION_ID_KEY = "_aipanel_pk";
64
64
  * 存放 opencode.json、dsh overlay 等运行时状态,不污染项目根目录。
65
65
  */
66
66
  export declare const AIPANEL_CACHE_DIR = "node_modules/.cache/aipanel";
67
+ /** ==================== 源码路径 ==================== */
68
+ /**
69
+ * 依赖安装目录名(判定文件是否来自依赖包的唯一依据)。
70
+ * 宿主侧上下文归一化与两个 Provider 的注入文案共用,避免各写一份 magic string。
71
+ */
72
+ export declare const DEPENDENCY_DIR_NAME = "node_modules";
73
+ /**
74
+ * 依赖内部文件的归属提示(注入给 agent 的上下文文案)。
75
+ * 两个 Provider 共用同一措辞:agent 拿到 node_modules 路径时最容易顺着它去改第三方实现,
76
+ * 归属必须显式声明,而不是让「源码文件路径」这行字暗示它是项目源码。
77
+ */
78
+ export declare const DEPENDENCY_SOURCE_NOTE = "\u4F9D\u8D56\u5305\u5185\u6587\u4EF6\uFF08node_modules\uFF09\uFF1A\u901A\u5E38\u4E0D\u5E94\u76F4\u63A5\u4FEE\u6539\uFF0C\u9875\u9762\u884C\u4E3A\u8BF7\u6539\u9879\u76EE\u5185\u6E90\u7801";
67
79
  /** ==================== Chrome DevTools ==================== */
68
80
  /** Chrome DevTools Protocol 默认端口 */
69
81
  export declare const CHROME_DEVTOOLS_PORT = 9222;
@@ -84,6 +96,8 @@ export declare const EXT_MSG: {
84
96
  readonly REQUEST_PAGE_CONTEXT: "REQUEST_PAGE_CONTEXT";
85
97
  readonly SELECTION_START: "SELECTION_START";
86
98
  readonly SELECTION_STOP: "SELECTION_STOP";
99
+ /** Side Panel → 目标 Tab:请目标页定位某个已选节点(跳回其页面并呼吸高亮) */
100
+ readonly LOCATE_NODE: "LOCATE_NODE";
87
101
  readonly CS_QUERY_WINDOW: "__CS_QUERY_WINDOW__";
88
102
  /** Side Panel → Background:立即轮询一次并回传当前服务信息 */
89
103
  readonly FORCE_POLL: "FORCE_POLL";
@@ -118,6 +132,8 @@ export declare const WIDGET_MSG: {
118
132
  readonly FOCUS_SESSION: "AIPANEL_FOCUS_SESSION";
119
133
  /** 无 deepLink 能力的 Provider:iframe 确认目标会话已激活且渲染稳定(携带 sessionId) */
120
134
  readonly SESSION_READY: "AIPANEL_SESSION_READY";
135
+ /** iframe → 宿主:点节点 chip 时把该节点交回挂件,跳回它被选中时的页面并闪烁高亮;载荷 { element } */
136
+ readonly LOCATE_NODE: "AIPANEL_LOCATE_NODE";
121
137
  };
122
138
  /** ==================== API 路径补充 ==================== */
123
139
  /** Provider 预热 API 路径 */
@@ -24,6 +24,8 @@ const INIT_MARKER = "__AIPANEL_INITIALIZED__";
24
24
  const SELECTED_ELEMENTS_KEY = "__aipanel_selected_elements__";
25
25
  const SESSION_ID_KEY = "_aipanel_pk";
26
26
  const AIPANEL_CACHE_DIR = "node_modules/.cache/aipanel";
27
+ const DEPENDENCY_DIR_NAME = "node_modules";
28
+ const DEPENDENCY_SOURCE_NOTE = `\u4F9D\u8D56\u5305\u5185\u6587\u4EF6\uFF08${DEPENDENCY_DIR_NAME}\uFF09\uFF1A\u901A\u5E38\u4E0D\u5E94\u76F4\u63A5\u4FEE\u6539\uFF0C\u9875\u9762\u884C\u4E3A\u8BF7\u6539\u9879\u76EE\u5185\u6E90\u7801`;
27
29
  const CHROME_DEVTOOLS_PORT = 9222;
28
30
  const CHROME_DEVTOOLS_CHECK_TIMEOUT = 2e3;
29
31
  const EXT_BROADCAST = {
@@ -39,6 +41,8 @@ const EXT_MSG = {
39
41
  REQUEST_PAGE_CONTEXT: "REQUEST_PAGE_CONTEXT",
40
42
  SELECTION_START: "SELECTION_START",
41
43
  SELECTION_STOP: "SELECTION_STOP",
44
+ /** Side Panel → 目标 Tab:请目标页定位某个已选节点(跳回其页面并呼吸高亮) */
45
+ LOCATE_NODE: "LOCATE_NODE",
42
46
  CS_QUERY_WINDOW: "__CS_QUERY_WINDOW__",
43
47
  /** Side Panel → Background:立即轮询一次并回传当前服务信息 */
44
48
  FORCE_POLL: "FORCE_POLL"
@@ -66,7 +70,9 @@ const WIDGET_MSG = {
66
70
  /** 无 deepLink 能力的 Provider:通知 iframe 聚焦指定会话 */
67
71
  FOCUS_SESSION: "AIPANEL_FOCUS_SESSION",
68
72
  /** 无 deepLink 能力的 Provider:iframe 确认目标会话已激活且渲染稳定(携带 sessionId) */
69
- SESSION_READY: "AIPANEL_SESSION_READY"
73
+ SESSION_READY: "AIPANEL_SESSION_READY",
74
+ /** iframe → 宿主:点节点 chip 时把该节点交回挂件,跳回它被选中时的页面并闪烁高亮;载荷 { element } */
75
+ LOCATE_NODE: "AIPANEL_LOCATE_NODE"
70
76
  };
71
77
  const WARMUP_API_PATH = "/__aipanel_warmup__";
72
78
  const BRIDGE_SCRIPT_PATH = "/__aipanel_bridge__.js";
@@ -160,6 +166,8 @@ export {
160
166
  DEFAULT_PROXY_PORT,
161
167
  DEFAULT_RETRIES,
162
168
  DEFAULT_WEB_PORT,
169
+ DEPENDENCY_DIR_NAME,
170
+ DEPENDENCY_SOURCE_NOTE,
163
171
  EXT_BROADCAST,
164
172
  EXT_MSG,
165
173
  HOST_EVENTS_API_PATH,
@@ -1,3 +1,4 @@
1
+ import { fileNameOf } from "./utils.mjs";
1
2
  const COMMAND_CHECK_TIMEOUT_MS = 6e4;
2
3
  const SOURCE_EXTENSIONS = [
3
4
  ".js",
@@ -12,7 +13,7 @@ const SOURCE_EXTENSIONS = [
12
13
  ];
13
14
  function matchesExtensions(filePath, extensions) {
14
15
  if (!extensions?.length) return true;
15
- const base = filePath.replace(/\\/g, "/").split("/").pop() ?? "";
16
+ const base = fileNameOf(filePath);
16
17
  const dot = base.lastIndexOf(".");
17
18
  if (dot <= 0) return false;
18
19
  return extensions.includes(base.slice(dot).toLowerCase());
@@ -1,4 +1,5 @@
1
1
  import { INSPECTOR_ADAPTER_IDS } from "../constants.mjs";
2
+ import { isDependencyPath } from "../utils.mjs";
2
3
  import {
3
4
  parseSourceLocation
4
5
  } from "./types.mjs";
@@ -63,9 +64,8 @@ function resolveFromVueInstance(element) {
63
64
  function mergeSourceLocations(markerLocation, instanceLocation) {
64
65
  if (!markerLocation?.file && !instanceLocation?.file) return null;
65
66
  if (markerLocation?.file && instanceLocation?.file) {
66
- const isNodeModules = (path) => path.includes("node_modules");
67
- if (!isNodeModules(markerLocation.file)) return markerLocation;
68
- if (!isNodeModules(instanceLocation.file)) return instanceLocation;
67
+ if (!isDependencyPath(markerLocation.file)) return markerLocation;
68
+ if (!isDependencyPath(instanceLocation.file)) return instanceLocation;
69
69
  return markerLocation;
70
70
  }
71
71
  return markerLocation?.file ? markerLocation : instanceLocation;
@@ -77,7 +77,11 @@ export interface LogFileConfig {
77
77
  export interface SelectedElement {
78
78
  /** 节点唯一 id(`@节点[n<id>]` 引用标记与上下文注入共用;由 ensureNodeId 分配) */
79
79
  id?: string;
80
- /** 文件路径 */
80
+ /**
81
+ * 文件路径(绝对路径)。
82
+ * 浏览器只上报相对路径(各 Inspector 基准不同),由 Vite 端点按宿主基准归一化后落库,
83
+ * 消费方(Provider 注入、UI 展示)拿到的恒为绝对路径。
84
+ */
81
85
  filePath: string | null;
82
86
  /** 行号 */
83
87
  line: number | null;
@@ -305,6 +309,8 @@ export type AIPanelWidgetEmits = {
305
309
  (e: "select-session", session: AIPanelWidgetSession): void;
306
310
  (e: "delete-session", session: AIPanelWidgetSession): void;
307
311
  (e: "click-selected-node", element: AIPanelSelectedElement): void;
312
+ /** 宿主无法在本地定位(扩展模式下页面不在本上下文):把节点交给宿主转发到目标页 */
313
+ (e: "locate-node", element: AIPanelSelectedElement): void;
308
314
  (e: "remove-selected-node", payload: AIPanelRemoveSelectedPayload): void;
309
315
  (e: "clear-selected-nodes"): void;
310
316
  (e: "empty-action"): void;
@@ -1,3 +1,33 @@
1
+ /**
2
+ * 路径分隔符归一化(反斜杠 → 正斜杠)。
3
+ * 浏览器侧没有 node:path,路径处理统一按字符串做;宿主的绝对路径在 Windows 上会带 `\`。
4
+ * @param filePath - 原始路径
5
+ * @returns 分隔符统一为正斜杠的路径
6
+ */
7
+ export declare function normalizePathSeparators(filePath: string): string;
8
+ /**
9
+ * 取路径的文件名(最后一段),兼容 `/` 与 `\` 分隔。
10
+ * 挂件气泡/节点卡片(浏览器)与进程日志(node)共用,避免各处自写 split("/").pop()。
11
+ * @param filePath - 原始路径
12
+ * @returns 文件名;路径为空或以分隔符结尾时返回空串
13
+ */
14
+ export declare function fileNameOf(filePath: string): string;
15
+ /**
16
+ * 路径是否位于依赖安装目录内(含 pnpm 的 `.pnpm/<pkg>@<ver>/node_modules/<pkg>` 真身)。
17
+ * 按路径分段判定,避免误伤名字里含 node_modules 的项目目录。
18
+ * @param filePath - 原始路径
19
+ * @returns 为依赖内部文件时 true
20
+ */
21
+ export declare function isDependencyPath(filePath: string): boolean;
22
+ /**
23
+ * 拼「路径:行:列」形式的源码位置(形如 `/proj/src/App.vue:53:11`)。
24
+ * 节点上下文注入(两个 Provider)与编辑器跳转(launch-editor 的位置格式)共用同一形态。
25
+ * @param filePath - 文件路径
26
+ * @param line - 行号(缺省时只返回路径)
27
+ * @param column - 列号(缺省时只到行)
28
+ * @returns 源码位置字符串
29
+ */
30
+ export declare function formatSourceLocation(filePath: string, line?: number | null, column?: number | null): string;
1
31
  /**
2
32
  * 取(或生成)元素的节点唯一 id:优先复用已赋值的 id,否则生成随机 id 并写回元素。
3
33
  * 同一引用在会话标记(`@节点[n<id>]`)与上下文注入里使用同一个 id;
@@ -1,4 +1,18 @@
1
- import { DEFAULT_RETRIES, RETRY_DELAY } from "./constants.mjs";
1
+ import { DEFAULT_RETRIES, DEPENDENCY_DIR_NAME, RETRY_DELAY } from "./constants.mjs";
2
+ function normalizePathSeparators(filePath) {
3
+ return filePath.replace(/\\/g, "/");
4
+ }
5
+ function fileNameOf(filePath) {
6
+ const normalized = normalizePathSeparators(filePath);
7
+ return normalized.slice(normalized.lastIndexOf("/") + 1);
8
+ }
9
+ function isDependencyPath(filePath) {
10
+ return normalizePathSeparators(filePath).split("/").includes(DEPENDENCY_DIR_NAME);
11
+ }
12
+ function formatSourceLocation(filePath, line, column) {
13
+ if (!line) return filePath;
14
+ return column ? `${filePath}:${line}:${column}` : `${filePath}:${line}`;
15
+ }
2
16
  function ensureNodeId(element) {
3
17
  if (element.id) return element.id;
4
18
  const random = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID().replace(/-/g, "").slice(0, 8) : Math.random().toString(36).slice(2, 10);
@@ -92,6 +106,10 @@ export {
92
106
  base64Encode,
93
107
  ensureNodeId,
94
108
  extractTextFromResponse,
109
+ fileNameOf,
110
+ formatSourceLocation,
111
+ isDependencyPath,
112
+ normalizePathSeparators,
95
113
  parseNodeMentions,
96
114
  sleep,
97
115
  toNodeMention,
@@ -2,6 +2,7 @@ var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
4
  import { LOG_PREFIX } from "../common/constants.mjs";
5
+ import { fileNameOf } from "../common/utils.mjs";
5
6
  import {
6
7
  LEVEL_NAMES,
7
8
  LogLevel,
@@ -39,7 +40,7 @@ function getCallerInfo(depth = 3) {
39
40
  const match = targetLine.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/);
40
41
  if (!match) return "";
41
42
  const [, funcName, filePath, line] = match;
42
- const fileName = filePath.split("/").pop() || filePath;
43
+ const fileName = fileNameOf(filePath) || filePath;
43
44
  const func = funcName || "<anonymous>";
44
45
  return `${fileName}:${line} ${func}`;
45
46
  }
@@ -30,6 +30,8 @@ __export(constants_exports, {
30
30
  DEFAULT_PROXY_PORT: () => DEFAULT_PROXY_PORT,
31
31
  DEFAULT_RETRIES: () => DEFAULT_RETRIES,
32
32
  DEFAULT_WEB_PORT: () => DEFAULT_WEB_PORT,
33
+ DEPENDENCY_DIR_NAME: () => DEPENDENCY_DIR_NAME,
34
+ DEPENDENCY_SOURCE_NOTE: () => DEPENDENCY_SOURCE_NOTE,
33
35
  EXT_BROADCAST: () => EXT_BROADCAST,
34
36
  EXT_MSG: () => EXT_MSG,
35
37
  HOST_EVENTS_API_PATH: () => HOST_EVENTS_API_PATH,
@@ -97,6 +99,8 @@ const INIT_MARKER = "__AIPANEL_INITIALIZED__";
97
99
  const SELECTED_ELEMENTS_KEY = "__aipanel_selected_elements__";
98
100
  const SESSION_ID_KEY = "_aipanel_pk";
99
101
  const AIPANEL_CACHE_DIR = "node_modules/.cache/aipanel";
102
+ const DEPENDENCY_DIR_NAME = "node_modules";
103
+ const DEPENDENCY_SOURCE_NOTE = `\u4F9D\u8D56\u5305\u5185\u6587\u4EF6\uFF08${DEPENDENCY_DIR_NAME}\uFF09\uFF1A\u901A\u5E38\u4E0D\u5E94\u76F4\u63A5\u4FEE\u6539\uFF0C\u9875\u9762\u884C\u4E3A\u8BF7\u6539\u9879\u76EE\u5185\u6E90\u7801`;
100
104
  const CHROME_DEVTOOLS_PORT = 9222;
101
105
  const CHROME_DEVTOOLS_CHECK_TIMEOUT = 2e3;
102
106
  const EXT_BROADCAST = {
@@ -112,6 +116,8 @@ const EXT_MSG = {
112
116
  REQUEST_PAGE_CONTEXT: "REQUEST_PAGE_CONTEXT",
113
117
  SELECTION_START: "SELECTION_START",
114
118
  SELECTION_STOP: "SELECTION_STOP",
119
+ /** Side Panel → 目标 Tab:请目标页定位某个已选节点(跳回其页面并呼吸高亮) */
120
+ LOCATE_NODE: "LOCATE_NODE",
115
121
  CS_QUERY_WINDOW: "__CS_QUERY_WINDOW__",
116
122
  /** Side Panel → Background:立即轮询一次并回传当前服务信息 */
117
123
  FORCE_POLL: "FORCE_POLL"
@@ -139,7 +145,9 @@ const WIDGET_MSG = {
139
145
  /** 无 deepLink 能力的 Provider:通知 iframe 聚焦指定会话 */
140
146
  FOCUS_SESSION: "AIPANEL_FOCUS_SESSION",
141
147
  /** 无 deepLink 能力的 Provider:iframe 确认目标会话已激活且渲染稳定(携带 sessionId) */
142
- SESSION_READY: "AIPANEL_SESSION_READY"
148
+ SESSION_READY: "AIPANEL_SESSION_READY",
149
+ /** iframe → 宿主:点节点 chip 时把该节点交回挂件,跳回它被选中时的页面并闪烁高亮;载荷 { element } */
150
+ LOCATE_NODE: "AIPANEL_LOCATE_NODE"
143
151
  };
144
152
  const WARMUP_API_PATH = "/__aipanel_warmup__";
145
153
  const BRIDGE_SCRIPT_PATH = "/__aipanel_bridge__.js";
@@ -234,6 +242,8 @@ const INSPECTOR_ADAPTER_IDS = {
234
242
  DEFAULT_PROXY_PORT,
235
243
  DEFAULT_RETRIES,
236
244
  DEFAULT_WEB_PORT,
245
+ DEPENDENCY_DIR_NAME,
246
+ DEPENDENCY_SOURCE_NOTE,
237
247
  EXT_BROADCAST,
238
248
  EXT_MSG,
239
249
  HOST_EVENTS_API_PATH,
@@ -56,7 +56,7 @@ export declare const NOTIFICATION_DURATION = 3000;
56
56
  export declare const INIT_MARKER = "__AIPANEL_INITIALIZED__";
57
57
  /** 选中元素存储键 */
58
58
  export declare const SELECTED_ELEMENTS_KEY = "__aipanel_selected_elements__";
59
- /** 页面会话标识键(sessionStorage,跨导航标识同一 Tab 的页面上下文) */
59
+ /** 页内会话标识键(sessionStorage,跨导航标识同一 Tab 的页面上下文) */
60
60
  export declare const SESSION_ID_KEY = "_aipanel_pk";
61
61
  /** ==================== 缓存目录 ==================== */
62
62
  /**
@@ -64,6 +64,18 @@ export declare const SESSION_ID_KEY = "_aipanel_pk";
64
64
  * 存放 opencode.json、dsh overlay 等运行时状态,不污染项目根目录。
65
65
  */
66
66
  export declare const AIPANEL_CACHE_DIR = "node_modules/.cache/aipanel";
67
+ /** ==================== 源码路径 ==================== */
68
+ /**
69
+ * 依赖安装目录名(判定文件是否来自依赖包的唯一依据)。
70
+ * 宿主侧上下文归一化与两个 Provider 的注入文案共用,避免各写一份 magic string。
71
+ */
72
+ export declare const DEPENDENCY_DIR_NAME = "node_modules";
73
+ /**
74
+ * 依赖内部文件的归属提示(注入给 agent 的上下文文案)。
75
+ * 两个 Provider 共用同一措辞:agent 拿到 node_modules 路径时最容易顺着它去改第三方实现,
76
+ * 归属必须显式声明,而不是让「源码文件路径」这行字暗示它是项目源码。
77
+ */
78
+ export declare const DEPENDENCY_SOURCE_NOTE = "\u4F9D\u8D56\u5305\u5185\u6587\u4EF6\uFF08node_modules\uFF09\uFF1A\u901A\u5E38\u4E0D\u5E94\u76F4\u63A5\u4FEE\u6539\uFF0C\u9875\u9762\u884C\u4E3A\u8BF7\u6539\u9879\u76EE\u5185\u6E90\u7801";
67
79
  /** ==================== Chrome DevTools ==================== */
68
80
  /** Chrome DevTools Protocol 默认端口 */
69
81
  export declare const CHROME_DEVTOOLS_PORT = 9222;
@@ -84,6 +96,8 @@ export declare const EXT_MSG: {
84
96
  readonly REQUEST_PAGE_CONTEXT: "REQUEST_PAGE_CONTEXT";
85
97
  readonly SELECTION_START: "SELECTION_START";
86
98
  readonly SELECTION_STOP: "SELECTION_STOP";
99
+ /** Side Panel → 目标 Tab:请目标页定位某个已选节点(跳回其页面并呼吸高亮) */
100
+ readonly LOCATE_NODE: "LOCATE_NODE";
87
101
  readonly CS_QUERY_WINDOW: "__CS_QUERY_WINDOW__";
88
102
  /** Side Panel → Background:立即轮询一次并回传当前服务信息 */
89
103
  readonly FORCE_POLL: "FORCE_POLL";
@@ -118,6 +132,8 @@ export declare const WIDGET_MSG: {
118
132
  readonly FOCUS_SESSION: "AIPANEL_FOCUS_SESSION";
119
133
  /** 无 deepLink 能力的 Provider:iframe 确认目标会话已激活且渲染稳定(携带 sessionId) */
120
134
  readonly SESSION_READY: "AIPANEL_SESSION_READY";
135
+ /** iframe → 宿主:点节点 chip 时把该节点交回挂件,跳回它被选中时的页面并闪烁高亮;载荷 { element } */
136
+ readonly LOCATE_NODE: "AIPANEL_LOCATE_NODE";
121
137
  };
122
138
  /** ==================== API 路径补充 ==================== */
123
139
  /** Provider 预热 API 路径 */
@@ -33,6 +33,7 @@ __export(diagnostics_exports, {
33
33
  resolveDiagnosticsPolicy: () => resolveDiagnosticsPolicy
34
34
  });
35
35
  module.exports = __toCommonJS(diagnostics_exports);
36
+ var import_utils = require("./utils.cjs");
36
37
  const COMMAND_CHECK_TIMEOUT_MS = 6e4;
37
38
  const SOURCE_EXTENSIONS = [
38
39
  ".js",
@@ -47,7 +48,7 @@ const SOURCE_EXTENSIONS = [
47
48
  ];
48
49
  function matchesExtensions(filePath, extensions) {
49
50
  if (!extensions?.length) return true;
50
- const base = filePath.replace(/\\/g, "/").split("/").pop() ?? "";
51
+ const base = (0, import_utils.fileNameOf)(filePath);
51
52
  const dot = base.lastIndexOf(".");
52
53
  if (dot <= 0) return false;
53
54
  return extensions.includes(base.slice(dot).toLowerCase());
@@ -21,6 +21,7 @@ __export(vue_exports, {
21
21
  });
22
22
  module.exports = __toCommonJS(vue_exports);
23
23
  var import_constants = require("../constants.cjs");
24
+ var import_utils = require("../utils.cjs");
24
25
  var import_types = require("./types.cjs");
25
26
  const SOURCE_LOCATION_ATTRIBUTE = "data-v-inspector";
26
27
  const VNODE_SOURCE_KEY = "__v_inspector";
@@ -83,9 +84,8 @@ function resolveFromVueInstance(element) {
83
84
  function mergeSourceLocations(markerLocation, instanceLocation) {
84
85
  if (!markerLocation?.file && !instanceLocation?.file) return null;
85
86
  if (markerLocation?.file && instanceLocation?.file) {
86
- const isNodeModules = (path) => path.includes("node_modules");
87
- if (!isNodeModules(markerLocation.file)) return markerLocation;
88
- if (!isNodeModules(instanceLocation.file)) return instanceLocation;
87
+ if (!(0, import_utils.isDependencyPath)(markerLocation.file)) return markerLocation;
88
+ if (!(0, import_utils.isDependencyPath)(instanceLocation.file)) return instanceLocation;
89
89
  return markerLocation;
90
90
  }
91
91
  return markerLocation?.file ? markerLocation : instanceLocation;
@@ -77,7 +77,11 @@ export interface LogFileConfig {
77
77
  export interface SelectedElement {
78
78
  /** 节点唯一 id(`@节点[n<id>]` 引用标记与上下文注入共用;由 ensureNodeId 分配) */
79
79
  id?: string;
80
- /** 文件路径 */
80
+ /**
81
+ * 文件路径(绝对路径)。
82
+ * 浏览器只上报相对路径(各 Inspector 基准不同),由 Vite 端点按宿主基准归一化后落库,
83
+ * 消费方(Provider 注入、UI 展示)拿到的恒为绝对路径。
84
+ */
81
85
  filePath: string | null;
82
86
  /** 行号 */
83
87
  line: number | null;
@@ -305,6 +309,8 @@ export type AIPanelWidgetEmits = {
305
309
  (e: "select-session", session: AIPanelWidgetSession): void;
306
310
  (e: "delete-session", session: AIPanelWidgetSession): void;
307
311
  (e: "click-selected-node", element: AIPanelSelectedElement): void;
312
+ /** 宿主无法在本地定位(扩展模式下页面不在本上下文):把节点交给宿主转发到目标页 */
313
+ (e: "locate-node", element: AIPanelSelectedElement): void;
308
314
  (e: "remove-selected-node", payload: AIPanelRemoveSelectedPayload): void;
309
315
  (e: "clear-selected-nodes"): void;
310
316
  (e: "empty-action"): void;
@@ -21,6 +21,10 @@ __export(utils_exports, {
21
21
  base64Encode: () => base64Encode,
22
22
  ensureNodeId: () => ensureNodeId,
23
23
  extractTextFromResponse: () => extractTextFromResponse,
24
+ fileNameOf: () => fileNameOf,
25
+ formatSourceLocation: () => formatSourceLocation,
26
+ isDependencyPath: () => isDependencyPath,
27
+ normalizePathSeparators: () => normalizePathSeparators,
24
28
  parseNodeMentions: () => parseNodeMentions,
25
29
  sleep: () => sleep,
26
30
  toNodeMention: () => toNodeMention,
@@ -30,6 +34,20 @@ __export(utils_exports, {
30
34
  });
31
35
  module.exports = __toCommonJS(utils_exports);
32
36
  var import_constants = require("./constants.cjs");
37
+ function normalizePathSeparators(filePath) {
38
+ return filePath.replace(/\\/g, "/");
39
+ }
40
+ function fileNameOf(filePath) {
41
+ const normalized = normalizePathSeparators(filePath);
42
+ return normalized.slice(normalized.lastIndexOf("/") + 1);
43
+ }
44
+ function isDependencyPath(filePath) {
45
+ return normalizePathSeparators(filePath).split("/").includes(import_constants.DEPENDENCY_DIR_NAME);
46
+ }
47
+ function formatSourceLocation(filePath, line, column) {
48
+ if (!line) return filePath;
49
+ return column ? `${filePath}:${line}:${column}` : `${filePath}:${line}`;
50
+ }
33
51
  function ensureNodeId(element) {
34
52
  if (element.id) return element.id;
35
53
  const random = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID().replace(/-/g, "").slice(0, 8) : Math.random().toString(36).slice(2, 10);
@@ -124,6 +142,10 @@ async function withRetries(fn, options = {}) {
124
142
  base64Encode,
125
143
  ensureNodeId,
126
144
  extractTextFromResponse,
145
+ fileNameOf,
146
+ formatSourceLocation,
147
+ isDependencyPath,
148
+ normalizePathSeparators,
127
149
  parseNodeMentions,
128
150
  sleep,
129
151
  toNodeMention,
@@ -1,3 +1,33 @@
1
+ /**
2
+ * 路径分隔符归一化(反斜杠 → 正斜杠)。
3
+ * 浏览器侧没有 node:path,路径处理统一按字符串做;宿主的绝对路径在 Windows 上会带 `\`。
4
+ * @param filePath - 原始路径
5
+ * @returns 分隔符统一为正斜杠的路径
6
+ */
7
+ export declare function normalizePathSeparators(filePath: string): string;
8
+ /**
9
+ * 取路径的文件名(最后一段),兼容 `/` 与 `\` 分隔。
10
+ * 挂件气泡/节点卡片(浏览器)与进程日志(node)共用,避免各处自写 split("/").pop()。
11
+ * @param filePath - 原始路径
12
+ * @returns 文件名;路径为空或以分隔符结尾时返回空串
13
+ */
14
+ export declare function fileNameOf(filePath: string): string;
15
+ /**
16
+ * 路径是否位于依赖安装目录内(含 pnpm 的 `.pnpm/<pkg>@<ver>/node_modules/<pkg>` 真身)。
17
+ * 按路径分段判定,避免误伤名字里含 node_modules 的项目目录。
18
+ * @param filePath - 原始路径
19
+ * @returns 为依赖内部文件时 true
20
+ */
21
+ export declare function isDependencyPath(filePath: string): boolean;
22
+ /**
23
+ * 拼「路径:行:列」形式的源码位置(形如 `/proj/src/App.vue:53:11`)。
24
+ * 节点上下文注入(两个 Provider)与编辑器跳转(launch-editor 的位置格式)共用同一形态。
25
+ * @param filePath - 文件路径
26
+ * @param line - 行号(缺省时只返回路径)
27
+ * @param column - 列号(缺省时只到行)
28
+ * @returns 源码位置字符串
29
+ */
30
+ export declare function formatSourceLocation(filePath: string, line?: number | null, column?: number | null): string;
1
31
  /**
2
32
  * 取(或生成)元素的节点唯一 id:优先复用已赋值的 id,否则生成随机 id 并写回元素。
3
33
  * 同一引用在会话标记(`@节点[n<id>]`)与上下文注入里使用同一个 id;
@@ -29,6 +29,7 @@ __export(node_logger_exports, {
29
29
  });
30
30
  module.exports = __toCommonJS(node_logger_exports);
31
31
  var import_constants = require("../common/constants.cjs");
32
+ var import_utils = require("../common/utils.cjs");
32
33
  var import_logger_core = require("../common/logger-core.cjs");
33
34
  const COLORS = {
34
35
  reset: "\x1B[0m",
@@ -58,7 +59,7 @@ function getCallerInfo(depth = 3) {
58
59
  const match = targetLine.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/);
59
60
  if (!match) return "";
60
61
  const [, funcName, filePath, line] = match;
61
- const fileName = filePath.split("/").pop() || filePath;
62
+ const fileName = (0, import_utils.fileNameOf)(filePath) || filePath;
62
63
  const func = funcName || "<anonymous>";
63
64
  return `${fileName}:${line} ${func}`;
64
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipanel/core",
3
- "version": "1.3.8",
3
+ "version": "1.3.9",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "main": "lib/index.cjs",