diff --git a/agent/cli.go b/agent/cli.go
index 0bc285573d..6c114dc37a 100644
--- a/agent/cli.go
+++ b/agent/cli.go
@@ -26,9 +26,9 @@ func init() {
setupCmd.Flags().String("auth-token-hash", "", "Auth token hash")
setupCmd.Flags().String("wireguard-private-key", "", "Wireguard private key")
- setupCmd.Flags().String("wireguard-address", "", "Wireguard address")
- setupCmd.Flags().String("docker-network-gateway-address", "", "Docker network gateway address")
- setupCmd.Flags().String("docker-network-subnet", "", "Docker network subnet")
+ setupCmd.Flags().String("wireguard-address", "", "Wireguard address ip/cidr")
+ setupCmd.Flags().String("docker-network-gateway-address", "", "Docker network gateway address (ip)")
+ setupCmd.Flags().String("docker-network-subnet", "", "Docker network subnet ip/cidr")
setupCmd.Flags().String("swiftwave-service-address", "", "Swiftwave service address ip:port")
setupCmd.Flags().Bool("enable-haproxy", false, "Enable haproxy")
diff --git a/swiftwave_service/cmd/root.go b/swiftwave_service/cmd/root.go
index 2ed7e7f4cc..6876943a43 100644
--- a/swiftwave_service/cmd/root.go
+++ b/swiftwave_service/cmd/root.go
@@ -3,11 +3,12 @@ package cmd
import (
_ "embed"
"fmt"
+ "os"
+
swiftwave_config "github.com/swiftwave-org/swiftwave/swiftwave_service/config"
"github.com/swiftwave-org/swiftwave/swiftwave_service/config/local_config"
"github.com/swiftwave-org/swiftwave/swiftwave_service/config/system_config/bootstrap"
"github.com/swiftwave-org/swiftwave/swiftwave_service/db"
- "os"
"github.com/spf13/cobra"
)
@@ -15,10 +16,12 @@ import (
var config *swiftwave_config.Config
func init() {
+ rootCmd.Flags().SortFlags = false
rootCmd.AddCommand(initCmd)
rootCmd.AddCommand(configCmd)
rootCmd.AddCommand(dbMigrateCmd)
rootCmd.AddCommand(tlsCmd)
+ rootCmd.AddCommand(serverManagementCmd)
rootCmd.AddCommand(userManagementCmd)
rootCmd.AddCommand(startCmd)
rootCmd.AddCommand(serviceCmd)
diff --git a/swiftwave_service/cmd/server.go b/swiftwave_service/cmd/server.go
new file mode 100644
index 0000000000..4b64cfd10c
--- /dev/null
+++ b/swiftwave_service/cmd/server.go
@@ -0,0 +1,144 @@
+package cmd
+
+import (
+ "fmt"
+ "os"
+ "text/tabwriter"
+
+ "github.com/spf13/cobra"
+ "github.com/swiftwave-org/swiftwave/swiftwave_service/core"
+ "github.com/swiftwave-org/swiftwave/swiftwave_service/db"
+)
+
+func init() {
+ serverManagementCmd.Flags().SortFlags = false
+ serverManagementCmd.AddCommand(createServerCmd)
+ serverManagementCmd.AddCommand(deleteServerCmd)
+ serverManagementCmd.AddCommand(listServerCmd)
+ serverManagementCmd.AddCommand(getSetupAgentCmd)
+
+ createServerCmd.Flags().String("name", "", "Server Name")
+ createServerCmd.Flags().String("ip", "", "Server IP")
+ createServerCmd.MarkFlagRequired("name")
+ createServerCmd.MarkFlagRequired("ip")
+
+ deleteServerCmd.Flags().String("name", "", "Server Name")
+ deleteServerCmd.MarkFlagRequired("name")
+}
+
+var serverManagementCmd = &cobra.Command{
+ Use: "server",
+ Short: "Manage servers",
+ Run: func(cmd *cobra.Command, args []string) {
+ err := cmd.Help()
+ if err != nil {
+ return
+ }
+ },
+}
+
+var createServerCmd = &cobra.Command{
+ Use: "create",
+ Short: "Create a new server",
+ Long: "Create a new server",
+ Run: func(cmd *cobra.Command, args []string) {
+ server_name := cmd.Flag("name").Value.String()
+ server_ip := cmd.Flag("ip").Value.String()
+ if server_name == "" || server_ip == "" {
+ printError("server name and ip are required")
+ err := cmd.Help()
+ if err != nil {
+ return
+ }
+ return
+ }
+ // Initiating database client
+ dbClient, err := db.GetClient(config.LocalConfig, 10)
+ if err != nil {
+ printError("Failed to connect to database")
+ return
+ }
+ // Create server
+ server := core.Server{
+ Name: server_name,
+ PublicIP: server_ip,
+ }
+ err = core.CreateServer(dbClient, &server)
+ if err != nil {
+ printError("Failed to create server")
+ return
+ }
+ printSuccess("Created server > " + server_name)
+ },
+}
+
+var deleteServerCmd = &cobra.Command{
+ Use: "delete",
+ Short: "Delete a server",
+ Long: "Delete a server",
+ Run: func(cmd *cobra.Command, args []string) {
+ server_name := cmd.Flag("name").Value.String()
+ if server_name == "" {
+ printError("server name is required")
+ err := cmd.Help()
+ if err != nil {
+ return
+ }
+ return
+ }
+ // Initiating database client
+ dbClient, err := db.GetClient(config.LocalConfig, 10)
+ if err != nil {
+ printError("Failed to connect to database")
+ return
+ }
+ server, err := core.FetchServerByName(dbClient, server_name)
+ if err != nil {
+ printError("Failed to fetch server")
+ return
+ }
+ // Delete server
+ err = core.DeleteServer(dbClient, server.ID)
+ if err != nil {
+ printError("Failed to delete server")
+ return
+ }
+ },
+}
+
+var listServerCmd = &cobra.Command{
+ Use: "list",
+ Short: "List servers",
+ Long: "List servers",
+ Run: func(cmd *cobra.Command, args []string) {
+ dbClient, err := db.GetClient(config.LocalConfig, 10)
+ if err != nil {
+ printError("Failed to connect to database")
+ return
+ }
+ servers, err := core.FetchAllServers(dbClient)
+ if err != nil {
+ printError("Failed to fetch servers")
+ return
+ }
+ // Create a tab writer
+ w := tabwriter.NewWriter(os.Stdout, 0, 8, 2, ' ', tabwriter.AlignRight)
+ w.Write([]byte("ID\tName\tIP\tWireguard IP\tDeployment\tProxy\tStatus\tLast Ping\n"))
+ for _, server := range servers {
+ w.Write([]byte(fmt.Sprintf("%d\t%s\t%s\t%s\t%s\t%v\t%s\t%s\n", server.ID, server.Name, server.PublicIP, server.WireguardConfig.IP, server.Status, server.ProxyEnabled, server.Status, server.LastPing.Format("2006-01-02 15:04:05"))))
+ }
+ w.Flush()
+ },
+}
+
+var getSetupAgentCmd = &cobra.Command{
+ Use: "setup-agent",
+ Short: "Setup agent on server",
+ Long: "Setup agent on server",
+ Run: func(cmd *cobra.Command, args []string) {
+ err := cmd.Help()
+ if err != nil {
+ return
+ }
+ },
+}
diff --git a/swiftwave_service/console/assets/index.html b/swiftwave_service/console/assets/index.html
deleted file mode 100644
index 4444be730b..0000000000
--- a/swiftwave_service/console/assets/index.html
+++ /dev/null
@@ -1,107 +0,0 @@
-
-
-
-
-
- []
- [Host]
- [Logged in as user]
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/swiftwave_service/console/assets/main.js b/swiftwave_service/console/assets/main.js
deleted file mode 100644
index 936c655545..0000000000
--- a/swiftwave_service/console/assets/main.js
+++ /dev/null
@@ -1,210 +0,0 @@
-// Utility
-function hideStatus() {
- document.getElementById('status_text').style.display = 'none';
- document.getElementById('loader_container').style.display = 'none';
-}
-
-function showStatus(text) {
- document.getElementById('status_text').getElementsByTagName("span")[0].innerText = text;
- document.getElementById('status_text').style.display = 'block';
- document.getElementById('loader_container').style.display = 'flex';
-}
-
-function hideInfoPanel() {
- document.getElementById('info_section_container').style.display = 'none';
-}
-
-function showInfoPanel() {
- document.getElementById('info_section_container').style.display = 'block';
-}
-
-function getAuthorizationHeader() {
- return "Bearer " + localStorage.getItem("token");
-}
-
-
-// Initiate terminal
-const term = new Terminal({
- cursorBlink: true,
-});
-
-const fitAddon = new FitAddon.FitAddon();
-term.loadAddon(fitAddon);
-term.open(document.getElementById('terminal'));
-fitAddon.fit();
-
-// Handle copy from terminal
-term.attachCustomKeyEventHandler(function (e) {
- // Ctrl + Shift + C
- if (e.ctrlKey && e.shiftKey && (e.keyCode === 67)) {
- e.preventDefault()
- navigator.clipboard.writeText(term.getSelection()).catch(() => console.log(e.message));
- return false;
- }
-});
-
-// Terminal SSH initialization
-async function init() {
- // check if not on localhost and not secure
- if ((location.hostname !== "localhost" && location.hostname !== "127.0.0.1" && location.hostname.endsWith(".local")) && location.protocol !== "https:") {
- showStatus("Please use a secure connection (https)");
- return;
- }
- const urlParams = new URLSearchParams(window.location.search);
- let data = {}
- let targetType = "unknown"
- if (urlParams.has('server')) {
- targetType = "server"
- // fetch server id
- const serverId = urlParams.get('server');
- showStatus("Authenticating...");
- // generate a console request
- const response = await fetch(`/console/token/server/${serverId}`, {
- method: "POST", headers: {
- 'Content-Type': 'application/json',
- 'Authorization': getAuthorizationHeader()
- }
- });
- if (!response.ok) {
- showStatus("Error: " + response.statusText);
- return;
- }
- data = await response.json();
- } else if (urlParams.has('application')) {
- targetType = "application"
- // fetch application id
- const applicationId = urlParams.get('application');
- showStatus("Fetching available servers...");
- // find servers
- const response = await fetch(`/console/application/${applicationId}/servers`, {
- method: "GET", headers: {
- 'Content-Type': 'application/json',
- 'Authorization': getAuthorizationHeader()
- }
- });
- if (!response.ok) {
- showStatus("Error: " + response.statusText);
- return;
- }
- const servers = await response.json();
- if (servers.length === 0) {
- showStatus("Error: No servers available");
- return;
- }
- showStatus("Select preferred server to connect for application console");
- const serverList = Object.keys(servers);
- let serverListString = "Available Servers:\n";
- for (let i = 0; i < serverList.length; i++) {
- serverListString += `[${i + 1}] ${serverList[i]}\n`
- }
- serverListString = serverListString + "[x] Cancel"
- serverListString = serverListString + "\n\nEnter serial no of server :";
- let serverSrNo = 0;
- while (serverSrNo < 1 || serverSrNo > serverList.length) {
- serverSrNo = prompt(serverListString);
- if (!serverSrNo) {
- showStatus("Error: No server selected");
- }
- if (serverSrNo === "x") {
- showStatus("Aborted ! Refresh to try again")
- return;
- }
- }
- // send request to server
- showStatus("Authenticating...");
- // /console/token/application/:id/:server_id
- const response2 = await fetch(`/console/token/application/${applicationId}/${servers[serverList[parseInt(serverSrNo) - 1]]}`, {
- method: "POST",
- headers: {
- 'Content-Type': 'application/json',
- 'Authorization': getAuthorizationHeader()
- }
- });
- if (!response2.ok) {
- showStatus("Error: " + response2.statusText);
- return;
- }
- data = await response2.json();
- } else {
- showStatus("Console Info not found");
- return
- }
-
- // fetch request_id and token
- const requestId = data.request_id;
- const token = data.token;
- if (!requestId || !token) {
- showStatus("Error: Some error occurred");
- return;
- }
- let target, host_info, host_user;
- if (targetType === "server") {
- target = data.target.hostname;
- host_info = `${data.target.ip} (${data.target.hostname})`
- host_user = data.target.user
- } else if (targetType === "application") {
- target = data.target.application;
- host_info = `${data.target.server.ip} (${data.target.server.hostname})`
- host_user = data.target.server.user
- } else {
- showStatus("Unknown configuration");
- return;
- }
- document.title = `[${targetType}] ${target}`;
- // Set all the info
- document.getElementById('target_type').innerText = targetType;
- document.getElementById('target_name').innerText = target;
- document.getElementById('host_info').innerText = host_info;
- document.getElementById('host_user').innerText = host_user;
- showInfoPanel()
-
- // connect to websocket using the request_id and token
- let protocol = "ws";
- if (location.protocol === "https:") {
- protocol = "wss";
- }
- showStatus("Connecting to server...");
- const ws = new WebSocket(`${protocol}://${location.host}/console/ws/${requestId}/${token}/${term.rows}/${term.cols}`);
- ws.binaryType = "arraybuffer";
-
- // error handler
- ws.onerror = function (e) {
- showStatus("Error: " + e.message);
- }
- ws.onopen = function () {
- hideStatus();
- // handle data received from server
- ws.onmessage = function (evt) {
- if (evt.data instanceof ArrayBuffer) {
- term.write(new Uint8Array(evt.data))
- } else {
- console.log("invalid data received")
- }
- }
- // handle data sent from terminal
- term.onData((e) => ws.send(new TextEncoder().encode(e)))
- // handle terminal resize
- window.addEventListener('resize', () => {
- fitAddon.fit();
- const payload = {
- cols: term.cols, rows: term.rows
- }
- let payloadStr = JSON.stringify(payload);
- payloadStr = "\x04" + payloadStr;
- ws.send(new TextEncoder().encode(payloadStr))
- });
- }
- // handle connection close
- ws.onclose = function () {
- showStatus("Connection lost. Refresh to reconnect.")
- }
-}
-
-// Initiate the connection
-init()
- .then(() => {
- console.log("console init done")
- })
- .catch((e) => {
- showStatus("Error: " + e.message);
- })
\ No newline at end of file
diff --git a/swiftwave_service/console/assets/xterm-addon-fit.js b/swiftwave_service/console/assets/xterm-addon-fit.js
deleted file mode 100644
index 7384f10466..0000000000
--- a/swiftwave_service/console/assets/xterm-addon-fit.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
- * Skipped minification because the original files appears to be already minified.
- * Original file: /npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js
- *
- * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
- */
-!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(self,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core,t=e._renderService.dimensions;if(0===t.css.cell.width||0===t.css.cell.height)return;const r=0===this._terminal.options.scrollback?0:e.viewport.scrollBarWidth,i=window.getComputedStyle(this._terminal.element.parentElement),o=parseInt(i.getPropertyValue("height")),s=Math.max(0,parseInt(i.getPropertyValue("width"))),n=window.getComputedStyle(this._terminal.element),l=o-(parseInt(n.getPropertyValue("padding-top"))+parseInt(n.getPropertyValue("padding-bottom"))),a=s-(parseInt(n.getPropertyValue("padding-right"))+parseInt(n.getPropertyValue("padding-left")))-r;return{cols:Math.max(2,Math.floor(a/t.css.cell.width)),rows:Math.max(1,Math.floor(l/t.css.cell.height))}}}})(),e})()));
-//# sourceMappingURL=xterm-addon-fit.js.map
\ No newline at end of file
diff --git a/swiftwave_service/console/assets/xterm.css b/swiftwave_service/console/assets/xterm.css
deleted file mode 100644
index edbf0c8107..0000000000
--- a/swiftwave_service/console/assets/xterm.css
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
-v5.4.0
-*/
-
-/**
- * Copyright (c) 2014 The xterm.js authors. All rights reserved.
- * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
- * https://github.com/chjj/term.js
- * @license MIT
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * Originally forked from (with the author's permission):
- * Fabrice Bellard's javascript vt100 for jslinux:
- * http://bellard.org/jslinux/
- * Copyright (c) 2011 Fabrice Bellard
- * The original design remains. The terminal itself
- * has been extended to include xterm CSI codes, among
- * other features.
- */
-
-/**
- * Default styles for xterm.js
- */
-
-.xterm {
- cursor: text;
- position: relative;
- user-select: none;
- -ms-user-select: none;
- -webkit-user-select: none;
-}
-
-.xterm.focus,
-.xterm:focus {
- outline: none;
-}
-
-.xterm .xterm-helpers {
- position: absolute;
- top: 0;
- /**
- * The z-index of the helpers must be higher than the canvases in order for
- * IMEs to appear on top.
- */
- z-index: 5;
-}
-
-.xterm .xterm-helper-textarea {
- padding: 0;
- border: 0;
- margin: 0;
- /* Move textarea out of the screen to the far left, so that the cursor is not visible */
- position: absolute;
- opacity: 0;
- left: -9999em;
- top: 0;
- width: 0;
- height: 0;
- z-index: -5;
- /** Prevent wrapping so the IME appears against the textarea at the correct position */
- white-space: nowrap;
- overflow: hidden;
- resize: none;
-}
-
-.xterm .composition-view {
- /* TODO: Composition position got messed up somewhere */
- background: #000;
- color: #FFF;
- display: none;
- position: absolute;
- white-space: nowrap;
- z-index: 1;
-}
-
-.xterm .composition-view.active {
- display: block;
-}
-
-.xterm .xterm-viewport {
- /* On OS X this is required in order for the scroll bar to appear fully opaque */
- background-color: #000;
- overflow-y: auto;
- cursor: default;
- position: absolute;
- right: 0;
- left: 0;
- top: 0;
- bottom: 0;
-}
-
-.xterm .xterm-screen {
- position: relative;
-}
-
-.xterm .xterm-screen canvas {
- position: absolute;
- left: 0;
- top: 0;
-}
-
-.xterm .xterm-scroll-area {
- visibility: hidden;
-}
-
-.xterm-char-measure-element {
- display: inline-block;
- visibility: hidden;
- position: absolute;
- top: 0;
- left: -9999em;
- line-height: normal;
-}
-
-.xterm.enable-mouse-events {
- /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
- cursor: default;
-}
-
-.xterm.xterm-cursor-pointer,
-.xterm .xterm-cursor-pointer {
- cursor: pointer;
-}
-
-.xterm.column-select.focus {
- /* Column selection mode */
- cursor: crosshair;
-}
-
-.xterm .xterm-accessibility:not(.debug),
-.xterm .xterm-message {
- position: absolute;
- left: 0;
- top: 0;
- bottom: 0;
- right: 0;
- z-index: 10;
- color: transparent;
- pointer-events: none;
-}
-
-.xterm .xterm-accessibility-tree:not(.debug) *::selection {
- color: transparent;
-}
-
-.xterm .xterm-accessibility-tree {
- user-select: text;
- white-space: pre;
-}
-
-.xterm .live-region {
- position: absolute;
- left: -9999px;
- width: 1px;
- height: 1px;
- overflow: hidden;
-}
-
-.xterm-dim {
- /* Dim should not apply to background, so the opacity of the foreground color is applied
- * explicitly in the generated class and reset to 1 here */
- opacity: 1 !important;
-}
-
-.xterm-underline-1 { text-decoration: underline; }
-.xterm-underline-2 { text-decoration: double underline; }
-.xterm-underline-3 { text-decoration: wavy underline; }
-.xterm-underline-4 { text-decoration: dotted underline; }
-.xterm-underline-5 { text-decoration: dashed underline; }
-
-.xterm-overline {
- text-decoration: overline;
-}
-
-.xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
-.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
-.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
-.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
-.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }
-
-.xterm-strikethrough {
- text-decoration: line-through;
-}
-
-.xterm-screen .xterm-decoration-container .xterm-decoration {
- z-index: 6;
- position: absolute;
-}
-
-.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
- z-index: 7;
-}
-
-.xterm-decoration-overview-ruler {
- z-index: 8;
- position: absolute;
- top: 0;
- right: 0;
- pointer-events: none;
-}
-
-.xterm-decoration-top {
- z-index: 2;
- position: relative;
-}
\ No newline at end of file
diff --git a/swiftwave_service/console/assets/xterm.js b/swiftwave_service/console/assets/xterm.js
deleted file mode 100644
index bb50e25072..0000000000
--- a/swiftwave_service/console/assets/xterm.js
+++ /dev/null
@@ -1,3 +0,0 @@
-// v5.4.0
-!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i=t();for(var s in i)("object"==typeof exports?exports:e)[s]=i[s]}}(globalThis,(()=>(()=>{"use strict";var e={4567:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.AccessibilityManager=void 0;const n=i(9042),o=i(9924),a=i(844),h=i(4725),c=i(2585),l=i(3656);let d=t.AccessibilityManager=class extends a.Disposable{constructor(e,t,i,s){super(),this._terminal=e,this._coreBrowserService=i,this._renderService=s,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="",this._accessibilityContainer=this._coreBrowserService.mainDocument.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=this._coreBrowserService.mainDocument.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let e=0;e