tabby-bianbu-mcp 0.5.2 → 0.5.4

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/CHANGELOG.md CHANGED
@@ -2,6 +2,40 @@
2
2
 
3
3
  All notable changes to `tabby-bianbu-mcp` will be documented in this file.
4
4
 
5
+ ## [0.5.4] - 2026-03-23
6
+
7
+ ### Fixed
8
+ - `private baseName()` made public so Angular AOT compilation can access it from templates
9
+ - `Ctrl+Shift+N` shortcut was unreachable because `Ctrl+N` handler fired first; reordered to check shift-modified combo first
10
+ - `RequestLane` workers now stop cleanly when the scheduler is reconfigured, preventing leaked infinite loops
11
+ - `cancelTransfer` now handles `queued` transfers in addition to `running` ones
12
+ - `super.onFrontendReady()` is now properly awaited in `BianbuCloudShellTabComponent`
13
+
14
+ ### Removed
15
+ - dead `readFileAsBase64` method (was never called)
16
+ - dead `shellTab.component.pug` template (component uses `BaseTerminalTabComponent.template`)
17
+ - unused `minIntervalMs` config default (was never read)
18
+ - non-functional `settings.enabled` toggle (no code gated on it)
19
+
20
+ ### Changed
21
+ - parallel chunked upload/download workers now use `offsets.shift()` instead of a shared mutable counter for clearer single-threaded safety
22
+
23
+ ### Known security considerations
24
+ - API key (`X-API-KEY`) is transmitted in an HTTP header; when using plaintext `http://` URLs the key is sent unencrypted — users should prefer HTTPS endpoints
25
+ - `as_root=true` on any MCP tool bypasses `FILE_ROOT` path restrictions and executes commands via `sudo -n`
26
+ - the "Copyable MCP snippet" in settings exposes the real API key — avoid sharing it in bug reports
27
+ - `pug@2.x` has known prototype pollution advisories; it is used only at build time and does not affect runtime
28
+
29
+ ## [0.5.3] - 2026-03-20
30
+
31
+ ### Added
32
+ - downloadable local maintenance session logs with timestamps and stable session names
33
+ - downloadable remote maintenance logs that bundle the status file and remote installer log into one timestamped file
34
+
35
+ ### Changed
36
+ - detached maintenance sessions now pass an explicit session name into `bianbu_agent_proxy.sh`
37
+ - remote installer logs now include UTC timestamps and the maintenance session name on every line
38
+
5
39
  ## [0.5.2] - 2026-03-20
6
40
 
7
41
  ### Fixed
package/README.md CHANGED
@@ -66,6 +66,13 @@ When you click `Push latest installer + upgrade`:
66
66
  5. the plugin waits for both installer completion and the expected remote health/version report before declaring success
67
67
  6. if the cutover fails, the installer auto-rolls back to the previous backup instead of leaving the remote half-upgraded
68
68
 
69
+ After a maintenance run, the settings page can also download:
70
+
71
+ - a local session log generated by the plugin
72
+ - a remote session log bundle containing the status file and installer log
73
+
74
+ Both downloads use timestamped filenames and carry the maintenance session name for easier debugging.
75
+
69
76
  This is the intended commercial-release upgrade path because the client and remote installer ship together.
70
77
 
71
78
  The remote installer now defaults `ENABLE_PASSWORDLESS_SUDO=false`. If you want unattended `as_root=true` maintenance through MCP, opt in explicitly and understand the security trade-off.
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "fileName": "bianbu_agent_proxy.sh",
3
3
  "sourceFile": "bianbu_agent_proxy.sh",
4
- "scriptVersion": "1.2.1",
4
+ "scriptVersion": "1.2.2",
5
5
  "serverVersion": "1.2.0",
6
- "sha256": "2579a96d69100625feef3ac205a877a3b742c62adec1db2ee284dcd3cc834fb4",
7
- "bytes": 67586,
8
- "generatedAt": "2026-03-20T04:59:13.343Z"
6
+ "sha256": "729442eb8fa2311e67415ed78d5dbf0c12e58281e075cd3cc43cbb74c595e5b9",
7
+ "bytes": 67756,
8
+ "generatedAt": "2026-03-23T02:15:11.160Z"
9
9
  }
@@ -4,7 +4,7 @@ set -Eeuo pipefail
4
4
  umask 077
5
5
 
6
6
  SCRIPT_NAME="$(basename "$0")"
7
- SCRIPT_VERSION="${SCRIPT_VERSION:-1.2.1}"
7
+ SCRIPT_VERSION="${SCRIPT_VERSION:-1.2.2}"
8
8
  SERVER_VERSION="${SERVER_VERSION:-1.2.0}"
9
9
  APP_NAME="bianbu-mcp-server"
10
10
  INSTALL_ROOT="/opt/${APP_NAME}"
@@ -28,6 +28,8 @@ MAX_REQUEST_BODY_MB="${MAX_REQUEST_BODY_MB:-8}"
28
28
  TLS_CERT_FILE="${TLS_CERT_FILE:-}"
29
29
  TLS_KEY_FILE="${TLS_KEY_FILE:-}"
30
30
  MCP_TRANSPORT_MODE="${MCP_TRANSPORT_MODE:-stateless}"
31
+ DEFAULT_SESSION_NAME="manual-$(date -u +%Y%m%dT%H%M%SZ)"
32
+ SESSION_NAME="${SESSION_NAME:-$DEFAULT_SESSION_NAME}"
31
33
 
32
34
  LAST_BACKUP_DIR=""
33
35
  ROLLBACK_ON_EXIT=0
@@ -36,7 +38,7 @@ ROLLBACK_STAGING_ROOT=""
36
38
  ROLLBACK_PREVIOUS_ROOT=""
37
39
 
38
40
  log() {
39
- printf '[%s] %s\n' "$SCRIPT_NAME" "$*"
41
+ printf '[%s] [%s] [%s] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$SCRIPT_NAME" "$SESSION_NAME" "$*"
40
42
  }
41
43
 
