@tianshu-ai/local-bridge 0.11.4 → 0.12.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/dist/bridge-manager.js +111 -0
- package/dist/bridge-manager.js.map +1 -0
- package/dist/profiles.js +113 -0
- package/dist/profiles.js.map +1 -0
- package/dist/tray.js +110 -265
- package/dist/tray.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// BridgeManager — manages multiple bridge child processes, one per profile.
|
|
2
|
+
//
|
|
3
|
+
// Each profile gets its own child process running `tsbridge --server ...`.
|
|
4
|
+
// The manager tracks state and provides start/stop per profile.
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { logPath } from "./profiles.js";
|
|
10
|
+
export class BridgeManager {
|
|
11
|
+
processes = new Map();
|
|
12
|
+
states = new Map();
|
|
13
|
+
onChange = null;
|
|
14
|
+
getState(profileId) {
|
|
15
|
+
return this.states.get(profileId) ?? "stopped";
|
|
16
|
+
}
|
|
17
|
+
getAllStatus(profiles) {
|
|
18
|
+
return profiles.map((p) => ({
|
|
19
|
+
id: p.id,
|
|
20
|
+
name: p.name,
|
|
21
|
+
state: this.getState(p.id),
|
|
22
|
+
server: p.server,
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
isRunning(profileId) {
|
|
26
|
+
const proc = this.processes.get(profileId);
|
|
27
|
+
return proc !== null && proc !== undefined && proc.exitCode === null && !proc.killed;
|
|
28
|
+
}
|
|
29
|
+
isAnyRunning() {
|
|
30
|
+
for (const [id] of this.processes) {
|
|
31
|
+
if (this.isRunning(id))
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
start(profile) {
|
|
37
|
+
if (this.isRunning(profile.id))
|
|
38
|
+
return;
|
|
39
|
+
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
40
|
+
const entry = path.join(here, "index.js");
|
|
41
|
+
const args = [entry, ...buildArgs(profile)];
|
|
42
|
+
let out = "ignore";
|
|
43
|
+
try {
|
|
44
|
+
out = fs.openSync(logPath(profile.id), "a");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
out = "ignore";
|
|
48
|
+
}
|
|
49
|
+
this.setState(profile.id, "starting");
|
|
50
|
+
const proc = spawn(process.execPath, args, {
|
|
51
|
+
stdio: ["ignore", out, out],
|
|
52
|
+
windowsHide: true,
|
|
53
|
+
});
|
|
54
|
+
proc.on("spawn", () => {
|
|
55
|
+
this.setState(profile.id, "running");
|
|
56
|
+
});
|
|
57
|
+
proc.on("exit", (code) => {
|
|
58
|
+
this.processes.delete(profile.id);
|
|
59
|
+
this.setState(profile.id, code === 0 ? "stopped" : "error");
|
|
60
|
+
});
|
|
61
|
+
proc.on("error", () => {
|
|
62
|
+
this.processes.delete(profile.id);
|
|
63
|
+
this.setState(profile.id, "error");
|
|
64
|
+
});
|
|
65
|
+
this.processes.set(profile.id, proc);
|
|
66
|
+
}
|
|
67
|
+
stop(profileId) {
|
|
68
|
+
const proc = this.processes.get(profileId);
|
|
69
|
+
if (proc && this.isRunning(profileId)) {
|
|
70
|
+
proc.kill();
|
|
71
|
+
}
|
|
72
|
+
this.processes.delete(profileId);
|
|
73
|
+
this.setState(profileId, "stopped");
|
|
74
|
+
}
|
|
75
|
+
stopAll() {
|
|
76
|
+
for (const [id] of this.processes) {
|
|
77
|
+
this.stop(id);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Start all profiles marked autoStart. */
|
|
81
|
+
startAutoStart(profiles) {
|
|
82
|
+
for (const p of profiles) {
|
|
83
|
+
if (p.autoStart)
|
|
84
|
+
this.start(p);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
setState(profileId, state) {
|
|
88
|
+
this.states.set(profileId, state);
|
|
89
|
+
this.onChange?.();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function buildArgs(p) {
|
|
93
|
+
const args = ["--server", p.server];
|
|
94
|
+
if (p.token)
|
|
95
|
+
args.push("--token", p.token);
|
|
96
|
+
if (p.device)
|
|
97
|
+
args.push("--device", p.device);
|
|
98
|
+
if (p.browser) {
|
|
99
|
+
if (p.engine === "stealth")
|
|
100
|
+
args.push("--browser-engine", "stealth");
|
|
101
|
+
if (p.headless)
|
|
102
|
+
args.push("--headless");
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
args.push("--no-browser");
|
|
106
|
+
}
|
|
107
|
+
if (p.shell)
|
|
108
|
+
args.push("--shell");
|
|
109
|
+
return args;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=bridge-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-manager.js","sourceRoot":"","sources":["../src/bridge-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/profiles.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Multi-profile configuration for the bridge.
|
|
2
|
+
//
|
|
3
|
+
// Backward-compatible: reads old single-server config.json and
|
|
4
|
+
// migrates it to profiles[] on save. Each profile has its own
|
|
5
|
+
// server/token/device/label and can be started/stopped independently.
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
// ── Paths ──────────────────────────────────────────────────────────
|
|
10
|
+
export function configDir() {
|
|
11
|
+
const dir = path.join(os.homedir(), ".tianshu-bridge");
|
|
12
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
13
|
+
return dir;
|
|
14
|
+
}
|
|
15
|
+
export function configPath() {
|
|
16
|
+
return path.join(configDir(), "config.json");
|
|
17
|
+
}
|
|
18
|
+
export function logPath(profileId) {
|
|
19
|
+
if (profileId) {
|
|
20
|
+
return path.join(configDir(), `bridge-${profileId}.log`);
|
|
21
|
+
}
|
|
22
|
+
return path.join(configDir(), "bridge.log");
|
|
23
|
+
}
|
|
24
|
+
// ── Profile helpers ────────────────────────────────────────────────
|
|
25
|
+
function generateId() {
|
|
26
|
+
return `p_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
27
|
+
}
|
|
28
|
+
export function createProfile(partial) {
|
|
29
|
+
return {
|
|
30
|
+
id: partial.id ?? generateId(),
|
|
31
|
+
name: partial.name ?? new URL(partial.server).hostname,
|
|
32
|
+
server: partial.server,
|
|
33
|
+
token: partial.token ?? "",
|
|
34
|
+
device: partial.device ?? os.hostname() ?? "bridge",
|
|
35
|
+
autoStart: partial.autoStart ?? true,
|
|
36
|
+
browser: partial.browser ?? true,
|
|
37
|
+
engine: partial.engine ?? "own",
|
|
38
|
+
headless: partial.headless ?? false,
|
|
39
|
+
shell: partial.shell ?? false,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// ── Load / Save ────────────────────────────────────────────────────
|
|
43
|
+
export function loadConfig() {
|
|
44
|
+
const p = configPath();
|
|
45
|
+
try {
|
|
46
|
+
const raw = JSON.parse(fs.readFileSync(p, "utf8"));
|
|
47
|
+
// New format: has profiles[]
|
|
48
|
+
if (Array.isArray(raw.profiles)) {
|
|
49
|
+
return {
|
|
50
|
+
profiles: raw.profiles.map((p) => createProfile({ server: p.server ?? "ws://localhost:3110/ws", ...p })),
|
|
51
|
+
defaults: raw.defaults,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
// Legacy format: single server/token at top level → migrate
|
|
55
|
+
const legacy = raw;
|
|
56
|
+
if (legacy.server) {
|
|
57
|
+
const profile = createProfile({
|
|
58
|
+
name: "Default",
|
|
59
|
+
server: legacy.server,
|
|
60
|
+
token: legacy.token ?? "",
|
|
61
|
+
device: legacy.device ?? "",
|
|
62
|
+
browser: legacy.browser ?? true,
|
|
63
|
+
engine: legacy.engine ?? "own",
|
|
64
|
+
headless: legacy.headless ?? false,
|
|
65
|
+
shell: legacy.shell ?? false,
|
|
66
|
+
});
|
|
67
|
+
const config = { profiles: [profile] };
|
|
68
|
+
// Save migrated format
|
|
69
|
+
saveConfig(config);
|
|
70
|
+
return config;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
/* no file or parse error */
|
|
75
|
+
}
|
|
76
|
+
// Default: empty profiles list
|
|
77
|
+
return { profiles: [] };
|
|
78
|
+
}
|
|
79
|
+
export function saveConfig(config) {
|
|
80
|
+
const p = configPath();
|
|
81
|
+
fs.writeFileSync(p, JSON.stringify(config, null, 2) + "\n");
|
|
82
|
+
}
|
|
83
|
+
export function ensureConfigFile() {
|
|
84
|
+
const p = configPath();
|
|
85
|
+
if (!fs.existsSync(p)) {
|
|
86
|
+
saveConfig({ profiles: [] });
|
|
87
|
+
}
|
|
88
|
+
return p;
|
|
89
|
+
}
|
|
90
|
+
// ── Convenience ────────────────────────────────────────────────────
|
|
91
|
+
export function addProfile(config, partial) {
|
|
92
|
+
const profile = createProfile(partial);
|
|
93
|
+
config.profiles.push(profile);
|
|
94
|
+
saveConfig(config);
|
|
95
|
+
return profile;
|
|
96
|
+
}
|
|
97
|
+
export function removeProfile(config, id) {
|
|
98
|
+
const idx = config.profiles.findIndex((p) => p.id === id);
|
|
99
|
+
if (idx < 0)
|
|
100
|
+
return false;
|
|
101
|
+
config.profiles.splice(idx, 1);
|
|
102
|
+
saveConfig(config);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
export function updateProfile(config, id, patch) {
|
|
106
|
+
const profile = config.profiles.find((p) => p.id === id);
|
|
107
|
+
if (!profile)
|
|
108
|
+
return false;
|
|
109
|
+
Object.assign(profile, patch);
|
|
110
|
+
saveConfig(config);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiles.js","sourceRoot":"","sources":["../src/profiles.ts"],"names":[],"mappings":"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"}
|
package/dist/tray.js
CHANGED
|
@@ -1,87 +1,25 @@
|
|
|
1
|
-
// `tsbridge tray` —
|
|
1
|
+
// `tsbridge tray` — multi-profile system-tray bridge manager.
|
|
2
2
|
//
|
|
3
|
-
// One Node process, one
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// with a live status line + Start/Stop, a Settings hint, Open Log, and Quit.
|
|
7
|
-
// The bridge itself runs as a spawned `tsbridge --server …` child so the
|
|
8
|
-
// tray and the connection are decoupled (crash of one doesn't take the
|
|
9
|
-
// other down).
|
|
3
|
+
// One Node process, one tray icon. Each profile in config.json gets its
|
|
4
|
+
// own child process and its own status line in the tray menu. Profiles
|
|
5
|
+
// marked autoStart launch when the tray starts.
|
|
10
6
|
//
|
|
11
|
-
// Config lives at ~/.tianshu-bridge/config.json —
|
|
12
|
-
//
|
|
13
|
-
import { spawn } from "node:child_process";
|
|
7
|
+
// Config lives at ~/.tianshu-bridge/config.json — supports both the
|
|
8
|
+
// legacy single-server format and the new multi-profile format.
|
|
14
9
|
import fs from "node:fs";
|
|
15
|
-
import os from "node:os";
|
|
16
10
|
import path from "node:path";
|
|
17
11
|
import { fileURLToPath } from "node:url";
|
|
12
|
+
import { loadConfig, configPath, ensureConfigFile, logPath } from "./profiles.js";
|
|
13
|
+
import { BridgeManager } from "./bridge-manager.js";
|
|
18
14
|
import { openSettingsWindow } from "./settings-window.js";
|
|
19
|
-
// systray2 is CommonJS. Under NodeNext the default-import interop
|
|
20
|
-
// mis-resolves the class (sees a namespace with no construct signature),
|
|
21
|
-
// so load it through createRequire to get the real constructor, and type
|
|
22
|
-
// it structurally so we don't depend on the package's ESM typings.
|
|
23
15
|
import { createRequire } from "node:module";
|
|
24
16
|
const require = createRequire(import.meta.url);
|
|
25
17
|
const SysTray = require("systray2").default;
|
|
26
|
-
// A separator menu item (systray2 uses the literal "<SEPARATOR>" title).
|
|
27
18
|
const SEPARATOR = { title: "<SEPARATOR>", tooltip: "", enabled: true };
|
|
28
|
-
|
|
29
|
-
const dir = path.join(os.homedir(), ".tianshu-bridge");
|
|
30
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
31
|
-
return dir;
|
|
32
|
-
}
|
|
33
|
-
function configPath() {
|
|
34
|
-
return path.join(configDir(), "config.json");
|
|
35
|
-
}
|
|
36
|
-
function logPath() {
|
|
37
|
-
return path.join(configDir(), "bridge.log");
|
|
38
|
-
}
|
|
39
|
-
function defaultConfig() {
|
|
40
|
-
return {
|
|
41
|
-
server: "ws://localhost:3110/ws",
|
|
42
|
-
token: "",
|
|
43
|
-
browser: true,
|
|
44
|
-
engine: "own",
|
|
45
|
-
headless: false,
|
|
46
|
-
shell: false,
|
|
47
|
-
device: "",
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
function loadConfig() {
|
|
51
|
-
const defaults = defaultConfig();
|
|
52
|
-
try {
|
|
53
|
-
const raw = JSON.parse(fs.readFileSync(configPath(), "utf8"));
|
|
54
|
-
return { ...defaults, ...raw };
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
return defaults;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/** Ensure config.json exists on disk so "Edit settings" always has a
|
|
61
|
-
* file to open (first run hasn't Started the bridge yet, so nothing
|
|
62
|
-
* has written it). Returns the path. Best-effort. */
|
|
63
|
-
function ensureConfigFile() {
|
|
64
|
-
const p = configPath();
|
|
65
|
-
try {
|
|
66
|
-
if (!fs.existsSync(p)) {
|
|
67
|
-
fs.writeFileSync(p, JSON.stringify(defaultConfig(), null, 2) + "\n");
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
/* best-effort */
|
|
72
|
-
}
|
|
73
|
-
return p;
|
|
74
|
-
}
|
|
75
|
-
// ─── tray icons ─────────────────────────────────────────────────────
|
|
76
|
-
//
|
|
77
|
-
// systray2 wants a base64 icon string: .ico on Windows, .png elsewhere.
|
|
78
|
-
// We ship two tiny icons next to the built module (assets/) and read them
|
|
79
|
-
// as base64 at runtime. Falls back to an empty string if missing (the
|
|
80
|
-
// tray still works, just without a custom glyph).
|
|
19
|
+
// ── icons ──────────────────────────────────────────────────────────
|
|
81
20
|
function iconBase64(state) {
|
|
82
21
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
83
22
|
const ext = process.platform === "win32" ? "ico" : "png";
|
|
84
|
-
// dist/tray.js → ../assets/tray-<state>.<ext> (shipped in the package)
|
|
85
23
|
const candidates = [
|
|
86
24
|
path.join(here, "..", "assets", `tray-${state}.${ext}`),
|
|
87
25
|
path.join(here, "assets", `tray-${state}.${ext}`),
|
|
@@ -90,89 +28,12 @@ function iconBase64(state) {
|
|
|
90
28
|
try {
|
|
91
29
|
return fs.readFileSync(p).toString("base64");
|
|
92
30
|
}
|
|
93
|
-
catch {
|
|
94
|
-
/* try next */
|
|
95
|
-
}
|
|
31
|
+
catch { /* try next */ }
|
|
96
32
|
}
|
|
97
33
|
return "";
|
|
98
34
|
}
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
const args = ["--server", cfg.server];
|
|
102
|
-
if (cfg.token)
|
|
103
|
-
args.push("--token", cfg.token);
|
|
104
|
-
if (cfg.browser) {
|
|
105
|
-
if (cfg.engine === "stealth")
|
|
106
|
-
args.push("--browser-engine", "stealth");
|
|
107
|
-
if (cfg.headless)
|
|
108
|
-
args.push("--headless");
|
|
109
|
-
}
|
|
110
|
-
else {
|
|
111
|
-
args.push("--no-browser");
|
|
112
|
-
}
|
|
113
|
-
if (cfg.shell)
|
|
114
|
-
args.push("--shell");
|
|
115
|
-
if (cfg.device)
|
|
116
|
-
args.push("--device", cfg.device);
|
|
117
|
-
return args;
|
|
118
|
-
}
|
|
119
|
-
/** Resolve how to launch the bridge CLI as a child.
|
|
120
|
-
* We re-invoke THIS package's own entry (dist/index.js) with the
|
|
121
|
-
* current node binary, so we don't depend on `tsbridge` being on PATH
|
|
122
|
-
* and don't recurse into the tray subcommand. */
|
|
123
|
-
function bridgeSpawn(cfg) {
|
|
124
|
-
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
125
|
-
const entry = path.join(here, "index.js");
|
|
126
|
-
return { cmd: process.execPath, args: [entry, ...buildArgs(cfg)] };
|
|
127
|
-
}
|
|
128
|
-
class Bridge {
|
|
129
|
-
proc = null;
|
|
130
|
-
onStateChange = null;
|
|
131
|
-
get running() {
|
|
132
|
-
return this.proc !== null && this.proc.exitCode === null && !this.proc.killed;
|
|
133
|
-
}
|
|
134
|
-
start(cfg) {
|
|
135
|
-
this.stop();
|
|
136
|
-
const { cmd, args } = bridgeSpawn(cfg);
|
|
137
|
-
// Log stdout/stderr to ~/.tianshu-bridge/bridge.log for troubleshooting.
|
|
138
|
-
let out = "ignore";
|
|
139
|
-
try {
|
|
140
|
-
out = fs.openSync(logPath(), "a");
|
|
141
|
-
}
|
|
142
|
-
catch {
|
|
143
|
-
out = "ignore";
|
|
144
|
-
}
|
|
145
|
-
const p = spawn(cmd, args, {
|
|
146
|
-
stdio: ["ignore", out, out],
|
|
147
|
-
// detached:false → child dies with the tray; that's what we want.
|
|
148
|
-
windowsHide: true,
|
|
149
|
-
});
|
|
150
|
-
p.on("exit", () => {
|
|
151
|
-
this.proc = null;
|
|
152
|
-
this.onStateChange?.();
|
|
153
|
-
});
|
|
154
|
-
p.on("error", () => {
|
|
155
|
-
this.proc = null;
|
|
156
|
-
this.onStateChange?.();
|
|
157
|
-
});
|
|
158
|
-
this.proc = p;
|
|
159
|
-
this.onStateChange?.();
|
|
160
|
-
}
|
|
161
|
-
stop() {
|
|
162
|
-
if (this.proc && this.running) {
|
|
163
|
-
this.proc.kill();
|
|
164
|
-
}
|
|
165
|
-
this.proc = null;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
// ─── tray ───────────────────────────────────────────────────────────
|
|
169
|
-
/**
|
|
170
|
-
* systray2 copies its prebuilt tray binary into ~/.cache/node-systray/
|
|
171
|
-
* (and also ships one inside the package's traybin/), but it does NOT
|
|
172
|
-
* mark it executable on Unix — so the very first launch fails with
|
|
173
|
-
* EACCES. Fix it up before we start: chmod +x every candidate binary
|
|
174
|
-
* that exists. No-op on Windows (.exe needs no exec bit).
|
|
175
|
-
*/
|
|
35
|
+
// ── tray binary fix ────────────────────────────────────────────────
|
|
36
|
+
import os from "node:os";
|
|
176
37
|
function ensureTrayBinExecutable() {
|
|
177
38
|
if (process.platform === "win32")
|
|
178
39
|
return;
|
|
@@ -180,7 +41,6 @@ function ensureTrayBinExecutable() {
|
|
|
180
41
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
181
42
|
const candidates = [
|
|
182
43
|
path.join(os.homedir(), ".cache", "node-systray", "2.1.4", binName),
|
|
183
|
-
// package-internal copy (dist/../node_modules or bundled traybin)
|
|
184
44
|
path.join(here, "..", "node_modules", "systray2", "traybin", binName),
|
|
185
45
|
];
|
|
186
46
|
for (const p of candidates) {
|
|
@@ -188,134 +48,130 @@ function ensureTrayBinExecutable() {
|
|
|
188
48
|
if (fs.existsSync(p))
|
|
189
49
|
fs.chmodSync(p, 0o755);
|
|
190
50
|
}
|
|
191
|
-
catch {
|
|
192
|
-
/* best-effort */
|
|
193
|
-
}
|
|
51
|
+
catch { /* best-effort */ }
|
|
194
52
|
}
|
|
195
53
|
}
|
|
54
|
+
// ── state indicators ───────────────────────────────────────────────
|
|
55
|
+
const STATE_ICON = { stopped: "○", starting: "◌", running: "●", error: "✕" };
|
|
56
|
+
// ── tray ───────────────────────────────────────────────────────────
|
|
196
57
|
export async function runTray() {
|
|
197
|
-
let
|
|
198
|
-
const
|
|
58
|
+
let config = loadConfig();
|
|
59
|
+
const manager = new BridgeManager();
|
|
199
60
|
ensureTrayBinExecutable();
|
|
200
|
-
//
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
61
|
+
// ── Build menu items ───────────────────────────────────────────
|
|
62
|
+
function buildProfileItems() {
|
|
63
|
+
if (config.profiles.length === 0) {
|
|
64
|
+
return [{
|
|
65
|
+
title: "No profiles configured",
|
|
66
|
+
tooltip: "Open Settings to add a server profile",
|
|
67
|
+
enabled: false,
|
|
68
|
+
}];
|
|
69
|
+
}
|
|
70
|
+
const items = [];
|
|
71
|
+
for (const p of config.profiles) {
|
|
72
|
+
const state = manager.getState(p.id);
|
|
73
|
+
const running = manager.isRunning(p.id);
|
|
74
|
+
// Profile status line
|
|
75
|
+
items.push({
|
|
76
|
+
title: `${STATE_ICON[state]} ${p.name} — ${p.server.replace(/^wss?:\/\//, "").replace(/\/ws$/, "")}`,
|
|
77
|
+
tooltip: `${p.name}: ${state}`,
|
|
78
|
+
enabled: false,
|
|
79
|
+
});
|
|
80
|
+
// Start/Stop toggle
|
|
81
|
+
items.push({
|
|
82
|
+
title: running ? ` Stop ${p.name}` : ` Start ${p.name}`,
|
|
83
|
+
tooltip: running ? `Stop profile ${p.name}` : `Start profile ${p.name}`,
|
|
84
|
+
enabled: true,
|
|
85
|
+
click: () => {
|
|
86
|
+
if (running) {
|
|
87
|
+
manager.stop(p.id);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
manager.start(p);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return items;
|
|
96
|
+
}
|
|
229
97
|
const settingsItem = {
|
|
230
98
|
title: "Settings…",
|
|
231
|
-
tooltip: "Edit
|
|
99
|
+
tooltip: "Edit bridge settings",
|
|
232
100
|
enabled: true,
|
|
233
101
|
click: () => {
|
|
234
102
|
const p = ensureConfigFile();
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
headless: c.headless,
|
|
244
|
-
shell: c.shell,
|
|
245
|
-
device: c.device,
|
|
103
|
+
openSettingsWindow(p, config.profiles[0] ?? {
|
|
104
|
+
server: "ws://localhost:3110/ws",
|
|
105
|
+
token: "",
|
|
106
|
+
browser: true,
|
|
107
|
+
engine: "own",
|
|
108
|
+
headless: false,
|
|
109
|
+
shell: false,
|
|
110
|
+
device: "",
|
|
246
111
|
});
|
|
247
|
-
if (!opened)
|
|
248
|
-
openPath(p);
|
|
249
112
|
},
|
|
250
113
|
};
|
|
114
|
+
const startAllItem = {
|
|
115
|
+
title: "Start All",
|
|
116
|
+
tooltip: "Start all profiles",
|
|
117
|
+
enabled: true,
|
|
118
|
+
click: () => {
|
|
119
|
+
config = loadConfig();
|
|
120
|
+
for (const p of config.profiles)
|
|
121
|
+
manager.start(p);
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
const stopAllItem = {
|
|
125
|
+
title: "Stop All",
|
|
126
|
+
tooltip: "Stop all profiles",
|
|
127
|
+
enabled: true,
|
|
128
|
+
click: () => manager.stopAll(),
|
|
129
|
+
};
|
|
251
130
|
const logItem = {
|
|
252
|
-
title: "Open
|
|
253
|
-
tooltip: "Open bridge
|
|
131
|
+
title: "Open Log",
|
|
132
|
+
tooltip: "Open bridge log",
|
|
254
133
|
enabled: true,
|
|
255
134
|
click: () => openPath(logPath()),
|
|
256
135
|
};
|
|
257
136
|
const quitItem = {
|
|
258
137
|
title: "Quit",
|
|
259
|
-
tooltip: "Stop
|
|
138
|
+
tooltip: "Stop all and quit",
|
|
260
139
|
enabled: true,
|
|
261
140
|
click: () => {
|
|
262
|
-
|
|
141
|
+
manager.stopAll();
|
|
263
142
|
void systray.kill(true);
|
|
264
143
|
},
|
|
265
144
|
};
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
145
|
+
function buildMenu() {
|
|
146
|
+
const anyRunning = manager.isAnyRunning();
|
|
147
|
+
return {
|
|
148
|
+
icon: iconBase64(anyRunning ? "on" : "off"),
|
|
269
149
|
isTemplateIcon: process.platform === "darwin",
|
|
270
150
|
title: "Tianshu Bridge",
|
|
271
|
-
tooltip:
|
|
151
|
+
tooltip: anyRunning
|
|
152
|
+
? `Tianshu Bridge: ${config.profiles.filter((p) => manager.isRunning(p.id)).length}/${config.profiles.length} connected`
|
|
153
|
+
: "Tianshu Bridge: stopped",
|
|
272
154
|
items: [
|
|
273
|
-
|
|
274
|
-
serverItem,
|
|
155
|
+
...buildProfileItems(),
|
|
275
156
|
SEPARATOR,
|
|
276
|
-
|
|
277
|
-
|
|
157
|
+
startAllItem,
|
|
158
|
+
stopAllItem,
|
|
278
159
|
SEPARATOR,
|
|
279
160
|
settingsItem,
|
|
280
161
|
logItem,
|
|
281
162
|
SEPARATOR,
|
|
282
163
|
quitItem,
|
|
283
164
|
],
|
|
284
|
-
}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const systray = new SysTray({
|
|
168
|
+
menu: buildMenu(),
|
|
285
169
|
debug: false,
|
|
286
|
-
copyDir: true,
|
|
287
|
-
// running from inside node_modules on some setups)
|
|
170
|
+
copyDir: true,
|
|
288
171
|
});
|
|
289
|
-
//
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
statusItem.title = on ? "● Connected" : "○ Stopped";
|
|
293
|
-
startItem.hidden = on;
|
|
294
|
-
stopItem.hidden = !on;
|
|
295
|
-
void systray.sendAction({ type: "update-item", item: statusItem });
|
|
296
|
-
void systray.sendAction({ type: "update-item", item: startItem });
|
|
297
|
-
void systray.sendAction({ type: "update-item", item: stopItem });
|
|
298
|
-
void systray.sendAction({
|
|
299
|
-
type: "update-menu",
|
|
300
|
-
menu: {
|
|
301
|
-
icon: iconBase64(on ? "on" : "off"),
|
|
302
|
-
isTemplateIcon: process.platform === "darwin",
|
|
303
|
-
title: "Tianshu Bridge",
|
|
304
|
-
tooltip: on ? "Tianshu Bridge: connected" : "Tianshu Bridge: stopped",
|
|
305
|
-
items: [
|
|
306
|
-
statusItem,
|
|
307
|
-
serverItem,
|
|
308
|
-
SEPARATOR,
|
|
309
|
-
startItem,
|
|
310
|
-
stopItem,
|
|
311
|
-
SEPARATOR,
|
|
312
|
-
settingsItem,
|
|
313
|
-
logItem,
|
|
314
|
-
SEPARATOR,
|
|
315
|
-
quitItem,
|
|
316
|
-
],
|
|
317
|
-
},
|
|
318
|
-
});
|
|
172
|
+
// Refresh the menu whenever a profile's state changes.
|
|
173
|
+
manager.onChange = () => {
|
|
174
|
+
void systray.sendAction({ type: "update-menu", menu: buildMenu() });
|
|
319
175
|
};
|
|
320
176
|
systray.onClick((action) => {
|
|
321
177
|
const item = action.item;
|
|
@@ -323,37 +179,28 @@ export async function runTray() {
|
|
|
323
179
|
item.click();
|
|
324
180
|
});
|
|
325
181
|
await systray.ready();
|
|
326
|
-
console.log(`[local-bridge] tray running — config
|
|
327
|
-
`
|
|
328
|
-
//
|
|
182
|
+
console.log(`[local-bridge] tray running — ${config.profiles.length} profile(s)\n` +
|
|
183
|
+
` Config: ${configPath()}`);
|
|
184
|
+
// Auto-start profiles
|
|
185
|
+
manager.startAutoStart(config.profiles);
|
|
329
186
|
return await new Promise((resolve) => {
|
|
330
187
|
systray.onExit(() => resolve(0));
|
|
331
188
|
});
|
|
332
189
|
}
|
|
333
|
-
//
|
|
334
|
-
/** Open a file with the OS default handler (cross-platform).
|
|
335
|
-
* Windows: `cmd /c start "" file` is flaky under a detached spawn and
|
|
336
|
-
* fails if the file type has no association, so open the file's default
|
|
337
|
-
* editor via a small chain — try the shell association, then notepad as
|
|
338
|
-
* a guaranteed fallback for our .json/.log text files. */
|
|
190
|
+
// ── util ───────────────────────────────────────────────────────────
|
|
339
191
|
function openPath(target) {
|
|
192
|
+
const { spawn } = require("node:child_process");
|
|
340
193
|
const plat = process.platform;
|
|
341
194
|
try {
|
|
342
195
|
if (plat === "win32") {
|
|
343
|
-
// `explorer <file>` opens with the registered handler; if none,
|
|
344
|
-
// fall back to notepad (always present) for our text files.
|
|
345
196
|
const child = spawn("cmd", ["/d", "/s", "/c", "start", "", target], {
|
|
346
|
-
stdio: "ignore",
|
|
347
|
-
detached: true,
|
|
348
|
-
windowsHide: true,
|
|
197
|
+
stdio: "ignore", detached: true, windowsHide: true,
|
|
349
198
|
});
|
|
350
199
|
child.on("error", () => {
|
|
351
200
|
try {
|
|
352
201
|
spawn("notepad", [target], { stdio: "ignore", detached: true }).unref();
|
|
353
202
|
}
|
|
354
|
-
catch {
|
|
355
|
-
/* give up */
|
|
356
|
-
}
|
|
203
|
+
catch { }
|
|
357
204
|
});
|
|
358
205
|
child.unref();
|
|
359
206
|
return;
|
|
@@ -361,8 +208,6 @@ function openPath(target) {
|
|
|
361
208
|
const cmd = plat === "darwin" ? "open" : "xdg-open";
|
|
362
209
|
spawn(cmd, [target], { stdio: "ignore", detached: true }).unref();
|
|
363
210
|
}
|
|
364
|
-
catch {
|
|
365
|
-
/* best-effort */
|
|
366
|
-
}
|
|
211
|
+
catch { }
|
|
367
212
|
}
|
|
368
213
|
//# sourceMappingURL=tray.js.map
|
package/dist/tray.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tray.js","sourceRoot":"","sources":["../src/tray.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"tray.js","sourceRoot":"","sources":["../src/tray.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.
|
|
3
|
+
"version": "0.12.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": {
|