opendevbrowser 0.0.11 → 0.0.12

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.
@@ -1,5 +1,126 @@
1
1
  import { ConnectionManager } from "./services/ConnectionManager.js";
2
+ import { DEFAULT_AUTO_CONNECT, DEFAULT_AUTO_PAIR, DEFAULT_DISCOVERY_PORT, DEFAULT_PAIRING_ENABLED, DEFAULT_RELAY_PORT } from "./relay-settings.js";
2
3
  const connection = new ConnectionManager();
4
+ let autoConnectInFlight = false;
5
+ const updateBadge = (status) => {
6
+ const isConnected = status === "connected";
7
+ chrome.action.setBadgeText({ text: isConnected ? "ON" : "OFF" });
8
+ chrome.action.setBadgeBackgroundColor({
9
+ color: isConnected ? "#20d5c6" : "#5b667a"
10
+ });
11
+ };
12
+ const parsePort = (value) => {
13
+ if (typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 65535) {
14
+ return value;
15
+ }
16
+ if (typeof value === "string" && value.trim()) {
17
+ const parsed = Number(value);
18
+ if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65535) {
19
+ return parsed;
20
+ }
21
+ }
22
+ return null;
23
+ };
24
+ const fetchRelayConfig = async (port) => {
25
+ try {
26
+ const response = await fetch(`http://127.0.0.1:${port}/config`, {
27
+ method: "GET",
28
+ headers: { "Accept": "application/json" }
29
+ });
30
+ if (!response.ok) {
31
+ return null;
32
+ }
33
+ const data = await response.json();
34
+ const relayPort = parsePort(data.relayPort);
35
+ if (!relayPort) {
36
+ return null;
37
+ }
38
+ const pairingRequired = typeof data.pairingRequired === "boolean" ? data.pairingRequired : true;
39
+ return { relayPort, pairingRequired };
40
+ }
41
+ catch {
42
+ return null;
43
+ }
44
+ };
45
+ const fetchTokenFromPlugin = async (port) => {
46
+ try {
47
+ const response = await fetch(`http://127.0.0.1:${port}/pair`, {
48
+ method: "GET",
49
+ headers: { "Accept": "application/json" }
50
+ });
51
+ if (!response.ok) {
52
+ return null;
53
+ }
54
+ const data = await response.json();
55
+ return typeof data.token === "string" ? data.token : null;
56
+ }
57
+ catch {
58
+ return null;
59
+ }
60
+ };
61
+ const attemptAutoConnect = async () => {
62
+ const data = await new Promise((resolve) => {
63
+ chrome.storage.local.get(["autoConnect", "autoPair", "pairingEnabled", "pairingToken", "relayPort"], (items) => {
64
+ resolve(items);
65
+ });
66
+ });
67
+ const autoConnect = typeof data.autoConnect === "boolean" ? data.autoConnect : DEFAULT_AUTO_CONNECT;
68
+ if (!autoConnect || connection.getStatus() === "connected") {
69
+ return;
70
+ }
71
+ const autoPair = typeof data.autoPair === "boolean" ? data.autoPair : DEFAULT_AUTO_PAIR;
72
+ const pairingEnabled = typeof data.pairingEnabled === "boolean" ? data.pairingEnabled : DEFAULT_PAIRING_ENABLED;
73
+ if (autoPair && pairingEnabled) {
74
+ const config = await fetchRelayConfig(DEFAULT_DISCOVERY_PORT);
75
+ const relayPort = config?.relayPort ?? parsePort(data.relayPort) ?? DEFAULT_RELAY_PORT;
76
+ if (config?.relayPort) {
77
+ chrome.storage.local.set({ relayPort: config.relayPort });
78
+ }
79
+ const pairingRequired = config?.pairingRequired ?? true;
80
+ if (pairingRequired) {
81
+ const fetchedToken = await fetchTokenFromPlugin(relayPort);
82
+ if (fetchedToken) {
83
+ chrome.storage.local.set({ pairingToken: fetchedToken });
84
+ }
85
+ else {
86
+ return;
87
+ }
88
+ }
89
+ }
90
+ await connection.connect();
91
+ };
92
+ const autoConnect = async () => {
93
+ if (autoConnectInFlight) {
94
+ return;
95
+ }
96
+ autoConnectInFlight = true;
97
+ try {
98
+ await attemptAutoConnect();
99
+ }
100
+ catch {
101
+ connection.disconnect();
102
+ }
103
+ finally {
104
+ autoConnectInFlight = false;
105
+ }
106
+ };
107
+ connection.onStatus(updateBadge);
108
+ updateBadge(connection.getStatus());
109
+ chrome.runtime.onStartup.addListener(() => {
110
+ autoConnect().catch(() => { });
111
+ });
112
+ chrome.runtime.onInstalled.addListener(() => {
113
+ autoConnect().catch(() => { });
114
+ });
115
+ autoConnect().catch(() => { });
116
+ chrome.storage.onChanged.addListener((changes, area) => {
117
+ if (area !== "local") {
118
+ return;
119
+ }
120
+ if (changes.autoConnect?.newValue === true) {
121
+ autoConnect().catch(() => { });
122
+ }
123
+ });
3
124
  chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
