nikcli-remote 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-FYVPBMXV.js → chunk-3IFHAOGG.js} +588 -231
- package/dist/index.cjs +587 -230
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/{server-OYMSDTRP.js → server-MBJQBTJF.js} +1 -1
- package/package.json +1 -1
- package/src/web-client.ts +593 -236
package/dist/index.cjs
CHANGED
|
@@ -5503,292 +5503,649 @@ function getWebClient() {
|
|
|
5503
5503
|
<html lang="en">
|
|
5504
5504
|
<head>
|
|
5505
5505
|
<meta charset="UTF-8">
|
|
5506
|
-
<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">
|
|
5507
5507
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
5508
|
-
<meta name="
|
|
5508
|
+
<meta name="mobile-web-app-capable" content="yes">
|
|
5509
|
+
<meta name="theme-color" content="#0d1117">
|
|
5509
5510
|
<title>NikCLI Remote</title>
|
|
5510
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
|
|
5511
5511
|
<style>
|
|
5512
|
-
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5513
5512
|
:root {
|
|
5514
|
-
--bg
|
|
5513
|
+
--bg: #0d1117;
|
|
5515
5514
|
--bg-secondary: #161b22;
|
|
5515
|
+
--fg: #e6edf3;
|
|
5516
|
+
--fg-muted: #8b949e;
|
|
5516
5517
|
--accent: #58a6ff;
|
|
5517
5518
|
--success: #3fb950;
|
|
5519
|
+
--warning: #d29922;
|
|
5520
|
+
--error: #f85149;
|
|
5518
5521
|
--border: #30363d;
|
|
5522
|
+
--font-mono: 'SF Mono', 'Fira Code', 'Consolas', monospace;
|
|
5519
5523
|
}
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5524
|
+
|
|
5525
|
+
* {
|
|
5526
|
+
box-sizing: border-box;
|
|
5527
|
+
margin: 0;
|
|
5528
|
+
padding: 0;
|
|
5529
|
+
-webkit-tap-highlight-color: transparent;
|
|
5530
|
+
}
|
|
5531
|
+
|
|
5532
|
+
html, body {
|
|
5533
|
+
height: 100%;
|
|
5534
|
+
background: var(--bg);
|
|
5535
|
+
color: var(--fg);
|
|
5536
|
+
font-family: var(--font-mono);
|
|
5537
|
+
font-size: 14px;
|
|
5538
|
+
overflow: hidden;
|
|
5539
|
+
touch-action: manipulation;
|
|
5540
|
+
}
|
|
5541
|
+
|
|
5542
|
+
#app {
|
|
5525
5543
|
display: flex;
|
|
5526
5544
|
flex-direction: column;
|
|
5545
|
+
height: 100%;
|
|
5546
|
+
height: 100dvh;
|
|
5527
5547
|
}
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
5548
|
+
|
|
5549
|
+
/* Header */
|
|
5550
|
+
#header {
|
|
5551
|
+
display: flex;
|
|
5552
|
+
align-items: center;
|
|
5553
|
+
justify-content: space-between;
|
|
5532
5554
|
padding: 12px 16px;
|
|
5555
|
+
background: var(--bg-secondary);
|
|
5556
|
+
border-bottom: 1px solid var(--border);
|
|
5533
5557
|
flex-shrink: 0;
|
|
5534
|
-
padding-bottom: env(safe-area-inset-bottom, 12px);
|
|
5535
5558
|
}
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5559
|
+
|
|
5560
|
+
#header h1 {
|
|
5561
|
+
font-size: 16px;
|
|
5562
|
+
font-weight: 600;
|
|
5563
|
+
color: var(--accent);
|
|
5564
|
+
display: flex;
|
|
5565
|
+
align-items: center;
|
|
5566
|
+
gap: 8px;
|
|
5567
|
+
}
|
|
5568
|
+
|
|
5569
|
+
#header h1::before {
|
|
5570
|
+
content: '';
|
|
5571
|
+
width: 10px;
|
|
5572
|
+
height: 10px;
|
|
5573
|
+
background: var(--accent);
|
|
5574
|
+
border-radius: 2px;
|
|
5575
|
+
}
|
|
5576
|
+
|
|
5577
|
+
#status {
|
|
5578
|
+
display: flex;
|
|
5579
|
+
align-items: center;
|
|
5580
|
+
gap: 6px;
|
|
5581
|
+
font-size: 12px;
|
|
5582
|
+
color: var(--fg-muted);
|
|
5583
|
+
}
|
|
5584
|
+
|
|
5585
|
+
#status-dot {
|
|
5586
|
+
width: 8px;
|
|
5587
|
+
height: 8px;
|
|
5588
|
+
border-radius: 50%;
|
|
5589
|
+
background: var(--error);
|
|
5590
|
+
transition: background 0.3s;
|
|
5591
|
+
}
|
|
5592
|
+
|
|
5593
|
+
#status-dot.connected {
|
|
5594
|
+
background: var(--success);
|
|
5595
|
+
}
|
|
5596
|
+
|
|
5597
|
+
#status-dot.connecting {
|
|
5598
|
+
background: var(--warning);
|
|
5599
|
+
animation: pulse 1s infinite;
|
|
5600
|
+
}
|
|
5601
|
+
|
|
5602
|
+
@keyframes pulse {
|
|
5603
|
+
0%, 100% { opacity: 1; }
|
|
5604
|
+
50% { opacity: 0.5; }
|
|
5605
|
+
}
|
|
5606
|
+
|
|
5607
|
+
/* Terminal */
|
|
5608
|
+
#terminal-container {
|
|
5539
5609
|
flex: 1;
|
|
5540
|
-
|
|
5610
|
+
overflow: hidden;
|
|
5611
|
+
position: relative;
|
|
5612
|
+
}
|
|
5613
|
+
|
|
5614
|
+
#terminal {
|
|
5615
|
+
height: 100%;
|
|
5616
|
+
padding: 12px;
|
|
5617
|
+
overflow-y: auto;
|
|
5618
|
+
overflow-x: hidden;
|
|
5619
|
+
font-size: 13px;
|
|
5620
|
+
line-height: 1.5;
|
|
5621
|
+
white-space: pre-wrap;
|
|
5622
|
+
word-break: break-all;
|
|
5623
|
+
-webkit-overflow-scrolling: touch;
|
|
5624
|
+
}
|
|
5625
|
+
|
|
5626
|
+
#terminal::-webkit-scrollbar {
|
|
5627
|
+
width: 6px;
|
|
5628
|
+
}
|
|
5629
|
+
|
|
5630
|
+
#terminal::-webkit-scrollbar-track {
|
|
5631
|
+
background: var(--bg);
|
|
5632
|
+
}
|
|
5633
|
+
|
|
5634
|
+
#terminal::-webkit-scrollbar-thumb {
|
|
5635
|
+
background: var(--border);
|
|
5636
|
+
border-radius: 3px;
|
|
5637
|
+
}
|
|
5638
|
+
|
|
5639
|
+
.cursor {
|
|
5640
|
+
display: inline-block;
|
|
5641
|
+
width: 8px;
|
|
5642
|
+
height: 16px;
|
|
5643
|
+
background: var(--fg);
|
|
5644
|
+
animation: blink 1s step-end infinite;
|
|
5645
|
+
vertical-align: text-bottom;
|
|
5646
|
+
}
|
|
5647
|
+
|
|
5648
|
+
@keyframes blink {
|
|
5649
|
+
50% { opacity: 0; }
|
|
5650
|
+
}
|
|
5651
|
+
|
|
5652
|
+
/* Notifications */
|
|
5653
|
+
#notifications {
|
|
5654
|
+
position: fixed;
|
|
5655
|
+
top: 60px;
|
|
5656
|
+
left: 12px;
|
|
5657
|
+
right: 12px;
|
|
5658
|
+
z-index: 1000;
|
|
5659
|
+
pointer-events: none;
|
|
5660
|
+
}
|
|
5661
|
+
|
|
5662
|
+
.notification {
|
|
5663
|
+
background: var(--bg-secondary);
|
|
5541
5664
|
border: 1px solid var(--border);
|
|
5542
|
-
border-radius:
|
|
5665
|
+
border-radius: 8px;
|
|
5543
5666
|
padding: 12px 16px;
|
|
5544
|
-
|
|
5667
|
+
margin-bottom: 8px;
|
|
5668
|
+
animation: slideIn 0.3s ease;
|
|
5669
|
+
pointer-events: auto;
|
|
5670
|
+
box-shadow: 0 4px 12px rgba(0,0,0,0.4);
|
|
5671
|
+
}
|
|
5672
|
+
|
|
5673
|
+
.notification.success { border-left: 3px solid var(--success); }
|
|
5674
|
+
.notification.error { border-left: 3px solid var(--error); }
|
|
5675
|
+
.notification.warning { border-left: 3px solid var(--warning); }
|
|
5676
|
+
.notification.info { border-left: 3px solid var(--accent); }
|
|
5677
|
+
|
|
5678
|
+
.notification h4 {
|
|
5679
|
+
font-size: 14px;
|
|
5680
|
+
font-weight: 600;
|
|
5681
|
+
margin-bottom: 4px;
|
|
5682
|
+
}
|
|
5683
|
+
|
|
5684
|
+
.notification p {
|
|
5685
|
+
font-size: 12px;
|
|
5686
|
+
color: var(--fg-muted);
|
|
5687
|
+
}
|
|
5688
|
+
|
|
5689
|
+
@keyframes slideIn {
|
|
5690
|
+
from { transform: translateY(-20px); opacity: 0; }
|
|
5691
|
+
to { transform: translateY(0); opacity: 1; }
|
|
5692
|
+
}
|
|
5693
|
+
|
|
5694
|
+
/* Quick Keys */
|
|
5695
|
+
#quickkeys {
|
|
5696
|
+
display: grid;
|
|
5697
|
+
grid-template-columns: repeat(6, 1fr);
|
|
5698
|
+
gap: 6px;
|
|
5699
|
+
padding: 8px 12px;
|
|
5700
|
+
background: var(--bg-secondary);
|
|
5701
|
+
border-top: 1px solid var(--border);
|
|
5702
|
+
flex-shrink: 0;
|
|
5703
|
+
}
|
|
5704
|
+
|
|
5705
|
+
.qkey {
|
|
5706
|
+
background: var(--bg);
|
|
5707
|
+
border: 1px solid var(--border);
|
|
5708
|
+
border-radius: 6px;
|
|
5709
|
+
padding: 10px 4px;
|
|
5710
|
+
color: var(--fg);
|
|
5711
|
+
font-size: 11px;
|
|
5712
|
+
font-family: var(--font-mono);
|
|
5713
|
+
text-align: center;
|
|
5714
|
+
cursor: pointer;
|
|
5715
|
+
user-select: none;
|
|
5716
|
+
transition: background 0.1s, transform 0.1s;
|
|
5717
|
+
}
|
|
5718
|
+
|
|
5719
|
+
.qkey:active {
|
|
5720
|
+
background: var(--border);
|
|
5721
|
+
transform: scale(0.95);
|
|
5722
|
+
}
|
|
5723
|
+
|
|
5724
|
+
.qkey.wide {
|
|
5725
|
+
grid-column: span 2;
|
|
5726
|
+
}
|
|
5727
|
+
|
|
5728
|
+
.qkey.accent {
|
|
5729
|
+
background: var(--accent);
|
|
5730
|
+
border-color: var(--accent);
|
|
5731
|
+
color: #fff;
|
|
5732
|
+
}
|
|
5733
|
+
|
|
5734
|
+
/* Input */
|
|
5735
|
+
#input-container {
|
|
5736
|
+
padding: 12px;
|
|
5737
|
+
background: var(--bg-secondary);
|
|
5738
|
+
border-top: 1px solid var(--border);
|
|
5739
|
+
flex-shrink: 0;
|
|
5740
|
+
}
|
|
5741
|
+
|
|
5742
|
+
#input-row {
|
|
5743
|
+
display: flex;
|
|
5744
|
+
gap: 8px;
|
|
5745
|
+
}
|
|
5746
|
+
|
|
5747
|
+
#input {
|
|
5748
|
+
flex: 1;
|
|
5749
|
+
background: var(--bg);
|
|
5750
|
+
border: 1px solid var(--border);
|
|
5751
|
+
border-radius: 8px;
|
|
5752
|
+
padding: 12px 14px;
|
|
5753
|
+
color: var(--fg);
|
|
5754
|
+
font-family: var(--font-mono);
|
|
5545
5755
|
font-size: 16px;
|
|
5546
|
-
font-family: 'SF Mono', Monaco, monospace;
|
|
5547
5756
|
outline: none;
|
|
5548
|
-
|
|
5757
|
+
transition: border-color 0.2s;
|
|
5758
|
+
}
|
|
5759
|
+
|
|
5760
|
+
#input:focus {
|
|
5761
|
+
border-color: var(--accent);
|
|
5549
5762
|
}
|
|
5550
|
-
|
|
5551
|
-
#
|
|
5763
|
+
|
|
5764
|
+
#input::placeholder {
|
|
5765
|
+
color: var(--fg-muted);
|
|
5766
|
+
}
|
|
5767
|
+
|
|
5768
|
+
#send {
|
|
5552
5769
|
background: var(--accent);
|
|
5553
|
-
color:
|
|
5770
|
+
color: #fff;
|
|
5554
5771
|
border: none;
|
|
5555
|
-
border-radius:
|
|
5556
|
-
padding: 12px
|
|
5557
|
-
font-size:
|
|
5772
|
+
border-radius: 8px;
|
|
5773
|
+
padding: 12px 20px;
|
|
5774
|
+
font-size: 14px;
|
|
5558
5775
|
font-weight: 600;
|
|
5776
|
+
font-family: var(--font-mono);
|
|
5559
5777
|
cursor: pointer;
|
|
5560
|
-
|
|
5778
|
+
transition: opacity 0.2s, transform 0.1s;
|
|
5561
5779
|
}
|
|
5562
|
-
|
|
5563
|
-
#
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5780
|
+
|
|
5781
|
+
#send:active {
|
|
5782
|
+
opacity: 0.8;
|
|
5783
|
+
transform: scale(0.98);
|
|
5784
|
+
}
|
|
5785
|
+
|
|
5786
|
+
/* Auth Screen */
|
|
5787
|
+
#auth-screen {
|
|
5788
|
+
position: fixed;
|
|
5789
|
+
inset: 0;
|
|
5790
|
+
background: var(--bg);
|
|
5567
5791
|
display: flex;
|
|
5568
|
-
|
|
5792
|
+
flex-direction: column;
|
|
5569
5793
|
align-items: center;
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
}
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
}
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
@
|
|
5603
|
-
#input-
|
|
5604
|
-
|
|
5794
|
+
justify-content: center;
|
|
5795
|
+
gap: 20px;
|
|
5796
|
+
z-index: 2000;
|
|
5797
|
+
}
|
|
5798
|
+
|
|
5799
|
+
#auth-screen.hidden {
|
|
5800
|
+
display: none;
|
|
5801
|
+
}
|
|
5802
|
+
|
|
5803
|
+
.spinner {
|
|
5804
|
+
width: 40px;
|
|
5805
|
+
height: 40px;
|
|
5806
|
+
border: 3px solid var(--border);
|
|
5807
|
+
border-top-color: var(--accent);
|
|
5808
|
+
border-radius: 50%;
|
|
5809
|
+
animation: spin 1s linear infinite;
|
|
5810
|
+
}
|
|
5811
|
+
|
|
5812
|
+
@keyframes spin {
|
|
5813
|
+
to { transform: rotate(360deg); }
|
|
5814
|
+
}
|
|
5815
|
+
|
|
5816
|
+
#auth-screen p {
|
|
5817
|
+
color: var(--fg-muted);
|
|
5818
|
+
font-size: 14px;
|
|
5819
|
+
}
|
|
5820
|
+
|
|
5821
|
+
#auth-screen .error {
|
|
5822
|
+
color: var(--error);
|
|
5823
|
+
}
|
|
5824
|
+
|
|
5825
|
+
/* Safe area padding for notched devices */
|
|
5826
|
+
@supports (padding: env(safe-area-inset-bottom)) {
|
|
5827
|
+
#input-container {
|
|
5828
|
+
padding-bottom: calc(12px + env(safe-area-inset-bottom));
|
|
5829
|
+
}
|
|
5830
|
+
}
|
|
5831
|
+
|
|
5832
|
+
/* Landscape adjustments */
|
|
5833
|
+
@media (max-height: 500px) {
|
|
5834
|
+
#quickkeys {
|
|
5835
|
+
grid-template-columns: repeat(12, 1fr);
|
|
5836
|
+
padding: 6px 8px;
|
|
5837
|
+
}
|
|
5838
|
+
.qkey {
|
|
5839
|
+
padding: 8px 2px;
|
|
5840
|
+
font-size: 10px;
|
|
5841
|
+
}
|
|
5842
|
+
#terminal {
|
|
5843
|
+
font-size: 12px;
|
|
5844
|
+
}
|
|
5605
5845
|
}
|
|
5606
5846
|
</style>
|
|
5607
5847
|
</head>
|
|
5608
5848
|
<body>
|
|
5609
|
-
<div id="
|
|
5610
|
-
<div
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
<div id="auth-text">Connecting...</div>
|
|
5849
|
+
<div id="app">
|
|
5850
|
+
<div id="auth-screen">
|
|
5851
|
+
<div class="spinner"></div>
|
|
5852
|
+
<p id="auth-status">Connecting to NikCLI...</p>
|
|
5614
5853
|
</div>
|
|
5615
|
-
<div class="quick-btns">
|
|
5616
|
-
<button class="quick-btn" onclick="send('help')">/help</button>
|
|
5617
|
-
<button class="quick-btn" onclick="send('ls -la')">ls -la</button>
|
|
5618
|
-
<button class="quick-btn" onclick="send('pwd')">pwd</button>
|
|
5619
|
-
<button class="quick-btn" onclick="send('whoami')">whoami</button>
|
|
5620
|
-
<button class="quick-btn" onclick="send('clear')">clear</button>
|
|
5621
|
-
</div>
|
|
5622
|
-
<div class="hint">Mobile keyboard to type commands</div>
|
|
5623
|
-
</div>
|
|
5624
5854
|
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
<
|
|
5628
|
-
|
|
5855
|
+
<header id="header">
|
|
5856
|
+
<h1>NikCLI Remote</h1>
|
|
5857
|
+
<div id="status">
|
|
5858
|
+
<span id="status-dot" class="connecting"></span>
|
|
5859
|
+
<span id="status-text">Connecting</span>
|
|
5860
|
+
</div>
|
|
5861
|
+
</header>
|
|
5862
|
+
|
|
5863
|
+
<div id="terminal-container">
|
|
5864
|
+
<div id="terminal"></div>
|
|
5629
5865
|
</div>
|
|
5630
|
-
<span id="session-id" style="color: #8b949e;"></span>
|
|
5631
|
-
</div>
|
|
5632
5866
|
|
|
5633
|
-
|
|
5867
|
+
<div id="notifications"></div>
|
|
5634
5868
|
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
<
|
|
5638
|
-
<
|
|
5639
|
-
<button
|
|
5640
|
-
|
|
5869
|
+
<div id="quickkeys">
|
|
5870
|
+
<button class="qkey" data-key="\\t">Tab</button>
|
|
5871
|
+
<button class="qkey" data-key="\\x1b[A">\u2191</button>
|
|
5872
|
+
<button class="qkey" data-key="\\x1b[B">\u2193</button>
|
|
5873
|
+
<button class="qkey" data-key="\\x1b[D">\u2190</button>
|
|
5874
|
+
<button class="qkey" data-key="\\x1b[C">\u2192</button>
|
|
5875
|
+
<button class="qkey" data-key="\\x1b">Esc</button>
|
|
5876
|
+
<button class="qkey" data-key="\\x03">^C</button>
|
|
5877
|
+
<button class="qkey" data-key="\\x04">^D</button>
|
|
5878
|
+
<button class="qkey" data-key="\\x1a">^Z</button>
|
|
5879
|
+
<button class="qkey" data-key="\\x0c">^L</button>
|
|
5880
|
+
<button class="qkey wide accent" data-key="\\r">Enter \u23CE</button>
|
|
5881
|
+
</div>
|
|
5882
|
+
|
|
5883
|
+
<div id="input-container">
|
|
5884
|
+
<div id="input-row">
|
|
5885
|
+
<input type="text" id="input" placeholder="Type command..." autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">
|
|
5886
|
+
<button id="send">Send</button>
|
|
5887
|
+
</div>
|
|
5888
|
+
</div>
|
|
5641
5889
|
</div>
|
|
5642
5890
|
|
|
5643
|
-
<script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"></script>
|
|
5644
|
-
<script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
|
|
5645
5891
|
<script>
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
const sessionId = new URLSearchParams(location.search).get('s') || '';
|
|
5649
|
-
|
|
5650
|
-
const authOverlay = document.getElementById('auth-overlay');
|
|
5651
|
-
const authMsg = document.getElementById('auth-msg');
|
|
5652
|
-
const authText = document.getElementById('auth-text');
|
|
5653
|
-
const statusDot = document.getElementById('status-dot');
|
|
5654
|
-
const statusText = document.getElementById('status-text');
|
|
5655
|
-
const sessionSpan = document.getElementById('session-id');
|
|
5656
|
-
const cmdInput = document.getElementById('cmd-input');
|
|
5657
|
-
|
|
5658
|
-
// Initialize xterm.js
|
|
5659
|
-
term = new Terminal({
|
|
5660
|
-
cursorBlink: true,
|
|
5661
|
-
fontSize: 14,
|
|
5662
|
-
fontFamily: '"SF Mono", Monaco, Consolas, monospace',
|
|
5663
|
-
theme: {
|
|
5664
|
-
background: '#0d1117',
|
|
5665
|
-
foreground: '#e6edf3',
|
|
5666
|
-
cursor: '#3fb950',
|
|
5667
|
-
selectionBackground: '#264f78',
|
|
5668
|
-
black: '#484f58',
|
|
5669
|
-
red: '#f85149',
|
|
5670
|
-
green: '#3fb950',
|
|
5671
|
-
yellow: '#d29922',
|
|
5672
|
-
blue: '#58a6ff',
|
|
5673
|
-
magenta: '#a371f7',
|
|
5674
|
-
cyan: '#39c5cf',
|
|
5675
|
-
white: '#e6edf3',
|
|
5676
|
-
brightBlack: '#6e7681',
|
|
5677
|
-
brightRed: '#ffa198',
|
|
5678
|
-
brightGreen: '#7ee787',
|
|
5679
|
-
brightYellow: '#f0883e',
|
|
5680
|
-
brightBlue: '#79c0ff',
|
|
5681
|
-
brightMagenta: '#d2a8ff',
|
|
5682
|
-
brightCyan: '#56d4db',
|
|
5683
|
-
brightWhite: '#f0f6fc'
|
|
5684
|
-
},
|
|
5685
|
-
convertEol: true
|
|
5686
|
-
});
|
|
5892
|
+
(function() {
|
|
5893
|
+
'use strict';
|
|
5687
5894
|
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
term.writeln('Initializing NikCLI Remote...');
|
|
5693
|
-
term.writeln('Type commands below');
|
|
5694
|
-
|
|
5695
|
-
// Resize handler
|
|
5696
|
-
window.addEventListener('resize', () => {
|
|
5697
|
-
clearTimeout(window.resizeTimer);
|
|
5698
|
-
window.resizeTimer = setTimeout(() => fitAddon.fit(), 100);
|
|
5699
|
-
});
|
|
5895
|
+
// Parse URL params
|
|
5896
|
+
const params = new URLSearchParams(location.search);
|
|
5897
|
+
const token = params.get('t');
|
|
5898
|
+
const sessionId = params.get('s');
|
|
5700
5899
|
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5900
|
+
// DOM elements
|
|
5901
|
+
const terminal = document.getElementById('terminal');
|
|
5902
|
+
const input = document.getElementById('input');
|
|
5903
|
+
const sendBtn = document.getElementById('send');
|
|
5904
|
+
const statusDot = document.getElementById('status-dot');
|
|
5905
|
+
const statusText = document.getElementById('status-text');
|
|
5906
|
+
const authScreen = document.getElementById('auth-screen');
|
|
5907
|
+
const authStatus = document.getElementById('auth-status');
|
|
5908
|
+
const notifications = document.getElementById('notifications');
|
|
5708
5909
|
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5910
|
+
// State
|
|
5911
|
+
let ws = null;
|
|
5912
|
+
let reconnectAttempts = 0;
|
|
5913
|
+
const maxReconnectAttempts = 5;
|
|
5914
|
+
let terminalEnabled = true;
|
|
5712
5915
|
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
};
|
|
5916
|
+
// Connect to WebSocket
|
|
5917
|
+
function connect() {
|
|
5918
|
+
const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
5919
|
+
ws = new WebSocket(protocol + '//' + location.host);
|
|
5718
5920
|
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5921
|
+
ws.onopen = function() {
|
|
5922
|
+
setStatus('connecting', 'Authenticating...');
|
|
5923
|
+
ws.send(JSON.stringify({ type: 'auth', token: token }));
|
|
5924
|
+
};
|
|
5722
5925
|
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5926
|
+
ws.onmessage = function(event) {
|
|
5927
|
+
try {
|
|
5928
|
+
const msg = JSON.parse(event.data);
|
|
5929
|
+
handleMessage(msg);
|
|
5930
|
+
} catch (e) {
|
|
5931
|
+
console.error('Parse error:', e);
|
|
5932
|
+
}
|
|
5933
|
+
};
|
|
5729
5934
|
|
|
5730
|
-
|
|
5731
|
-
|
|
5935
|
+
ws.onclose = function() {
|
|
5936
|
+
setStatus('disconnected', 'Disconnected');
|
|
5937
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
5938
|
+
reconnectAttempts++;
|
|
5939
|
+
const delay = Math.min(2000 * reconnectAttempts, 10000);
|
|
5940
|
+
setTimeout(connect, delay);
|
|
5941
|
+
} else {
|
|
5942
|
+
authStatus.textContent = 'Connection failed. Refresh to retry.';
|
|
5943
|
+
authStatus.classList.add('error');
|
|
5944
|
+
authScreen.classList.remove('hidden');
|
|
5945
|
+
}
|
|
5946
|
+
};
|
|
5732
5947
|
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
ws.send(JSON.stringify({ type: 'auth', token }));
|
|
5737
|
-
break;
|
|
5738
|
-
case 'auth:success':
|
|
5739
|
-
connected = true;
|
|
5740
|
-
authOverlay.classList.add('hidden');
|
|
5741
|
-
setStatus('connected', 'Connected');
|
|
5742
|
-
sessionSpan.textContent = sessionId ? 'Session: ' + sessionId : '';
|
|
5743
|
-
term.writeln('\u2713 Connected to NikCLI');
|
|
5744
|
-
break;
|
|
5745
|
-
case 'auth:failed':
|
|
5746
|
-
authMsg.classList.add('error');
|
|
5747
|
-
authText.textContent = 'Authentication failed';
|
|
5748
|
-
break;
|
|
5749
|
-
case 'terminal:output':
|
|
5750
|
-
if (msg.payload?.data) term.write(msg.payload.data);
|
|
5751
|
-
break;
|
|
5752
|
-
case 'terminal:exit':
|
|
5753
|
-
term.writeln('Process exited: ' + (msg.payload?.code || 0) + '');
|
|
5754
|
-
break;
|
|
5948
|
+
ws.onerror = function() {
|
|
5949
|
+
console.error('WebSocket error');
|
|
5950
|
+
};
|
|
5755
5951
|
}
|
|
5756
|
-
}
|
|
5757
5952
|
|
|
5758
|
-
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5953
|
+
// Handle incoming message
|
|
5954
|
+
function handleMessage(msg) {
|
|
5955
|
+
switch (msg.type) {
|
|
5956
|
+
case 'auth:required':
|
|
5957
|
+
// Already sent auth on open
|
|
5958
|
+
break;
|
|
5762
5959
|
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
}
|
|
5960
|
+
case 'auth:success':
|
|
5961
|
+
authScreen.classList.add('hidden');
|
|
5962
|
+
setStatus('connected', 'Connected');
|
|
5963
|
+
reconnectAttempts = 0;
|
|
5964
|
+
terminalEnabled = msg.payload?.terminalEnabled !== false;
|
|
5965
|
+
if (terminalEnabled) {
|
|
5966
|
+
appendOutput('\\x1b[32mConnected to NikCLI\\x1b[0m\\n\\n');
|
|
5967
|
+
}
|
|
5968
|
+
break;
|
|
5773
5969
|
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5970
|
+
case 'auth:failed':
|
|
5971
|
+
authStatus.textContent = 'Authentication failed';
|
|
5972
|
+
authStatus.classList.add('error');
|
|
5973
|
+
break;
|
|
5974
|
+
|
|
5975
|
+
case 'terminal:output':
|
|
5976
|
+
if (msg.payload?.data) {
|
|
5977
|
+
appendOutput(msg.payload.data);
|
|
5978
|
+
}
|
|
5979
|
+
break;
|
|
5980
|
+
|
|
5981
|
+
case 'terminal:exit':
|
|
5982
|
+
appendOutput('\\n\\x1b[33m[Process exited with code ' + (msg.payload?.code || 0) + ']\\x1b[0m\\n');
|
|
5983
|
+
break;
|
|
5984
|
+
|
|
5985
|
+
case 'notification':
|
|
5986
|
+
showNotification(msg.payload);
|
|
5987
|
+
break;
|
|
5988
|
+
|
|
5989
|
+
case 'session:end':
|
|
5990
|
+
appendOutput('\\n\\x1b[31m[Session ended]\\x1b[0m\\n');
|
|
5991
|
+
setStatus('disconnected', 'Session ended');
|
|
5992
|
+
break;
|
|
5779
5993
|
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5994
|
+
case 'pong':
|
|
5995
|
+
// Heartbeat response
|
|
5996
|
+
break;
|
|
5997
|
+
|
|
5998
|
+
default:
|
|
5999
|
+
console.log('Unknown message:', msg.type);
|
|
6000
|
+
}
|
|
5784
6001
|
}
|
|
5785
|
-
});
|
|
5786
6002
|
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
6003
|
+
// Append text to terminal with ANSI support
|
|
6004
|
+
function appendOutput(text) {
|
|
6005
|
+
// Simple ANSI to HTML conversion
|
|
6006
|
+
const html = ansiToHtml(text);
|
|
6007
|
+
terminal.innerHTML += html;
|
|
6008
|
+
terminal.scrollTop = terminal.scrollHeight;
|
|
6009
|
+
}
|
|
6010
|
+
|
|
6011
|
+
// Basic ANSI to HTML
|
|
6012
|
+
function ansiToHtml(text) {
|
|
6013
|
+
const ansiColors = {
|
|
6014
|
+
'30': '#6e7681', '31': '#f85149', '32': '#3fb950', '33': '#d29922',
|
|
6015
|
+
'34': '#58a6ff', '35': '#bc8cff', '36': '#76e3ea', '37': '#e6edf3',
|
|
6016
|
+
'90': '#6e7681', '91': '#f85149', '92': '#3fb950', '93': '#d29922',
|
|
6017
|
+
'94': '#58a6ff', '95': '#bc8cff', '96': '#76e3ea', '97': '#ffffff'
|
|
6018
|
+
};
|
|
6019
|
+
|
|
6020
|
+
let result = '';
|
|
6021
|
+
let currentStyle = '';
|
|
6022
|
+
|
|
6023
|
+
const parts = text.split(/\\x1b\\[([0-9;]+)m/);
|
|
6024
|
+
for (let i = 0; i < parts.length; i++) {
|
|
6025
|
+
if (i % 2 === 0) {
|
|
6026
|
+
// Text content
|
|
6027
|
+
result += escapeHtml(parts[i]);
|
|
6028
|
+
} else {
|
|
6029
|
+
// ANSI code
|
|
6030
|
+
const codes = parts[i].split(';');
|
|
6031
|
+
for (const code of codes) {
|
|
6032
|
+
if (code === '0') {
|
|
6033
|
+
if (currentStyle) {
|
|
6034
|
+
result += '</span>';
|
|
6035
|
+
currentStyle = '';
|
|
6036
|
+
}
|
|
6037
|
+
} else if (code === '1') {
|
|
6038
|
+
currentStyle = 'font-weight:bold;';
|
|
6039
|
+
result += '<span style="' + currentStyle + '">';
|
|
6040
|
+
} else if (ansiColors[code]) {
|
|
6041
|
+
if (currentStyle) result += '</span>';
|
|
6042
|
+
currentStyle = 'color:' + ansiColors[code] + ';';
|
|
6043
|
+
result += '<span style="' + currentStyle + '">';
|
|
6044
|
+
}
|
|
6045
|
+
}
|
|
6046
|
+
}
|
|
6047
|
+
}
|
|
6048
|
+
|
|
6049
|
+
if (currentStyle) result += '</span>';
|
|
6050
|
+
return result;
|
|
6051
|
+
}
|
|
6052
|
+
|
|
6053
|
+
// Escape HTML
|
|
6054
|
+
function escapeHtml(text) {
|
|
6055
|
+
return text
|
|
6056
|
+
.replace(/&/g, '&')
|
|
6057
|
+
.replace(/</g, '<')
|
|
6058
|
+
.replace(/>/g, '>')
|
|
6059
|
+
.replace(/"/g, '"')
|
|
6060
|
+
.replace(/\\n/g, '<br>')
|
|
6061
|
+
.replace(/ /g, ' ');
|
|
6062
|
+
}
|
|
5790
6063
|
|
|
5791
|
-
|
|
6064
|
+
// Set connection status
|
|
6065
|
+
function setStatus(state, text) {
|
|
6066
|
+
statusDot.className = state === 'connected' ? 'connected' :
|
|
6067
|
+
state === 'connecting' ? 'connecting' : '';
|
|
6068
|
+
statusText.textContent = text;
|
|
6069
|
+
}
|
|
6070
|
+
|
|
6071
|
+
// Send data to terminal
|
|
6072
|
+
function send(data) {
|
|
6073
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
6074
|
+
ws.send(JSON.stringify({ type: 'terminal:input', data: data }));
|
|
6075
|
+
}
|
|
6076
|
+
}
|
|
6077
|
+
|
|
6078
|
+
// Show notification
|
|
6079
|
+
function showNotification(n) {
|
|
6080
|
+
if (!n) return;
|
|
6081
|
+
const el = document.createElement('div');
|
|
6082
|
+
el.className = 'notification ' + (n.type || 'info');
|
|
6083
|
+
el.innerHTML = '<h4>' + escapeHtml(n.title || 'Notification') + '</h4>' +
|
|
6084
|
+
'<p>' + escapeHtml(n.body || '') + '</p>';
|
|
6085
|
+
notifications.appendChild(el);
|
|
6086
|
+
setTimeout(function() { el.remove(); }, 5000);
|
|
6087
|
+
}
|
|
6088
|
+
|
|
6089
|
+
// Event: Send button
|
|
6090
|
+
sendBtn.onclick = function() {
|
|
6091
|
+
if (input.value) {
|
|
6092
|
+
send(input.value + '\\r');
|
|
6093
|
+
input.value = '';
|
|
6094
|
+
}
|
|
6095
|
+
input.focus();
|
|
6096
|
+
};
|
|
6097
|
+
|
|
6098
|
+
// Event: Enter key in input
|
|
6099
|
+
input.onkeydown = function(e) {
|
|
6100
|
+
if (e.key === 'Enter') {
|
|
6101
|
+
e.preventDefault();
|
|
6102
|
+
sendBtn.click();
|
|
6103
|
+
}
|
|
6104
|
+
};
|
|
6105
|
+
|
|
6106
|
+
// Event: Quick keys
|
|
6107
|
+
document.querySelectorAll('.qkey').forEach(function(btn) {
|
|
6108
|
+
btn.onclick = function() {
|
|
6109
|
+
const key = btn.dataset.key;
|
|
6110
|
+
const decoded = key
|
|
6111
|
+
.replace(/\\\\x([0-9a-f]{2})/gi, function(_, hex) {
|
|
6112
|
+
return String.fromCharCode(parseInt(hex, 16));
|
|
6113
|
+
})
|
|
6114
|
+
.replace(/\\\\t/g, '\\t')
|
|
6115
|
+
.replace(/\\\\r/g, '\\r')
|
|
6116
|
+
.replace(/\\\\n/g, '\\n');
|
|
6117
|
+
send(decoded);
|
|
6118
|
+
input.focus();
|
|
6119
|
+
};
|
|
6120
|
+
});
|
|
6121
|
+
|
|
6122
|
+
// Heartbeat
|
|
6123
|
+
setInterval(function() {
|
|
6124
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
6125
|
+
ws.send(JSON.stringify({ type: 'ping' }));
|
|
6126
|
+
}
|
|
6127
|
+
}, 25000);
|
|
6128
|
+
|
|
6129
|
+
// Handle resize
|
|
6130
|
+
function sendResize() {
|
|
6131
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
6132
|
+
const cols = Math.floor(terminal.clientWidth / 8);
|
|
6133
|
+
const rows = Math.floor(terminal.clientHeight / 18);
|
|
6134
|
+
ws.send(JSON.stringify({ type: 'terminal:resize', cols: cols, rows: rows }));
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6137
|
+
|
|
6138
|
+
window.addEventListener('resize', sendResize);
|
|
6139
|
+
setTimeout(sendResize, 1000);
|
|
6140
|
+
|
|
6141
|
+
// Start connection
|
|
6142
|
+
if (token) {
|
|
6143
|
+
connect();
|
|
6144
|
+
} else {
|
|
6145
|
+
authStatus.textContent = 'Invalid session URL';
|
|
6146
|
+
authStatus.classList.add('error');
|
|
6147
|
+
}
|
|
6148
|
+
})();
|
|
5792
6149
|
</script>
|
|
5793
6150
|
</body>
|
|
5794
6151
|
</html>`;
|