@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.
- package/app/TianshuBridge.swift +58 -11
- package/package.json +1 -1
package/app/TianshuBridge.swift
CHANGED
|
@@ -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
|
|
20
|
-
var
|
|
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.
|
|
94
|
-
|
|
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
|
-
|
|
178
|
-
|
|
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
|
-
|
|
247
|
-
|
|
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:
|
|
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:
|
|
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.
|
|
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": {
|