@tianshu-ai/local-bridge 0.6.1 → 0.7.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.
@@ -16,8 +16,10 @@ import Foundation
16
16
  struct Config: Codable {
17
17
  var server: String = "ws://localhost:3110/ws"
18
18
  var token: String = ""
19
- var engine: String = "own" // "own" | "stealth"
20
- var headless: Bool = false
19
+ var browser: Bool = true // expose browser tools (on by default)
20
+ var engine: String = "own" // "own" | "stealth" (browser sub-option)
21
+ var headless: Bool = false // browser sub-option
22
+ var shell: Bool = false // expose exec + file sync (opt-in)
21
23
  var device: String = "" // empty → hostname
22
24
 
23
25
  static var path: URL {
@@ -90,8 +92,13 @@ final class Bridge {
90
92
  }
91
93
  var args = ["--server", cfg.server]
92
94
  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.browser {
96
+ if cfg.engine == "stealth" { args += ["--browser-engine", "stealth"] }
97
+ if cfg.headless { args += ["--headless"] }
98
+ } else {
99
+ args += ["--no-browser"]
100
+ }
101
+ if cfg.shell { args += ["--shell"] }
95
102
  if !cfg.device.isEmpty { args += ["--device", cfg.device] }
96
103
 
97
104
  let p = Process()
@@ -174,8 +181,9 @@ final class AppController: NSObject, NSApplicationDelegate {
174
181
  menu.addItem(statusMI)
175
182
  menu.addItem(NSMenuItem(title: "Server: \(cfg.server)", action: nil, keyEquivalent: ""))
176
183
  let eng = cfg.engine == "stealth" ? "stealth" : "own"
177
- menu.addItem(NSMenuItem(title: "Browser: \(eng)\(cfg.headless ? " (headless)" : "")",
178
- action: nil, keyEquivalent: ""))
184
+ let browserDesc = cfg.browser ? "\(eng)\(cfg.headless ? " (headless)" : "")" : "off"
185
+ menu.addItem(NSMenuItem(title: "Browser: \(browserDesc)", action: nil, keyEquivalent: ""))
186
+ menu.addItem(NSMenuItem(title: "Shell: \(cfg.shell ? "on" : "off")", action: nil, keyEquivalent: ""))
179
187
  menu.addItem(.separator())
180
188
 
181
189
  if bridge.isRunning {
@@ -215,8 +223,10 @@ final class AppController: NSObject, NSApplicationDelegate {
215
223
 
216
224
  private var serverField: NSTextField!
217
225
  private var tokenField: NSTextField!
226
+ private var browserCheck: NSButton!
218
227
  private var enginePopup: NSPopUpButton!
219
228
  private var headlessCheck: NSButton!
229
+ private var shellCheck: NSButton!
220
230
 
221
231
  private func makeSettingsView() -> NSView {
222
232
  let v = NSView(frame: NSRect(x: 0, y: 0, width: 380, height: 300))
@@ -229,7 +239,7 @@ final class AppController: NSObject, NSApplicationDelegate {
229
239
  paste.frame = NSRect(x: 16, y: 258, width: 200, height: 28)
230
240
  paste.bezelStyle = .rounded
231
241
  v.addSubview(paste)
232
- let hint = NSTextField(labelWithString: "(copies server + token + browser settings)")
242
+ let hint = NSTextField(labelWithString: "(copies server + token + browser + shell settings)")
233
243
  hint.frame = NSRect(x: 16, y: 238, width: 348, height: 16)
234
244
  hint.font = NSFont.systemFont(ofSize: 10)
235
245
  hint.textColor = .secondaryLabelColor
@@ -243,19 +253,41 @@ final class AppController: NSObject, NSApplicationDelegate {
243
253
  tokenField = NSTextField(frame: NSRect(x: 110, y: 170, width: 250, height: 24))
244
254
  tokenField.stringValue = cfg.token; v.addSubview(tokenField)
245
255
 
246
- v.addSubview(label("Browser", 134))
247
- enginePopup = NSPopUpButton(frame: NSRect(x: 110, y: 130, width: 250, height: 26))
256
+ // ── Capabilities (Browser + Shell are sibling optional tools) ──
257
+ let capLabel = NSTextField(labelWithString: "Capabilities")
258
+ capLabel.frame = NSRect(x: 16, y: 138, width: 200, height: 18)
259
+ capLabel.font = NSFont.boldSystemFont(ofSize: 12)
260
+ v.addSubview(capLabel)
261
+
262
+ // Browser (top-level toggle) + its sub-options (engine, headless).
263
+ browserCheck = NSButton(checkboxWithTitle: "Browser (control a local browser)",
264
+ target: self, action: #selector(toggleBrowserOptions))
265
+ browserCheck.frame = NSRect(x: 24, y: 116, width: 300, height: 20)
266
+ browserCheck.state = cfg.browser ? .on : .off
267
+ v.addSubview(browserCheck)
268
+
269
+ enginePopup = NSPopUpButton(frame: NSRect(x: 44, y: 88, width: 250, height: 26))
248
270
  enginePopup.addItems(withTitles: ["own (your Chrome)", "stealth (CloakBrowser)"])
249
271
  enginePopup.selectItem(at: cfg.engine == "stealth" ? 1 : 0)
250
272
  v.addSubview(enginePopup)
251
273
 
252
274
  headlessCheck = NSButton(checkboxWithTitle: "Headless (no window)", target: nil, action: nil)
253
- headlessCheck.frame = NSRect(x: 110, y: 98, width: 250, height: 20)
275
+ headlessCheck.frame = NSRect(x: 44, y: 66, width: 250, height: 20)
254
276
  headlessCheck.state = cfg.headless ? .on : .off
255
277
  v.addSubview(headlessCheck)
256
278
 
279
+ // Shell (sibling top-level toggle).
280
+ shellCheck = NSButton(checkboxWithTitle: "Shell (run commands + sync files)", target: nil, action: nil)
281
+ shellCheck.frame = NSRect(x: 24, y: 40, width: 300, height: 20)
282
+ shellCheck.state = cfg.shell ? .on : .off
283
+ shellCheck.toolTip = "Exposes exec + file sync on your machine, jailed to ~/.tianshu_shell. Off by default; enable only for a server you trust."
284
+ v.addSubview(shellCheck)
285
+
286
+ // Grey out browser sub-options when browser is off.
287
+ updateBrowserOptionsEnabled()
288
+
257
289
  let save = NSButton(title: "Save & Restart", target: self, action: #selector(saveSettings))
258
- save.frame = NSRect(x: 200, y: 20, width: 160, height: 32)
290
+ save.frame = NSRect(x: 200, y: 8, width: 160, height: 30)
259
291
  save.bezelStyle = .rounded
260
292
  v.addSubview(save)
261
293
  return v
@@ -265,7 +297,9 @@ final class AppController: NSObject, NSApplicationDelegate {
265
297
  cfg.server = serverField.stringValue.trimmingCharacters(in: .whitespaces)
266
298
  cfg.token = tokenField.stringValue.trimmingCharacters(in: .whitespaces)
267
299
  cfg.engine = enginePopup.indexOfSelectedItem == 1 ? "stealth" : "own"
300
+ cfg.browser = browserCheck.state == .on
268
301
  cfg.headless = headlessCheck.state == .on
302
+ cfg.shell = shellCheck.state == .on
269
303
  cfg.save()
270
304
  configWindow?.close()
271
305
  rebuildMenu()
@@ -295,6 +329,19 @@ final class AppController: NSObject, NSApplicationDelegate {
295
329
  if let t = params["token"] { tokenField.stringValue = t }
296
330
  if let e = params["engine"] { enginePopup.selectItem(at: e == "stealth" ? 1 : 0) }
297
331
  if let h = params["headless"] { headlessCheck.state = (h == "1" || h == "true") ? .on : .off }
332
+ if let s = params["shell"] { shellCheck.state = (s == "1" || s == "true") ? .on : .off }
333
+ if let b = params["browser"] { browserCheck.state = (b == "1" || b == "true") ? .on : .off }
334
+ updateBrowserOptionsEnabled()
335
+ }
336
+
337
+ // Enable/disable the browser sub-options (engine + headless) to
338
+ // reflect the top-level Browser toggle — they're meaningless when
339
+ // browser is off.
340
+ @objc func toggleBrowserOptions() { updateBrowserOptionsEnabled() }
341
+ private func updateBrowserOptionsEnabled() {
342
+ let on = browserCheck?.state == .on
343
+ enginePopup?.isEnabled = on
344
+ headlessCheck?.isEnabled = on
298
345
  }
299
346
 
300
347
  private func alert(_ title: String, _ body: String) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tianshu-ai/local-bridge",
3
- "version": "0.6.1",
3
+ "version": "0.7.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": {