network-terminal 1.0.6 → 1.0.8
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/bin/cli.js +454 -26
- package/package.json +4 -4
- package/standalone-dist/assets/index-DNYlGkS8.js +40 -0
- package/{standalone → standalone-dist}/index.html +1 -1
- package/src/components/LogEntry.tsx +0 -121
- package/src/components/NetworkTerminal.tsx +0 -219
- package/src/components/Terminal.tsx +0 -86
- package/src/components/TerminalHeader.tsx +0 -93
- package/src/hooks/useNetworkInterceptor.ts +0 -190
- package/src/index.ts +0 -17
- package/src/types.ts +0 -50
- package/src/utils/colors.ts +0 -18
- package/src/utils/formatters.ts +0 -26
- package/src/vite-plugin.ts +0 -88
- package/standalone/main.tsx +0 -216
- package/standalone/vite.config.js +0 -22
package/bin/cli.js
CHANGED
|
@@ -1,40 +1,468 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const http = require('http');
|
|
4
|
+
const fs = require('fs');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
|
|
6
7
|
const args = process.argv.slice(2);
|
|
8
|
+
|
|
9
|
+
// Parse arguments
|
|
7
10
|
const portIndex = args.indexOf('--port');
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
const proxyIndex = args.indexOf('--proxy');
|
|
12
|
+
const port = portIndex !== -1 ? parseInt(args[portIndex + 1], 10) : 3001;
|
|
13
|
+
const targetPort = proxyIndex !== -1 ? parseInt(args[proxyIndex + 1], 10) : 3000;
|
|
14
|
+
|
|
15
|
+
const distPath = path.join(__dirname, '..', 'standalone-dist');
|
|
16
|
+
|
|
17
|
+
// Read the built JS file to inline it
|
|
18
|
+
let inlineScript = '';
|
|
19
|
+
try {
|
|
20
|
+
const assetsDir = path.join(distPath, 'assets');
|
|
21
|
+
const files = fs.readdirSync(assetsDir);
|
|
22
|
+
const jsFile = files.find(f => f.endsWith('.js'));
|
|
23
|
+
if (jsFile) {
|
|
24
|
+
inlineScript = fs.readFileSync(path.join(assetsDir, jsFile), 'utf-8');
|
|
25
|
+
}
|
|
26
|
+
} catch (e) {
|
|
27
|
+
// Will use CDN fallback
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Script to inject Network Terminal
|
|
31
|
+
const injectionScript = `
|
|
32
|
+
<script type="module">
|
|
33
|
+
// Network Terminal Injection
|
|
34
|
+
(function() {
|
|
35
|
+
// Create container for Network Terminal
|
|
36
|
+
const container = document.createElement('div');
|
|
37
|
+
container.id = 'network-terminal-root';
|
|
38
|
+
document.body.appendChild(container);
|
|
39
|
+
|
|
40
|
+
// Store original fetch and XHR
|
|
41
|
+
const originalFetch = window.fetch;
|
|
42
|
+
const originalXHR = window.XMLHttpRequest;
|
|
43
|
+
|
|
44
|
+
// Network logs storage
|
|
45
|
+
window.__networkLogs = [];
|
|
46
|
+
window.__networkTerminalUpdate = null;
|
|
47
|
+
|
|
48
|
+
// Intercept Fetch
|
|
49
|
+
window.fetch = async function(...args) {
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
const [url, options = {}] = args;
|
|
52
|
+
const method = options.method || 'GET';
|
|
53
|
+
|
|
54
|
+
const log = {
|
|
55
|
+
id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
|
|
56
|
+
method: method.toUpperCase(),
|
|
57
|
+
url: typeof url === 'string' ? url : url.toString(),
|
|
58
|
+
timestamp: new Date().toISOString(),
|
|
59
|
+
requestBody: options.body ? tryParse(options.body) : null,
|
|
60
|
+
status: null,
|
|
61
|
+
responseBody: null,
|
|
62
|
+
duration: null,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
const response = await originalFetch.apply(this, args);
|
|
67
|
+
log.status = response.status;
|
|
68
|
+
log.duration = Date.now() - startTime;
|
|
69
|
+
|
|
70
|
+
// Clone response to read body
|
|
71
|
+
const cloned = response.clone();
|
|
72
|
+
try {
|
|
73
|
+
const text = await cloned.text();
|
|
74
|
+
log.responseBody = tryParse(text);
|
|
75
|
+
} catch (e) {}
|
|
76
|
+
|
|
77
|
+
addLog(log);
|
|
78
|
+
return response;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
log.status = 0;
|
|
81
|
+
log.duration = Date.now() - startTime;
|
|
82
|
+
log.error = error.message;
|
|
83
|
+
addLog(log);
|
|
84
|
+
throw error;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Intercept XHR
|
|
89
|
+
window.XMLHttpRequest = function() {
|
|
90
|
+
const xhr = new originalXHR();
|
|
91
|
+
const startTime = Date.now();
|
|
92
|
+
let method, url, requestBody;
|
|
93
|
+
|
|
94
|
+
const originalOpen = xhr.open;
|
|
95
|
+
xhr.open = function(m, u, ...rest) {
|
|
96
|
+
method = m;
|
|
97
|
+
url = u;
|
|
98
|
+
return originalOpen.apply(this, [m, u, ...rest]);
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
const originalSend = xhr.send;
|
|
102
|
+
xhr.send = function(body) {
|
|
103
|
+
requestBody = body;
|
|
104
|
+
|
|
105
|
+
xhr.addEventListener('loadend', function() {
|
|
106
|
+
const log = {
|
|
107
|
+
id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
|
|
108
|
+
method: (method || 'GET').toUpperCase(),
|
|
109
|
+
url: url,
|
|
110
|
+
timestamp: new Date().toISOString(),
|
|
111
|
+
requestBody: tryParse(requestBody),
|
|
112
|
+
status: xhr.status,
|
|
113
|
+
responseBody: tryParse(xhr.responseText),
|
|
114
|
+
duration: Date.now() - startTime,
|
|
115
|
+
};
|
|
116
|
+
addLog(log);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return originalSend.apply(this, arguments);
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
return xhr;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
function tryParse(data) {
|
|
126
|
+
if (!data) return null;
|
|
127
|
+
if (typeof data === 'object') return data;
|
|
128
|
+
try {
|
|
129
|
+
return JSON.parse(data);
|
|
130
|
+
} catch (e) {
|
|
131
|
+
return data;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function addLog(log) {
|
|
136
|
+
window.__networkLogs.unshift(log);
|
|
137
|
+
if (window.__networkLogs.length > 100) {
|
|
138
|
+
window.__networkLogs.pop();
|
|
139
|
+
}
|
|
140
|
+
if (window.__networkTerminalUpdate) {
|
|
141
|
+
window.__networkTerminalUpdate([...window.__networkLogs]);
|
|
142
|
+
}
|
|
143
|
+
// Also log to console for CLI visibility
|
|
144
|
+
console.log('[Network]', log.method, log.status || '...', log.url, log.duration ? log.duration + 'ms' : '');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Styles
|
|
148
|
+
const styles = document.createElement('style');
|
|
149
|
+
styles.textContent = \`
|
|
150
|
+
#network-terminal-panel {
|
|
151
|
+
position: fixed;
|
|
152
|
+
bottom: 0;
|
|
153
|
+
left: 0;
|
|
154
|
+
right: 0;
|
|
155
|
+
height: 300px;
|
|
156
|
+
background: #0f172a;
|
|
157
|
+
border-top: 2px solid #4ade80;
|
|
158
|
+
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
159
|
+
font-size: 12px;
|
|
160
|
+
color: #e2e8f0;
|
|
161
|
+
z-index: 999999;
|
|
162
|
+
display: flex;
|
|
163
|
+
flex-direction: column;
|
|
164
|
+
transition: transform 0.3s ease;
|
|
165
|
+
}
|
|
166
|
+
#network-terminal-panel.collapsed {
|
|
167
|
+
transform: translateY(calc(100% - 36px));
|
|
168
|
+
}
|
|
169
|
+
.nt-header {
|
|
170
|
+
display: flex;
|
|
171
|
+
justify-content: space-between;
|
|
172
|
+
align-items: center;
|
|
173
|
+
padding: 8px 16px;
|
|
174
|
+
background: #1e293b;
|
|
175
|
+
border-bottom: 1px solid #334155;
|
|
176
|
+
cursor: pointer;
|
|
177
|
+
user-select: none;
|
|
178
|
+
}
|
|
179
|
+
.nt-title {
|
|
180
|
+
color: #4ade80;
|
|
181
|
+
font-weight: bold;
|
|
182
|
+
}
|
|
183
|
+
.nt-controls {
|
|
184
|
+
display: flex;
|
|
185
|
+
gap: 12px;
|
|
186
|
+
}
|
|
187
|
+
.nt-btn {
|
|
188
|
+
background: #334155;
|
|
189
|
+
border: none;
|
|
190
|
+
color: #94a3b8;
|
|
191
|
+
padding: 4px 8px;
|
|
192
|
+
border-radius: 4px;
|
|
193
|
+
cursor: pointer;
|
|
194
|
+
font-size: 11px;
|
|
195
|
+
}
|
|
196
|
+
.nt-btn:hover {
|
|
197
|
+
background: #475569;
|
|
198
|
+
color: #e2e8f0;
|
|
199
|
+
}
|
|
200
|
+
.nt-logs {
|
|
201
|
+
flex: 1;
|
|
202
|
+
overflow-y: auto;
|
|
203
|
+
padding: 8px 0;
|
|
204
|
+
}
|
|
205
|
+
.nt-log {
|
|
206
|
+
display: flex;
|
|
207
|
+
align-items: flex-start;
|
|
208
|
+
padding: 6px 16px;
|
|
209
|
+
border-bottom: 1px solid #1e293b;
|
|
210
|
+
cursor: pointer;
|
|
211
|
+
}
|
|
212
|
+
.nt-log:hover {
|
|
213
|
+
background: #1e293b;
|
|
214
|
+
}
|
|
215
|
+
.nt-log.expanded {
|
|
216
|
+
flex-direction: column;
|
|
217
|
+
background: #1e293b;
|
|
218
|
+
}
|
|
219
|
+
.nt-log-main {
|
|
220
|
+
display: flex;
|
|
221
|
+
align-items: center;
|
|
222
|
+
gap: 12px;
|
|
223
|
+
width: 100%;
|
|
224
|
+
}
|
|
225
|
+
.nt-method {
|
|
226
|
+
font-weight: bold;
|
|
227
|
+
width: 60px;
|
|
228
|
+
flex-shrink: 0;
|
|
229
|
+
}
|
|
230
|
+
.nt-method.GET { color: #22c55e; }
|
|
231
|
+
.nt-method.POST { color: #3b82f6; }
|
|
232
|
+
.nt-method.PUT { color: #f59e0b; }
|
|
233
|
+
.nt-method.DELETE { color: #ef4444; }
|
|
234
|
+
.nt-method.PATCH { color: #a855f7; }
|
|
235
|
+
.nt-status {
|
|
236
|
+
width: 40px;
|
|
237
|
+
flex-shrink: 0;
|
|
238
|
+
text-align: center;
|
|
239
|
+
padding: 2px 6px;
|
|
240
|
+
border-radius: 4px;
|
|
241
|
+
font-size: 11px;
|
|
242
|
+
}
|
|
243
|
+
.nt-status.success { background: #166534; color: #4ade80; }
|
|
244
|
+
.nt-status.error { background: #991b1b; color: #fca5a5; }
|
|
245
|
+
.nt-status.pending { background: #374151; color: #9ca3af; }
|
|
246
|
+
.nt-url {
|
|
247
|
+
flex: 1;
|
|
248
|
+
overflow: hidden;
|
|
249
|
+
text-overflow: ellipsis;
|
|
250
|
+
white-space: nowrap;
|
|
251
|
+
color: #94a3b8;
|
|
252
|
+
}
|
|
253
|
+
.nt-duration {
|
|
254
|
+
color: #64748b;
|
|
255
|
+
font-size: 11px;
|
|
256
|
+
width: 60px;
|
|
257
|
+
text-align: right;
|
|
258
|
+
flex-shrink: 0;
|
|
259
|
+
}
|
|
260
|
+
.nt-details {
|
|
261
|
+
margin-top: 8px;
|
|
262
|
+
padding: 8px;
|
|
263
|
+
background: #0f172a;
|
|
264
|
+
border-radius: 4px;
|
|
265
|
+
width: 100%;
|
|
266
|
+
max-height: 200px;
|
|
267
|
+
overflow: auto;
|
|
268
|
+
}
|
|
269
|
+
.nt-details pre {
|
|
270
|
+
margin: 0;
|
|
271
|
+
white-space: pre-wrap;
|
|
272
|
+
word-break: break-all;
|
|
273
|
+
color: #cbd5e1;
|
|
274
|
+
font-size: 11px;
|
|
275
|
+
}
|
|
276
|
+
.nt-detail-label {
|
|
277
|
+
color: #4ade80;
|
|
278
|
+
font-weight: bold;
|
|
279
|
+
margin-top: 8px;
|
|
280
|
+
margin-bottom: 4px;
|
|
281
|
+
}
|
|
282
|
+
.nt-detail-label:first-child {
|
|
283
|
+
margin-top: 0;
|
|
284
|
+
}
|
|
285
|
+
.nt-empty {
|
|
286
|
+
text-align: center;
|
|
287
|
+
padding: 40px;
|
|
288
|
+
color: #64748b;
|
|
289
|
+
}
|
|
290
|
+
\`;
|
|
291
|
+
document.head.appendChild(styles);
|
|
292
|
+
|
|
293
|
+
// Create UI
|
|
294
|
+
function createUI() {
|
|
295
|
+
const panel = document.createElement('div');
|
|
296
|
+
panel.id = 'network-terminal-panel';
|
|
297
|
+
panel.innerHTML = \`
|
|
298
|
+
<div class="nt-header">
|
|
299
|
+
<span class="nt-title">>_ Network Terminal</span>
|
|
300
|
+
<div class="nt-controls">
|
|
301
|
+
<button class="nt-btn" id="nt-clear">Clear</button>
|
|
302
|
+
<button class="nt-btn" id="nt-toggle">_</button>
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
<div class="nt-logs" id="nt-logs">
|
|
306
|
+
<div class="nt-empty">Waiting for network requests...</div>
|
|
307
|
+
</div>
|
|
308
|
+
\`;
|
|
309
|
+
document.body.appendChild(panel);
|
|
310
|
+
|
|
311
|
+
// Toggle collapse
|
|
312
|
+
document.getElementById('nt-toggle').addEventListener('click', (e) => {
|
|
313
|
+
e.stopPropagation();
|
|
314
|
+
panel.classList.toggle('collapsed');
|
|
315
|
+
e.target.textContent = panel.classList.contains('collapsed') ? '▲' : '_';
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Clear logs
|
|
319
|
+
document.getElementById('nt-clear').addEventListener('click', (e) => {
|
|
320
|
+
e.stopPropagation();
|
|
321
|
+
window.__networkLogs = [];
|
|
322
|
+
renderLogs([]);
|
|
24
323
|
});
|
|
25
324
|
|
|
26
|
-
|
|
325
|
+
// Header click to toggle
|
|
326
|
+
panel.querySelector('.nt-header').addEventListener('click', () => {
|
|
327
|
+
panel.classList.toggle('collapsed');
|
|
328
|
+
document.getElementById('nt-toggle').textContent = panel.classList.contains('collapsed') ? '▲' : '_';
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
let expandedId = null;
|
|
333
|
+
|
|
334
|
+
function renderLogs(logs) {
|
|
335
|
+
const container = document.getElementById('nt-logs');
|
|
336
|
+
if (!logs.length) {
|
|
337
|
+
container.innerHTML = '<div class="nt-empty">Waiting for network requests...</div>';
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
27
340
|
|
|
28
|
-
|
|
341
|
+
container.innerHTML = logs.map(log => {
|
|
342
|
+
const isExpanded = expandedId === log.id;
|
|
343
|
+
const statusClass = !log.status ? 'pending' : log.status < 400 ? 'success' : 'error';
|
|
29
344
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
345
|
+
return \`
|
|
346
|
+
<div class="nt-log \${isExpanded ? 'expanded' : ''}" data-id="\${log.id}">
|
|
347
|
+
<div class="nt-log-main">
|
|
348
|
+
<span class="nt-method \${log.method}">\${log.method}</span>
|
|
349
|
+
<span class="nt-status \${statusClass}">\${log.status || '...'}</span>
|
|
350
|
+
<span class="nt-url">\${log.url}</span>
|
|
351
|
+
<span class="nt-duration">\${log.duration ? log.duration + 'ms' : ''}</span>
|
|
352
|
+
</div>
|
|
353
|
+
\${isExpanded ? \`
|
|
354
|
+
<div class="nt-details">
|
|
355
|
+
\${log.requestBody ? \`<div class="nt-detail-label">Request Body:</div><pre>\${formatJson(log.requestBody)}</pre>\` : ''}
|
|
356
|
+
\${log.responseBody ? \`<div class="nt-detail-label">Response:</div><pre>\${formatJson(log.responseBody)}</pre>\` : ''}
|
|
357
|
+
</div>
|
|
358
|
+
\` : ''}
|
|
359
|
+
</div>
|
|
360
|
+
\`;
|
|
361
|
+
}).join('');
|
|
33
362
|
|
|
34
|
-
|
|
35
|
-
|
|
363
|
+
// Add click handlers
|
|
364
|
+
container.querySelectorAll('.nt-log').forEach(el => {
|
|
365
|
+
el.addEventListener('click', () => {
|
|
366
|
+
const id = el.dataset.id;
|
|
367
|
+
expandedId = expandedId === id ? null : id;
|
|
368
|
+
renderLogs(window.__networkLogs);
|
|
369
|
+
});
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function formatJson(data) {
|
|
374
|
+
if (typeof data === 'string') return escapeHtml(data);
|
|
375
|
+
try {
|
|
376
|
+
return escapeHtml(JSON.stringify(data, null, 2));
|
|
377
|
+
} catch (e) {
|
|
378
|
+
return escapeHtml(String(data));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function escapeHtml(str) {
|
|
383
|
+
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Initialize
|
|
387
|
+
createUI();
|
|
388
|
+
window.__networkTerminalUpdate = renderLogs;
|
|
389
|
+
})();
|
|
390
|
+
</script>
|
|
391
|
+
`;
|
|
392
|
+
|
|
393
|
+
console.log('\x1b[32m%s\x1b[0m', '\n >_ Network Terminal (Proxy Mode)\n');
|
|
394
|
+
console.log(` Proxying requests from port ${port} → ${targetPort}`);
|
|
395
|
+
console.log(' Starting proxy server...\n');
|
|
396
|
+
|
|
397
|
+
const server = http.createServer((req, res) => {
|
|
398
|
+
const options = {
|
|
399
|
+
hostname: 'localhost',
|
|
400
|
+
port: targetPort,
|
|
401
|
+
path: req.url,
|
|
402
|
+
method: req.method,
|
|
403
|
+
headers: req.headers,
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const proxyReq = http.request(options, (proxyRes) => {
|
|
407
|
+
const contentType = proxyRes.headers['content-type'] || '';
|
|
408
|
+
const isHtml = contentType.includes('text/html');
|
|
409
|
+
|
|
410
|
+
if (isHtml) {
|
|
411
|
+
// Collect the response body
|
|
412
|
+
let body = '';
|
|
413
|
+
proxyRes.on('data', chunk => body += chunk);
|
|
414
|
+
proxyRes.on('end', () => {
|
|
415
|
+
// Inject the Network Terminal before </body>
|
|
416
|
+
let modified = body;
|
|
417
|
+
if (body.includes('</body>')) {
|
|
418
|
+
modified = body.replace('</body>', injectionScript + '</body>');
|
|
419
|
+
} else if (body.includes('</html>')) {
|
|
420
|
+
modified = body.replace('</html>', injectionScript + '</html>');
|
|
421
|
+
} else {
|
|
422
|
+
modified = body + injectionScript;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Update content-length
|
|
426
|
+
const newHeaders = { ...proxyRes.headers };
|
|
427
|
+
delete newHeaders['content-length'];
|
|
428
|
+
delete newHeaders['content-encoding']; // Remove encoding since we modified content
|
|
429
|
+
|
|
430
|
+
res.writeHead(proxyRes.statusCode, newHeaders);
|
|
431
|
+
res.end(modified);
|
|
432
|
+
});
|
|
433
|
+
} else {
|
|
434
|
+
// Pass through non-HTML responses
|
|
435
|
+
res.writeHead(proxyRes.statusCode, proxyRes.headers);
|
|
436
|
+
proxyRes.pipe(res);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
proxyReq.on('error', (err) => {
|
|
441
|
+
console.error(` \x1b[31mProxy error:\x1b[0m ${err.message}`);
|
|
442
|
+
res.writeHead(502);
|
|
443
|
+
res.end(`Proxy Error: Could not connect to localhost:${targetPort}\n\nMake sure your app is running on port ${targetPort}`);
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
req.pipe(proxyReq);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
server.on('error', (err) => {
|
|
450
|
+
if (err.code === 'EADDRINUSE') {
|
|
451
|
+
console.error(`\x1b[31m Error: Port ${port} is already in use\x1b[0m`);
|
|
452
|
+
console.error(` Try: npx network-terminal --port ${port + 1} --proxy ${targetPort}`);
|
|
36
453
|
process.exit(1);
|
|
37
454
|
}
|
|
38
|
-
|
|
455
|
+
console.error('Server error:', err);
|
|
456
|
+
process.exit(1);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
server.listen(port, () => {
|
|
460
|
+
console.log('\x1b[32m%s\x1b[0m', ` Proxy server running!\n`);
|
|
461
|
+
console.log(` Open your app at: \x1b[36mhttp://localhost:${port}\x1b[0m`);
|
|
462
|
+
console.log(` (instead of http://localhost:${targetPort})\n`);
|
|
463
|
+
console.log(' Press \x1b[33mCtrl+C\x1b[0m to stop\n');
|
|
39
464
|
|
|
40
|
-
|
|
465
|
+
// Open browser
|
|
466
|
+
const open = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
467
|
+
require('child_process').exec(`${open} http://localhost:${port}`);
|
|
468
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "network-terminal",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "A browser-based terminal UI for monitoring Fetch/XHR requests with real-time display of routes, payloads, and responses",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
"files": [
|
|
24
24
|
"dist",
|
|
25
25
|
"bin",
|
|
26
|
-
"standalone"
|
|
27
|
-
"src"
|
|
26
|
+
"standalone-dist"
|
|
28
27
|
],
|
|
29
28
|
"scripts": {
|
|
30
|
-
"build": "tsup",
|
|
29
|
+
"build": "tsup && npm run build:standalone",
|
|
30
|
+
"build:standalone": "vite build --config standalone/vite.config.js",
|
|
31
31
|
"dev": "tsup --watch",
|
|
32
32
|
"prepublishOnly": "yarn build",
|
|
33
33
|
"typecheck": "tsc --noEmit"
|