glidercli 0.1.4 → 0.2.0
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 +12 -7
- package/bin/glider.js +243 -3
- package/lib/bcdp.js +482 -0
- package/lib/beval.js +78 -0
- package/lib/bserve.js +311 -0
- package/package.json +8 -2
- package/.git-personal-enforced +0 -4
- package/.github/hooks/post-checkout +0 -24
- package/.github/hooks/pre-commit +0 -30
- package/.github/hooks/pre-push +0 -13
- package/.github/scripts/health-check.sh +0 -127
- package/.github/scripts/setup.sh +0 -19
- package/assets/icons/.gitkeep +0 -0
- package/repo.config.json +0 -31
package/lib/bserve.js
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* browser-relay-server.js
|
|
4
|
+
* Minimal CDP relay server - connects to Chrome extension for browser automation
|
|
5
|
+
* Based on playwriter architecture but stripped down for direct scripting
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { WebSocketServer, WebSocket } = require('ws');
|
|
9
|
+
const http = require('http');
|
|
10
|
+
|
|
11
|
+
const PORT = process.env.RELAY_PORT || 19988;
|
|
12
|
+
const HOST = '127.0.0.1';
|
|
13
|
+
|
|
14
|
+
// State
|
|
15
|
+
let extensionWs = null;
|
|
16
|
+
const playwrightClients = new Map();
|
|
17
|
+
const connectedTargets = new Map();
|
|
18
|
+
const pendingRequests = new Map();
|
|
19
|
+
let messageId = 0;
|
|
20
|
+
|
|
21
|
+
// Create HTTP server
|
|
22
|
+
const server = http.createServer((req, res) => {
|
|
23
|
+
if (req.url === '/') {
|
|
24
|
+
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
25
|
+
res.end('OK');
|
|
26
|
+
} else if (req.url === '/status') {
|
|
27
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
28
|
+
res.end(JSON.stringify({
|
|
29
|
+
extension: extensionWs !== null,
|
|
30
|
+
targets: connectedTargets.size,
|
|
31
|
+
clients: playwrightClients.size
|
|
32
|
+
}));
|
|
33
|
+
} else if (req.url === '/targets') {
|
|
34
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
35
|
+
res.end(JSON.stringify(Array.from(connectedTargets.values())));
|
|
36
|
+
} else if (req.url === '/cdp' && req.method === 'POST') {
|
|
37
|
+
// HTTP POST endpoint for CDP commands
|
|
38
|
+
let body = '';
|
|
39
|
+
req.on('data', chunk => body += chunk);
|
|
40
|
+
req.on('end', async () => {
|
|
41
|
+
try {
|
|
42
|
+
const { method, params, sessionId } = JSON.parse(body);
|
|
43
|
+
const result = await routeCDPCommand({ method, params, sessionId });
|
|
44
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
45
|
+
res.end(JSON.stringify(result));
|
|
46
|
+
} catch (e) {
|
|
47
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
48
|
+
res.end(JSON.stringify({ error: e.message }));
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
} else {
|
|
52
|
+
res.writeHead(404);
|
|
53
|
+
res.end('Not Found');
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// WebSocket server
|
|
58
|
+
const wss = new WebSocketServer({ server });
|
|
59
|
+
|
|
60
|
+
wss.on('connection', (ws, req) => {
|
|
61
|
+
const path = req.url;
|
|
62
|
+
|
|
63
|
+
if (path === '/extension') {
|
|
64
|
+
handleExtensionConnection(ws);
|
|
65
|
+
} else if (path.startsWith('/cdp')) {
|
|
66
|
+
const clientId = path.split('/')[2] || 'default';
|
|
67
|
+
handleCDPConnection(ws, clientId);
|
|
68
|
+
} else {
|
|
69
|
+
ws.close(1000, 'Unknown path');
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
function handleExtensionConnection(ws) {
|
|
74
|
+
if (extensionWs) {
|
|
75
|
+
console.log('[relay] Replacing existing extension connection');
|
|
76
|
+
extensionWs.close(4001, 'Replaced');
|
|
77
|
+
connectedTargets.clear();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
extensionWs = ws;
|
|
81
|
+
console.log('[relay] Extension connected');
|
|
82
|
+
|
|
83
|
+
// Ping to keep alive
|
|
84
|
+
const pingInterval = setInterval(() => {
|
|
85
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
86
|
+
ws.send(JSON.stringify({ method: 'ping' }));
|
|
87
|
+
}
|
|
88
|
+
}, 5000);
|
|
89
|
+
|
|
90
|
+
ws.on('message', (data) => {
|
|
91
|
+
try {
|
|
92
|
+
const msg = JSON.parse(data.toString());
|
|
93
|
+
handleExtensionMessage(msg);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
console.error('[relay] Error parsing extension message:', e);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
ws.on('close', () => {
|
|
100
|
+
console.log('[relay] Extension disconnected');
|
|
101
|
+
clearInterval(pingInterval);
|
|
102
|
+
extensionWs = null;
|
|
103
|
+
connectedTargets.clear();
|
|
104
|
+
|
|
105
|
+
// Notify all clients
|
|
106
|
+
for (const client of playwrightClients.values()) {
|
|
107
|
+
client.ws.close(1000, 'Extension disconnected');
|
|
108
|
+
}
|
|
109
|
+
playwrightClients.clear();
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function handleExtensionMessage(msg) {
|
|
114
|
+
// Response to our request
|
|
115
|
+
if (msg.id !== undefined) {
|
|
116
|
+
const pending = pendingRequests.get(msg.id);
|
|
117
|
+
if (pending) {
|
|
118
|
+
pendingRequests.delete(msg.id);
|
|
119
|
+
if (msg.error) {
|
|
120
|
+
pending.reject(new Error(msg.error));
|
|
121
|
+
} else {
|
|
122
|
+
pending.resolve(msg.result);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Pong
|
|
129
|
+
if (msg.method === 'pong') return;
|
|
130
|
+
|
|
131
|
+
// Log from extension
|
|
132
|
+
if (msg.method === 'log') {
|
|
133
|
+
console.log(`[ext:${msg.params.level}]`, ...msg.params.args);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// CDP event from extension
|
|
138
|
+
if (msg.method === 'forwardCDPEvent') {
|
|
139
|
+
const { method, params, sessionId } = msg.params;
|
|
140
|
+
|
|
141
|
+
// Track targets
|
|
142
|
+
if (method === 'Target.attachedToTarget') {
|
|
143
|
+
connectedTargets.set(params.sessionId, {
|
|
144
|
+
sessionId: params.sessionId,
|
|
145
|
+
targetId: params.targetInfo.targetId,
|
|
146
|
+
targetInfo: params.targetInfo
|
|
147
|
+
});
|
|
148
|
+
console.log(`[relay] Target attached: ${params.targetInfo.url}`);
|
|
149
|
+
} else if (method === 'Target.detachedFromTarget') {
|
|
150
|
+
connectedTargets.delete(params.sessionId);
|
|
151
|
+
console.log(`[relay] Target detached: ${params.sessionId}`);
|
|
152
|
+
} else if (method === 'Target.targetInfoChanged') {
|
|
153
|
+
const target = Array.from(connectedTargets.values())
|
|
154
|
+
.find(t => t.targetId === params.targetInfo.targetId);
|
|
155
|
+
if (target) {
|
|
156
|
+
target.targetInfo = params.targetInfo;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Forward to all CDP clients
|
|
161
|
+
const cdpEvent = { method, params, sessionId };
|
|
162
|
+
for (const client of playwrightClients.values()) {
|
|
163
|
+
client.ws.send(JSON.stringify(cdpEvent));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function handleCDPConnection(ws, clientId) {
|
|
169
|
+
if (playwrightClients.has(clientId)) {
|
|
170
|
+
ws.close(1000, 'Client ID already connected');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
playwrightClients.set(clientId, { id: clientId, ws });
|
|
175
|
+
console.log(`[relay] CDP client connected: ${clientId}`);
|
|
176
|
+
|
|
177
|
+
ws.on('message', async (data) => {
|
|
178
|
+
try {
|
|
179
|
+
const msg = JSON.parse(data.toString());
|
|
180
|
+
const { id, method, params, sessionId } = msg;
|
|
181
|
+
|
|
182
|
+
if (!extensionWs) {
|
|
183
|
+
ws.send(JSON.stringify({ id, error: { message: 'Extension not connected' } }));
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
const result = await routeCDPCommand({ method, params, sessionId });
|
|
189
|
+
ws.send(JSON.stringify({ id, sessionId, result }));
|
|
190
|
+
|
|
191
|
+
// Send attachedToTarget events after setAutoAttach
|
|
192
|
+
if (method === 'Target.setAutoAttach' && !sessionId) {
|
|
193
|
+
for (const target of connectedTargets.values()) {
|
|
194
|
+
ws.send(JSON.stringify({
|
|
195
|
+
method: 'Target.attachedToTarget',
|
|
196
|
+
params: {
|
|
197
|
+
sessionId: target.sessionId,
|
|
198
|
+
targetInfo: { ...target.targetInfo, attached: true },
|
|
199
|
+
waitingForDebugger: false
|
|
200
|
+
}
|
|
201
|
+
}));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
} catch (e) {
|
|
205
|
+
ws.send(JSON.stringify({ id, sessionId, error: { message: e.message } }));
|
|
206
|
+
}
|
|
207
|
+
} catch (e) {
|
|
208
|
+
console.error('[relay] Error handling CDP message:', e);
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
ws.on('close', () => {
|
|
213
|
+
playwrightClients.delete(clientId);
|
|
214
|
+
console.log(`[relay] CDP client disconnected: ${clientId}`);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function sendToExtension({ method, params, timeout = 30000 }) {
|
|
219
|
+
if (!extensionWs) throw new Error('Extension not connected');
|
|
220
|
+
|
|
221
|
+
const id = ++messageId;
|
|
222
|
+
extensionWs.send(JSON.stringify({ id, method, params }));
|
|
223
|
+
|
|
224
|
+
return new Promise((resolve, reject) => {
|
|
225
|
+
const timer = setTimeout(() => {
|
|
226
|
+
pendingRequests.delete(id);
|
|
227
|
+
reject(new Error(`Timeout: ${method}`));
|
|
228
|
+
}, timeout);
|
|
229
|
+
|
|
230
|
+
pendingRequests.set(id, {
|
|
231
|
+
resolve: (result) => { clearTimeout(timer); resolve(result); },
|
|
232
|
+
reject: (error) => { clearTimeout(timer); reject(error); }
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function routeCDPCommand({ method, params, sessionId }) {
|
|
238
|
+
// Handle some commands locally
|
|
239
|
+
switch (method) {
|
|
240
|
+
case 'Browser.getVersion':
|
|
241
|
+
return {
|
|
242
|
+
protocolVersion: '1.3',
|
|
243
|
+
product: 'Chrome/Extension-Bridge',
|
|
244
|
+
revision: '1.0.0',
|
|
245
|
+
userAgent: 'CDP-Bridge/1.0.0',
|
|
246
|
+
jsVersion: 'V8'
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
case 'Target.setAutoAttach':
|
|
250
|
+
case 'Target.setDiscoverTargets':
|
|
251
|
+
return {};
|
|
252
|
+
|
|
253
|
+
case 'Target.getTargets':
|
|
254
|
+
return {
|
|
255
|
+
targetInfos: Array.from(connectedTargets.values())
|
|
256
|
+
.map(t => ({ ...t.targetInfo, attached: true }))
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
case 'Target.attachToTarget':
|
|
260
|
+
const targetId = params?.targetId;
|
|
261
|
+
for (const target of connectedTargets.values()) {
|
|
262
|
+
if (target.targetId === targetId) {
|
|
263
|
+
return { sessionId: target.sessionId };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
throw new Error(`Target ${targetId} not found`);
|
|
267
|
+
|
|
268
|
+
case 'Target.getTargetInfo':
|
|
269
|
+
if (params?.targetId) {
|
|
270
|
+
for (const target of connectedTargets.values()) {
|
|
271
|
+
if (target.targetId === params.targetId) {
|
|
272
|
+
return { targetInfo: target.targetInfo };
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (sessionId) {
|
|
277
|
+
const target = connectedTargets.get(sessionId);
|
|
278
|
+
if (target) return { targetInfo: target.targetInfo };
|
|
279
|
+
}
|
|
280
|
+
const first = Array.from(connectedTargets.values())[0];
|
|
281
|
+
return { targetInfo: first?.targetInfo };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Forward to extension
|
|
285
|
+
return await sendToExtension({
|
|
286
|
+
method: 'forwardCDPCommand',
|
|
287
|
+
params: { sessionId, method, params }
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Export for use as module
|
|
292
|
+
module.exports = { server, wss, routeCDPCommand };
|
|
293
|
+
|
|
294
|
+
// Start server if run directly
|
|
295
|
+
if (require.main === module) {
|
|
296
|
+
server.listen(PORT, HOST, () => {
|
|
297
|
+
console.log(`[relay] CDP relay server running on ws://${HOST}:${PORT}`);
|
|
298
|
+
console.log('[relay] Endpoints:');
|
|
299
|
+
console.log(` - Extension: ws://${HOST}:${PORT}/extension`);
|
|
300
|
+
console.log(` - CDP: ws://${HOST}:${PORT}/cdp`);
|
|
301
|
+
console.log(` - Status: http://${HOST}:${PORT}/status`);
|
|
302
|
+
console.log(` - Targets: http://${HOST}:${PORT}/targets`);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
process.on('SIGINT', () => {
|
|
306
|
+
console.log('\n[relay] Shutting down...');
|
|
307
|
+
wss.close();
|
|
308
|
+
server.close();
|
|
309
|
+
process.exit(0);
|
|
310
|
+
});
|
|
311
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glidercli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Browser automation CLI with autonomous loop execution. Control Chrome via CDP, run YAML task files, execute in Ralph Wiggum loops.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -33,5 +33,11 @@
|
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"ws": "^8.18.0",
|
|
35
35
|
"yaml": "^2.7.0"
|
|
36
|
-
}
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"bin/",
|
|
39
|
+
"lib/",
|
|
40
|
+
"index.js",
|
|
41
|
+
"README.md"
|
|
42
|
+
]
|
|
37
43
|
}
|
package/.git-personal-enforced
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Auto-prompt to set email on checkout if not set correctly
|
|
3
|
-
# If allowed_emails is empty, skip check
|
|
4
|
-
|
|
5
|
-
ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
6
|
-
CONFIG="$ROOT/repo.config.json"
|
|
7
|
-
EMAIL=$(git config user.email 2>/dev/null)
|
|
8
|
-
|
|
9
|
-
# Read allowed emails from config
|
|
10
|
-
if [[ -f "$CONFIG" ]] && command -v jq &>/dev/null; then
|
|
11
|
-
ALLOWED=$(jq -r '.allowed_emails[]' "$CONFIG" 2>/dev/null)
|
|
12
|
-
else
|
|
13
|
-
exit 0
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
# If no emails configured, skip check
|
|
17
|
-
[[ -z "$ALLOWED" ]] && exit 0
|
|
18
|
-
|
|
19
|
-
echo "$ALLOWED" | grep -qx "$EMAIL" && exit 0
|
|
20
|
-
|
|
21
|
-
echo ""
|
|
22
|
-
echo "⚠ Git email not set. Pick one from repo.config.json:"
|
|
23
|
-
echo "$ALLOWED" | sed 's/^/ git config user.email '\''/' | sed 's/$/'\''/'
|
|
24
|
-
echo ""
|
package/.github/hooks/pre-commit
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# Blocks commits if email not in allowed list (reads from repo.config.json)
|
|
3
|
-
# If allowed_emails is empty, skip check (user hasn't configured yet)
|
|
4
|
-
|
|
5
|
-
ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
6
|
-
CONFIG="$ROOT/repo.config.json"
|
|
7
|
-
EMAIL=$(git config user.email)
|
|
8
|
-
|
|
9
|
-
# Read allowed emails from config
|
|
10
|
-
if [[ -f "$CONFIG" ]] && command -v jq &>/dev/null; then
|
|
11
|
-
ALLOWED=$(jq -r '.allowed_emails[]' "$CONFIG" 2>/dev/null)
|
|
12
|
-
else
|
|
13
|
-
# No config or jq - skip check
|
|
14
|
-
exit 0
|
|
15
|
-
fi
|
|
16
|
-
|
|
17
|
-
# If no emails configured, skip check
|
|
18
|
-
[[ -z "$ALLOWED" ]] && exit 0
|
|
19
|
-
|
|
20
|
-
echo "$ALLOWED" | grep -qx "$EMAIL" && exit 0
|
|
21
|
-
|
|
22
|
-
echo ""
|
|
23
|
-
echo "❌ BLOCKED: $EMAIL not allowed"
|
|
24
|
-
echo ""
|
|
25
|
-
echo "Allowed emails (from repo.config.json):"
|
|
26
|
-
echo "$ALLOWED" | sed 's/^/ /'
|
|
27
|
-
echo ""
|
|
28
|
-
echo "Fix: git config user.email '<one of the above>'"
|
|
29
|
-
echo ""
|
|
30
|
-
exit 1
|
package/.github/hooks/pre-push
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# pre-push hook - runs health check before push
|
|
3
|
-
# Blocks push if large files or embedded repos detected
|
|
4
|
-
|
|
5
|
-
ROOT="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
6
|
-
HEALTH_SCRIPT="$ROOT/.github/scripts/health-check.sh"
|
|
7
|
-
|
|
8
|
-
if [[ -x "$HEALTH_SCRIPT" ]]; then
|
|
9
|
-
"$HEALTH_SCRIPT"
|
|
10
|
-
exit $?
|
|
11
|
-
fi
|
|
12
|
-
|
|
13
|
-
exit 0
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# health-check.sh - Git health check before commit/push
|
|
3
|
-
# Checks: large files (>90MB), embedded git repos
|
|
4
|
-
# Run: .github/scripts/health-check.sh [size_mb]
|
|
5
|
-
|
|
6
|
-
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
7
|
-
SIZE_MB="${1:-90}"
|
|
8
|
-
LIMIT=$((SIZE_MB * 1024 * 1024))
|
|
9
|
-
|
|
10
|
-
cd "$ROOT" || exit 1
|
|
11
|
-
|
|
12
|
-
echo "🏥 Git Health Check: $ROOT"
|
|
13
|
-
echo "==================="
|
|
14
|
-
echo ""
|
|
15
|
-
|
|
16
|
-
ISSUES=0
|
|
17
|
-
|
|
18
|
-
# ============================================================================
|
|
19
|
-
# CHECK 1: Large files (>90MB default)
|
|
20
|
-
# ============================================================================
|
|
21
|
-
echo "🔍 Checking for files > ${SIZE_MB}MB..."
|
|
22
|
-
echo ""
|
|
23
|
-
|
|
24
|
-
echo "=== UNSTAGED/UNTRACKED ==="
|
|
25
|
-
while IFS= read -r line; do
|
|
26
|
-
[[ -z "$line" ]] && continue
|
|
27
|
-
file="${line:3}"
|
|
28
|
-
# Handle renames
|
|
29
|
-
[[ "$line" == R* ]] && file="${file##* -> }"
|
|
30
|
-
if [[ -f "$file" ]]; then
|
|
31
|
-
fsize=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null || echo "0")
|
|
32
|
-
if [[ "$fsize" -gt "$LIMIT" ]]; then
|
|
33
|
-
size_human=$((fsize / 1024 / 1024))
|
|
34
|
-
echo " 🔴 $file (${size_human}MB)"
|
|
35
|
-
ISSUES=1
|
|
36
|
-
fi
|
|
37
|
-
fi
|
|
38
|
-
done < <(git status --porcelain 2>/dev/null)
|
|
39
|
-
|
|
40
|
-
echo ""
|
|
41
|
-
echo "=== STAGED ==="
|
|
42
|
-
while IFS= read -r file; do
|
|
43
|
-
[[ -z "$file" ]] && continue
|
|
44
|
-
if [[ -f "$file" ]]; then
|
|
45
|
-
fsize=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null || echo "0")
|
|
46
|
-
if [[ "$fsize" -gt "$LIMIT" ]]; then
|
|
47
|
-
size_human=$((fsize / 1024 / 1024))
|
|
48
|
-
echo " 🔴 $file (${size_human}MB)"
|
|
49
|
-
ISSUES=1
|
|
50
|
-
fi
|
|
51
|
-
fi
|
|
52
|
-
done < <(git diff --cached --name-only 2>/dev/null)
|
|
53
|
-
|
|
54
|
-
echo ""
|
|
55
|
-
echo "=== COMMITTED (last 10) ==="
|
|
56
|
-
while IFS= read -r file; do
|
|
57
|
-
[[ -z "$file" ]] && continue
|
|
58
|
-
if [[ -f "$file" ]]; then
|
|
59
|
-
fsize=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null || echo "0")
|
|
60
|
-
if [[ "$fsize" -gt "$LIMIT" ]]; then
|
|
61
|
-
size_human=$((fsize / 1024 / 1024))
|
|
62
|
-
echo " 🔴 $file (${size_human}MB)"
|
|
63
|
-
ISSUES=1
|
|
64
|
-
fi
|
|
65
|
-
fi
|
|
66
|
-
done < <(git log --oneline -10 --diff-filter=A --name-only --pretty=format:"" 2>/dev/null | sort -u)
|
|
67
|
-
|
|
68
|
-
if [[ $ISSUES -eq 0 ]]; then
|
|
69
|
-
echo " 🟢 No large files"
|
|
70
|
-
fi
|
|
71
|
-
|
|
72
|
-
echo ""
|
|
73
|
-
|
|
74
|
-
# ============================================================================
|
|
75
|
-
# CHECK 2: Embedded git repos
|
|
76
|
-
# ============================================================================
|
|
77
|
-
echo "🔍 Checking for embedded git repos..."
|
|
78
|
-
echo ""
|
|
79
|
-
|
|
80
|
-
ROOT_GIT="$ROOT/.git"
|
|
81
|
-
EMBEDDED=()
|
|
82
|
-
|
|
83
|
-
while IFS= read -r git_dir; do
|
|
84
|
-
[[ -z "$git_dir" ]] && continue
|
|
85
|
-
[[ "$git_dir" == "$ROOT_GIT" ]] && continue
|
|
86
|
-
|
|
87
|
-
repo_dir="${git_dir%/.git}"
|
|
88
|
-
relative="${repo_dir#$ROOT/}"
|
|
89
|
-
|
|
90
|
-
# Check if tracked or not ignored
|
|
91
|
-
is_tracked=false
|
|
92
|
-
git ls-files --cached "$relative" 2>/dev/null | grep -q . && is_tracked=true
|
|
93
|
-
|
|
94
|
-
is_ignored=false
|
|
95
|
-
git check-ignore -q "$relative" 2>/dev/null && is_ignored=true
|
|
96
|
-
|
|
97
|
-
if [[ "$is_tracked" == "true" ]]; then
|
|
98
|
-
echo " 🔴 $relative (TRACKED - needs removal)"
|
|
99
|
-
EMBEDDED+=("$relative")
|
|
100
|
-
ISSUES=1
|
|
101
|
-
elif [[ "$is_ignored" == "false" ]]; then
|
|
102
|
-
echo " 🟡 $relative (not ignored - add to .gitignore)"
|
|
103
|
-
EMBEDDED+=("$relative")
|
|
104
|
-
ISSUES=1
|
|
105
|
-
fi
|
|
106
|
-
done < <(find "$ROOT" -type d -name ".git" 2>/dev/null | sort)
|
|
107
|
-
|
|
108
|
-
if [[ ${#EMBEDDED[@]} -eq 0 ]]; then
|
|
109
|
-
echo " 🟢 No embedded repos"
|
|
110
|
-
else
|
|
111
|
-
echo ""
|
|
112
|
-
echo "Fix: Add to .gitignore:"
|
|
113
|
-
for repo in "${EMBEDDED[@]}"; do
|
|
114
|
-
echo " $repo/"
|
|
115
|
-
done
|
|
116
|
-
fi
|
|
117
|
-
|
|
118
|
-
echo ""
|
|
119
|
-
echo "==================="
|
|
120
|
-
|
|
121
|
-
if [[ $ISSUES -eq 0 ]]; then
|
|
122
|
-
echo "🟢 All checks passed"
|
|
123
|
-
exit 0
|
|
124
|
-
else
|
|
125
|
-
echo "🔴 Issues found - fix before pushing"
|
|
126
|
-
exit 1
|
|
127
|
-
fi
|
package/.github/scripts/setup.sh
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# setup.sh - run after cloning
|
|
3
|
-
# Installs hooks and prompts for email config
|
|
4
|
-
|
|
5
|
-
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
6
|
-
cd "$ROOT"
|
|
7
|
-
|
|
8
|
-
# 1. Install hooks
|
|
9
|
-
mkdir -p .git/hooks
|
|
10
|
-
cp .github/hooks/* .git/hooks/ 2>/dev/null
|
|
11
|
-
chmod +x .git/hooks/* 2>/dev/null
|
|
12
|
-
echo "✓ Hooks installed"
|
|
13
|
-
|
|
14
|
-
# 2. Prompt user to set email
|
|
15
|
-
echo ""
|
|
16
|
-
echo "Set your git email:"
|
|
17
|
-
echo " git config user.email 'github.relock416@passmail.net' # vdutts"
|
|
18
|
-
echo " git config user.email 'me@vd7.io' # vdutts7"
|
|
19
|
-
echo ""
|
package/assets/icons/.gitkeep
DELETED
|
File without changes
|
package/repo.config.json
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"repo": {
|
|
3
|
-
"name": "glidercli",
|
|
4
|
-
"description": "Browser automation CLI with autonomous loop execution",
|
|
5
|
-
"homepage": "https://npmjs.com/package/glidercli",
|
|
6
|
-
"topics": ["ralph", "claude", "claude-code", "autonomous-agents", "browser-automation", "cdp", "chrome-devtools", "ralph-wiggum"]
|
|
7
|
-
},
|
|
8
|
-
"owner": {
|
|
9
|
-
"github_username": "vdutts7",
|
|
10
|
-
"website": "https://vd7.io",
|
|
11
|
-
"twitter": "vaboratory"
|
|
12
|
-
},
|
|
13
|
-
"allowed_emails": [
|
|
14
|
-
"github.relock416@passmail.net",
|
|
15
|
-
"me@vd7.io"
|
|
16
|
-
],
|
|
17
|
-
"social_preview": {
|
|
18
|
-
"title": "glidercli",
|
|
19
|
-
"icons_dir": "assets/icons",
|
|
20
|
-
"icon_creator": "https://vd7.dev/icon-creator",
|
|
21
|
-
"dimensions": {
|
|
22
|
-
"width": 1280,
|
|
23
|
-
"height": 640
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
"npm": {
|
|
27
|
-
"package_name": "glidercli",
|
|
28
|
-
"alt_package": "@vd7/glider",
|
|
29
|
-
"bin_command": "glider"
|
|
30
|
-
}
|
|
31
|
-
}
|