@naanlang/naan 1.4.4 → 1.6.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.md +2 -2
- package/README.md +4 -4
- package/bin/index.js +25 -3
- package/dist/env_web.js +164 -22
- package/dist/naan.min.js +5 -5
- package/frameworks/browser/browser.nlg +2 -2
- package/frameworks/browser/https_request.nlg +2 -2
- package/frameworks/browser/sworker.js +28 -26
- package/frameworks/browser/terminals.nlg +342 -700
- package/frameworks/browser/worker.nlg +255 -0
- package/frameworks/browser/workers.nlg +5 -5
- package/frameworks/browser/ws_client.nlg +15 -10
- package/frameworks/client/apiclient.nlg +204 -19
- package/frameworks/client/client.nlg +22 -2
- package/frameworks/client/psm_client.nlg +12 -3
- package/frameworks/client/relaycon.nlg +87 -161
- package/frameworks/common/events.nlg +101 -0
- package/frameworks/common/repltools.nlg +80 -1
- package/frameworks/node/apiserver.nlg +39 -246
- package/frameworks/node/filesystem.nlg +39 -12
- package/frameworks/node/gitter.nlg +9 -7
- package/frameworks/node/http_request.nlg +34 -13
- package/frameworks/node/https_request.nlg +8 -3
- package/frameworks/node/node.nlg +2 -0
- package/frameworks/node/pty.nlg +346 -0
- package/frameworks/node/shell.nlg +4 -4
- package/frameworks/node/worker.nlg +409 -539
- package/frameworks/node/ws_client.nlg +2 -2
- package/frameworks/project/build.nlg +1 -1
- package/frameworks/project/projects.nlg +2 -1
- package/frameworks/running/debugnub.nlg +17 -4
- package/frameworks/running/executors.nlg +27 -274
- package/frameworks/running/instances.nlg +393 -0
- package/frameworks/running/remote_req.nlg +143 -0
- package/frameworks/running/remote_res.nlg +89 -0
- package/frameworks/running/taskexec.nlg +2 -2
- package/frameworks/service/imp.nlg +736 -0
- package/frameworks/service/model.nlg +71 -0
- package/frameworks/service/nubnode.nlg +105 -0
- package/frameworks/service/nubweb.nlg +106 -0
- package/frameworks/service/service.nlg +28 -0
- package/frameworks/storage/dbt_pouch.nlg +26 -13
- package/frameworks/storage/psm.nlg +39 -5
- package/frameworks/storage/psm_dbtables.nlg +10 -2
- package/lib/browser/env_web.js +171 -29
- package/lib/browser/env_webworker.js +91 -5
- package/lib/core/naanlib.js +5 -5
- package/lib/env_node.js +184 -8
- package/lib/env_nodeworker.js +74 -1
- package/package.json +1 -1
- package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
- package/plugins/serviceAws/aws_dynamo.nlg +1 -0
- package/plugins/serviceAws/aws_s3.nlg +3 -3
- package/plugins/serviceAws/aws_sqs.nlg +3 -1
- package/plugins/serviceAws/aws_utils.nlg +3 -1
- package/plugins/serviceAws/dbt_aws.nlg +22 -20
- package/plugins/serviceAws/psm_aws.nlg +2 -1
- package/plugins/serviceNexara/speechrec.nlg +10 -6
- package/test/test_02_context.nlg +24 -1
- package/test/test_03_jsinterop.nlg +32 -3
- package/plugins/serviceGC/russian_trusted_root_ca_pem.crt +0 -33
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* pty.nlg
|
|
3
|
+
* Naanlib/frameworks/node
|
|
4
|
+
*
|
|
5
|
+
* Pseudo-terminal access for Linux, Windows, and MacOS.
|
|
6
|
+
*
|
|
7
|
+
* column positioning: // // !
|
|
8
|
+
*
|
|
9
|
+
* Copyright (c) 2026 by Richard C. Zulch
|
|
10
|
+
*
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* PtyManager
|
|
16
|
+
*
|
|
17
|
+
* Fork a process with a pseudo-terminal (pty). This returns a pty-process manager object that can be
|
|
18
|
+
* used to manage these processes. On the NodeJS main thread this instantiates the (not thread safe)
|
|
19
|
+
* node-pty library. On worker threads it creates a connection to the main thread to actually perform
|
|
20
|
+
* the work, including data transfers. More than one process can be created simultaneously. If this
|
|
21
|
+
* worker dies then its processes are killed.
|
|
22
|
+
* There is one global PtyManager per thread, including the main thread. Spawn processes from
|
|
23
|
+
* your PtyManager instance and then kill them when done.
|
|
24
|
+
*
|
|
25
|
+
* This uses node-pty, with the API defined here:
|
|
26
|
+
* https://github.com/microsoft/node-pty/blob/main/typings/node-pty.d.ts
|
|
27
|
+
*
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
pty_instance = false;
|
|
31
|
+
|
|
32
|
+
closure PtyManager() {
|
|
33
|
+
global(js, pty_instance)
|
|
34
|
+
if pty_instance
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
if js.k.isMainThread
|
|
38
|
+
pty_instance = ptyManagerMain()
|
|
39
|
+
else
|
|
40
|
+
pty_instance = ptyManagerWorker()
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
/*
|
|
45
|
+
* ptyManagerMain
|
|
46
|
+
*
|
|
47
|
+
* Manager of pty processes on the NodeJS main thread.
|
|
48
|
+
*
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
closure ptyManagerMain(local ptym, pty) {
|
|
52
|
+
global(fs, os, js)
|
|
53
|
+
ptym = new(object, this)
|
|
54
|
+
ptym.workers = []
|
|
55
|
+
|
|
56
|
+
// install
|
|
57
|
+
//
|
|
58
|
+
// Install and/or update the node-pty package.
|
|
59
|
+
//
|
|
60
|
+
ptym.install = closure install(rootpath, local error, fs, output) {
|
|
61
|
+
if !rootpath
|
|
62
|
+
rootpath = js.d
|
|
63
|
+
debuglog("node-pty install at", rootpath)
|
|
64
|
+
`(error, fs) = Filesystem(rootpath)
|
|
65
|
+
`(error, output) = fs.execLines("npm", {
|
|
66
|
+
cmdargs: ["install", "--save", "@lydell/node-pty", "--prefix", js.d ]
|
|
67
|
+
execops: {
|
|
68
|
+
cwd: rootpath
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
if !error {
|
|
72
|
+
if output.exitCode != 0
|
|
73
|
+
error = Error("npm install exit code: ${output.exitCode}", {
|
|
74
|
+
stderr: output.stderr.join("\n")
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
if error
|
|
78
|
+
list(error)
|
|
79
|
+
else
|
|
80
|
+
list(false, output.stdout.join("\n"))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// open
|
|
84
|
+
//
|
|
85
|
+
// Open the pty manager, installing if needed, returning an error.
|
|
86
|
+
//
|
|
87
|
+
ptym.open = closure open(local error, stdout) {
|
|
88
|
+
if !pty {
|
|
89
|
+
`(error, pty) = await(js.i("@lydell/node-pty"))
|
|
90
|
+
if error {
|
|
91
|
+
`(error, stdout) = install()
|
|
92
|
+
if !error
|
|
93
|
+
`(error, pty) = await(js.i("@lydell/node-pty"))
|
|
94
|
+
if error
|
|
95
|
+
return (list(Error("can't use node-pty", error)))
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
list(false, { ok: true })
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// close
|
|
102
|
+
//
|
|
103
|
+
// Close the pty manager, killing any open spawned processes.
|
|
104
|
+
//
|
|
105
|
+
ptym.close = closure close() {
|
|
106
|
+
// ### kill any open spawned processes
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// spawn
|
|
110
|
+
//
|
|
111
|
+
// Spawn a new pty process, returning a result tuple. If executable is omitted then a shell is
|
|
112
|
+
// spawned.
|
|
113
|
+
//
|
|
114
|
+
// options:
|
|
115
|
+
// {
|
|
116
|
+
// workerGUID: <string> // if specified, kills pty process when worker terminates
|
|
117
|
+
// }
|
|
118
|
+
//
|
|
119
|
+
//
|
|
120
|
+
ptym.spawn = closure spawn(executable, args, notify, options,
|
|
121
|
+
local ptySpawned, ptypro) {
|
|
122
|
+
|
|
123
|
+
if !executable {
|
|
124
|
+
if os.platform() == "win32"
|
|
125
|
+
executable = "powershell.exe"
|
|
126
|
+
else
|
|
127
|
+
executable = js.g.process.env.SHELL || "/bin/bash" // default shell with safe fallback
|
|
128
|
+
}
|
|
129
|
+
if !array(args)
|
|
130
|
+
args = []
|
|
131
|
+
options = merge({
|
|
132
|
+
name: "xterm-color"
|
|
133
|
+
cols: 80
|
|
134
|
+
rows: 25
|
|
135
|
+
cwd: js.g.process.env.HOME
|
|
136
|
+
env: js.g.process.env
|
|
137
|
+
}, options)
|
|
138
|
+
|
|
139
|
+
ptySpawned = pty.spawn(executable, args, { // spawn the pty process
|
|
140
|
+
name: options.name
|
|
141
|
+
cols: options.cols
|
|
142
|
+
rows: options.rows
|
|
143
|
+
cwd: options.cwd
|
|
144
|
+
env: options.env
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
ptypro = ptyProcess(ptySpawned, notify)
|
|
148
|
+
ptym.workers.push({ // track all pty processes
|
|
149
|
+
pid: ptySpawned.pid
|
|
150
|
+
workerGUID: options.workerGUID || undefined
|
|
151
|
+
ptySpawned: ptySpawned
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
ptySpawned.onExit(function(event, local ptydex) {
|
|
155
|
+
ptydex = ptym.workers.findIndex(function(item) { // pty process died
|
|
156
|
+
ptySpawned.pid == item.pid
|
|
157
|
+
})
|
|
158
|
+
if ptydex >= 0
|
|
159
|
+
ptym.workers.splice(ptydex)
|
|
160
|
+
ptypro.killed(event)
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
if options.workerGUID
|
|
164
|
+
WorkerController().watcher.watch(
|
|
165
|
+
function(action, worker, local ptydex) { // worker-owner of pty process died
|
|
166
|
+
if action == "terminated" {
|
|
167
|
+
ptydex = ptym.workers.findIndex(function(item) {
|
|
168
|
+
options.workerGUID == item.workerGUID
|
|
169
|
+
})
|
|
170
|
+
if ptydex >= 0 {
|
|
171
|
+
ptySpawned.kill()
|
|
172
|
+
ptym.workers.splice(ptydex)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
list(false, ptypro)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// finis
|
|
180
|
+
ptym
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
/*
|
|
185
|
+
* ptyProcess
|
|
186
|
+
*
|
|
187
|
+
* A spawned pty process.
|
|
188
|
+
*
|
|
189
|
+
*/
|
|
190
|
+
|
|
191
|
+
closure ptyProcess(ptySpawned, notify, local ptyp) {
|
|
192
|
+
global(js)
|
|
193
|
+
ptyp = new(object, this)
|
|
194
|
+
|
|
195
|
+
// onData
|
|
196
|
+
//
|
|
197
|
+
// Data received.
|
|
198
|
+
//
|
|
199
|
+
ptySpawned.onData(function(data) {
|
|
200
|
+
notify.ptyData(data)
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// killed
|
|
204
|
+
//
|
|
205
|
+
// Process was killed.
|
|
206
|
+
//
|
|
207
|
+
ptyp.killed = function killed(event) {
|
|
208
|
+
notify.ptyKilled(event)
|
|
209
|
+
ptySpawned = false
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// write
|
|
213
|
+
//
|
|
214
|
+
// Write text or a buffer to the spawned process.
|
|
215
|
+
//
|
|
216
|
+
ptyp.write = closure write(textbuf) {
|
|
217
|
+
ptySpawned.write(textbuf)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// resize
|
|
221
|
+
//
|
|
222
|
+
// Tell pty that terminal size changed.
|
|
223
|
+
//
|
|
224
|
+
ptyp.resize = closure resize(cols, rows) {
|
|
225
|
+
ptySpawned.resize(cols, rows)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// clear
|
|
229
|
+
//
|
|
230
|
+
// Tell pty that terminal was cleared.
|
|
231
|
+
//
|
|
232
|
+
ptyp.clear = closure clear() {
|
|
233
|
+
ptySpawned.clear()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// kill
|
|
237
|
+
//
|
|
238
|
+
// Kill the spawned process.
|
|
239
|
+
//
|
|
240
|
+
ptyp.kill = closure kill(signame) {
|
|
241
|
+
ptySpawned.kill(signame)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// finis
|
|
245
|
+
ptyp
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
/*
|
|
250
|
+
* ptyManagerWorker
|
|
251
|
+
*
|
|
252
|
+
* Manager of pty processes on NodeJS worker threads.
|
|
253
|
+
*
|
|
254
|
+
*/
|
|
255
|
+
|
|
256
|
+
closure ptyManagerWorker(local ptyw, mainContext, mainPty) {
|
|
257
|
+
global(PtyManager)
|
|
258
|
+
ptyw = new(object, this)
|
|
259
|
+
|
|
260
|
+
// mainPtyManager
|
|
261
|
+
//
|
|
262
|
+
// Return (a proxy to) the main thread PtyManager.
|
|
263
|
+
//
|
|
264
|
+
closure mainPtyManager(local result, error, main) {
|
|
265
|
+
// evalPtyManager - executed remotely on the main thread
|
|
266
|
+
function evalPtyManager() {
|
|
267
|
+
require("naanlib:frameworks/node/pty.nlg").PtyManager()
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if !mainContext {
|
|
271
|
+
`(error, main) = require("naanlib:frameworks/running/mainthread.nlg").MainThread()
|
|
272
|
+
if !error
|
|
273
|
+
`(error, mainContext) = main.context()
|
|
274
|
+
if error
|
|
275
|
+
return (list(error))
|
|
276
|
+
}
|
|
277
|
+
result = mainContext.evalq(evalPtyManager(), `(evalPtyManager))
|
|
278
|
+
while tuple(result) && !result.0
|
|
279
|
+
result = result.1 // peel off the wrappers
|
|
280
|
+
if !result
|
|
281
|
+
return (list(Error("PtyManager failed")))
|
|
282
|
+
list(false, result)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// open
|
|
286
|
+
//
|
|
287
|
+
// Open the pty manager.
|
|
288
|
+
//
|
|
289
|
+
ptyw.open = closure open(local error) {
|
|
290
|
+
`(error, mainPty) = mainPtyManager()
|
|
291
|
+
if mainPty
|
|
292
|
+
`(error) = mainPty.open()
|
|
293
|
+
if error
|
|
294
|
+
list(error)
|
|
295
|
+
else
|
|
296
|
+
list(false, { ok: true })
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// close
|
|
300
|
+
//
|
|
301
|
+
// Close our pty manager and kill off our spawned processes.
|
|
302
|
+
//
|
|
303
|
+
ptyw.close = closure close() {
|
|
304
|
+
if !mainContext
|
|
305
|
+
return
|
|
306
|
+
|
|
307
|
+
// ### kill our spawned processes
|
|
308
|
+
|
|
309
|
+
mainContext = false
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// spawn
|
|
313
|
+
//
|
|
314
|
+
// Spawn a new pty process, returning a result tuple. If executable is omitted then a shell is
|
|
315
|
+
// spawned.
|
|
316
|
+
//
|
|
317
|
+
ptyw.spawn = closure spawn(executable, args, notify, options) {
|
|
318
|
+
if mainPty
|
|
319
|
+
mainPty.spawn(executable, args, notify, options)
|
|
320
|
+
else
|
|
321
|
+
list(Error("ptyManagerWorker:spawn: not open"))
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// finis
|
|
325
|
+
ptyw
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
/*
|
|
330
|
+
* ptyInit
|
|
331
|
+
*
|
|
332
|
+
* Initialize the node module.
|
|
333
|
+
*
|
|
334
|
+
*
|
|
335
|
+
*/
|
|
336
|
+
|
|
337
|
+
function ptyInit(local manifest) {
|
|
338
|
+
manifest = `(PtyManager, ptyManagerMain, ptyProcess, ptyManagerWorker, ptyInit)
|
|
339
|
+
|
|
340
|
+
Naan.module.build(module.id, "pty", function(modobj, compobj) {
|
|
341
|
+
require("./filesystem.nlg")
|
|
342
|
+
require("./worker.nlg")
|
|
343
|
+
compobj.manifest = manifest
|
|
344
|
+
modobj.exports.PtyManager = PtyManager
|
|
345
|
+
})
|
|
346
|
+
} ();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*
|
|
7
7
|
* column positioning: // // !
|
|
8
8
|
*
|
|
9
|
-
* Copyright (c) 2024 by Richard C. Zulch
|
|
9
|
+
* Copyright (c) 2024-2025 by Richard C. Zulch
|
|
10
10
|
*
|
|
11
11
|
*/
|
|
12
12
|
|
|
@@ -68,9 +68,9 @@ closure ShellConnect(options, local shob, error, fs, xparser, shelly) {
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
if cwd
|
|
71
|
-
xlparams.
|
|
71
|
+
xlparams.spawnops = { cwd: cwd }
|
|
72
72
|
else if options.cwd
|
|
73
|
-
xlparams.
|
|
73
|
+
xlparams.spawnops = { cwd: options.cwd }
|
|
74
74
|
fs.execLines(cmd, xlparams, function(error, result) {
|
|
75
75
|
evalactive(list(`assign, `(compress, "$$"), report)) // assign to $$ in active namespace
|
|
76
76
|
pending.signal(list(error, merge(result, report)))
|
|
@@ -80,7 +80,7 @@ closure ShellConnect(options, local shob, error, fs, xparser, shelly) {
|
|
|
80
80
|
|
|
81
81
|
// shellparser
|
|
82
82
|
// intercept command-line parsing and look for a !shell escape
|
|
83
|
-
function shellparser(source, grammar, operators, local token) {
|
|
83
|
+
function shellparser(source, grammar, operators, local token, text) {
|
|
84
84
|
token = source.tokenread()
|
|
85
85
|
if token.atom == `! {
|
|
86
86
|
source.tokenlast(true) // "consume" the ! token
|