comate-ui-inspector 0.1.2-alpha.4 → 0.1.2-alpha.6

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.
package/README.md CHANGED
@@ -33,7 +33,7 @@ ui-inspector install # 注册 native host + 个人级 hook,并打印
33
33
  - **生效域名**:默认已含 `localhost` / `127.0.0.1`;项目域名(如 `https://local.oreateai.com`)自己加,添加时浏览器会弹权限申请,允许后即在该域名注入选取脚本。
34
34
  - **传输通路**:
35
35
  - `本地(Native Messaging)`——默认,浏览器按需拉起 `host.mjs`,零常驻。
36
- - `远程(HTTP bridge)`——填 bridge 地址(默认 `http://127.0.0.1:8917`),用于远程开发(见下)。
36
+ - `远程(HTTP bridge)`——只填**本地端口**(host 固定 127.0.0.1,默认 `8917`),用于远程开发(见下)。
37
37
 
38
38
  ## 远程开发(HTTP bridge)
39
39
 
@@ -46,7 +46,7 @@ ui-inspector install --remote # --port 默认 8917;跳过无浏览
46
46
  ui-inspector link <remote-ssh-目标> --port <dev端口>
47
47
  ```
48
48
 
49
- `install --remote` 会打印 MCP 配置(贴进**远程**的 Comate 并 reload),并把 bridge 后台拉起(日志在 `~/.ui-inspector/bridge.log`)。`link` 本质是 `ssh -N -L 8917:127.0.0.1:8917 -L <dev端口>:127.0.0.1:<dev端口> <remote>`,保持前台、Ctrl-C 断开。
49
+ `install --remote` 会打印 MCP 配置(贴进**远程**的 Comate 并 reload),并把 bridge 后台拉起(日志在 `~/.ui-inspector/bridge.log`)。`link` 本质是 `ssh -N -L <本地bridge端口>:127.0.0.1:<远程bridge端口> -L <dev端口>:127.0.0.1:<dev端口> <remote>`,保持前台、Ctrl-C 断开。远程 bridge 端口不必和本地一致:本地用 `--bridge-port`、远程用 `--remote-bridge-port`(默认两者相同);弹窗里填的是**本地**那个端口。
50
50
 
51
51
  链路断了(ssh 掉线 / 远程 bridge 或 dev server 停了)时,先在本地自检:
52
52
 
@@ -56,7 +56,7 @@ ui-inspector doctor <remote> --port <dev端口> # 定位断在哪层并打印
56
56
 
57
57
  恢复顺序:先确保远程 bridge 活(`ui-inspector bridge` / `install --remote`)→ 再接本地隧道(`ui-inspector link`)→ 再看远程 dev server。
58
58
 
59
- 本地仍需一次:加载扩展(`chrome://extensions` 选 `extension` 目录),在弹窗把传输切到 `远程(HTTP bridge)`、加上你的 dev 域名。bridge 只绑 127.0.0.1、不暴露到网络,访问全程走 SSH 转发,所以 https 页面连 `127.0.0.1` 不触发混合内容拦截。
59
+ 本地仍需一次:加载扩展(`chrome://extensions` 选 `extension` 目录),在弹窗把传输切到 `远程(HTTP bridge)`、填本地端口、加上你的 dev 域名。bridge 只绑 127.0.0.1、不暴露到网络,访问全程走 SSH 转发,所以 https 页面连 `127.0.0.1` 不触发混合内容拦截。
60
60
 
61
61
  ## 卸载 / 状态
62
62
 
package/cli.mjs CHANGED
@@ -134,19 +134,28 @@ function install({remote, port}) {
134
134
  }
135
135
 
136
136
  // 本地笔记本:把远程 bridge 端口 + dev server 端口都转发到本地 127.0.0.1。