42
44
  die() {
@@ -3,11 +3,9 @@ import { ConfigProvider } from 'tabby-core';
3
3
  export declare class BianbuMcpConfigProvider extends ConfigProvider {
4
4
  defaults: {
5
5
  bianbuMcp: {
6
- enabled: boolean;
7
6
  name: string;
8
7
  url: string;
9
8
  apiKey: string;
10
- minIntervalMs: number;
11
9
  maxRetries: number;
12
10
  retryBaseMs: number;
13
11
  interactiveConcurrency: number;
@@ -94,11 +94,10 @@ export declare class BianbuCloudFilesTabComponent extends BaseTabComponent {
94
94
  private runUploadTransfer;
95
95
  private runDownloadTransfer;
96
96
  private clearPreview;
97
- private baseName;
97
+ baseName(path: string): string;
98
98
  private isTextLike;
99
99
  private joinPath;
100
100
  private uint8ToBase64;
101
101
  private base64ToUint8;
102
- private readFileAsBase64;
103
102
  }
104
103
  export {};
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("@angular/common"),require("@angular/core"),require("@angular/forms"),require("fs"),require("tabby-core"),require("tabby-settings"),require("tabby-terminal"));else if("function"==typeof define&&define.amd)define(["@angular/common","@angular/core","@angular/forms","fs","tabby-core","tabby-settings","tabby-terminal"],t);else{var i="object"==typeof exports?t(require("@angular/common"),require("@angular/core"),require("@angular/forms"),require("fs"),require("tabby-core"),require("tabby-settings"),require("tabby-terminal")):t(e["@angular/common"],e["@angular/core"],e["@angular/forms"],e.fs,e["tabby-core"],e["tabby-settings"],e["tabby-terminal"]);for(var n in i)("object"==typeof exports?exports:e)[n]=i[n]}}(global,(e,t,i,n,s,o,r)=>{return a={102(e,t,i){var n=i(169);e.exports=(n.default||n).apply(n,[])},426(e,t,i){var n=i(295);e.exports=(n.default||n).apply(n,[])},169(e,t,i){i(766),e.exports=function(e){return""+'<div style="height:100%; display:flex; flex-direction:column; background:#111827; color:#e5e7eb"><div class="topbar" style="padding:12px 14px; border-bottom:1px solid #334155; display:flex; flex-direction:column; gap:10px; background:#0f172a"><div class="toolbar" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap"><button class="btn btn-sm btn-secondary" type="button" (click)="goBack()" [disabled]="busy || historyIndex &lt;= 0" title="Back (Alt+Left)" style="min-width:34px; font-weight:700">←</button><button class="btn btn-sm btn-secondary" type="button" (click)="goForward()" [disabled]="busy || historyIndex &gt;= history.length - 1" title="Forward (Alt+Right)" style="min-width:34px; font-weight:700">→</button><button class="btn btn-sm btn-secondary" type="button" (click)="goUp()" [disabled]="busy || currentPath === &quot;.&quot; || currentPath === &quot;/&quot;" title="Up (Alt+Up / Backspace)" style="min-width:34px; font-weight:700">↑</button><button class="btn btn-sm btn-secondary" type="button" (click)="refresh()" [disabled]="busy" title="Refresh (F5)" style="min-width:34px; font-weight:700">⟳</button><button class="btn btn-sm btn-secondary" type="button" (click)="openCreateDirectoryPrompt()" [disabled]="busy">New folder</button><button class="btn btn-sm btn-secondary" type="button" (click)="openCreateFilePrompt()" [disabled]="busy">New file</button><label class="btn btn-sm btn-secondary" style="margin:0">Upload<input type="file" multiple="multiple" style="display:none" (change)="uploadFile($event)" [disabled]="busy"></label><button class="btn btn-sm btn-secondary" type="button" (click)="downloadSelected()" [disabled]="busy || !selectedPath">Download</button><button class="btn btn-sm btn-secondary" type="button" (click)="transfersVisible = !transfersVisible">Transfers</button><label style="display:flex; align-items:center; gap:6px; margin-left:auto; background:#1e293b; padding:6px 10px; border-radius:8px"><input type="checkbox" [(ngModel)]="asRoot" (ngModelChange)="refresh()"><span>as_root</span></label></div><div class="address" style="display:flex; gap:8px; align-items:center"><div style="display:flex; gap:4px; align-items:center; flex-wrap:wrap; padding:10px 12px; border-radius:10px; background:#1e293b; border:1px solid #334155; flex:1"><button class="btn btn-link" type="button" *ngFor="let crumb of breadcrumbs; let i=index" (click)="navigateBreadcrumb(i)" style="padding:0 4px; min-height:auto; color:#f8fafc; text-decoration:none; font-weight:600">{{ crumb }}</button></div><input class="form-control" id="bianbu-path-input" type="text" style="max-width:420px; background:#0b1220; color:#f8fafc; border:1px solid #334155" [(ngModel)]="pathInput" (keyup.enter)="navigateToInput()" title="Ctrl+L to focus path"></div><div class="searchrow" style="display:flex; gap:8px; align-items:center"><input class="form-control" type="text" style="background:#0b1220; color:#f8fafc; border:1px solid #334155" [(ngModel)]="searchText" (ngModelChange)="applyFilter()" placeholder="Filter current directory…"><div style="color:#cbd5e1; white-space:nowrap">{{ status }}</div></div></div><div class="main" style="display:grid; grid-template-columns: minmax(460px, 48%) 1fr; gap:0; flex:1; min-height:0"><div class="pane left" style="display:flex; flex-direction:column; min-height:0; border-right:1px solid #334155; position:relative; background:#0b1220" [style.outline]="dragActive ? &quot;2px dashed #4ade80&quot; : &quot;none&quot;" (dragover)="onDragOver($event)" (dragleave)="onDragLeave()" (drop)="onDrop($event)"><div *ngIf="dragActive" style="position:absolute; inset:12px; border-radius:12px; background:rgba(74,222,128,0.12); display:flex; align-items:center; justify-content:center; z-index:10; pointer-events:none; color:#d1fae5; font-size:18px; font-weight:700">Drop file(s) to queue upload</div><div style="overflow:auto; flex:1"><table style="width:100%; border-collapse:collapse; table-layout:fixed"><thead style="position:sticky; top:0; z-index:2; background:#0f172a"><tr style="text-align:left; color:#cbd5e1; border-bottom:1px solid #334155"><th style="padding:10px 12px; width:58%">Name</th><th style="padding:10px 12px; width:16%">Type</th><th style="padding:10px 12px; width:26%">Size</th></tr></thead><tbody><tr *ngFor="let item of filteredItems; let i=index" [style.background]="i === selectedIndex ? &quot;#1d4ed8&quot; : &quot;transparent&quot;" [style.color]="i === selectedIndex ? &quot;#ffffff&quot; : &quot;#e5e7eb&quot;" style="border-bottom:1px solid #1e293b; cursor:default" (click)="selectItem(item)" (dblclick)="openItem(item)" (contextmenu)="showContextMenu($event, item)"><td style="padding:10px 12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-weight:600">{{ baseName(item.path) }}</td><td style="padding:10px 12px">{{ item.is_dir ? \'dir\' : \'file\' }}</td><td style="padding:10px 12px">{{ item.size }}</td></tr></tbody></table></div></div><div class="pane right" style="display:flex; flex-direction:column; min-height:0; background:#111827"><div class="previewHeader" style="padding:12px 14px; border-bottom:1px solid #334155; display:flex; justify-content:space-between; gap:12px; align-items:center; background:#0f172a"><div><h4 style="margin:0; color:#f8fafc">Preview / Editor</h4><div style="color:#94a3b8; font-size:.9em">{{ selectedPath || \'Select a file from the left pane\' }}</div></div><div style="display:flex; gap:8px"><button class="btn btn-sm btn-secondary" type="button" (click)="downloadSelected()" [disabled]="busy || !selectedPath">Download</button><button class="btn btn-sm btn-primary" type="button" (click)="saveSelected()" [disabled]="busy || !selectedPath || !selectedIsText">Save</button></div></div><div style="flex:1; min-height:0; display:flex"><textarea class="form-control" *ngIf="selectedPath &amp;&amp; selectedIsText" style="flex:1; min-height:0; border:none; border-radius:0; background:#111827; color:#f8fafc; padding:14px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace" [(ngModel)]="selectedContent" [disabled]="busy" placeholder="Open a text file to preview or edit it"></textarea><div *ngIf="selectedPath &amp;&amp; !selectedIsText" style="flex:1; display:flex; align-items:center; justify-content:center; text-align:center; color:#cbd5e1; padding:24px"><div><div style="font-size:20px; font-weight:700; margin-bottom:10px">This file type is not previewed inline</div><div style="margin-bottom:16px; color:#94a3b8">Download the file to inspect it locally.</div><button class="btn btn-primary" type="button" (click)="downloadSelected()">Download file</button></div></div><div *ngIf="!selectedPath" style="flex:1; display:flex; align-items:center; justify-content:center; text-align:center; color:#94a3b8">Select a file from the left pane to preview or edit it.</div></div></div></div><div class="contextMenu" *ngIf="contextMenuVisible &amp;&amp; contextItem" [style.left.px]="contextMenuX" [style.top.px]="contextMenuY" style="position:fixed; z-index:1000; min-width:180px; background:#0f172a; border:1px solid #334155; border-radius:10px; box-shadow:0 12px 30px rgba(0,0,0,.45); padding:6px"><button class="btn btn-link" type="button" style="display:block; width:100%; text-align:left; color:#f8fafc; text-decoration:none; padding:8px 10px" (click)="openItem(contextItem); hideContextMenu()">Open</button><button class="btn btn-link" type="button" style="display:block; width:100%; text-align:left; color:#f8fafc; text-decoration:none; padding:8px 10px" (click)="openRenamePrompt(contextItem); hideContextMenu()">Rename</button><button class="btn btn-link" type="button" style="display:block; width:100%; text-align:left; color:#fca5a5; text-decoration:none; padding:8px 10px" (click)="deleteItem(contextItem); hideContextMenu()">Delete</button></div><div class="promptOverlay" *ngIf="promptVisible" style="position:fixed; inset:0; background:rgba(2,6,23,.65); z-index:1100; display:flex; align-items:center; justify-content:center"><div class="promptCard" style="width:420px; background:#0f172a; border:1px solid #334155; border-radius:14px; padding:18px; box-shadow:0 20px 50px rgba(0,0,0,.45)"><h3 style="margin-top:0; color:#f8fafc">{{ promptTitle }}</h3><input class="form-control" type="text" style="background:#0b1220; color:#f8fafc; border:1px solid #334155" [(ngModel)]="promptValue" [placeholder]="promptPlaceholder" (keyup.enter)="confirmPrompt()" (keyup.escape)="closePrompt()"><div style="display:flex; justify-content:flex-end; gap:10px; margin-top:16px"><button class="btn btn-secondary" type="button" (click)="closePrompt()">Cancel</button><button class="btn btn-primary" type="button" (click)="confirmPrompt()">Confirm</button></div></div></div><div class="transferOverlay" *ngIf="transfersVisible" style="position:fixed; right:16px; bottom:16px; width:360px; max-height:50vh; overflow:auto; z-index:900; background:#0f172a; border:1px solid #334155; border-radius:14px; box-shadow:0 20px 50px rgba(0,0,0,.45)"><div style="padding:12px 14px; border-bottom:1px solid #334155; display:flex; justify-content:space-between; align-items:center"><b style="color:#f8fafc">Transfers</b><button class="btn btn-sm btn-secondary" type="button" (click)="transfersVisible = false">Hide</button></div><div *ngIf="!transfers.length" style="padding:14px; color:#94a3b8">No uploads or downloads yet.</div><div *ngFor="let transfer of transfers" style="padding:12px 14px; border-bottom:1px solid #1e293b"><div style="display:flex; justify-content:space-between; gap:12px; margin-bottom:6px"><div style="color:#f8fafc; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis">{{ transfer.name }}</div><div style="color:#94a3b8; font-size:.9em">{{ transfer.direction }}</div></div><div style="color:#94a3b8; font-size:.9em; margin-bottom:8px">{{ transfer.status }} • {{ transferRate(transfer) }}</div><div style="height:8px; border-radius:999px; background:#1e293b; overflow:hidden; margin-bottom:8px"><div *ngIf="transferPercent(transfer) !== null" [style.width.%]="transferPercent(transfer)" style="height:100%; background:#3b82f6"></div><div *ngIf="transferPercent(transfer) === null &amp;&amp; transfer.status === &quot;running&quot;" style="height:100%; width:45%; background:linear-gradient(90deg,#334155,#60a5fa,#334155)"></div></div><div style="display:flex; justify-content:space-between; align-items:center"><div style="color:#cbd5e1; font-size:.9em">{{ transfer.bytesDone }} / {{ transfer.bytesTotal || \'unknown\' }} bytes</div><button class="btn btn-sm btn-outline-danger" type="button" *ngIf="transfer.status === &quot;running&quot;" (click)="cancelTransfer(transfer)">Cancel</button></div></div></div></div>'}},295(e,t,i){i(766),e.exports=function(e){return""+'<div style="padding: 20px; display:flex; flex-direction:column; gap:16px"><div class="hero" style="padding:18px; border-radius:12px; background:linear-gradient(135deg, rgba(25,53,103,.9), rgba(17,94,89,.85)); color:white"><h2 style="margin:0 0 8px 0">Bianbu MCP</h2><p style="margin:0; opacity:.92">Configure your Bianbu Cloud MCP endpoint, validate remote health, and push the latest bundled installer directly from Tabby.</p></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Quick actions</h3><div style="display:flex; gap:12px; flex-wrap:wrap"><button class="btn btn-primary" type="button" (click)="openShell()">Open Bianbu Cloud Shell</button><button class="btn btn-secondary" type="button" (click)="openFiles()">Open Bianbu Cloud Files</button><button class="btn btn-secondary" type="button" (click)="testConnection()" [disabled]="diagnosticsBusy || validationErrors.length &gt; 0">Test connection</button><button class="btn btn-secondary" type="button" (click)="refreshHealth()" [disabled]="diagnosticsBusy || validationErrors.length &gt; 0">Fetch remote health</button></div></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Connection settings</h3><div class="grid" style="display:grid; grid-template-columns: 190px 1fr; gap:12px; align-items:center"><label>Enable integration</label><input type="checkbox" [(ngModel)]="settings.enabled" (ngModelChange)="save()"><label>Profile name</label><input class="form-control" type="text" [(ngModel)]="settings.name" (ngModelChange)="save()" placeholder="bianbu"><label>MCP URL</label><input class="form-control" type="text" [(ngModel)]="settings.url" (ngModelChange)="save()" placeholder="https://your-domain.example.com/mcp"><label>X-API-KEY</label><input class="form-control" type="password" [(ngModel)]="settings.apiKey" (ngModelChange)="save()" placeholder="your-x-api-key"><label>Interactive slots</label><input class="form-control" type="number" min="1" step="1" [(ngModel)]="settings.interactiveConcurrency" (ngModelChange)="save()"><label>Transfer slots</label><input class="form-control" type="number" min="1" max="30" step="1" [(ngModel)]="settings.transferConcurrency" (ngModelChange)="save()"><label>Worker cadence (ms)</label><input class="form-control" type="number" min="10" step="10" [(ngModel)]="settings.workerCadenceMs" (ngModelChange)="save()"><label>Max retries</label><input class="form-control" type="number" min="0" [(ngModel)]="settings.maxRetries" (ngModelChange)="save()"><label>Retry base (ms)</label><input class="form-control" type="number" min="0" [(ngModel)]="settings.retryBaseMs" (ngModelChange)="save()"><label>Upload chunk (bytes)</label><input class="form-control" type="number" min="16384" step="1024" [(ngModel)]="settings.uploadChunkBytes" (ngModelChange)="save()"><label>Download chunk (bytes)</label><input class="form-control" type="number" min="16384" step="1024" [(ngModel)]="settings.downloadChunkBytes" (ngModelChange)="save()"><label>Notes</label><textarea class="form-control" rows="3" [(ngModel)]="settings.notes" (ngModelChange)="save()" placeholder="Optional notes"></textarea></div></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Remote maintenance</h3><div class="grid" style="display:grid; grid-template-columns: 190px 1fr; gap:12px; align-items:center"><label>Installer remote path</label><input class="form-control" type="text" [(ngModel)]="settings.installerRemotePath" (ngModelChange)="save()" placeholder="/tmp/bianbu_agent_proxy.sh"><label>Maintenance as root</label><input type="checkbox" [(ngModel)]="settings.maintenanceAsRoot" (ngModelChange)="save()"><label>Reconnect poll (ms)</label><input class="form-control" type="number" min="500" [(ngModel)]="settings.reconnectPollMs" (ngModelChange)="save()"><label>Health timeout (ms)</label><input class="form-control" type="number" min="5000" [(ngModel)]="settings.upgradeHealthTimeoutMs" (ngModelChange)="save()"></div><div style="display:flex; gap:12px; flex-wrap:wrap; margin-top:16px"><button class="btn btn-primary" type="button" (click)="pushUpgrade(&quot;up&quot;)" [disabled]="maintenanceBusy || validationErrors.length &gt; 0">Push latest installer + upgrade</button><button class="btn btn-secondary" type="button" (click)="pushUpgrade(&quot;repair&quot;)" [disabled]="maintenanceBusy || validationErrors.length &gt; 0">Push latest installer + repair</button></div><div *ngIf="lastMaintenanceAt" style="margin-top:12px; color:#cbd5e1"><b>Last maintenance:</b> {{ lastMaintenanceAt }}<br><span>{{ lastMaintenanceSummary }}</span></div></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Release diagnostics</h3><div style="display:grid; grid-template-columns: repeat(2, minmax(220px, 1fr)); gap:12px"><div style="padding:12px; border-radius:10px; background:rgba(255,255,255,0.02)"><b>Bundled installer</b><div style="margin-top:8px; opacity:.9">Script version: {{ bundledInstaller?.scriptVersion || \'unavailable\' }}</div><div style="opacity:.9">Server version: {{ bundledInstaller?.serverVersion || \'unavailable\' }}</div><div style="opacity:.9">SHA-256: {{ bundledInstaller?.sha256 || \'unavailable\' }}</div></div><div style="padding:12px; border-radius:10px; background:rgba(255,255,255,0.02)"><b>Remote status</b><div style="margin-top:8px; opacity:.9">Remote script version: {{ remoteHealth?.scriptVersion || \'unknown\' }}</div><div style="opacity:.9">Remote server version: {{ remoteHealth?.serverVersion || \'unknown\' }}</div><div style="opacity:.9">Transport: {{ remoteHealth?.transportMode || \'unknown\' }}</div><div style="opacity:.9">FILE_ROOT: {{ remoteHealth?.fileRoot || \'unknown\' }}</div><div style="opacity:.9">Supports rename_path: {{ remoteHealth?.supports?.renamePath ? \'yes\' : \'no\' }}</div><div style="opacity:.9">Supports shell sessions: {{ remoteHealth?.supports?.shellSession ? \'yes\' : \'no\' }}</div><div style="opacity:.9">Supports chunked transfers: {{ remoteHealth?.supports?.chunkedTransfers ? \'yes\' : \'no\' }}</div><div style="opacity:.9">Supports parallel chunk offsets: {{ remoteHealth?.supports?.parallelChunkOffsets ? \'yes\' : \'no\' }}</div><div style="opacity:.9">Last diagnostic: {{ lastDiagnosticAt || \'never\' }}</div></div></div><div *ngIf="validationErrors.length" style="margin-top:16px; color:#fca5a5"><b>Configuration issues</b><ul style="margin:8px 0 0 18px"><li *ngFor="let error of validationErrors">{{ error }}</li></ul></div><div *ngIf="lastError" style="margin-top:16px; color:#fca5a5; white-space:pre-wrap"><b>Last error</b><div style="margin-top:8px">{{ lastError }}</div></div></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Copyable MCP snippet</h3><pre style="white-space: pre-wrap; margin-bottom:0">{{ sampleJson }}</pre></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Reminder</h3><ul style="margin-bottom:0"><li>MCP URL is typically <code>https://&lt;domain&gt;/mcp</code></li><li><code>X-API-KEY</code> is copied from the Bianbu Cloud API Key page</li><li>Push-upgrade uploads the bundled installer to the configured remote path, launches it remotely, then waits for health to recover</li><li>Shell and Files tabs are UI views backed by MCP calls, not native SSH/SFTP transport</li></ul></div></div>'}},766(e,t,i){"use strict";var n=Object.prototype.hasOwnProperty;function s(e,t){return Array.isArray(e)?function(e,t){for(var i,n="",o="",r=Array.isArray(t),a=0;a<e.length;a++)(i=s(e[a]))&&(r&&t[a]&&(i=l(i)),n=n+o+i,o=" ");return n}(e,t):e&&"object"==typeof e?function(e){var t="",i="";for(var s in e)s&&e[s]&&n.call(e,s)&&(t=t+i+s,i=" ");return t}(e):e||""}function o(e){if(!e)return"";if("object"==typeof e){var t="";for(var i in e)n.call(e,i)&&(t=t+i+":"+e[i]+";");return t}return e+""}function r(e,t,i,n){if(!1===t||null==t||!t&&("class"===e||"style"===e))return"";if(!0===t)return" "+(n?e:e+'="'+e+'"');var s=typeof t;return"object"!==s&&"function"!==s||"function"!=typeof t.toJSON||(t=t.toJSON()),"string"==typeof t||(t=JSON.stringify(t),i||-1===t.indexOf('"'))?(i&&(t=l(t))," "+e+'="'+t+'"'):" "+e+"='"+t.replace(/'/g,"&#39;")+"'"}t.merge=function e(t,i){if(1===arguments.length){for(var n=t[0],s=1;s<t.length;s++)n=e(n,t[s]);return n}for(var r in i)if("class"===r){var a=t[r]||[];t[r]=(Array.isArray(a)?a:[a]).concat(i[r]||[])}else if("style"===r){a=(a=o(t[r]))&&";"!==a[a.length-1]?a+";":a;var l=o(i[r]);l=l&&";"!==l[l.length-1]?l+";":l,t[r]=a+l}else t[r]=i[r];return t},t.classes=s,t.style=o,t.attr=r,t.attrs=function(e,t){var i="";for(var a in e)if(n.call(e,a)){var l=e[a];if("class"===a){i=r(a,l=s(l),!1,t)+i;continue}"style"===a&&(l=o(l)),i+=r(a,l,!1,t)}return i};var a=/["&<>]/;function l(e){var t=""+e,i=a.exec(t);if(!i)return e;var n,s,o,r="";for(n=i.index,s=0;n<t.length;n++){switch(t.charCodeAt(n)){case 34:o="&quot;";break;case 38:o="&amp;";break;case 60:o="&lt;";break;case 62:o="&gt;";break;default:continue}s!==n&&(r+=t.substring(s,n)),s=n+1,r+=o}return s!==n?r+t.substring(s,n):r}t.escape=l,t.rethrow=function e(t,n,s,o){if(!(t instanceof Error))throw t;if(!("undefined"==typeof window&&n||o))throw t.message+=" on line "+s,t;try{o=o||i(947).readFileSync(n,"utf8")}catch(i){e(t,null,s)}var r=3,a=o.split("\n"),l=Math.max(s-r,0),d=Math.min(a.length,s+r);throw r=a.slice(l,d).map(function(e,t){var i=t+l+1;return(i==s?" > ":" ")+i+"| "+e}).join("\n"),t.path=n,t.message=(n||"Pug")+":"+s+"\n"+r+"\n\n"+t.message,t}},40(e,t,i){"use strict";var n=this&&this.__decorate||function(e,t,i,n){var s,o=arguments.length,r=o<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,n);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(r=(o<3?s(r):o>3?s(t,i,r):s(t,i))||r);return o>3&&r&&Object.defineProperty(t,i,r),r},s=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuCloudCommandProvider=void 0;const o=i(860),r=i(650),a=i(678),l=i(241);let d=class extends r.CommandProvider{constructor(e){super(),this.app=e}async provide(){return[{id:"bianbu-cloud-shell",label:"Open Bianbu Cloud Shell",sublabel:"Remote shell over MCP run_command",locations:[r.CommandLocation.StartPage],run:async()=>{this.app.openNewTab({type:a.BianbuCloudShellTabComponent})}},{id:"bianbu-cloud-files",label:"Open Bianbu Cloud Files",sublabel:"File manager over MCP file tools",locations:[r.CommandLocation.StartPage],run:async()=>{this.app.openNewTab({type:l.BianbuCloudFilesTabComponent})}}]}};d=n([(0,o.Injectable)(),s("design:paramtypes",[r.AppService])],d),t.BianbuCloudCommandProvider=d},889(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuMcpConfigProvider=void 0;const n=i(650);class s extends n.ConfigProvider{constructor(){super(...arguments),this.defaults={bianbuMcp:{enabled:!0,name:"bianbu",url:"",apiKey:"",minIntervalMs:1e3,maxRetries:2,retryBaseMs:1e3,interactiveConcurrency:2,transferConcurrency:30,workerCadenceMs:100,uploadChunkBytes:32768,downloadChunkBytes:131072,notes:"",installerRemotePath:"/tmp/bianbu_agent_proxy.sh",maintenanceAsRoot:!0,reconnectPollMs:2e3,upgradeHealthTimeoutMs:12e4}}}}t.BianbuMcpConfigProvider=s},241(e,t,i){"use strict";var n=this&&this.__decorate||function(e,t,i,n){var s,o=arguments.length,r=o<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,n);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(r=(o<3?s(r):o>3?s(t,i,r):s(t,i))||r);return o>3&&r&&Object.defineProperty(t,i,r),r},s=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuCloudFilesTabComponent=void 0;const o=i(860),r=i(650),a=i(885);let l=class extends r.BaseTabComponent{constructor(e,t,i,n){super(e),this.mcp=t,this.notifications=i,this.platform=n,this.currentPath=".",this.pathInput=".",this.asRoot=!1,this.busy=!1,this.items=[],this.filteredItems=[],this.selectedPath="",this.selectedContent="",this.selectedIsText=!1,this.status="Ready",this.dragActive=!1,this.searchText="",this.history=["."],this.historyIndex=0,this.selectedIndex=-1,this.contextMenuVisible=!1,this.contextMenuX=0,this.contextMenuY=0,this.contextItem=null,this.promptVisible=!1,this.promptTitle="",this.promptPlaceholder="",this.promptValue="",this.promptAction=null,this.promptTarget=null,this.transfersVisible=!0,this.transfers=[],this.transferSeq=1,this.transferQueue=[],this.transferQueueRunning=!1,this.setTitle("Bianbu Cloud Files"),this.icon="folder-open"}ngOnInit(){this.refresh().catch(()=>null)}get breadcrumbs(){if(!this.currentPath||"."===this.currentPath)return["."];const e=this.currentPath.replace(/\\/g,"/");if("/"===e)return["/"];const t=e.split("/").filter(Boolean);return e.startsWith("/")?["/"].concat(t):t}get selectedItem(){var e;return null!==(e=this.filteredItems[this.selectedIndex])&&void 0!==e?e:null}onDocumentClick(){this.hideContextMenu()}onKeyDown(e){var t;const i=e.target,n=null===(t=null==i?void 0:i.tagName)||void 0===t?void 0:t.toLowerCase(),s=["input","textarea"].includes(n||"")||this.promptVisible;if("F5"===e.key)return e.preventDefault(),void this.refresh();if(e.altKey&&"ArrowUp"===e.key)return e.preventDefault(),void this.goUp();if(e.altKey&&"ArrowLeft"===e.key)return e.preventDefault(),void this.goBack();if(e.altKey&&"ArrowRight"===e.key)return e.preventDefault(),void this.goForward();if(e.ctrlKey&&"l"===e.key.toLowerCase()){e.preventDefault();const t=document.getElementById("bianbu-path-input");return null==t||t.focus(),void(null==t||t.select())}return e.ctrlKey&&"n"===e.key.toLowerCase()?(e.preventDefault(),void this.openCreateFilePrompt()):e.ctrlKey&&e.shiftKey&&"n"===e.key.toLowerCase()?(e.preventDefault(),void this.openCreateDirectoryPrompt()):s?void 0:"Backspace"===e.key?(e.preventDefault(),void this.goUp()):"Delete"===e.key&&this.selectedItem?(e.preventDefault(),void this.deleteItem(this.selectedItem)):"F2"===e.key&&this.selectedItem?(e.preventDefault(),void this.openRenamePrompt(this.selectedItem)):"Enter"===e.key&&this.selectedItem?(e.preventDefault(),void this.openItem(this.selectedItem)):"ArrowDown"===e.key?(e.preventDefault(),void this.moveSelection(1)):"ArrowUp"===e.key?(e.preventDefault(),void this.moveSelection(-1)):void 0}async refresh(){this.busy=!0,this.status="Loading directory…",this.pathInput=this.currentPath,this.setProgress(.25);try{const e=await this.mcp.listDirectory(this.currentPath||".",this.asRoot);this.items=e.items||[],this.applyFilter(),this.status=`Loaded ${this.items.length} item(s)`,this.filteredItems.length&&this.selectedIndex<0&&(this.selectedIndex=0)}catch(e){this.notifications.error("Failed to list directory",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1,this.setProgress(null)}}applyFilter(){const e=this.searchText.trim().toLowerCase();this.filteredItems=this.items.filter(t=>{const i=this.baseName(t.path).toLowerCase();return!e||i.includes(e)||t.path.toLowerCase().includes(e)}),this.selectedIndex>=this.filteredItems.length&&(this.selectedIndex=this.filteredItems.length-1)}navigateToInput(){this.navigate(this.pathInput||".")}navigate(e,t=!0){this.currentPath=e||".",this.pathInput=this.currentPath,t&&(this.history=this.history.slice(0,this.historyIndex+1),this.history.push(this.currentPath),this.historyIndex=this.history.length-1),this.clearPreview(),this.selectedIndex=-1,this.refresh()}navigateBreadcrumb(e){if("."===this.currentPath&&0===e)return void this.navigate(".",!0);const t=this.breadcrumbs;if("/"===t[0])return 0===e?void this.navigate("/",!0):void this.navigate("/"+t.slice(1,e+1).join("/"),!0);this.navigate(t.slice(0,e+1).join("/"),!0)}goBack(){this.historyIndex<=0||(this.historyIndex-=1,this.navigate(this.history[this.historyIndex],!1))}goForward(){this.historyIndex>=this.history.length-1||(this.historyIndex+=1,this.navigate(this.history[this.historyIndex],!1))}goUp(){if("."===this.currentPath||"/"===this.currentPath)return;const e=this.currentPath.replace(/\\/g,"/"),t=e.split("/").slice(0,-1).join("/")||(e.startsWith("/")?"/":".");this.navigate(t,!0)}moveSelection(e){this.filteredItems.length?this.selectedIndex<0?this.selectedIndex=0:this.selectedIndex=Math.max(0,Math.min(this.filteredItems.length-1,this.selectedIndex+e)):this.selectedIndex=-1}selectItem(e){this.selectedIndex=this.filteredItems.indexOf(e)}async openItem(e){if(this.selectItem(e),e.is_dir)this.navigate(e.path,!0);else{this.busy=!0,this.status=`Opening ${e.path}`,this.selectedPath=e.path,this.selectedIsText=this.isTextLike(e.path);try{if(this.selectedIsText){const t=await this.mcp.readTextFile(e.path,524288,this.asRoot);this.selectedContent=t.content||"",this.status=`Opened ${e.path}`}else this.selectedContent="",this.status=`Binary / non-text file selected: ${e.path}`}catch(e){this.notifications.error("Failed to open file",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e),this.selectedContent=""}finally{this.busy=!1}}}async saveSelected(){if(this.selectedPath&&this.selectedIsText){this.busy=!0,this.status=`Saving ${this.selectedPath}`;try{await this.mcp.writeTextFile(this.selectedPath,this.selectedContent,this.asRoot),this.notifications.notice("File saved"),this.status=`Saved ${this.selectedPath}`,await this.refresh()}catch(e){this.notifications.error("Failed to save file",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1}}}openCreateDirectoryPrompt(){this.promptVisible=!0,this.promptTitle="Create new folder",this.promptPlaceholder="Folder name",this.promptValue="",this.promptAction="mkdir",this.promptTarget=null}openCreateFilePrompt(){this.promptVisible=!0,this.promptTitle="Create new file",this.promptPlaceholder="File name",this.promptValue="",this.promptAction="newfile",this.promptTarget=null}openRenamePrompt(e){this.promptVisible=!0,this.promptTitle="Rename",this.promptPlaceholder="New name",this.promptValue=this.baseName(e.path),this.promptAction="rename",this.promptTarget=e}closePrompt(){this.promptVisible=!1,this.promptAction=null,this.promptTarget=null,this.promptValue=""}async confirmPrompt(){const e=this.promptValue.trim();if(e&&this.promptAction)return"mkdir"===this.promptAction?(await this.createDirectory(e),void this.closePrompt()):"newfile"===this.promptAction?(await this.createFile(e),void this.closePrompt()):void("rename"===this.promptAction&&this.promptTarget&&(await this.renameItem(this.promptTarget,e),this.closePrompt()));this.closePrompt()}async createDirectory(e){const t=this.joinPath(this.currentPath,e);this.busy=!0;try{await this.mcp.makeDirectory(t,this.asRoot),await this.refresh(),this.notifications.notice("Directory created")}catch(e){this.notifications.error("Failed to create directory",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1}}async createFile(e){const t=this.joinPath(this.currentPath,e);this.busy=!0;try{await this.mcp.writeTextFile(t,"",this.asRoot),await this.refresh(),this.notifications.notice("File created")}catch(e){this.notifications.error("Failed to create file",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1}}async renameItem(e,t){const i=e.path.split("/").slice(0,-1).join("/")||".",n=this.joinPath(i,t);try{await this.mcp.renamePath(e.path,n,this.asRoot),this.notifications.notice("Path renamed"),this.selectedPath===e.path&&(this.selectedPath=n),await this.refresh()}catch(e){const t=String((null==e?void 0:e.message)||e);this.notifications.error("Rename failed",t),this.status=t}}async deleteItem(e){this.busy=!0;try{await this.mcp.deletePath(e.path,!!e.is_dir,this.asRoot),this.selectedPath===e.path&&this.clearPreview(),await this.refresh(),this.notifications.notice("Path deleted")}catch(e){this.notifications.error("Failed to delete path",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1}}showContextMenu(e,t){e.preventDefault(),e.stopPropagation(),this.selectItem(t),this.contextItem=t,this.contextMenuVisible=!0,this.contextMenuX=e.clientX,this.contextMenuY=e.clientY}hideContextMenu(){this.contextMenuVisible=!1,this.contextItem=null}async uploadFile(e){const t=e.target,i=Array.from(t.files||[]);if(i.length){for(const e of i)this.enqueueUpload(e);t.value=""}}createTransfer(e,t,i,n,s="queued"){return{id:this.transferSeq++,name:e,direction:t,status:s,bytesDone:0,bytesTotal:i,startedAt:Date.now(),controller:n}}cancelTransfer(e){var t;"running"===e.status&&(null===(t=e.controller)||void 0===t||t.abort(),e.status="cancelled",e.finishedAt=Date.now())}transferPercent(e){return e.bytesTotal?Math.max(0,Math.min(100,Math.round(e.bytesDone/e.bytesTotal*100))):null}transferRate(e){const t=e.finishedAt||Date.now(),i=Math.max(1,(t-e.startedAt)/1e3),n=e.bytesDone||0;return`${Math.round(n/i/1024)} KB/s`}async uploadDroppedFile(e){this.enqueueUpload(e)}onDragOver(e){e.preventDefault(),this.dragActive=!0}onDragLeave(){this.dragActive=!1}async onDrop(e){var t;e.preventDefault(),this.dragActive=!1;const i=Array.from((null===(t=e.dataTransfer)||void 0===t?void 0:t.files)||[]);if(i.length)for(const e of i)this.enqueueUpload(e)}async downloadSelected(){this.selectedPath&&this.enqueueDownload(this.selectedPath)}enqueueUpload(e){const t=new AbortController,i=this.createTransfer(e.name,"upload",e.size,t,"queued");this.queueTransferJob(i,()=>this.runUploadTransfer(e,i))}enqueueDownload(e){const t=new AbortController,i=this.createTransfer(this.baseName(e),"download",null,t,"queued");this.queueTransferJob(i,()=>this.runDownloadTransfer(e,i))}queueTransferJob(e,t){this.transfers.unshift(e),this.transferQueue.push(async()=>{"cancelled"!==e.status&&(e.status="running",e.startedAt=Date.now(),await t())}),this.pumpTransferQueue()}async pumpTransferQueue(){if(!this.transferQueueRunning){this.transferQueueRunning=!0;try{for(;this.transferQueue.length;){const e=this.transferQueue.shift();e&&await e()}}finally{this.transferQueueRunning=!1}}}async runUploadTransfer(e,t){var i,n,s;let o=null;try{const r=this.joinPath(this.currentPath,e.name),a=Math.max(16384,Number(null!==(i=this.mcp.settings.uploadChunkBytes)&&void 0!==i?i:32768)),l=Math.max(1,Math.min(Number(null!==(n=this.mcp.settings.transferConcurrency)&&void 0!==n?n:30),Math.ceil(e.size/a)||1));o=await this.mcp.uploadChunkedBegin(r,this.asRoot,e.size,a);const d=[];for(let t=0;t<e.size;t+=a)d.push(t);let c=0,u=null;const h=Array.from({length:l},async()=>{for(var i,n;!(null===(i=t.controller)||void 0===i?void 0:i.signal.aborted);){const i=c;if(c+=1,i>=d.length)return;if(u)return;const s=d[i];try{const i=new Uint8Array(await e.slice(s,Math.min(e.size,s+a)).arrayBuffer());await this.mcp.uploadChunkedPart(o.upload_id,this.uint8ToBase64(i),s,null===(n=t.controller)||void 0===n?void 0:n.signal),t.bytesDone+=i.length}catch(e){return void(u=u||e)}}});if(await Promise.all(h),null===(s=t.controller)||void 0===s?void 0:s.signal.aborted)return await this.mcp.uploadChunkedAbort(o.upload_id).catch(()=>null),t.status="cancelled",void(t.finishedAt=Date.now());if(u)throw u;await this.mcp.uploadChunkedFinish(o.upload_id),t.bytesDone=e.size,t.status="done",t.finishedAt=Date.now(),this.notifications.notice(`Uploaded ${e.name}`),await this.refresh()}catch(e){(null==o?void 0:o.upload_id)&&await this.mcp.uploadChunkedAbort(o.upload_id).catch(()=>null),"cancelled"!==t.status&&(t.status="error",t.error=String((null==e?void 0:e.message)||e),t.finishedAt=Date.now(),this.notifications.error("Upload failed",t.error),this.status=t.error)}}async runDownloadTransfer(e,t){var i,n,s,o;let r=null,a=null;try{const o=Math.max(16384,Number(null!==(i=this.mcp.settings.downloadChunkBytes)&&void 0!==i?i:131072));if(r=await this.mcp.downloadChunkedBegin(e,this.asRoot,o),t.bytesTotal=r.total_size,a=await this.platform.startDownload(this.baseName(e)||"download.bin",420,r.total_size||0),!a)return t.status="cancelled",t.finishedAt=Date.now(),void await this.mcp.downloadChunkedClose(r.download_id).catch(()=>null);const l=[];for(let e=0;e<Number(r.total_size||0);e+=o)l.push(e);const d=Math.max(1,Math.min(Number(null!==(n=this.mcp.settings.transferConcurrency)&&void 0!==n?n:30),l.length||1)),c=new Map;let u=0,h=0,p=Promise.resolve(),m=null;const f=async()=>{for(;c.has(h);){const e=c.get(h);c.delete(h),await a.write(e),h+=e.length}},b=Array.from({length:d},async()=>{for(var e,i;!(null===(e=t.controller)||void 0===e?void 0:e.signal.aborted);){const e=u;if(u+=1,e>=l.length)return;if(m)return;const n=l[e];try{const e=await this.mcp.downloadChunkedPart(r.download_id,n,o,null===(i=t.controller)||void 0===i?void 0:i.signal),s=this.base64ToUint8(e.content_base64);c.set(n,s),t.bytesDone+=s.length,p=p.then(()=>f()),await p}catch(e){return void(m=m||e)}}});if(await Promise.all(b),await p,null===(s=t.controller)||void 0===s?void 0:s.signal.aborted)return await this.mcp.downloadChunkedClose(r.download_id).catch(()=>null),a.close(),t.status="cancelled",void(t.finishedAt=Date.now());if(m)throw m;await this.mcp.downloadChunkedClose(r.download_id),a.close(),t.status="done",t.finishedAt=Date.now(),this.notifications.notice(`Downloaded ${this.baseName(e)}`)}catch(e){null===(o=null==a?void 0:a.close)||void 0===o||o.call(a),(null==r?void 0:r.download_id)&&await this.mcp.downloadChunkedClose(r.download_id).catch(()=>null),"cancelled"!==t.status&&(t.status="error",t.error=String((null==e?void 0:e.message)||e),t.finishedAt=Date.now(),this.notifications.error("Download failed",t.error),this.status=t.error)}}clearPreview(){this.selectedPath="",this.selectedContent="",this.selectedIsText=!1}baseName(e){return e.split("/").pop()||e}isTextLike(e){const t=e.toLowerCase();return[".txt",".md",".json",".yaml",".yml",".xml",".ini",".cfg",".conf",".log",".sh",".py",".js",".ts",".tsx",".jsx",".html",".css",".scss",".c",".cpp",".h",".hpp",".java",".go",".rs",".sql"].some(e=>t.endsWith(e))}joinPath(e,t){return e&&"."!==e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}uint8ToBase64(e){let t="";for(let i=0;i<e.length;i+=32768)t+=String.fromCharCode(...e.subarray(i,i+32768));return btoa(t)}base64ToUint8(e){const t=atob(e),i=new Uint8Array(t.length);for(let e=0;e<t.length;e++)i[e]=t.charCodeAt(e);return i}readFileAsBase64(e){return new Promise((t,i)=>{const n=new FileReader;n.onload=()=>{const e=String(n.result||"");t(e.split(",")[1]||"")},n.onerror=()=>i(n.error),n.readAsDataURL(e)})}};n([(0,o.HostListener)("document:click"),s("design:type",Function),s("design:paramtypes",[]),s("design:returntype",void 0)],l.prototype,"onDocumentClick",null),n([(0,o.HostListener)("document:keydown",["$event"]),s("design:type",Function),s("design:paramtypes",[KeyboardEvent]),s("design:returntype",void 0)],l.prototype,"onKeyDown",null),l=n([(0,o.Component)({template:i(102)}),s("design:paramtypes",[o.Injector,a.BianbuMcpService,r.NotificationsService,r.PlatformService])],l),t.BianbuCloudFilesTabComponent=l},440(e,t,i){"use strict";var n=this&&this.__createBinding||(Object.create?function(e,t,i,n){void 0===n&&(n=i);var s=Object.getOwnPropertyDescriptor(t,i);s&&!("get"in s?!t.__esModule:s.writable||s.configurable)||(s={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,n,s)}:function(e,t,i,n){void 0===n&&(n=i),e[n]=t[i]}),s=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__decorate||function(e,t,i,n){var s,o=arguments.length,r=o<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,n);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(r=(o<3?s(r):o>3?s(t,i,r):s(t,i))||r);return o>3&&r&&Object.defineProperty(t,i,r),r},r=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&n(t,e,i);return s(t,e),t};Object.defineProperty(t,"__esModule",{value:!0});const a=i(860),l=i(358),d=i(182),c=r(i(650)),u=i(700),h=i(889),p=i(717),m=i(645),f=i(40),b=i(885),g=i(678),y=i(241),v=i(104);let x=class{};x=o([(0,a.NgModule)({imports:[l.CommonModule,d.FormsModule,c.default],providers:[b.BianbuMcpService,{provide:c.ConfigProvider,useClass:h.BianbuMcpConfigProvider,multi:!0},{provide:u.SettingsTabProvider,useClass:p.BianbuMcpSettingsTabProvider,multi:!0},{provide:c.CommandProvider,useClass:f.BianbuCloudCommandProvider,multi:!0},{provide:c.ProfileProvider,useClass:v.BianbuCloudProfileProvider,multi:!0}],declarations:[m.BianbuMcpSettingsComponent,g.BianbuCloudShellTabComponent,y.BianbuCloudFilesTabComponent],entryComponents:[m.BianbuMcpSettingsComponent,g.BianbuCloudShellTabComponent,y.BianbuCloudFilesTabComponent]})],x),t.default=x},885(e,t,i){"use strict";var n=this&&this.__decorate||function(e,t,i,n){var s,o=arguments.length,r=o<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,n);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(r=(o<3?s(r):o>3?s(t,i,r):s(t,i))||r);return o>3&&r&&Object.defineProperty(t,i,r),r},s=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuMcpService=void 0;const o=i(860),r=i(650),a=i(979);function l(){const e=new Error("Request aborted");return e.name="AbortError",e}class d{constructor(e,t){this.concurrency=e,this.cadenceMs=t,this.queue=[],this.waiters=[],this.started=!1}enqueue(e,t){return(null==t?void 0:t.aborted)?Promise.reject(l()):new Promise((i,n)=>{const s={run:e,resolve:i,reject:n,signal:t,cleanup:()=>{}};if(t){const e=()=>{const e=this.queue.indexOf(s);e>=0&&(this.queue.splice(e,1),s.cleanup(),n(l()))};t.addEventListener("abort",e,{once:!0}),s.cleanup=()=>t.removeEventListener("abort",e)}this.queue.push(s),this.dispatch(),this.startWorkers()})}startWorkers(){if(!this.started){this.started=!0;for(let e=0;e<this.concurrency;e++)this.workerLoop()}}dispatch(){for(;this.queue.length&&this.waiters.length;){const e=this.waiters.shift(),t=this.queue.shift();e&&t&&e(t)}}async take(){return this.queue.length?this.queue.shift():new Promise(e=>this.waiters.push(e))}async workerLoop(){for(var e;;){const t=await this.take();if(null===(e=t.signal)||void 0===e?void 0:e.aborted)t.cleanup(),t.reject(l());else try{const e=await t.run();t.resolve(e)}catch(e){t.reject(e)}finally{t.cleanup()}this.cadenceMs>0&&await new Promise(e=>setTimeout(e,this.cadenceMs))}}}let c=class{constructor(e){this.config=e,this.bundledInstallerCache=null,this.interactiveLane=new d(2,100),this.transferLane=new d(30,100),this.schedulerKey=""}get settings(){return this.config.store.bianbuMcp}get validationErrors(){return(0,a.validateConnectionSettings)(this.settings)}get bundledInstaller(){return this.bundledInstallerCache||(this.bundledInstallerCache=(0,a.loadBundledInstaller)(__dirname)),this.bundledInstallerCache}get normalizedUrl(){return(0,a.normalizeMcpUrl)(this.settings.url||"")}ensureScheduler(){var e,t,i;const n=Math.max(1,Number(null!==(e=this.settings.interactiveConcurrency)&&void 0!==e?e:2)),s=Math.max(1,Number(null!==(t=this.settings.transferConcurrency)&&void 0!==t?t:30)),o=Math.max(10,Number(null!==(i=this.settings.workerCadenceMs)&&void 0!==i?i:100)),r=`${n}:${s}:${o}`;r!==this.schedulerKey&&(this.schedulerKey=r,this.interactiveLane=new d(n,o),this.transferLane=new d(s,o))}sleep(e){return new Promise(t=>setTimeout(t,e))}isMissingPathError(e){return/(file|path) not found:/i.test(String((null==e?void 0:e.message)||e||""))}async readRemoteTextIfExists(e,t,i){try{const n=await this.readTextFile(e,t,i);return String((null==n?void 0:n.content)||"")}catch(e){if(this.isMissingPathError(e))return null;throw e}}async readRemoteLogSnippet(e,t){return String(await this.readRemoteTextIfExists(e,262144,t)||"").trim()}shouldRetryStatus(e){return[429,502,503,504].includes(e)}parseBody(e){try{return JSON.parse(e)}catch{const t=e.split(/\r?\n/).map(e=>e.trim()).filter(e=>e.startsWith("data:")).map(e=>e.slice(5).trim()).filter(Boolean);for(let e=t.length-1;e>=0;e--)try{return JSON.parse(t[e])}catch{}}return{error:{message:e||"Invalid MCP response"}}}laneForTool(e){return e.startsWith("upload_chunked_")||e.startsWith("download_chunked_")?"transfer":"interactive"}async executeRequest(e,t){var i,n,s,o,r,a,l,d;const c=this.settings,u=this.normalizedUrl;if(!u)throw new Error("MCP URL is required");const h=Math.max(0,Number(null!==(i=c.maxRetries)&&void 0!==i?i:2)),p=Math.max(100,Number(null!==(n=c.retryBaseMs)&&void 0!==n?n:1e3));let m="",f=0,b=null;for(let i=0;i<=h;i++){try{const i=await fetch(u,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json, text/event-stream","MCP-Protocol-Version":"2025-11-25","X-API-KEY":c.apiKey},body:JSON.stringify(e),signal:t}),n=await i.text();if(m=n,f=i.status,i.ok&&!this.shouldRetryStatus(i.status)){const e=this.parseBody(n);if(e.error)throw new Error(e.error.message||"MCP error");if((null===(s=e.result)||void 0===s?void 0:s.isError)&&(null===(a=null===(r=null===(o=e.result)||void 0===o?void 0:o.content)||void 0===r?void 0:r[0])||void 0===a?void 0:a.text))throw new Error(e.result.content[0].text);return void 0!==(null===(l=e.result)||void 0===l?void 0:l.structuredContent)?e.result.structuredContent:e.result}if(!this.shouldRetryStatus(i.status)){const e=this.parseBody(n);if(null===(d=e.error)||void 0===d?void 0:d.message)throw new Error(e.error.message);throw new Error(`MCP request failed with status ${i.status}: ${n}`)}}catch(e){if(b=e,(null==t?void 0:t.aborted)||"AbortError"===(null==e?void 0:e.name))throw e;if(i===h)break}i<h&&await this.sleep(p*(i+1))}throw b||new Error(`MCP request failed with status ${f}: ${m}`)}async request(e,t,i="interactive"){return this.ensureScheduler(),("transfer"===i?this.transferLane:this.interactiveLane).enqueue(()=>this.executeRequest(e,t),t)}async callTool(e,t,i,n){return this.request({jsonrpc:"2.0",id:`${e}-${Date.now()}-${Math.random().toString(16).slice(2)}`,method:"tools/call",params:{name:e,arguments:t}},i,n||this.laneForTool(e))}async healthRaw(e){return this.callTool("health",{},e,"interactive")}async getHealth(e){const t=await this.healthRaw(e);return(0,a.parseRemoteHealth)(t)}async health(){return this.healthRaw()}async runCommand(e,t,i,n){return this.callTool("run_command",{command:e,cwd:t,timeout_seconds:i,as_root:n},void 0,"interactive")}async openShellSession(e,t){return this.callTool("open_shell_session",{cwd:e,as_root:t},void 0,"interactive")}async execShellSession(e,t,i){return this.callTool("exec_shell_session",{session_id:e,command:t,timeout_seconds:i},void 0,"interactive")}async closeShellSession(e){return this.callTool("close_shell_session",{session_id:e},void 0,"interactive")}async listDirectory(e,t){return this.callTool("list_directory",{path:e,as_root:t},void 0,"interactive")}async readTextFile(e,t,i){return this.callTool("read_text_file",{path:e,max_bytes:t,encoding:"utf-8",as_root:i},void 0,"interactive")}async writeTextFile(e,t,i){return this.callTool("write_text_file",{path:e,content:t,overwrite:!0,encoding:"utf-8",as_root:i},void 0,"interactive")}async makeDirectory(e,t){return this.callTool("make_directory",{path:e,parents:!0,as_root:t},void 0,"interactive")}async deletePath(e,t,i){return this.callTool("delete_path",{path:e,recursive:t,as_root:i},void 0,"interactive")}async renamePath(e,t,i){return this.callTool("rename_path",{path:e,dest:t,as_root:i},void 0,"interactive")}async uploadBinaryFile(e,t,i,n){return this.callTool("upload_binary_file",{path:e,content_base64:t,overwrite:!0,as_root:i},n,"interactive")}async uploadChunkedBegin(e,t,i,n){return this.callTool("upload_chunked_begin",{path:e,overwrite:!0,as_root:t,total_size:i,chunk_bytes:n},void 0,"transfer")}async uploadChunkedPart(e,t,i,n){return this.callTool("upload_chunked_part",{upload_id:e,content_base64:t,offset:i},n,"transfer")}async uploadChunkedFinish(e){return this.callTool("upload_chunked_finish",{upload_id:e},void 0,"transfer")}async uploadChunkedAbort(e){return this.callTool("upload_chunked_abort",{upload_id:e},void 0,"transfer")}async downloadBinaryFile(e,t,i){return this.callTool("download_binary_file",{path:e,max_bytes:67108864,as_root:t},i,"interactive")}async downloadChunkedBegin(e,t,i=131072){return this.callTool("download_chunked_begin",{path:e,as_root:t,chunk_bytes:i},void 0,"transfer")}async downloadChunkedPart(e,t,i,n){return this.callTool("download_chunked_part",{download_id:e,offset:t,chunk_bytes:i},n,"transfer")}async downloadChunkedClose(e){return this.callTool("download_chunked_close",{download_id:e},void 0,"transfer")}async waitForHealth(e){const t=Math.max(5e3,Number(e.timeoutMs||12e4)),i=Math.max(500,Number(e.intervalMs||2e3)),n=Date.now()+t;let s=null;for(;Date.now()<n;){try{return await this.getHealth()}catch(e){s=e}await this.sleep(i)}throw new Error(`Remote health check did not recover within ${t} ms: ${String((null==s?void 0:s.message)||s||"unknown error")}`)}async waitForInstallerCompletion(e){var t,i;const n=Math.max(5e3,Number(e.timeoutMs||12e4)),s=Math.max(500,Number(e.intervalMs||2e3)),o=Date.now()+n;let r=null,l=null,d=null;for(;Date.now()<o;){try{l=await this.getHealth()}catch(e){r=e,await this.sleep(s);continue}try{const i=await this.readRemoteTextIfExists(e.statusPath,65536,e.asRoot);if(i){if(d=(0,a.parseRemoteInstallerStatus)(i),!d.ok){const i=await this.readRemoteLogSnippet(e.logPath,e.asRoot),n=i?` Remote log:\n${i}`:` Remote log path: ${e.logPath}`;throw new Error(`Remote ${e.action} exited with code ${null!==(t=d.exitCode)&&void 0!==t?t:"unknown"}.${n}`)}if(l.scriptVersion===e.expectedScriptVersion&&l.serverVersion===e.expectedServerVersion)return{health:l,status:d};r=new Error(`Remote installer completed, but health reports script=${l.scriptVersion||"unknown"} server=${l.serverVersion||"unknown"} instead of expected script=${e.expectedScriptVersion} server=${e.expectedServerVersion}`)}}catch(e){r=e}await this.sleep(s)}const c=l?await this.readRemoteLogSnippet(e.logPath,e.asRoot).catch(()=>""):"",u=d?` Last status: ok=${d.ok} exit=${null!==(i=d.exitCode)&&void 0!==i?i:"unknown"} finished_at=${d.finishedAt||"unknown"}.`:` Status file path: ${e.statusPath}.`,h=c?` Remote log:\n${c}`:` Remote log path: ${e.logPath}`;throw new Error(`Remote ${e.action} did not complete within ${n} ms.${u} Last error: ${String((null==r?void 0:r.message)||r||"unknown error")}.${h}`)}async pushInstallerAndUpgrade(e={}){var t,i,n,s,o;const r=this.bundledInstaller,l=String(e.remotePath||this.settings.installerRemotePath||"/tmp/bianbu_agent_proxy.sh").trim(),d=null!==(t=e.asRoot)&&void 0!==t?t:Boolean(this.settings.maintenanceAsRoot),c=e.action||"up",u=Number(null!==(n=null!==(i=e.reconnectPollMs)&&void 0!==i?i:this.settings.reconnectPollMs)&&void 0!==n?n:2e3),h=Number(null!==(o=null!==(s=e.healthTimeoutMs)&&void 0!==s?s:this.settings.upgradeHealthTimeoutMs)&&void 0!==o?o:12e4);if(!l)throw new Error("Remote installer path is required");const p=(0,a.remoteInstallerLogPath)(l),m=(0,a.remoteInstallerStatusPath)(l);await this.writeTextFile(l,r.script,d);const f=await this.runCommand((0,a.buildDetachedInstallerCommand)(l,c,p,m),".",30,d),{health:b,status:g}=await this.waitForInstallerCompletion({action:c,asRoot:d,expectedScriptVersion:r.metadata.scriptVersion,expectedServerVersion:r.metadata.serverVersion,intervalMs:u,logPath:p,statusPath:m,timeoutMs:h});return{action:c,asRoot:d,health:b,installer:r.metadata,logPath:p,remotePath:l,start:f,status:g,statusPath:m}}};c=n([(0,o.Injectable)(),s("design:paramtypes",[r.ConfigService])],c),t.BianbuMcpService=c},104(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuCloudProfileProvider=void 0;const n=i(650),s=i(678),o=i(241);class r extends n.ProfileProvider{constructor(){super(...arguments),this.id="bianbu-cloud",this.name="Bianbu Cloud",this.configDefaults={}}async getBuiltinProfiles(){return[{id:"bianbu-cloud-shell",type:"bianbu-cloud",name:"Bianbu Cloud Shell",icon:"terminal",color:"#2b6cb0",group:"Bianbu Cloud",disableDynamicTitle:!1,behaviorOnSessionEnd:"keep",weight:0,isBuiltin:!0,isTemplate:!1,options:{kind:"shell"}},{id:"bianbu-cloud-files",type:"bianbu-cloud",name:"Bianbu Cloud Files",icon:"folder-open",color:"#0f766e",group:"Bianbu Cloud",disableDynamicTitle:!0,behaviorOnSessionEnd:"keep",weight:1,isBuiltin:!0,isTemplate:!1,options:{kind:"files"}}]}async getNewTabParameters(e){return"files"===e.options.kind?{type:o.BianbuCloudFilesTabComponent,inputs:{profile:e}}:{type:s.BianbuCloudShellTabComponent,inputs:{profile:e}}}getSuggestedName(){return null}getDescription(e){var t;return"files"===(null===(t=e.options)||void 0===t?void 0:t.kind)?"Explorer-like file access backed by MCP file tools":"Terminal-like shell session backed by MCP run_command"}}t.BianbuCloudProfileProvider=r},979(e,t,i){"use strict";var n=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.parseRemoteHealth=t.loadBundledInstaller=t.parseRemoteInstallerStatus=t.buildDetachedInstallerCommand=t.remoteInstallerStatusPath=t.remoteInstallerLogPath=t.appendRemoteSuffix=t.remoteDirName=t.shellQuote=t.validateConnectionSettings=t.normalizeMcpUrl=void 0;const s=i(982),o=n(i(947)),r=n(i(928));function a(e){return String(e||"").trim().replace(/\/+$/,"")}function l(e){return`'${String(e).replace(/'/g,"'\"'\"'")}'`}function d(e){const t=String(e||".").replace(/\\/g,"/"),i=t.lastIndexOf("/");return i<0?".":t.slice(0,i)||"/"}function c(e,t){return`${String(e||".").replace(/\\/g,"/")}${t}`}function u(e){return c(e,".log")}function h(e){return c(e,".status.json")}function p(e){return(0,s.createHash)("sha256").update(e,"utf8").digest("hex")}t.normalizeMcpUrl=a,t.validateConnectionSettings=function(e){const t=[],i=a((null==e?void 0:e.url)||"");if(i)try{const e=new URL(i);["http:","https:"].includes(e.protocol)||t.push("MCP URL must start with http:// or https://")}catch{t.push("MCP URL is not a valid URL")}else t.push("MCP URL is required");return String((null==e?void 0:e.apiKey)||"").trim()||t.push("X-API-KEY is required"),t},t.shellQuote=l,t.remoteDirName=d,t.appendRemoteSuffix=c,t.remoteInstallerLogPath=u,t.remoteInstallerStatusPath=h,t.buildDetachedInstallerCommand=function(e,t,i=u(e),n=h(e)){const s=d(e),o=[`rm -f ${l(i)} ${l(n)}`,`bash ${l(e)} ${t} > ${l(i)} 2>&1`,"__bianbu_rc=$?","__bianbu_ok=false",'[ "$__bianbu_rc" -eq 0 ] && __bianbu_ok=true','__bianbu_ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"',`printf '{"ok":%s,"exit_code":%s,"finished_at":"%s","action":"%s","log_path":"%s"}\\n' "$__bianbu_ok" "$__bianbu_rc" "$__bianbu_ts" ${l(t)} ${l(i)} > ${l(n)}`].join("; ");return[`mkdir -p ${l(s)}`,`chmod 700 ${l(e)}`,`nohup bash -lc ${l(o)} > /dev/null 2>&1 < /dev/null & printf '__BIANBU_UPGRADE_STARTED__%s\\n' "$!"`].join(" && ")},t.parseRemoteInstallerStatus=function(e){var t;const i="string"==typeof e?JSON.parse(String(e||"").trim()||"{}"):e||{},n=null!==(t=i.exit_code)&&void 0!==t?t:i.exitCode,s=null==n?null:Number(n);return{ok:"boolean"==typeof i.ok?i.ok:0===s,exitCode:Number.isFinite(s)?s:null,finishedAt:i.finished_at||i.finishedAt||null,action:i.action||null,logPath:i.log_path||i.logPath||null,raw:i}},t.loadBundledInstaller=function(e=__dirname){let t=null;for(const i of function(e){return[r.default.resolve(e,"../assets"),r.default.resolve(e,"../../assets"),r.default.resolve(process.cwd(),"assets")]}(e)){const e=r.default.join(i,"bianbu_agent_proxy.sh"),n=r.default.join(i,"bianbu_agent_proxy.meta.json"),s=o.default.existsSync(e),a=o.default.existsSync(n);if(s||a)try{const t=o.default.readFileSync(e,"utf8"),i=JSON.parse(o.default.readFileSync(n,"utf8"));if(!(null==i?void 0:i.sha256)||!(null==i?void 0:i.scriptVersion)||!(null==i?void 0:i.serverVersion))throw new Error(`Bundled installer metadata is incomplete at ${n}`);if(p(t)!==i.sha256)throw new Error(`Bundled installer SHA-256 mismatch for ${e}`);return{script:t,metadata:i}}catch(e){t=e;break}}throw new Error(`Unable to load bundled remote installer assets: ${String((null==t?void 0:t.message)||t)}`)},t.parseRemoteHealth=function(e){var t,i,n,s,o,r,a,l;const d=null!==(s=null!==(n=null!==(t=null==e?void 0:e.structuredContent)&&void 0!==t?t:null===(i=null==e?void 0:e.result)||void 0===i?void 0:i.structuredContent)&&void 0!==n?n:e)&&void 0!==s?s:{},c=Array.isArray(d.tools)?d.tools.map(e=>String(e)):[],u={renamePath:Boolean(null===(o=null==d?void 0:d.supports)||void 0===o?void 0:o.rename_path)||c.includes("rename_path"),shellSession:Boolean(null===(r=null==d?void 0:d.supports)||void 0===r?void 0:r.shell_session)||c.includes("open_shell_session"),chunkedTransfers:Boolean(null===(a=null==d?void 0:d.supports)||void 0===a?void 0:a.chunked_transfers)||c.includes("upload_chunked_begin")&&c.includes("download_chunked_begin"),parallelChunkOffsets:Boolean(null===(l=null==d?void 0:d.supports)||void 0===l?void 0:l.parallel_chunk_offsets)};return{ok:!1!==d.ok,serverVersion:d.server_version||d.version||null,scriptVersion:d.script_version||null,transportMode:d.transport_mode||null,fileRoot:d.file_root||null,hasSudo:"boolean"==typeof d.has_sudo?d.has_sudo:null,tools:c,supports:u,raw:d}}},645(e,t,i){"use strict";var n=this&&this.__decorate||function(e,t,i,n){var s,o=arguments.length,r=o<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,n);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(r=(o<3?s(r):o>3?s(t,i,r):s(t,i))||r);return o>3&&r&&Object.defineProperty(t,i,r),r},s=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuMcpSettingsComponent=void 0;const o=i(860),r=i(650),a=i(241),l=i(678),d=i(885);let c=class{constructor(e,t,i,n){this.config=e,this.app=t,this.mcp=i,this.notifications=n,this.diagnosticsBusy=!1,this.maintenanceBusy=!1,this.remoteHealth=null,this.lastDiagnosticAt="",this.lastMaintenanceAt="",this.lastMaintenanceSummary="",this.lastError=""}get settings(){return this.config.store.bianbuMcp}get bundledInstaller(){try{return this.mcp.bundledInstaller.metadata}catch{return null}}get validationErrors(){return this.mcp.validationErrors}get sampleJson(){const e=this.settings;return JSON.stringify({mcpServers:{[e.name||"bianbu"]:{type:"http",url:e.url||"https://your-domain.example.com/mcp",headers:{"X-API-KEY":e.apiKey||"your-x-api-key"}}}},null,2)}save(){this.config.save()}openShell(){this.app.openNewTab({type:l.BianbuCloudShellTabComponent})}openFiles(){this.app.openNewTab({type:a.BianbuCloudFilesTabComponent})}async testConnection(){await this.refreshHealth("Connection verified")}async refreshHealth(e="Remote health refreshed"){this.lastError="",this.diagnosticsBusy=!0;try{this.remoteHealth=await this.mcp.getHealth(),this.lastDiagnosticAt=(new Date).toLocaleString(),this.notifications.notice(e)}catch(e){this.lastError=String((null==e?void 0:e.message)||e),this.notifications.error("Bianbu MCP health check failed",this.lastError)}finally{this.diagnosticsBusy=!1}}async pushUpgrade(e="up"){this.lastError="",this.maintenanceBusy=!0;try{const t=await this.mcp.pushInstallerAndUpgrade({action:e,asRoot:!!this.settings.maintenanceAsRoot,healthTimeoutMs:this.settings.upgradeHealthTimeoutMs,reconnectPollMs:this.settings.reconnectPollMs,remotePath:this.settings.installerRemotePath});this.remoteHealth=t.health,this.lastMaintenanceAt=(new Date).toLocaleString(),this.lastMaintenanceSummary=`Action=${t.action} remotePath=${t.remotePath} logPath=${t.logPath} script=${t.health.scriptVersion||"unknown"} server=${t.health.serverVersion||"unknown"}`,this.notifications.notice("repair"===e?"Remote repair finished":"Remote upgrade finished")}catch(t){this.lastError=String((null==t?void 0:t.message)||t),this.notifications.error("repair"===e?"Remote repair failed":"Remote upgrade failed",this.lastError)}finally{this.maintenanceBusy=!1}}};c=n([(0,o.Component)({template:i(426)}),s("design:paramtypes",[r.ConfigService,r.AppService,d.BianbuMcpService,r.NotificationsService])],c),t.BianbuMcpSettingsComponent=c},717(e,t,i){"use strict";var n=this&&this.__decorate||function(e,t,i,n){var s,o=arguments.length,r=o<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,n);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(r=(o<3?s(r):o>3?s(t,i,r):s(t,i))||r);return o>3&&r&&Object.defineProperty(t,i,r),r};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuMcpSettingsTabProvider=void 0;const s=i(860),o=i(700),r=i(645);let a=class extends o.SettingsTabProvider{constructor(){super(...arguments),this.id="bianbu-mcp",this.icon="plug",this.title="Bianbu MCP"}getComponentType(){return r.BianbuMcpSettingsComponent}};a=n([(0,s.Injectable)()],a),t.BianbuMcpSettingsTabProvider=a},864(e,t,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuShellSession=void 0;const n=i(349);class s extends n.BaseSession{constructor(e,t){super(e),this.mcp=t,this.cwd=".",this.currentLine="",this.running=!1,this.asRoot=!1,this.sessionId=null}async start(e){this.open=!0,this.cwd=(null==e?void 0:e.cwd)||".",this.asRoot=!!(null==e?void 0:e.asRoot),this.emitText("Connected to Bianbu Cloud MCP shell\r\n");try{const e=await this.mcp.openShellSession(this.cwd,this.asRoot);this.sessionId=e.session_id||null,this.cwd=e.cwd||this.cwd}catch(e){this.sessionId=null,this.logger.warn("Falling back to stateless MCP shell mode",e),this.emitText(`Shell session fallback: ${String((null==e?void 0:e.message)||e)}\r\n`)}this.emitPrompt()}resize(e,t){}write(e){const t=e.toString("utf-8");for(const e of t)this.handleChar(e)}kill(){this.running=!1,this.closeRemoteSession()}async gracefullyKillProcess(){this.running=!1,await this.closeRemoteSession()}async destroy(){await this.closeRemoteSession(),await super.destroy()}supportsWorkingDirectory(){return!0}async getWorkingDirectory(){return this.cwd}async handleChar(e){if(this.running)""===e&&this.emitText("^C\r\n");else{if("\r"===e||"\n"===e){const e=this.currentLine;return this.currentLine="",this.emitText("\r\n"),e.trim()&&await this.execute(e),void this.emitPrompt()}if(""!==e&&"\b"!==e)return""===e?(this.currentLine="",this.emitText("^C\r\n"),void this.emitPrompt()):void(e>=" "&&(this.currentLine+=e,this.emitText(e)));this.currentLine.length&&(this.currentLine=this.currentLine.slice(0,-1),this.emitText("\b \b"))}}async execute(e){this.running=!0;try{const t=this.sessionId?await this.mcp.execShellSession(this.sessionId,e,120):await this.mcp.runCommand(e,this.cwd,120,this.asRoot);t.stdout&&this.emitText(this.normalize(t.stdout)),t.stderr&&this.emitText(this.normalize(t.stderr)),0===t.exit_code||t.stderr||this.emitText(`command exited with code ${t.exit_code}\r\n`),t.cwd&&(this.cwd=t.cwd)}catch(e){this.emitText(this.normalize(String((null==e?void 0:e.message)||e)))}finally{this.running=!1}}async closeRemoteSession(){if(!this.sessionId)return;const e=this.sessionId;this.sessionId=null;try{await this.mcp.closeShellSession(e)}catch(e){this.logger.warn("Failed to close MCP shell session",e)}}emitPrompt(){const e=this.asRoot?"#":"$";this.emitText(`[bianbu ${this.cwd}]${e} `)}emitText(e){this.emitOutput(Buffer.from(e,"utf-8"))}normalize(e){return e.replace(/\r?\n/g,"\r\n")+(e.endsWith("\n")?"":"\r\n")}}t.BianbuShellSession=s},678(e,t,i){"use strict";var n=this&&this.__decorate||function(e,t,i,n){var s,o=arguments.length,r=o<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,n);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(r=(o<3?s(r):o>3?s(t,i,r):s(t,i))||r);return o>3&&r&&Object.defineProperty(t,i,r),r},s=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuCloudShellTabComponent=void 0;const o=i(860),r=i(349),a=i(650),l=i(885),d=i(864);let c=class extends r.BaseTerminalTabComponent{constructor(e,t,i){super(e),this.mcp=t,this.localNotifications=i,this.asRoot=!1,this.cwd=".",this.setTitle("Bianbu Cloud Shell"),this.icon="terminal",this.enableToolbar=!0,this.profile=this.profile||{id:"bianbu-cloud-shell",type:"bianbu-cloud",name:"Bianbu Cloud Shell",group:"Bianbu Cloud",options:{kind:"shell"},icon:"terminal",color:"#2b6cb0",disableDynamicTitle:!1,behaviorOnSessionEnd:"keep",weight:0,isBuiltin:!0,isTemplate:!1}}async onFrontendReady(){const e=new d.BianbuShellSession(this.logger,this.mcp);this.setSession(e,!0),await e.start({cwd:this.cwd,asRoot:this.asRoot}),e.releaseInitialDataBuffer(),this.localNotifications.notice("Bianbu Cloud shell ready"),super.onFrontendReady()}};n([(0,o.Input)(),s("design:type",Object)],c.prototype,"profile",void 0),n([(0,o.Input)(),s("design:type",Object)],c.prototype,"asRoot",void 0),n([(0,o.Input)(),s("design:type",Object)],c.prototype,"cwd",void 0),c=n([(0,o.Component)({template:r.BaseTerminalTabComponent.template,styles:r.BaseTerminalTabComponent.styles,animations:r.BaseTerminalTabComponent.animations}),s("design:paramtypes",[o.Injector,l.BianbuMcpService,a.NotificationsService])],c),t.BianbuCloudShellTabComponent=c},982(e){"use strict";e.exports=require("crypto")},928(e){"use strict";e.exports=require("path")},358(t){"use strict";t.exports=e},860(e){"use strict";e.exports=t},182(e){"use strict";e.exports=i},947(e){"use strict";e.exports=n},650(e){"use strict";e.exports=s},700(e){"use strict";e.exports=o},349(e){"use strict";e.exports=r}},l={},function e(t){var i=l[t];if(void 0!==i)return i.exports;var n=l[t]={exports:{}};return a[t].call(n.exports,n,n.exports,e),n.exports}(440);var a,l});
1
+ !function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("@angular/common"),require("@angular/core"),require("@angular/forms"),require("fs"),require("tabby-core"),require("tabby-settings"),require("tabby-terminal"));else if("function"==typeof define&&define.amd)define(["@angular/common","@angular/core","@angular/forms","fs","tabby-core","tabby-settings","tabby-terminal"],t);else{var n="object"==typeof exports?t(require("@angular/common"),require("@angular/core"),require("@angular/forms"),require("fs"),require("tabby-core"),require("tabby-settings"),require("tabby-terminal")):t(e["@angular/common"],e["@angular/core"],e["@angular/forms"],e.fs,e["tabby-core"],e["tabby-settings"],e["tabby-terminal"]);for(var s in n)("object"==typeof exports?exports:e)[s]=n[s]}}(global,(e,t,n,s,i,o,a)=>{return r={102(e,t,n){var s=n(169);e.exports=(s.default||s).apply(s,[])},426(e,t,n){var s=n(295);e.exports=(s.default||s).apply(s,[])},169(e,t,n){n(766),e.exports=function(e){return""+'<div style="height:100%; display:flex; flex-direction:column; background:#111827; color:#e5e7eb"><div class="topbar" style="padding:12px 14px; border-bottom:1px solid #334155; display:flex; flex-direction:column; gap:10px; background:#0f172a"><div class="toolbar" style="display:flex; gap:8px; align-items:center; flex-wrap:wrap"><button class="btn btn-sm btn-secondary" type="button" (click)="goBack()" [disabled]="busy || historyIndex &lt;= 0" title="Back (Alt+Left)" style="min-width:34px; font-weight:700">←</button><button class="btn btn-sm btn-secondary" type="button" (click)="goForward()" [disabled]="busy || historyIndex &gt;= history.length - 1" title="Forward (Alt+Right)" style="min-width:34px; font-weight:700">→</button><button class="btn btn-sm btn-secondary" type="button" (click)="goUp()" [disabled]="busy || currentPath === &quot;.&quot; || currentPath === &quot;/&quot;" title="Up (Alt+Up / Backspace)" style="min-width:34px; font-weight:700">↑</button><button class="btn btn-sm btn-secondary" type="button" (click)="refresh()" [disabled]="busy" title="Refresh (F5)" style="min-width:34px; font-weight:700">⟳</button><button class="btn btn-sm btn-secondary" type="button" (click)="openCreateDirectoryPrompt()" [disabled]="busy">New folder</button><button class="btn btn-sm btn-secondary" type="button" (click)="openCreateFilePrompt()" [disabled]="busy">New file</button><label class="btn btn-sm btn-secondary" style="margin:0">Upload<input type="file" multiple="multiple" style="display:none" (change)="uploadFile($event)" [disabled]="busy"></label><button class="btn btn-sm btn-secondary" type="button" (click)="downloadSelected()" [disabled]="busy || !selectedPath">Download</button><button class="btn btn-sm btn-secondary" type="button" (click)="transfersVisible = !transfersVisible">Transfers</button><label style="display:flex; align-items:center; gap:6px; margin-left:auto; background:#1e293b; padding:6px 10px; border-radius:8px"><input type="checkbox" [(ngModel)]="asRoot" (ngModelChange)="refresh()"><span>as_root</span></label></div><div class="address" style="display:flex; gap:8px; align-items:center"><div style="display:flex; gap:4px; align-items:center; flex-wrap:wrap; padding:10px 12px; border-radius:10px; background:#1e293b; border:1px solid #334155; flex:1"><button class="btn btn-link" type="button" *ngFor="let crumb of breadcrumbs; let i=index" (click)="navigateBreadcrumb(i)" style="padding:0 4px; min-height:auto; color:#f8fafc; text-decoration:none; font-weight:600">{{ crumb }}</button></div><input class="form-control" id="bianbu-path-input" type="text" style="max-width:420px; background:#0b1220; color:#f8fafc; border:1px solid #334155" [(ngModel)]="pathInput" (keyup.enter)="navigateToInput()" title="Ctrl+L to focus path"></div><div class="searchrow" style="display:flex; gap:8px; align-items:center"><input class="form-control" type="text" style="background:#0b1220; color:#f8fafc; border:1px solid #334155" [(ngModel)]="searchText" (ngModelChange)="applyFilter()" placeholder="Filter current directory…"><div style="color:#cbd5e1; white-space:nowrap">{{ status }}</div></div></div><div class="main" style="display:grid; grid-template-columns: minmax(460px, 48%) 1fr; gap:0; flex:1; min-height:0"><div class="pane left" style="display:flex; flex-direction:column; min-height:0; border-right:1px solid #334155; position:relative; background:#0b1220" [style.outline]="dragActive ? &quot;2px dashed #4ade80&quot; : &quot;none&quot;" (dragover)="onDragOver($event)" (dragleave)="onDragLeave()" (drop)="onDrop($event)"><div *ngIf="dragActive" style="position:absolute; inset:12px; border-radius:12px; background:rgba(74,222,128,0.12); display:flex; align-items:center; justify-content:center; z-index:10; pointer-events:none; color:#d1fae5; font-size:18px; font-weight:700">Drop file(s) to queue upload</div><div style="overflow:auto; flex:1"><table style="width:100%; border-collapse:collapse; table-layout:fixed"><thead style="position:sticky; top:0; z-index:2; background:#0f172a"><tr style="text-align:left; color:#cbd5e1; border-bottom:1px solid #334155"><th style="padding:10px 12px; width:58%">Name</th><th style="padding:10px 12px; width:16%">Type</th><th style="padding:10px 12px; width:26%">Size</th></tr></thead><tbody><tr *ngFor="let item of filteredItems; let i=index" [style.background]="i === selectedIndex ? &quot;#1d4ed8&quot; : &quot;transparent&quot;" [style.color]="i === selectedIndex ? &quot;#ffffff&quot; : &quot;#e5e7eb&quot;" style="border-bottom:1px solid #1e293b; cursor:default" (click)="selectItem(item)" (dblclick)="openItem(item)" (contextmenu)="showContextMenu($event, item)"><td style="padding:10px 12px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-weight:600">{{ baseName(item.path) }}</td><td style="padding:10px 12px">{{ item.is_dir ? \'dir\' : \'file\' }}</td><td style="padding:10px 12px">{{ item.size }}</td></tr></tbody></table></div></div><div class="pane right" style="display:flex; flex-direction:column; min-height:0; background:#111827"><div class="previewHeader" style="padding:12px 14px; border-bottom:1px solid #334155; display:flex; justify-content:space-between; gap:12px; align-items:center; background:#0f172a"><div><h4 style="margin:0; color:#f8fafc">Preview / Editor</h4><div style="color:#94a3b8; font-size:.9em">{{ selectedPath || \'Select a file from the left pane\' }}</div></div><div style="display:flex; gap:8px"><button class="btn btn-sm btn-secondary" type="button" (click)="downloadSelected()" [disabled]="busy || !selectedPath">Download</button><button class="btn btn-sm btn-primary" type="button" (click)="saveSelected()" [disabled]="busy || !selectedPath || !selectedIsText">Save</button></div></div><div style="flex:1; min-height:0; display:flex"><textarea class="form-control" *ngIf="selectedPath &amp;&amp; selectedIsText" style="flex:1; min-height:0; border:none; border-radius:0; background:#111827; color:#f8fafc; padding:14px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace" [(ngModel)]="selectedContent" [disabled]="busy" placeholder="Open a text file to preview or edit it"></textarea><div *ngIf="selectedPath &amp;&amp; !selectedIsText" style="flex:1; display:flex; align-items:center; justify-content:center; text-align:center; color:#cbd5e1; padding:24px"><div><div style="font-size:20px; font-weight:700; margin-bottom:10px">This file type is not previewed inline</div><div style="margin-bottom:16px; color:#94a3b8">Download the file to inspect it locally.</div><button class="btn btn-primary" type="button" (click)="downloadSelected()">Download file</button></div></div><div *ngIf="!selectedPath" style="flex:1; display:flex; align-items:center; justify-content:center; text-align:center; color:#94a3b8">Select a file from the left pane to preview or edit it.</div></div></div></div><div class="contextMenu" *ngIf="contextMenuVisible &amp;&amp; contextItem" [style.left.px]="contextMenuX" [style.top.px]="contextMenuY" style="position:fixed; z-index:1000; min-width:180px; background:#0f172a; border:1px solid #334155; border-radius:10px; box-shadow:0 12px 30px rgba(0,0,0,.45); padding:6px"><button class="btn btn-link" type="button" style="display:block; width:100%; text-align:left; color:#f8fafc; text-decoration:none; padding:8px 10px" (click)="openItem(contextItem); hideContextMenu()">Open</button><button class="btn btn-link" type="button" style="display:block; width:100%; text-align:left; color:#f8fafc; text-decoration:none; padding:8px 10px" (click)="openRenamePrompt(contextItem); hideContextMenu()">Rename</button><button class="btn btn-link" type="button" style="display:block; width:100%; text-align:left; color:#fca5a5; text-decoration:none; padding:8px 10px" (click)="deleteItem(contextItem); hideContextMenu()">Delete</button></div><div class="promptOverlay" *ngIf="promptVisible" style="position:fixed; inset:0; background:rgba(2,6,23,.65); z-index:1100; display:flex; align-items:center; justify-content:center"><div class="promptCard" style="width:420px; background:#0f172a; border:1px solid #334155; border-radius:14px; padding:18px; box-shadow:0 20px 50px rgba(0,0,0,.45)"><h3 style="margin-top:0; color:#f8fafc">{{ promptTitle }}</h3><input class="form-control" type="text" style="background:#0b1220; color:#f8fafc; border:1px solid #334155" [(ngModel)]="promptValue" [placeholder]="promptPlaceholder" (keyup.enter)="confirmPrompt()" (keyup.escape)="closePrompt()"><div style="display:flex; justify-content:flex-end; gap:10px; margin-top:16px"><button class="btn btn-secondary" type="button" (click)="closePrompt()">Cancel</button><button class="btn btn-primary" type="button" (click)="confirmPrompt()">Confirm</button></div></div></div><div class="transferOverlay" *ngIf="transfersVisible" style="position:fixed; right:16px; bottom:16px; width:360px; max-height:50vh; overflow:auto; z-index:900; background:#0f172a; border:1px solid #334155; border-radius:14px; box-shadow:0 20px 50px rgba(0,0,0,.45)"><div style="padding:12px 14px; border-bottom:1px solid #334155; display:flex; justify-content:space-between; align-items:center"><b style="color:#f8fafc">Transfers</b><button class="btn btn-sm btn-secondary" type="button" (click)="transfersVisible = false">Hide</button></div><div *ngIf="!transfers.length" style="padding:14px; color:#94a3b8">No uploads or downloads yet.</div><div *ngFor="let transfer of transfers" style="padding:12px 14px; border-bottom:1px solid #1e293b"><div style="display:flex; justify-content:space-between; gap:12px; margin-bottom:6px"><div style="color:#f8fafc; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis">{{ transfer.name }}</div><div style="color:#94a3b8; font-size:.9em">{{ transfer.direction }}</div></div><div style="color:#94a3b8; font-size:.9em; margin-bottom:8px">{{ transfer.status }} • {{ transferRate(transfer) }}</div><div style="height:8px; border-radius:999px; background:#1e293b; overflow:hidden; margin-bottom:8px"><div *ngIf="transferPercent(transfer) !== null" [style.width.%]="transferPercent(transfer)" style="height:100%; background:#3b82f6"></div><div *ngIf="transferPercent(transfer) === null &amp;&amp; transfer.status === &quot;running&quot;" style="height:100%; width:45%; background:linear-gradient(90deg,#334155,#60a5fa,#334155)"></div></div><div style="display:flex; justify-content:space-between; align-items:center"><div style="color:#cbd5e1; font-size:.9em">{{ transfer.bytesDone }} / {{ transfer.bytesTotal || \'unknown\' }} bytes</div><button class="btn btn-sm btn-outline-danger" type="button" *ngIf="transfer.status === &quot;running&quot;" (click)="cancelTransfer(transfer)">Cancel</button></div></div></div></div>'}},295(e,t,n){n(766),e.exports=function(e){return""+'<div style="padding: 20px; display:flex; flex-direction:column; gap:16px"><div class="hero" style="padding:18px; border-radius:12px; background:linear-gradient(135deg, rgba(25,53,103,.9), rgba(17,94,89,.85)); color:white"><h2 style="margin:0 0 8px 0">Bianbu MCP</h2><p style="margin:0; opacity:.92">Configure your Bianbu Cloud MCP endpoint, validate remote health, and push the latest bundled installer directly from Tabby.</p></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Quick actions</h3><div style="display:flex; gap:12px; flex-wrap:wrap"><button class="btn btn-primary" type="button" (click)="openShell()">Open Bianbu Cloud Shell</button><button class="btn btn-secondary" type="button" (click)="openFiles()">Open Bianbu Cloud Files</button><button class="btn btn-secondary" type="button" (click)="testConnection()" [disabled]="diagnosticsBusy || validationErrors.length &gt; 0">Test connection</button><button class="btn btn-secondary" type="button" (click)="refreshHealth()" [disabled]="diagnosticsBusy || validationErrors.length &gt; 0">Fetch remote health</button></div></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Connection settings</h3><div class="grid" style="display:grid; grid-template-columns: 190px 1fr; gap:12px; align-items:center"><label>Profile name</label><input class="form-control" type="text" [(ngModel)]="settings.name" (ngModelChange)="save()" placeholder="bianbu"><label>MCP URL</label><input class="form-control" type="text" [(ngModel)]="settings.url" (ngModelChange)="save()" placeholder="https://your-domain.example.com/mcp"><label>X-API-KEY</label><input class="form-control" type="password" [(ngModel)]="settings.apiKey" (ngModelChange)="save()" placeholder="your-x-api-key"><label>Interactive slots</label><input class="form-control" type="number" min="1" step="1" [(ngModel)]="settings.interactiveConcurrency" (ngModelChange)="save()"><label>Transfer slots</label><input class="form-control" type="number" min="1" max="30" step="1" [(ngModel)]="settings.transferConcurrency" (ngModelChange)="save()"><label>Worker cadence (ms)</label><input class="form-control" type="number" min="10" step="10" [(ngModel)]="settings.workerCadenceMs" (ngModelChange)="save()"><label>Max retries</label><input class="form-control" type="number" min="0" [(ngModel)]="settings.maxRetries" (ngModelChange)="save()"><label>Retry base (ms)</label><input class="form-control" type="number" min="0" [(ngModel)]="settings.retryBaseMs" (ngModelChange)="save()"><label>Upload chunk (bytes)</label><input class="form-control" type="number" min="16384" step="1024" [(ngModel)]="settings.uploadChunkBytes" (ngModelChange)="save()"><label>Download chunk (bytes)</label><input class="form-control" type="number" min="16384" step="1024" [(ngModel)]="settings.downloadChunkBytes" (ngModelChange)="save()"><label>Notes</label><textarea class="form-control" rows="3" [(ngModel)]="settings.notes" (ngModelChange)="save()" placeholder="Optional notes"></textarea></div></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Remote maintenance</h3><div class="grid" style="display:grid; grid-template-columns: 190px 1fr; gap:12px; align-items:center"><label>Installer remote path</label><input class="form-control" type="text" [(ngModel)]="settings.installerRemotePath" (ngModelChange)="save()" placeholder="/tmp/bianbu_agent_proxy.sh"><label>Maintenance as root</label><input type="checkbox" [(ngModel)]="settings.maintenanceAsRoot" (ngModelChange)="save()"><label>Reconnect poll (ms)</label><input class="form-control" type="number" min="500" [(ngModel)]="settings.reconnectPollMs" (ngModelChange)="save()"><label>Health timeout (ms)</label><input class="form-control" type="number" min="5000" [(ngModel)]="settings.upgradeHealthTimeoutMs" (ngModelChange)="save()"></div><div style="display:flex; gap:12px; flex-wrap:wrap; margin-top:16px"><button class="btn btn-primary" type="button" (click)="pushUpgrade(&quot;up&quot;)" [disabled]="maintenanceBusy || validationErrors.length &gt; 0">Push latest installer + upgrade</button><button class="btn btn-secondary" type="button" (click)="pushUpgrade(&quot;repair&quot;)" [disabled]="maintenanceBusy || validationErrors.length &gt; 0">Push latest installer + repair</button></div><div *ngIf="lastMaintenanceAt" style="margin-top:12px; color:#cbd5e1"><b>Last maintenance:</b> {{ lastMaintenanceAt }}<br><span>{{ lastMaintenanceSummary }}</span><div *ngIf="latestMaintenanceSession" style="margin-top:8px"><div>Session: {{ latestMaintenanceSession.sessionName }}</div><div style="display:flex; gap:12px; flex-wrap:wrap; margin-top:8px"><button class="btn btn-secondary" type="button" (click)="downloadLocalMaintenanceLog()" [disabled]="maintenanceBusy">Download local session log</button><button class="btn btn-secondary" type="button" (click)="downloadRemoteMaintenanceLog()" [disabled]="maintenanceBusy || validationErrors.length &gt; 0">Download remote session log</button></div></div></div></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Release diagnostics</h3><div style="display:grid; grid-template-columns: repeat(2, minmax(220px, 1fr)); gap:12px"><div style="padding:12px; border-radius:10px; background:rgba(255,255,255,0.02)"><b>Bundled installer</b><div style="margin-top:8px; opacity:.9">Script version: {{ bundledInstaller?.scriptVersion || \'unavailable\' }}</div><div style="opacity:.9">Server version: {{ bundledInstaller?.serverVersion || \'unavailable\' }}</div><div style="opacity:.9">SHA-256: {{ bundledInstaller?.sha256 || \'unavailable\' }}</div></div><div style="padding:12px; border-radius:10px; background:rgba(255,255,255,0.02)"><b>Remote status</b><div style="margin-top:8px; opacity:.9">Remote script version: {{ remoteHealth?.scriptVersion || \'unknown\' }}</div><div style="opacity:.9">Remote server version: {{ remoteHealth?.serverVersion || \'unknown\' }}</div><div style="opacity:.9">Transport: {{ remoteHealth?.transportMode || \'unknown\' }}</div><div style="opacity:.9">FILE_ROOT: {{ remoteHealth?.fileRoot || \'unknown\' }}</div><div style="opacity:.9">Supports rename_path: {{ remoteHealth?.supports?.renamePath ? \'yes\' : \'no\' }}</div><div style="opacity:.9">Supports shell sessions: {{ remoteHealth?.supports?.shellSession ? \'yes\' : \'no\' }}</div><div style="opacity:.9">Supports chunked transfers: {{ remoteHealth?.supports?.chunkedTransfers ? \'yes\' : \'no\' }}</div><div style="opacity:.9">Supports parallel chunk offsets: {{ remoteHealth?.supports?.parallelChunkOffsets ? \'yes\' : \'no\' }}</div><div style="opacity:.9">Last diagnostic: {{ lastDiagnosticAt || \'never\' }}</div></div></div><div *ngIf="validationErrors.length" style="margin-top:16px; color:#fca5a5"><b>Configuration issues</b><ul style="margin:8px 0 0 18px"><li *ngFor="let error of validationErrors">{{ error }}</li></ul></div><div *ngIf="lastError" style="margin-top:16px; color:#fca5a5; white-space:pre-wrap"><b>Last error</b><div style="margin-top:8px">{{ lastError }}</div></div></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Copyable MCP snippet</h3><pre style="white-space: pre-wrap; margin-bottom:0">{{ sampleJson }}</pre></div><div class="card" style="padding:16px; border-radius:10px; background: rgba(255,255,255,0.03); border: 1px solid rgba(255,255,255,0.08)"><h3 style="margin-top:0">Reminder</h3><ul style="margin-bottom:0"><li>MCP URL is typically <code>https://&lt;domain&gt;/mcp</code></li><li><code>X-API-KEY</code> is copied from the Bianbu Cloud API Key page</li><li>Push-upgrade uploads the bundled installer to the configured remote path, launches it remotely, then waits for health to recover</li><li>Shell and Files tabs are UI views backed by MCP calls, not native SSH/SFTP transport</li></ul></div></div>'}},766(e,t,n){"use strict";var s=Object.prototype.hasOwnProperty;function i(e,t){return Array.isArray(e)?function(e,t){for(var n,s="",o="",a=Array.isArray(t),r=0;r<e.length;r++)(n=i(e[r]))&&(a&&t[r]&&(n=l(n)),s=s+o+n,o=" ");return s}(e,t):e&&"object"==typeof e?function(e){var t="",n="";for(var i in e)i&&e[i]&&s.call(e,i)&&(t=t+n+i,n=" ");return t}(e):e||""}function o(e){if(!e)return"";if("object"==typeof e){var t="";for(var n in e)s.call(e,n)&&(t=t+n+":"+e[n]+";");return t}return e+""}function a(e,t,n,s){if(!1===t||null==t||!t&&("class"===e||"style"===e))return"";if(!0===t)return" "+(s?e:e+'="'+e+'"');var i=typeof t;return"object"!==i&&"function"!==i||"function"!=typeof t.toJSON||(t=t.toJSON()),"string"==typeof t||(t=JSON.stringify(t),n||-1===t.indexOf('"'))?(n&&(t=l(t))," "+e+'="'+t+'"'):" "+e+"='"+t.replace(/'/g,"&#39;")+"'"}t.merge=function e(t,n){if(1===arguments.length){for(var s=t[0],i=1;i<t.length;i++)s=e(s,t[i]);return s}for(var a in n)if("class"===a){var r=t[a]||[];t[a]=(Array.isArray(r)?r:[r]).concat(n[a]||[])}else if("style"===a){r=(r=o(t[a]))&&";"!==r[r.length-1]?r+";":r;var l=o(n[a]);l=l&&";"!==l[l.length-1]?l+";":l,t[a]=r+l}else t[a]=n[a];return t},t.classes=i,t.style=o,t.attr=a,t.attrs=function(e,t){var n="";for(var r in e)if(s.call(e,r)){var l=e[r];if("class"===r){n=a(r,l=i(l),!1,t)+n;continue}"style"===r&&(l=o(l)),n+=a(r,l,!1,t)}return n};var r=/["&<>]/;function l(e){var t=""+e,n=r.exec(t);if(!n)return e;var s,i,o,a="";for(s=n.index,i=0;s<t.length;s++){switch(t.charCodeAt(s)){case 34:o="&quot;";break;case 38:o="&amp;";break;case 60:o="&lt;";break;case 62:o="&gt;";break;default:continue}i!==s&&(a+=t.substring(i,s)),i=s+1,a+=o}return i!==s?a+t.substring(i,s):a}t.escape=l,t.rethrow=function e(t,s,i,o){if(!(t instanceof Error))throw t;if(!("undefined"==typeof window&&s||o))throw t.message+=" on line "+i,t;try{o=o||n(947).readFileSync(s,"utf8")}catch(n){e(t,null,i)}var a=3,r=o.split("\n"),l=Math.max(i-a,0),d=Math.min(r.length,i+a);throw a=r.slice(l,d).map(function(e,t){var n=t+l+1;return(n==i?" > ":" ")+n+"| "+e}).join("\n"),t.path=s,t.message=(s||"Pug")+":"+i+"\n"+a+"\n\n"+t.message,t}},40(e,t,n){"use strict";var s=this&&this.__decorate||function(e,t,n,s){var i,o=arguments.length,a=o<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,n):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,s);else for(var r=e.length-1;r>=0;r--)(i=e[r])&&(a=(o<3?i(a):o>3?i(t,n,a):i(t,n))||a);return o>3&&a&&Object.defineProperty(t,n,a),a},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuCloudCommandProvider=void 0;const o=n(860),a=n(650),r=n(678),l=n(241);let d=class extends a.CommandProvider{constructor(e){super(),this.app=e}async provide(){return[{id:"bianbu-cloud-shell",label:"Open Bianbu Cloud Shell",sublabel:"Remote shell over MCP run_command",locations:[a.CommandLocation.StartPage],run:async()=>{this.app.openNewTab({type:r.BianbuCloudShellTabComponent})}},{id:"bianbu-cloud-files",label:"Open Bianbu Cloud Files",sublabel:"File manager over MCP file tools",locations:[a.CommandLocation.StartPage],run:async()=>{this.app.openNewTab({type:l.BianbuCloudFilesTabComponent})}}]}};d=s([(0,o.Injectable)(),i("design:paramtypes",[a.AppService])],d),t.BianbuCloudCommandProvider=d},889(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuMcpConfigProvider=void 0;const s=n(650);class i extends s.ConfigProvider{constructor(){super(...arguments),this.defaults={bianbuMcp:{name:"bianbu",url:"",apiKey:"",maxRetries:2,retryBaseMs:1e3,interactiveConcurrency:2,transferConcurrency:30,workerCadenceMs:100,uploadChunkBytes:32768,downloadChunkBytes:131072,notes:"",installerRemotePath:"/tmp/bianbu_agent_proxy.sh",maintenanceAsRoot:!0,reconnectPollMs:2e3,upgradeHealthTimeoutMs:12e4}}}}t.BianbuMcpConfigProvider=i},241(e,t,n){"use strict";var s=this&&this.__decorate||function(e,t,n,s){var i,o=arguments.length,a=o<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,n):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,s);else for(var r=e.length-1;r>=0;r--)(i=e[r])&&(a=(o<3?i(a):o>3?i(t,n,a):i(t,n))||a);return o>3&&a&&Object.defineProperty(t,n,a),a},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuCloudFilesTabComponent=void 0;const o=n(860),a=n(650),r=n(885);let l=class extends a.BaseTabComponent{constructor(e,t,n,s){super(e),this.mcp=t,this.notifications=n,this.platform=s,this.currentPath=".",this.pathInput=".",this.asRoot=!1,this.busy=!1,this.items=[],this.filteredItems=[],this.selectedPath="",this.selectedContent="",this.selectedIsText=!1,this.status="Ready",this.dragActive=!1,this.searchText="",this.history=["."],this.historyIndex=0,this.selectedIndex=-1,this.contextMenuVisible=!1,this.contextMenuX=0,this.contextMenuY=0,this.contextItem=null,this.promptVisible=!1,this.promptTitle="",this.promptPlaceholder="",this.promptValue="",this.promptAction=null,this.promptTarget=null,this.transfersVisible=!0,this.transfers=[],this.transferSeq=1,this.transferQueue=[],this.transferQueueRunning=!1,this.setTitle("Bianbu Cloud Files"),this.icon="folder-open"}ngOnInit(){this.refresh().catch(()=>null)}get breadcrumbs(){if(!this.currentPath||"."===this.currentPath)return["."];const e=this.currentPath.replace(/\\/g,"/");if("/"===e)return["/"];const t=e.split("/").filter(Boolean);return e.startsWith("/")?["/"].concat(t):t}get selectedItem(){var e;return null!==(e=this.filteredItems[this.selectedIndex])&&void 0!==e?e:null}onDocumentClick(){this.hideContextMenu()}onKeyDown(e){var t;const n=e.target,s=null===(t=null==n?void 0:n.tagName)||void 0===t?void 0:t.toLowerCase(),i=["input","textarea"].includes(s||"")||this.promptVisible;if("F5"===e.key)return e.preventDefault(),void this.refresh();if(e.altKey&&"ArrowUp"===e.key)return e.preventDefault(),void this.goUp();if(e.altKey&&"ArrowLeft"===e.key)return e.preventDefault(),void this.goBack();if(e.altKey&&"ArrowRight"===e.key)return e.preventDefault(),void this.goForward();if(e.ctrlKey&&"l"===e.key.toLowerCase()){e.preventDefault();const t=document.getElementById("bianbu-path-input");return null==t||t.focus(),void(null==t||t.select())}return e.ctrlKey&&e.shiftKey&&"n"===e.key.toLowerCase()?(e.preventDefault(),void this.openCreateDirectoryPrompt()):e.ctrlKey&&!e.shiftKey&&"n"===e.key.toLowerCase()?(e.preventDefault(),void this.openCreateFilePrompt()):i?void 0:"Backspace"===e.key?(e.preventDefault(),void this.goUp()):"Delete"===e.key&&this.selectedItem?(e.preventDefault(),void this.deleteItem(this.selectedItem)):"F2"===e.key&&this.selectedItem?(e.preventDefault(),void this.openRenamePrompt(this.selectedItem)):"Enter"===e.key&&this.selectedItem?(e.preventDefault(),void this.openItem(this.selectedItem)):"ArrowDown"===e.key?(e.preventDefault(),void this.moveSelection(1)):"ArrowUp"===e.key?(e.preventDefault(),void this.moveSelection(-1)):void 0}async refresh(){this.busy=!0,this.status="Loading directory…",this.pathInput=this.currentPath,this.setProgress(.25);try{const e=await this.mcp.listDirectory(this.currentPath||".",this.asRoot);this.items=e.items||[],this.applyFilter(),this.status=`Loaded ${this.items.length} item(s)`,this.filteredItems.length&&this.selectedIndex<0&&(this.selectedIndex=0)}catch(e){this.notifications.error("Failed to list directory",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1,this.setProgress(null)}}applyFilter(){const e=this.searchText.trim().toLowerCase();this.filteredItems=this.items.filter(t=>{const n=this.baseName(t.path).toLowerCase();return!e||n.includes(e)||t.path.toLowerCase().includes(e)}),this.selectedIndex>=this.filteredItems.length&&(this.selectedIndex=this.filteredItems.length-1)}navigateToInput(){this.navigate(this.pathInput||".")}navigate(e,t=!0){this.currentPath=e||".",this.pathInput=this.currentPath,t&&(this.history=this.history.slice(0,this.historyIndex+1),this.history.push(this.currentPath),this.historyIndex=this.history.length-1),this.clearPreview(),this.selectedIndex=-1,this.refresh()}navigateBreadcrumb(e){if("."===this.currentPath&&0===e)return void this.navigate(".",!0);const t=this.breadcrumbs;if("/"===t[0])return 0===e?void this.navigate("/",!0):void this.navigate("/"+t.slice(1,e+1).join("/"),!0);this.navigate(t.slice(0,e+1).join("/"),!0)}goBack(){this.historyIndex<=0||(this.historyIndex-=1,this.navigate(this.history[this.historyIndex],!1))}goForward(){this.historyIndex>=this.history.length-1||(this.historyIndex+=1,this.navigate(this.history[this.historyIndex],!1))}goUp(){if("."===this.currentPath||"/"===this.currentPath)return;const e=this.currentPath.replace(/\\/g,"/"),t=e.split("/").slice(0,-1).join("/")||(e.startsWith("/")?"/":".");this.navigate(t,!0)}moveSelection(e){this.filteredItems.length?this.selectedIndex<0?this.selectedIndex=0:this.selectedIndex=Math.max(0,Math.min(this.filteredItems.length-1,this.selectedIndex+e)):this.selectedIndex=-1}selectItem(e){this.selectedIndex=this.filteredItems.indexOf(e)}async openItem(e){if(this.selectItem(e),e.is_dir)this.navigate(e.path,!0);else{this.busy=!0,this.status=`Opening ${e.path}`,this.selectedPath=e.path,this.selectedIsText=this.isTextLike(e.path);try{if(this.selectedIsText){const t=await this.mcp.readTextFile(e.path,524288,this.asRoot);this.selectedContent=t.content||"",this.status=`Opened ${e.path}`}else this.selectedContent="",this.status=`Binary / non-text file selected: ${e.path}`}catch(e){this.notifications.error("Failed to open file",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e),this.selectedContent=""}finally{this.busy=!1}}}async saveSelected(){if(this.selectedPath&&this.selectedIsText){this.busy=!0,this.status=`Saving ${this.selectedPath}`;try{await this.mcp.writeTextFile(this.selectedPath,this.selectedContent,this.asRoot),this.notifications.notice("File saved"),this.status=`Saved ${this.selectedPath}`,await this.refresh()}catch(e){this.notifications.error("Failed to save file",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1}}}openCreateDirectoryPrompt(){this.promptVisible=!0,this.promptTitle="Create new folder",this.promptPlaceholder="Folder name",this.promptValue="",this.promptAction="mkdir",this.promptTarget=null}openCreateFilePrompt(){this.promptVisible=!0,this.promptTitle="Create new file",this.promptPlaceholder="File name",this.promptValue="",this.promptAction="newfile",this.promptTarget=null}openRenamePrompt(e){this.promptVisible=!0,this.promptTitle="Rename",this.promptPlaceholder="New name",this.promptValue=this.baseName(e.path),this.promptAction="rename",this.promptTarget=e}closePrompt(){this.promptVisible=!1,this.promptAction=null,this.promptTarget=null,this.promptValue=""}async confirmPrompt(){const e=this.promptValue.trim();if(e&&this.promptAction)return"mkdir"===this.promptAction?(await this.createDirectory(e),void this.closePrompt()):"newfile"===this.promptAction?(await this.createFile(e),void this.closePrompt()):void("rename"===this.promptAction&&this.promptTarget&&(await this.renameItem(this.promptTarget,e),this.closePrompt()));this.closePrompt()}async createDirectory(e){const t=this.joinPath(this.currentPath,e);this.busy=!0;try{await this.mcp.makeDirectory(t,this.asRoot),await this.refresh(),this.notifications.notice("Directory created")}catch(e){this.notifications.error("Failed to create directory",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1}}async createFile(e){const t=this.joinPath(this.currentPath,e);this.busy=!0;try{await this.mcp.writeTextFile(t,"",this.asRoot),await this.refresh(),this.notifications.notice("File created")}catch(e){this.notifications.error("Failed to create file",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1}}async renameItem(e,t){const n=e.path.split("/").slice(0,-1).join("/")||".",s=this.joinPath(n,t);try{await this.mcp.renamePath(e.path,s,this.asRoot),this.notifications.notice("Path renamed"),this.selectedPath===e.path&&(this.selectedPath=s),await this.refresh()}catch(e){const t=String((null==e?void 0:e.message)||e);this.notifications.error("Rename failed",t),this.status=t}}async deleteItem(e){this.busy=!0;try{await this.mcp.deletePath(e.path,!!e.is_dir,this.asRoot),this.selectedPath===e.path&&this.clearPreview(),await this.refresh(),this.notifications.notice("Path deleted")}catch(e){this.notifications.error("Failed to delete path",String((null==e?void 0:e.message)||e)),this.status=String((null==e?void 0:e.message)||e)}finally{this.busy=!1}}showContextMenu(e,t){e.preventDefault(),e.stopPropagation(),this.selectItem(t),this.contextItem=t,this.contextMenuVisible=!0,this.contextMenuX=e.clientX,this.contextMenuY=e.clientY}hideContextMenu(){this.contextMenuVisible=!1,this.contextItem=null}async uploadFile(e){const t=e.target,n=Array.from(t.files||[]);if(n.length){for(const e of n)this.enqueueUpload(e);t.value=""}}createTransfer(e,t,n,s,i="queued"){return{id:this.transferSeq++,name:e,direction:t,status:i,bytesDone:0,bytesTotal:n,startedAt:Date.now(),controller:s}}cancelTransfer(e){var t;"running"!==e.status&&"queued"!==e.status||(null===(t=e.controller)||void 0===t||t.abort(),e.status="cancelled",e.finishedAt=Date.now())}transferPercent(e){return e.bytesTotal?Math.max(0,Math.min(100,Math.round(e.bytesDone/e.bytesTotal*100))):null}transferRate(e){const t=e.finishedAt||Date.now(),n=Math.max(1,(t-e.startedAt)/1e3),s=e.bytesDone||0;return`${Math.round(s/n/1024)} KB/s`}async uploadDroppedFile(e){this.enqueueUpload(e)}onDragOver(e){e.preventDefault(),this.dragActive=!0}onDragLeave(){this.dragActive=!1}async onDrop(e){var t;e.preventDefault(),this.dragActive=!1;const n=Array.from((null===(t=e.dataTransfer)||void 0===t?void 0:t.files)||[]);if(n.length)for(const e of n)this.enqueueUpload(e)}async downloadSelected(){this.selectedPath&&this.enqueueDownload(this.selectedPath)}enqueueUpload(e){const t=new AbortController,n=this.createTransfer(e.name,"upload",e.size,t,"queued");this.queueTransferJob(n,()=>this.runUploadTransfer(e,n))}enqueueDownload(e){const t=new AbortController,n=this.createTransfer(this.baseName(e),"download",null,t,"queued");this.queueTransferJob(n,()=>this.runDownloadTransfer(e,n))}queueTransferJob(e,t){this.transfers.unshift(e),this.transferQueue.push(async()=>{"cancelled"!==e.status&&(e.status="running",e.startedAt=Date.now(),await t())}),this.pumpTransferQueue()}async pumpTransferQueue(){if(!this.transferQueueRunning){this.transferQueueRunning=!0;try{for(;this.transferQueue.length;){const e=this.transferQueue.shift();e&&await e()}}finally{this.transferQueueRunning=!1}}}async runUploadTransfer(e,t){var n,s,i;let o=null;try{const a=this.joinPath(this.currentPath,e.name),r=Math.max(16384,Number(null!==(n=this.mcp.settings.uploadChunkBytes)&&void 0!==n?n:32768)),l=Math.max(1,Math.min(Number(null!==(s=this.mcp.settings.transferConcurrency)&&void 0!==s?s:30),Math.ceil(e.size/r)||1));o=await this.mcp.uploadChunkedBegin(a,this.asRoot,e.size,r);const d=[];for(let t=0;t<e.size;t+=r)d.push(t);let c=null;const u=Array.from({length:l},async()=>{for(var n,s;!(null===(n=t.controller)||void 0===n?void 0:n.signal.aborted);){const n=d.shift();if(void 0===n)return;if(c)return;try{const i=new Uint8Array(await e.slice(n,Math.min(e.size,n+r)).arrayBuffer());await this.mcp.uploadChunkedPart(o.upload_id,this.uint8ToBase64(i),n,null===(s=t.controller)||void 0===s?void 0:s.signal),t.bytesDone+=i.length}catch(e){return void(c=c||e)}}});if(await Promise.all(u),null===(i=t.controller)||void 0===i?void 0:i.signal.aborted)return await this.mcp.uploadChunkedAbort(o.upload_id).catch(()=>null),t.status="cancelled",void(t.finishedAt=Date.now());if(c)throw c;await this.mcp.uploadChunkedFinish(o.upload_id),t.bytesDone=e.size,t.status="done",t.finishedAt=Date.now(),this.notifications.notice(`Uploaded ${e.name}`),await this.refresh()}catch(e){(null==o?void 0:o.upload_id)&&await this.mcp.uploadChunkedAbort(o.upload_id).catch(()=>null),"cancelled"!==t.status&&(t.status="error",t.error=String((null==e?void 0:e.message)||e),t.finishedAt=Date.now(),this.notifications.error("Upload failed",t.error),this.status=t.error)}}async runDownloadTransfer(e,t){var n,s,i,o;let a=null,r=null;try{const o=Math.max(16384,Number(null!==(n=this.mcp.settings.downloadChunkBytes)&&void 0!==n?n:131072));if(a=await this.mcp.downloadChunkedBegin(e,this.asRoot,o),t.bytesTotal=a.total_size,r=await this.platform.startDownload(this.baseName(e)||"download.bin",420,a.total_size||0),!r)return t.status="cancelled",t.finishedAt=Date.now(),void await this.mcp.downloadChunkedClose(a.download_id).catch(()=>null);const l=[];for(let e=0;e<Number(a.total_size||0);e+=o)l.push(e);const d=Math.max(1,Math.min(Number(null!==(s=this.mcp.settings.transferConcurrency)&&void 0!==s?s:30),l.length||1)),c=new Map;let u=0,h=Promise.resolve(),p=null;const m=async()=>{for(;c.has(u);){const e=c.get(u);c.delete(u),await r.write(e),u+=e.length}},f=Array.from({length:d},async()=>{for(var e,n;!(null===(e=t.controller)||void 0===e?void 0:e.signal.aborted);){const e=l.shift();if(void 0===e)return;if(p)return;try{const s=await this.mcp.downloadChunkedPart(a.download_id,e,o,null===(n=t.controller)||void 0===n?void 0:n.signal),i=this.base64ToUint8(s.content_base64);c.set(e,i),t.bytesDone+=i.length,h=h.then(()=>m()),await h}catch(e){return void(p=p||e)}}});if(await Promise.all(f),await h,null===(i=t.controller)||void 0===i?void 0:i.signal.aborted)return await this.mcp.downloadChunkedClose(a.download_id).catch(()=>null),r.close(),t.status="cancelled",void(t.finishedAt=Date.now());if(p)throw p;await this.mcp.downloadChunkedClose(a.download_id),r.close(),t.status="done",t.finishedAt=Date.now(),this.notifications.notice(`Downloaded ${this.baseName(e)}`)}catch(e){null===(o=null==r?void 0:r.close)||void 0===o||o.call(r),(null==a?void 0:a.download_id)&&await this.mcp.downloadChunkedClose(a.download_id).catch(()=>null),"cancelled"!==t.status&&(t.status="error",t.error=String((null==e?void 0:e.message)||e),t.finishedAt=Date.now(),this.notifications.error("Download failed",t.error),this.status=t.error)}}clearPreview(){this.selectedPath="",this.selectedContent="",this.selectedIsText=!1}baseName(e){return e.split("/").pop()||e}isTextLike(e){const t=e.toLowerCase();return[".txt",".md",".json",".yaml",".yml",".xml",".ini",".cfg",".conf",".log",".sh",".py",".js",".ts",".tsx",".jsx",".html",".css",".scss",".c",".cpp",".h",".hpp",".java",".go",".rs",".sql"].some(e=>t.endsWith(e))}joinPath(e,t){return e&&"."!==e?e.endsWith("/")?`${e}${t}`:`${e}/${t}`:t}uint8ToBase64(e){let t="";for(let n=0;n<e.length;n+=32768)t+=String.fromCharCode(...e.subarray(n,n+32768));return btoa(t)}base64ToUint8(e){const t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}};s([(0,o.HostListener)("document:click"),i("design:type",Function),i("design:paramtypes",[]),i("design:returntype",void 0)],l.prototype,"onDocumentClick",null),s([(0,o.HostListener)("document:keydown",["$event"]),i("design:type",Function),i("design:paramtypes",[KeyboardEvent]),i("design:returntype",void 0)],l.prototype,"onKeyDown",null),l=s([(0,o.Component)({template:n(102)}),i("design:paramtypes",[o.Injector,r.BianbuMcpService,a.NotificationsService,a.PlatformService])],l),t.BianbuCloudFilesTabComponent=l},440(e,t,n){"use strict";var s=this&&this.__createBinding||(Object.create?function(e,t,n,s){void 0===s&&(s=n);var i=Object.getOwnPropertyDescriptor(t,n);i&&!("get"in i?!t.__esModule:i.writable||i.configurable)||(i={enumerable:!0,get:function(){return t[n]}}),Object.defineProperty(e,s,i)}:function(e,t,n,s){void 0===s&&(s=n),e[s]=t[n]}),i=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__decorate||function(e,t,n,s){var i,o=arguments.length,a=o<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,n):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,s);else for(var r=e.length-1;r>=0;r--)(i=e[r])&&(a=(o<3?i(a):o>3?i(t,n,a):i(t,n))||a);return o>3&&a&&Object.defineProperty(t,n,a),a},a=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)"default"!==n&&Object.prototype.hasOwnProperty.call(e,n)&&s(t,e,n);return i(t,e),t};Object.defineProperty(t,"__esModule",{value:!0});const r=n(860),l=n(358),d=n(182),c=a(n(650)),u=n(700),h=n(889),p=n(717),m=n(645),f=n(40),g=n(885),b=n(678),y=n(241),v=n(104);let w=class{};w=o([(0,r.NgModule)({imports:[l.CommonModule,d.FormsModule,c.default],providers:[g.BianbuMcpService,{provide:c.ConfigProvider,useClass:h.BianbuMcpConfigProvider,multi:!0},{provide:u.SettingsTabProvider,useClass:p.BianbuMcpSettingsTabProvider,multi:!0},{provide:c.CommandProvider,useClass:f.BianbuCloudCommandProvider,multi:!0},{provide:c.ProfileProvider,useClass:v.BianbuCloudProfileProvider,multi:!0}],declarations:[m.BianbuMcpSettingsComponent,b.BianbuCloudShellTabComponent,y.BianbuCloudFilesTabComponent],entryComponents:[m.BianbuMcpSettingsComponent,b.BianbuCloudShellTabComponent,y.BianbuCloudFilesTabComponent]})],w),t.default=w},885(e,t,n){"use strict";var s=this&&this.__decorate||function(e,t,n,s){var i,o=arguments.length,a=o<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,n):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,s);else for(var r=e.length-1;r>=0;r--)(i=e[r])&&(a=(o<3?i(a):o>3?i(t,n,a):i(t,n))||a);return o>3&&a&&Object.defineProperty(t,n,a),a},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuMcpService=void 0;const o=n(860),a=n(650),r=n(979),l=n(935);function d(){const e=new Error("Request aborted");return e.name="AbortError",e}class c{constructor(e,t){this.concurrency=e,this.cadenceMs=t,this.queue=[],this.waiters=[],this.started=!1,this.stopped=!1}stop(){this.stopped=!0;const e={run:()=>Promise.resolve(),resolve:()=>{},reject:()=>{},cleanup:()=>{}};for(const t of this.waiters)t(e);this.waiters.length=0}enqueue(e,t){return(null==t?void 0:t.aborted)?Promise.reject(d()):new Promise((n,s)=>{const i={run:e,resolve:n,reject:s,signal:t,cleanup:()=>{}};if(t){const e=()=>{const e=this.queue.indexOf(i);e>=0&&(this.queue.splice(e,1),i.cleanup(),s(d()))};t.addEventListener("abort",e,{once:!0}),i.cleanup=()=>t.removeEventListener("abort",e)}this.queue.push(i),this.dispatch(),this.startWorkers()})}startWorkers(){if(!this.started){this.started=!0;for(let e=0;e<this.concurrency;e++)this.workerLoop()}}dispatch(){for(;this.queue.length&&this.waiters.length;){const e=this.waiters.shift(),t=this.queue.shift();e&&t&&e(t)}}async take(){return this.queue.length?this.queue.shift():new Promise(e=>this.waiters.push(e))}async workerLoop(){for(var e;!this.stopped;){const t=await this.take();if(this.stopped)return;if(null===(e=t.signal)||void 0===e?void 0:e.aborted)t.cleanup(),t.reject(d());else try{const e=await t.run();t.resolve(e)}catch(e){t.reject(e)}finally{t.cleanup()}this.cadenceMs>0&&await new Promise(e=>setTimeout(e,this.cadenceMs))}}}let u=class{constructor(e){this.config=e,this.bundledInstallerCache=null,this.interactiveLane=new c(2,100),this.latestMaintenanceSessionValue=null,this.transferLane=new c(30,100),this.schedulerKey=""}get settings(){return this.config.store.bianbuMcp}get validationErrors(){return(0,r.validateConnectionSettings)(this.settings)}get bundledInstaller(){return this.bundledInstallerCache||(this.bundledInstallerCache=(0,r.loadBundledInstaller)(__dirname)),this.bundledInstallerCache}get normalizedUrl(){return(0,r.normalizeMcpUrl)(this.settings.url||"")}get latestMaintenanceSession(){return this.latestMaintenanceSessionValue}ensureScheduler(){var e,t,n;const s=Math.max(1,Number(null!==(e=this.settings.interactiveConcurrency)&&void 0!==e?e:2)),i=Math.max(1,Number(null!==(t=this.settings.transferConcurrency)&&void 0!==t?t:30)),o=Math.max(10,Number(null!==(n=this.settings.workerCadenceMs)&&void 0!==n?n:100)),a=`${s}:${i}:${o}`;a!==this.schedulerKey&&(this.schedulerKey=a,this.interactiveLane.stop(),this.transferLane.stop(),this.interactiveLane=new c(s,o),this.transferLane=new c(i,o))}sleep(e){return new Promise(t=>setTimeout(t,e))}isMissingPathError(e){return/(file|path) not found:/i.test(String((null==e?void 0:e.message)||e||""))}async readRemoteTextIfExists(e,t,n){try{const s=await this.readTextFile(e,t,n);return String((null==s?void 0:s.content)||"")}catch(e){if(this.isMissingPathError(e))return null;throw e}}async readRemoteLogSnippet(e,t){return String(await this.readRemoteTextIfExists(e,262144,t)||"").trim()}createMaintenanceSession(e,t,n){const s=(new Date).toISOString(),i=(0,l.createSessionLog)({action:e,asRoot:n,kind:"maintenance",remotePath:t,sessionName:(0,l.createSessionName)("maintenance",e,s),startedAt:s});return this.latestMaintenanceSessionValue=i,i}logSession(e,t,n,s){(0,l.appendSessionLogEntry)(e,{level:t,message:n,details:s,timestamp:(new Date).toISOString()})}getLatestMaintenanceLocalLogDownloadPayload(){if(!this.latestMaintenanceSessionValue)throw new Error("No maintenance session log is available yet");return(0,l.buildLocalLogDownloadPayload)(this.latestMaintenanceSessionValue)}async getLatestMaintenanceRemoteLogDownloadPayload(){const e=this.latestMaintenanceSessionValue;if(!e)throw new Error("No maintenance session log is available yet");if(!e.remoteLogPath)throw new Error("No remote log path is recorded for the latest maintenance session");const t=await this.readRemoteTextIfExists(e.remoteLogPath,1048576,Boolean(e.asRoot)),n=e.remoteStatusPath?await this.readRemoteTextIfExists(e.remoteStatusPath,131072,Boolean(e.asRoot)):null;return(0,l.buildRemoteLogDownloadPayload)({session:e,remoteLogText:t,remoteStatusText:n})}shouldRetryStatus(e){return[429,502,503,504].includes(e)}parseBody(e){try{return JSON.parse(e)}catch{const t=e.split(/\r?\n/).map(e=>e.trim()).filter(e=>e.startsWith("data:")).map(e=>e.slice(5).trim()).filter(Boolean);for(let e=t.length-1;e>=0;e--)try{return JSON.parse(t[e])}catch{}}return{error:{message:e||"Invalid MCP response"}}}laneForTool(e){return e.startsWith("upload_chunked_")||e.startsWith("download_chunked_")?"transfer":"interactive"}async executeRequest(e,t){var n,s,i,o,a,r,l,d;const c=this.settings,u=this.normalizedUrl;if(!u)throw new Error("MCP URL is required");const h=Math.max(0,Number(null!==(n=c.maxRetries)&&void 0!==n?n:2)),p=Math.max(100,Number(null!==(s=c.retryBaseMs)&&void 0!==s?s:1e3));let m="",f=0,g=null;for(let n=0;n<=h;n++){try{const n=await fetch(u,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json, text/event-stream","MCP-Protocol-Version":"2025-11-25","X-API-KEY":c.apiKey},body:JSON.stringify(e),signal:t}),s=await n.text();if(m=s,f=n.status,n.ok&&!this.shouldRetryStatus(n.status)){const e=this.parseBody(s);if(e.error)throw new Error(e.error.message||"MCP error");if((null===(i=e.result)||void 0===i?void 0:i.isError)&&(null===(r=null===(a=null===(o=e.result)||void 0===o?void 0:o.content)||void 0===a?void 0:a[0])||void 0===r?void 0:r.text))throw new Error(e.result.content[0].text);return void 0!==(null===(l=e.result)||void 0===l?void 0:l.structuredContent)?e.result.structuredContent:e.result}if(!this.shouldRetryStatus(n.status)){const e=this.parseBody(s);if(null===(d=e.error)||void 0===d?void 0:d.message)throw new Error(e.error.message);throw new Error(`MCP request failed with status ${n.status}: ${s}`)}}catch(e){if(g=e,(null==t?void 0:t.aborted)||"AbortError"===(null==e?void 0:e.name))throw e;if(n===h)break}n<h&&await this.sleep(p*(n+1))}throw g||new Error(`MCP request failed with status ${f}: ${m}`)}async request(e,t,n="interactive"){return this.ensureScheduler(),("transfer"===n?this.transferLane:this.interactiveLane).enqueue(()=>this.executeRequest(e,t),t)}async callTool(e,t,n,s){return this.request({jsonrpc:"2.0",id:`${e}-${Date.now()}-${Math.random().toString(16).slice(2)}`,method:"tools/call",params:{name:e,arguments:t}},n,s||this.laneForTool(e))}async healthRaw(e){return this.callTool("health",{},e,"interactive")}async getHealth(e){const t=await this.healthRaw(e);return(0,r.parseRemoteHealth)(t)}async health(){return this.healthRaw()}async runCommand(e,t,n,s){return this.callTool("run_command",{command:e,cwd:t,timeout_seconds:n,as_root:s},void 0,"interactive")}async openShellSession(e,t){return this.callTool("open_shell_session",{cwd:e,as_root:t},void 0,"interactive")}async execShellSession(e,t,n){return this.callTool("exec_shell_session",{session_id:e,command:t,timeout_seconds:n},void 0,"interactive")}async closeShellSession(e){return this.callTool("close_shell_session",{session_id:e},void 0,"interactive")}async listDirectory(e,t){return this.callTool("list_directory",{path:e,as_root:t},void 0,"interactive")}async readTextFile(e,t,n){return this.callTool("read_text_file",{path:e,max_bytes:t,encoding:"utf-8",as_root:n},void 0,"interactive")}async writeTextFile(e,t,n){return this.callTool("write_text_file",{path:e,content:t,overwrite:!0,encoding:"utf-8",as_root:n},void 0,"interactive")}async makeDirectory(e,t){return this.callTool("make_directory",{path:e,parents:!0,as_root:t},void 0,"interactive")}async deletePath(e,t,n){return this.callTool("delete_path",{path:e,recursive:t,as_root:n},void 0,"interactive")}async renamePath(e,t,n){return this.callTool("rename_path",{path:e,dest:t,as_root:n},void 0,"interactive")}async uploadBinaryFile(e,t,n,s){return this.callTool("upload_binary_file",{path:e,content_base64:t,overwrite:!0,as_root:n},s,"interactive")}async uploadChunkedBegin(e,t,n,s){return this.callTool("upload_chunked_begin",{path:e,overwrite:!0,as_root:t,total_size:n,chunk_bytes:s},void 0,"transfer")}async uploadChunkedPart(e,t,n,s){return this.callTool("upload_chunked_part",{upload_id:e,content_base64:t,offset:n},s,"transfer")}async uploadChunkedFinish(e){return this.callTool("upload_chunked_finish",{upload_id:e},void 0,"transfer")}async uploadChunkedAbort(e){return this.callTool("upload_chunked_abort",{upload_id:e},void 0,"transfer")}async downloadBinaryFile(e,t,n){return this.callTool("download_binary_file",{path:e,max_bytes:67108864,as_root:t},n,"interactive")}async downloadChunkedBegin(e,t,n=131072){return this.callTool("download_chunked_begin",{path:e,as_root:t,chunk_bytes:n},void 0,"transfer")}async downloadChunkedPart(e,t,n,s){return this.callTool("download_chunked_part",{download_id:e,offset:t,chunk_bytes:n},s,"transfer")}async downloadChunkedClose(e){return this.callTool("download_chunked_close",{download_id:e},void 0,"transfer")}async waitForHealth(e){const t=Math.max(5e3,Number(e.timeoutMs||12e4)),n=Math.max(500,Number(e.intervalMs||2e3)),s=Date.now()+t;let i=null;for(;Date.now()<s;){try{return await this.getHealth()}catch(e){i=e}await this.sleep(n)}throw new Error(`Remote health check did not recover within ${t} ms: ${String((null==i?void 0:i.message)||i||"unknown error")}`)}async waitForInstallerCompletion(e){var t,n,s;const i=Math.max(5e3,Number(e.timeoutMs||12e4)),o=Math.max(500,Number(e.intervalMs||2e3)),a=Date.now()+i;let l=null,d=null,c=null;for(;Date.now()<a;){try{d=await this.getHealth(),this.logSession(e.session,"info","Remote health poll succeeded",`script=${d.scriptVersion||"unknown"} server=${d.serverVersion||"unknown"} transport=${d.transportMode||"unknown"}`)}catch(t){l=t,this.logSession(e.session,"warn","Remote health poll failed",String((null==t?void 0:t.message)||t)),await this.sleep(o);continue}try{const s=await this.readRemoteTextIfExists(e.statusPath,65536,e.asRoot);if(s){if(c=(0,r.parseRemoteInstallerStatus)(s),this.logSession(e.session,"info","Remote installer status file detected",`ok=${c.ok} exit=${null!==(t=c.exitCode)&&void 0!==t?t:"unknown"} session=${c.sessionName||"unknown"}`),c.sessionName&&c.sessionName!==e.expectedSessionName)throw new Error(`Remote status session_name=${c.sessionName} does not match expected ${e.expectedSessionName}`);if(!c.ok){const t=await this.readRemoteLogSnippet(e.logPath,e.asRoot),s=t?` Remote log:\n${t}`:` Remote log path: ${e.logPath}`;throw new Error(`Remote ${e.action} exited with code ${null!==(n=c.exitCode)&&void 0!==n?n:"unknown"}.${s}`)}if(d.scriptVersion===e.expectedScriptVersion&&d.serverVersion===e.expectedServerVersion)return this.logSession(e.session,"info","Remote installer finished with expected versions"),{health:d,status:c};l=new Error(`Remote installer completed, but health reports script=${d.scriptVersion||"unknown"} server=${d.serverVersion||"unknown"} instead of expected script=${e.expectedScriptVersion} server=${e.expectedServerVersion}`),this.logSession(e.session,"warn","Remote installer completed before expected versions were observed",String(l.message||l))}else this.logSession(e.session,"info","Remote installer status file not ready yet",e.statusPath)}catch(t){l=t,this.logSession(e.session,"error","Remote installer completion check failed",String((null==t?void 0:t.message)||t))}await this.sleep(o)}const u=d?await this.readRemoteLogSnippet(e.logPath,e.asRoot).catch(()=>""):"",h=c?` Last status: ok=${c.ok} exit=${null!==(s=c.exitCode)&&void 0!==s?s:"unknown"} finished_at=${c.finishedAt||"unknown"} session_name=${c.sessionName||"unknown"}.`:` Status file path: ${e.statusPath}.`,p=u?` Remote log:\n${u}`:` Remote log path: ${e.logPath}`;throw new Error(`Remote ${e.action} did not complete within ${i} ms.${h} Last error: ${String((null==l?void 0:l.message)||l||"unknown error")}.${p}`)}async pushInstallerAndUpgrade(e={}){var t,n,s,i,o;const a=this.bundledInstaller,d=String(e.remotePath||this.settings.installerRemotePath||"/tmp/bianbu_agent_proxy.sh").trim(),c=null!==(t=e.asRoot)&&void 0!==t?t:Boolean(this.settings.maintenanceAsRoot),u=e.action||"up",h=Number(null!==(s=null!==(n=e.reconnectPollMs)&&void 0!==n?n:this.settings.reconnectPollMs)&&void 0!==s?s:2e3),p=Number(null!==(o=null!==(i=e.healthTimeoutMs)&&void 0!==i?i:this.settings.upgradeHealthTimeoutMs)&&void 0!==o?o:12e4);if(!d)throw new Error("Remote installer path is required");const m=this.createMaintenanceSession(u,d,c),f=(0,r.remoteInstallerLogPath)(d),g=(0,r.remoteInstallerStatusPath)(d);m.remoteLogPath=f,m.remoteStatusPath=g,this.logSession(m,"info","Starting remote maintenance session",`remote_path=${d} action=${u}`);try{this.logSession(m,"info","Uploading bundled installer to remote host",d),await this.writeTextFile(d,a.script,c);const e=await this.runCommand((0,r.buildDetachedInstallerCommand)(d,u,f,g,m.sessionName),".",30,c);this.logSession(m,"info","Detached installer command launched",JSON.stringify(e,null,2));const{health:t,status:n}=await this.waitForInstallerCompletion({action:u,asRoot:c,expectedSessionName:m.sessionName,expectedScriptVersion:a.metadata.scriptVersion,expectedServerVersion:a.metadata.serverVersion,intervalMs:h,logPath:f,session:m,statusPath:g,timeoutMs:p});return(0,l.finishSessionLog)(m,"done"),this.logSession(m,"info","Remote maintenance session finished successfully"),{action:u,asRoot:c,health:t,installer:a.metadata,logPath:f,remotePath:d,session:m,start:e,status:n,statusPath:g}}catch(e){const t=String((null==e?void 0:e.message)||e);throw this.logSession(m,"error","Remote maintenance session failed",t),(0,l.finishSessionLog)(m,"error",void 0,t),e}}};u=s([(0,o.Injectable)(),i("design:paramtypes",[a.ConfigService])],u),t.BianbuMcpService=u},104(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuCloudProfileProvider=void 0;const s=n(650),i=n(678),o=n(241);class a extends s.ProfileProvider{constructor(){super(...arguments),this.id="bianbu-cloud",this.name="Bianbu Cloud",this.configDefaults={}}async getBuiltinProfiles(){return[{id:"bianbu-cloud-shell",type:"bianbu-cloud",name:"Bianbu Cloud Shell",icon:"terminal",color:"#2b6cb0",group:"Bianbu Cloud",disableDynamicTitle:!1,behaviorOnSessionEnd:"keep",weight:0,isBuiltin:!0,isTemplate:!1,options:{kind:"shell"}},{id:"bianbu-cloud-files",type:"bianbu-cloud",name:"Bianbu Cloud Files",icon:"folder-open",color:"#0f766e",group:"Bianbu Cloud",disableDynamicTitle:!0,behaviorOnSessionEnd:"keep",weight:1,isBuiltin:!0,isTemplate:!1,options:{kind:"files"}}]}async getNewTabParameters(e){return"files"===e.options.kind?{type:o.BianbuCloudFilesTabComponent,inputs:{profile:e}}:{type:i.BianbuCloudShellTabComponent,inputs:{profile:e}}}getSuggestedName(){return null}getDescription(e){var t;return"files"===(null===(t=e.options)||void 0===t?void 0:t.kind)?"Explorer-like file access backed by MCP file tools":"Terminal-like shell session backed by MCP run_command"}}t.BianbuCloudProfileProvider=a},979(e,t,n){"use strict";var s=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.parseRemoteHealth=t.loadBundledInstaller=t.parseRemoteInstallerStatus=t.buildDetachedInstallerCommand=t.remoteInstallerStatusPath=t.remoteInstallerLogPath=t.appendRemoteSuffix=t.remoteDirName=t.shellQuote=t.validateConnectionSettings=t.normalizeMcpUrl=void 0;const i=n(982),o=s(n(947)),a=s(n(928));function r(e){return String(e||"").trim().replace(/\/+$/,"")}function l(e){return`'${String(e).replace(/'/g,"'\"'\"'")}'`}function d(e){const t=String(e||".").replace(/\\/g,"/"),n=t.lastIndexOf("/");return n<0?".":t.slice(0,n)||"/"}function c(e,t){return`${String(e||".").replace(/\\/g,"/")}${t}`}function u(e){return c(e,".log")}function h(e){return c(e,".status.json")}function p(e){return(0,i.createHash)("sha256").update(e,"utf8").digest("hex")}t.normalizeMcpUrl=r,t.validateConnectionSettings=function(e){const t=[],n=r((null==e?void 0:e.url)||"");if(n)try{const e=new URL(n);["http:","https:"].includes(e.protocol)||t.push("MCP URL must start with http:// or https://")}catch{t.push("MCP URL is not a valid URL")}else t.push("MCP URL is required");return String((null==e?void 0:e.apiKey)||"").trim()||t.push("X-API-KEY is required"),t},t.shellQuote=l,t.remoteDirName=d,t.appendRemoteSuffix=c,t.remoteInstallerLogPath=u,t.remoteInstallerStatusPath=h,t.buildDetachedInstallerCommand=function(e,t,n=u(e),s=h(e),i=`maintenance-${t}`){const o=d(e),a=[`rm -f ${l(n)} ${l(s)}`,`export SESSION_NAME=${l(i)}`,`bash ${l(e)} ${t} > ${l(n)} 2>&1`,"__bianbu_rc=$?","__bianbu_ok=false",'[ "$__bianbu_rc" -eq 0 ] && __bianbu_ok=true','__bianbu_ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"',`printf '{"ok":%s,"exit_code":%s,"finished_at":"%s","action":"%s","log_path":"%s","session_name":"%s"}\\n' "$__bianbu_ok" "$__bianbu_rc" "$__bianbu_ts" ${l(t)} ${l(n)} ${l(i)} > ${l(s)}`].join("; ");return[`mkdir -p ${l(o)}`,`chmod 700 ${l(e)}`,`nohup bash -lc ${l(a)} > /dev/null 2>&1 < /dev/null & printf '__BIANBU_UPGRADE_STARTED__%s\\n' "$!"`].join(" && ")},t.parseRemoteInstallerStatus=function(e){var t;const n="string"==typeof e?JSON.parse(String(e||"").trim()||"{}"):e||{},s=null!==(t=n.exit_code)&&void 0!==t?t:n.exitCode,i=null==s?null:Number(s);return{ok:"boolean"==typeof n.ok?n.ok:0===i,exitCode:Number.isFinite(i)?i:null,finishedAt:n.finished_at||n.finishedAt||null,action:n.action||null,logPath:n.log_path||n.logPath||null,sessionName:n.session_name||n.sessionName||null,raw:n}},t.loadBundledInstaller=function(e=__dirname){let t=null;for(const n of function(e){return[a.default.resolve(e,"../assets"),a.default.resolve(e,"../../assets"),a.default.resolve(process.cwd(),"assets")]}(e)){const e=a.default.join(n,"bianbu_agent_proxy.sh"),s=a.default.join(n,"bianbu_agent_proxy.meta.json"),i=o.default.existsSync(e),r=o.default.existsSync(s);if(i||r)try{const t=o.default.readFileSync(e,"utf8"),n=JSON.parse(o.default.readFileSync(s,"utf8"));if(!(null==n?void 0:n.sha256)||!(null==n?void 0:n.scriptVersion)||!(null==n?void 0:n.serverVersion))throw new Error(`Bundled installer metadata is incomplete at ${s}`);if(p(t)!==n.sha256)throw new Error(`Bundled installer SHA-256 mismatch for ${e}`);return{script:t,metadata:n}}catch(e){t=e;break}}throw new Error(`Unable to load bundled remote installer assets: ${String((null==t?void 0:t.message)||t)}`)},t.parseRemoteHealth=function(e){var t,n,s,i,o,a,r,l;const d=null!==(i=null!==(s=null!==(t=null==e?void 0:e.structuredContent)&&void 0!==t?t:null===(n=null==e?void 0:e.result)||void 0===n?void 0:n.structuredContent)&&void 0!==s?s:e)&&void 0!==i?i:{},c=Array.isArray(d.tools)?d.tools.map(e=>String(e)):[],u={renamePath:Boolean(null===(o=null==d?void 0:d.supports)||void 0===o?void 0:o.rename_path)||c.includes("rename_path"),shellSession:Boolean(null===(a=null==d?void 0:d.supports)||void 0===a?void 0:a.shell_session)||c.includes("open_shell_session"),chunkedTransfers:Boolean(null===(r=null==d?void 0:d.supports)||void 0===r?void 0:r.chunked_transfers)||c.includes("upload_chunked_begin")&&c.includes("download_chunked_begin"),parallelChunkOffsets:Boolean(null===(l=null==d?void 0:d.supports)||void 0===l?void 0:l.parallel_chunk_offsets)};return{ok:!1!==d.ok,serverVersion:d.server_version||d.version||null,scriptVersion:d.script_version||null,transportMode:d.transport_mode||null,fileRoot:d.file_root||null,hasSudo:"boolean"==typeof d.has_sudo?d.has_sudo:null,tools:c,supports:u,raw:d}}},935(e,t){"use strict";function n(e){return String(e).padStart(2,"0")}function s(e=new Date){return new Date(e||Date.now()).toISOString().replace(/\.\d{3}Z$/,"Z")}function i(e=new Date){const t=new Date(e||Date.now());return[String(t.getUTCFullYear()),n(t.getUTCMonth()+1),n(t.getUTCDate()),"T",n(t.getUTCHours()),n(t.getUTCMinutes()),n(t.getUTCSeconds()),"Z"].join("")}function o(e){return String(e||"session").replace(/[^a-zA-Z0-9._-]+/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")||"session"}function a(e){const t=["# bianbu local session log",`session_name=${e.sessionName}`,`kind=${e.kind}`,`action=${e.action}`,`status=${e.status}`,`started_at=${e.startedAt}`,`finished_at=${e.finishedAt||""}`,`remote_path=${e.remotePath||""}`,`remote_log_path=${e.remoteLogPath||""}`,`remote_status_path=${e.remoteStatusPath||""}`,`as_root=${void 0===e.asRoot?"":String(e.asRoot)}`,"","--- local_log ---"],n=e.entries.length?e.entries.map(t=>function(e,t){const n=[`[${t.timestamp}] [${e}] [${t.level.toUpperCase()}] ${t.message}`],s=String(t.details||"").trim();if(s)for(const e of s.split(/\r?\n/))n.push(` ${e}`);return n.join("\n")}(e.sessionName,t)):["[no log entries recorded]"];return`${t.join("\n")}\n${n.join("\n")}\n`}Object.defineProperty(t,"__esModule",{value:!0}),t.buildRemoteLogDownloadPayload=t.buildLocalLogDownloadPayload=t.renderLocalSessionLog=t.finishSessionLog=t.appendSessionLogEntry=t.createSessionLog=t.createSessionName=t.toCompactUtcStamp=t.toIsoUtc=void 0,t.toIsoUtc=s,t.toCompactUtcStamp=i,t.createSessionName=function(e,t,n=new Date){return`${String(e||"session").trim()}-${String(t||"run").trim()}-${i(n)}`},t.createSessionLog=function(e){return{action:e.action,asRoot:e.asRoot,entries:[],kind:e.kind,remoteLogPath:e.remoteLogPath||null,remotePath:e.remotePath||null,remoteStatusPath:e.remoteStatusPath||null,sessionName:e.sessionName,startedAt:e.startedAt||s(),status:"running"}},t.appendSessionLogEntry=function(e,t){const n={timestamp:t.timestamp||s(),level:t.level,message:t.message,details:t.details||null};return e.entries.push(n),n},t.finishSessionLog=function(e,t,n=new Date,i){return e.status=t,e.finishedAt=s(n),e.error=i||null,e},t.renderLocalSessionLog=a,t.buildLocalLogDownloadPayload=function(e,t=new Date){return{fileName:`${o(e.sessionName)}-local-${i(t)}.log`,content:a(e)}},t.buildRemoteLogDownloadPayload=function(e){const t=s(e.downloadedAt),n=String(e.remoteStatusText||"").trim()||"[remote status unavailable]",a=String(e.remoteLogText||"").trim()||"[remote log unavailable]",r=e.session,l=["# bianbu remote session log",`downloaded_at=${t}`,`session_name=${r.sessionName}`,`kind=${r.kind}`,`action=${r.action}`,`status=${r.status}`,`started_at=${r.startedAt}`,`finished_at=${r.finishedAt||""}`,`remote_path=${r.remotePath||""}`,`remote_log_path=${r.remoteLogPath||""}`,`remote_status_path=${r.remoteStatusPath||""}`,"","--- remote_status ---",n,"","--- remote_log ---",a,""].join("\n");return{fileName:`${o(r.sessionName)}-remote-${i(e.downloadedAt)}.log`,content:l}}},645(e,t,n){"use strict";var s=this&&this.__decorate||function(e,t,n,s){var i,o=arguments.length,a=o<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,n):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,s);else for(var r=e.length-1;r>=0;r--)(i=e[r])&&(a=(o<3?i(a):o>3?i(t,n,a):i(t,n))||a);return o>3&&a&&Object.defineProperty(t,n,a),a},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuMcpSettingsComponent=void 0;const o=n(860),a=n(650),r=n(241),l=n(678),d=n(885);let c=class{constructor(e,t,n,s,i){this.config=e,this.app=t,this.mcp=n,this.notifications=s,this.platform=i,this.diagnosticsBusy=!1,this.maintenanceBusy=!1,this.remoteHealth=null,this.lastDiagnosticAt="",this.lastMaintenanceAt="",this.lastMaintenanceSummary="",this.lastError=""}get settings(){return this.config.store.bianbuMcp}get bundledInstaller(){try{return this.mcp.bundledInstaller.metadata}catch{return null}}get validationErrors(){return this.mcp.validationErrors}get latestMaintenanceSession(){return this.mcp.latestMaintenanceSession}get sampleJson(){const e=this.settings;return JSON.stringify({mcpServers:{[e.name||"bianbu"]:{type:"http",url:e.url||"https://your-domain.example.com/mcp",headers:{"X-API-KEY":e.apiKey||"your-x-api-key"}}}},null,2)}save(){this.config.save()}openShell(){this.app.openNewTab({type:l.BianbuCloudShellTabComponent})}openFiles(){this.app.openNewTab({type:r.BianbuCloudFilesTabComponent})}async testConnection(){await this.refreshHealth("Connection verified")}async refreshHealth(e="Remote health refreshed"){this.lastError="",this.diagnosticsBusy=!0;try{this.remoteHealth=await this.mcp.getHealth(),this.lastDiagnosticAt=(new Date).toLocaleString(),this.notifications.notice(e)}catch(e){this.lastError=String((null==e?void 0:e.message)||e),this.notifications.error("Bianbu MCP health check failed",this.lastError)}finally{this.diagnosticsBusy=!1}}async pushUpgrade(e="up"){var t,n,s,i;this.lastError="",this.maintenanceBusy=!0;try{const n=await this.mcp.pushInstallerAndUpgrade({action:e,asRoot:!!this.settings.maintenanceAsRoot,healthTimeoutMs:this.settings.upgradeHealthTimeoutMs,reconnectPollMs:this.settings.reconnectPollMs,remotePath:this.settings.installerRemotePath});this.remoteHealth=n.health,this.lastMaintenanceAt=(new Date).toLocaleString(),this.lastMaintenanceSummary=`session=${(null===(t=n.session)||void 0===t?void 0:t.sessionName)||"unknown"} action=${n.action} remotePath=${n.remotePath} logPath=${n.logPath} statusPath=${n.statusPath} script=${n.health.scriptVersion||"unknown"} server=${n.health.serverVersion||"unknown"}`,this.notifications.notice("repair"===e?"Remote repair finished":"Remote upgrade finished")}catch(t){this.lastMaintenanceAt=(new Date).toLocaleString(),this.lastMaintenanceSummary=`session=${(null===(n=this.latestMaintenanceSession)||void 0===n?void 0:n.sessionName)||"unknown"} action=${e} remotePath=${this.settings.installerRemotePath||"unknown"} logPath=${(null===(s=this.latestMaintenanceSession)||void 0===s?void 0:s.remoteLogPath)||"unknown"} statusPath=${(null===(i=this.latestMaintenanceSession)||void 0===i?void 0:i.remoteStatusPath)||"unknown"}`,this.lastError=String((null==t?void 0:t.message)||t),this.notifications.error("repair"===e?"Remote repair failed":"Remote upgrade failed",this.lastError)}finally{this.maintenanceBusy=!1}}async downloadLocalMaintenanceLog(){try{const e=this.mcp.getLatestMaintenanceLocalLogDownloadPayload();await this.downloadTextFile(e.fileName,e.content),this.notifications.notice("Local maintenance log downloaded")}catch(e){this.lastError=String((null==e?void 0:e.message)||e),this.notifications.error("Failed to download local maintenance log",this.lastError)}}async downloadRemoteMaintenanceLog(){try{const e=await this.mcp.getLatestMaintenanceRemoteLogDownloadPayload();await this.downloadTextFile(e.fileName,e.content),this.notifications.notice("Remote maintenance log downloaded")}catch(e){this.lastError=String((null==e?void 0:e.message)||e),this.notifications.error("Failed to download remote maintenance log",this.lastError)}}async downloadTextFile(e,t){const n=(new TextEncoder).encode(t),s=await this.platform.startDownload(e,420,n.length);if(!s)throw new Error("Download cancelled");try{await s.write(n),s.close()}catch(e){throw s.close(),e}}};c=s([(0,o.Component)({template:n(426)}),i("design:paramtypes",[a.ConfigService,a.AppService,d.BianbuMcpService,a.NotificationsService,a.PlatformService])],c),t.BianbuMcpSettingsComponent=c},717(e,t,n){"use strict";var s=this&&this.__decorate||function(e,t,n,s){var i,o=arguments.length,a=o<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,n):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,s);else for(var r=e.length-1;r>=0;r--)(i=e[r])&&(a=(o<3?i(a):o>3?i(t,n,a):i(t,n))||a);return o>3&&a&&Object.defineProperty(t,n,a),a};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuMcpSettingsTabProvider=void 0;const i=n(860),o=n(700),a=n(645);let r=class extends o.SettingsTabProvider{constructor(){super(...arguments),this.id="bianbu-mcp",this.icon="plug",this.title="Bianbu MCP"}getComponentType(){return a.BianbuMcpSettingsComponent}};r=s([(0,i.Injectable)()],r),t.BianbuMcpSettingsTabProvider=r},864(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuShellSession=void 0;const s=n(349);class i extends s.BaseSession{constructor(e,t){super(e),this.mcp=t,this.cwd=".",this.currentLine="",this.running=!1,this.asRoot=!1,this.sessionId=null}async start(e){this.open=!0,this.cwd=(null==e?void 0:e.cwd)||".",this.asRoot=!!(null==e?void 0:e.asRoot),this.emitText("Connected to Bianbu Cloud MCP shell\r\n");try{const e=await this.mcp.openShellSession(this.cwd,this.asRoot);this.sessionId=e.session_id||null,this.cwd=e.cwd||this.cwd}catch(e){this.sessionId=null,this.logger.warn("Falling back to stateless MCP shell mode",e),this.emitText(`Shell session fallback: ${String((null==e?void 0:e.message)||e)}\r\n`)}this.emitPrompt()}resize(e,t){}write(e){const t=e.toString("utf-8");for(const e of t)this.handleChar(e)}kill(){this.running=!1,this.closeRemoteSession()}async gracefullyKillProcess(){this.running=!1,await this.closeRemoteSession()}async destroy(){await this.closeRemoteSession(),await super.destroy()}supportsWorkingDirectory(){return!0}async getWorkingDirectory(){return this.cwd}async handleChar(e){if(this.running)""===e&&this.emitText("^C\r\n");else{if("\r"===e||"\n"===e){const e=this.currentLine;return this.currentLine="",this.emitText("\r\n"),e.trim()&&await this.execute(e),void this.emitPrompt()}if(""!==e&&"\b"!==e)return""===e?(this.currentLine="",this.emitText("^C\r\n"),void this.emitPrompt()):void(e>=" "&&(this.currentLine+=e,this.emitText(e)));this.currentLine.length&&(this.currentLine=this.currentLine.slice(0,-1),this.emitText("\b \b"))}}async execute(e){this.running=!0;try{const t=this.sessionId?await this.mcp.execShellSession(this.sessionId,e,120):await this.mcp.runCommand(e,this.cwd,120,this.asRoot);t.stdout&&this.emitText(this.normalize(t.stdout)),t.stderr&&this.emitText(this.normalize(t.stderr)),0===t.exit_code||t.stderr||this.emitText(`command exited with code ${t.exit_code}\r\n`),t.cwd&&(this.cwd=t.cwd)}catch(e){this.emitText(this.normalize(String((null==e?void 0:e.message)||e)))}finally{this.running=!1}}async closeRemoteSession(){if(!this.sessionId)return;const e=this.sessionId;this.sessionId=null;try{await this.mcp.closeShellSession(e)}catch(e){this.logger.warn("Failed to close MCP shell session",e)}}emitPrompt(){const e=this.asRoot?"#":"$";this.emitText(`[bianbu ${this.cwd}]${e} `)}emitText(e){this.emitOutput(Buffer.from(e,"utf-8"))}normalize(e){return e.replace(/\r?\n/g,"\r\n")+(e.endsWith("\n")?"":"\r\n")}}t.BianbuShellSession=i},678(e,t,n){"use strict";var s=this&&this.__decorate||function(e,t,n,s){var i,o=arguments.length,a=o<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,n):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(e,t,n,s);else for(var r=e.length-1;r>=0;r--)(i=e[r])&&(a=(o<3?i(a):o>3?i(t,n,a):i(t,n))||a);return o>3&&a&&Object.defineProperty(t,n,a),a},i=this&&this.__metadata||function(e,t){if("object"==typeof Reflect&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)};Object.defineProperty(t,"__esModule",{value:!0}),t.BianbuCloudShellTabComponent=void 0;const o=n(860),a=n(349),r=n(650),l=n(885),d=n(864);let c=class extends a.BaseTerminalTabComponent{constructor(e,t,n){super(e),this.mcp=t,this.localNotifications=n,this.asRoot=!1,this.cwd=".",this.setTitle("Bianbu Cloud Shell"),this.icon="terminal",this.enableToolbar=!0,this.profile=this.profile||{id:"bianbu-cloud-shell",type:"bianbu-cloud",name:"Bianbu Cloud Shell",group:"Bianbu Cloud",options:{kind:"shell"},icon:"terminal",color:"#2b6cb0",disableDynamicTitle:!1,behaviorOnSessionEnd:"keep",weight:0,isBuiltin:!0,isTemplate:!1}}async onFrontendReady(){const e=new d.BianbuShellSession(this.logger,this.mcp);this.setSession(e,!0),await e.start({cwd:this.cwd,asRoot:this.asRoot}),e.releaseInitialDataBuffer(),this.localNotifications.notice("Bianbu Cloud shell ready"),await super.onFrontendReady()}};s([(0,o.Input)(),i("design:type",Object)],c.prototype,"profile",void 0),s([(0,o.Input)(),i("design:type",Object)],c.prototype,"asRoot",void 0),s([(0,o.Input)(),i("design:type",Object)],c.prototype,"cwd",void 0),c=s([(0,o.Component)({template:a.BaseTerminalTabComponent.template,styles:a.BaseTerminalTabComponent.styles,animations:a.BaseTerminalTabComponent.animations}),i("design:paramtypes",[o.Injector,l.BianbuMcpService,r.NotificationsService])],c),t.BianbuCloudShellTabComponent=c},982(e){"use strict";e.exports=require("crypto")},928(e){"use strict";e.exports=require("path")},358(t){"use strict";t.exports=e},860(e){"use strict";e.exports=t},182(e){"use strict";e.exports=n},947(e){"use strict";e.exports=s},650(e){"use strict";e.exports=i},700(e){"use strict";e.exports=o},349(e){"use strict";e.exports=a}},l={},function e(t){var n=l[t];if(void 0!==n)return n.exports;var s=l[t]={exports:{}};return r[t].call(s.exports,s,s.exports,e),s.exports}(440);var r,l});
2
2
  //# sourceMappingURL=index.js.map