@ai-ecoverse/slicc-kernel 1.108.4 → 1.109.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/node.js CHANGED
@@ -6435,7 +6435,7 @@ function satisfies(version, range2) {
6435
6435
  }
6436
6436
 
6437
6437
  // src/version.ts
6438
- var KERNEL_VERSION = true ? "1.108.4" : "0.0.0-dev";
6438
+ var KERNEL_VERSION = true ? "1.109.0" : "0.0.0-dev";
6439
6439
  var OS_RELEASE = "6.0.0-slicc";
6440
6440
  function procVersion() {
6441
6441
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -6794,23 +6794,115 @@ function binfmtOf(binfmts, path) {
6794
6794
  return best === void 0 ? void 0 : binfmts.get(best);
6795
6795
  }
6796
6796
 
6797
- // src/cron/clock.ts
6798
- var systemCronClock = {
6799
- now: () => Date.now(),
6800
- sleep(until, signal) {
6801
- if (signal.aborted) return Promise.resolve();
6802
- return new Promise((resolve2) => {
6803
- const done4 = () => {
6804
- clearTimeout(timer);
6805
- signal.removeEventListener("abort", done4);
6806
- resolve2();
6807
- };
6808
- const timer = setTimeout(done4, Math.max(0, until - Date.now()));
6809
- timer.unref?.();
6810
- signal.addEventListener("abort", done4, { once: true });
6811
- });
6812
- }
6797
+ // src/cron/command.ts
6798
+ var CRONTAB_PATH = "/usr/bin/crontab";
6799
+ var CRONTAB_COMMAND = {
6800
+ name: "crontab",
6801
+ abi: "js",
6802
+ glue: CRONTAB_PATH,
6803
+ wasm: CRONTAB_PATH,
6804
+ argv0: "crontab"
6813
6805
  };
6806
+ var CRONTAB_SOURCE = `export default async function crontab(ctx) {
6807
+ const say = (fd, text) => ctx.write(fd, text).catch(() => undefined);
6808
+ const usage = async () => {
6809
+ await say(2, "usage: crontab [-u user] { -l | -r | -e | file | - }\\n");
6810
+ return 1;
6811
+ };
6812
+ const args = ctx.argv.slice(1);
6813
+ let user;
6814
+ let action;
6815
+ let file;
6816
+ for (let i = 0; i < args.length; i++) {
6817
+ const arg = args[i];
6818
+ if (arg === "-u") {
6819
+ user = args[++i];
6820
+ if (user === undefined) return usage();
6821
+ } else if (arg === "-l" || arg === "-r" || arg === "-e") {
6822
+ if (action !== undefined) return usage();
6823
+ action = arg.slice(1);
6824
+ } else if (arg === "-" || !arg.startsWith("-")) {
6825
+ if (file !== undefined) return usage();
6826
+ file = arg;
6827
+ } else return usage();
6828
+ }
6829
+ if ((action === undefined) === (file === undefined)) return usage();
6830
+ const call = (c) => ctx.crontab(user === undefined ? c : { ...c, user });
6831
+ const failed = async (answer) => {
6832
+ await say(2, answer.error + "\\n");
6833
+ return 1;
6834
+ };
6835
+ const decode = (bytes) => new TextDecoder().decode(bytes);
6836
+ if (action === "l") {
6837
+ const answer = await call({ action: "list" });
6838
+ if (answer.error !== undefined) return failed(answer);
6839
+ await say(1, answer.text);
6840
+ return 0;
6841
+ }
6842
+ if (action === "r") {
6843
+ const answer = await call({ action: "remove" });
6844
+ return answer.error !== undefined ? failed(answer) : 0;
6845
+ }
6846
+ if (action === "e") return edit();
6847
+ let text;
6848
+ try {
6849
+ if (file === "-") {
6850
+ const parts = [];
6851
+ for (;;) {
6852
+ const chunk = await ctx.read(0);
6853
+ if (chunk.length === 0) break;
6854
+ parts.push(decode(chunk));
6855
+ }
6856
+ text = parts.join("");
6857
+ } else text = decode(await ctx.fs.readFile(ctx.resolve(file)));
6858
+ } catch (err) {
6859
+ await say(2, "crontab: " + file + ": " + (err && err.code ? err.code : String(err)) + "\\n");
6860
+ return 1;
6861
+ }
6862
+ const answer = await call({ action: "install", text });
6863
+ return answer.error !== undefined ? failed(answer) : 0;
6864
+
6865
+ async function edit() {
6866
+ const editor = ctx.env.VISUAL || ctx.env.EDITOR;
6867
+ if (!editor) {
6868
+ await say(2, "crontab: set VISUAL or EDITOR to edit, or install a file with crontab <file>\\n");
6869
+ return 1;
6870
+ }
6871
+ const current = await call({ action: "list" });
6872
+ const before = current.error === undefined ? current.text : "";
6873
+ if (current.error !== undefined && !current.error.startsWith("no crontab for ")) return failed(current);
6874
+ const tmp = "/tmp/crontab." + ctx.pid + "." + crypto.randomUUID();
6875
+ try {
6876
+ const file = await ctx.open(tmp, { write: true, create: true, exclusive: true, mode: 0o600 });
6877
+ await file.write(0, new TextEncoder().encode(before));
6878
+ await file.close();
6879
+ } catch (err) {
6880
+ await say(2, "crontab: " + tmp + ": " + (err && err.code ? err.code : String(err)) + "\\n");
6881
+ return 1;
6882
+ }
6883
+ const child = await ctx.spawn({ argv: ["/bin/bash", "-c", editor + ' "$1"', "crontab", tmp] });
6884
+ const ended = await ctx.wait(child.pid);
6885
+ if (ended.status !== 0) {
6886
+ await say(2, "crontab: the editor exited with status " + ended.status + ", crontab not changed\\n");
6887
+ await ctx.fs.rm(tmp).catch(() => undefined);
6888
+ return 1;
6889
+ }
6890
+ const after = decode(await ctx.fs.readFile(tmp));
6891
+ if (after === before) {
6892
+ await say(2, "crontab: no changes made to crontab\\n");
6893
+ await ctx.fs.rm(tmp).catch(() => undefined);
6894
+ return 0;
6895
+ }
6896
+ const answer = await call({ action: "install", text: after });
6897
+ if (answer.error !== undefined) {
6898
+ await say(2, answer.error + "\\ncrontab: your edit is kept in " + tmp + "\\n");
6899
+ return 1;
6900
+ }
6901
+ await ctx.fs.rm(tmp).catch(() => undefined);
6902
+ return 0;
6903
+ }
6904
+ }
6905
+ `;
6814
6906
 
6815
6907
  // src/cron/schedule.ts
6816
6908
  var MISSED_CAP = 1e5;
@@ -7056,6 +7148,83 @@ function parseCrontab(text, path, system = false) {
7056
7148
  return { jobs, errors };
7057
7149
  }
7058
7150
 
7151
+ // src/cron/crontab.ts
7152
+ var CRONTAB_MAX = 256 * 1024;
7153
+ var ACTIONS = /* @__PURE__ */ new Set(["list", "remove", "install"]);
7154
+ function crontabCall(req) {
7155
+ const { action, user, text } = req;
7156
+ if (typeof action !== "string" || !ACTIONS.has(action)) return void 0;
7157
+ if (user !== void 0 && typeof user !== "string") return void 0;
7158
+ if (action === "install" && typeof text !== "string") return void 0;
7159
+ return {
7160
+ op: "proc-crontab",
7161
+ action,
7162
+ ...user !== void 0 ? { user } : {},
7163
+ ...action === "install" ? { text } : {}
7164
+ };
7165
+ }
7166
+ var Crontabs = class {
7167
+ o;
7168
+ constructor(options) {
7169
+ this.o = options;
7170
+ }
7171
+ async request(cred, call) {
7172
+ const caller = await this.o.lookup(cred.ruid);
7173
+ if (!caller) return { error: `crontab: your uid ${cred.ruid} is not in /etc/passwd` };
7174
+ let target = caller;
7175
+ if (call.user !== void 0 && call.user !== caller.name) {
7176
+ if (cred.euid !== 0) return { error: "crontab: must be privileged to use -u" };
7177
+ const found = await this.o.lookup(call.user);
7178
+ if (!found) return { error: `crontab: user ${call.user} unknown` };
7179
+ target = found;
7180
+ }
7181
+ const path = `${SPOOL_DIR}/${target.name}`;
7182
+ const none = { error: `no crontab for ${target.name}` };
7183
+ const { fs } = this.o;
7184
+ if (call.action === "install") return this.install(path, target, call.text);
7185
+ if (!await fs.exists(path)) return none;
7186
+ if (call.action === "list") return { text: await fs.readFile(path) };
7187
+ await fs.rm(path);
7188
+ return { text: "" };
7189
+ }
7190
+ async install(path, target, text) {
7191
+ if (text.length > CRONTAB_MAX) return { error: "crontab: the table is too long" };
7192
+ const { errors } = parseCrontab(text, path);
7193
+ if (errors.length > 0) {
7194
+ const lines2 = errors.map(({ line, message: message3 }) => `"-":${line}: ${message3}`);
7195
+ return { error: ["crontab: errors in crontab file, cannot install", ...lines2].join("\n") };
7196
+ }
7197
+ const { fs, owners } = this.o;
7198
+ if (!await fs.exists(SPOOL_DIR)) {
7199
+ await fs.mkdir(SPOOL_DIR, { recursive: true });
7200
+ if (owners) await fs.chown?.(SPOOL_DIR, { uid: 0, gid: 0, mode: 448, follow: false });
7201
+ }
7202
+ const fresh2 = `${SPOOL_DIR}/.${target.name}.${crypto.randomUUID()}.new`;
7203
+ const owner = owners ? { uid: target.uid, gid: target.gid, mode: 384 } : void 0;
7204
+ await fs.writeFile(fresh2, text, owner);
7205
+ await fs.rename(fresh2, path);
7206
+ return { text: "" };
7207
+ }
7208
+ };
7209
+
7210
+ // src/cron/clock.ts
7211
+ var systemCronClock = {
7212
+ now: () => Date.now(),
7213
+ sleep(until, signal) {
7214
+ if (signal.aborted) return Promise.resolve();
7215
+ return new Promise((resolve2) => {
7216
+ const done4 = () => {
7217
+ clearTimeout(timer);
7218
+ signal.removeEventListener("abort", done4);
7219
+ resolve2();
7220
+ };
7221
+ const timer = setTimeout(done4, Math.max(0, until - Date.now()));
7222
+ timer.unref?.();
7223
+ signal.addEventListener("abort", done4, { once: true });
7224
+ });
7225
+ }
7226
+ };
7227
+
7059
7228
  // src/cron/service.ts
7060
7229
  var CRON_DIR = "/var/spool/cron";
7061
7230
  var STAMP_PATH = `${CRON_DIR}/stamp`;
@@ -7232,7 +7401,7 @@ var CronService = class {
7232
7401
  }
7233
7402
  const names = await fs.readdir(SPOOL_DIR).catch(() => []);
7234
7403
  for (const name2 of names.sort()) {
7235
- if (name2.startsWith(".") || name2.endsWith("~") || name2.endsWith(".new")) continue;
7404
+ if (name2.startsWith(".") || name2.endsWith("~")) continue;
7236
7405
  const path = `${SPOOL_DIR}/${name2}`;
7237
7406
  const account = await this.host.lookup(name2);
7238
7407
  if (!account) {
@@ -11391,12 +11560,11 @@ function attachSyncSabResponder(port, sab, token, opts) {
11391
11560
  if (disposed) return;
11392
11561
  const { status, payload } = encodeSabResult(result);
11393
11562
  drop(id2);
11394
- const entry = {
11395
- status,
11396
- payload,
11397
- timer: setTimeout(() => drop(id2), PENDING_TTL_MS)
11398
- };
11399
- pending.set(id2, entry);
11563
+ const entry = { status, payload };
11564
+ if (payload.byteLength > window.byteLength) {
11565
+ entry.timer = setTimeout(() => drop(id2), PENDING_TTL_MS);
11566
+ pending.set(id2, entry);
11567
+ }
11400
11568
  publish(id2, entry, 0);
11401
11569
  }
11402
11570
  const handler = (event) => {
@@ -12106,6 +12274,8 @@ var AsyncRangedFile = class {
12106
12274
 
12107
12275
  // src/realm/sync-sab-bridge.ts
12108
12276
  var PATH_OPS = new Set(SYNC_FS_OPS);
12277
+ var SPIN_CORES = 4;
12278
+ var spinning = (globalThis.navigator?.hardwareConcurrency ?? 0) >= SPIN_CORES;
12109
12279
 
12110
12280
  // src/realm/live-vfs-fs.ts
12111
12281
  var EARLY_WHOLE_FILE = 64 * 1024;
@@ -16583,6 +16753,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
16583
16753
  "proc-wait",
16584
16754
  "proc-captured",
16585
16755
  "proc-sudo",
16756
+ "proc-crontab",
16586
16757
  "proc-run",
16587
16758
  "proc-exit",
16588
16759
  "proc-kill-self",
@@ -17772,8 +17943,17 @@ var WasmProcess = class {
17772
17943
  return { ok: true, kind: "bytes", bytes: this.children.captured(req.pid, req.slot) };
17773
17944
  case "proc-sudo":
17774
17945
  return { ok: true, kind: "json", json: await this.sudo(req, cancelled) };
17946
+ case "proc-crontab":
17947
+ return { ok: true, kind: "json", json: await this.crontab(req) };
17775
17948
  }
17776
17949
  }
17950
+ async crontab(req) {
17951
+ const { crontab } = this.options;
17952
+ if (!crontab) throw new KernelError("ENOSYS");
17953
+ const call = crontabCall(req);
17954
+ if (!call) throw new KernelError("EINVAL");
17955
+ return crontab(call);
17956
+ }
17777
17957
  signalSyscall(req) {
17778
17958
  if (req.op === "sig-inherited") {
17779
17959
  return { ok: true, kind: "json", json: this.inherited & ~this.caught };
@@ -18176,6 +18356,7 @@ function processOptions(opts, header4, hooks, created3) {
18176
18356
  ...opts.writesBack ? { writesBack: opts.writesBack } : {},
18177
18357
  processes: opts.processes,
18178
18358
  ...opts.sudo ? { sudo: opts.sudo } : {},
18359
+ ...opts.crontab ? { crontab: opts.crontab } : {},
18179
18360
  openFiles: opts.openFiles,
18180
18361
  ...opts.nodes ? { nodes: opts.nodes } : {},
18181
18362
  mounts: opts.mounts,
@@ -21711,6 +21892,7 @@ var LAYOUT = [
21711
21892
  ["/usr", 493],
21712
21893
  ["/usr/local", 493]
21713
21894
  ];
21895
+ var IN_TREE = /* @__PURE__ */ new Set([SUDO_PATH, CRONTAB_PATH]);
21714
21896
  var LOAD_SAMPLE_MS = 5e3;
21715
21897
  var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
21716
21898
  var encoder8 = new TextEncoder();
@@ -21941,6 +22123,7 @@ var Launcher = class {
21941
22123
  boot = Date.now();
21942
22124
  bootId = crypto.randomUUID();
21943
22125
  cron;
22126
+ crontabs;
21944
22127
  terminals = 0;
21945
22128
  constructor(options) {
21946
22129
  this.usersMode = usersMode(options.users);
@@ -22141,7 +22324,9 @@ var Launcher = class {
22141
22324
  found = this.trustedAppended().then(async (appended2) => {
22142
22325
  const tiers = await this.tiers(scope, appended2);
22143
22326
  return scanCommands(this.scanned, tiers, tiers.length - appended2.length);
22144
- }).then((commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND));
22327
+ }).then(
22328
+ (commands) => this.refusals(commands).set(SUDO_COMMAND.name, SUDO_COMMAND).set(CRONTAB_COMMAND.name, CRONTAB_COMMAND)
22329
+ );
22145
22330
  this.catalogs.set(scope, found);
22146
22331
  }
22147
22332
  return found;
@@ -22546,7 +22731,7 @@ var Launcher = class {
22546
22731
  const root = packageRootOf(at, roots) ?? PACKAGE_ROOT.exec(at)?.[1];
22547
22732
  if (root !== void 0) files.push(`${root}/package.json`);
22548
22733
  }
22549
- return [...new Set(files)].filter((path) => path !== "" && path !== SUDO_PATH);
22734
+ return [...new Set(files)].filter((path) => path !== "" && !IN_TREE.has(path));
22550
22735
  }
22551
22736
  async unrunnable(file, cwd, scope) {
22552
22737
  const path = this.fs.resolvePath(cwd, file);
@@ -22686,6 +22871,9 @@ var Launcher = class {
22686
22871
  if (target.abi === "js" && target.wasm === SUDO_PATH) {
22687
22872
  return { abi: "js", glue: SUDO_SOURCE, path: SUDO_PATH };
22688
22873
  }
22874
+ if (target.abi === "js" && target.wasm === CRONTAB_PATH) {
22875
+ return { abi: "js", glue: CRONTAB_SOURCE, path: CRONTAB_PATH };
22876
+ }
22689
22877
  if (target.abi === "js" && target.wasm === REFUSED_PATH) {
22690
22878
  return { abi: "js", glue: REFUSED_SOURCE, path: REFUSED_PATH };
22691
22879
  }
@@ -22841,6 +23029,7 @@ var Launcher = class {
22841
23029
  cpu: this.cpuTotal()
22842
23030
  }),
22843
23031
  sudo: (call, signal) => this.sudo(pid, call, signal),
23032
+ crontab: (call) => this.crontab(pid, call),
22844
23033
  openFiles: this.openFiles,
22845
23034
  nodes: stack.nodes,
22846
23035
  held: this.held,
@@ -23059,6 +23248,14 @@ var Launcher = class {
23059
23248
  const threads = this.stateOf(pid) === "S" ? this.processes.get(pid)?.threads?.() : void 0;
23060
23249
  return threads ? { threads } : {};
23061
23250
  }
23251
+ crontab(pid, call) {
23252
+ this.crontabs ??= new Crontabs({
23253
+ fs: this.fs,
23254
+ owners: this.owners,
23255
+ lookup: (user) => this.users.lookup(user)
23256
+ });
23257
+ return this.crontabs.request(this.credOf(pid), call);
23258
+ }
23062
23259
  sudo(pid, call, signal) {
23063
23260
  const caller = {
23064
23261
  cred: this.credOf(pid),