137
- function link({remote, devPort, bridgePort}) {
137
+ // 远程 bridge 监听端口可与本地不同:-L <本地bridge端口>:127.0.0.1:<远程bridge端口>。
138
+ function link({remote, devPort, bridgePort, remoteBridgePort}) {
138
139
  if (!remote) {
139
- out('用法:ui-inspector link <remote-ssh-目标> --port <dev端口> [--bridge-port 8917]');
140
+ out(
141
+ '用法:ui-inspector link <remote-ssh-目标> --port <dev端口> [--bridge-port 8917] [--remote-bridge-port <远程端口>]'
142
+ );
140
143
  out('例: ui-inspector link dev-box --port 5173');
141
144
  return;
142
145
  }
143
- const args = ['-N', '-L', `${bridgePort}:127.0.0.1:${bridgePort}`];
146
+ const rbp = remoteBridgePort || bridgePort;
147
+ const args = ['-N', '-L', `${bridgePort}:127.0.0.1:${rbp}`];
144
148
  if (devPort) {
145
149
  args.push('-L', `${devPort}:127.0.0.1:${devPort}`);
146
150
  }
147
151
  args.push(remote);
148
152
  out(`转发中(Ctrl-C 断开):ssh ${args.join(' ')}`);
149
- out(` bridge → 127.0.0.1:${bridgePort}${devPort ? `,dev → 127.0.0.1:${devPort}` : ''}`);
153
+ out(
154
+ ` bridge → 本地 127.0.0.1:${bridgePort} ⇢ 远程 127.0.0.1:${rbp}${devPort ? `;dev → 127.0.0.1:${devPort}` : ''}`
155
+ );
156
+ out(
157
+ ` 提示:弹窗里的端口填本地的 ${bridgePort};远程 bridge 需在 ${rbp} 上监听(ui-inspector bridge --port ${rbp})。`
158
+ );
150
159
  const child = spawn('ssh', args, {stdio: 'inherit'});
151
160
  child.on('exit', code => process.exit(code ?? 0));
152
161
  }
@@ -219,17 +228,20 @@ function parseLink() {
219
228
  let remote;
220
229
  let devPort;
221
230
  let bridgePort = '8917';
231
+ let remoteBridgePort;
222
232
  for (let i = 0; i < rest.length; i++) {
223
233
  const a = rest[i];
224
234
  if (a === '--port') {
225
235
  devPort = rest[++i];
226
236
  } else if (a === '--bridge-port') {
227
237
  bridgePort = rest[++i];
238
+ } else if (a === '--remote-bridge-port') {
239
+ remoteBridgePort = rest[++i];
228
240
  } else if (!a.startsWith('-')) {
229
241
  remote = a;
230
242
  }
231
243
  }
232
- return {remote, devPort, bridgePort};
244
+ return {remote, devPort, bridgePort, remoteBridgePort};
233
245
  }
234
246
 
235
247
  // 探测本地某端口的 http 可达性(2s 超时)。用 node:http 而非 fetch,兼容 node 18+。
@@ -4,17 +4,18 @@
4
4
  * 两件事:
5
5
  * 1) 传输适配:把内容脚本的 ingest/state 转发给本地后端。
6
6
  * - native(默认,本地开发):chrome.runtime.sendNativeMessage 按需拉起 host.mjs。
7
- * - http(远程开发):fetch 配置的 bridgeUrl(/ingest、/state),远程机上跑 bridge/server.mjs。
7
+ * - http(远程开发):fetch http://127.0.0.1:<bridgePort>(/ingest、/state),远程机上跑 bridge/server.mjs。
8
8
  * 2) 动态注册内容脚本:目标域名不再硬编码在 manifest,改由弹窗(popup)写入 storage,
9
9
  * 这里按配置用 chrome.scripting 注册(MAIN + ISOLATED 两份)。
10
10
  */
11
11
  const HOST = 'com.oreate.ui_inspector';
12
12
 
13
13
  // localhost / 127.0.0.1 是通用开发地址,作默认种子;项目专属域名(如 local.oreateai.com)在弹窗里加。
