verteilen-core 1.0.0
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/LICENSE +21 -0
- package/README.md +2 -0
- package/bun.lock +730 -0
- package/index.ts +1 -0
- package/jest.config.js +8 -0
- package/package.json +42 -0
- package/src/client/analysis.ts +377 -0
- package/src/client/client.ts +230 -0
- package/src/client/cluster.ts +125 -0
- package/src/client/execute.ts +210 -0
- package/src/client/http.ts +45 -0
- package/src/client/javascript.ts +535 -0
- package/src/client/job_execute.ts +216 -0
- package/src/client/job_parameter.ts +41 -0
- package/src/client/os.ts +210 -0
- package/src/client/parameter.ts +58 -0
- package/src/client/resource.ts +121 -0
- package/src/client/shell.ts +147 -0
- package/src/interface/base.ts +82 -0
- package/src/interface/bus.ts +144 -0
- package/src/interface/enum.ts +181 -0
- package/src/interface/execute.ts +47 -0
- package/src/interface/record.ts +131 -0
- package/src/interface/server.ts +91 -0
- package/src/interface/struct.ts +292 -0
- package/src/interface/table.ts +34 -0
- package/src/interface/ui.ts +35 -0
- package/src/interface.ts +50 -0
- package/src/lan/en.json +395 -0
- package/src/lan/zh_TW.json +395 -0
- package/src/plugins/i18n.ts +20 -0
- package/src/script/console_manager.ts +135 -0
- package/src/script/console_server_manager.ts +46 -0
- package/src/script/execute/base.ts +309 -0
- package/src/script/execute/feedback.ts +212 -0
- package/src/script/execute/region_job.ts +14 -0
- package/src/script/execute/region_project.ts +23 -0
- package/src/script/execute/region_subtask.ts +14 -0
- package/src/script/execute/region_task.ts +23 -0
- package/src/script/execute/runner.ts +339 -0
- package/src/script/execute/util_parser.ts +175 -0
- package/src/script/execute_manager.ts +348 -0
- package/src/script/socket_manager.ts +329 -0
- package/src/script/webhook_manager.ts +6 -0
- package/src/script/webhook_server_manager.ts +102 -0
- package/src/util/server/console_handle.ts +248 -0
- package/src/util/server/log_handle.ts +194 -0
- package/test/TEST.ts +63 -0
- package/test/client/execute.test.ts +54 -0
- package/test/client/javascript.test.ts +78 -0
- package/test/client/server.test.ts +26 -0
- package/test/client/task.test.ts +136 -0
- package/test/script/parser.test.ts +110 -0
- package/test/script/socket.test.ts +27 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
// ========================
|
|
2
|
+
//
|
|
3
|
+
// Share Codebase
|
|
4
|
+
//
|
|
5
|
+
// ========================
|
|
6
|
+
import { Header, Job, PluginList, ResourceType, SystemLoad } from '../interface'
|
|
7
|
+
import { ClientHTTP } from './http'
|
|
8
|
+
import { ClientJobExecute } from './job_execute'
|
|
9
|
+
import { ClientResource } from './resource'
|
|
10
|
+
|
|
11
|
+
let worker: ClientJobExecute | undefined = undefined
|
|
12
|
+
|
|
13
|
+
process.stdin.resume();
|
|
14
|
+
process.stdin.setEncoding('utf8');
|
|
15
|
+
process.stdin.on('data', (chunk) => {
|
|
16
|
+
if(chunk.toString().startsWith("kill")){
|
|
17
|
+
if(worker != undefined){
|
|
18
|
+
worker.stop_all()
|
|
19
|
+
}
|
|
20
|
+
setTimeout(process.exit(1), 1000);
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The message handle for reply
|
|
26
|
+
* @param msg Message
|
|
27
|
+
* @param tag Message prefix
|
|
28
|
+
*/
|
|
29
|
+
const messager = (msg:string, tag?:string) => {
|
|
30
|
+
const d:Header = {
|
|
31
|
+
name: 'messager',
|
|
32
|
+
meta: tag,
|
|
33
|
+
data: msg
|
|
34
|
+
}
|
|
35
|
+
console.log(JSON.stringify(d))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The message handle for reply with print on screen ffeature
|
|
40
|
+
* @param msg Message
|
|
41
|
+
* @param tag Message prefix
|
|
42
|
+
*/
|
|
43
|
+
const messager_log = (msg:string, tag?:string, meta?:string) => {
|
|
44
|
+
const d:Header = {
|
|
45
|
+
name: 'messager_log',
|
|
46
|
+
meta: meta,
|
|
47
|
+
data: `[${tag}] ${msg}`
|
|
48
|
+
}
|
|
49
|
+
console.log(JSON.stringify(d))
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Return the error message to main thread
|
|
53
|
+
* @param err Error instance
|
|
54
|
+
*/
|
|
55
|
+
const ERROR = (err:any) => {
|
|
56
|
+
const d:Header = {
|
|
57
|
+
name: "error",
|
|
58
|
+
meta: "Execute job failed",
|
|
59
|
+
data: `(${err.code ?? 'unknown'}) ${err.message}`,
|
|
60
|
+
}
|
|
61
|
+
console.log(JSON.stringify(d))
|
|
62
|
+
process.exit(1)
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Job execute task
|
|
66
|
+
*/
|
|
67
|
+
const execute_job = () => {
|
|
68
|
+
if(process.env.job == undefined || process.env.plugin == undefined){
|
|
69
|
+
process.exit(1)
|
|
70
|
+
}
|
|
71
|
+
const d:Job = JSON.parse(process.env.job)
|
|
72
|
+
const p:PluginList = JSON.parse(process.env.plugin)
|
|
73
|
+
worker = new ClientJobExecute(messager, messager_log, d, undefined, p)
|
|
74
|
+
worker.execute().then(x => {
|
|
75
|
+
messager_log(x)
|
|
76
|
+
process.exit(0)
|
|
77
|
+
})
|
|
78
|
+
.catch(err => ERROR(err))
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Query resource task
|
|
82
|
+
*/
|
|
83
|
+
const execute_resource = () => {
|
|
84
|
+
const r:ClientResource = new ClientResource()
|
|
85
|
+
messager("Resource query")
|
|
86
|
+
const cache:SystemLoad | undefined = process.env.cache == undefined ? undefined : JSON.parse(process.env.cache)
|
|
87
|
+
const type:ResourceType = cache == undefined ? ResourceType.ALL : ResourceType.BATTERY | ResourceType.LOAD | ResourceType.NETWORK | ResourceType.RAM
|
|
88
|
+
r.Query(cache, type).then(x => {
|
|
89
|
+
const h:Header = {
|
|
90
|
+
name: 'resource',
|
|
91
|
+
data: x
|
|
92
|
+
}
|
|
93
|
+
console.log(JSON.stringify(h))
|
|
94
|
+
}).catch(err => ERROR(err))
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Query http task
|
|
98
|
+
*/
|
|
99
|
+
const execute_http = () => {
|
|
100
|
+
const m:string = process.env.method || 'GET'
|
|
101
|
+
const u:string = process.env.url || ''
|
|
102
|
+
const p:any = process.env.params
|
|
103
|
+
const r:ClientHTTP = new ClientHTTP(u, m, p)
|
|
104
|
+
r.RUN()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* The entry point for the cluster thread.
|
|
109
|
+
*/
|
|
110
|
+
export function RUN(){
|
|
111
|
+
// The cluster currently spawn should execute a job
|
|
112
|
+
switch(process.env.type){
|
|
113
|
+
case 'JOB':
|
|
114
|
+
execute_job()
|
|
115
|
+
break
|
|
116
|
+
case 'RESOURCE':
|
|
117
|
+
execute_resource()
|
|
118
|
+
break
|
|
119
|
+
case 'HTTP':
|
|
120
|
+
execute_http()
|
|
121
|
+
break
|
|
122
|
+
default:
|
|
123
|
+
process.exit(1)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
// ========================
|
|
2
|
+
//
|
|
3
|
+
// Share Codebase
|
|
4
|
+
//
|
|
5
|
+
// ========================
|
|
6
|
+
import { ChildProcess, spawn } from 'child_process';
|
|
7
|
+
import { WebSocket } from 'ws';
|
|
8
|
+
import { DataType, FeedBack, Header, Job, JobCategory, JobType2Text, JobTypeText, Libraries, Messager, Messager_log, Parameter, Setter } from "../interface";
|
|
9
|
+
import { i18n } from "../plugins/i18n";
|
|
10
|
+
import { Client } from "./client";
|
|
11
|
+
import { ClientParameter } from './parameter';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Execute worker, Execute the job container
|
|
15
|
+
*/
|
|
16
|
+
export class ClientExecute {
|
|
17
|
+
uuid:string
|
|
18
|
+
private parameter:Parameter | undefined = undefined
|
|
19
|
+
private libraries:Libraries | undefined = undefined
|
|
20
|
+
private tag: string = ''
|
|
21
|
+
private workers:Array<ChildProcess> = []
|
|
22
|
+
private client:Client;
|
|
23
|
+
|
|
24
|
+
private messager:Messager
|
|
25
|
+
private messager_log:Messager_log
|
|
26
|
+
|
|
27
|
+
public get count() : number {
|
|
28
|
+
return this.workers.length
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
constructor(_uuid:string, _messager:Messager, _messager_log:Messager_log, _client:Client){
|
|
32
|
+
this.uuid =_uuid
|
|
33
|
+
this.client = _client
|
|
34
|
+
this.messager = _messager
|
|
35
|
+
this.messager_log = _messager_log
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The stop signal, It will trying to kill the process if currently running
|
|
40
|
+
*/
|
|
41
|
+
stop_job = () => {
|
|
42
|
+
this.messager_log(`[Execute] Stop All: ${this.workers.length}`)
|
|
43
|
+
this.workers.forEach(x => {
|
|
44
|
+
x.stdin!.cork()
|
|
45
|
+
x.stdin!.write("kill\n")
|
|
46
|
+
x.stdin!.uncork()
|
|
47
|
+
x.stdin!.end()
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* The entry function to execute the job container
|
|
53
|
+
* @param job Target job
|
|
54
|
+
*/
|
|
55
|
+
execute_job = (job:Job, source:WebSocket) => {
|
|
56
|
+
this.messager_log(`[Execute] ${job.uuid} ${job.category == JobCategory.Execution ? i18n.global.t(JobTypeText[job.type]) : i18n.global.t(JobType2Text[job.type])}`, job.uuid, job.runtime_uuid)
|
|
57
|
+
this.tag = job.uuid
|
|
58
|
+
this.execute_job_worker(job, source)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private execute_job_worker(job:Job, source:WebSocket){
|
|
62
|
+
const child = spawn(Client.workerPath(), [],
|
|
63
|
+
{
|
|
64
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
65
|
+
windowsHide: true,
|
|
66
|
+
shell: true,
|
|
67
|
+
env: {
|
|
68
|
+
...process.env,
|
|
69
|
+
type: "JOB",
|
|
70
|
+
job: JSON.stringify(job),
|
|
71
|
+
plugin: JSON.stringify(this.client.plugins),
|
|
72
|
+
parameter: JSON.stringify(this.parameter),
|
|
73
|
+
libraries: JSON.stringify(this.libraries),
|
|
74
|
+
}
|
|
75
|
+
})
|
|
76
|
+
child.stdin.setDefaultEncoding('utf-8')
|
|
77
|
+
this.workers.push(child)
|
|
78
|
+
const para = new ClientParameter(source)
|
|
79
|
+
let k = ""
|
|
80
|
+
|
|
81
|
+
const workerFeedbackExec = (str:string) => {
|
|
82
|
+
try{
|
|
83
|
+
const msg:Header = JSON.parse(str)
|
|
84
|
+
if(msg.name == 'messager'){
|
|
85
|
+
this.messager(msg.data, job.uuid)
|
|
86
|
+
}
|
|
87
|
+
else if(msg.name == 'messager_log'){
|
|
88
|
+
this.messager_log(msg.data, job.uuid, job.runtime_uuid)
|
|
89
|
+
}
|
|
90
|
+
else if(msg.name == 'error'){
|
|
91
|
+
if(msg.data instanceof String) this.messager_log(msg.data.toString(), job.uuid, job.runtime_uuid)
|
|
92
|
+
else this.messager_log(JSON.stringify(msg.data), job.uuid, job.runtime_uuid)
|
|
93
|
+
}
|
|
94
|
+
else if(msg.name == 'feedbackstring'){
|
|
95
|
+
para.feedbackstring(msg.data)
|
|
96
|
+
}
|
|
97
|
+
else if(msg.name == 'feedbackboolean'){
|
|
98
|
+
para.feedbackboolean(msg.data)
|
|
99
|
+
}
|
|
100
|
+
else if(msg.name == 'feedbacknumber'){
|
|
101
|
+
para.feedbacknumber(msg.data)
|
|
102
|
+
}
|
|
103
|
+
else if(msg.name == 'feedbackobject'){
|
|
104
|
+
para.feedbackobject(msg.data)
|
|
105
|
+
}
|
|
106
|
+
else if(msg.name == 'feedbacklist'){
|
|
107
|
+
para.feedbacklist(msg.data)
|
|
108
|
+
}
|
|
109
|
+
else if(msg.name == 'feedbackselect'){
|
|
110
|
+
para.feedbackselect(msg.data)
|
|
111
|
+
}
|
|
112
|
+
}catch(err:any){
|
|
113
|
+
this.messager_log(`Error: ${str}`, job.uuid, job.runtime_uuid)
|
|
114
|
+
this.messager_log(`(${err.code ?? 'unknown'}) ${err.message}`, job.uuid, job.runtime_uuid)
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
const workerFeedback = (str:string) => {
|
|
118
|
+
for(let i = 0; i < str.length; i++){
|
|
119
|
+
if(str[i] != '\n') k += str[i]
|
|
120
|
+
else {
|
|
121
|
+
workerFeedbackExec(k)
|
|
122
|
+
k = ''
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
child.on('error', (err) => {
|
|
128
|
+
this.messager_log(`[Worker Error] ${err}`, job.uuid, job.runtime_uuid)
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
child.on('exit', (code, signal) => {
|
|
132
|
+
this.job_finish(code || 0, signal || '', job, source)
|
|
133
|
+
const index = this.workers.findIndex(x => x == child)
|
|
134
|
+
if(index != -1) this.workers.splice(index, 1)
|
|
135
|
+
})
|
|
136
|
+
child.on('message', (message, sendHandle) => {
|
|
137
|
+
workerFeedback(message.toString())
|
|
138
|
+
})
|
|
139
|
+
child.stdout.setEncoding('utf8');
|
|
140
|
+
child.stdout.on('data', (chunk) => {
|
|
141
|
+
workerFeedback(chunk.toString())
|
|
142
|
+
})
|
|
143
|
+
child.stderr.setEncoding('utf8');
|
|
144
|
+
child.stderr.on('data', (chunk) => {
|
|
145
|
+
workerFeedback(chunk.toString())
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private job_finish(code:number, signal:string, job:Job, source:WebSocket){
|
|
150
|
+
this.messager_log( code == 0 ?
|
|
151
|
+
`[Execute] Successfully: ${code} ${signal}` :
|
|
152
|
+
`[Execute] Error: ${code} ${signal}`, job.uuid, job.runtime_uuid)
|
|
153
|
+
const data:FeedBack = { job_uuid: job.uuid, runtime_uuid: job.runtime_uuid!, meta: code, message: signal }
|
|
154
|
+
const h:Header = { name: 'feedback_job', data: data }
|
|
155
|
+
if(source.readyState == WebSocket.OPEN){
|
|
156
|
+
source.send(JSON.stringify(h))
|
|
157
|
+
}
|
|
158
|
+
this.tag = ''
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Update parameter, Called by cluster server
|
|
163
|
+
* @param data Target container
|
|
164
|
+
*/
|
|
165
|
+
set_parameter = (data:Parameter) => {
|
|
166
|
+
this.parameter = data
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Update libraries, Called by cluster server
|
|
171
|
+
* @param data Target container
|
|
172
|
+
*/
|
|
173
|
+
set_libs = (data:Libraries) => {
|
|
174
|
+
this.libraries = data
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Update parameter string, Called by cluster server
|
|
179
|
+
* @deprecated The method should not be used
|
|
180
|
+
* @param data Target keyvalue
|
|
181
|
+
*/
|
|
182
|
+
set_string = (data:Setter) => {
|
|
183
|
+
if(this.parameter == undefined) return
|
|
184
|
+
const index = this.parameter.containers.findIndex(x => x.name == data.key&& x.type == DataType.String)
|
|
185
|
+
if(index != -1) this.parameter.containers[index].value = data.value
|
|
186
|
+
this.messager_log(`[Parameter string sync] ${data.key} = ${data.value}`)
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Update parameter number, Called by cluster server
|
|
190
|
+
* @deprecated The method should not be used
|
|
191
|
+
* @param data Target keyvalue
|
|
192
|
+
*/
|
|
193
|
+
set_number = (data:Setter) => {
|
|
194
|
+
if(this.parameter == undefined) return
|
|
195
|
+
const index = this.parameter.containers.findIndex(x => x.name == data.key && x.type == DataType.Number)
|
|
196
|
+
if(index != -1) this.parameter.containers[index].value = data.value
|
|
197
|
+
this.messager_log(`[Parameter number sync] ${data.key} = ${data.value}`)
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Update parameter boolean, Called by cluster server
|
|
201
|
+
* @deprecated The method should not be used
|
|
202
|
+
* @param data Target keyvalue
|
|
203
|
+
*/
|
|
204
|
+
set_boolean = (data:Setter) => {
|
|
205
|
+
if(this.parameter == undefined) return
|
|
206
|
+
const index = this.parameter.containers.findIndex(x => x.name == data.key && x.type == DataType.Boolean)
|
|
207
|
+
if(index != -1) this.parameter.containers[index].value = data.value
|
|
208
|
+
this.messager_log(`[Parameter boolean sync] ${data.key} = ${data.value}`)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// ========================
|
|
2
|
+
//
|
|
3
|
+
// Share Codebase
|
|
4
|
+
//
|
|
5
|
+
// ========================
|
|
6
|
+
import { Header } from "../interface"
|
|
7
|
+
|
|
8
|
+
export class ClientHTTP {
|
|
9
|
+
url: string
|
|
10
|
+
method: string
|
|
11
|
+
params: any
|
|
12
|
+
|
|
13
|
+
constructor(_url:string, _method:string, _params:any){
|
|
14
|
+
this.url = _url
|
|
15
|
+
this.method = _method
|
|
16
|
+
this.params = _params
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
RUN = () => {
|
|
20
|
+
fetch(this.url, {
|
|
21
|
+
method: this.method,
|
|
22
|
+
body: this.params
|
|
23
|
+
}).then(x => {
|
|
24
|
+
x.text().then(y => {
|
|
25
|
+
const h:Header = {
|
|
26
|
+
name: "result",
|
|
27
|
+
data: {
|
|
28
|
+
status: x.status,
|
|
29
|
+
statusText: x.statusText,
|
|
30
|
+
headers: x.headers,
|
|
31
|
+
ok: x.ok,
|
|
32
|
+
data: y
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
console.log(JSON.stringify(h))
|
|
36
|
+
})
|
|
37
|
+
}).catch((reason) => {
|
|
38
|
+
const h:Header = {
|
|
39
|
+
name: "error",
|
|
40
|
+
data: reason
|
|
41
|
+
}
|
|
42
|
+
console.log(JSON.stringify(h))
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
}
|