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.
Files changed (55) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2 -0
  3. package/bun.lock +730 -0
  4. package/index.ts +1 -0
  5. package/jest.config.js +8 -0
  6. package/package.json +42 -0
  7. package/src/client/analysis.ts +377 -0
  8. package/src/client/client.ts +230 -0
  9. package/src/client/cluster.ts +125 -0
  10. package/src/client/execute.ts +210 -0
  11. package/src/client/http.ts +45 -0
  12. package/src/client/javascript.ts +535 -0
  13. package/src/client/job_execute.ts +216 -0
  14. package/src/client/job_parameter.ts +41 -0
  15. package/src/client/os.ts +210 -0
  16. package/src/client/parameter.ts +58 -0
  17. package/src/client/resource.ts +121 -0
  18. package/src/client/shell.ts +147 -0
  19. package/src/interface/base.ts +82 -0
  20. package/src/interface/bus.ts +144 -0
  21. package/src/interface/enum.ts +181 -0
  22. package/src/interface/execute.ts +47 -0
  23. package/src/interface/record.ts +131 -0
  24. package/src/interface/server.ts +91 -0
  25. package/src/interface/struct.ts +292 -0
  26. package/src/interface/table.ts +34 -0
  27. package/src/interface/ui.ts +35 -0
  28. package/src/interface.ts +50 -0
  29. package/src/lan/en.json +395 -0
  30. package/src/lan/zh_TW.json +395 -0
  31. package/src/plugins/i18n.ts +20 -0
  32. package/src/script/console_manager.ts +135 -0
  33. package/src/script/console_server_manager.ts +46 -0
  34. package/src/script/execute/base.ts +309 -0
  35. package/src/script/execute/feedback.ts +212 -0
  36. package/src/script/execute/region_job.ts +14 -0
  37. package/src/script/execute/region_project.ts +23 -0
  38. package/src/script/execute/region_subtask.ts +14 -0
  39. package/src/script/execute/region_task.ts +23 -0
  40. package/src/script/execute/runner.ts +339 -0
  41. package/src/script/execute/util_parser.ts +175 -0
  42. package/src/script/execute_manager.ts +348 -0
  43. package/src/script/socket_manager.ts +329 -0
  44. package/src/script/webhook_manager.ts +6 -0
  45. package/src/script/webhook_server_manager.ts +102 -0
  46. package/src/util/server/console_handle.ts +248 -0
  47. package/src/util/server/log_handle.ts +194 -0
  48. package/test/TEST.ts +63 -0
  49. package/test/client/execute.test.ts +54 -0
  50. package/test/client/javascript.test.ts +78 -0
  51. package/test/client/server.test.ts +26 -0
  52. package/test/client/task.test.ts +136 -0
  53. package/test/script/parser.test.ts +110 -0
  54. package/test/script/socket.test.ts +27 -0
  55. 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
+ }