4
125
  const respond = (status) => {
5
126
  sendResponse(status);
@@ -1,23 +1,32 @@
1
- import { DEFAULT_AUTO_PAIR, DEFAULT_DISCOVERY_PORT, DEFAULT_PAIRING_ENABLED, DEFAULT_PAIRING_TOKEN, DEFAULT_RELAY_PORT } from "./relay-settings.js";
1
+ import { DEFAULT_AUTO_CONNECT, DEFAULT_AUTO_PAIR, DEFAULT_DISCOVERY_PORT, DEFAULT_PAIRING_ENABLED, DEFAULT_PAIRING_TOKEN, DEFAULT_RELAY_PORT } from "./relay-settings.js";
2
2
  const statusEl = document.getElementById("status");
3
3
  const statusIndicator = document.getElementById("statusIndicator");
4
+ const statusPill = document.getElementById("statusPill");
5
+ const statusNote = document.getElementById("statusNote");
4
6
  const toggleButton = document.getElementById("toggle");
5
7
  const relayPortInput = document.getElementById("relayPort");
6
8
  const pairingTokenInput = document.getElementById("pairingToken");
7
9
  const pairingEnabledInput = document.getElementById("pairingEnabled");
8
10
  const autoPairInput = document.getElementById("autoPair");
9
- if (!statusEl || !statusIndicator || !toggleButton || !relayPortInput || !pairingTokenInput || !pairingEnabledInput || !autoPairInput) {
11
+ const autoConnectInput = document.getElementById("autoConnect");
12
+ if (!statusEl || !statusIndicator || !statusPill || !statusNote || !toggleButton || !relayPortInput || !pairingTokenInput || !pairingEnabledInput || !autoPairInput || !autoConnectInput) {
10
13
  throw new Error("Popup DOM missing required elements");
11
14
  }
15
+ const defaultNote = "Local relay only. Tokens stay on-device.";
16
+ const setNote = (message = defaultNote) => {
17
+ statusNote.textContent = message;
18
+ };
12
19
  const setStatus = (status) => {
13
20
  const isConnected = status === "connected";
14
21
  statusEl.textContent = isConnected ? "Connected" : "Disconnected";
15
22
  toggleButton.textContent = isConnected ? "Disconnect" : "Connect";
16
23
  if (isConnected) {
17
24
  statusIndicator.classList.add("connected");
25
+ statusPill.classList.add("connected");
18
26
  }
19
27
  else {
20
28
  statusIndicator.classList.remove("connected");
29
+ statusPill.classList.remove("connected");
21
30
  }
22
31
  };
23
32
  const sendMessage = (message) => {
@@ -30,6 +39,7 @@ const sendMessage = (message) => {
30
39
  const refreshStatus = async () => {
31
40
  const response = await sendMessage({ type: "status" });
32
41
  setStatus(response.status);
42
+ setNote();
33
43
  };
34
44
  const fetchTokenFromPlugin = async (port) => {
35
45
  try {
@@ -89,23 +99,29 @@ const applyRelayPort = (port) => {
89
99
  };
90
100
  const loadSettings = async () => {
91
101
  const data = await new Promise((resolve) => {
92
- chrome.storage.local.get(["pairingToken", "pairingEnabled", "relayPort", "autoPair"], (items) => {
102
+ chrome.storage.local.get(["pairingToken", "pairingEnabled", "relayPort", "autoPair", "autoConnect"], (items) => {
93
103
  resolve(items);
94
104
  });
95
105
  });
96
106
  const autoPair = typeof data.autoPair === "boolean" ? data.autoPair : DEFAULT_AUTO_PAIR;
107
+ const autoConnect = typeof data.autoConnect === "boolean" ? data.autoConnect : DEFAULT_AUTO_CONNECT;
97
108
  const pairingEnabled = typeof data.pairingEnabled === "boolean"
98
109
  ? data.pairingEnabled
99
110
  : DEFAULT_PAIRING_ENABLED;
100
111
  const rawToken = typeof data.pairingToken === "string" ? data.pairingToken.trim() : "";
101
112
  const tokenValue = pairingEnabled ? (rawToken || DEFAULT_PAIRING_TOKEN || "") : rawToken;
102
113
  const portValue = parsePort(data.relayPort) ?? DEFAULT_RELAY_PORT;
114
+ autoConnectInput.checked = autoConnect;
103
115
  autoPairInput.checked = autoPair;
104
116
  pairingEnabledInput.checked = pairingEnabled;
105
117
  pairingTokenInput.disabled = !pairingEnabled || autoPair;
106
118
  pairingTokenInput.value = tokenValue;
107
119
  relayPortInput.value = Number.isInteger(portValue) ? String(portValue) : String(DEFAULT_RELAY_PORT);
120
+ setNote();
108
121
  const updates = {};
122
+ if (typeof data.autoConnect !== "boolean") {
123
+ updates.autoConnect = autoConnect;
124
+ }
109
125
  if (typeof data.autoPair !== "boolean") {
110
126
  updates.autoPair = autoPair;
111
127
  }
@@ -118,6 +134,7 @@ const loadSettings = async () => {
118
134
  };
119
135
  const toggle = async () => {
120
136
  const isConnected = statusEl.textContent === "Connected";
137
+ setNote();
121
138
  if (!isConnected && autoPairInput.checked && pairingEnabledInput.checked) {
122
139
  const config = await fetchRelayConfig(DEFAULT_DISCOVERY_PORT);
123
140
  const relayPort = config?.relayPort ?? parsePort(relayPortInput.value) ?? DEFAULT_RELAY_PORT;
@@ -132,7 +149,8 @@ const toggle = async () => {
132
149
  chrome.storage.local.set({ pairingToken: fetchedToken });
133
150
  }
134
151
  else {
135
- statusEl.textContent = "Failed to fetch token from plugin";
152
+ setStatus("disconnected");
153
+ setNote("Auto-pair failed. Start the plugin and retry.");
136
154
  setTimeout(() => refreshStatus(), 2000);
137
155
  return;
138
156
  }
@@ -142,6 +160,7 @@ const toggle = async () => {
142
160
  type: isConnected ? "disconnect" : "connect"
143
161
  });
144
162
  setStatus(response.status);
163
+ setNote();
145
164
  };
146
165
  toggleButton.addEventListener("click", () => {
147
166
  toggle().catch(() => {
@@ -156,7 +175,17 @@ autoPairInput.addEventListener("change", () => {
156
175
  pairingTokenInput.placeholder = "Will be fetched automatically";
157
176
  }
158
177
  else {
159
- pairingTokenInput.placeholder = "Enter token or enable Auto-Pair";
178
+ pairingTokenInput.placeholder = "Enter token or enable auto-pair";
179
+ }
180
+ });
181
+ autoConnectInput.addEventListener("change", () => {
182
+ const enabled = autoConnectInput.checked;
183
+ chrome.storage.local.set({ autoConnect: enabled });
184
+ if (enabled && statusEl.textContent !== "Connected") {
185
+ toggle().catch(() => {
186
+ setStatus("disconnected");
187
+ setNote();
188
+ });
160
189
  }
161
190
  });
162
191
  pairingTokenInput.addEventListener("input", () => {
@@ -187,11 +216,14 @@ relayPortInput.addEventListener("input", () => {
187
216
  });
188
217
  refreshStatus().catch(() => {
189
218
  setStatus("disconnected");
219
+ setNote();
190
220
  });
191
221
  loadSettings().catch(() => {
222
+ autoConnectInput.checked = DEFAULT_AUTO_CONNECT;
192
223
  autoPairInput.checked = DEFAULT_AUTO_PAIR;
193
224
  pairingEnabledInput.checked = DEFAULT_PAIRING_ENABLED;
194
225
  pairingTokenInput.disabled = !DEFAULT_PAIRING_ENABLED;
195
226
  pairingTokenInput.value = DEFAULT_PAIRING_TOKEN || "";
196
227
  relayPortInput.value = String(DEFAULT_RELAY_PORT);
228
+ setNote();
197
229
  });
@@ -2,4 +2,5 @@ export const DEFAULT_RELAY_PORT = 8787;
2
2
  export const DEFAULT_DISCOVERY_PORT = 8787;
3
3
  export const DEFAULT_PAIRING_TOKEN = null;
4
4
  export const DEFAULT_PAIRING_ENABLED = true;
5
- export const DEFAULT_AUTO_PAIR = false;
5
+ export const DEFAULT_AUTO_PAIR = true;
6
+ export const DEFAULT_AUTO_CONNECT = true;
Binary file
Binary file
Binary file
Binary file
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "OpenDevBrowser Relay",
4
- "version": "0.0.11",
4
+ "version": "0.0.12",
5
5
  "description": "Optional bridge to reuse existing Chrome tabs with OpenDevBrowser.",
6
6
  "permissions": [
7
7
  "debugger",