@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.
Files changed (61) hide show
  1. package/LICENSE.md +2 -2
  2. package/README.md +4 -4
  3. package/bin/index.js +25 -3
  4. package/dist/env_web.js +164 -22
  5. package/dist/naan.min.js +5 -5
  6. package/frameworks/browser/browser.nlg +2 -2
  7. package/frameworks/browser/https_request.nlg +2 -2
  8. package/frameworks/browser/sworker.js +28 -26
  9. package/frameworks/browser/terminals.nlg +342 -700
  10. package/frameworks/browser/worker.nlg +255 -0
  11. package/frameworks/browser/workers.nlg +5 -5
  12. package/frameworks/browser/ws_client.nlg +15 -10
  13. package/frameworks/client/apiclient.nlg +204 -19
  14. package/frameworks/client/client.nlg +22 -2
  15. package/frameworks/client/psm_client.nlg +12 -3
  16. package/frameworks/client/relaycon.nlg +87 -161
  17. package/frameworks/common/events.nlg +101 -0
  18. package/frameworks/common/repltools.nlg +80 -1
  19. package/frameworks/node/apiserver.nlg +39 -246
  20. package/frameworks/node/filesystem.nlg +39 -12
  21. package/frameworks/node/gitter.nlg +9 -7
  22. package/frameworks/node/http_request.nlg +34 -13
  23. package/frameworks/node/https_request.nlg +8 -3
  24. package/frameworks/node/node.nlg +2 -0
  25. package/frameworks/node/pty.nlg +346 -0
  26. package/frameworks/node/shell.nlg +4 -4
  27. package/frameworks/node/worker.nlg +409 -539
  28. package/frameworks/node/ws_client.nlg +2 -2
  29. package/frameworks/project/build.nlg +1 -1
  30. package/frameworks/project/projects.nlg +2 -1
  31. package/frameworks/running/debugnub.nlg +17 -4
  32. package/frameworks/running/executors.nlg +27 -274
  33. package/frameworks/running/instances.nlg +393 -0
  34. package/frameworks/running/remote_req.nlg +143 -0
  35. package/frameworks/running/remote_res.nlg +89 -0
  36. package/frameworks/running/taskexec.nlg +2 -2
  37. package/frameworks/service/imp.nlg +736 -0
  38. package/frameworks/service/model.nlg +71 -0
  39. package/frameworks/service/nubnode.nlg +105 -0
  40. package/frameworks/service/nubweb.nlg +106 -0
  41. package/frameworks/service/service.nlg +28 -0
  42. package/frameworks/storage/dbt_pouch.nlg +26 -13
  43. package/frameworks/storage/psm.nlg +39 -5
  44. package/frameworks/storage/psm_dbtables.nlg +10 -2
  45. package/lib/browser/env_web.js +171 -29
  46. package/lib/browser/env_webworker.js +91 -5
  47. package/lib/core/naanlib.js +5 -5
  48. package/lib/env_node.js +184 -8
  49. package/lib/env_nodeworker.js +74 -1
  50. package/package.json +1 -1
  51. package/plugins/serviceAws/aws_cloudwatchlogs.nlg +2 -1
  52. package/plugins/serviceAws/aws_dynamo.nlg +1 -0
  53. package/plugins/serviceAws/aws_s3.nlg +3 -3
  54. package/plugins/serviceAws/aws_sqs.nlg +3 -1
  55. package/plugins/serviceAws/aws_utils.nlg +3 -1
  56. package/plugins/serviceAws/dbt_aws.nlg +22 -20
  57. package/plugins/serviceAws/psm_aws.nlg +2 -1
  58. package/plugins/serviceNexara/speechrec.nlg +10 -6
  59. package/test/test_02_context.nlg +24 -1
  60. package/test/test_03_jsinterop.nlg +32 -3
  61. 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.execops = { cwd: cwd }
71
+ xlparams.spawnops = { cwd: cwd }
72
72
  else if options.cwd
73
- xlparams.execops = { cwd: options.cwd }
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