@duheso/zerocli 0.8.1 → 0.8.3
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/README.md +147 -0
- package/chrome-extension/background.js +912 -0
- package/chrome-extension/content.js +26 -0
- package/chrome-extension/icons/icon128.png +0 -0
- package/chrome-extension/icons/icon16.png +0 -0
- package/chrome-extension/icons/icon48.png +0 -0
- package/chrome-extension/manifest.json +56 -0
- package/chrome-extension/offscreen.html +7 -0
- package/chrome-extension/offscreen.js +28 -0
- package/chrome-extension/popup.css +190 -0
- package/chrome-extension/popup.html +65 -0
- package/chrome-extension/popup.js +76 -0
- package/dist/cli.mjs +81 -100
- package/package.json +3 -1
- package/scripts/setup-chrome-native-host.mjs +228 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// ZeroCLI Browser Extension — Content Script
|
|
2
|
+
// This script is injected into all pages. It listens for messages from the background worker.
|
|
3
|
+
|
|
4
|
+
(function () {
|
|
5
|
+
if (window.__zerocliContentScriptLoaded) return;
|
|
6
|
+
window.__zerocliContentScriptLoaded = true;
|
|
7
|
+
|
|
8
|
+
// Content scripts can receive messages from background via chrome.runtime.onMessage
|
|
9
|
+
// All DOM operations are delegated to the background via scripting.executeScript,
|
|
10
|
+
// which runs functions directly in page context.
|
|
11
|
+
// This content script is a lightweight shim for any additional cross-origin needs.
|
|
12
|
+
|
|
13
|
+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
14
|
+
if (message.type === 'ping') {
|
|
15
|
+
sendResponse({ type: 'pong', url: location.href });
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (message.type === 'get_url') {
|
|
20
|
+
sendResponse({ url: location.href, title: document.title });
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return false;
|
|
25
|
+
});
|
|
26
|
+
})();
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"manifest_version": 3,
|
|
3
|
+
"name": "ZeroCLI Browser Extension",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"description": "Connects ZeroCLI to your Chrome browser for automated browser control, screenshots, and web automation.",
|
|
6
|
+
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5/GiPKFZcQoYRyPVCk+gH6SmIZ9L+LoyWtnHxCTjO97Hq1e80CtBzDk85S6CqScFw/mPFYGSfiZTWkU6YBEQctg1NsCnc4F6CLUhb5CRbrOQse3p1vb1w/64uylRwsbOIOdiEQWMPccz5gGowGtWFfAMskrpdBhsFxb085Sz7e5DIZN9x+QBw2qvbcfgPTV2L7pGSsey5sjXmRYATOvRM1Bt6VHdCx9Ir4Qr/JNK6KOZkyqt17Oag8emCdk44eYnsvh6NBtWx1mDvDx/ZQ0BUb1WFiYvBcI3Zt/pZja0SS/PGi9iVpj5AIWk0FLPJC/iThui44NhPkCIB2spQIg7qQIDAQAB",
|
|
7
|
+
"icons": {
|
|
8
|
+
"16": "icons/icon16.png",
|
|
9
|
+
"48": "icons/icon48.png",
|
|
10
|
+
"128": "icons/icon128.png"
|
|
11
|
+
},
|
|
12
|
+
"action": {
|
|
13
|
+
"default_popup": "popup.html",
|
|
14
|
+
"default_icon": {
|
|
15
|
+
"16": "icons/icon16.png",
|
|
16
|
+
"48": "icons/icon48.png",
|
|
17
|
+
"128": "icons/icon128.png"
|
|
18
|
+
},
|
|
19
|
+
"default_title": "ZeroCLI Browser Extension"
|
|
20
|
+
},
|
|
21
|
+
"background": {
|
|
22
|
+
"service_worker": "background.js",
|
|
23
|
+
"type": "module"
|
|
24
|
+
},
|
|
25
|
+
"content_scripts": [
|
|
26
|
+
{
|
|
27
|
+
"matches": ["<all_urls>"],
|
|
28
|
+
"js": ["content.js"],
|
|
29
|
+
"run_at": "document_idle",
|
|
30
|
+
"all_frames": false
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"permissions": [
|
|
34
|
+
"tabs",
|
|
35
|
+
"activeTab",
|
|
36
|
+
"scripting",
|
|
37
|
+
"debugger",
|
|
38
|
+
"nativeMessaging",
|
|
39
|
+
"storage",
|
|
40
|
+
"tabCapture",
|
|
41
|
+
"windows",
|
|
42
|
+
"notifications"
|
|
43
|
+
],
|
|
44
|
+
"host_permissions": [
|
|
45
|
+
"<all_urls>"
|
|
46
|
+
],
|
|
47
|
+
"content_security_policy": {
|
|
48
|
+
"extension_pages": "script-src 'self'; object-src 'self'"
|
|
49
|
+
},
|
|
50
|
+
"web_accessible_resources": [
|
|
51
|
+
{
|
|
52
|
+
"resources": ["offscreen.html"],
|
|
53
|
+
"matches": ["<all_urls>"]
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// ZeroCLI Browser Extension — Offscreen Document
|
|
2
|
+
// Used for GIF creation and other off-screen processing tasks.
|
|
3
|
+
// This document runs in an offscreen context and can use canvas/media APIs.
|
|
4
|
+
|
|
5
|
+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
6
|
+
if (message.type === 'create_gif') {
|
|
7
|
+
handleCreateGif(message.frames, message.options)
|
|
8
|
+
.then((result) => sendResponse({ success: true, ...result }))
|
|
9
|
+
.catch((err) => sendResponse({ success: false, error: err.message }));
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
async function handleCreateGif(frames, options = {}) {
|
|
15
|
+
const { width = 800, height = 600, fps = 5 } = options;
|
|
16
|
+
|
|
17
|
+
// Note: Full GIF encoding requires a GIF encoder library (e.g. gif.js).
|
|
18
|
+
// This implementation captures frames and returns metadata.
|
|
19
|
+
// For production GIF generation, integrate gif.js or ffmpeg.wasm.
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
frameCount: frames.length,
|
|
23
|
+
width,
|
|
24
|
+
height,
|
|
25
|
+
fps,
|
|
26
|
+
message: `Processed ${frames.length} frames at ${fps}fps`,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
9
|
+
background: #0f0f0f;
|
|
10
|
+
color: #e5e5e5;
|
|
11
|
+
width: 280px;
|
|
12
|
+
min-height: 200px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.container {
|
|
16
|
+
padding: 16px;
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
gap: 16px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
header {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
gap: 8px;
|
|
26
|
+
padding-bottom: 12px;
|
|
27
|
+
border-bottom: 1px solid #222;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.logo {
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
gap: 10px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.logo-icon {
|
|
37
|
+
font-size: 24px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.logo-text h1 {
|
|
41
|
+
font-size: 16px;
|
|
42
|
+
font-weight: 700;
|
|
43
|
+
color: #fff;
|
|
44
|
+
line-height: 1.2;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.logo-text .subtitle {
|
|
48
|
+
font-size: 11px;
|
|
49
|
+
color: #666;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.status-card {
|
|
53
|
+
background: #161616;
|
|
54
|
+
border: 1px solid #222;
|
|
55
|
+
border-radius: 8px;
|
|
56
|
+
padding: 12px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
.status-indicator {
|
|
60
|
+
display: flex;
|
|
61
|
+
align-items: center;
|
|
62
|
+
gap: 8px;
|
|
63
|
+
margin-bottom: 6px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.status-dot {
|
|
67
|
+
width: 8px;
|
|
68
|
+
height: 8px;
|
|
69
|
+
border-radius: 50%;
|
|
70
|
+
background: #444;
|
|
71
|
+
flex-shrink: 0;
|
|
72
|
+
transition: background 0.3s ease;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.status-dot.connected {
|
|
76
|
+
background: #22c55e;
|
|
77
|
+
box-shadow: 0 0 6px #22c55e66;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.status-dot.disconnected {
|
|
81
|
+
background: #ef4444;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.status-dot.checking {
|
|
85
|
+
background: #f59e0b;
|
|
86
|
+
animation: pulse 1.5s infinite;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
@keyframes pulse {
|
|
90
|
+
0%, 100% { opacity: 1; }
|
|
91
|
+
50% { opacity: 0.4; }
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.status-label {
|
|
95
|
+
font-size: 14px;
|
|
96
|
+
font-weight: 600;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.status-label.connected { color: #22c55e; }
|
|
100
|
+
.status-label.disconnected { color: #ef4444; }
|
|
101
|
+
.status-label.checking { color: #f59e0b; }
|
|
102
|
+
|
|
103
|
+
.status-description {
|
|
104
|
+
font-size: 12px;
|
|
105
|
+
color: #666;
|
|
106
|
+
line-height: 1.4;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
.info-section {
|
|
110
|
+
background: #161616;
|
|
111
|
+
border: 1px solid #222;
|
|
112
|
+
border-radius: 8px;
|
|
113
|
+
padding: 10px 12px;
|
|
114
|
+
display: flex;
|
|
115
|
+
flex-direction: column;
|
|
116
|
+
gap: 6px;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.info-row {
|
|
120
|
+
display: flex;
|
|
121
|
+
justify-content: space-between;
|
|
122
|
+
align-items: center;
|
|
123
|
+
font-size: 12px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.info-label {
|
|
127
|
+
color: #666;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.info-value {
|
|
131
|
+
color: #aaa;
|
|
132
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
133
|
+
font-size: 11px;
|
|
134
|
+
max-width: 140px;
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
text-overflow: ellipsis;
|
|
137
|
+
white-space: nowrap;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.instructions {
|
|
141
|
+
background: #161616;
|
|
142
|
+
border: 1px solid #333;
|
|
143
|
+
border-radius: 8px;
|
|
144
|
+
padding: 12px;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.instructions h3 {
|
|
148
|
+
font-size: 13px;
|
|
149
|
+
color: #f59e0b;
|
|
150
|
+
margin-bottom: 8px;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.instructions ol {
|
|
154
|
+
padding-left: 16px;
|
|
155
|
+
display: flex;
|
|
156
|
+
flex-direction: column;
|
|
157
|
+
gap: 4px;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.instructions li {
|
|
161
|
+
font-size: 12px;
|
|
162
|
+
color: #aaa;
|
|
163
|
+
line-height: 1.5;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.instructions code {
|
|
167
|
+
background: #222;
|
|
168
|
+
color: #e5e5e5;
|
|
169
|
+
padding: 1px 4px;
|
|
170
|
+
border-radius: 3px;
|
|
171
|
+
font-family: 'SF Mono', 'Fira Code', monospace;
|
|
172
|
+
font-size: 11px;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
footer {
|
|
176
|
+
border-top: 1px solid #222;
|
|
177
|
+
padding-top: 10px;
|
|
178
|
+
text-align: center;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.footer-link {
|
|
182
|
+
font-size: 11px;
|
|
183
|
+
color: #444;
|
|
184
|
+
text-decoration: none;
|
|
185
|
+
transition: color 0.2s;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.footer-link:hover {
|
|
189
|
+
color: #888;
|
|
190
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>ZeroCLI Browser Extension</title>
|
|
7
|
+
<link rel="stylesheet" href="popup.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<header>
|
|
12
|
+
<div class="logo">
|
|
13
|
+
<span class="logo-icon">⚡</span>
|
|
14
|
+
<div class="logo-text">
|
|
15
|
+
<h1>ZeroCLI</h1>
|
|
16
|
+
<span class="subtitle">Browser Extension</span>
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
</header>
|
|
20
|
+
|
|
21
|
+
<main>
|
|
22
|
+
<div class="status-card" id="statusCard">
|
|
23
|
+
<div class="status-indicator">
|
|
24
|
+
<span class="status-dot" id="statusDot"></span>
|
|
25
|
+
<span class="status-label" id="statusLabel">Checking...</span>
|
|
26
|
+
</div>
|
|
27
|
+
<p class="status-description" id="statusDescription"></p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
<div class="info-section" id="infoSection" style="display:none">
|
|
31
|
+
<div class="info-row">
|
|
32
|
+
<span class="info-label">Native Host</span>
|
|
33
|
+
<span class="info-value" id="nativeHostStatus">—</span>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="info-row">
|
|
36
|
+
<span class="info-label">Extension ID</span>
|
|
37
|
+
<span class="info-value" id="extensionId">—</span>
|
|
38
|
+
</div>
|
|
39
|
+
<div class="info-row">
|
|
40
|
+
<span class="info-label">Version</span>
|
|
41
|
+
<span class="info-value" id="extensionVersion">—</span>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="instructions" id="setupInstructions" style="display:none">
|
|
46
|
+
<h3>Setup Required</h3>
|
|
47
|
+
<ol>
|
|
48
|
+
<li>Ensure ZeroCLI is installed and running</li>
|
|
49
|
+
<li>Run: <code>zero --chrome</code></li>
|
|
50
|
+
<li>The native host will be auto-installed</li>
|
|
51
|
+
<li>Reload this extension if needed</li>
|
|
52
|
+
</ol>
|
|
53
|
+
</div>
|
|
54
|
+
</main>
|
|
55
|
+
|
|
56
|
+
<footer>
|
|
57
|
+
<a href="https://github.com/Duheso/ZeroCLI" target="_blank" class="footer-link">
|
|
58
|
+
ZeroCLI on GitHub
|
|
59
|
+
</a>
|
|
60
|
+
</footer>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<script src="popup.js"></script>
|
|
64
|
+
</body>
|
|
65
|
+
</html>
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// ZeroCLI Browser Extension — Popup Script
|
|
2
|
+
|
|
3
|
+
const NATIVE_HOST_NAME = 'com.duheso.zerocli_browser_extension';
|
|
4
|
+
|
|
5
|
+
document.addEventListener('DOMContentLoaded', async () => {
|
|
6
|
+
const statusDot = document.getElementById('statusDot');
|
|
7
|
+
const statusLabel = document.getElementById('statusLabel');
|
|
8
|
+
const statusDescription = document.getElementById('statusDescription');
|
|
9
|
+
const infoSection = document.getElementById('infoSection');
|
|
10
|
+
const setupInstructions = document.getElementById('setupInstructions');
|
|
11
|
+
const nativeHostStatus = document.getElementById('nativeHostStatus');
|
|
12
|
+
const extensionIdEl = document.getElementById('extensionId');
|
|
13
|
+
const extensionVersionEl = document.getElementById('extensionVersion');
|
|
14
|
+
|
|
15
|
+
// Show extension info
|
|
16
|
+
const manifest = chrome.runtime.getManifest();
|
|
17
|
+
const extId = chrome.runtime.id;
|
|
18
|
+
|
|
19
|
+
extensionIdEl.textContent = extId.slice(0, 20) + '...';
|
|
20
|
+
extensionIdEl.title = extId;
|
|
21
|
+
extensionVersionEl.textContent = manifest.version;
|
|
22
|
+
|
|
23
|
+
// Check connection status by probing native host
|
|
24
|
+
setStatus('checking', 'Checking...', 'Testing connection to ZeroCLI...');
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const port = chrome.runtime.connectNative(NATIVE_HOST_NAME);
|
|
28
|
+
|
|
29
|
+
let responded = false;
|
|
30
|
+
const timeout = setTimeout(() => {
|
|
31
|
+
if (!responded) {
|
|
32
|
+
port.disconnect();
|
|
33
|
+
setStatus('disconnected', 'Disconnected', 'ZeroCLI native host not responding. Run: zero --chrome');
|
|
34
|
+
nativeHostStatus.textContent = 'Not running';
|
|
35
|
+
setupInstructions.style.display = 'block';
|
|
36
|
+
}
|
|
37
|
+
}, 2000);
|
|
38
|
+
|
|
39
|
+
port.onMessage.addListener((msg) => {
|
|
40
|
+
if (msg.type === 'pong' && !responded) {
|
|
41
|
+
responded = true;
|
|
42
|
+
clearTimeout(timeout);
|
|
43
|
+
port.disconnect();
|
|
44
|
+
setStatus('connected', 'Connected', 'ZeroCLI is connected and ready for browser automation.');
|
|
45
|
+
nativeHostStatus.textContent = 'Running';
|
|
46
|
+
infoSection.style.display = 'flex';
|
|
47
|
+
setupInstructions.style.display = 'none';
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
port.onDisconnect.addListener(() => {
|
|
52
|
+
if (!responded) {
|
|
53
|
+
clearTimeout(timeout);
|
|
54
|
+
const err = chrome.runtime.lastError?.message || 'Native host not found';
|
|
55
|
+
setStatus('disconnected', 'Disconnected', `Native host error: ${err}`);
|
|
56
|
+
nativeHostStatus.textContent = 'Not found';
|
|
57
|
+
infoSection.style.display = 'flex';
|
|
58
|
+
setupInstructions.style.display = 'block';
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
port.postMessage({ type: 'ping' });
|
|
63
|
+
} catch (err) {
|
|
64
|
+
setStatus('disconnected', 'Error', `Could not connect: ${err.message}`);
|
|
65
|
+
nativeHostStatus.textContent = 'Error';
|
|
66
|
+
infoSection.style.display = 'flex';
|
|
67
|
+
setupInstructions.style.display = 'block';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function setStatus(state, label, description) {
|
|
71
|
+
statusDot.className = `status-dot ${state}`;
|
|
72
|
+
statusLabel.className = `status-label ${state}`;
|
|
73
|
+
statusLabel.textContent = label;
|
|
74
|
+
statusDescription.textContent = description;
|
|
75
|
+
}
|
|
76
|
+
});
|