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.
- package/README.md +224 -25
- package/dist/chunk-WTFSMBVH.js +2815 -0
- package/dist/chunk-WTFSMBVH.js.map +1 -0
- package/dist/cli/index.js +1589 -71
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +164 -2698
- package/dist/index.js.map +1 -1
- package/dist/opendevbrowser.js +164 -2698
- package/dist/opendevbrowser.js.map +1 -1
- package/extension/dist/background.js +121 -0
- package/extension/dist/popup.js +93 -14
- package/extension/dist/relay-settings.js +3 -1
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon32.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +1 -1
- package/extension/popup.html +326 -66
- package/package.json +2 -2
- package/skills/AGENTS.md +1 -0
- package/dist/chunk-R5VUZEUU.js +0 -128
- package/dist/chunk-R5VUZEUU.js.map +0 -1
|
@@ -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);
|
package/extension/dist/popup.js
CHANGED
|
@@ -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
|
-
|
|
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 =
|
|
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
|
|
83
|
-
const
|
|
84
|
-
if (
|
|
85
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
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 =
|
|
5
|
+
export const DEFAULT_AUTO_PAIR = true;
|
|
6
|
+
export const DEFAULT_AUTO_CONNECT = true;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/extension/manifest.json
CHANGED