# 【智体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分布式终端效果图
使用效果:
# 开源的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带来的无以伦比的轻松与快乐!