@tianshu-ai/local-bridge 0.4.0 → 0.5.0

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
@@ -15,7 +15,7 @@ your machine tianshu server
15
15
  ┌──────────────┐ outbound WSS ┌──────────────────┐
16
16
  │ local-bridge │ ────────────────▶ │ /ws (Local Bridge │
17
17
  │ • browser │ register tools │ plugin) │
18
- │ • echo │ ◀──── tools/call ─│ → agent uses │
18
+ │ tools │ ◀──── tools/call ─│ → agent uses │
19
19
  └──────────────┘ ──── result ─────▶ │ your tools │
20
20
  └──────────────────┘
21
21
  ```
@@ -38,6 +38,25 @@ npx @tianshu-ai/local-bridge --server wss://your-tianshu.example.com/ws --token
38
38
  The **Local Bridge** panel in tianshu shows a ready-to-copy command with
39
39
  your server URL + token filled in.
40
40
 
41
+ ### macOS menu-bar app
42
+
43
+ Prefer a UI? Build a tiny native menu-bar app (no Electron) that wraps
44
+ the CLI — a menu-bar icon with Start/Stop + Settings (server / token /
45
+ browser engine / headless):
46
+
47
+ ```bash
48
+ npm i -g @tianshu-ai/local-bridge
49
+ tsbridge install-app --run
50
+ ```
51
+
52
+ That installs `Tianshu Bridge.app` to `~/Applications` (menu-bar only, no
53
+ Dock icon) and launches it. Click the bolt icon → Settings to configure,
54
+ then Start. Requires Xcode command line tools (`xcode-select --install`)
55
+ for the one-time `swiftc` build. First launch: right-click → Open
56
+ (unsigned local build).
57
+
58
+ (`app/install-app.sh` is also available for a clone-based build.)
59
+
41
60
  For a local dev server with auth off, the token is optional:
42
61
 
43
62
  ```bash
@@ -49,11 +68,15 @@ tsbridge --server ws://localhost:3110/ws
49
68
  ### Keeping it updated
50
69
 
51
70
  ```bash
52
- tsbridge update # check npm for a newer version
53
- tsbridge update --yes # check and self-update (npm i -g …@latest)
54
- tsbridge version # print the installed version
71
+ tsbridge update # update to the latest version if newer (default)
72
+ tsbridge update --check # just check; exit 1 if an update is available
73
+ tsbridge update --dry-run # print the npm command without running it
74
+ tsbridge update --tag next # target a non-`latest` dist-tag
75
+ tsbridge version # print the installed version
55
76
  ```
56
77
 
78
+ (Same semantics as `tianshu update`.)
79
+
57
80
  ### Flags
58
81
 
59
82
  | flag | meaning |
@@ -62,7 +85,7 @@ tsbridge version # print the installed version
62
85
  | `--token <token>` | connection token from the Local Bridge panel |
63
86
  | `--device <id>` | stable device id (default: hostname) |
64
87
  | `--label <name>` | human label shown in the panel |
65
- | `--no-browser` | expose only the connectivity `echo` tool |
88
+ | `--no-browser` | don't expose browser tools |
66
89
  | `--headless` | run the browser without a window (default: headful) |
67
90
  | `--cdp <url>` | connect to an already-running Chrome's CDP endpoint (default probe `http://127.0.0.1:9222`; `off` to skip) |
68
91
  | `--chrome-channel <ch>` | which installed browser to launch: `chrome` (default), `msedge`, … |
@@ -85,10 +108,16 @@ To expose your everyday browser, start Chrome with a debugging port:
85
108
 
86
109
  ## Tools
87
110
 
88
- - `echo` — connectivity check.
89
- - `browser_navigate` / `browser_get_text` / `browser_click` /
90
- `browser_screenshot` — drive your real local Chrome (via CDP connect
91
- or system-Chrome launch; automation-detection softened).
111
+ Both engines expose the **full Playwright-MCP toolset** (navigate,
112
+ snapshot, click, type, fill_form, evaluate, tabs, screenshot, …). Only
113
+ the underlying browser differs:
114
+
115
+ - **own** — `@playwright/mcp` pointed at *your* Chrome: connects over CDP
116
+ to a Chrome already running with `--remote-debugging-port`, else
117
+ launches your system Chrome (`--browser chrome`). Real cookies +
118
+ fingerprint, no Chromium download.
119
+ - **stealth** — `cloakbrowser-mcp`: the same toolset on CloakBrowser
120
+ stealth Chromium (source-level anti-bot-detection patches).
92
121
 
93
122
  ## Protocol
94
123
 
