@naanlang/naan 1.6.0 → 1.7.1
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.md +1 -1
- package/README.md +1 -1
- package/bin/index.js +2 -2
- package/dist/env_web.js +3 -32
- package/dist/naan.min.js +10 -10
- package/frameworks/browser/sworker.js +23 -23
- package/frameworks/browser/terminals.nlg +161 -212
- package/frameworks/browser/worker.nlg +78 -30
- package/frameworks/browser/workers.nlg +1 -1
- package/frameworks/browser/ws_client.nlg +23 -87
- package/frameworks/client/apiclient.nlg +91 -58
- package/frameworks/client/client.nlg +22 -2
- package/frameworks/client/psm_client.nlg +10 -14
- package/frameworks/client/relaycon.nlg +1 -1
- package/frameworks/common/common.nlg +13 -9
- package/frameworks/node/apiserver.nlg +1 -2
- package/frameworks/node/node.nlg +1 -0
- package/frameworks/node/pty.nlg +21 -21
- package/frameworks/node/shell.nlg +26 -3
- package/frameworks/node/worker.nlg +166 -84
- package/frameworks/node/ws_client.nlg +25 -84
- package/frameworks/project/build.nlg +83 -32
- package/frameworks/project/projects.nlg +2 -2
- package/frameworks/running/debugcom.nlg +20 -3
- package/frameworks/running/debugnub.nlg +16 -7
- package/frameworks/running/executors.nlg +36 -26
- package/frameworks/running/instances.nlg +34 -22
- package/frameworks/running/remote_req.nlg +6 -9
- package/frameworks/running/remote_res.nlg +2 -2
- package/frameworks/running/sourcecode.nlg +3 -2
- package/frameworks/running/taskexec.nlg +14 -57
- package/frameworks/service/imp.nlg +37 -5
- package/frameworks/service/nubnode.nlg +39 -8
- package/frameworks/service/nubweb.nlg +28 -10
- package/frameworks/storage/file_manager.nlg +3 -2
- package/lib/browser/env_web.js +10 -39
- package/lib/browser/env_webworker.js +1 -30
- package/lib/core/naanlib.js +10 -10
- package/lib/env_node.js +5 -22
- package/lib/env_nodeworker.js +8 -43
- package/lib/node_server_init.nlg +149 -0
- package/lib/node_worker.js +36 -0
- package/lib/node_worker_init.nlg +43 -0
- package/package.json +1 -1
- package/plugins/openSearch/openSearch.nlg +3 -2
- package/plugins/openSearch/os_dynamo.nlg +3 -2
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +44 -2
- package/plugins/serviceAws/aws_dynamo.nlg +144 -63
- package/plugins/serviceAws/aws_utils.nlg +6 -11
- package/plugins/serviceAws/serviceAws.nlg +3 -2
- package/test/test_01_core.nlg +2 -2
- package/test/test_02_context.nlg +8 -8
- package/test/test_03_jsinterop.nlg +3 -3
- package/test/test_07_lingo.nlg +1 -1
|
@@ -10,13 +10,6 @@
|
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
/*
|
|
14
|
-
* globals
|
|
15
|
-
*
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
gWorkerController = false;
|
|
19
|
-
|
|
20
13
|
|
|
21
14
|
/*
|
|
22
15
|
* CurBot
|
|
@@ -46,13 +39,12 @@ closure CurBot(options, local curbot) {
|
|
|
46
39
|
debugtext: ONdebugText.proc
|
|
47
40
|
status: ONstatus.proc
|
|
48
41
|
debugout: ONdebugOut.proc
|
|
49
|
-
messageout: ONmessageOut.proc
|
|
50
42
|
ioctl: ONioctl.proc
|
|
51
43
|
})
|
|
52
44
|
curbot.guid = 1
|
|
53
45
|
curbot.host = js.t // our magic Naan controller reference
|
|
54
46
|
|
|
55
|
-
curbot.instanceID =
|
|
47
|
+
curbot.instanceID = App.imp.getUUID("CurBot(${options.name}) instanceID")
|
|
56
48
|
curbot.sender = App.imp.register(curbot, {
|
|
57
49
|
name: curbot.name
|
|
58
50
|
type: curbot.type
|
|
@@ -86,12 +78,46 @@ closure CurBot(options, local curbot) {
|
|
|
86
78
|
//
|
|
87
79
|
// Send the message to all our subscribers.
|
|
88
80
|
//
|
|
89
|
-
function notifyAll(message, exceptID, local topDestID) {
|
|
81
|
+
curbot.notifyAll = function notifyAll(message, exceptID, local topDestID) {
|
|
90
82
|
for topDestID in curbot.topsubs
|
|
91
83
|
if topDestID != exceptID
|
|
92
84
|
curbot.sender(topDestID, message)
|
|
93
85
|
}
|
|
94
86
|
|
|
87
|
+
// textEntry
|
|
88
|
+
//
|
|
89
|
+
// Simulate text entry for subprocess support.
|
|
90
|
+
//
|
|
91
|
+
curbot.textEntry = function textEntry(text) {
|
|
92
|
+
curbot.host.DispatchMessage({
|
|
93
|
+
id: "keyline"
|
|
94
|
+
text: text
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// rawStart
|
|
99
|
+
//
|
|
100
|
+
// Configure ioctls for raw terminal operation using the spawned PTY process.
|
|
101
|
+
//
|
|
102
|
+
curbot.rawStart = closure rawStart(ptySpawned, local dokey, doresize, doattach) {
|
|
103
|
+
dokey = xnew(function(msg, arg) { ptySpawned.write(arg.key) })
|
|
104
|
+
js.t.ioctl_add_listener("raw-key", dokey)
|
|
105
|
+
doresize = xnew(function(msg, dim) { ptySpawned.resize(dim.cols, dim.rows) })
|
|
106
|
+
js.t.ioctl_add_listener("term-resize", doresize)
|
|
107
|
+
js.t.ioctl("raw", true)
|
|
108
|
+
doattach = xnew(function(msg, arg) { js.t.ioctl("raw", true) }) // restore raw mode when we are attached
|
|
109
|
+
js.t.ioctl_add_listener("term-attach", doattach)
|
|
110
|
+
|
|
111
|
+
curbot.rawStop = function rawStop() {
|
|
112
|
+
js.t.ioctl("raw")
|
|
113
|
+
js.t.ioctl_remove_listener("raw-key", dokey)
|
|
114
|
+
js.t.ioctl_remove_listener("term-resize", doresize)
|
|
115
|
+
js.t.ioctl_remove_listener("term-attach", doattach)
|
|
116
|
+
dokey = doresize = doattach = false
|
|
117
|
+
curbot.rawStop = undefined
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
95
121
|
// impReceive
|
|
96
122
|
//
|
|
97
123
|
// Received a payload.
|
|
@@ -104,11 +130,14 @@ closure CurBot(options, local curbot) {
|
|
|
104
130
|
subscribe(sourceID)
|
|
105
131
|
else if message.id == "unsubscribe"
|
|
106
132
|
unsubscribe(sourceID)
|
|
107
|
-
else if message.id == "typed"
|
|
108
|
-
notifyAll(message, sourceID)
|
|
109
133
|
else {
|
|
110
|
-
|
|
111
|
-
|
|
134
|
+
subscribe(sourceID) // auto-subscribe
|
|
135
|
+
if message.id == "typed"
|
|
136
|
+
notifyAll(message, sourceID)
|
|
137
|
+
else if message.id == "keyline" && curbot.keyin
|
|
138
|
+
curbot.keyin(message.text) // terminal keyboard -> pty subprocess
|
|
139
|
+
else
|
|
140
|
+
curbot.host.DispatchMessage(message)
|
|
112
141
|
}
|
|
113
142
|
}
|
|
114
143
|
|
|
@@ -117,7 +146,7 @@ closure CurBot(options, local curbot) {
|
|
|
117
146
|
// Received an unknown destination notice.
|
|
118
147
|
//
|
|
119
148
|
curbot.impUnknown = function impUnknown(unknownID, sourceID, gateway) {
|
|
120
|
-
if curbot.topsubs
|
|
149
|
+
if curbot.topsubs.indexOf(unknownID) >= 0 { // did not have courtesy to unsubscribe!
|
|
121
150
|
unsubscribe(unknownID)
|
|
122
151
|
return
|
|
123
152
|
}
|
|
@@ -125,28 +154,15 @@ closure CurBot(options, local curbot) {
|
|
|
125
154
|
debuglog("CurBot.impUnknownID:", unknownID, "sourceID:", sourceID)
|
|
126
155
|
}
|
|
127
156
|
|
|
128
|
-
// replyHook
|
|
129
|
-
//
|
|
130
|
-
// Intercept the ReplyMessage function to see if we should handle it.
|
|
131
|
-
//
|
|
132
|
-
function replyHook(data) {
|
|
133
|
-
// js.g.console.log("CurBot.replyHook:\n", Dialect.print(new(data)))
|
|
134
|
-
// debuglog("CurBot.replyHook:\n", Dialect.print(new(data)))
|
|
135
|
-
/* ###
|
|
136
|
-
if data._guid
|
|
137
|
-
workco.uworkers[data._guid].msgport.postMessage({
|
|
138
|
-
id: "targetreceive"
|
|
139
|
-
data: data
|
|
140
|
-
})
|
|
141
|
-
*/
|
|
142
|
-
}
|
|
143
|
-
curbot.host.OnReplyHook(replyHook.proc)
|
|
144
|
-
|
|
145
157
|
// ONtextOut
|
|
146
158
|
//
|
|
147
159
|
// Process console text output by Naan.
|
|
148
160
|
//
|
|
149
161
|
function ONtextOut(text) {
|
|
162
|
+
if curbot.naanin {
|
|
163
|
+
curbot.naanin(text) // naan output -> pty subprocess
|
|
164
|
+
return
|
|
165
|
+
}
|
|
150
166
|
notifyAll({
|
|
151
167
|
id: "textout",
|
|
152
168
|
text: text,
|
|
@@ -187,22 +203,6 @@ closure CurBot(options, local curbot) {
|
|
|
187
203
|
})
|
|
188
204
|
}
|
|
189
205
|
|
|
190
|
-
// ONmessageOut
|
|
191
|
-
//
|
|
192
|
-
// Process message output.
|
|
193
|
-
//
|
|
194
|
-
function ONmessageOut(data, local _guid) {
|
|
195
|
-
if data._guid {
|
|
196
|
-
_guid = data._guid
|
|
197
|
-
data._guid = undefined
|
|
198
|
-
curbot.sender(_guid, {
|
|
199
|
-
id: "targetout",
|
|
200
|
-
data: data
|
|
201
|
-
})
|
|
202
|
-
return
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
206
|
// ONioctl
|
|
207
207
|
//
|
|
208
208
|
// Process ioctl.
|
|
@@ -236,11 +236,17 @@ closure CurBot(options, local curbot) {
|
|
|
236
236
|
* type: <string> // target category, e.g. "Local"
|
|
237
237
|
* startup: <dictionary> // startup options
|
|
238
238
|
* topOriginID: <string> // so top can route to us through IMP
|
|
239
|
+
* rootPath: <string> // root path of worker's folder, must have lib/node_worker_init.nlg
|
|
240
|
+
* node: {
|
|
241
|
+
* // see NodeJS docs
|
|
242
|
+
* workerData: { // passed to node_worker.js
|
|
243
|
+
* }
|
|
244
|
+
* }
|
|
239
245
|
* }
|
|
240
246
|
*
|
|
241
247
|
*/
|
|
242
248
|
|
|
243
|
-
closure NowBot(options, local nowbot, subChannel) {
|
|
249
|
+
closure NowBot(options, local nowbot, nodeops, subChannel, ptySpawned) {
|
|
244
250
|
global(App, js, threads, JSpath)
|
|
245
251
|
nowbot = new(object, this)
|
|
246
252
|
nowbot.topsubs = []
|
|
@@ -248,13 +254,19 @@ closure NowBot(options, local nowbot, subChannel) {
|
|
|
248
254
|
type: "node-worker"
|
|
249
255
|
}, options)
|
|
250
256
|
|
|
251
|
-
|
|
252
|
-
|
|
257
|
+
nodeops = merge({
|
|
258
|
+
// execArgv: ["--inspect-brk"] // unfortunately doesn't work; use VS Code instead
|
|
259
|
+
}, options.node)
|
|
260
|
+
if options.rootPath
|
|
261
|
+
nodeops.workerData = merge({
|
|
262
|
+
rootPath: options.rootPath
|
|
263
|
+
}, nodeops.workerData)
|
|
264
|
+
nowbot.thread = xnew(threads.Worker, JSpath.resolve(js.d, "lib/node_worker.js"), nodeops)
|
|
253
265
|
subChannel = xnew(threads.MessageChannel)
|
|
254
266
|
nowbot.thread.postMessage({ hereIsYourPort: subChannel.port1 }, [subChannel.port1])
|
|
255
267
|
nowbot.msgport = subChannel.port2
|
|
256
268
|
|
|
257
|
-
nowbot.instanceID =
|
|
269
|
+
nowbot.instanceID = App.imp.getUUID("NowBot(${options.name}) instanceID")
|
|
258
270
|
nowbot.sender = App.imp.register(nowbot, {
|
|
259
271
|
name: options.name
|
|
260
272
|
type: options.type
|
|
@@ -303,6 +315,43 @@ closure NowBot(options, local nowbot, subChannel) {
|
|
|
303
315
|
nowbot.sender(topDestID, message)
|
|
304
316
|
}
|
|
305
317
|
|
|
318
|
+
// textEntry
|
|
319
|
+
//
|
|
320
|
+
// Simulate text entry for subprocess support.
|
|
321
|
+
//
|
|
322
|
+
nowbot.textEntry = function textEntry(text) {
|
|
323
|
+
nowbot.msgport.postMessage({
|
|
324
|
+
id: "keyline"
|
|
325
|
+
text: text
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ioctl
|
|
330
|
+
//
|
|
331
|
+
// Send an ioctl to the terminal.
|
|
332
|
+
//
|
|
333
|
+
nowbot.ioctl = function ioctl(op, arg) {
|
|
334
|
+
notifyAll({
|
|
335
|
+
id: "ioctl"
|
|
336
|
+
op: op
|
|
337
|
+
arg: arg
|
|
338
|
+
})
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// rawMode
|
|
342
|
+
//
|
|
343
|
+
// Configure ioctls for raw terminal operation using the spawned PTY process.
|
|
344
|
+
//
|
|
345
|
+
nowbot.rawStart = function rawStart(ptys) {
|
|
346
|
+
ptySpawned = ptys
|
|
347
|
+
ioctl("raw", true)
|
|
348
|
+
nowbot.rawStop = function rawStop() {
|
|
349
|
+
ioctl("raw", false)
|
|
350
|
+
nowbot.rawStop = undefined
|
|
351
|
+
ptySpawned = false
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
306
355
|
// impReceive
|
|
307
356
|
//
|
|
308
357
|
// Received a payload.
|
|
@@ -314,22 +363,35 @@ closure NowBot(options, local nowbot, subChannel) {
|
|
|
314
363
|
subscribe(sourceID)
|
|
315
364
|
else if message.id == "unsubscribe"
|
|
316
365
|
unsubscribe(sourceID)
|
|
317
|
-
else if message.id == "typed"
|
|
318
|
-
notifyAll(message, sourceID)
|
|
319
366
|
else {
|
|
367
|
+
subscribe(sourceID) // auto-subscribe
|
|
368
|
+
if message.id == "typed" {
|
|
369
|
+
notifyAll(message, sourceID)
|
|
370
|
+
return
|
|
371
|
+
}
|
|
372
|
+
if ptySpawned { // raw mode: kludgey, but works
|
|
373
|
+
if message.id == "ioctl-event" {
|
|
374
|
+
if message.msg == "raw-key"
|
|
375
|
+
ptySpawned.write(message.arg.key)
|
|
376
|
+
else if message.msg == "term-resize"
|
|
377
|
+
ptySpawned.resize(message.arg.cols, message.arg.rows)
|
|
378
|
+
}
|
|
379
|
+
if (message.id == "attached")
|
|
380
|
+
ioctl("raw", true) // restore raw mode after terminal attach
|
|
381
|
+
}
|
|
320
382
|
if message.id == "keyline" && nowbot.keyin
|
|
321
383
|
nowbot.keyin(message.text) // terminal keyboard -> pty subprocess
|
|
322
384
|
else
|
|
323
385
|
nowbot.msgport.postMessage(message)
|
|
324
386
|
}
|
|
325
387
|
}
|
|
326
|
-
|
|
388
|
+
|
|
327
389
|
// impUnknown
|
|
328
390
|
//
|
|
329
391
|
// Received an unknown destination notice.
|
|
330
392
|
//
|
|
331
393
|
nowbot.impUnknown = function impUnknown(unknownID, sourceID, gateway) {
|
|
332
|
-
if nowbot.topsubs
|
|
394
|
+
if nowbot.topsubs.indexOf(unknownID) >= 0 { // did not have courtesy to unsubscribe!
|
|
333
395
|
unsubscribe(unknownID)
|
|
334
396
|
return
|
|
335
397
|
}
|
|
@@ -348,9 +410,11 @@ closure NowBot(options, local nowbot, subChannel) {
|
|
|
348
410
|
id: "terminated"
|
|
349
411
|
error: error
|
|
350
412
|
})
|
|
413
|
+
nowbot.dispatchEvent("terminated", nowbot.instanceID, { error: error })
|
|
414
|
+
nowbot.thread = false
|
|
351
415
|
destroy()
|
|
352
416
|
})
|
|
353
|
-
|
|
417
|
+
|
|
354
418
|
// onexit
|
|
355
419
|
//
|
|
356
420
|
// Note that the worker thread has terminated.
|
|
@@ -360,9 +424,21 @@ closure NowBot(options, local nowbot, subChannel) {
|
|
|
360
424
|
id: "terminated"
|
|
361
425
|
exitCode: exitCode
|
|
362
426
|
})
|
|
427
|
+
nowbot.dispatchEvent("terminated", nowbot.instanceID, { exitCode: exitCode })
|
|
428
|
+
nowbot.thread = false
|
|
363
429
|
destroy()
|
|
364
430
|
})
|
|
365
431
|
|
|
432
|
+
// close
|
|
433
|
+
//
|
|
434
|
+
// Close and remove our IMP resources, but leave the worker alone.
|
|
435
|
+
//
|
|
436
|
+
nowbot.close = function close() {
|
|
437
|
+
nowbot.impconn.destroy()
|
|
438
|
+
nowbot.impconn = false
|
|
439
|
+
App.imp.unregister(nowbot.instanceID)
|
|
440
|
+
}
|
|
441
|
+
|
|
366
442
|
// destroy
|
|
367
443
|
//
|
|
368
444
|
// Ensure the worker is destroyed.
|
|
@@ -370,9 +446,7 @@ closure NowBot(options, local nowbot, subChannel) {
|
|
|
370
446
|
nowbot.destroy = function destroy() {
|
|
371
447
|
terminate()
|
|
372
448
|
nowbot.thread = false
|
|
373
|
-
|
|
374
|
-
nowbot.impconn = false
|
|
375
|
-
App.imp.unregister(nowbot.instanceID)
|
|
449
|
+
close()
|
|
376
450
|
}
|
|
377
451
|
|
|
378
452
|
// NaaN message
|
|
@@ -380,42 +454,35 @@ closure NowBot(options, local nowbot, subChannel) {
|
|
|
380
454
|
// A message has been received from NaaN running on the worker thread.
|
|
381
455
|
//
|
|
382
456
|
nowbot.msgport.on("message", function (msg, local startmsg) {
|
|
383
|
-
if msg.id == "naan-imp-
|
|
457
|
+
if msg.id == "naan-imp-out" {
|
|
384
458
|
nowbot.impconn.dispatch(new(msg.data))
|
|
385
459
|
return
|
|
386
460
|
} else if msg.id == "loaded" {
|
|
387
461
|
startmsg = {
|
|
388
462
|
id: "start"
|
|
389
463
|
state: false // resume state, if we have any
|
|
390
|
-
altcmd:
|
|
464
|
+
altcmd: options.startup.initcmds
|
|
391
465
|
dirpath: js.d
|
|
392
466
|
workerID: workerID
|
|
393
|
-
workerGUID: worker.guid
|
|
394
467
|
metadata: {
|
|
395
468
|
mainInstanceID: App.imp.us.instanceID
|
|
396
469
|
name: options.name
|
|
470
|
+
maintext: options.startup.maintext.concat("\n")
|
|
397
471
|
}
|
|
398
472
|
}
|
|
399
|
-
if options.startup.initcmds
|
|
400
|
-
startmsg.altcmd = options.startup.initcmds.concat("\n")
|
|
401
473
|
if options.startup.dirpath
|
|
402
474
|
startmsg.dirpath = options.startup.dirpath
|
|
403
475
|
nowbot.msgport.postMessage(startmsg)
|
|
404
476
|
notifyAll({
|
|
405
477
|
id: "started"
|
|
406
478
|
})
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
js.t.DispatchMessage({
|
|
410
|
-
id: "targetin"
|
|
411
|
-
data: msg.data
|
|
412
|
-
}) }
|
|
413
|
-
else if msg.id == "textout" && nowbot.naanin
|
|
479
|
+
nowbot.dispatchEvent("started", nowbot.instanceID)
|
|
480
|
+
} else if msg.id == "textout" && nowbot.naanin
|
|
414
481
|
nowbot.naanin(msg.text) // naan output -> pty subprocess
|
|
415
482
|
else
|
|
416
483
|
notifyAll(msg)
|
|
417
484
|
})
|
|
418
|
-
|
|
485
|
+
|
|
419
486
|
// terminate
|
|
420
487
|
//
|
|
421
488
|
// Terminate this worker asap.
|
|
@@ -440,6 +507,7 @@ closure NowBot(options, local nowbot, subChannel) {
|
|
|
440
507
|
}
|
|
441
508
|
|
|
442
509
|
nowbot.subprocess = MakeSubProcessor(nowbot) // Enable PTY subprocesses on our terminal
|
|
510
|
+
Events(nowbot)
|
|
443
511
|
|
|
444
512
|
// finis
|
|
445
513
|
|
|
@@ -482,7 +550,7 @@ closure NobBot(options, local nobbot)
|
|
|
482
550
|
|
|
483
551
|
nobbot.impconn = App.imp.responder(function(data) {
|
|
484
552
|
nobbot.msgport.postMessage({ // send to worker
|
|
485
|
-
id: "naan-imp-
|
|
553
|
+
id: "naan-imp-in" // scoped for NodeWorkerNub
|
|
486
554
|
data: data
|
|
487
555
|
})
|
|
488
556
|
list(false, { sent: true })
|
|
@@ -518,7 +586,9 @@ closure NobBot(options, local nobbot)
|
|
|
518
586
|
* return value is a result tuple with an object that can be used to kill or monitor the
|
|
519
587
|
* subprocess.
|
|
520
588
|
*
|
|
521
|
-
*
|
|
589
|
+
* exec.subprocess(executable, args, options)
|
|
590
|
+
*
|
|
591
|
+
* The subprocess can be connected to the terminal raw, or in a variety of ways:
|
|
522
592
|
*
|
|
523
593
|
* term-keyboard | term-display | worker-in | worker-out
|
|
524
594
|
* -------------------------------------|---------------|------------|-----------
|
|
@@ -527,29 +597,28 @@ closure NobBot(options, local nobbot)
|
|
|
527
597
|
*
|
|
528
598
|
* Options:
|
|
529
599
|
* {
|
|
530
|
-
* input:
|
|
531
|
-
* output:
|
|
532
|
-
*
|
|
600
|
+
* input: false | "keyboard" | "pipe"
|
|
601
|
+
* output: false | "display" | "pipe" | "tee"
|
|
602
|
+
* raw: <boolean>
|
|
603
|
+
* spawn: <PtyManager.spawn options>
|
|
533
604
|
* }
|
|
534
605
|
*
|
|
535
606
|
*/
|
|
536
607
|
|
|
537
608
|
closure MakeSubProcessor(execbot) {
|
|
609
|
+
global()
|
|
538
610
|
closure subprocess(
|
|
539
611
|
executable, args, options, local error, pty) {
|
|
540
612
|
|
|
541
613
|
// ptyData - text has been returned from the subprocess
|
|
542
614
|
function ptyData(text) {
|
|
543
615
|
if options.output == "display" || options.output == "tee"
|
|
544
|
-
notifyAll({
|
|
616
|
+
execbot.notifyAll({ // send to display
|
|
545
617
|
id: "textout",
|
|
546
618
|
text: text
|
|
547
619
|
})
|
|
548
620
|
if options.output == "pipe" || options.output == "tee"
|
|
549
|
-
|
|
550
|
-
id: "keyline"
|
|
551
|
-
text: text
|
|
552
|
-
})
|
|
621
|
+
execbot.textEntry(text) // send to NaaN worker
|
|
553
622
|
}
|
|
554
623
|
|
|
555
624
|
// ptyKilled - the subprocess has terminated
|
|
@@ -558,6 +627,10 @@ closure MakeSubProcessor(execbot) {
|
|
|
558
627
|
execbot.keyin = false
|
|
559
628
|
if execbot.naanin === naanin
|
|
560
629
|
execbot.naanin = false
|
|
630
|
+
if options.raw {
|
|
631
|
+
execbot.rawStop()
|
|
632
|
+
execbot.textEntry("\x15mu();;\n") // send to NaaN worker
|
|
633
|
+
}
|
|
561
634
|
}
|
|
562
635
|
|
|
563
636
|
// keyin - the terminal has generated text for the subprocess
|
|
@@ -570,6 +643,7 @@ closure MakeSubProcessor(execbot) {
|
|
|
570
643
|
pty.write(text)
|
|
571
644
|
}
|
|
572
645
|
|
|
646
|
+
require("./pty.nlg") // load on demand
|
|
573
647
|
`(error, pty) = PtyManager().spawn(executable, args, {
|
|
574
648
|
ptyData: ptyData
|
|
575
649
|
ptyKilled: ptyKilled
|
|
@@ -577,9 +651,17 @@ closure MakeSubProcessor(execbot) {
|
|
|
577
651
|
if error
|
|
578
652
|
return (list(error))
|
|
579
653
|
if options.input == "keyboard"
|
|
580
|
-
|
|
654
|
+
execbot.keyin = keyin // replace existing
|
|
581
655
|
else if options.input == "pipe"
|
|
582
656
|
execbot.naanin = naanin // replace existing
|
|
657
|
+
if options.raw
|
|
658
|
+
execbot.rawStart(pty)
|
|
659
|
+
|
|
660
|
+
// our worker has terminated
|
|
661
|
+
execbot.addEventListener("terminated", function() {
|
|
662
|
+
pty.kill()
|
|
663
|
+
})
|
|
664
|
+
|
|
583
665
|
list(false, pty)
|
|
584
666
|
}
|
|
585
667
|
};
|
|
@@ -13,106 +13,47 @@
|
|
|
13
13
|
/*
|
|
14
14
|
* WebSocket
|
|
15
15
|
*
|
|
16
|
-
* Create a WebSocket client connection object.
|
|
16
|
+
* Create a WebSocket client connection object, returning a tuple.
|
|
17
17
|
*
|
|
18
18
|
* Required:
|
|
19
|
-
*
|
|
19
|
+
* urlstr: <string>
|
|
20
20
|
*
|
|
21
21
|
* Options:
|
|
22
22
|
* {
|
|
23
23
|
* wss: <boolean> // true to use secure connection
|
|
24
|
+
* query: <string> // query string starting with ?
|
|
24
25
|
* onopen: <proc>(e) // connection has opened
|
|
25
26
|
* onmessage: <proc>(e, message) // message received
|
|
26
27
|
* onerror: <proc>(e) // error occurred
|
|
27
28
|
* onclose: <proc>(e) // connection has closed
|
|
28
|
-
* query: <string> // query string starting with ? for insecure connections
|
|
29
|
-
* querys: <string> // query string starting with ? for secure connections
|
|
30
29
|
* }
|
|
31
30
|
*
|
|
32
31
|
*/
|
|
33
32
|
|
|
34
|
-
closure WebSocket(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
closure WebSocket(urlstr, options, local error, wslib, url, ws) {
|
|
34
|
+
`(error, wslib) = await(js.i("ws"))
|
|
35
|
+
if error
|
|
36
|
+
return (list(error))
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
wsock.connect = closure connect(local error, ws, url, pending) {
|
|
44
|
-
if wsock.ws
|
|
45
|
-
return (list(false, { open: true }))
|
|
46
|
-
`(error, ws) = await(js.i("ws"))
|
|
47
|
-
if error
|
|
48
|
-
return (list(error))
|
|
49
|
-
if options.wss
|
|
50
|
-
url = "wss://${host}${options.querys||''}"
|
|
51
|
-
else
|
|
52
|
-
url = "ws://${host}${options.query||''}"
|
|
53
|
-
wsock.ws = xnew(ws.default, url)
|
|
54
|
-
pending = new(nonce)
|
|
55
|
-
|
|
56
|
-
// onopen
|
|
57
|
-
// The WebSocket connection is now open.
|
|
58
|
-
wsock.ws.onopen = closure onopen(e) {
|
|
59
|
-
pending.signal(list(false, { ok: true }))
|
|
60
|
-
options.onopen(e, e.target, e.type)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// onmessage
|
|
64
|
-
// A message was received from the worker via WebSockets.
|
|
65
|
-
wsock.ws.onmessage = closure onmessage(e, local message) {
|
|
66
|
-
try {
|
|
67
|
-
message = new(JSONparse(e.data))
|
|
68
|
-
} catch {
|
|
69
|
-
debuglog("can't decode incoming ws message:", exception)
|
|
70
|
-
options.onerror(Error("can't decode incoming message:", exception))
|
|
71
|
-
return
|
|
72
|
-
}
|
|
73
|
-
options.onmessage(e, message)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// onerror
|
|
77
|
-
// An error occurred on the WebSocket connection.
|
|
78
|
-
wsock.ws.onerror = closure onerror(e, local error) {
|
|
79
|
-
error = Error("websocket failed", e)
|
|
80
|
-
options.onerror(e, error)
|
|
81
|
-
pending.signal(list(error)) // ensure connect completes
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// onclose
|
|
85
|
-
// The WebSocket connection was closed.
|
|
86
|
-
wsock.ws.onclose = closure onclose(e) {
|
|
87
|
-
options.onclose(e, e.code, e.reason)
|
|
88
|
-
pending.signal(list(Error("connection closed"))) // ensure connect completes
|
|
89
|
-
wsock.ws = false
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
pending.wait()
|
|
38
|
+
try {
|
|
39
|
+
url = xnew(js.g.URL, urlstr)
|
|
40
|
+
} catch {
|
|
41
|
+
return (list(Error("invalid URL ${urlstr}", exception)))
|
|
93
42
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
if !reason
|
|
109
|
-
reason = 1000 // normal close
|
|
110
|
-
wsock.ws.close(reason)
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// finis
|
|
114
|
-
|
|
115
|
-
wsock
|
|
43
|
+
options = merge({
|
|
44
|
+
query: url.search || ''
|
|
45
|
+
wss: url.protocol == "https:"
|
|
46
|
+
}, options)
|
|
47
|
+
if options.wss
|
|
48
|
+
url = "wss://${url.host}${options.query}"
|
|
49
|
+
else
|
|
50
|
+
url = "ws://${url.host}${options.query}"
|
|
51
|
+
ws = xnew(wslib.default, url)
|
|
52
|
+
ws.onopen = options.onopen
|
|
53
|
+
ws.onmessage = options.onmessage
|
|
54
|
+
ws.onerror = options.onerror
|
|
55
|
+
ws.onclose = options.onclose
|
|
56
|
+
list(false, ws)
|
|
116
57
|
};
|
|
117
58
|
|
|
118
59
|
|