opendevbrowser 0.0.10 → 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_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 {
@@ -47,25 +57,71 @@ const fetchTokenFromPlugin = async (port) => {
47
57
  return null;
48
58
  }
49
59
  };
60
+ const parsePort = (value) => {
61
+ if (typeof value === "number" && Number.isInteger(value) && value > 0 && value <= 65535) {
62
+ return value;
63
+ }
64
+ if (typeof value === "string" && value.trim()) {
65
+ const parsed = Number(value);
66
+ if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65535) {
67
+ return parsed;
68
+ }
69
+ }
70
+ return null;
71
+ };
72
+ const fetchRelayConfig = async (port) => {
73
+ try {
74
+ const response = await fetch(`http://127.0.0.1:${port}/config`, {
75
+ method: "GET",
76
+ headers: { "Accept": "application/json" }
77
+ });
78
+ if (!response.ok) {
79
+ return null;
80
+ }
81
+ const data = await response.json();
82
+ const relayPort = parsePort(data.relayPort);
83
+ if (!relayPort) {
84
+ return null;
85
+ }
86
+ const pairingRequired = typeof data.pairingRequired === "boolean" ? data.pairingRequired : true;
87
+ return { relayPort, pairingRequired };
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ };
93
+ const applyRelayPort = (port) => {
94
+ const nextValue = String(port);
95
+ if (relayPortInput.value !== nextValue) {
96
+ relayPortInput.value = nextValue;
97
+ }
98
+ chrome.storage.local.set({ relayPort: port });
99
+ };
50
100
  const loadSettings = async () => {
51
101
  const data = await new Promise((resolve) => {
52
- chrome.storage.local.get(["pairingToken", "pairingEnabled", "relayPort", "autoPair"], (items) => {
102
+ chrome.storage.local.get(["pairingToken", "pairingEnabled", "relayPort", "autoPair", "autoConnect"], (items) => {
53
103
  resolve(items);
54
104
  });
55
105
  });
56
106
  const autoPair = typeof data.autoPair === "boolean" ? data.autoPair : DEFAULT_AUTO_PAIR;
107
+ const autoConnect = typeof data.autoConnect === "boolean" ? data.autoConnect : DEFAULT_AUTO_CONNECT;
57
108
  const pairingEnabled = typeof data.pairingEnabled === "boolean"
58
109
  ? data.pairingEnabled
59
110
  : DEFAULT_PAIRING_ENABLED;
60
111
  const rawToken = typeof data.pairingToken === "string" ? data.pairingToken.trim() : "";
61
112
  const tokenValue = pairingEnabled ? (rawToken || DEFAULT_PAIRING_TOKEN || "") : rawToken;
62
- const portValue = typeof data.relayPort === "number" ? data.relayPort : DEFAULT_RELAY_PORT;
113
+ const portValue = parsePort(data.relayPort) ?? DEFAULT_RELAY_PORT;
114
+ autoConnectInput.checked = autoConnect;
63
115
  autoPairInput.checked = autoPair;
64
116
  pairingEnabledInput.checked = pairingEnabled;
65
117
  pairingTokenInput.disabled = !pairingEnabled || autoPair;
66
118
  pairingTokenInput.value = tokenValue;
67
119
  relayPortInput.value = Number.isInteger(portValue) ? String(portValue) : String(DEFAULT_RELAY_PORT);
120
+ setNote();
68
121
  const updates = {};
122
+ if (typeof data.autoConnect !== "boolean") {
123
+ updates.autoConnect = autoConnect;
124
+ }
69
125
  if (typeof data.autoPair !== "boolean") {
70
126
  updates.autoPair = autoPair;
71
127
  }
@@ -78,23 +134,33 @@ const loadSettings = async () => {
78
134
  };
79
135
  const toggle = async () => {
80
136
  const isConnected = statusEl.textContent === "Connected";
137
+ setNote();
81
138
  if (!isConnected && autoPairInput.checked && pairingEnabledInput.checked) {
82
- const port = parseInt(relayPortInput.value, 10) || DEFAULT_RELAY_PORT;
83
- const fetchedToken = await fetchTokenFromPlugin(port);
84
- if (fetchedToken) {
85
- pairingTokenInput.value = fetchedToken;
86
- chrome.storage.local.set({ pairingToken: fetchedToken });
139
+ const config = await fetchRelayConfig(DEFAULT_DISCOVERY_PORT);
140
+ const relayPort = config?.relayPort ?? parsePort(relayPortInput.value) ?? DEFAULT_RELAY_PORT;
141
+ if (config?.relayPort) {
142
+ applyRelayPort(config.relayPort);
87
143
  }
88
- else {
89
- statusEl.textContent = "Failed to fetch token from plugin";
90
- setTimeout(() => refreshStatus(), 2000);
91
- return;
144
+ const pairingRequired = config?.pairingRequired ?? true;
145
+ if (pairingRequired) {
146
+ const fetchedToken = await fetchTokenFromPlugin(relayPort);
147
+ if (fetchedToken) {
148
+ pairingTokenInput.value = fetchedToken;
149
+ chrome.storage.local.set({ pairingToken: fetchedToken });
150
+ }
151
+ else {
152
+ setStatus("disconnected");
153
+ setNote("Auto-pair failed. Start the plugin and retry.");
154
+ setTimeout(() => refreshStatus(), 2000);
155
+ return;
156
+ }
92
157
  }
93
158
  }
94
159
  const response = await sendMessage({
95
160
  type: isConnected ? "disconnect" : "connect"
96
161
  });
97
162
  setStatus(response.status);
163
+ setNote();
98
164
  };
99
165
  toggleButton.addEventListener("click", () => {
100
166
  toggle().catch(() => {
@@ -109,7 +175,17 @@ autoPairInput.addEventListener("change", () => {
109
175
  pairingTokenInput.placeholder = "Will be fetched automatically";
110
176
  }
111
177
  else {
112
- 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
+ });
113
189
  }
114
190
  });
115
191
  pairingTokenInput.addEventListener("input", () => {
@@ -140,11 +216,14 @@ relayPortInput.addEventListener("input", () => {
140
216
  });
141
217
  refreshStatus().catch(() => {
142
218
  setStatus("disconnected");
219
+ setNote();
143
220
  });
144
221
  loadSettings().catch(() => {
222
+ autoConnectInput.checked = DEFAULT_AUTO_CONNECT;
145
223
  autoPairInput.checked = DEFAULT_AUTO_PAIR;
146
224
  pairingEnabledInput.checked = DEFAULT_PAIRING_ENABLED;
147
225
  pairingTokenInput.disabled = !DEFAULT_PAIRING_ENABLED;
148
226
  pairingTokenInput.value = DEFAULT_PAIRING_TOKEN || "";
149
227
  relayPortInput.value = String(DEFAULT_RELAY_PORT);
228
+ setNote();
150
229
  });
@@ -1,4 +1,6 @@
1
1
  export const DEFAULT_RELAY_PORT = 8787;
2
+ export const DEFAULT_DISCOVERY_PORT = 8787;
2
3
  export const DEFAULT_PAIRING_TOKEN = null;
3
4
  export const DEFAULT_PAIRING_ENABLED = true;
4
- 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.10",
4
+ "version": "0.0.12",
5
5
  "description": "Optional bridge to reuse existing Chrome tabs with OpenDevBrowser.",
6
6
  "permissions": [
7
7
  "debugger",