@@ -0,0 +1,311 @@
1
+ // Tianshu Local Bridge — macOS menu-bar app.
2
+ //
3
+ // A thin GUI wrapper around the `tsbridge` CLI: lives only in the menu
4
+ // bar (LSUIElement), lets you set the server URL / token / browser
5
+ // engine / headless, and Start/Stop the bridge. It spawns `tsbridge`
6
+ // with the chosen flags and shows connection status. Config persists to
7
+ // ~/.tianshu-bridge/config.json.
8
+ //
9
+ // Built on demand by install-app.sh via `swiftc` — no external deps.
10
+
11
+ import AppKit
12
+ import Foundation
13
+
14
+ // ─── config ─────────────────────────────────────────────────────────
15
+
16
+ struct Config: Codable {
17
+ var server: String = "ws://localhost:3110/ws"
18
+ var token: String = ""
19
+ var engine: String = "own" // "own" | "stealth"
20
+ var headless: Bool = false
21
+ var device: String = "" // empty → hostname
22
+
23
+ static var path: URL {
24
+ let dir = FileManager.default.homeDirectoryForCurrentUser
25
+ .appendingPathComponent(".tianshu-bridge", isDirectory: true)
26
+ try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
27
+ return dir.appendingPathComponent("config.json")
28
+ }
29
+
30
+ static func load() -> Config {
31
+ guard let data = try? Data(contentsOf: path),
32
+ let cfg = try? JSONDecoder().decode(Config.self, from: data) else {
33
+ return Config()
34
+ }
35
+ return cfg
36
+ }
37
+
38
+ func save() {
39
+ if let data = try? JSONEncoder().encode(self) {
40
+ try? data.write(to: Config.path)
41
+ }
42
+ }
43
+ }
44
+
45
+ // ─── locating tsbridge / node ────────────────────────────────────────
46
+
47
+ // GUI apps don't inherit the shell PATH, and tsbridge often lives under
48
+ // a Node version manager (nvm/fnm). Resolve a usable command + PATH.
49
+ func resolveTsbridge() -> (cmd: String, env: [String: String])? {
50
+ let fm = FileManager.default
51
+ let home = fm.homeDirectoryForCurrentUser.path
52
+ var candidates: [String] = [
53
+ "/usr/local/bin/tsbridge",
54
+ "/opt/homebrew/bin/tsbridge",
55
+ ]
56
+ // nvm: newest version dir that has tsbridge
57
+ let nvm = "\(home)/.nvm/versions/node"
58
+ if let vers = try? fm.contentsOfDirectory(atPath: nvm) {
59
+ for v in vers.sorted(by: >) {
60
+ candidates.append("\(nvm)/\(v)/bin/tsbridge")
61
+ }
62
+ }
63
+ for c in candidates where fm.isExecutableFile(atPath: c) {
64
+ // Prepend the bin dir to PATH so the spawned tsbridge finds
65
+ // `node` and `npx` (needed for @playwright/mcp / cloakbrowser).
66
+ let bin = (c as NSString).deletingLastPathComponent
67
+ var env = ProcessInfo.processInfo.environment
68
+ env["PATH"] = "\(bin):/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin"
69
+ // GUI-launched processes can start with an empty/missing HOME;
70
+ // @playwright/mcp & cloakbrowser use it for their output dir
71
+ // (else they try to mkdir /.playwright-mcp and crash). Force it.
72
+ if (env["HOME"] ?? "").isEmpty { env["HOME"] = NSHomeDirectory() }
73
+ return (c, env)
74
+ }
75
+ return nil
76
+ }
77
+
78
+ // ─── the bridge process ──────────────────────────────────────────────
79
+
80
+ final class Bridge {
81
+ private var proc: Process?
82
+ var isRunning: Bool { proc?.isRunning ?? false }
83
+ var onStateChange: (() -> Void)?
84
+
85
+ func start(_ cfg: Config) {
86
+ stop()
87
+ guard let (cmd, env) = resolveTsbridge() else {
88
+ notify("tsbridge not found", "Install it: npm i -g @tianshu-ai/local-bridge")
89
+ return
90
+ }
91
+ var args = ["--server", cfg.server]
92
+ if !cfg.token.isEmpty { args += ["--token", cfg.token] }
93
+ if cfg.engine == "stealth" { args += ["--browser-engine", "stealth"] }
94
+ if cfg.headless { args += ["--headless"] }
95
+ if !cfg.device.isEmpty { args += ["--device", cfg.device] }
96
+
97
+ let p = Process()
98
+ p.executableURL = URL(fileURLWithPath: cmd)
99
+ p.arguments = args
100
+ p.environment = env
101
+ // Log to ~/.tianshu-bridge/bridge.log for troubleshooting.
102
+ let logURL = Config.path.deletingLastPathComponent().appendingPathComponent("bridge.log")
103
+ FileManager.default.createFile(atPath: logURL.path, contents: nil)
104
+ if let fh = try? FileHandle(forWritingTo: logURL) {
105
+ p.standardOutput = fh
106
+ p.standardError = fh
107
+ }
108
+ p.terminationHandler = { [weak self] _ in
109
+ DispatchQueue.main.async { self?.onStateChange?() }
110
+ }
111
+ do {
112
+ try p.run()
113
+ proc = p
114
+ } catch {
115
+ notify("Failed to start bridge", error.localizedDescription)
116
+ }
117
+ onStateChange?()
118
+ }
119
+
120
+ func stop() {
121
+ if let p = proc, p.isRunning { p.terminate() }
122
+ proc = nil
123
+ }
124
+
125
+ private func notify(_ title: String, _ body: String) {
126
+ let a = NSAlert()
127
+ a.messageText = title
128
+ a.informativeText = body
129
+ a.runModal()
130
+ }
131
+ }
132
+
133
+ // ─── menu-bar controller ─────────────────────────────────────────────
134
+
135
+ final class AppController: NSObject, NSApplicationDelegate {
136
+ // Created in applicationDidFinishLaunching (not at init) so the app
137
+ // is fully up before we claim a status-bar slot.
138
+ var statusItem: NSStatusItem!
139
+ let bridge = Bridge()
140
+ var cfg = Config.load()
141
+ var configWindow: NSWindow?
142
+
143
+ func applicationDidFinishLaunching(_ notification: Notification) {
144
+ statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
145
+ updateIcon()
146
+ bridge.onStateChange = { [weak self] in
147
+ self?.updateIcon()
148
+ self?.rebuildMenu()
149
+ }
150
+ rebuildMenu()
151
+ }
152
+
153
+ func updateIcon() {
154
+ guard let btn = statusItem.button else { return }
155
+ let on = bridge.isRunning
156
+ let name = on ? "bolt.horizontal.circle.fill" : "bolt.horizontal.circle"
157
+ if let img = NSImage(systemSymbolName: name, accessibilityDescription: "Tianshu Bridge") {
158
+ img.isTemplate = true
159
+ btn.image = img
160
+ btn.title = ""
161
+ } else {
162
+ // SF Symbol unavailable → always-visible text fallback.
163
+ btn.image = nil
164
+ btn.title = on ? "⚡●" : "⚡"
165
+ }
166
+ btn.toolTip = on ? "Tianshu Bridge: connected" : "Tianshu Bridge: stopped"
167
+ }
168
+
169
+ func rebuildMenu() {
170
+ let menu = NSMenu()
171
+ let status = bridge.isRunning ? "● Connected" : "○ Stopped"
172
+ let statusMI = NSMenuItem(title: status, action: nil, keyEquivalent: "")
173
+ statusMI.isEnabled = false
174
+ menu.addItem(statusMI)
175
+ menu.addItem(NSMenuItem(title: "Server: \(cfg.server)", action: nil, keyEquivalent: ""))
176
+ let eng = cfg.engine == "stealth" ? "stealth" : "own"
177
+ menu.addItem(NSMenuItem(title: "Browser: \(eng)\(cfg.headless ? " (headless)" : "")",
178
+ action: nil, keyEquivalent: ""))
179
+ menu.addItem(.separator())
180
+
181
+ if bridge.isRunning {
182
+ menu.addItem(withTitle: "Stop", action: #selector(stop), keyEquivalent: "s").target = self
183
+ } else {
184
+ menu.addItem(withTitle: "Start", action: #selector(start), keyEquivalent: "s").target = self
185
+ }
186
+ menu.addItem(withTitle: "Settings…", action: #selector(openSettings), keyEquivalent: ",").target = self
187
+ menu.addItem(withTitle: "Open Log", action: #selector(openLog), keyEquivalent: "").target = self
188
+ menu.addItem(.separator())
189
+ menu.addItem(withTitle: "Quit", action: #selector(quit), keyEquivalent: "q").target = self
190
+ statusItem.menu = menu
191
+ }
192
+
193
+ @objc func start() { bridge.start(cfg) }
194
+ @objc func stop() { bridge.stop(); updateIcon(); rebuildMenu() }
195
+ @objc func openLog() {
196
+ let log = Config.path.deletingLastPathComponent().appendingPathComponent("bridge.log")
197
+ NSWorkspace.shared.open(log)
198
+ }
199
+ @objc func quit() { bridge.stop(); NSApp.terminate(nil) }
200
+
201
+ // ── settings window (simple form) ──
202
+ @objc func openSettings() {
203
+ if configWindow == nil {
204
+ let w = NSWindow(contentRect: NSRect(x: 0, y: 0, width: 380, height: 300),
205
+ styleMask: [.titled, .closable], backing: .buffered, defer: false)
206
+ w.title = "Tianshu Bridge Settings"
207
+ w.center()
208
+ w.contentView = makeSettingsView()
209
+ w.isReleasedWhenClosed = false
210
+ configWindow = w
211
+ }
212
+ NSApp.activate(ignoringOtherApps: true)
213
+ configWindow?.makeKeyAndOrderFront(nil)
214
+ }
215
+
216
+ private var serverField: NSTextField!
217
+ private var tokenField: NSTextField!
218
+ private var enginePopup: NSPopUpButton!
219
+ private var headlessCheck: NSButton!
220
+
221
+ private func makeSettingsView() -> NSView {
222
+ let v = NSView(frame: NSRect(x: 0, y: 0, width: 380, height: 300))
223
+ func label(_ s: String, _ y: CGFloat) -> NSTextField {
224
+ let l = NSTextField(labelWithString: s); l.frame = NSRect(x: 16, y: y, width: 90, height: 20); return l
225
+ }
226
+ // Paste-config row: grab the config string copied from the
227
+ // tianshu Local Bridge panel and fill every field at once.
228
+ let paste = NSButton(title: "Paste from tianshu", target: self, action: #selector(pasteConfig))
229
+ paste.frame = NSRect(x: 16, y: 258, width: 200, height: 28)
230
+ paste.bezelStyle = .rounded
231
+ v.addSubview(paste)
232
+ let hint = NSTextField(labelWithString: "(copies server + token + browser settings)")
233
+ hint.frame = NSRect(x: 16, y: 238, width: 348, height: 16)
234
+ hint.font = NSFont.systemFont(ofSize: 10)
235
+ hint.textColor = .secondaryLabelColor
236
+ v.addSubview(hint)
237
+
238
+ v.addSubview(label("Server", 210))
239
+ serverField = NSTextField(frame: NSRect(x: 110, y: 208, width: 250, height: 24))
240
+ serverField.stringValue = cfg.server; v.addSubview(serverField)
241
+
242
+ v.addSubview(label("Token", 172))
243
+ tokenField = NSTextField(frame: NSRect(x: 110, y: 170, width: 250, height: 24))
244
+ tokenField.stringValue = cfg.token; v.addSubview(tokenField)
245
+
246
+ v.addSubview(label("Browser", 134))
247
+ enginePopup = NSPopUpButton(frame: NSRect(x: 110, y: 130, width: 250, height: 26))
248
+ enginePopup.addItems(withTitles: ["own (your Chrome)", "stealth (CloakBrowser)"])
249
+ enginePopup.selectItem(at: cfg.engine == "stealth" ? 1 : 0)
250
+ v.addSubview(enginePopup)
251
+
252
+ headlessCheck = NSButton(checkboxWithTitle: "Headless (no window)", target: nil, action: nil)
253
+ headlessCheck.frame = NSRect(x: 110, y: 98, width: 250, height: 20)
254
+ headlessCheck.state = cfg.headless ? .on : .off
255
+ v.addSubview(headlessCheck)
256
+
257
+ let save = NSButton(title: "Save & Restart", target: self, action: #selector(saveSettings))
258
+ save.frame = NSRect(x: 200, y: 20, width: 160, height: 32)
259
+ save.bezelStyle = .rounded
260
+ v.addSubview(save)
261
+ return v
262
+ }
263
+
264
+ @objc func saveSettings() {
265
+ cfg.server = serverField.stringValue.trimmingCharacters(in: .whitespaces)
266
+ cfg.token = tokenField.stringValue.trimmingCharacters(in: .whitespaces)
267
+ cfg.engine = enginePopup.indexOfSelectedItem == 1 ? "stealth" : "own"
268
+ cfg.headless = headlessCheck.state == .on
269
+ cfg.save()
270
+ configWindow?.close()
271
+ rebuildMenu()
272
+ if bridge.isRunning { bridge.start(cfg) } // restart with new config
273
+ }
274
+
275
+ // Parse the config string copied from the tianshu panel and fill the
276
+ // form. Accepts a `tsbridge://configure?server=…&token=…&engine=…&
277
+ // headless=…` URL, or a bare query string of the same params.
278
+ @objc func pasteConfig() {
279
+ let raw = (NSPasteboard.general.string(forType: .string) ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
280
+ guard !raw.isEmpty else { return alert("Clipboard is empty", "Copy the config from the tianshu Local Bridge panel first.") }
281
+ // Extract the query part whether it's a full URL or bare query.
282
+ var query = raw
283
+ if let q = raw.range(of: "?") { query = String(raw[raw.index(after: q.lowerBound)...]) }
284
+ var params: [String: String] = [:]
285
+ for pair in query.split(separator: "&") {
286
+ let kv = pair.split(separator: "=", maxSplits: 1).map(String.init)
287
+ if kv.count == 2 {
288
+ params[kv[0]] = kv[1].removingPercentEncoding ?? kv[1]
289
+ }
290
+ }
291
+ guard let server = params["server"], !server.isEmpty else {
292
+ return alert("Couldn't read config", "Expected a tsbridge://configure?server=… string copied from the tianshu panel.")
293
+ }
294
+ serverField.stringValue = server
295
+ if let t = params["token"] { tokenField.stringValue = t }
296
+ if let e = params["engine"] { enginePopup.selectItem(at: e == "stealth" ? 1 : 0) }
297
+ if let h = params["headless"] { headlessCheck.state = (h == "1" || h == "true") ? .on : .off }
298
+ }
299
+
300
+ private func alert(_ title: String, _ body: String) {
301
+ let a = NSAlert(); a.messageText = title; a.informativeText = body; a.runModal()
302
+ }
303
+ }
304
+
305
+ // ─── main ────────────────────────────────────────────────────────────
306
+
307
+ let app = NSApplication.shared
308
+ app.setActivationPolicy(.accessory) // menu-bar only, no Dock icon
309
+ let controller = AppController()
310
+ app.delegate = controller
311
+ app.run()
@@ -90,7 +90,16 @@ export class BridgeConnection {
90
90
  }, delay);
91
91
  });
92
92
  ws.on("error", (err) => {
93
- this.log(`socket error: ${err instanceof Error ? err.message : String(err)}`);
93
+ const e = err;
94
+ const detail = [e.code, e.message].filter(Boolean).join(" ") ||
95
+ (err ? String(err) : "unknown (connection refused / handshake rejected?)");
96
+ this.log(`socket error connecting to ${this.opts.server}: ${detail}`);
97
+ });
98
+ // Surface non-101 handshake responses (auth/path problems), which
99
+ // otherwise arrive as an opaque error.
100
+ ws.on("unexpected-response", (_req, res) => {
101
+ this.log(`server rejected the WebSocket upgrade: HTTP ${res.statusCode} ${res.statusMessage ?? ""} ` +
102
+ `(check --server URL/path and --token). Expected /ws to accept an upgrade.`);
94
103
  });
95
104
  }
96
105
  send(msg) {
@@ -1 +1 @@
1
- {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -13,24 +13,41 @@
13
13
  // --token <token> connection token from the Local Bridge panel
14
14
  // --device <id> stable device id (default: hostname)
15
15
  // --label <name> human label shown in the panel (default: device id)
16
- // --no-browser don't expose browser tools (echo only)
16
+ // --no-browser don't expose browser tools
17
17
  // --browser-engine own | stealth (default: own)
18
18
  // own = your system/running Chrome (no download,
19
19
  // real cookies+fingerprint)
20
20
  // stealth = cloakbrowser-mcp (CloakBrowser stealth
21
21
  // Chromium, full Playwright-MCP toolset,
22
22
  // passes bot detection; ~200MB first run)
23
- // --headless run the browser without a window (own engine)
23
+ // --headless run the browser without a window (own + stealth;
24
+ // default is headful — a window you can watch)
24
25
  // --cdp <url> own: connect to a running Chrome's CDP endpoint
25
26
  // (default probe http://127.0.0.1:9222; "off" to skip)
26
27
  // --chrome-channel own: which installed browser to launch (chrome, msedge, …)
27
28
  // --user-data-dir own: reuse a Chrome profile dir so logins carry over
29
+ //
30
+ // Subcommands:
31
+ // tsbridge update [--check|--dry-run|--tag <t>] self-update
32
+ // tsbridge install-app [--run] build + install the
33
+ // macOS menu-bar app
34
+ // tsbridge version print installed version
28
35
  import os from "node:os";
29
36
  import { BridgeConnection } from "./connection.js";
30
- import { makeBrowserTools } from "./browser.js";
31
- import { connectMcpChild } from "./mcp-child.js";
37
+ import { connectMcpChild, bridgeOutputDir } from "./mcp-child.js";
32
38
  import { runUpdate, installedVersion } from "./update.js";
33
- import { textResult } from "./protocol.js";
39
+ import { installApp } from "./install-app.js";
40
+ /** Is a Chrome already listening on this CDP endpoint? */
41
+ async function probeCdp(cdpUrl) {
42
+ try {
43
+ const base = cdpUrl.replace(/\/$/, "");
44
+ const res = await fetch(`${base}/json/version`, { signal: AbortSignal.timeout(2500) });
45
+ return res.ok;
46
+ }
47
+ catch {
48
+ return false;
49
+ }
50
+ }
34
51
  function parseArgs(argv) {
35
52
  const out = {};
36
53
  for (let i = 0; i < argv.length; i++) {
@@ -55,18 +72,30 @@ async function main() {
55
72
  const sub = argv.find((a) => !a.startsWith("--"));
56
73
  const args = parseArgs(argv);
57
74
  if (sub === "update") {
58
- process.exit(await runUpdate(args.yes === true || args.y === true));
75
+ process.exit(await runUpdate({
76
+ check: args.check === true,
77
+ dryRun: args["dry-run"] === true,
78
+ tag: typeof args.tag === "string" ? args.tag : undefined,
79
+ }));
59
80
  }
60
81
  if (sub === "version" || args.version === true || args.v === true) {
61
82
  process.stdout.write(`@tianshu-ai/local-bridge v${installedVersion()}\n`);
62
83
  process.exit(0);
63
84
  }
85
+ if (sub === "install-app") {
86
+ process.exit(installApp({
87
+ run: args.run === true,
88
+ dest: typeof args.dest === "string" ? args.dest : undefined,
89
+ }));
90
+ }
64
91
  const server = typeof args.server === "string" ? args.server : "";
65
92
  if (!server) {
66
93
  console.error("error: --server <wss://host/ws> is required");
67
94
  console.error("example: tsbridge --server wss://tianshu.example.com/ws --token ***");
68
95
  console.error("install: npm i -g @tianshu-ai/local-bridge (then run `tsbridge`)");
69
- console.error("commands: tsbridge update [--yes] | tsbridge version");
96
+ console.error("commands: tsbridge update [--check|--dry-run|--tag <t>]");
97
+ console.error(" tsbridge install-app [--run] (macOS menu-bar app)");
98
+ console.error(" tsbridge version");
70
99
  process.exit(2);
71
100
  }
72
101
  const token = typeof args.token === "string" ? args.token : undefined;
@@ -78,30 +107,21 @@ async function main() {
78
107
  const cdpUrl = cdpArg === "off" ? "" : cdpArg;
79
108
  const channel = typeof args["chrome-channel"] === "string" ? args["chrome-channel"] : "chrome";
80
109
  const userDataDir = typeof args["user-data-dir"] === "string" ? args["user-data-dir"] : "";
81
- // Always ship a trivial echo tool so the round-trip can be verified
82
- // without a browser install.
83
- const echo = {
84
- descriptor: {
85
- name: "echo",
86
- description: "Echo the given text back (bridge connectivity check).",
87
- inputSchema: { type: "object", properties: { text: { type: "string" } }, required: ["text"] },
88
- },
89
- async run(a) {
90
- return textResult(String(a.text ?? ""));
91
- },
92
- };
93
110
  const browserOn = args["no-browser"] !== true;
94
111
  const engine = args["browser-engine"] === "stealth" ? "stealth" : "own";
95
- const tools = [echo];
112
+ const tools = [];
96
113
  if (browserOn) {
97
114
  if (engine === "stealth") {
98
115
  // Full Playwright-MCP toolset pointed at CloakBrowser stealth
99
116
  // Chromium. Spawned as a child MCP server over stdio.
100
117
  try {
118
+ // cloakbrowser-mcp reads PLAYWRIGHT_MCP_HEADLESS (default true =
119
+ // headless). Set it explicitly from our headful/--headless flag
120
+ // so the window shows when the user wants it.
101
121
  const stealthTools = await connectMcpChild({
102
122
  command: "npx",
103
123
  args: ["-y", "cloakbrowser-mcp@latest"],
104
- env: headful ? {} : { PWMCP_HEADLESS: "1" },
124
+ env: { PLAYWRIGHT_MCP_HEADLESS: headful ? "false" : "true" },
105
125
  log: (m) => console.log(`[local-bridge] ${m}`),
106
126
  });
107
127
  tools.push(...stealthTools);
@@ -112,15 +132,42 @@ async function main() {
112
132
  }
113
133
  }
114
134
  else {
115
- tools.push(...makeBrowserTools({
116
- cdpUrl,
117
- channel,
118
- userDataDir,
119
- headful,
120
- log: (m) => console.log(`[local-bridge] ${m}`),
121
- }));
135
+ // own engine: the full @playwright/mcp toolset pointed at the
136
+ // user's real Chrome — connect to a running Chrome over CDP if one
137
+ // is up, else launch the system Chrome (channel). Same tool
138
+ // surface as stealth; only the underlying browser differs.
139
+ const log = (m) => console.log(`[local-bridge] ${m}`);
140
+ // Pin output dir explicitly so it never resolves against a bad cwd.
141
+ const mcpArgs = ["-y", "@playwright/mcp@latest", "--output-dir", bridgeOutputDir()];
142
+ const cdpUp = cdpUrl ? await probeCdp(cdpUrl) : false;
143
+ if (cdpUp) {
144
+ mcpArgs.push("--cdp-endpoint", cdpUrl);
145
+ log(`own: connecting to your running Chrome at ${cdpUrl}`);
146
+ }
147
+ else {
148
+ // @playwright/mcp: --browser takes a browser OR chrome channel
149
+ // ("chrome", "msedge", …). Point it at the system Chrome.
150
+ mcpArgs.push("--browser", channel);
151
+ if (userDataDir)
152
+ mcpArgs.push("--user-data-dir", userDataDir);
153
+ if (!headful)
154
+ mcpArgs.push("--headless");
155
+ log(`own: launching system ${channel}${headful ? "" : " (headless)"}`);
156
+ }
157
+ try {
158
+ const ownTools = await connectMcpChild({ command: "npx", args: mcpArgs, log });
159
+ tools.push(...ownTools);
160
+ }
161
+ catch (err) {
162
+ console.error(`[local-bridge] failed to start browser (@playwright/mcp): ${err instanceof Error ? err.message : String(err)}`);
163
+ process.exit(1);
164
+ }
122
165
  }
123
166
  }
167
+ if (tools.length === 0) {
168
+ console.error("[local-bridge] no tools enabled — nothing to expose. Remove --no-browser (or enable a capability) and try again.");
169
+ process.exit(2);
170
+ }
124
171
  const conn = new BridgeConnection({
125
172
  server,
126
173
  token,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ // `tsbridge install-app` — build + install the macOS menu-bar app.
2
+ //
3
+ // Compiles a tiny native AppKit menu-bar app (swiftc, no Electron) that
4
+ // wraps this CLI: a menu-bar icon with Start/Stop + Settings (server /
5
+ // token / browser engine / headless). The Swift source ships in the npm
6
+ // package (app/TianshuBridge.swift); we assemble the .app bundle and
7
+ // compile it on the user's machine.
8
+ import { spawnSync } from "node:child_process";
9
+ import fs from "node:fs";
10
+ import os from "node:os";
11
+ import path from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ const APP_NAME = "Tianshu Bridge";
14
+ const BUNDLE_ID = "ai.tianshu.local-bridge";
15
+ function findSwiftSource() {
16
+ // dist/install-app.js → ../app/TianshuBridge.swift (shipped in the package)
17
+ const here = path.dirname(fileURLToPath(import.meta.url));
18
+ const candidates = [
19
+ path.join(here, "..", "app", "TianshuBridge.swift"),
20
+ path.join(here, "app", "TianshuBridge.swift"),
21
+ ];
22
+ return candidates.find((p) => fs.existsSync(p)) ?? null;
23
+ }
24
+ export function installApp(opts = {}) {
25
+ if (process.platform !== "darwin") {
26
+ console.error("error: the menu-bar app is macOS-only.");
27
+ return 1;
28
+ }
29
+ const swiftc = spawnSync("swiftc", ["--version"], { stdio: "ignore" });
30
+ if (swiftc.status !== 0) {
31
+ console.error("error: swiftc not found. Install Xcode command line tools:");
32
+ console.error(" xcode-select --install");
33
+ return 1;
34
+ }
35
+ const src = findSwiftSource();
36
+ if (!src) {
37
+ console.error("error: bundled Swift source not found in the package.");
38
+ return 1;
39
+ }
40
+ const dest = opts.dest || path.join(os.homedir(), "Applications");
41
+ const appDir = path.join(dest, `${APP_NAME}.app`);
42
+ const macosDir = path.join(appDir, "Contents", "MacOS");
43
+ const resDir = path.join(appDir, "Contents", "Resources");
44
+ console.log(`→ Building ${APP_NAME}.app …`);
45
+ fs.rmSync(appDir, { recursive: true, force: true });
46
+ fs.mkdirSync(macosDir, { recursive: true });
47
+ fs.mkdirSync(resDir, { recursive: true });
48
+ const plist = `<?xml version="1.0" encoding="UTF-8"?>
49
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
50
+ <plist version="1.0">
51
+ <dict>
52
+ <key>CFBundleName</key><string>${APP_NAME}</string>
53
+ <key>CFBundleDisplayName</key><string>${APP_NAME}</string>
54
+ <key>CFBundleIdentifier</key><string>${BUNDLE_ID}</string>
55
+ <key>CFBundleVersion</key><string>1</string>
56
+ <key>CFBundleShortVersionString</key><string>1.0</string>
57
+ <key>CFBundlePackageType</key><string>APPL</string>
58
+ <key>CFBundleExecutable</key><string>TianshuBridge</string>
59
+ <key>LSMinimumSystemVersion</key><string>13.0</string>
60
+ <key>LSUIElement</key><true/>
61
+ </dict>
62
+ </plist>
63
+ `;
64
+ fs.writeFileSync(path.join(appDir, "Contents", "Info.plist"), plist);
65
+ console.log("→ Compiling Swift (menu-bar app) …");
66
+ const build = spawnSync("swiftc", ["-O", "-o", path.join(macosDir, "TianshuBridge"), src, "-framework", "AppKit", "-framework", "Foundation"], { stdio: "inherit" });
67
+ if (build.status !== 0) {
68
+ console.error("error: swiftc build failed.");
69
+ return 1;
70
+ }
71
+ // Clear any quarantine attribute so `open` / Gatekeeper doesn't
72
+ // silently refuse to launch the freshly-written bundle.
73
+ spawnSync("xattr", ["-dr", "com.apple.quarantine", appDir], { stdio: "ignore" });
74
+ // Ad-hoc sign so Gatekeeper allows a locally-built app.
75
+ spawnSync("codesign", ["--force", "--deep", "--sign", "-", appDir], { stdio: "ignore" });
76
+ console.log(`✓ Installed: ${appDir}`);
77
+ console.log("");
78
+ console.log(`It lives in the menu bar (bolt icon). Click it → Settings to set your`);
79
+ console.log(`server URL + token, then Start. First launch: right-click the app → Open`);
80
+ console.log(`to bypass Gatekeeper (unsigned local build).`);
81
+ if (opts.run) {
82
+ console.log("→ Launching …");
83
+ spawnSync("open", [appDir], { stdio: "ignore" });
84
+ }
85
+ return 0;
86
+ }
87
+ //# sourceMappingURL=install-app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install-app.js","sourceRoot":"","sources":["../src/install-app.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,EAAE;AACF,wEAAwE;AACxE,uEAAuE;AACvE,wEAAwE;AACxE,qEAAqE;AACrE,oCAAoC;AAEpC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,QAAQ,GAAG,gBAAgB,CAAC;AAClC,MAAM,SAAS,GAAG,yBAAyB,CAAC;AAE5C,SAAS,eAAe;IACtB,4EAA4E;IAC5E,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,MAAM,UAAU,GAAG;QACjB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,qBAAqB,CAAC;QACnD,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,qBAAqB,CAAC;KAC9C,CAAC;IACF,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AAC1D,CAAC;AASD,MAAM,UAAU,UAAU,CAAC,OAAuB,EAAE;IAClD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,OAAO,CAAC,KAAK,CAAC,wCAAwC,CAAC,CAAC;QACxD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACvE,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAC5E,OAAO,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;QAC1C,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,GAAG,GAAG,eAAe,EAAE,CAAC;IAC9B,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;QACvE,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;IAClE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,QAAQ,MAAM,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC;IAE1D,OAAO,CAAC,GAAG,CAAC,cAAc,QAAQ,QAAQ,CAAC,CAAC;IAC5C,EAAE,CAAC,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,MAAM,KAAK,GAAG;;;;mCAImB,QAAQ;0CACD,QAAQ;yCACT,SAAS;;;;;;;;;CASjD,CAAC;IACA,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;IAErE,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,SAAS,CACrB,QAAQ,EACR,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,CAAC,EAC3G,EAAE,KAAK,EAAE,SAAS,EAAE,CACrB,CAAC;IACF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;QAC7C,OAAO,CAAC,CAAC;IACX,CAAC;IAED,gEAAgE;IAChE,wDAAwD;IACxD,SAAS,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,sBAAsB,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjF,wDAAwD;IACxD,SAAS,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IAEzF,OAAO,CAAC,GAAG,CAAC,gBAAgB,MAAM,EAAE,CAAC,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,CAAC,GAAG,CAAC,uEAAuE,CAAC,CAAC;IACrF,OAAO,CAAC,GAAG,CAAC,0EAA0E,CAAC,CAAC;IACxF,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;IAE5D,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC7B,SAAS,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
package/dist/mcp-child.js CHANGED
@@ -3,17 +3,43 @@
3
3
  // client, and wrap each of its tools as a LocalTool that proxies
4
4
  // tools/call. This turns local-bridge into a general "expose a local
5
5
  // MCP server to tianshu" bridge — browser (stealth) is the first user.
6
+ import os from "node:os";
7
+ import fs from "node:fs";
8
+ import path from "node:path";
6
9
  import { Client } from "@modelcontextprotocol/sdk/client/index.js";
7
10
  import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
8
11
  import { textResult } from "./protocol.js";
9
12
  /** Start the child MCP server, list its tools, and return them wrapped
10
13
  * as LocalTools. The Client stays connected for the process lifetime;
11
14
  * tools/call is proxied straight through. */
15
+ /** Our own working dir for the browser MCP children. Fixed + explicit
16
+ * so output never lands in a surprise cwd. The crash we hit
17
+ * (mkdir '/.playwright-mcp') was cloakbrowser/@playwright-mcp
18
+ * resolving `.playwright-mcp` against cwd, which is `/` when launched
19
+ * from a GUI app. Pin it under ~/.tianshu-bridge instead. */
20
+ export function bridgeOutputDir() {
21
+ const dir = path.join(os.homedir(), ".tianshu-bridge", "playwright-mcp");
22
+ try {
23
+ fs.mkdirSync(dir, { recursive: true });
24
+ }
25
+ catch {
26
+ /* best-effort */
27
+ }
28
+ return dir;
29
+ }
12
30
  export async function connectMcpChild(opts) {
31
+ const baseEnv = { ...process.env };
32
+ if (!baseEnv.HOME)
33
+ baseEnv.HOME = os.homedir();
34
+ // Pin the output dir explicitly (both cloakbrowser-mcp via env and
35
+ // @playwright/mcp via --output-dir honor this), so it never resolves
36
+ // to a bad cwd like '/'.
37
+ const outDir = bridgeOutputDir();
38
+ baseEnv.PLAYWRIGHT_MCP_OUTPUT_DIR = outDir;
13
39
  const transport = new StdioClientTransport({
14
40
  command: opts.command,
15
41
  args: opts.args,
16
- env: { ...process.env, ...(opts.env ?? {}) },
42
+ env: { ...baseEnv, ...(opts.env ?? {}) },
17
43
  stderr: "inherit",
18
44
  });
19
45
  const client = new Client({ name: opts.clientName ?? "tianshu-local-bridge", version: "0.1.0" });
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-child.js","sourceRoot":"","sources":["../src/mcp-child.ts"],"names":[],"mappings":"AAAA,wEAAwE;AACxE,sEAAsE;AACtE,iEAAiE;AACjE,qEAAqE;AACrE,uEAAuE;AAEvE,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,UAAU,EAAwD,MAAM,eAAe,CAAC;AAejG;;8CAE8C;AAC9C,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAqB;IACzD,MAAM,SAAS,GAAG,IAAI,oBAAoB,CAAC;QACzC,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,EAA4B;QACtE,MAAM,EAAE,SAAS;KAClB,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,IAAI,sBAAsB,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IACjG,IAAI,CAAC,GAAG,CAAC,4BAA4B,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC5E,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEhC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,EAAE,CAAC;IACxC,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7B,MAAM,UAAU,GAAmB;YACjC,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,SAAS;YACvC,WAAW,EAAG,CAAC,CAAC,WAAmD,IAAI,SAAS;SACjF,CAAC;QACF,KAAK,CAAC,IAAI,CAAC;YACT,UAAU;YACV,KAAK,CAAC,GAAG,CAAC,IAA6B;gBACrC,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;oBACrE,2DAA2D;oBAC3D,kDAAkD;oBAClD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAE,GAA6B,CAAC,OAAO,CAAC;wBACnE,CAAC,CAAC,CAAE,GAA0C,CAAC,OAAO,CAAC;wBACvD,CAAC,CAAC,EAAE,CAAC;oBACP,OAAO;wBACL,OAAO,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;wBAC5E,OAAO,EAAG,GAA6B,CAAC,OAAO,KAAK,IAAI;qBACzD,CAAC;gBACJ,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,OAAO,UAAU,CACf,wBAAwB,CAAC,CAAC,IAAI,aAAa,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAC7F,IAAI,CACL,CAAC;gBACJ,CAAC;YACH,CAAC;SACF,CAAC,CAAC;IACL,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,yBAAyB,KAAK,CAAC,MAAM,QAAQ,CAAC,CAAC;IACxD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"mcp-child.js","sourceRoot":"","sources":["../src/mcp-child.ts"],"names":[],"mappings":"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"}
package/dist/update.js CHANGED
@@ -1,18 +1,23 @@
1
- // `tsbridge update` — check npm for a newer @tianshu-ai/local-bridge and
2
- // optionally self-update the global install.
1
+ // `tsbridge update` — self-update the global @tianshu-ai/local-bridge
2
+ // install. Behaviour mirrors `tianshu update` for a consistent CLI:
3
3
  //
4
- // Check-only by default; pass --yes to actually run the install. Uses
5
- // the npm registry JSON API (no auth) for the check, and `npm i -g` for
6
- // the apply so it works regardless of how the CLI was installed.
4
+ // tsbridge update → check + install if newer (default: install)
5
+ // tsbridge update --check → just compare; exit 1 if an update exists
6
+ // tsbridge update --dry-run → print the npm command, don't run it
7
+ // tsbridge update --tag next → target a non-`latest` dist-tag
8
+ //
9
+ // Exit codes:
10
+ // 0 → up to date, OR update succeeded, OR --check found nothing
11
+ // 1 → --check found an available update (script-friendly signal)
12
+ // 2 → error (network, npm install failed, git-checkout, …)
7
13
  import { createRequire } from "node:module";
8
14
  import { spawn } from "node:child_process";
9
- const PKG = "@tianshu-ai/local-bridge";
15
+ export const PACKAGE_NAME = "@tianshu-ai/local-bridge";
10
16
  const REGISTRY = "https://registry.npmjs.org";
11
17
  /** Installed version, read from this package's own package.json. */
12
18
  export function installedVersion() {
13
19
  try {
14
20
  const require = createRequire(import.meta.url);
15
- // dist/update.js → ../package.json
16
21
  const pkg = require("../package.json");
17
22
  return pkg.version ?? "0.0.0";
18
23
  }
@@ -20,51 +25,24 @@ export function installedVersion() {
20
25
  return "0.0.0";
21
26
  }
22
27
  }
23
- /** Fetch the latest published version + dist-tags from npm. */
24
- async function fetchLatest() {
28
+ async function fetchDistTag(tag) {
25
29
  try {
26
- const res = await fetch(`${REGISTRY}/${encodeURIComponent(PKG).replace("%40", "@")}/latest`, {
27
- headers: { accept: "application/json" },
28
- signal: AbortSignal.timeout(10_000),
29
- });
30
+ const url = `${REGISTRY}/${PACKAGE_NAME.replace("/", "%2f")}/${encodeURIComponent(tag)}`;
31
+ const res = await fetch(url, { headers: { accept: "application/json" }, signal: AbortSignal.timeout(10_000) });
30
32
  if (!res.ok)
31
- return null;
33
+ return { ok: false, error: `HTTP ${res.status}` };
32
34
  const json = (await res.json());
33
- return json.version ?? null;
35
+ if (!json.version)
36
+ return { ok: false, error: "no version in registry response" };
37
+ return { ok: true, version: json.version };
34
38
  }
35
- catch {
36
- return null;
37
- }
38
- }
39
- /** Semver-ish compare: returns true if `remote` is strictly newer than
40
- * `local`. Handles plain x.y.z; prerelease tags compare lexically as a
41
- * best-effort tiebreak. */
42
- function isNewer(remote, local) {
43
- const parse = (v) => {
44
- const [core, pre] = v.split("-", 2);
45
- const nums = core.split(".").map((n) => parseInt(n, 10) || 0);
46
- return { nums, pre: pre ?? "" };
47
- };
48
- const a = parse(remote);
49
- const b = parse(local);
50
- for (let i = 0; i < 3; i++) {
51
- const x = a.nums[i] ?? 0;
52
- const y = b.nums[i] ?? 0;
53
- if (x !== y)
54
- return x > y;
39
+ catch (err) {
40
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
55
41
  }
56
- // equal core: a release (no pre) beats a prerelease; else lexical
57
- if (a.pre === b.pre)
58
- return false;
59
- if (!a.pre)
60
- return true;
61
- if (!b.pre)
62
- return false;
63
- return a.pre > b.pre;
64
42
  }
65
- function runNpmInstall() {
43
+ function runNpmInstall(target) {
66
44
  return new Promise((resolve) => {
67
- const child = spawn("npm", ["install", "-g", `${PKG}@latest`], {
45
+ const child = spawn("npm", ["install", "-g", target], {
68
46
  stdio: "inherit",
69
47
  shell: process.platform === "win32",
70
48
  });
@@ -72,34 +50,40 @@ function runNpmInstall() {
72
50
  child.on("error", () => resolve(1));
73
51
  });
74
52
  }
75
- /** Run the update subcommand. `apply` = actually install the new version. */
76
- export async function runUpdate(apply) {
77
- const local = installedVersion();
78
- process.stdout.write(`[tsbridge] installed: v${local}\n`);
79
- process.stdout.write(`[tsbridge] checking ${REGISTRY} for a newer ${PKG}…\n`);
80
- const latest = await fetchLatest();
81
- if (!latest) {
82
- process.stderr.write("[tsbridge] could not reach the npm registry.\n");
83
- return 1;
53
+ /** `tsbridge update [--check] [--tag <name>] [--dry-run]`. */
54
+ export async function runUpdate(opts = {}) {
55
+ const tag = opts.tag ?? "latest";
56
+ const current = installedVersion();
57
+ console.log(`Current version: ${current}`);
58
+ const remote = await fetchDistTag(tag);
59
+ if (!remote.ok) {
60
+ console.error(`Couldn't reach npm registry: ${remote.error}`);
61
+ console.error("Check your network / proxy and retry. If you're offline, skip the update.");
62
+ return 2;
84
63
  }
85
- if (!isNewer(latest, local)) {
86
- process.stdout.write(`[tsbridge] up to date (latest is v${latest}).\n`);
64
+ console.log(`Latest on \`${tag}\`: ${remote.version}`);
65
+ if (current === remote.version) {
66
+ console.log("Already up to date.");
87
67
  return 0;
88
68
  }
89
- process.stdout.write(`[tsbridge] a newer version is available: v${latest}\n`);
90
- if (!apply) {
91
- process.stdout.write(`[tsbridge] to update, run:\n npm i -g ${PKG}@latest\n or:\n tsbridge update --yes\n`);
69
+ if (opts.check) {
70
+ console.log(`Update available: ${current} → ${remote.version}. Run \`tsbridge update\` to install.`);
71
+ return 1;
72
+ }
73
+ const target = `${PACKAGE_NAME}@${remote.version}`;
74
+ const cmd = `npm install -g ${target}`;
75
+ if (opts.dryRun) {
76
+ console.log(`Would run: ${cmd}`);
92
77
  return 0;
93
78
  }
94
- process.stdout.write(`[tsbridge] updating v${local} → v${latest}…\n`);
95
- const code = await runNpmInstall();
79
+ console.log(`Installing ${remote.version}…`);
80
+ const code = await runNpmInstall(target);
96
81
  if (code === 0) {
97
- process.stdout.write(`[tsbridge] updated to v${latest}. Restart any running bridge.\n`);
98
- }
99
- else {
100
- process.stderr.write(`[tsbridge] update failed (npm exit ${code}). Try manually: npm i -g ${PKG}@latest\n` +
101
- ` (a permission error usually means you need sudo, or a Node version manager's global dir.)\n`);
82
+ console.log(`Updated to ${remote.version}. Restart any running bridge (tsbridge --server …).`);
83
+ return 0;
102
84
  }
103
- return code;
85
+ console.error(`npm install failed (exit ${code}). Try manually: ${cmd}\n` +
86
+ " A permission error usually means you need sudo, or a Node version manager's global dir.");
87
+ return 2;
104
88
  }
105
89
  //# sourceMappingURL=update.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"update.js","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tianshu-ai/local-bridge",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Local bridge for tianshu — dial into a tianshu server over WebSocket and expose your machine's tools (browser, files, shell) to the agent via reverse-MCP.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -18,6 +18,7 @@
18
18
  },
19
19
  "files": [
20
20
  "dist",
21
+ "app/TianshuBridge.swift",
21
22
  "README.md"
22
23
  ],
23
24
  "scripts": {
package/dist/browser.js DELETED
@@ -1,170 +0,0 @@
1
- // Local browser tools, driven by Playwright against the user's OWN
2
- // Chrome — never a bundled/downloaded Chromium.
3
- //
4
- // Acquisition strategy (best → fallback), so we reuse the real browser
5
- // with its real fingerprint + logged-in cookies and avoid a heavy
6
- // download:
7
- //
8
- // 1. CONNECT over CDP to a Chrome the user already has running with
9
- // `--remote-debugging-port` (default probe: http://127.0.0.1:9222).
10
- // This is the user's actual browser — real profile, real session.
11
- // 2. LAUNCH the system-installed Google Chrome via Playwright's
12
- // `channel: "chrome"` (no Chromium download). Optionally reuse the
13
- // user's profile via `userDataDir` so logins carry over. Launched
14
- // with automation-detection softened.
15
- //
16
- // Nothing here downloads a browser; if neither path works the tool
17
- // returns a clear, actionable error.
18
- import { textResult } from "./protocol.js";
19
- // Args that soften automation detection (navigator.webdriver etc.).
20
- const STEALTH_ARGS = [
21
- "--disable-blink-features=AutomationControlled",
22
- "--no-default-browser-check",
23
- "--no-first-run",
24
- ];
25
- async function probeCdp(cdpUrl) {
26
- if (!cdpUrl)
27
- return false;
28
- try {
29
- const base = cdpUrl.replace(/\/$/, "");
30
- const res = await fetch(`${base}/json/version`, {
31
- signal: AbortSignal.timeout(2500),
32
- });
33
- return res.ok;
34
- }
35
- catch {
36
- return false;
37
- }
38
- }
39
- /** Acquire a Page, connecting to the user's Chrome or launching the
40
- * system Chrome. Cached after first success. */
41
- async function ensurePage(state, cfg) {
42
- if (state.page && !state.page.isClosed())
43
- return state.page;
44
- const { chromium } = await import("playwright");
45
- // 1) Connect to an already-running Chrome over CDP.
46
- if (!state.browser && !state.context && (await probeCdp(cfg.cdpUrl))) {
47
- try {
48
- const browser = await chromium.connectOverCDP(cfg.cdpUrl);
49
- state.browser = browser;
50
- const ctx = browser.contexts()[0] ?? (await browser.newContext());
51
- state.context = ctx;
52
- state.page = ctx.pages()[0] ?? (await ctx.newPage());
53
- state.how = `connected to your running Chrome at ${cfg.cdpUrl}`;
54
- cfg.log(state.how);
55
- return state.page;
56
- }
57
- catch (err) {
58
- cfg.log(`CDP connect failed (${err instanceof Error ? err.message : String(err)}); falling back to launch`);
59
- }
60
- }
61
- // 2) Launch the system-installed Chrome (no Chromium download).
62
- if (cfg.userDataDir) {
63
- // Persistent context reuses the user's profile → logged-in sessions.
64
- const ctx = await chromium.launchPersistentContext(cfg.userDataDir, {
65
- channel: cfg.channel,
66
- headless: !cfg.headful,
67
- args: STEALTH_ARGS,
68
- });
69
- state.context = ctx;
70
- state.page = ctx.pages()[0] ?? (await ctx.newPage());
71
- state.how = `launched system ${cfg.channel} with your profile (${cfg.userDataDir})`;
72
- }
73
- else {
74
- const browser = await chromium.launch({
75
- channel: cfg.channel,
76
- headless: !cfg.headful,
77
- args: STEALTH_ARGS,
78
- });
79
- state.browser = browser;
80
- state.context = await browser.newContext();
81
- state.page = await state.context.newPage();
82
- state.how = `launched system ${cfg.channel} (ephemeral profile)`;
83
- }
84
- cfg.log(state.how);
85
- return state.page;
86
- }
87
- export function makeBrowserTools(cfg) {
88
- const state = { browser: null, context: null, page: null, how: "" };
89
- const navigate = {
90
- descriptor: {
91
- name: "browser_navigate",
92
- description: "Open a URL in the user's local browser and return the page title. Uses their real Chrome (real cookies/session).",
93
- inputSchema: {
94
- type: "object",
95
- properties: { url: { type: "string", description: "Absolute http(s) URL to open." } },
96
- required: ["url"],
97
- },
98
- },
99
- async run(args) {
100
- const url = String(args.url ?? "");
101
- if (!/^https?:\/\//i.test(url))
102
- return textResult("url must be an absolute http(s) URL", true);
103
- const page = await ensurePage(state, cfg);
104
- await page.goto(url, { waitUntil: "domcontentloaded", timeout: 45_000 });
105
- const title = await page.title();
106
- return textResult(`Navigated to ${page.url()}\nTitle: ${title}\n(${state.how})`);
107
- },
108
- };
109
- const getText = {
110
- descriptor: {
111
- name: "browser_get_text",
112
- description: "Return the visible text of the current page (optionally a CSS selector).",
113
- inputSchema: {
114
- type: "object",
115
- properties: { selector: { type: "string", description: "Optional CSS selector; defaults to <body>." } },
116
- },
117
- },
118
- async run(args) {
119
- if (!state.page || state.page.isClosed())
120
- return textResult("no page open — call browser_navigate first", true);
121
- const selector = typeof args.selector === "string" && args.selector ? args.selector : "body";
122
- const text = await state.page.locator(selector).first().innerText({ timeout: 15_000 }).catch(() => "");
123
- return textResult(text.slice(0, 8000) || "(no text found)");
124
- },
125
- };
126
- const click = {
127
- descriptor: {
128
- name: "browser_click",
129
- description: "Click the first element matching a CSS selector on the current page.",
130
- inputSchema: {
131
- type: "object",
132
- properties: { selector: { type: "string" } },
133
- required: ["selector"],
134
- },
135
- },
136
- async run(args) {
137
- if (!state.page || state.page.isClosed())
138
- return textResult("no page open — call browser_navigate first", true);
139
- const selector = String(args.selector ?? "");
140
- await state.page.locator(selector).first().click({ timeout: 15_000 });
141
- return textResult(`clicked ${selector}\nNow at: ${state.page.url()}`);
142
- },
143
- };
144
- const screenshot = {
145
- descriptor: {
146
- name: "browser_screenshot",
147
- description: "Capture a screenshot of the current page. The image travels back to the server and is saved to your workspace; the tool returns its path (call bridge_view_image to look at it).",
148
- inputSchema: {
149
- type: "object",
150
- properties: { fullPage: { type: "boolean", description: "Capture the full scrollable page (default false)." } },
151
- },
152
- },
153
- async run(args) {
154
- if (!state.page || state.page.isClosed())
155
- return textResult("no page open — call browser_navigate first", true);
156
- const buf = await state.page.screenshot({ fullPage: args.fullPage === true, type: "png" });
157
- // Return the bytes as an MCP image block so the server can save
158
- // them under the user's workspace. Bytes do NOT enter the agent
159
- // context by default — the server stores + surfaces only a path.
160
- return {
161
- content: [
162
- { type: "text", text: `screenshot of ${state.page.url()}` },
163
- { type: "image", data: buf.toString("base64"), mimeType: "image/png" },
164
- ],
165
- };
166
- },
167
- };
168
- return [navigate, getText, click, screenshot];
169
- }
170
- //# sourceMappingURL=browser.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"browser.js","sourceRoot":"","sources":["../src/browser.ts"],"names":[],"mappings":"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"}