@ai-ecoverse/slicc-kernel 1.108.5 → 1.110.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.
@@ -17,6 +17,11 @@ export interface Command {
17
17
  imports?: string;
18
18
  refused?: string;
19
19
  shows?: string;
20
+ variants?: CommandVariant[];
21
+ }
22
+ export interface CommandVariant {
23
+ env: Record<string, string>;
24
+ wasm: string;
20
25
  }
21
26
  interface Manifest {
22
27
  name?: unknown;
@@ -32,6 +37,7 @@ interface Manifest {
32
37
  replaces?: unknown;
33
38
  };
34
39
  }
40
+ export declare function variantFor(variants: readonly CommandVariant[] | undefined, env: Record<string, string>): string | undefined;
35
41
  export declare function commandsOf(pkg: string, manifest: Manifest): Command[];
36
42
  export declare function pnpmGlobalRoots(fs: KernelFs, home: string): Promise<string[]>;
37
43
  export declare function engineRefusal(manifest: Manifest, kernel: string): string | undefined;
@@ -0,0 +1,4 @@
1
+ import type { Command } from '../commands.js';
2
+ export declare const CRONTAB_PATH = "/usr/bin/crontab";
3
+ export declare const CRONTAB_COMMAND: Command;
4
+ export declare const CRONTAB_SOURCE = "export default async function crontab(ctx) {\n const say = (fd, text) => ctx.write(fd, text).catch(() => undefined);\n const usage = async () => {\n await say(2, \"usage: crontab [-u user] { -l | -r | -e | file | - }\\n\");\n return 1;\n };\n const args = ctx.argv.slice(1);\n let user;\n let action;\n let file;\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"-u\") {\n user = args[++i];\n if (user === undefined) return usage();\n } else if (arg === \"-l\" || arg === \"-r\" || arg === \"-e\") {\n if (action !== undefined) return usage();\n action = arg.slice(1);\n } else if (arg === \"-\" || !arg.startsWith(\"-\")) {\n if (file !== undefined) return usage();\n file = arg;\n } else return usage();\n }\n if ((action === undefined) === (file === undefined)) return usage();\n const call = (c) => ctx.crontab(user === undefined ? c : { ...c, user });\n const failed = async (answer) => {\n await say(2, answer.error + \"\\n\");\n return 1;\n };\n const decode = (bytes) => new TextDecoder().decode(bytes);\n if (action === \"l\") {\n const answer = await call({ action: \"list\" });\n if (answer.error !== undefined) return failed(answer);\n await say(1, answer.text);\n return 0;\n }\n if (action === \"r\") {\n const answer = await call({ action: \"remove\" });\n return answer.error !== undefined ? failed(answer) : 0;\n }\n if (action === \"e\") return edit();\n let text;\n try {\n if (file === \"-\") {\n const stream = new TextDecoder();\n const parts = [];\n for (;;) {\n const chunk = await ctx.read(0);\n if (chunk.length === 0) break;\n parts.push(stream.decode(chunk, { stream: true }));\n }\n text = parts.join(\"\") + stream.decode();\n } else text = decode(await ctx.fs.readFile(ctx.resolve(file)));\n } catch (err) {\n await say(2, \"crontab: \" + file + \": \" + (err && err.code ? err.code : String(err)) + \"\\n\");\n return 1;\n }\n const answer = await call({ action: \"install\", text });\n return answer.error !== undefined ? failed(answer) : 0;\n\n async function edit() {\n const editor = ctx.env.VISUAL || ctx.env.EDITOR;\n if (!editor) {\n await say(2, \"crontab: set VISUAL or EDITOR to edit, or install a file with crontab <file>\\n\");\n return 1;\n }\n const current = await call({ action: \"list\" });\n const before = current.error === undefined ? current.text : \"\";\n if (current.error !== undefined && !current.error.startsWith(\"no crontab for \")) return failed(current);\n const tmp = \"/tmp/crontab.\" + ctx.pid + \".\" + crypto.randomUUID();\n try {\n const file = await ctx.open(tmp, { write: true, create: true, exclusive: true, mode: 0o600 });\n await file.write(0, new TextEncoder().encode(before));\n await file.close();\n } catch (err) {\n await say(2, \"crontab: \" + tmp + \": \" + (err && err.code ? err.code : String(err)) + \"\\n\");\n return 1;\n }\n const child = await ctx.spawn({ argv: [\"/bin/bash\", \"-c\", editor + ' \"$1\"', \"crontab\", tmp] });\n const ended = await ctx.wait(child.pid);\n if (ended.status !== 0) {\n await say(2, \"crontab: the editor exited with status \" + ended.status + \", crontab not changed\\n\");\n await ctx.fs.rm(tmp).catch(() => undefined);\n return 1;\n }\n const after = decode(await ctx.fs.readFile(tmp));\n if (after === before) {\n await say(2, \"crontab: no changes made to crontab\\n\");\n await ctx.fs.rm(tmp).catch(() => undefined);\n return 0;\n }\n const answer = await call({ action: \"install\", text: after });\n if (answer.error !== undefined) {\n await say(2, answer.error + \"\\ncrontab: your edit is kept in \" + tmp + \"\\n\");\n return 1;\n }\n await ctx.fs.rm(tmp).catch(() => undefined);\n return 0;\n }\n}\n";
@@ -0,0 +1,27 @@
1
+ import type { KernelFs } from '../fs/types.js';
2
+ import type { Cred } from '../kernel/cred.js';
3
+ import type { Account } from '../users.js';
4
+ export declare const CRONTAB_MAX: number;
5
+ export interface CrontabCall {
6
+ op: 'proc-crontab';
7
+ action: 'list' | 'remove' | 'install';
8
+ user?: string;
9
+ text?: string;
10
+ }
11
+ export type CrontabResult = {
12
+ text: string;
13
+ } | {
14
+ error: string;
15
+ };
16
+ export interface CrontabsOptions {
17
+ fs: KernelFs;
18
+ owners: boolean;
19
+ lookup(user: string | number): Promise<Account | undefined>;
20
+ }
21
+ export declare function crontabCall(req: Partial<CrontabCall>): CrontabCall | undefined;
22
+ export declare class Crontabs {
23
+ private readonly o;
24
+ constructor(options: CrontabsOptions);
25
+ request(cred: Readonly<Cred>, call: CrontabCall): Promise<CrontabResult>;
26
+ private install;
27
+ }
@@ -34,7 +34,7 @@ export { serveHostfsPort } from './mount/hostfs.js';
34
34
  export type { MountEntry, MountSpec } from './mount/mount-fs.js';
