# 【智体OS】新发布rtterm分布式终端:dtns.connector使用dpkg插件机制集成了xterm.js的web终端能力——结合dtns-api可跨网络访问算力终端设备!

dtns.network是一款主要由JavaScript编写的智体世界引擎(内嵌了three.js编辑器的定制版-支持以第一视角浏览3D场馆),可以在浏览器和node.js、deno、electron上运行,它是一个跨平台的软件,支持多个操作系统使用!

dtns.connector是dtns.network的客户端软件,允许多用户方便自由地连接dtns.network的智体设备。支持使用内置的poplang智体编程语言实现3D组件的智能化编程——语法超简单,一句话语法,人人轻松上手!通过poplang智体编程,可轻松创建、编辑、分发xverse-3D智体应用。

本次更新的内容为:rtterm实时分布式终端(已集成到dtns.os的系统应用面板中)!

# 更新内容

1、使用dpkg机制,集成xterm.js,开发了rtterm分布式终端的DPKG插件

2、rtterm支持任意命令行指令:例如ps、ls、cd、pkill、ps等等,方便管理算力设备(windows或linux、安卓、mac等)

3、支持命令的复制粘贴功能,方便进行OPS运维

4、支持访问内网算力设备,主要是依赖于dtns-api来进行访问(分布式终端),可取代大部分的代理访问机制

5、集成到了dtns.os的系统应用面板中,可以方便地使用。

6、完全开源:rtterm开源、dtns.os和dtns.network等项目均开源。详见文末、或访问dtns.os智体OS官网。

# rtterm分布式终端效果图

1.png

使用效果:

2.png

# 开源的RTTerm代码


/* eslint-disable */

<!--

 * @Description: RTTerm的linux终端命令行执行环境

 * @Author: poplang

 * @Date: 2024-11-4

 * @LastEditors:

 * @LastEditTime:

 -->

 <template>

    <div style="width: 100%;height: 100%;padding:0px;margin: 0px;background-repeat: no-repeat;background-size: cover;" ref="rtvideoroombody">

      <div @click="back" style="color:black;position: fixed;left:8px;top:8px;z-index: 399;"> ❮返回 </div>

      <div style="color:black;position: fixed;left:0;right:0;top:8px;z-index: 359;text-align: center;  font-size: 18px;font-weight: 800;">{{ title }}</div>

      <!-- <div  style="color:white;position: fixed;right:8px;top:8px;z-index: 399;"> <span @click="reqCreate">创建</span>   <span @click="reqMatch">匹配</span>   <span @click="reqRooms">房间</span></div> -->

      <!-- <div style="color:red;position: fixed;right:8px;top:8px;z-index: 399;"><b>[分数] {{ top_cnt }} : {{ success_cnt  }}</b></div> -->

      <div style="position: fixed;z-index: 199; display:flex;bottom: 5px;left:0;right: 0;height: auto;text-align: center;margin-bottom: 2px;padding:0px 10px 0px 10px;">

      </div>

      <div id="terminal" style="position:fixed;top: 50px;bottom: 0px;left: 0px;right: 0px;z-index: 9;overflow-x: hidden;overflow-y: auto;">

      </div>

    </div>

  </template>

  <script>

import rttermJSON  from './rtterm.json'

 

function initRTTermCSS()

{

    console.log('into initRTTermCSS:')

    console.log('xtermjs:',rttermJSON.xtermcss.length)

    // const u8arr = new TextEncoder().encode(opencvStr)

    const bstr = atob(rttermJSON.xtermcss)

    let n = bstr.length

    const u8arr = new Uint8Array(n);

    while (n--) {

        u8arr[n] = bstr.charCodeAt(n);

    }

    const cssfileURI = window.URL.createObjectURL(new Blob([u8arr], { type: 'text/css' }))

 

    if(!window.g_xtermcss_loaded_flag)

    {

        window.g_xtermcss_loaded_flag = true

        const cssEle = document.createElement('link');

        cssEle.setAttribute('rel', 'stylesheet');

        cssEle.setAttribute('type', 'text/css');

        cssEle.setAttribute('href', cssfileURI);

        document.head.appendChild(cssEle);

    }

    return true

}

function initRTTerm()

