nikcli-remote 1.0.1 → 1.0.2
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/dist/{chunk-QLGPGAPC.js → chunk-DRL7JX54.js} +225 -639
- package/dist/index.cjs +225 -772
- package/dist/index.d.cts +3 -78
- package/dist/index.d.ts +3 -78
- package/dist/index.js +4 -135
- package/dist/{server-ISK4MDQQ.js → server-J7SPDGZO.js} +1 -1
- package/package.json +1 -1
- package/src/index.ts +4 -61
- package/src/server.ts +3 -6
- package/src/tunnel.ts +11 -7
- package/src/web-client.ts +214 -593
package/dist/index.cjs
CHANGED
|
@@ -5318,40 +5318,6 @@ var require_localtunnel = __commonJS({
|
|
|
5318
5318
|
});
|
|
5319
5319
|
|
|
5320
5320
|
// src/tunnel.ts
|
|
5321
|
-
async function checkTunnelAvailability(provider) {
|
|
5322
|
-
try {
|
|
5323
|
-
const { execSync } = await import("child_process");
|
|
5324
|
-
switch (provider) {
|
|
5325
|
-
case "localtunnel":
|
|
5326
|
-
try {
|
|
5327
|
-
await Promise.resolve().then(() => __toESM(require_localtunnel(), 1));
|
|
5328
|
-
return true;
|
|
5329
|
-
} catch {
|
|
5330
|
-
execSync("npx localtunnel --version", { stdio: "pipe" });
|
|
5331
|
-
return true;
|
|
5332
|
-
}
|
|
5333
|
-
case "cloudflared":
|
|
5334
|
-
execSync("cloudflared --version", { stdio: "pipe" });
|
|
5335
|
-
return true;
|
|
5336
|
-
case "ngrok":
|
|
5337
|
-
execSync("ngrok version", { stdio: "pipe" });
|
|
5338
|
-
return true;
|
|
5339
|
-
default:
|
|
5340
|
-
return false;
|
|
5341
|
-
}
|
|
5342
|
-
} catch {
|
|
5343
|
-
return false;
|
|
5344
|
-
}
|
|
5345
|
-
}
|
|
5346
|
-
async function findAvailableTunnel() {
|
|
5347
|
-
const providers = ["localtunnel", "cloudflared", "ngrok"];
|
|
5348
|
-
for (const provider of providers) {
|
|
5349
|
-
if (await checkTunnelAvailability(provider)) {
|
|
5350
|
-
return provider;
|
|
5351
|
-
}
|
|
5352
|
-
}
|
|
5353
|
-
return null;
|
|
5354
|
-
}
|
|
5355
5321
|
var import_node_child_process2, TunnelManager;
|
|
5356
5322
|
var init_tunnel = __esm({
|
|
5357
5323
|
"src/tunnel.ts"() {
|
|
@@ -5422,7 +5388,7 @@ var init_tunnel = __esm({
|
|
|
5422
5388
|
*/
|
|
5423
5389
|
createLocaltunnelCli(port) {
|
|
5424
5390
|
return new Promise((resolve, reject) => {
|
|
5425
|
-
this.process = (0, import_node_child_process2.spawn)("npx", ["localtunnel", "--port", port.toString()], {
|
|
5391
|
+
this.process = (0, import_node_child_process2.spawn)("npx", ["localtunnel", "--port", port.toString(), "--print-requests", "false"], {
|
|
5426
5392
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5427
5393
|
shell: true
|
|
5428
5394
|
});
|
|
@@ -5439,8 +5405,7 @@ var init_tunnel = __esm({
|
|
|
5439
5405
|
resolve(match[1]);
|
|
5440
5406
|
}
|
|
5441
5407
|
});
|
|
5442
|
-
this.process.stderr?.on("data", (
|
|
5443
|
-
output += data.toString();
|
|
5408
|
+
this.process.stderr?.on("data", () => {
|
|
5444
5409
|
});
|
|
5445
5410
|
this.process.on("error", (error) => {
|
|
5446
5411
|
clearTimeout(timeout);
|
|
@@ -5461,7 +5426,7 @@ var init_tunnel = __esm({
|
|
|
5461
5426
|
return new Promise((resolve, reject) => {
|
|
5462
5427
|
this.process = (0, import_node_child_process2.spawn)(
|
|
5463
5428
|
"cloudflared",
|
|
5464
|
-
["tunnel", "--url", `http://localhost:${port}
|
|
5429
|
+
["tunnel", "--url", `http://localhost:${port}`, "--metrics", "localhost:0"],
|
|
5465
5430
|
{
|
|
5466
5431
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5467
5432
|
}
|
|
@@ -5498,7 +5463,7 @@ var init_tunnel = __esm({
|
|
|
5498
5463
|
*/
|
|
5499
5464
|
createNgrok(port) {
|
|
5500
5465
|
return new Promise((resolve, reject) => {
|
|
5501
|
-
this.process = (0, import_node_child_process2.spawn)("ngrok", ["http", port.toString(), "--log=stdout"], {
|
|
5466
|
+
this.process = (0, import_node_child_process2.spawn)("ngrok", ["http", port.toString(), "--log=stdout", "--log-level=info"], {
|
|
5502
5467
|
stdio: ["pipe", "pipe", "pipe"]
|
|
5503
5468
|
});
|
|
5504
5469
|
let output = "";
|
|
@@ -5514,8 +5479,7 @@ var init_tunnel = __esm({
|
|
|
5514
5479
|
resolve(match[1]);
|
|
5515
5480
|
}
|
|
5516
5481
|
});
|
|
5517
|
-
this.process.stderr?.on("data", (
|
|
5518
|
-
output += data.toString();
|
|
5482
|
+
this.process.stderr?.on("data", () => {
|
|
5519
5483
|
});
|
|
5520
5484
|
this.process.on("error", (error) => {
|
|
5521
5485
|
clearTimeout(timeout);
|
|
@@ -5539,649 +5503,278 @@ function getWebClient() {
|
|
|
5539
5503
|
<html lang="en">
|
|
5540
5504
|
<head>
|
|
5541
5505
|
<meta charset="UTF-8">
|
|
5542
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
5506
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
|
|
5543
5507
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
5544
|
-
<meta name="mobile-web-app-
|
|
5545
|
-
<meta name="theme-color" content="#0d1117">
|
|
5508
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
5546
5509
|
<title>NikCLI Remote</title>
|
|
5547
5510
|
<style>
|
|
5511
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5548
5512
|
:root {
|
|
5549
|
-
--bg: #0d1117;
|
|
5513
|
+
--bg-primary: #0d1117;
|
|
5550
5514
|
--bg-secondary: #161b22;
|
|
5551
|
-
--fg: #e6edf3;
|
|
5552
|
-
--fg-muted: #8b949e;
|
|
5553
5515
|
--accent: #58a6ff;
|
|
5554
5516
|
--success: #3fb950;
|
|
5555
|
-
--warning: #d29922;
|
|
5556
|
-
--error: #f85149;
|
|
5557
5517
|
--border: #30363d;
|
|
5558
|
-
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
|
5559
5518
|
}
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
-webkit-tap-highlight-color: transparent;
|
|
5566
|
-
}
|
|
5567
|
-
|
|
5568
|
-
html, body {
|
|
5569
|
-
height: 100%;
|
|
5570
|
-
background: var(--bg);
|
|
5571
|
-
color: var(--fg);
|
|
5572
|
-
font-family: var(--font-mono);
|
|
5573
|
-
font-size: 14px;
|
|
5574
|
-
overflow: hidden;
|
|
5575
|
-
touch-action: manipulation;
|
|
5576
|
-
}
|
|
5577
|
-
|
|
5578
|
-
#app {
|
|
5519
|
+
html, body { height: 100%; overflow: hidden; touch-action: manipulation; }
|
|
5520
|
+
body {
|
|
5521
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
5522
|
+
background: var(--bg-primary);
|
|
5523
|
+
color: #e6edf3;
|
|
5579
5524
|
display: flex;
|
|
5580
5525
|
flex-direction: column;
|
|
5581
|
-
height: 100%;
|
|
5582
|
-
height: 100dvh;
|
|
5583
5526
|
}
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
#header {
|
|
5587
|
-
display: flex;
|
|
5588
|
-
align-items: center;
|
|
5589
|
-
justify-content: space-between;
|
|
5590
|
-
padding: 12px 16px;
|
|
5591
|
-
background: var(--bg-secondary);
|
|
5592
|
-
border-bottom: 1px solid var(--border);
|
|
5593
|
-
flex-shrink: 0;
|
|
5594
|
-
}
|
|
5595
|
-
|
|
5596
|
-
#header h1 {
|
|
5597
|
-
font-size: 16px;
|
|
5598
|
-
font-weight: 600;
|
|
5599
|
-
color: var(--accent);
|
|
5600
|
-
display: flex;
|
|
5601
|
-
align-items: center;
|
|
5602
|
-
gap: 8px;
|
|
5603
|
-
}
|
|
5604
|
-
|
|
5605
|
-
#header h1::before {
|
|
5606
|
-
content: '';
|
|
5607
|
-
width: 10px;
|
|
5608
|
-
height: 10px;
|
|
5609
|
-
background: var(--accent);
|
|
5610
|
-
border-radius: 2px;
|
|
5611
|
-
}
|
|
5612
|
-
|
|
5613
|
-
#status {
|
|
5614
|
-
display: flex;
|
|
5615
|
-
align-items: center;
|
|
5616
|
-
gap: 6px;
|
|
5617
|
-
font-size: 12px;
|
|
5618
|
-
color: var(--fg-muted);
|
|
5619
|
-
}
|
|
5620
|
-
|
|
5621
|
-
#status-dot {
|
|
5622
|
-
width: 8px;
|
|
5623
|
-
height: 8px;
|
|
5624
|
-
border-radius: 50%;
|
|
5625
|
-
background: var(--error);
|
|
5626
|
-
transition: background 0.3s;
|
|
5627
|
-
}
|
|
5628
|
-
|
|
5629
|
-
#status-dot.connected {
|
|
5630
|
-
background: var(--success);
|
|
5631
|
-
}
|
|
5632
|
-
|
|
5633
|
-
#status-dot.connecting {
|
|
5634
|
-
background: var(--warning);
|
|
5635
|
-
animation: pulse 1s infinite;
|
|
5636
|
-
}
|
|
5637
|
-
|
|
5638
|
-
@keyframes pulse {
|
|
5639
|
-
0%, 100% { opacity: 1; }
|
|
5640
|
-
50% { opacity: 0.5; }
|
|
5641
|
-
}
|
|
5642
|
-
|
|
5643
|
-
/* Terminal */
|
|
5644
|
-
#terminal-container {
|
|
5645
|
-
flex: 1;
|
|
5646
|
-
overflow: hidden;
|
|
5647
|
-
position: relative;
|
|
5648
|
-
}
|
|
5649
|
-
|
|
5650
|
-
#terminal {
|
|
5651
|
-
height: 100%;
|
|
5652
|
-
padding: 12px;
|
|
5653
|
-
overflow-y: auto;
|
|
5654
|
-
overflow-x: hidden;
|
|
5655
|
-
font-size: 13px;
|
|
5656
|
-
line-height: 1.5;
|
|
5657
|
-
white-space: pre-wrap;
|
|
5658
|
-
word-break: break-all;
|
|
5659
|
-
-webkit-overflow-scrolling: touch;
|
|
5660
|
-
}
|
|
5661
|
-
|
|
5662
|
-
#terminal::-webkit-scrollbar {
|
|
5663
|
-
width: 6px;
|
|
5664
|
-
}
|
|
5665
|
-
|
|
5666
|
-
#terminal::-webkit-scrollbar-track {
|
|
5667
|
-
background: var(--bg);
|
|
5668
|
-
}
|
|
5669
|
-
|
|
5670
|
-
#terminal::-webkit-scrollbar-thumb {
|
|
5671
|
-
background: var(--border);
|
|
5672
|
-
border-radius: 3px;
|
|
5673
|
-
}
|
|
5674
|
-
|
|
5675
|
-
.cursor {
|
|
5676
|
-
display: inline-block;
|
|
5677
|
-
width: 8px;
|
|
5678
|
-
height: 16px;
|
|
5679
|
-
background: var(--fg);
|
|
5680
|
-
animation: blink 1s step-end infinite;
|
|
5681
|
-
vertical-align: text-bottom;
|
|
5682
|
-
}
|
|
5683
|
-
|
|
5684
|
-
@keyframes blink {
|
|
5685
|
-
50% { opacity: 0; }
|
|
5686
|
-
}
|
|
5687
|
-
|
|
5688
|
-
/* Notifications */
|
|
5689
|
-
#notifications {
|
|
5690
|
-
position: fixed;
|
|
5691
|
-
top: 60px;
|
|
5692
|
-
left: 12px;
|
|
5693
|
-
right: 12px;
|
|
5694
|
-
z-index: 1000;
|
|
5695
|
-
pointer-events: none;
|
|
5696
|
-
}
|
|
5697
|
-
|
|
5698
|
-
.notification {
|
|
5699
|
-
background: var(--bg-secondary);
|
|
5700
|
-
border: 1px solid var(--border);
|
|
5701
|
-
border-radius: 8px;
|
|
5702
|
-
padding: 12px 16px;
|
|
5703
|
-
margin-bottom: 8px;
|
|
5704
|
-
animation: slideIn 0.3s ease;
|
|
5705
|
-
pointer-events: auto;
|
|
5706
|
-
box-shadow: 0 4px 12px rgba(0,0,0,0.4);
|
|
5707
|
-
}
|
|
5708
|
-
|
|
5709
|
-
.notification.success { border-left: 3px solid var(--success); }
|
|
5710
|
-
.notification.error { border-left: 3px solid var(--error); }
|
|
5711
|
-
.notification.warning { border-left: 3px solid var(--warning); }
|
|
5712
|
-
.notification.info { border-left: 3px solid var(--accent); }
|
|
5713
|
-
|
|
5714
|
-
.notification h4 {
|
|
5715
|
-
font-size: 14px;
|
|
5716
|
-
font-weight: 600;
|
|
5717
|
-
margin-bottom: 4px;
|
|
5718
|
-
}
|
|
5719
|
-
|
|
5720
|
-
.notification p {
|
|
5721
|
-
font-size: 12px;
|
|
5722
|
-
color: var(--fg-muted);
|
|
5723
|
-
}
|
|
5724
|
-
|
|
5725
|
-
@keyframes slideIn {
|
|
5726
|
-
from { transform: translateY(-20px); opacity: 0; }
|
|
5727
|
-
to { transform: translateY(0); opacity: 1; }
|
|
5728
|
-
}
|
|
5729
|
-
|
|
5730
|
-
/* Quick Keys */
|
|
5731
|
-
#quickkeys {
|
|
5732
|
-
display: grid;
|
|
5733
|
-
grid-template-columns: repeat(6, 1fr);
|
|
5734
|
-
gap: 6px;
|
|
5735
|
-
padding: 8px 12px;
|
|
5736
|
-
background: var(--bg-secondary);
|
|
5737
|
-
border-top: 1px solid var(--border);
|
|
5738
|
-
flex-shrink: 0;
|
|
5739
|
-
}
|
|
5740
|
-
|
|
5741
|
-
.qkey {
|
|
5742
|
-
background: var(--bg);
|
|
5743
|
-
border: 1px solid var(--border);
|
|
5744
|
-
border-radius: 6px;
|
|
5745
|
-
padding: 10px 4px;
|
|
5746
|
-
color: var(--fg);
|
|
5747
|
-
font-size: 11px;
|
|
5748
|
-
font-family: var(--font-mono);
|
|
5749
|
-
text-align: center;
|
|
5750
|
-
cursor: pointer;
|
|
5751
|
-
user-select: none;
|
|
5752
|
-
transition: background 0.1s, transform 0.1s;
|
|
5753
|
-
}
|
|
5754
|
-
|
|
5755
|
-
.qkey:active {
|
|
5756
|
-
background: var(--border);
|
|
5757
|
-
transform: scale(0.95);
|
|
5758
|
-
}
|
|
5759
|
-
|
|
5760
|
-
.qkey.wide {
|
|
5761
|
-
grid-column: span 2;
|
|
5762
|
-
}
|
|
5763
|
-
|
|
5764
|
-
.qkey.accent {
|
|
5765
|
-
background: var(--accent);
|
|
5766
|
-
border-color: var(--accent);
|
|
5767
|
-
color: #fff;
|
|
5768
|
-
}
|
|
5769
|
-
|
|
5770
|
-
/* Input */
|
|
5771
|
-
#input-container {
|
|
5772
|
-
padding: 12px;
|
|
5527
|
+
#terminal { flex: 1; overflow: hidden; background: var(--bg-primary); }
|
|
5528
|
+
#input-area {
|
|
5773
5529
|
background: var(--bg-secondary);
|
|
5774
5530
|
border-top: 1px solid var(--border);
|
|
5531
|
+
padding: 12px 16px;
|
|
5775
5532
|
flex-shrink: 0;
|
|
5533
|
+
padding-bottom: env(safe-area-inset-bottom, 12px);
|
|
5776
5534
|
}
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
gap: 8px;
|
|
5781
|
-
}
|
|
5782
|
-
|
|
5783
|
-
#input {
|
|
5535
|
+
.input-row { display: flex; gap: 8px; align-items: center; }
|
|
5536
|
+
.prompt { color: var(--success); font-family: 'SF Mono', Monaco, monospace; font-size: 14px; white-space: nowrap; }
|
|
5537
|
+
#cmd-input {
|
|
5784
5538
|
flex: 1;
|
|
5785
|
-
background:
|
|
5539
|
+
background: #21262d;
|
|
5786
5540
|
border: 1px solid var(--border);
|
|
5787
|
-
border-radius:
|
|
5788
|
-
padding: 12px
|
|
5789
|
-
color:
|
|
5790
|
-
font-family: var(--font-mono);
|
|
5541
|
+
border-radius: 12px;
|
|
5542
|
+
padding: 12px 16px;
|
|
5543
|
+
color: #e6edf3;
|
|
5791
5544
|
font-size: 16px;
|
|
5545
|
+
font-family: 'SF Mono', Monaco, monospace;
|
|
5792
5546
|
outline: none;
|
|
5793
|
-
|
|
5794
|
-
}
|
|
5795
|
-
|
|
5796
|
-
#input:focus {
|
|
5797
|
-
border-color: var(--accent);
|
|
5798
|
-
}
|
|
5799
|
-
|
|
5800
|
-
#input::placeholder {
|
|
5801
|
-
color: var(--fg-muted);
|
|
5547
|
+
-webkit-appearance: none;
|
|
5802
5548
|
}
|
|
5803
|
-
|
|
5804
|
-
#send {
|
|
5549
|
+
#cmd-input:focus { border-color: var(--accent); }
|
|
5550
|
+
#send-btn {
|
|
5805
5551
|
background: var(--accent);
|
|
5806
|
-
color:
|
|
5552
|
+
color: white;
|
|
5807
5553
|
border: none;
|
|
5808
|
-
border-radius:
|
|
5809
|
-
padding: 12px
|
|
5810
|
-
font-size:
|
|
5554
|
+
border-radius: 12px;
|
|
5555
|
+
padding: 12px 24px;
|
|
5556
|
+
font-size: 16px;
|
|
5811
5557
|
font-weight: 600;
|
|
5812
|
-
font-family: var(--font-mono);
|
|
5813
5558
|
cursor: pointer;
|
|
5814
|
-
|
|
5815
|
-
}
|
|
5816
|
-
|
|
5817
|
-
#send:active {
|
|
5818
|
-
opacity: 0.8;
|
|
5819
|
-
transform: scale(0.98);
|
|
5559
|
+
-webkit-tap-highlight-color: transparent;
|
|
5820
5560
|
}
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
background: var(--bg);
|
|
5561
|
+
#send-btn:active { opacity: 0.7; }
|
|
5562
|
+
#status-bar {
|
|
5563
|
+
background: var(--bg-secondary);
|
|
5564
|
+
border-bottom: 1px solid var(--border);
|
|
5565
|
+
padding: 8px 16px;
|
|
5827
5566
|
display: flex;
|
|
5828
|
-
|
|
5567
|
+
justify-content: space-between;
|
|
5829
5568
|
align-items: center;
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
}
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
}
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
}
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
}
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
}
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
@
|
|
5863
|
-
#input-
|
|
5864
|
-
|
|
5865
|
-
}
|
|
5866
|
-
}
|
|
5867
|
-
|
|
5868
|
-
/* Landscape adjustments */
|
|
5869
|
-
@media (max-height: 500px) {
|
|
5870
|
-
#quickkeys {
|
|
5871
|
-
grid-template-columns: repeat(12, 1fr);
|
|
5872
|
-
padding: 6px 8px;
|
|
5873
|
-
}
|
|
5874
|
-
.qkey {
|
|
5875
|
-
padding: 8px 2px;
|
|
5876
|
-
font-size: 10px;
|
|
5877
|
-
}
|
|
5878
|
-
#terminal {
|
|
5879
|
-
font-size: 12px;
|
|
5880
|
-
}
|
|
5569
|
+
font-size: 12px;
|
|
5570
|
+
padding-top: env(safe-area-inset-top, 8px);
|
|
5571
|
+
}
|
|
5572
|
+
.status-row { display: flex; align-items: center; gap: 8px; }
|
|
5573
|
+
.status-dot { width: 8px; height: 8px; border-radius: 50%; background: #8b949e; }
|
|
5574
|
+
.status-dot.connected { background: var(--success); box-shadow: 0 0 8px var(--success); }
|
|
5575
|
+
.status-dot.connecting { background: var(--accent); animation: pulse 1s infinite; }
|
|
5576
|
+
.status-dot.disconnected { background: #f85149; }
|
|
5577
|
+
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
|
|
5578
|
+
#auth-overlay {
|
|
5579
|
+
position: fixed; inset: 0; background: var(--bg-primary);
|
|
5580
|
+
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
|
5581
|
+
padding: 20px; z-index: 100;
|
|
5582
|
+
}
|
|
5583
|
+
#auth-overlay.hidden { display: none; }
|
|
5584
|
+
.auth-title { font-size: 28px; font-weight: 700; margin-bottom: 8px; }
|
|
5585
|
+
.auth-subtitle { color: #8b949e; font-size: 16px; margin-bottom: 24px; }
|
|
5586
|
+
.auth-msg {
|
|
5587
|
+
background: var(--bg-secondary); padding: 20px 28px;
|
|
5588
|
+
border-radius: 16px; border: 1px solid var(--border); text-align: center;
|
|
5589
|
+
}
|
|
5590
|
+
.auth-msg.error { color: #f85149; border-color: #f85149; }
|
|
5591
|
+
.quick-btns {
|
|
5592
|
+
display: flex; gap: 8px; margin-top: 20px; flex-wrap: wrap; justify-content: center;
|
|
5593
|
+
}
|
|
5594
|
+
.quick-btn {
|
|
5595
|
+
background: #21262d; border: 1px solid var(--border); color: #e6edf3;
|
|
5596
|
+
padding: 10px 16px; border-radius: 8px; font-size: 14px;
|
|
5597
|
+
font-family: 'SF Mono', Monaco, monospace; cursor: pointer;
|
|
5598
|
+
}
|
|
5599
|
+
.quick-btn:active { background: #30363d; }
|
|
5600
|
+
.hint { font-size: 12px; color: #8b949e; margin-top: 12px; }
|
|
5601
|
+
@media (max-width: 600px) {
|
|
5602
|
+
#input-area { padding: 10px 12px; }
|
|
5603
|
+
.quick-btn { padding: 8px 12px; font-size: 12px; }
|
|
5881
5604
|
}
|
|
5882
5605
|
</style>
|
|
5883
5606
|
</head>
|
|
5884
5607
|
<body>
|
|
5885
|
-
<div id="
|
|
5886
|
-
<div
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
<header id="header">
|
|
5892
|
-
<h1>NikCLI Remote</h1>
|
|
5893
|
-
<div id="status">
|
|
5894
|
-
<span id="status-dot" class="connecting"></span>
|
|
5895
|
-
<span id="status-text">Connecting</span>
|
|
5896
|
-
</div>
|
|
5897
|
-
</header>
|
|
5898
|
-
|
|
5899
|
-
<div id="terminal-container">
|
|
5900
|
-
<div id="terminal"></div>
|
|
5608
|
+
<div id="auth-overlay">
|
|
5609
|
+
<div class="auth-title">\u{1F4F1} NikCLI Remote</div>
|
|
5610
|
+
<div class="auth-subtitle">Full terminal emulation</div>
|
|
5611
|
+
<div id="auth-msg" class="auth-msg">
|
|
5612
|
+
<div id="auth-text">Connecting...</div>
|
|
5901
5613
|
</div>
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
<button class="
|
|
5907
|
-
<button class="
|
|
5908
|
-
<button class="qkey" data-key="\\x1b[B">\u2193</button>
|
|
5909
|
-
<button class="qkey" data-key="\\x1b[D">\u2190</button>
|
|
5910
|
-
<button class="qkey" data-key="\\x1b[C">\u2192</button>
|
|
5911
|
-
<button class="qkey" data-key="\\x1b">Esc</button>
|
|
5912
|
-
<button class="qkey" data-key="\\x03">^C</button>
|
|
5913
|
-
<button class="qkey" data-key="\\x04">^D</button>
|
|
5914
|
-
<button class="qkey" data-key="\\x1a">^Z</button>
|
|
5915
|
-
<button class="qkey" data-key="\\x0c">^L</button>
|
|
5916
|
-
<button class="qkey wide accent" data-key="\\r">Enter \u23CE</button>
|
|
5614
|
+
<div class="quick-btns">
|
|
5615
|
+
<button class="quick-btn" onclick="send('help')">/help</button>
|
|
5616
|
+
<button class="quick-btn" onclick="send('ls -la')">ls -la</button>
|
|
5617
|
+
<button class="quick-btn" onclick="send('pwd')">pwd</button>
|
|
5618
|
+
<button class="quick-btn" onclick="send('whoami')">whoami</button>
|
|
5619
|
+
<button class="quick-btn" onclick="send('clear')">clear</button>
|
|
5917
5620
|
</div>
|
|
5621
|
+
<div class="hint">Mobile keyboard to type commands</div>
|
|
5622
|
+
</div>
|
|
5918
5623
|
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
</div>
|
|
5624
|
+
<div id="status-bar">
|
|
5625
|
+
<div class="status-row">
|
|
5626
|
+
<span class="status-dot" id="status-dot"></span>
|
|
5627
|
+
<span id="status-text">Disconnected</span>
|
|
5924
5628
|
</div>
|
|
5629
|
+
<span id="session-id" style="color: #8b949e;"></span>
|
|
5925
5630
|
</div>
|
|
5926
5631
|
|
|
5927
|
-
<
|
|
5928
|
-
(function() {
|
|
5929
|
-
'use strict';
|
|
5930
|
-
|
|
5931
|
-
// Parse URL params
|
|
5932
|
-
const params = new URLSearchParams(location.search);
|
|
5933
|
-
const token = params.get('t');
|
|
5934
|
-
const sessionId = params.get('s');
|
|
5935
|
-
|
|
5936
|
-
// DOM elements
|
|
5937
|
-
const terminal = document.getElementById('terminal');
|
|
5938
|
-
const input = document.getElementById('input');
|
|
5939
|
-
const sendBtn = document.getElementById('send');
|
|
5940
|
-
const statusDot = document.getElementById('status-dot');
|
|
5941
|
-
const statusText = document.getElementById('status-text');
|
|
5942
|
-
const authScreen = document.getElementById('auth-screen');
|
|
5943
|
-
const authStatus = document.getElementById('auth-status');
|
|
5944
|
-
const notifications = document.getElementById('notifications');
|
|
5945
|
-
|
|
5946
|
-
// State
|
|
5947
|
-
let ws = null;
|
|
5948
|
-
let reconnectAttempts = 0;
|
|
5949
|
-
const maxReconnectAttempts = 5;
|
|
5950
|
-
let terminalEnabled = true;
|
|
5951
|
-
|
|
5952
|
-
// Connect to WebSocket
|
|
5953
|
-
function connect() {
|
|
5954
|
-
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
5955
|
-
ws = new WebSocket(protocol + '//' + location.host);
|
|
5632
|
+
<div id="terminal"></div>
|
|
5956
5633
|
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
const msg = JSON.parse(event.data);
|
|
5965
|
-
handleMessage(msg);
|
|
5966
|
-
} catch (e) {
|
|
5967
|
-
console.error('Parse error:', e);
|
|
5968
|
-
}
|
|
5969
|
-
};
|
|
5970
|
-
|
|
5971
|
-
ws.onclose = function() {
|
|
5972
|
-
setStatus('disconnected', 'Disconnected');
|
|
5973
|
-
if (reconnectAttempts < maxReconnectAttempts) {
|
|
5974
|
-
reconnectAttempts++;
|
|
5975
|
-
const delay = Math.min(2000 * reconnectAttempts, 10000);
|
|
5976
|
-
setTimeout(connect, delay);
|
|
5977
|
-
} else {
|
|
5978
|
-
authStatus.textContent = 'Connection failed. Refresh to retry.';
|
|
5979
|
-
authStatus.classList.add('error');
|
|
5980
|
-
authScreen.classList.remove('hidden');
|
|
5981
|
-
}
|
|
5982
|
-
};
|
|
5983
|
-
|
|
5984
|
-
ws.onerror = function() {
|
|
5985
|
-
console.error('WebSocket error');
|
|
5986
|
-
};
|
|
5987
|
-
}
|
|
5988
|
-
|
|
5989
|
-
// Handle incoming message
|
|
5990
|
-
function handleMessage(msg) {
|
|
5991
|
-
switch (msg.type) {
|
|
5992
|
-
case 'auth:required':
|
|
5993
|
-
// Already sent auth on open
|
|
5994
|
-
break;
|
|
5995
|
-
|
|
5996
|
-
case 'auth:success':
|
|
5997
|
-
authScreen.classList.add('hidden');
|
|
5998
|
-
setStatus('connected', 'Connected');
|
|
5999
|
-
reconnectAttempts = 0;
|
|
6000
|
-
terminalEnabled = msg.payload?.terminalEnabled !== false;
|
|
6001
|
-
if (terminalEnabled) {
|
|
6002
|
-
appendOutput('\\x1b[32mConnected to NikCLI\\x1b[0m\\n\\n');
|
|
6003
|
-
}
|
|
6004
|
-
break;
|
|
6005
|
-
|
|
6006
|
-
case 'auth:failed':
|
|
6007
|
-
authStatus.textContent = 'Authentication failed';
|
|
6008
|
-
authStatus.classList.add('error');
|
|
6009
|
-
break;
|
|
6010
|
-
|
|
6011
|
-
case 'terminal:output':
|
|
6012
|
-
if (msg.payload?.data) {
|
|
6013
|
-
appendOutput(msg.payload.data);
|
|
6014
|
-
}
|
|
6015
|
-
break;
|
|
6016
|
-
|
|
6017
|
-
case 'terminal:exit':
|
|
6018
|
-
appendOutput('\\n\\x1b[33m[Process exited with code ' + (msg.payload?.code || 0) + ']\\x1b[0m\\n');
|
|
6019
|
-
break;
|
|
6020
|
-
|
|
6021
|
-
case 'notification':
|
|
6022
|
-
showNotification(msg.payload);
|
|
6023
|
-
break;
|
|
6024
|
-
|
|
6025
|
-
case 'session:end':
|
|
6026
|
-
appendOutput('\\n\\x1b[31m[Session ended]\\x1b[0m\\n');
|
|
6027
|
-
setStatus('disconnected', 'Session ended');
|
|
6028
|
-
break;
|
|
6029
|
-
|
|
6030
|
-
case 'pong':
|
|
6031
|
-
// Heartbeat response
|
|
6032
|
-
break;
|
|
5634
|
+
<div id="input-area">
|
|
5635
|
+
<form class="input-row" onsubmit="return handleSubmit(event)">
|
|
5636
|
+
<span class="prompt">$</span>
|
|
5637
|
+
<input type="text" id="cmd-input" placeholder="Type command..." autocomplete="off" enterkeyhint="send" inputmode="text">
|
|
5638
|
+
<button type="submit" id="send-btn">Send</button>
|
|
5639
|
+
</form>
|
|
5640
|
+
</div>
|
|
6033
5641
|
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
5642
|
+
<!-- hterm from Google's Chromium -->
|
|
5643
|
+
<script src="https://cdn.jsdelivr.net/npm/hterm@1.1.1/lib/hterm.js"></script>
|
|
5644
|
+
<script>
|
|
5645
|
+
let ws = null, term = null, connected = false, reconnectAttempts = 0;
|
|
5646
|
+
const token = new URLSearchParams(location.search).get('t') || '';
|
|
5647
|
+
const sessionId = new URLSearchParams(location.search).get('s') || '';
|
|
5648
|
+
|
|
5649
|
+
const authOverlay = document.getElementById('auth-overlay');
|
|
5650
|
+
const authMsg = document.getElementById('auth-msg');
|
|
5651
|
+
const authText = document.getElementById('auth-text');
|
|
5652
|
+
const statusDot = document.getElementById('status-dot');
|
|
5653
|
+
const statusText = document.getElementById('status-text');
|
|
5654
|
+
const sessionSpan = document.getElementById('session-id');
|
|
5655
|
+
const cmdInput = document.getElementById('cmd-input');
|
|
5656
|
+
|
|
5657
|
+
// Initialize hterm
|
|
5658
|
+
hterm.DefaultCharWidth = 8.53;
|
|
5659
|
+
hterm.DefaultRowHeight = 17;
|
|
5660
|
+
|
|
5661
|
+
const storage = new hterm.Storage.Memory();
|
|
5662
|
+
const prefs = new hterm.Preferences(storage);
|
|
5663
|
+
prefs.set('font-size', 14);
|
|
5664
|
+
prefs.set('font-family', '"SF Mono", Monaco, Consolas, monospace');
|
|
5665
|
+
prefs.set('background-color', '#0d1117');
|
|
5666
|
+
prefs.set('foreground-color', '#e6edf3');
|
|
5667
|
+
prefs.set('cursor-color', '#3fb950');
|
|
5668
|
+
prefs.set('selection-color', '#264f78');
|
|
5669
|
+
prefs.set('scrollbar-visible', false);
|
|
5670
|
+
prefs.set('cursor-blink', true);
|
|
5671
|
+
|
|
5672
|
+
term = new hterm.Terminal(storage, prefs);
|
|
5673
|
+
term.onTerminalReady = function() {
|
|
5674
|
+
term.io.writeUTF8('\x1B[32mInitializing NikCLI Remote...\x1B[0m\r
|
|
5675
|
+
');
|
|
5676
|
+
term.io.writeUTF8('\x1B[90mType commands below\x1B[0m\r
|
|
5677
|
+
');
|
|
5678
|
+
connect();
|
|
5679
|
+
};
|
|
5680
|
+
term.decorate(document.getElementById('terminal'));
|
|
5681
|
+
term.start();
|
|
6038
5682
|
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
terminal.scrollTop = terminal.scrollHeight;
|
|
5683
|
+
// Resize handler
|
|
5684
|
+
window.addEventListener('resize', () => {
|
|
5685
|
+
const el = document.getElementById('terminal');
|
|
5686
|
+
if (term && el) {
|
|
5687
|
+
term.setHeightAndWidth(el.clientHeight / 17, el.clientWidth / 8.53);
|
|
6045
5688
|
}
|
|
5689
|
+
});
|
|
6046
5690
|
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
'30': '#6e7681', '31': '#f85149', '32': '#3fb950', '33': '#d29922',
|
|
6051
|
-
'34': '#58a6ff', '35': '#bc8cff', '36': '#76e3ea', '37': '#e6edf3',
|
|
6052
|
-
'90': '#6e7681', '91': '#f85149', '92': '#3fb950', '93': '#d29922',
|
|
6053
|
-
'94': '#58a6ff', '95': '#bc8cff', '96': '#76e3ea', '97': '#ffffff'
|
|
6054
|
-
};
|
|
6055
|
-
|
|
6056
|
-
let result = '';
|
|
6057
|
-
let currentStyle = '';
|
|
6058
|
-
|
|
6059
|
-
const parts = text.split(/\\x1b\\[([0-9;]+)m/);
|
|
6060
|
-
for (let i = 0; i < parts.length; i++) {
|
|
6061
|
-
if (i % 2 === 0) {
|
|
6062
|
-
// Text content
|
|
6063
|
-
result += escapeHtml(parts[i]);
|
|
6064
|
-
} else {
|
|
6065
|
-
// ANSI code
|
|
6066
|
-
const codes = parts[i].split(';');
|
|
6067
|
-
for (const code of codes) {
|
|
6068
|
-
if (code === '0') {
|
|
6069
|
-
if (currentStyle) {
|
|
6070
|
-
result += '</span>';
|
|
6071
|
-
currentStyle = '';
|
|
6072
|
-
}
|
|
6073
|
-
} else if (code === '1') {
|
|
6074
|
-
currentStyle = 'font-weight:bold;';
|
|
6075
|
-
result += '<span style="' + currentStyle + '">';
|
|
6076
|
-
} else if (ansiColors[code]) {
|
|
6077
|
-
if (currentStyle) result += '</span>';
|
|
6078
|
-
currentStyle = 'color:' + ansiColors[code] + ';';
|
|
6079
|
-
result += '<span style="' + currentStyle + '">';
|
|
6080
|
-
}
|
|
6081
|
-
}
|
|
6082
|
-
}
|
|
6083
|
-
}
|
|
5691
|
+
function connect() {
|
|
5692
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
5693
|
+
ws = new WebSocket(protocol + '//' + location.host + '/ws');
|
|
6084
5694
|
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
5695
|
+
ws.onopen = () => {
|
|
5696
|
+
setStatus('connecting', 'Authenticating...');
|
|
5697
|
+
ws.send(JSON.stringify({ type: 'auth', token }));
|
|
5698
|
+
reconnectAttempts = 0;
|
|
5699
|
+
};
|
|
6088
5700
|
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
.replace(/&/g, '&')
|
|
6093
|
-
.replace(/</g, '<')
|
|
6094
|
-
.replace(/>/g, '>')
|
|
6095
|
-
.replace(/"/g, '"')
|
|
6096
|
-
.replace(/\\n/g, '<br>')
|
|
6097
|
-
.replace(/ /g, ' ');
|
|
6098
|
-
}
|
|
5701
|
+
ws.onmessage = (e) => {
|
|
5702
|
+
try { handleMessage(JSON.parse(e.data)); } catch (err) { console.error(err); }
|
|
5703
|
+
};
|
|
6099
5704
|
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
}
|
|
5705
|
+
ws.onclose = () => {
|
|
5706
|
+
setStatus('disconnected', 'Disconnected');
|
|
5707
|
+
connected = false;
|
|
5708
|
+
reconnectAttempts++;
|
|
5709
|
+
setTimeout(connect, Math.min(3000, reconnectAttempts * 500));
|
|
5710
|
+
};
|
|
6106
5711
|
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
6110
|
-
ws.send(JSON.stringify({ type: 'terminal:input', data: data }));
|
|
6111
|
-
}
|
|
6112
|
-
}
|
|
5712
|
+
ws.onerror = () => setStatus('disconnected', 'Connection error');
|
|
5713
|
+
}
|
|
6113
5714
|
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
|
|
6122
|
-
|
|
5715
|
+
function handleMessage(msg) {
|
|
5716
|
+
switch (msg.type) {
|
|
5717
|
+
case 'auth:required':
|
|
5718
|
+
ws.send(JSON.stringify({ type: 'auth', token }));
|
|
5719
|
+
break;
|
|
5720
|
+
case 'auth:success':
|
|
5721
|
+
connected = true;
|
|
5722
|
+
authOverlay.classList.add('hidden');
|
|
5723
|
+
setStatus('connected', 'Connected');
|
|
5724
|
+
sessionSpan.textContent = sessionId ? 'Session: ' + sessionId : '';
|
|
5725
|
+
term.io.writeUTF8('\r
|
|
5726
|
+
\x1B[32m\u2713 Connected to NikCLI\x1B[0m\r
|
|
5727
|
+
');
|
|
5728
|
+
break;
|
|
5729
|
+
case 'auth:failed':
|
|
5730
|
+
authMsg.classList.add('error');
|
|
5731
|
+
authText.textContent = 'Authentication failed';
|
|
5732
|
+
break;
|
|
5733
|
+
case 'terminal:output':
|
|
5734
|
+
if (msg.payload?.data) term.io.writeUTF8(msg.payload.data);
|
|
5735
|
+
break;
|
|
5736
|
+
case 'terminal:exit':
|
|
5737
|
+
term.io.writeUTF8('\r
|
|
5738
|
+
\x1B[33m[Process exited: ' + (msg.payload?.code || 0) + ']\x1B[0m\r
|
|
5739
|
+
');
|
|
5740
|
+
break;
|
|
6123
5741
|
}
|
|
5742
|
+
}
|
|
6124
5743
|
|
|
6125
|
-
|
|
6126
|
-
|
|
6127
|
-
|
|
6128
|
-
|
|
6129
|
-
input.value = '';
|
|
6130
|
-
}
|
|
6131
|
-
input.focus();
|
|
6132
|
-
};
|
|
6133
|
-
|
|
6134
|
-
// Event: Enter key in input
|
|
6135
|
-
input.onkeydown = function(e) {
|
|
6136
|
-
if (e.key === 'Enter') {
|
|
6137
|
-
e.preventDefault();
|
|
6138
|
-
sendBtn.click();
|
|
6139
|
-
}
|
|
6140
|
-
};
|
|
5744
|
+
function setStatus(state, text) {
|
|
5745
|
+
statusDot.className = 'status-dot ' + state;
|
|
5746
|
+
statusText.textContent = text;
|
|
5747
|
+
}
|
|
6141
5748
|
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
send(decoded);
|
|
6154
|
-
input.focus();
|
|
6155
|
-
};
|
|
6156
|
-
});
|
|
5749
|
+
function handleSubmit(e) {
|
|
5750
|
+
e?.preventDefault();
|
|
5751
|
+
const value = cmdInput.value.trim();
|
|
5752
|
+
if (!value || !connected) return;
|
|
5753
|
+
cmdInput.value = '';
|
|
5754
|
+
term.io.writeUTF8('$ ' + value + '\r
|
|
5755
|
+
');
|
|
5756
|
+
ws.send(JSON.stringify({ type: 'terminal:input', data: value + '\r' }));
|
|
5757
|
+
setTimeout(() => cmdInput.focus(), 50);
|
|
5758
|
+
return false;
|
|
5759
|
+
}
|
|
6157
5760
|
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
5761
|
+
function send(cmd) {
|
|
5762
|
+
if (!connected) return;
|
|
5763
|
+
term.io.writeUTF8('$ ' + cmd + '\r
|
|
5764
|
+
');
|
|
5765
|
+
ws.send(JSON.stringify({ type: 'terminal:input', data: cmd + '\r' }));
|
|
5766
|
+
}
|
|
6164
5767
|
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
const rows = Math.floor(terminal.clientHeight / 18);
|
|
6170
|
-
ws.send(JSON.stringify({ type: 'terminal:resize', cols: cols, rows: rows }));
|
|
6171
|
-
}
|
|
5768
|
+
cmdInput.addEventListener('keydown', (e) => {
|
|
5769
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
5770
|
+
e.preventDefault();
|
|
5771
|
+
handleSubmit();
|
|
6172
5772
|
}
|
|
5773
|
+
});
|
|
6173
5774
|
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
// Start connection
|
|
6178
|
-
if (token) {
|
|
6179
|
-
connect();
|
|
6180
|
-
} else {
|
|
6181
|
-
authStatus.textContent = 'Invalid session URL';
|
|
6182
|
-
authStatus.classList.add('error');
|
|
6183
|
-
}
|
|
6184
|
-
})();
|
|
5775
|
+
document.getElementById('terminal')?.addEventListener('click', () => {
|
|
5776
|
+
if (connected) cmdInput.focus();
|
|
5777
|
+
});
|
|
6185
5778
|
</script>
|
|
6186
5779
|
</body>
|
|
6187
5780
|
</html>`;
|
|
@@ -6283,6 +5876,7 @@ var init_server = __esm({
|
|
|
6283
5876
|
});
|
|
6284
5877
|
this.terminal.on("data", (data) => {
|
|
6285
5878
|
this.broadcast({ type: MessageTypes.TERMINAL_OUTPUT, payload: { data } });
|
|
5879
|
+
this.emit("terminal:output", data);
|
|
6286
5880
|
});
|
|
6287
5881
|
this.terminal.on("exit", (code) => {
|
|
6288
5882
|
this.broadcast({ type: MessageTypes.TERMINAL_EXIT, payload: { code } });
|
|
@@ -6499,9 +6093,6 @@ var init_server = __esm({
|
|
|
6499
6093
|
* Handle authentication
|
|
6500
6094
|
*/
|
|
6501
6095
|
handleAuth(client, token) {
|
|
6502
|
-
console.log(`[Tunnel] Auth attempt from ${client.id}, token length: ${token?.length || 0}`);
|
|
6503
|
-
console.log(`[Tunnel] Expected secret: ${this.sessionSecret?.substring(0, 8)}...`);
|
|
6504
|
-
console.log(`[Tunnel] Received token: ${token?.substring(0, 8)}...`);
|
|
6505
6096
|
if (token === this.sessionSecret) {
|
|
6506
6097
|
client.authenticated = true;
|
|
6507
6098
|
if (this.session) {
|
|
@@ -6522,9 +6113,7 @@ var init_server = __esm({
|
|
|
6522
6113
|
this.terminal?.start();
|
|
6523
6114
|
}
|
|
6524
6115
|
this.emit("client:connected", client.device);
|
|
6525
|
-
console.log(`[Tunnel] Auth success for ${client.id}`);
|
|
6526
6116
|
} else {
|
|
6527
|
-
console.log(`[Tunnel] Auth failed for ${client.id}`);
|
|
6528
6117
|
client.ws.send(JSON.stringify({ type: MessageTypes.AUTH_FAILED, timestamp: Date.now() }));
|
|
6529
6118
|
setTimeout(() => client.ws.close(1008, "Authentication failed"), 100);
|
|
6530
6119
|
}
|
|
@@ -6648,141 +6237,12 @@ __export(index_exports, {
|
|
|
6648
6237
|
MessageTypes: () => MessageTypes,
|
|
6649
6238
|
RemoteServer: () => RemoteServer,
|
|
6650
6239
|
TerminalManager: () => TerminalManager,
|
|
6651
|
-
TunnelManager: () => TunnelManager,
|
|
6652
|
-
checkTunnelAvailability: () => checkTunnelAvailability,
|
|
6653
6240
|
createRemoteServer: () => createRemoteServer,
|
|
6654
|
-
|
|
6655
|
-
generateQR: () => generateQR,
|
|
6656
|
-
generateQRDataURL: () => generateQRDataURL,
|
|
6657
|
-
getWebClient: () => getWebClient,
|
|
6658
|
-
progressBar: () => progressBar,
|
|
6659
|
-
renderSessionCard: () => renderSessionCard
|
|
6241
|
+
getWebClient: () => getWebClient
|
|
6660
6242
|
});
|
|
6661
6243
|
module.exports = __toCommonJS(index_exports);
|
|
6662
6244
|
init_server();
|
|
6663
6245
|
init_terminal();
|
|
6664
|
-
init_tunnel();
|
|
6665
|
-
|
|
6666
|
-
// src/qrcode.ts
|
|
6667
|
-
var QRCode = null;
|
|
6668
|
-
try {
|
|
6669
|
-
QRCode = require("qrcode");
|
|
6670
|
-
} catch {
|
|
6671
|
-
}
|
|
6672
|
-
async function generateQR(url, options = {}) {
|
|
6673
|
-
if (!QRCode) {
|
|
6674
|
-
return generateFallbackQR(url);
|
|
6675
|
-
}
|
|
6676
|
-
try {
|
|
6677
|
-
const qrString = await QRCode.toString(url, {
|
|
6678
|
-
type: "terminal",
|
|
6679
|
-
small: options.small ?? true,
|
|
6680
|
-
margin: options.margin ?? 1
|
|
6681
|
-
});
|
|
6682
|
-
return qrString;
|
|
6683
|
-
} catch {
|
|
6684
|
-
return generateFallbackQR(url);
|
|
6685
|
-
}
|
|
6686
|
-
}
|
|
6687
|
-
async function generateQRDataURL(url) {
|
|
6688
|
-
if (!QRCode) return null;
|
|
6689
|
-
try {
|
|
6690
|
-
return await QRCode.toDataURL(url, {
|
|
6691
|
-
margin: 2,
|
|
6692
|
-
width: 256,
|
|
6693
|
-
color: {
|
|
6694
|
-
dark: "#000000",
|
|
6695
|
-
light: "#ffffff"
|
|
6696
|
-
}
|
|
6697
|
-
});
|
|
6698
|
-
} catch {
|
|
6699
|
-
return null;
|
|
6700
|
-
}
|
|
6701
|
-
}
|
|
6702
|
-
function generateFallbackQR(url) {
|
|
6703
|
-
return `
|
|
6704
|
-
\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
|
|
6705
|
-
\u2502 \u2502
|
|
6706
|
-
\u2502 QR Code generation unavailable \u2502
|
|
6707
|
-
\u2502 \u2502
|
|
6708
|
-
\u2502 Install 'qrcode' package or \u2502
|
|
6709
|
-
\u2502 visit the URL directly: \u2502
|
|
6710
|
-
\u2502 \u2502
|
|
6711
|
-
\u2502 ${url.substring(0, 35)}${url.length > 35 ? "..." : ""}
|
|
6712
|
-
\u2502 \u2502
|
|
6713
|
-
\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
|
|
6714
|
-
`;
|
|
6715
|
-
}
|
|
6716
|
-
async function renderSessionCard(session) {
|
|
6717
|
-
const qr = await generateQR(session.qrUrl);
|
|
6718
|
-
const statusIcon = getStatusIcon(session.status);
|
|
6719
|
-
const statusColor = getStatusColor(session.status);
|
|
6720
|
-
const lines = [
|
|
6721
|
-
"",
|
|
6722
|
-
"\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E",
|
|
6723
|
-
"\u2502 NikCLI Remote Session \u2502",
|
|
6724
|
-
"\u2570\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256F",
|
|
6725
|
-
""
|
|
6726
|
-
];
|
|
6727
|
-
const qrLines = qr.split("\n").filter((l) => l.trim());
|
|
6728
|
-
for (const line of qrLines) {
|
|
6729
|
-
lines.push(" " + line);
|
|
6730
|
-
}
|
|
6731
|
-
lines.push("");
|
|
6732
|
-
lines.push("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
6733
|
-
lines.push("");
|
|
6734
|
-
lines.push(` Session: ${session.id}`);
|
|
6735
|
-
lines.push(` Status: ${statusColor}${statusIcon} ${session.status}\x1B[0m`);
|
|
6736
|
-
lines.push(` Devices: ${session.connectedDevices.length} connected`);
|
|
6737
|
-
lines.push("");
|
|
6738
|
-
if (session.tunnelUrl) {
|
|
6739
|
-
lines.push(` \x1B[36mPublic URL:\x1B[0m`);
|
|
6740
|
-
lines.push(` ${session.tunnelUrl}`);
|
|
6741
|
-
} else {
|
|
6742
|
-
lines.push(` \x1B[36mLocal URL:\x1B[0m`);
|
|
6743
|
-
lines.push(` ${session.localUrl}`);
|
|
6744
|
-
}
|
|
6745
|
-
lines.push("");
|
|
6746
|
-
lines.push(` \x1B[90mScan QR code or open URL on your phone\x1B[0m`);
|
|
6747
|
-
lines.push("");
|
|
6748
|
-
lines.push("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
6749
|
-
lines.push(" [q] Stop [r] Refresh [c] Copy URL");
|
|
6750
|
-
lines.push("");
|
|
6751
|
-
return lines.join("\n");
|
|
6752
|
-
}
|
|
6753
|
-
function getStatusIcon(status) {
|
|
6754
|
-
const icons = {
|
|
6755
|
-
starting: "\u25EF",
|
|
6756
|
-
waiting: "\u25C9",
|
|
6757
|
-
connected: "\u25CF",
|
|
6758
|
-
stopped: "\u25CB",
|
|
6759
|
-
error: "\u2716"
|
|
6760
|
-
};
|
|
6761
|
-
return icons[status] || "?";
|
|
6762
|
-
}
|
|
6763
|
-
function getStatusColor(status) {
|
|
6764
|
-
const colors = {
|
|
6765
|
-
starting: "\x1B[33m",
|
|
6766
|
-
// Yellow
|
|
6767
|
-
waiting: "\x1B[33m",
|
|
6768
|
-
// Yellow
|
|
6769
|
-
connected: "\x1B[32m",
|
|
6770
|
-
// Green
|
|
6771
|
-
stopped: "\x1B[90m",
|
|
6772
|
-
// Gray
|
|
6773
|
-
error: "\x1B[31m"
|
|
6774
|
-
// Red
|
|
6775
|
-
};
|
|
6776
|
-
return colors[status] || "";
|
|
6777
|
-
}
|
|
6778
|
-
function progressBar(current, total, width = 30) {
|
|
6779
|
-
const percent = Math.round(current / total * 100);
|
|
6780
|
-
const filled = Math.round(current / total * width);
|
|
6781
|
-
const empty = width - filled;
|
|
6782
|
-
return `[${"\u2588".repeat(filled)}${"\u2591".repeat(empty)}] ${percent}%`;
|
|
6783
|
-
}
|
|
6784
|
-
|
|
6785
|
-
// src/index.ts
|
|
6786
6246
|
init_web_client();
|
|
6787
6247
|
init_types();
|
|
6788
6248
|
async function createRemoteServer(config = {}) {
|
|
@@ -6797,13 +6257,6 @@ async function createRemoteServer(config = {}) {
|
|
|
6797
6257
|
MessageTypes,
|
|
6798
6258
|
RemoteServer,
|
|
6799
6259
|
TerminalManager,
|
|
6800
|
-
TunnelManager,
|
|
6801
|
-
checkTunnelAvailability,
|
|
6802
6260
|
createRemoteServer,
|
|
6803
|
-
|
|
6804
|
-
generateQR,
|
|
6805
|
-
generateQRDataURL,
|
|
6806
|
-
getWebClient,
|
|
6807
|
-
progressBar,
|
|
6808
|
-
renderSessionCard
|
|
6261
|
+
getWebClient
|
|
6809
6262
|
});
|