@duheso/zerocli 0.8.7 → 0.8.9
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/chrome-extension/background.js +64 -19
- package/dist/cli.mjs +21439 -25249
- package/package.json +1 -1
|
@@ -6,6 +6,8 @@ const VERSION = '1.0.0';
|
|
|
6
6
|
|
|
7
7
|
let nativePort = null;
|
|
8
8
|
let isConnected = false;
|
|
9
|
+
let isConnecting = false; // guard against concurrent connect attempts
|
|
10
|
+
let reconnectTimer = null; // handle for pending reconnect setTimeout
|
|
9
11
|
let pendingRequests = new Map(); // requestId -> { resolve, reject, timeoutId }
|
|
10
12
|
let requestCounter = 0;
|
|
11
13
|
let debuggerAttachedTabs = new Set();
|
|
@@ -34,21 +36,36 @@ function dbg(msg, ...args) {
|
|
|
34
36
|
console.log(`[ZeroCLI ${ts}] ${msg}`, ...args);
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
function scheduleReconnect(delayMs) {
|
|
40
|
+
// Cancel any pending reconnect to avoid stacking timers
|
|
41
|
+
if (reconnectTimer !== null) {
|
|
42
|
+
clearTimeout(reconnectTimer);
|
|
43
|
+
reconnectTimer = null;
|
|
44
|
+
}
|
|
45
|
+
reconnectTimer = setTimeout(() => {
|
|
46
|
+
reconnectTimer = null;
|
|
47
|
+
connectNativeHost();
|
|
48
|
+
}, delayMs);
|
|
49
|
+
}
|
|
50
|
+
|
|
37
51
|
function connectNativeHost() {
|
|
52
|
+
// Guard: don't start a new connection if one is already in progress or live
|
|
53
|
+
if (isConnecting || isConnected) {
|
|
54
|
+
dbg(`connectNativeHost skipped (isConnecting=${isConnecting}, isConnected=${isConnected})`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
isConnecting = true;
|
|
38
58
|
debugStats.connectAttempts++;
|
|
39
59
|
dbg(`Connecting to native host (attempt #${debugStats.connectAttempts})...`);
|
|
40
60
|
try {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
debugStats.connectSuccesses++;
|
|
44
|
-
updateIcon(true);
|
|
45
|
-
dbg('Native host connected successfully.');
|
|
61
|
+
const port = chrome.runtime.connectNative(NATIVE_HOST_NAME);
|
|
62
|
+
nativePort = port;
|
|
46
63
|
|
|
47
|
-
|
|
64
|
+
port.onMessage.addListener((message) => {
|
|
48
65
|
handleNativeMessage(message);
|
|
49
66
|
});
|
|
50
67
|
|
|
51
|
-
|
|
68
|
+
port.onDisconnect.addListener(() => {
|
|
52
69
|
// Read lastError to mark it as "checked" and suppress DevTools warning
|
|
53
70
|
const err = chrome.runtime.lastError;
|
|
54
71
|
const errMsg = err?.message ?? 'unknown reason';
|
|
@@ -57,39 +74,56 @@ function connectNativeHost() {
|
|
|
57
74
|
err.message?.includes('Cannot find native messaging host') ||
|
|
58
75
|
err.message?.includes('Specified native messaging host not found')
|
|
59
76
|
);
|
|
77
|
+
|
|
78
|
+
// Only process disconnect for the port we currently own
|
|
79
|
+
if (nativePort !== port) {
|
|
80
|
+
dbg(`Stale port disconnect ignored: ${errMsg}`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
60
84
|
debugStats.disconnects++;
|
|
61
85
|
debugStats.lastError = errMsg;
|
|
62
86
|
debugStats.lastErrorTime = Date.now();
|
|
63
87
|
isConnected = false;
|
|
88
|
+
isConnecting = false;
|
|
64
89
|
nativePort = null;
|
|
65
90
|
updateIcon(false);
|
|
66
91
|
dbg(`Native host disconnected (total disconnects: ${debugStats.disconnects}): ${errMsg}`);
|
|
92
|
+
|
|
67
93
|
// Reject all pending requests
|
|
68
|
-
for (const [
|
|
94
|
+
for (const [, pending] of pendingRequests) {
|
|
69
95
|
clearTimeout(pending.timeoutId);
|
|
70
96
|
pending.reject(new Error(err?.message ?? 'Native host disconnected'));
|
|
71
97
|
}
|
|
72
98
|
pendingRequests.clear();
|
|
99
|
+
|
|
73
100
|
if (hostNotFound) {
|
|
74
|
-
// Native host not installed yet — retry slowly (every 30s)
|
|
75
|
-
// User needs to run: zero --chrome (to register the native host)
|
|
76
101
|
console.warn('[ZeroCLI] Native messaging host not found. Run "zero --chrome" once to register it, then reload this extension.');
|
|
77
|
-
|
|
102
|
+
scheduleReconnect(30000);
|
|
78
103
|
} else {
|
|
79
|
-
|
|
80
|
-
setTimeout(connectNativeHost, 3000);
|
|
104
|
+
scheduleReconnect(3000);
|
|
81
105
|
}
|
|
82
106
|
});
|
|
83
107
|
|
|
108
|
+
// Mark connected only after port is set up (Chrome fires onDisconnect
|
|
109
|
+
// synchronously if the host binary isn't found, so this must come after)
|
|
110
|
+
isConnected = true;
|
|
111
|
+
isConnecting = false;
|
|
112
|
+
debugStats.connectSuccesses++;
|
|
113
|
+
updateIcon(true);
|
|
114
|
+
dbg('Native host connected successfully.');
|
|
115
|
+
|
|
84
116
|
// Send initial ping
|
|
85
117
|
sendToNative({ type: 'ping' });
|
|
86
118
|
} catch (err) {
|
|
87
119
|
isConnected = false;
|
|
120
|
+
isConnecting = false;
|
|
121
|
+
nativePort = null;
|
|
88
122
|
updateIcon(false);
|
|
89
123
|
debugStats.lastError = err?.message ?? String(err);
|
|
90
124
|
debugStats.lastErrorTime = Date.now();
|
|
91
125
|
console.warn('[ZeroCLI] connectNativeHost error:', err?.message);
|
|
92
|
-
|
|
126
|
+
scheduleReconnect(30000);
|
|
93
127
|
}
|
|
94
128
|
}
|
|
95
129
|
|
|
@@ -525,6 +559,11 @@ async function toolDebugInfo(_params) {
|
|
|
525
559
|
toolsDispatched: debugStats.toolsDispatched,
|
|
526
560
|
toolErrors: debugStats.toolErrors,
|
|
527
561
|
},
|
|
562
|
+
flags: {
|
|
563
|
+
isConnected,
|
|
564
|
+
isConnecting,
|
|
565
|
+
hasPendingReconnectTimer: reconnectTimer !== null,
|
|
566
|
+
},
|
|
528
567
|
lastError: debugStats.lastError,
|
|
529
568
|
lastErrorAgo: debugStats.lastErrorTime
|
|
530
569
|
? `${Math.round((Date.now() - debugStats.lastErrorTime) / 1000)}s ago`
|
|
@@ -563,10 +602,14 @@ async function waitForTabLoad(tabId, timeoutMs = 10000) {
|
|
|
563
602
|
}
|
|
564
603
|
|
|
565
604
|
async function executeInTab(tabId, func, args) {
|
|
605
|
+
// chrome.scripting.executeScript requires all args to be structured-clone
|
|
606
|
+
// serializable. undefined is NOT serializable, so replace with null.
|
|
607
|
+
// The injected functions already treat null as "not provided" (falsy checks).
|
|
608
|
+
const safeArgs = args.map(v => v === undefined ? null : v);
|
|
566
609
|
const results = await chrome.scripting.executeScript({
|
|
567
610
|
target: { tabId },
|
|
568
611
|
func,
|
|
569
|
-
args,
|
|
612
|
+
args: safeArgs,
|
|
570
613
|
});
|
|
571
614
|
return results[0]?.result;
|
|
572
615
|
}
|
|
@@ -977,6 +1020,7 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
|
977
1020
|
if (message?.type === 'get_status') {
|
|
978
1021
|
sendResponse({
|
|
979
1022
|
isConnected,
|
|
1023
|
+
isConnecting,
|
|
980
1024
|
nativePortAlive: nativePort !== null,
|
|
981
1025
|
version: VERSION,
|
|
982
1026
|
stats: { ...debugStats },
|
|
@@ -997,12 +1041,13 @@ chrome.alarms.create('zerocli-keepalive', { periodInMinutes: 0.33 }); // ~20 sec
|
|
|
997
1041
|
|
|
998
1042
|
chrome.alarms.onAlarm.addListener((alarm) => {
|
|
999
1043
|
if (alarm.name === 'zerocli-keepalive') {
|
|
1000
|
-
if (
|
|
1001
|
-
dbg('Keep-alive alarm: not connected, attempting reconnect...');
|
|
1002
|
-
connectNativeHost();
|
|
1003
|
-
} else {
|
|
1044
|
+
if (isConnected) {
|
|
1004
1045
|
// Send a lightweight ping to verify the connection is still healthy
|
|
1005
1046
|
sendToNative({ type: 'ping' });
|
|
1047
|
+
} else if (!isConnecting && reconnectTimer === null) {
|
|
1048
|
+
// Only reconnect if nothing else has already scheduled one
|
|
1049
|
+
dbg('Keep-alive alarm: not connected, scheduling reconnect...');
|
|
1050
|
+
scheduleReconnect(0);
|
|
1006
1051
|
}
|
|
1007
1052
|
}
|
|
1008
1053
|
});
|