{

    console.log('into initRTTerm:')

    initRTTermCSS()

    console.log('xtermjs:',rttermJSON.xtermjs.length)

    // const u8arr = new TextEncoder().encode(opencvStr)

    const bstr = atob(rttermJSON.xtermjs)

    let n = bstr.length

    const u8arr = new Uint8Array(n);

    while (n--) {

        u8arr[n] = bstr.charCodeAt(n);

    }

    const jsfileURI = window.URL.createObjectURL(new Blob([u8arr], { type: 'text/javascript' }))

 

    if(!window.g_xterm_loaded_flag)

    {

        window.g_xterm_loaded_flag = true

        const scriptEle = document.createElement('script')

        scriptEle.src =  jsfileURI//'static/kform/lib/k-form-design.umd.min.js'//'static/js/lib/k-form-design.umd.min.js'

        scriptEle.onload = async function() {

            console.log('xterm.js加载完了')

        }

        document.body.appendChild(scriptEle)

    }

    return true

}

 

  export default {

    name: "RtTerm",

    props: ["value"],

    components: {  },

    data() {

      return {

        title:'RTTerm',

        endTips:'$ ',

        xid:null,

        xterm:null,

      }

    },

    async created()

    {

        initRTTerm()

        console.log('into xterm.vue created')

        this.user_id = localStorage.user_id

   

    },

    mounted(){

        console.log('into xterm.vue mounted')

        if(typeof g_pop_event_bus!='undefined')

        g_pop_event_bus.on('rtterm',this.onRunResult)

        //if(window.g_pop_event_bus) window.g_pop_event_bus.removeListener(rtchess_channel_name,this.event_func)

        this.init()

    },

    beforeRouteLeave(to,from,next){

      console.log('into beforeRouteLeave')

      next();

    },

    methods: {

        back(){

            this.$router.go(-1)

        },

        onRunResult(data)

        {

            let xid = data.notify_type

            console.log('run-result-xid:',xid)

            if(xid!=this.xid)

            {

                return console.log('result-timeout-xid:'+xid,data)

            }

            if(data.ended)

            {

                this.xid = null

                this.xterm.write(this.endTips)

            }

            else if(data.data) this.xterm.write(data.data)

        },

        async run(cmd)

        {

            console.log('rtterm-run:',cmd)

            if(typeof g_dtnsManager=='undefined') return false

            this.xid = 'rtterm-xid-'+Date.now()+'-'+Math.random()

            let ret = await g_dtnsManager.run('dtns://web3:'+rpc_client.roomid+'/systemcmd/run',{cmd,xid:this.xid})

            console.log('run-cmd-call-dtns-api-ret:',ret)

            // xterm.write('\r\n'+this.endTips);

        },

        async init()

        {

            while(!window.Terminal) await new Promise((resolve)=>setTimeout(resolve,100))

            let term = new Terminal();

            term.open(document.getElementById('terminal'));

            let tips = this.endTips

            term.write(tips)

            let xterm = term

            this.xterm = term

            let input = []

            xterm.onKey((e) => {

                console.log('xterm-key:',e.domEvent.keyCode,e.key,e)

                if(this.xid) return

                const printable = !e.domEvent.altKey && !e.domEvent.ctrlKey && !e.domEvent.metaKey;

                if (e.domEvent.keyCode === 13) {

                    // 处理回车键,添加换行

                    // xterm.write('\r\n'+tips);

                    // term.write(tips)

                    // alert(input.join(''))

                    this.run(input.join(''))

                    input = []

                    xterm.write('\r\n')

                    // term.prompt()

                } else if (e.domEvent.keyCode === 8) {

                    // 处理退格键,删除最后一个字符

                    if(input.length<=0) return 

                    xterm.write('\b \b');

                    input.pop()

                }

                else if(e.key == '\x16')//pase  ctrl+v

                {

                    navigator.clipboard.readText().then(clipText => {

                        //ws.send(clipText);

                        // console.log("^V::", clipText);

                        xterm.write(clipText)

                        input = input.concat(clipText.split(''))

                    })

                }

                else if(e.key == '\x03') //copy ctrl+c

                {

                    navigator.clipboard.writeText(this.copy);

                    // console.log("^C::", this.copy);

                }

                else if (printable) {

                    input.push(e.key)

                    // 处理可打印字符

                    xterm.write(e.key);

                }

            });

            term.onSelectionChange( () => {

                if (term.hasSelection()) {

                    this.copy = term.getSelection();

                    // console.log("onselectchange:", this.copy);

                }

            })

            function resizeTerminal()

            {

                var cols = Math.floor(window.innerWidth / 9);

                var rows = Math.floor((window.innerHeight-50) / 18);

                term.resize(cols, rows);

            }

            window.addEventListener('resize', resizeTerminal);

            resizeTerminal()

        }

    }

  }

  </script>

<style scoped>

</style>

注:rtterm.json是一个json对象,包含了xterm.js和xterm.css的base64编码字符串。其中dpkg的插件机制,可详见dtns.plugin项目源码(有多个开源的dpkg插件案例,并且完全开源)。

rtterm分布式终端,方便了所有的终端算力设备的管理,并且不需要为任何的算力设备设置复杂的网络访问代理。使用dtns.os和dtns-api,可拥有智体OS带来的无以伦比的轻松与快乐!

# 分享自:dtns.os智体OS!