@ai-ecoverse/slicc-jsh 0.2.0 → 0.3.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/dist/host/jsh.mjs CHANGED
@@ -1,885 +1,10 @@
1
1
  #!/usr/bin/env node
2
-
3
- // src/host/jsh.mjs
4
- import { spawn as spawnChild } from "node:child_process";
5
- import { accessSync, constants as fsConstants, readFileSync, writeSync } from "node:fs";
6
- import { createRequire } from "node:module";
7
- import { constants as osConstants } from "node:os";
8
- import { delimiter, join, resolve } from "node:path";
9
- import { fileURLToPath } from "node:url";
10
- import vm from "node:vm";
11
-
12
- // package.json
13
- var package_default = {
14
- name: "@ai-ecoverse/slicc-jsh",
15
- version: "0.2.0",
16
- description: "Node.js-compatible runtime for slicc-kernel: the node and jsh commands (abi: js)",
17
- type: "module",
18
- license: "Apache-2.0",
19
- repository: {
20
- type: "git",
21
- url: "git+https://github.com/ai-ecoverse/slicc-jsh.git"
22
- },
23
- homepage: "https://github.com/ai-ecoverse/slicc-jsh",
24
- bugs: {
25
- url: "https://github.com/ai-ecoverse/slicc-jsh/issues"
26
- },
27
- bin: {
28
- jsh: "dist/host/jsh.mjs"
29
- },
30
- engines: {
31
- node: ">=22",
32
- "slicc-kernel": ">=1.53.2"
33
- },
34
- publishConfig: {
35
- access: "public",
36
- provenance: true,
37
- tag: "latest"
38
- },
39
- files: [
40
- "LICENSE",
41
- "NOTICE",
42
- "README.md",
43
- "dist"
44
- ],
45
- slicc: {
46
- commands: {
47
- node: {
48
- abi: "js",
49
- module: "dist/node.mjs",
50
- argv0: "node"
51
- },
52
- jsh: {
53
- abi: "js",
54
- module: "dist/jsh.mjs",
55
- argv0: "jsh"
56
- }
57
- },
58
- binfmt: {
59
- ".jsh": "jsh"
60
- }
61
- },
62
- scripts: {
63
- build: "node scripts/build.mjs && node scripts/build-host.mjs",
64
- prepack: "npm run build",
65
- prepare: "npm run build",
66
- lint: "node scripts/lint.mjs",
67
- test: "npm run test:golden && npm run test:jsh && npm run test:integration",
68
- "test:golden": "node --max-old-space-size=512 scripts/golden.mjs check",
69
- "test:integration": "node --max-old-space-size=512 --test --test-timeout=120000 test/integration/*.test.mjs",
70
- "test:cert": "node --max-old-space-size=512 test/integration/cert/run.mjs",
71
- "test:node-parallel": "node --max-old-space-size=512 test/node-parallel/run.mjs",
72
- "golden:record": "node --max-old-space-size=512 scripts/golden.mjs record",
73
- "test:jsh": "node --max-old-space-size=512 scripts/jsh-golden.mjs check",
74
- "jsh:record": "node --max-old-space-size=512 scripts/jsh-golden.mjs record"
75
- },
76
- devDependencies: {
77
- "@ai-ecoverse/slicc-kernel": "1.56.2",
78
- "abort-controller": "3.0.0",
79
- acorn: "8.19.0",
80
- esbuild: "0.28.2",
81
- "node-inspect-extracted": "3.3.4",
82
- process: "0.11.10",
83
- "readable-stream": "4.8.0",
84
- string_decoder: "1.3.0",
85
- ws: "8.22.0"
86
- }
87
- };
88
-
89
- // src/jsh/parse-flags.mjs
90
- function parseFlags(argv) {
91
- const positional = [];
92
- const flags = {};
93
- const passthrough = [];
94
- const set = (key, value) => {
95
- if (key in flags) {
96
- const prev = flags[key];
97
- if (Array.isArray(prev)) prev.push(String(value));
98
- else flags[key] = [String(prev), String(value)];
99
- } else {
100
- flags[key] = value;
101
- }
102
- };
103
- let i = 2;
104
- while (i < argv.length) {
105
- const arg = argv[i];
106
- if (arg === "--") {
107
- passthrough.push(...argv.slice(i + 1));
108
- break;
109
- }
110
- if (arg.startsWith("--")) {
111
- const body = arg.slice(2);
112
- const eq = body.indexOf("=");
113
- if (eq !== -1) {
114
- set(body.slice(0, eq), body.slice(eq + 1));
115
- i++;
116
- continue;
117
- }
118
- const next = argv[i + 1];
119
- if (next !== void 0 && !next.startsWith("-")) {
120
- set(body, next);
121
- i += 2;
122
- continue;
123
- }
124
- set(body, true);
125
- i++;
126
- continue;
127
- }
128
- if (arg.startsWith("-") && arg.length > 1) {
129
- for (const ch of arg.slice(1)) set(ch, true);
130
- i++;
131
- continue;
132
- }
133
- positional.push(arg);
134
- i++;
135
- }
136
- const subcommand = positional.length > 0 && /^[a-z][\w-]*$/i.test(positional[0]) ? positional[0] : null;
137
- return { positional, flags, subcommand, passthrough };
138
- }
139
- function attachParseFlags(argv) {
140
- if (Object.prototype.hasOwnProperty.call(argv, "parseFlags")) return argv;
141
- Object.defineProperty(argv, "parseFlags", {
142
- value: () => parseFlags(argv),
143
- enumerable: false,
144
- writable: false,
145
- configurable: true
146
- });
147
- return argv;
148
- }
149
-
150
- // src/jsh/sliccy/exec.mjs
151
- var SIGNALS = { SIGHUP: 1, SIGINT: 2, SIGQUIT: 3, SIGKILL: 9, SIGTERM: 15 };
152
- function killExitCode(sig = "SIGTERM") {
153
- const n = typeof sig === "number" ? sig : SIGNALS[sig] ?? 15;
154
- return 128 + n;
155
- }
156
- function createExec(engine2) {
157
- const shellArgv = (command, args = []) => [...engine2.shell, command, "jsh", ...args];
158
- const run = (argv, stdin) => engine2.spawn(argv, { stdin }).done;
159
- const exec = (command) => {
160
- if (typeof command !== "string") throw new TypeError("exec(command): command must be a string");
161
- return run(shellArgv(command));
162
- };
163
- exec.spawn = (argv) => {
164
- if (!Array.isArray(argv) || argv.length === 0) {
165
- throw new TypeError("exec.spawn(argv): argv must be a non-empty array");
166
- }
167
- return run(argv.map(String));
168
- };
169
- exec.start = (commandOrArgv, opts = {}) => {
170
- const argv = Array.isArray(commandOrArgv) ? [...commandOrArgv.map(String), ...(opts.args ?? []).map(String)] : shellArgv(String(commandOrArgv), (opts.args ?? []).map(String));
171
- const chunks = [];
172
- let child = null;
173
- let killed = false;
174
- let resolveDone;
175
- let rejectDone;
176
- const done = new Promise((resolve2, reject) => {
177
- resolveDone = resolve2;
178
- rejectDone = reject;
179
- });
180
- const fire = () => {
181
- if (child || killed) return;
182
- const stdin = chunks.length > 0 ? chunks.join("") : opts.stdin;
183
- child = engine2.spawn(argv, { stdin });
184
- child.done.then(resolveDone, rejectDone);
185
- };
186
- return {
187
- kill(sig) {
188
- if (child) return Promise.resolve(child.kill(sig ?? "SIGTERM")).then(() => true);
189
- killed = true;
190
- resolveDone({ stdout: "", stderr: "", exitCode: killExitCode(sig) });
191
- return Promise.resolve(true);
192
- },
193
- stdin: {
194
- write(chunk) {
195
- if (!child && !killed) chunks.push(String(chunk));
196
- },
197
- end: () => fire()
198
- },
199
- done
200
- };
201
- };
202
- exec.exec = exec;
203
- return exec;
204
- }
205
- function dirnameOf(path) {
206
- if (!path) return "";
207
- const idx = path.lastIndexOf("/");
208
- if (idx < 0) return "";
209
- if (idx === 0) return "/";
210
- return path.slice(0, idx);
211
- }
212
- function joinChild(dir, name) {
213
- if (dir === "") return name;
214
- if (dir === "/") return `/${name}`;
215
- return `${dir}/${name}`;
216
- }
217
- function createSkill({ argv, fs, exec }) {
218
- const dir = dirnameOf(argv[1] ?? "");
219
- const refs = joinChild(dir, "references");
220
- const assets = joinChild(dir, "assets");
221
- const configPath = joinChild(dir, ".config");
222
- async function readConfig() {
223
- let text;
224
- try {
225
- if (!fs.existsSync(configPath)) return null;
226
- text = await fs.promises.readFile(configPath, "utf8");
227
- } catch {
228
- return null;
229
- }
230
- if (!String(text).trim()) return null;
231
- let parsed;
232
- try {
233
- parsed = JSON.parse(text);
234
- } catch (err) {
235
- throw new Error(`skill.config(): failed to parse ${configPath}: ${err?.message ?? err}`);
236
- }
237
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
238
- throw new Error(`skill.config(): ${configPath} must contain a JSON object`);
239
- }
240
- async function config(updates) {
241
- const existing = await readConfig();
242
- if (updates === void 0) return existing;
243
- if (updates === null || typeof updates !== "object" || Array.isArray(updates)) {
244
- throw new TypeError("skill.config(updates): updates must be a plain object");
245
- }
246
- const merged = { ...existing ?? {}, ...updates };
247
- await fs.promises.writeFile(configPath, `${JSON.stringify(merged, null, 2)}
248
- `);
249
- return merged;
250
- }
251
- async function token(providerId) {
252
- if (typeof providerId !== "string" || !providerId.trim()) {
253
- throw new TypeError("skill.token(providerId): providerId must be a non-empty string");
254
- }
255
- const { stdout, stderr, exitCode } = await exec.spawn(["oauth-token", providerId]);
256
- if (exitCode !== 0) {
257
- const msg = stderr.trim() || `oauth-token exited with code ${exitCode}`;
258
- throw new Error(`skill.token('${providerId}'): ${msg}`);
259
- }
260
- return stdout.replace(/\r?\n+$/, "");
261
- }
262
- return Object.freeze({ dir, refs, assets, config, token });
263
- }
264
-
265
- // src/jsh/sliccy/http.mjs
266
- var HttpError = class extends Error {
267
- constructor(status, statusText, url, body) {
268
- const detail = typeof body === "string" && body ? `: ${body.slice(0, 200)}` : body && typeof body === "object" ? `: ${safeJson(body).slice(0, 200)}` : "";
269
- super(`HTTP ${status} ${statusText} ${url}${detail}`);
270
- this.name = "HttpError";
271
- this.status = status;
272
- this.statusText = statusText;
273
- this.url = url;
274
- this.body = body;
275
- }
276
- };
277
- function safeJson(value) {
278
- try {
279
- return JSON.stringify(value);
280
- } catch {
281
- return String(value);
282
- }
283
- }
284
- var DEFAULT_BACKOFF_BASE_MS = 500;
285
- function buildUrl(baseUrl, path, params) {
286
- let url;
287
- if (/^[a-z][a-z0-9+.-]*:\/\//i.test(path)) url = path;
288
- else if (baseUrl) {
289
- const base = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
290
- url = `${base}${path.startsWith("/") ? path : `/${path}`}`;
291
- } else url = path;
292
- if (!params) return url;
293
- const qs = [];
294
- const push = (k, v) => qs.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
295
- for (const [k, v] of Object.entries(params)) {
296
- if (v === void 0 || v === null) continue;
297
- if (Array.isArray(v)) {
298
- for (const item of v) if (item !== void 0 && item !== null) push(k, item);
299
- } else push(k, v);
300
- }
301
- if (qs.length === 0) return url;
302
- return url + (url.includes("?") ? "&" : "?") + qs.join("&");
303
- }
304
- function isJsonContentType(ct) {
305
- if (!ct) return false;
306
- const lower = ct.toLowerCase();
307
- return lower.startsWith("application/json") || /[+/]json(;|$|\s)/.test(lower);
308
- }
309
- function parseRetryAfter(value) {
310
- const trimmed = value?.trim();
311
- if (!trimmed) return null;
312
- if (/^\d+(\.\d+)?$/.test(trimmed)) {
313
- const seconds = Number(trimmed);
314
- if (Number.isFinite(seconds) && seconds >= 0) return Math.round(seconds * 1e3);
315
- }
316
- const date = Date.parse(trimmed);
317
- if (Number.isFinite(date)) return Math.max(0, date - Date.now());
318
- return null;
319
- }
320
- async function readBody(resp) {
321
- if (isJsonContentType(resp.headers.get("content-type"))) {
322
- const text = await resp.text();
323
- if (!text) return null;
324
- try {
325
- return JSON.parse(text);
326
- } catch {
327
- return text;
328
- }
329
- }
330
- return resp.text();
331
- }
332
- function serializeBody(body, headers) {
333
- if (body === void 0 || body === null) return void 0;
334
- if (typeof body === "string") return body;
335
- if (body instanceof ArrayBuffer || ArrayBuffer.isView(body) || typeof Blob !== "undefined" && body instanceof Blob || typeof FormData !== "undefined" && body instanceof FormData || typeof URLSearchParams !== "undefined" && body instanceof URLSearchParams || typeof ReadableStream !== "undefined" && body instanceof ReadableStream) {
336
- return body;
337
- }
338
- if (typeof body === "object") {
339
- if (!Object.keys(headers).some((k) => k.toLowerCase() === "content-type")) {
340
- headers["Content-Type"] = "application/json";
341
- }
342
- return JSON.stringify(body);
343
- }
344
- return String(body);
345
- }
346
- function createHttp({ fetch, sleep = (ms) => new Promise((r) => setTimeout(r, ms)) }) {
347
- function makeClient(config = {}) {
348
- const retryOn = new Set(config.retry?.on ?? []);
349
- const maxAttempts = Math.max(1, Math.trunc(config.retry?.maxAttempts ?? 1));
350
- async function request(method, path, opts = {}) {
351
- if (typeof fetch !== "function") throw new Error("sliccy:http: fetch is not available");
352
- const url = buildUrl(config.baseUrl, path, opts.params);
353
- const headers = { ...config.headers ?? {}, ...opts.headers ?? {} };
354
- if (config.token) {
355
- const tok = await config.token({ method, path, url });
356
- if (tok && !Object.keys(headers).some((k) => k.toLowerCase() === "authorization")) {
357
- headers.Authorization = `Bearer ${tok}`;
358
- }
359
- }
360
- const body = serializeBody(opts.body, headers);
361
- const init = { method, headers };
362
- if (body !== void 0) init.body = body;
363
- for (let attempt = 0; ; attempt++) {
364
- const timeoutCtl = config.timeoutMs ? new AbortController() : null;
365
- const timeoutId = timeoutCtl ? setTimeout(() => timeoutCtl.abort(new Error("timeout")), config.timeoutMs) : null;
366
- const signal = opts.signal && timeoutCtl ? AbortSignal.any([opts.signal, timeoutCtl.signal]) : opts.signal ?? timeoutCtl?.signal;
367
- let resp;
368
- try {
369
- resp = await fetch(url, signal ? { ...init, signal } : init);
370
- } finally {
371
- if (timeoutId !== null) clearTimeout(timeoutId);
372
- }
373
- if (resp.ok) {
374
- const respBody = await readBody(resp);
375
- if (opts.raw) {
376
- return {
377
- body: respBody,
378
- headers: Object.fromEntries(resp.headers.entries()),
379
- status: resp.status
380
- };
381
- }
382
- return respBody;
383
- }
384
- if (attempt + 1 >= maxAttempts || !retryOn.has(resp.status)) {
385
- const errBody = await readBody(resp).catch(() => null);
386
- throw new HttpError(resp.status, resp.statusText, url, errBody);
387
- }
388
- const retryAfter = parseRetryAfter(resp.headers.get("retry-after"));
389
- await sleep(retryAfter ?? DEFAULT_BACKOFF_BASE_MS * 2 ** attempt);
390
- }
391
- }
392
- return Object.freeze({
393
- get: (path, opts) => request("GET", path, opts),
394
- post: (path, opts) => request("POST", path, opts),
395
- put: (path, opts) => request("PUT", path, opts),
396
- patch: (path, opts) => request("PATCH", path, opts),
397
- delete: (path, opts) => request("DELETE", path, opts)
398
- });
399
- }
400
- return Object.freeze({ client: makeClient, HttpError });
401
- }
402
-
403
- // src/jsh/sliccy/pure.mjs
404
- var ANSI = {
405
- reset: "\x1B[0m",
406
- green: "\x1B[32m",
407
- red: "\x1B[31m",
408
- yellow: "\x1B[33m",
409
- gray: "\x1B[90m",
410
- bold: "\x1B[1m",
411
- cyan: "\x1B[36m",
412
- dim: "\x1B[2m"
413
- };
414
- function colorEnabled(env, stdoutIsTTY) {
415
- const force = env?.FORCE_COLOR;
416
- if (force !== void 0) return force !== "" && force !== "0" && force !== "false";
417
- if (env?.NO_COLOR) return false;
418
- return !!stdoutIsTTY;
419
- }
420
- function createColor(enabled) {
421
- const wrap = (code) => (s) => enabled ? `${code}${s}${ANSI.reset}` : String(s);
422
- return Object.freeze({
423
- enabled,
424
- green: wrap(ANSI.green),
425
- red: wrap(ANSI.red),
426
- yellow: wrap(ANSI.yellow),
427
- gray: wrap(ANSI.gray),
428
- bold: wrap(ANSI.bold),
429
- cyan: wrap(ANSI.cyan),
430
- dim: wrap(ANSI.dim)
431
- });
432
- }
433
- function createCli({ writeStdout, writeStderr, exit, color }) {
434
- const toLine = (v) => {
435
- if (typeof v === "string") return v;
436
- if (v instanceof Error) return v.message;
437
- if (v === null || v === void 0) return String(v);
438
- try {
439
- return JSON.stringify(v);
440
- } catch {
441
- return String(v);
442
- }
443
- };
444
- const prefixed = (paint, prefix, text, fallback) => {
445
- if (prefix === void 0) return `${paint(`${fallback}:`)} ${text}
446
- `;
447
- if (prefix === "") return `${paint(text)}
448
- `;
449
- return `${paint(`${prefix}:`)} ${text}
450
- `;
451
- };
452
- const prefixOf = (opts) => typeof opts === "object" && opts !== null && "prefix" in opts ? opts.prefix : void 0;
453
- return Object.freeze({
454
- die(msg, opts) {
455
- const code = typeof opts === "number" ? opts : opts?.exitCode ?? 1;
456
- writeStderr(prefixed(color.red, prefixOf(opts), toLine(msg), "Error"));
457
- exit(code);
458
- throw new Error("unreachable");
459
- },
460
- out(value) {
461
- if (typeof value === "string") {
462
- writeStdout(value.endsWith("\n") ? value : `${value}
463
- `);
464
- return;
465
- }
466
- try {
467
- writeStdout(`${JSON.stringify(value, null, 2)}
468
- `);
469
- } catch {
470
- writeStdout(`${String(value)}
471
- `);
472
- }
473
- },
474
- warn(msg, opts) {
475
- writeStderr(prefixed(color.yellow, prefixOf(opts), toLine(msg), "Warning"));
476
- },
477
- help(text) {
478
- writeStdout(text.endsWith("\n") ? text : `${text}
479
- `);
480
- exit(0);
481
- throw new Error("unreachable");
482
- }
483
- });
484
- }
485
- var ANSI_RE = /\u001b\[[0-9;]*m/g;
486
- var stripAnsi = (s) => s.replace(ANSI_RE, "");
487
- var visibleLength = (s) => stripAnsi(s).length;
488
- var pad2 = (n) => n < 10 ? `0${n}` : String(n);
489
- function trunc(s, n) {
490
- const text = String(s ?? "");
491
- if (n <= 0) return "";
492
- if (visibleLength(text) <= n) return text;
493
- if (n <= 1) return text.slice(0, n);
494
- return `${stripAnsi(text).slice(0, n - 1)}\u2026`;
495
- }
496
- function col(s, width) {
497
- const text = String(s ?? "");
498
- const vis = visibleLength(text);
499
- if (vis === width) return text;
500
- if (vis > width) return trunc(text, width);
501
- return text + " ".repeat(width - vis);
502
- }
503
- function table(rows, widths) {
504
- if (!rows.length) return "";
505
- const colCount = Math.max(...rows.map((r) => r.length));
506
- const computed = [];
507
- for (let i = 0; i < colCount; i++) {
508
- if (widths && widths[i] !== void 0) {
509
- computed[i] = widths[i];
510
- } else {
511
- let max = 0;
512
- for (const r of rows) max = Math.max(max, visibleLength(i < r.length ? String(r[i] ?? "") : ""));
513
- computed[i] = max;
514
- }
515
- }
516
- return rows.map(
517
- (r) => computed.map((w, i) => col(i < r.length ? String(r[i] ?? "") : "", w)).join(" ").replace(/\s+$/, "")
518
- ).join("\n");
519
- }
520
- function fmtDate(value, style = "short") {
521
- const d = value instanceof Date ? value : new Date(value);
522
- if (Number.isNaN(d.getTime())) return String(value);
523
- if (style === "iso") return d.toISOString();
524
- if (style === "locale") return new Intl.DateTimeFormat(void 0, { dateStyle: "medium" }).format(d);
525
- if (style === "human") {
526
- const diff = Date.now() - d.getTime();
527
- const abs = Math.abs(diff);
528
- if (abs < 6e4) return diff >= 0 ? "just now" : "in a moment";
529
- const tense = (n, u) => diff >= 0 ? `${n} ${u}${n === 1 ? "" : "s"} ago` : `in ${n} ${u}${n === 1 ? "" : "s"}`;
530
- if (abs < 36e5) return tense(Math.round(abs / 6e4), "minute");
531
- if (abs < 864e5) return tense(Math.round(abs / 36e5), "hour");
532
- if (abs < 26298e5) return tense(Math.round(abs / 864e5), "day");
533
- if (abs < 315576e5) return tense(Math.round(abs / 26298e5), "month");
534
- return tense(Math.round(abs / 315576e5), "year");
535
- }
536
- return `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
537
- }
538
- var fmt = Object.freeze({ trunc, col, table, date: fmtDate });
539
- var DURATION_UNITS_MS = {
540
- ms: 1,
541
- s: 1e3,
542
- m: 6e4,
543
- h: 36e5,
544
- d: 864e5,
545
- w: 6048e5,
546
- M: 26298e5,
547
- y: 315576e5
548
- };
549
- function parseDuration(spec) {
550
- if (typeof spec === "number" && Number.isFinite(spec)) return Math.trunc(spec);
551
- if (typeof spec !== "string") throw new TypeError("time.parseDuration: spec must be string or number");
552
- const m = /^([0-9]+(?:\.[0-9]+)?)\s*(ms|s|m|h|d|w|M|y)?$/.exec(spec.trim());
553
- if (!m) throw new RangeError(`time.parseDuration: unrecognized spec "${spec}"`);
554
- return Math.trunc(Number(m[1]) * DURATION_UNITS_MS[m[2] ?? "ms"]);
555
- }
556
- var time = Object.freeze({
557
- parseDuration,
558
- ago(spec, from = /* @__PURE__ */ new Date()) {
559
- return new Date(from.getTime() - parseDuration(spec));
560
- },
561
- range(spec, from = /* @__PURE__ */ new Date()) {
562
- const end = new Date(from.getTime());
563
- return { start: new Date(end.getTime() - parseDuration(spec)), end };
564
- },
565
- future(spec, from = /* @__PURE__ */ new Date()) {
566
- const start = new Date(from.getTime());
567
- return { start, end: new Date(start.getTime() + parseDuration(spec)) };
568
- },
569
- gmailDate(spec, from = /* @__PURE__ */ new Date()) {
570
- const d = new Date(from.getTime() - parseDuration(spec));
571
- return `${d.getFullYear()}/${pad2(d.getMonth() + 1)}/${pad2(d.getDate())}`;
572
- }
573
- });
574
- async function pool(concurrency, items, fn) {
575
- const n = Math.max(1, Math.trunc(concurrency) || 1);
576
- const results = new Array(items.length);
577
- let next = 0;
578
- const worker = async () => {
579
- for (; ; ) {
580
- const i = next++;
581
- if (i >= items.length) return;
582
- results[i] = await fn(items[i], i);
583
- }
584
- };
585
- const workers = [];
586
- for (let w = 0; w < Math.min(n, items.length); w++) workers.push(worker());
587
- await Promise.all(workers);
588
- return results;
589
- }
590
-
591
- // src/jsh/sliccy/index.mjs
592
- var SLICC_ONLY = /* @__PURE__ */ new Set(["agent", "usb", "serial", "hid", "computer"]);
593
- function isNotFound(err, request) {
594
- return err?.code === "MODULE_NOT_FOUND" && String(err.message).includes(`'${request}'`);
595
- }
596
- function createSliccyResolver({ engine: engine2, process: process2, require: require2 }) {
597
- let color;
598
- let exec;
599
- const getColor = () => {
600
- color ??= createColor(colorEnabled(process2.env, engine2.stdoutIsTTY));
601
- return color;
602
- };
603
- const getExec = () => {
604
- exec ??= createExec(engine2);
605
- return exec;
606
- };
607
- const factories = {
608
- cli: () => createCli({
609
- writeStdout: engine2.writeStdout,
610
- writeStderr: engine2.writeStderr,
611
- exit: (code) => process2.exit(code),
612
- color: getColor()
613
- }),
614
- color: getColor,
615
- exec: getExec,
616
- fmt: () => fmt,
617
- http: () => createHttp({ fetch: engine2.fetch }),
618
- pool: () => pool,
619
- skill: () => createSkill({ argv: process2.argv, fs: require2("fs"), exec: getExec() }),
620
- time: () => time
621
- };
622
- const cache = /* @__PURE__ */ new Map();
623
- const lookups = [require2, engine2.selfRequire].filter(Boolean);
624
- return function requireSliccy(id) {
625
- const name = id.slice("sliccy:".length);
626
- if (name === "") throw new Error("require('sliccy:'): empty sliccy: module name");
627
- if (cache.has(name)) return cache.get(name);
628
- let value;
629
- if (Object.prototype.hasOwnProperty.call(factories, name)) {
630
- value = factories[name]();
631
- } else {
632
- value = findPackage(name, lookups);
633
- }
634
- if (value === void 0) {
635
- if (SLICC_ONLY.has(name)) {
636
- throw new Error(`require('${id}'): sliccy:${name} is only available inside SLICC`);
637
- }
638
- const known = Object.keys(factories).sort().join(", ");
639
- throw new Error(
640
- `require('${id}'): unknown sliccy: module '${name}'. Known names: ${known}. Install @ai-ecoverse/sliccy-${name} to provide it.`
641
- );
642
- }
643
- cache.set(name, value);
644
- return value;
645
- };
646
- }
647
- function findPackage(name, lookups) {
648
- for (const pkg of [`@ai-ecoverse/sliccy-${name}`, `sliccy-${name}`]) {
649
- for (const req of lookups) {
650
- try {
651
- return req(pkg);
652
- } catch (err) {
653
- if (!isNotFound(err, pkg)) throw err;
654
- }
655
- }
656
- }
657
- return void 0;
658
- }
659
-
660
- // src/jsh/run.mjs
661
- var JSH_VERSION = package_default.version;
662
- var JSH_PARAMS = Object.freeze([
663
- "process",
664
- "console",
665
- "require",
666
- "module",
667
- "exports",
668
- "fetch",
669
- "__dirname",
670
- "__filename"
671
- ]);
672
- var JSH_USAGE = `Usage: jsh [--version | --help] <script.jsh> [args...]
673
-
674
- Runs a .jsh script: the body of an async function with process, console,
675
- require, module, exports, fetch, __dirname and __filename in scope.
676
- `;
677
- function parseJshArgv(args) {
678
- let i = 0;
679
- const first = args[0];
680
- if (first === "--version" || first === "-v") return { action: "version" };
681
- if (first === "--help" || first === "-h") return { action: "help" };
682
- if (first === "--") i = 1;
683
- else if (first?.startsWith("-") && first !== "-") {
684
- return { action: "error", message: `jsh: bad option: ${first}`, status: 9 };
685
- }
686
- if (i >= args.length) return { action: "usage" };
687
- return { action: "run", script: args[i], args: args.slice(i + 1) };
688
- }
689
- function prepareSource(source) {
690
- let text = source.charCodeAt(0) === 65279 ? source.slice(1) : source;
691
- if (text.startsWith("#!")) {
692
- const nl = text.indexOf("\n");
693
- text = nl === -1 ? "" : text.slice(nl);
694
- }
695
- return text;
696
- }
697
- function wrapSource(body) {
698
- return `(async function (${JSH_PARAMS.join(", ")}) {${body}
699
- })`;
700
- }
701
- function dirnameOf2(path) {
702
- const idx = path.lastIndexOf("/");
703
- if (idx <= 0) return "/";
704
- return path.slice(0, idx);
705
- }
706
- function wrapRequire(baseRequire, requireSliccy, module) {
707
- const require2 = (id) => typeof id === "string" && id.startsWith("sliccy:") ? requireSliccy(id) : baseRequire(id);
708
- const resolve2 = (id, options) => typeof id === "string" && id.startsWith("sliccy:") ? id : baseRequire.resolve(id, options);
709
- resolve2.paths = (id) => baseRequire.resolve.paths?.(id) ?? null;
710
- require2.resolve = resolve2;
711
- require2.cache = baseRequire.cache;
712
- require2.main = module;
713
- return require2;
714
- }
715
- async function runJsh({ filename, engine: engine2, process: process2, console: console2 }) {
716
- let source;
717
- try {
718
- source = engine2.readSource(filename);
719
- } catch (err) {
720
- engine2.writeStderr(`jsh: cannot open ${filename}: ${err?.code ?? err?.message ?? err}
721
- `);
722
- return engine2.exit(1);
723
- }
724
- attachParseFlags(process2.argv);
725
- try {
726
- process2.versions.jsh = JSH_VERSION;
727
- } catch {
728
- }
729
- const dirname = dirnameOf2(filename);
730
- const module = {
731
- id: ".",
732
- filename,
733
- path: dirname,
734
- exports: {},
735
- loaded: false,
736
- children: [],
737
- paths: []
738
- };
739
- const baseRequire = engine2.createRequire(filename);
740
- const requireSliccy = createSliccyResolver({ engine: engine2, process: process2, require: baseRequire });
741
- const require2 = wrapRequire(baseRequire, requireSliccy, module);
742
- try {
743
- const fn = engine2.compile(prepareSource(source), filename);
744
- await fn(process2, console2, require2, module, module.exports, engine2.fetch, dirname, filename);
745
- module.loaded = true;
746
- } catch (err) {
747
- if (engine2.isExit(err)) return engine2.exit(err.code);
748
- engine2.writeStderr(`${err?.stack ?? err}
749
- `);
750
- return engine2.exit(1);
751
- }
752
- return void 0;
753
- }
754
-
755
- // src/host/jsh.mjs
756
- var writeFd = (fd, text) => {
757
- const bytes = Buffer.from(String(text));
758
- let off = 0;
759
- while (off < bytes.length) {
760
- try {
761
- off += writeSync(fd, bytes, off);
762
- } catch (err) {
763
- if (err?.code === "EAGAIN") continue;
764
- throw err;
765
- }
766
- }
767
- };
768
- function findOnPath(name) {
769
- for (const dir of (process.env.PATH ?? "").split(delimiter)) {
770
- if (!dir) continue;
771
- const candidate = join(dir, name);
772
- try {
773
- accessSync(candidate, fsConstants.X_OK);
774
- return candidate;
775
- } catch {
776
- }
777
- }
778
- return null;
779
- }
780
- function silenceLoaderWarning() {
781
- const emitWarning = process.emitWarning;
782
- process.emitWarning = function filtered(warning, ...rest) {
783
- const text = typeof warning === "string" ? warning : warning?.message;
784
- if (typeof text === "string" && text.includes("USE_MAIN_CONTEXT_DEFAULT_LOADER")) return;
785
- return emitWarning.call(this, warning, ...rest);
786
- };
787
- }
788
- function spawn(argv, { stdin } = {}) {
789
- let child;
790
- const done = new Promise((resolveDone) => {
791
- let settled = false;
792
- const finish = (result) => {
793
- if (!settled) {
794
- settled = true;
795
- resolveDone(result);
796
- }
797
- };
798
- const out = [];
799
- const err = [];
800
- child = spawnChild(argv[0], argv.slice(1), {
801
- cwd: process.cwd(),
802
- env: process.env,
803
- stdio: [stdin === void 0 ? "ignore" : "pipe", "pipe", "pipe"]
804
- });
805
- child.stdout.on("data", (c) => out.push(c));
806
- child.stderr.on("data", (c) => err.push(c));
807
- child.on("error", (e) => {
808
- if (e?.code === "ENOENT") {
809
- finish({ stdout: "", stderr: `${argv[0]}: command not found
810
- `, exitCode: 127 });
811
- } else {
812
- finish({ stdout: "", stderr: `${argv[0]}: ${e?.message ?? e}
813
- `, exitCode: 126 });
814
- }
815
- });
816
- child.on("close", (code, signal) => {
817
- finish({
818
- stdout: Buffer.concat(out).toString("utf8"),
819
- stderr: Buffer.concat(err).toString("utf8"),
820
- exitCode: code ?? 128 + (osConstants.signals[signal] ?? 0)
821
- });
822
- });
823
- if (stdin !== void 0) {
824
- child.stdin.on("error", () => {
825
- });
826
- child.stdin.end(stdin);
827
- }
828
- });
829
- return { kill: (sig = "SIGTERM") => child.kill(sig), done };
830
- }
831
- var selfRequire = null;
832
- try {
833
- selfRequire = createRequire(fileURLToPath(import.meta.url));
834
- } catch {
835
- selfRequire = null;
836
- }
837
- var engine = {
838
- readSource: (filename) => readFileSync(filename, "utf8"),
839
- compile: (body, filename) => vm.runInThisContext(wrapSource(body), {
840
- filename,
841
- importModuleDynamically: vm.constants.USE_MAIN_CONTEXT_DEFAULT_LOADER
842
- }),
843
- createRequire: (filename) => createRequire(filename),
844
- selfRequire,
845
- writeStdout: (s) => writeFd(1, s),
846
- writeStderr: (s) => writeFd(2, s),
847
- get stdoutIsTTY() {
848
- return !!process.stdout.isTTY;
849
- },
850
- spawn,
851
- shell: [findOnPath("bash") ?? "sh", "-c"],
852
- fetch: globalThis.fetch,
853
- isExit: () => false,
854
- exit: (code) => process.exit(code)
855
- };
856
- async function main() {
857
- const parsed = parseJshArgv(process.argv.slice(2));
858
- if (parsed.action === "version") {
859
- writeFd(1, `${JSH_VERSION}
860
- `);
861
- return;
862
- }
863
- if (parsed.action === "help") {
864
- writeFd(1, JSH_USAGE);
865
- return;
866
- }
867
- if (parsed.action === "usage") {
868
- writeFd(2, JSH_USAGE);
869
- process.exit(2);
870
- }
871
- if (parsed.action === "error") {
872
- writeFd(2, `${parsed.message}
873
- ${JSH_USAGE}`);
874
- process.exit(parsed.status);
875
- }
876
- const filename = resolve(parsed.script);
877
- process.argv = [process.execPath, filename, ...parsed.args];
878
- silenceLoaderWarning();
879
- await runJsh({ filename, engine, process, console });
880
- }
881
- main().catch((err) => {
882
- writeFd(2, `${err?.stack ?? err}
883
- `);
884
- process.exit(1);
885
- });
2
+ // The package's `bin` (dist/host/jsh.mjs). No static imports: inside
3
+ // slicc-kernel this file can end up running under jsh's own `node` runtime
4
+ // (a global pnpm/npm install puts this bin on PATH ahead of the kernel's
5
+ // `jsh` command), where the host CLI's Node builtins are not available. That
6
+ // runtime advertises the kernel jsh under this symbol (src/jsh/kernel.mjs);
7
+ // nothing else sets it, so a real host always gets the host CLI.
8
+ const kernel = globalThis[Symbol.for('slicc-jsh.kernel')];
9
+ if (kernel) await kernel.runJsh(process.argv.slice(2));
10
+ else await import('./main.mjs');