35
35
  export type { ProcessMountPolicy, ProcessMountRequest } from './mount/syscall.js';
36
36
  export type { JsChild, JsExited, JsSpawnOptions, JsStdio } from './process/js/js-children.js';
37
- export type { JsFdStatus, JsFdType, JsFile, JsOpenOptions, JsProgramContext, JsSignal, JsSudoAnswer, } from './process/js/js-context.js';
37
+ export type { JsCrontabAnswer, JsCrontabCall, JsFdStatus, JsFdType, JsFile, JsOpenOptions, JsProgramContext, JsSignal, JsSudoAnswer, } from './process/js/js-context.js';
38
38
  export type { JsStatFs } from './process/js/js-io.js';
39
39
  export type { JsAddress, JsConnection, JsConnectOptions, JsListener, JsListenOptions, JsNet, } from './process/js/js-net.js';
40
40
  export type { JsRunOptions, JsRunResult, JsRunStdio } from './process/js/js-run.js';
@@ -67,6 +67,7 @@ export interface SpawnWasmOptions {
67
67
  writesBack?: (pid: number) => boolean;
68
68
  processes?: () => ProcessListing;
69
69
  sudo?: WasmProcessOptions['sudo'];
70
+ crontab?: WasmProcessOptions['crontab'];
70
71
  openFiles?: Set<VfsNodes>;
71
72
  nodes?: VfsNodes;
72
73
  statfs?: (path: string) => Promise<{
@@ -1,3 +1,4 @@
1
+ import { type CrontabCall, type CrontabResult } from '../cron/crontab.js';
1
2
  import { type Created, type Dac } from '../fs/dac.js';
2
3
  import type { KernelFs } from '../fs/types.js';
3
4
  import type { MountCall } from '../mount/syscall.js';
@@ -255,7 +256,7 @@ export type WasmSyscall = {
255
256
  op: 'proc-captured';
256
257
  pid: number;
257
258
  slot: number;
258
- } | SudoCall | RunRequest | {
259
+ } | SudoCall | CrontabCall | RunRequest | {
259
260
  op: 'proc-exit';
260
261
  status: number;
261
262
  } | {
@@ -368,6 +369,7 @@ export interface WasmProcessOptions {
368
369
  writesBack?: (pid: number) => boolean;
369
370
  processes?: () => ProcessListing;
370
371
  sudo?: (call: SudoCall, signal: AbortSignal) => Promise<SudoResult>;
372
+ crontab?: (call: CrontabCall) => Promise<CrontabResult>;
371
373
  openFiles?: Set<VfsNodes>;
372
374
  nodes?: VfsNodes;
373
375
  mounts?: () => MountLine[];
@@ -517,6 +519,7 @@ export declare class WasmProcess {
517
519
  private socketSyscall;
518
520
  private tty;
519
521
  private procSyscall;
522
+ private crontab;
520
523
  private signalSyscall;
521
524
  private reapsChildren;
522
525
  private sudo;
@@ -1,6 +1,6 @@
1
1
  import { CdpHosts } from './cdp/hosts.js';
2
2
  import type { CdpHook } from './cdp/types.js';
3
- import { type Abi, type Command } from './commands.js';
3
+ import { type Abi, type Command, type CommandVariant } from './commands.js';
4
4
  import type { CronClock } from './cron/clock.js';
5
5
  import { CronService } from './cron/service.js';
6
6
  import { FacadeHosts } from './facade/hosts.js';
@@ -129,6 +129,7 @@ interface Target {
129
129
  imports?: string;
130
130
  preopenRoot?: boolean;
131
131
  asked?: string;
132
+ variants?: readonly CommandVariant[];
132
133
  }
133
134
  export declare function tagOption(tag: unknown): void;
134
135
  export declare function expandDefaults(defaults: Readonly<Record<string, string>> | undefined, env: Readonly<Record<string, string>>): Record<string, string>;
@@ -226,6 +227,7 @@ export declare class Launcher {
226
227
  readonly boot: number;
227
228
  readonly bootId: `${string}-${string}-${string}-${string}-${string}`;
228
229
  readonly cron: CronService;
230
+ private crontabs;
229
231
  private terminals;
230
232
  constructor(options: LauncherOptions);
231
233
  private cronService;
@@ -318,6 +320,7 @@ export declare class Launcher {
318
320
  private timesOf;
319
321
  list(viewer?: Readonly<Cred>): ProcessInfo[];
320
322
  private threadsOf;
323
+ private crontab;
321
324
  private sudo;
322
325
  private spawner;
323
326
  private childGroup;
@@ -76,6 +76,7 @@ export interface JsProgramContext {
76
76
  wait(pid: number, options?: JsWaitOptions): Promise<JsExited>;
77
77
  kill(pid: number, signal?: JsSignal): Promise<void>;
78
78
  sudo(args: readonly string[]): Promise<JsSudoAnswer>;
79
+ crontab(call: JsCrontabCall): Promise<JsCrontabAnswer>;
79
80
  umask(mask?: number): Promise<number>;
80
81
  net: JsNet;
81
82
  websocket(url: string | URL, options?: JsWebSocketOptions): Promise<JsWebSocket>;
@@ -85,6 +86,18 @@ export interface JsProgramContext {
85
86
  onUncaught(listener: JsStrayListener): () => void;
86
87
  onUnhandledRejection(listener: JsStrayListener): () => void;
87
88
  }
89
+ export interface JsCrontabCall {
90
+ action: 'list' | 'remove' | 'install';
91
+ user?: string;
92
+ text?: string;
93
+ }
94
+ export type JsCrontabAnswer = {
95
+ text: string;
96
+ error?: undefined;
97
+ } | {
98
+ error: string;
99
+ text?: undefined;
100
+ };
88
101
  export type JsSudoAnswer = {
89
102
  error: string;
90
103
  errno: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-ecoverse/slicc-kernel",
3
- "version": "1.108.5",
3
+ "version": "1.110.0",
4
4
  "description": "SLICC's wasm-realm kernel: run Emscripten programs in the browser against OPFS, with fork, spawn, pipes and waitpid",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -93,10 +93,5 @@
93
93
  "semantic-release": "25.0.9",
94
94
  "typescript": "7.0.2",
95
95
  "wabt": "1.0.39"
96
- },
97
- "overrides": {
98
- "@ai-ecoverse/py-numpy": {
99
- "@ai-ecoverse/wasix-python": "$@ai-ecoverse/wasix-python"
100
- }
101
96
  }
102
97
  }