14
+ // 远程模式下扩展永远连本地 SSH 转发出来的 127.0.0.1,host 固定,只需配端口。
14
15
  const DEFAULTS = {
15
16
  domains: ['http://localhost/*', 'http://127.0.0.1/*'],
16
17
  transport: 'native',
17
- bridgeUrl: 'http://127.0.0.1:8917',
18
+ bridgePort: '8917',
18
19
  };
19
20
 
20
21
  function getConfig() {
@@ -34,8 +35,8 @@ function viaNative(message) {
34
35
  });
35
36
  }
36
37
 
37
- async function viaHttp(bridgeUrl, message) {
38
- const base = String(bridgeUrl || '').replace(/\/+$/, '');
38
+ async function viaHttp(port, message) {
39
+ const base = `http://127.0.0.1:${port}`;
39
40
  try {
40
41
  if (message.type === 'ingest') {
41
42
  const r = await fetch(`${base}/ingest`, {
@@ -54,7 +55,7 @@ async function viaHttp(bridgeUrl, message) {
54
55
 
55
56
  async function dispatch(message) {
56
57
  const cfg = await getConfig();
57
- return cfg.transport === 'http' ? viaHttp(cfg.bridgeUrl, message) : viaNative(message);
58
+ return cfg.transport === 'http' ? viaHttp(cfg.bridgePort, message) : viaNative(message);
58
59
  }
59
60
 
60
61
  chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => {
@@ -2,13 +2,20 @@
2
2
  <html lang="zh">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
5
+ <meta
6
+ name="viewport"
7
+ content="width=device-width, initial-scale=1"
8
+ />
6
9
  <style>
7
10
  body {
8
11
  width: 320px;
9
12
  margin: 0;
10
13
  padding: 14px;
11
- font: 13px/1.5 -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
14
+ font:
15
+ 13px/1.5 -apple-system,
16
+ BlinkMacSystemFont,
17
+ 'Segoe UI',
18
+ sans-serif;
12
19
  color: #1f2328;
13
20
  }
14
21
  h1 {
@@ -96,8 +103,17 @@
96
103
  <section>
97
104
  <div class="label">生效域名</div>
98
105
  <div class="row">
99
- <input id="domain-input" type="text" placeholder="如 https://local.oreateai.com" />
100
- <button id="add-domain" class="primary">添加</button>
106
+ <input
107
+ id="domain-input"
108
+ type="text"
109
+ placeholder="如 https://local.oreateai.com"
110
+ />
111
+ <button
112
+ id="add-domain"
113
+ class="primary"
114
+ >
115
+ 添加
116
+ </button>
101
117
  </div>
102
118
  <ul id="domain-list"></ul>
103
119
  <div class="hint">添加时浏览器会请求该域名的访问权限,允许后即在该域名注入选取脚本。</div>
@@ -105,15 +121,52 @@
105
121
 
106
122
  <section>
107
123
  <div class="label">传输通路</div>
108
- <label class="radio"><input type="radio" name="transport" value="native" /> 本地(Native Messaging,默认)</label>
109
- <label class="radio"><input type="radio" name="transport" value="http" /> 远程(HTTP bridge)</label>
110
- <div class="row" id="bridge-row" style="margin-top: 6px">
111
- <input id="bridge-url" type="text" placeholder="http://127.0.0.1:8917" />
124
+ <label class="radio">
125
+ <input
126
+ type="radio"
127
+ name="transport"
128
+ value="native"
129
+ />
130
+ 本地(Native Messaging,默认)
131
+ </label>
132
+ <label class="radio">
133
+ <input
134
+ type="radio"
135
+ name="transport"
136
+ value="http"
137
+ />
138
+ 远程(HTTP bridge)
139
+ </label>
140
+ <div id="bridge-config">
141
+ <div
142
+ class="row"
143
+ style="margin-top: 6px"
144
+ >
145
+ <span style="color: #656d76">127.0.0.1 :</span>
146
+ <input
147
+ id="bridge-port"
148
+ type="text"
149
+ inputmode="numeric"
150
+ placeholder="8917"
151
+ style="max-width: 90px"
152
+ />
153
+ </div>
154
+ <div class="hint">
155
+ 只填本地端口(host 固定 127.0.0.1)。远程开发:远程跑
156
+ <code>ui-inspector bridge</code>
157
+ ,
158
+ <code>ui-inspector link</code>
159
+ 把端口转发到本地。
160
+ </div>
112
161
  </div>
113
- <div class="hint">远程开发:在远程机跑 <code>ui-inspector bridge</code>,SSH 把端口转发到本地 127.0.0.1。</div>
114
162
  </section>
115
163
 
116
- <button id="save" class="primary">保存传输设置</button>
164
+ <button
165
+ id="save"
166
+ class="primary"
167
+ >
168
+ 保存传输设置
169
+ </button>
117
170
  <div id="msg"></div>
118
171
 
119
172
  <script src="popup.js"></script>
@@ -2,7 +2,7 @@
2
2
  const DEFAULTS = {
3
3
  domains: ['http://localhost/*', 'http://127.0.0.1/*'],
4
4
  transport: 'native',
5
- bridgeUrl: 'http://127.0.0.1:8917',
5
+ bridgePort: '8917',
6
6
  };
7
7
  const PROTECTED = new Set(DEFAULTS.domains); // 默认项不可删、不回收权限
8
8
 
@@ -99,23 +99,38 @@ async function removeDomain(match) {
99
99
  msg(`已删除 ${match}`);
100
100
  }
101
101
 
102
+ // 「本地」模式不需要 bridge 地址,隐藏输入框;「远程」才显示。
103
+ function toggleBridge() {
104
+ const transport = document.querySelector('input[name="transport"]:checked')?.value || 'native';
105
+ $('bridge-config').style.display = transport === 'http' ? 'block' : 'none';
106
+ }
107
+
102
108
  async function renderTransport() {
103
- const {transport, bridgeUrl} = await getConfig();
109
+ const {transport, bridgePort} = await getConfig();
104
110
  for (const r of document.querySelectorAll('input[name="transport"]')) {
105
111
  r.checked = r.value === transport;
106
112
  }
107
- $('bridge-url').value = bridgeUrl;
113
+ $('bridge-port').value = bridgePort;
114
+ toggleBridge();
108
115
  }
109
116
 
110
117
  async function saveTransport() {
111
118
  const transport = document.querySelector('input[name="transport"]:checked')?.value || 'native';
112
- const bridgeUrl = $('bridge-url').value.trim() || DEFAULTS.bridgeUrl;
113
- await chrome.storage.local.set({transport, bridgeUrl});
119
+ const raw = $('bridge-port').value.trim();
120
+ if (transport === 'http' && !/^\d+$/.test(raw)) {
121
+ msg('端口要是数字', false);
122
+ return;
123
+ }
124
+ const bridgePort = raw || DEFAULTS.bridgePort;
125
+ await chrome.storage.local.set({transport, bridgePort});
114
126
  msg('已保存传输设置');
115
127
  }
116
128
 
117
129
  $('add-domain').onclick = addDomain;
118
130
  $('save').onclick = saveTransport;
131
+ for (const r of document.querySelectorAll('input[name="transport"]')) {
132
+ r.addEventListener('change', toggleBridge);
133
+ }
119
134
  $('domain-input').addEventListener('keydown', e => {
120
135
  if (e.key === 'Enter') {
121
136
  addDomain();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comate-ui-inspector",
3
- "version": "0.1.2-alpha.4",
3
+ "version": "0.1.2-alpha.6",
4
4
  "description": "按住 Alt 点选页面元素,把「元素 + Vue 组件源码路径」交给 Comate 定位源码修改。浏览器扩展 + Native Messaging host + MCP + hook。",
5
5
  "type": "module",
6
6
  "license": "MIT",