@ai-ecoverse/slicc-kernel 1.74.0 → 1.75.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/README.md +14 -0
- package/dist/index.js +56 -8
- package/dist/index.js.map +4 -4
- package/dist/kernel-worker.js +285 -27
- package/dist/kernel-worker.js.map +4 -4
- package/dist/node-process-worker.js +183 -34
- package/dist/node-process-worker.js.map +3 -3
- package/dist/node.js +263 -38
- package/dist/node.js.map +4 -4
- package/dist/process-worker.js +183 -34
- package/dist/process-worker.js.map +3 -3
- package/package.json +1 -1
package/dist/kernel-worker.js
CHANGED
|
@@ -2255,9 +2255,11 @@ var KernelPipe = class {
|
|
|
2255
2255
|
// src/kernel/fd-table.ts
|
|
2256
2256
|
var KernelError = class extends Error {
|
|
2257
2257
|
code;
|
|
2258
|
-
|
|
2258
|
+
detail;
|
|
2259
|
+
constructor(code, detail) {
|
|
2259
2260
|
super(code);
|
|
2260
2261
|
this.code = code;
|
|
2262
|
+
if (detail !== void 0) this.detail = detail;
|
|
2261
2263
|
}
|
|
2262
2264
|
};
|
|
2263
2265
|
function pollFile(file) {
|
|
@@ -2400,6 +2402,7 @@ function kernelFdKind(file) {
|
|
|
2400
2402
|
if (file.heldMeta && "device" in file.heldMeta) return "device";
|
|
2401
2403
|
if (file.tty) return "tty";
|
|
2402
2404
|
if (file.sealed) return "sealed";
|
|
2405
|
+
if (file.clipboard) return "clipboard";
|
|
2403
2406
|
return file.seek ? "file" : "stream";
|
|
2404
2407
|
}
|
|
2405
2408
|
var FdTable = class _FdTable {
|
|
@@ -4190,6 +4193,203 @@ function serveClient(port, host) {
|
|
|
4190
4193
|
return { detach, closed: ended.promise };
|
|
4191
4194
|
}
|
|
4192
4195
|
|
|
4196
|
+
// src/kernel/clipboard.ts
|
|
4197
|
+
var CLIPBOARD_MAX_BYTES = 1024 * 1024;
|
|
4198
|
+
var CLIPBOARD_TIMEOUT_MS = 1e4;
|
|
4199
|
+
var encoder = new TextEncoder();
|
|
4200
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
4201
|
+
var READY = { readable: true, writable: true, hangup: false };
|
|
4202
|
+
function lineOf(err) {
|
|
4203
|
+
const text = err instanceof Error ? err.message : String(err);
|
|
4204
|
+
return (text.split("\n")[0] ?? "").slice(0, 200);
|
|
4205
|
+
}
|
|
4206
|
+
function clipboardFailure(err) {
|
|
4207
|
+
if (err instanceof KernelError) return err;
|
|
4208
|
+
const name2 = err?.name;
|
|
4209
|
+
return new KernelError(name2 === "NotAllowedError" ? "EACCES" : "EIO", lineOf(err));
|
|
4210
|
+
}
|
|
4211
|
+
function joined2(chunks, size) {
|
|
4212
|
+
if (chunks.length === 1) return chunks[0];
|
|
4213
|
+
const out = new Uint8Array(size);
|
|
4214
|
+
let at = 0;
|
|
4215
|
+
for (const chunk2 of chunks) {
|
|
4216
|
+
out.set(chunk2, at);
|
|
4217
|
+
at += chunk2.length;
|
|
4218
|
+
}
|
|
4219
|
+
return out;
|
|
4220
|
+
}
|
|
4221
|
+
async function interruptible(work, signal) {
|
|
4222
|
+
if (!signal) return work;
|
|
4223
|
+
if (signal.aborted) throw new KernelError("EINTR");
|
|
4224
|
+
let stop = () => {
|
|
4225
|
+
};
|
|
4226
|
+
const interrupted2 = new Promise((_, reject) => {
|
|
4227
|
+
stop = () => reject(new KernelError("EINTR"));
|
|
4228
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
4229
|
+
});
|
|
4230
|
+
try {
|
|
4231
|
+
return await Promise.race([work, interrupted2]);
|
|
4232
|
+
} finally {
|
|
4233
|
+
signal.removeEventListener("abort", stop);
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
var ClipboardChannel = class {
|
|
4237
|
+
stamp;
|
|
4238
|
+
access;
|
|
4239
|
+
broker;
|
|
4240
|
+
snapshot;
|
|
4241
|
+
taken;
|
|
4242
|
+
offset = 0;
|
|
4243
|
+
chunks = [];
|
|
4244
|
+
size = 0;
|
|
4245
|
+
wrote = false;
|
|
4246
|
+
failed = false;
|
|
4247
|
+
constructor(broker, access2, stamp) {
|
|
4248
|
+
this.broker = broker;
|
|
4249
|
+
this.access = access2;
|
|
4250
|
+
this.stamp = stamp;
|
|
4251
|
+
}
|
|
4252
|
+
get unread() {
|
|
4253
|
+
return this.taken === void 0 && this.snapshot === void 0;
|
|
4254
|
+
}
|
|
4255
|
+
get settled() {
|
|
4256
|
+
return this.taken !== void 0;
|
|
4257
|
+
}
|
|
4258
|
+
async read(max, signal) {
|
|
4259
|
+
if (this.access === "write") throw new KernelError("EBADF");
|
|
4260
|
+
if (!this.taken) {
|
|
4261
|
+
if (!this.snapshot) {
|
|
4262
|
+
const stamp = this.stamp;
|
|
4263
|
+
const asked = this.broker.ask(async (hook) => {
|
|
4264
|
+
const text = await hook.read(stamp);
|
|
4265
|
+
if (typeof text !== "string") throw new KernelError("EIO", "the clipboard gave no text");
|
|
4266
|
+
return encoder.encode(text);
|
|
4267
|
+
});
|
|
4268
|
+
this.snapshot = asked;
|
|
4269
|
+
asked.then(
|
|
4270
|
+
(bytes) => {
|
|
4271
|
+
if (this.snapshot === asked) this.taken = bytes;
|
|
4272
|
+
},
|
|
4273
|
+
() => {
|
|
4274
|
+
if (this.snapshot === asked) this.snapshot = void 0;
|
|
4275
|
+
}
|
|
4276
|
+
);
|
|
4277
|
+
}
|
|
4278
|
+
this.taken = await interruptible(this.snapshot, signal);
|
|
4279
|
+
}
|
|
4280
|
+
const out = this.taken.subarray(this.offset, this.offset + max);
|
|
4281
|
+
this.offset += out.length;
|
|
4282
|
+
return out;
|
|
4283
|
+
}
|
|
4284
|
+
write(bytes) {
|
|
4285
|
+
if (this.access === "read") throw new KernelError("EBADF");
|
|
4286
|
+
this.wrote = true;
|
|
4287
|
+
if (this.failed) throw new KernelError("EFBIG");
|
|
4288
|
+
if (this.size + bytes.length > CLIPBOARD_MAX_BYTES) {
|
|
4289
|
+
this.failed = true;
|
|
4290
|
+
this.chunks = [];
|
|
4291
|
+
this.size = 0;
|
|
4292
|
+
throw new KernelError("EFBIG");
|
|
4293
|
+
}
|
|
4294
|
+
if (bytes.length > 0) this.chunks.push(bytes.slice());
|
|
4295
|
+
this.size += bytes.length;
|
|
4296
|
+
return bytes.length;
|
|
4297
|
+
}
|
|
4298
|
+
poll() {
|
|
4299
|
+
return READY;
|
|
4300
|
+
}
|
|
4301
|
+
async close() {
|
|
4302
|
+
this.snapshot = void 0;
|
|
4303
|
+
this.taken = void 0;
|
|
4304
|
+
const commits = this.access === "write" || this.access === "both" && this.wrote;
|
|
4305
|
+
if (!commits || this.failed) return;
|
|
4306
|
+
const bytes = joined2(this.chunks, this.size);
|
|
4307
|
+
this.chunks = [];
|
|
4308
|
+
this.size = 0;
|
|
4309
|
+
let text;
|
|
4310
|
+
try {
|
|
4311
|
+
text = decoder.decode(bytes);
|
|
4312
|
+
} catch {
|
|
4313
|
+
throw new KernelError("EILSEQ", "the text is not valid UTF-8");
|
|
4314
|
+
}
|
|
4315
|
+
const stamp = this.stamp;
|
|
4316
|
+
await this.broker.ask(async (hook) => {
|
|
4317
|
+
await hook.write(text, stamp);
|
|
4318
|
+
});
|
|
4319
|
+
}
|
|
4320
|
+
};
|
|
4321
|
+
var ClipboardBroker = class {
|
|
4322
|
+
hook;
|
|
4323
|
+
users;
|
|
4324
|
+
timeoutMs;
|
|
4325
|
+
tagOf;
|
|
4326
|
+
constructor(hook, users, timeoutMs = CLIPBOARD_TIMEOUT_MS, tagOf = () => void 0) {
|
|
4327
|
+
this.hook = hook;
|
|
4328
|
+
this.users = users;
|
|
4329
|
+
this.timeoutMs = timeoutMs;
|
|
4330
|
+
this.tagOf = tagOf;
|
|
4331
|
+
}
|
|
4332
|
+
async ask(work) {
|
|
4333
|
+
const hook = this.hook;
|
|
4334
|
+
if (!hook) throw new KernelError("ENOTSUP", "no clipboard in this embedder");
|
|
4335
|
+
let timer;
|
|
4336
|
+
const late = new Promise((_, reject) => {
|
|
4337
|
+
timer = setTimeout(
|
|
4338
|
+
() => reject(new KernelError("EIO", "clipboard timed out")),
|
|
4339
|
+
this.timeoutMs
|
|
4340
|
+
);
|
|
4341
|
+
});
|
|
4342
|
+
const asked = Promise.resolve().then(() => work(hook)).catch((err) => {
|
|
4343
|
+
throw clipboardFailure(err);
|
|
4344
|
+
});
|
|
4345
|
+
try {
|
|
4346
|
+
return await Promise.race([asked, late]);
|
|
4347
|
+
} finally {
|
|
4348
|
+
clearTimeout(timer);
|
|
4349
|
+
}
|
|
4350
|
+
}
|
|
4351
|
+
open(access2, stamp) {
|
|
4352
|
+
const channel = new ClipboardChannel(this, access2, stamp);
|
|
4353
|
+
return new OpenFile({
|
|
4354
|
+
read: (max, signal) => channel.read(max, signal),
|
|
4355
|
+
write: async (bytes) => channel.write(bytes),
|
|
4356
|
+
poll: () => channel.poll(),
|
|
4357
|
+
close: () => channel.close(),
|
|
4358
|
+
clipboard: channel
|
|
4359
|
+
});
|
|
4360
|
+
}
|
|
4361
|
+
};
|
|
4362
|
+
|
|
4363
|
+
// src/clipboard-relay.ts
|
|
4364
|
+
function clipboardAsker(port) {
|
|
4365
|
+
const asks = /* @__PURE__ */ new Map();
|
|
4366
|
+
let next2 = 0;
|
|
4367
|
+
const ask = (op, stamp, text) => {
|
|
4368
|
+
const id2 = ++next2;
|
|
4369
|
+
const waiting = Promise.withResolvers();
|
|
4370
|
+
asks.set(id2, waiting);
|
|
4371
|
+
const timer = setTimeout(() => {
|
|
4372
|
+
if (asks.delete(id2)) waiting.reject(new Error("clipboard timed out"));
|
|
4373
|
+
}, CLIPBOARD_TIMEOUT_MS + 1e3);
|
|
4374
|
+
port.postMessage({ clipboardAsk: { id: id2, op, stamp, ...text !== void 0 ? { text } : {} } });
|
|
4375
|
+
return waiting.promise.finally(() => clearTimeout(timer));
|
|
4376
|
+
};
|
|
4377
|
+
const hook = {
|
|
4378
|
+
read: async (stamp) => await ask("read", stamp) ?? "",
|
|
4379
|
+
write: async (text, stamp) => {
|
|
4380
|
+
await ask("write", stamp, text);
|
|
4381
|
+
}
|
|
4382
|
+
};
|
|
4383
|
+
const answer = ({ id: id2, text, error }) => {
|
|
4384
|
+
const waiting = asks.get(id2);
|
|
4385
|
+
if (!waiting) return;
|
|
4386
|
+
asks.delete(id2);
|
|
4387
|
+
if (error) waiting.reject(Object.assign(new Error(error.message), { name: error.name }));
|
|
4388
|
+
else waiting.resolve(text);
|
|
4389
|
+
};
|
|
4390
|
+
return { hook, answer };
|
|
4391
|
+
}
|
|
4392
|
+
|
|
4193
4393
|
// src/kernel/net/loopback-names.ts
|
|
4194
4394
|
var HOST_LOOPBACK = "host.slicc.internal";
|
|
4195
4395
|
var HOST_LOOPBACK_ADDRESS = [10, 0, 2, 2];
|
|
@@ -9191,20 +9391,20 @@ var SAB_DEFAULT_WINDOW_BYTES = 1024 * 1024;
|
|
|
9191
9391
|
var SAB_MIN_BYTES = SAB_HEADER_BYTES + 4096;
|
|
9192
9392
|
var SYNC_SAB_REQ_MSG = "sync-sab-req";
|
|
9193
9393
|
var SYNC_SAB_NEXT_MSG = "sync-sab-next";
|
|
9194
|
-
var
|
|
9195
|
-
var
|
|
9394
|
+
var encoder2 = new TextEncoder();
|
|
9395
|
+
var decoder2 = new TextDecoder();
|
|
9196
9396
|
function encodeSabResult(result) {
|
|
9197
9397
|
if (!result.ok) {
|
|
9198
9398
|
return {
|
|
9199
9399
|
status: SAB_STATUS_ERRNO,
|
|
9200
|
-
payload:
|
|
9400
|
+
payload: encoder2.encode(JSON.stringify({ errno: result.errno, message: result.message }))
|
|
9201
9401
|
};
|
|
9202
9402
|
}
|
|
9203
9403
|
switch (result.kind) {
|
|
9204
9404
|
case "bytes":
|
|
9205
9405
|
return { status: SAB_STATUS_BYTES, payload: result.bytes };
|
|
9206
9406
|
case "json":
|
|
9207
|
-
return { status: SAB_STATUS_JSON, payload:
|
|
9407
|
+
return { status: SAB_STATUS_JSON, payload: encoder2.encode(JSON.stringify(result.json)) };
|
|
9208
9408
|
default:
|
|
9209
9409
|
return { status: SAB_STATUS_VOID, payload: new Uint8Array(0) };
|
|
9210
9410
|
}
|
|
@@ -10678,7 +10878,7 @@ function defaultTermios() {
|
|
|
10678
10878
|
c_cc: cCc
|
|
10679
10879
|
};
|
|
10680
10880
|
}
|
|
10681
|
-
var
|
|
10881
|
+
var encoder3 = new TextEncoder();
|
|
10682
10882
|
function isContinuation(byte) {
|
|
10683
10883
|
return (byte & 192) === 128;
|
|
10684
10884
|
}
|
|
@@ -10800,7 +11000,7 @@ var KernelTty = class {
|
|
|
10800
11000
|
this.line.push(byte);
|
|
10801
11001
|
this.readable.push(Uint8Array.from(this.line));
|
|
10802
11002
|
this.line = [];
|
|
10803
|
-
if (echo) this.output(
|
|
11003
|
+
if (echo) this.output(encoder3.encode("\n"));
|
|
10804
11004
|
} else {
|
|
10805
11005
|
this.line.push(byte);
|
|
10806
11006
|
if (echo) this.echo(byte);
|
|
@@ -10825,7 +11025,7 @@ var KernelTty = class {
|
|
|
10825
11025
|
const utf82 = (this.termios.c_iflag & IUTF8) !== 0;
|
|
10826
11026
|
const cells = utf82 ? dropped2.filter((b) => !isContinuation(b)).length : dropped2.length;
|
|
10827
11027
|
if (cells > 0 && this.termios.c_lflag & ECHO) {
|
|
10828
|
-
this.output(
|
|
11028
|
+
this.output(encoder3.encode("\b \b".repeat(cells)), false);
|
|
10829
11029
|
}
|
|
10830
11030
|
}
|
|
10831
11031
|
echo(byte) {
|
|
@@ -11314,7 +11514,7 @@ async function socketSyscall(req, proc) {
|
|
|
11314
11514
|
dialing?.catch(() => void 0);
|
|
11315
11515
|
throw new KernelError("EINPROGRESS");
|
|
11316
11516
|
}
|
|
11317
|
-
if (dialing !== void 0) await
|
|
11517
|
+
if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
|
|
11318
11518
|
return ok();
|
|
11319
11519
|
}
|
|
11320
11520
|
case "sock-shutdown":
|
|
@@ -11338,7 +11538,7 @@ async function socketSyscall(req, proc) {
|
|
|
11338
11538
|
}
|
|
11339
11539
|
}
|
|
11340
11540
|
}
|
|
11341
|
-
function
|
|
11541
|
+
function interruptible2(dialing, signal) {
|
|
11342
11542
|
return new Promise((resolve, reject) => {
|
|
11343
11543
|
const abort = () => reject(new KernelError("EINTR"));
|
|
11344
11544
|
signal.addEventListener("abort", abort, { once: true });
|
|
@@ -12005,6 +12205,7 @@ var SYSCALL_OPS = /* @__PURE__ */ new Set([
|
|
|
12005
12205
|
"fd-tty-names",
|
|
12006
12206
|
"fd-tty-owner",
|
|
12007
12207
|
"fd-open-sealed",
|
|
12208
|
+
"fd-open-clipboard",
|
|
12008
12209
|
"tty-get",
|
|
12009
12210
|
"tty-set",
|
|
12010
12211
|
"tty-winsz",
|
|
@@ -12247,7 +12448,7 @@ var WasmProcess = class {
|
|
|
12247
12448
|
return await this.procSyscall(req, cancelled);
|
|
12248
12449
|
} catch (e) {
|
|
12249
12450
|
if (e instanceof KernelError || e instanceof SpawnError) {
|
|
12250
|
-
return { ok: false, errno: e.code, message: e.code };
|
|
12451
|
+
return { ok: false, errno: e.code, message: e.detail ?? e.code };
|
|
12251
12452
|
}
|
|
12252
12453
|
const code = e?.code;
|
|
12253
12454
|
if (typeof code === "string" && /^E[A-Z]+$/.test(code)) {
|
|
@@ -12290,6 +12491,7 @@ var WasmProcess = class {
|
|
|
12290
12491
|
const read = req.peek ? file.peek : file.read;
|
|
12291
12492
|
if (!read) throw new KernelError(req.peek && file.read ? "EOPNOTSUPP" : "EBADF");
|
|
12292
12493
|
if (file.tty) this.checkForeground(file.tty);
|
|
12494
|
+
if (file.clipboard) return this.readClipboard(file.clipboard, req, cancelled);
|
|
12293
12495
|
if (req.max <= 0) return new Uint8Array(0);
|
|
12294
12496
|
if (file instanceof KernelSocket && file.unconnected) throw new KernelError("ENOTCONN");
|
|
12295
12497
|
const ready2 = pollFile(file).readable;
|
|
@@ -12297,6 +12499,13 @@ var WasmProcess = class {
|
|
|
12297
12499
|
const signal = ready2 ? this.interrupt.signal : this.blockingSignal(cancelled);
|
|
12298
12500
|
return read.call(file, Math.max(0, Math.min(req.max, MAX_READ)), signal);
|
|
12299
12501
|
}
|
|
12502
|
+
async readClipboard(channel, req, cancelled) {
|
|
12503
|
+
if (channel.access === "write") throw new KernelError("EBADF");
|
|
12504
|
+
if (req.max <= 0) return new Uint8Array(0);
|
|
12505
|
+
if (channel.unread) channel.stamp = await this.clipboardStamp();
|
|
12506
|
+
const signal = channel.settled ? this.interrupt.signal : this.blockingSignal(cancelled);
|
|
12507
|
+
return channel.read(Math.min(req.max, MAX_READ), signal);
|
|
12508
|
+
}
|
|
12300
12509
|
async write(fd, body, nonblock = false, cancelled) {
|
|
12301
12510
|
const file = this.fds.get(fd).file;
|
|
12302
12511
|
if (file.sealed) return file.sealed.send(body, await this.sealedCaller(), this.gone.signal);
|
|
@@ -12400,6 +12609,36 @@ var WasmProcess = class {
|
|
|
12400
12609
|
if (!broker) throw new KernelError("ENOENT");
|
|
12401
12610
|
return this.fds.install(broker.open(), 3);
|
|
12402
12611
|
}
|
|
12612
|
+
async openClipboard(flags2) {
|
|
12613
|
+
const broker = this.options.clipboard;
|
|
12614
|
+
if (!broker) throw new KernelError("ENOENT");
|
|
12615
|
+
const mode = flags2 & WRITE_ACCESS;
|
|
12616
|
+
const access2 = mode === 0 ? "read" : mode === 1 ? "write" : "both";
|
|
12617
|
+
return this.fds.install(broker.open(access2, await this.clipboardStamp()), 3);
|
|
12618
|
+
}
|
|
12619
|
+
async clipboardStamp() {
|
|
12620
|
+
const shown = await this.options.identity?.();
|
|
12621
|
+
const broker = this.options.clipboard;
|
|
12622
|
+
const tag = broker.tagOf(this.pid);
|
|
12623
|
+
return {
|
|
12624
|
+
pid: shown?.pid ?? this.pid,
|
|
12625
|
+
uid: this.cred.euid,
|
|
12626
|
+
gid: this.cred.egid,
|
|
12627
|
+
users: broker.users,
|
|
12628
|
+
...tag !== void 0 ? { tag } : {}
|
|
12629
|
+
};
|
|
12630
|
+
}
|
|
12631
|
+
async stampClipboards() {
|
|
12632
|
+
if (!this.options.clipboard) return;
|
|
12633
|
+
const open = this.fds.numbers().filter((fd) => this.fds.get(fd).file.clipboard);
|
|
12634
|
+
if (open.length === 0) return;
|
|
12635
|
+
const stamp = await this.clipboardStamp().catch(() => void 0);
|
|
12636
|
+
if (!stamp) return;
|
|
12637
|
+
for (const fd of open) {
|
|
12638
|
+
const channel = this.fds.get(fd).file.clipboard;
|
|
12639
|
+
if (channel) channel.stamp = stamp;
|
|
12640
|
+
}
|
|
12641
|
+
}
|
|
12403
12642
|
async sealedCaller() {
|
|
12404
12643
|
const shown = await this.options.identity?.();
|
|
12405
12644
|
const broker = this.options.sealed;
|
|
@@ -12474,10 +12713,13 @@ var WasmProcess = class {
|
|
|
12474
12713
|
kind: "json",
|
|
12475
12714
|
json: await this.write(req.fd, req.body, req.nonblock, cancelled)
|
|
12476
12715
|
};
|
|
12477
|
-
case "fd-close":
|
|
12716
|
+
case "fd-close": {
|
|
12478
12717
|
this.options.locks?.closed(this.pid, req.fd);
|
|
12718
|
+
const channel = this.fds.get(req.fd).file.clipboard;
|
|
12719
|
+
if (channel) channel.stamp = await this.clipboardStamp();
|
|
12479
12720
|
await Promise.resolve(this.fds.close(req.fd));
|
|
12480
12721
|
return { ok: true, kind: "void" };
|
|
12722
|
+
}
|
|
12481
12723
|
case "fd-pipe": {
|
|
12482
12724
|
const pipe = openPipe();
|
|
12483
12725
|
const read = this.fds.install(pipe.read, 3);
|
|
@@ -12535,6 +12777,8 @@ var WasmProcess = class {
|
|
|
12535
12777
|
return { ok: true, kind: "json", json: this.options.jobs?.terminalNames() ?? [] };
|
|
12536
12778
|
case "fd-open-sealed":
|
|
12537
12779
|
return { ok: true, kind: "json", json: this.openSealed() };
|
|
12780
|
+
case "fd-open-clipboard":
|
|
12781
|
+
return { ok: true, kind: "json", json: await this.openClipboard(req.flags) };
|
|
12538
12782
|
case "fd-tty-owner":
|
|
12539
12783
|
return { ok: true, kind: "json", json: this.terminalByName(req.name).owner ?? null };
|
|
12540
12784
|
case "fd-select": {
|
|
@@ -12986,6 +13230,7 @@ var WasmProcess = class {
|
|
|
12986
13230
|
this.options.locks?.release(this.pid);
|
|
12987
13231
|
this.asyncOps.close();
|
|
12988
13232
|
for (const pid of this.children.pids()) this.options.onReap?.(pid);
|
|
13233
|
+
await this.stampClipboards();
|
|
12989
13234
|
await this.fds.closeAll();
|
|
12990
13235
|
if (!this.options.nodes) this.options.openFiles?.delete(this.nodes);
|
|
12991
13236
|
await this.options.http?.closeAll();
|
|
@@ -13168,6 +13413,7 @@ function processOptions(opts, header3, hooks, created3) {
|
|
|
13168
13413
|
...inherited(opts),
|
|
13169
13414
|
...opts.owners ? { owners: true } : {},
|
|
13170
13415
|
...opts.sealed ? { sealed: opts.sealed } : {},
|
|
13416
|
+
...opts.clipboard ? { clipboard: opts.clipboard } : {},
|
|
13171
13417
|
...opts.readonlyAt ? { readonlyAt: opts.readonlyAt } : {},
|
|
13172
13418
|
...opts.dac ? { guard: { dac: opts.dac, fs: opts.fs, created: created3 } } : {},
|
|
13173
13419
|
...opts.identity ? { identity: opts.identity } : {},
|
|
@@ -13672,14 +13918,14 @@ var SEALED_OPS = /* @__PURE__ */ new Set([
|
|
|
13672
13918
|
"cancel",
|
|
13673
13919
|
"materialize"
|
|
13674
13920
|
]);
|
|
13675
|
-
var
|
|
13676
|
-
var
|
|
13921
|
+
var encoder4 = new TextEncoder();
|
|
13922
|
+
var decoder3 = new TextDecoder("utf-8", { fatal: true });
|
|
13677
13923
|
function parseSealed(bytes) {
|
|
13678
13924
|
try {
|
|
13679
13925
|
if (bytes.length > SEALED_MAX_BYTES) throw new KernelError("EMSGSIZE");
|
|
13680
13926
|
let parsed2;
|
|
13681
13927
|
try {
|
|
13682
|
-
parsed2 = JSON.parse(
|
|
13928
|
+
parsed2 = JSON.parse(decoder3.decode(bytes));
|
|
13683
13929
|
} catch {
|
|
13684
13930
|
throw new KernelError("EINVAL");
|
|
13685
13931
|
}
|
|
@@ -13705,13 +13951,13 @@ function serialized(reply) {
|
|
|
13705
13951
|
const text = JSON.stringify(reply);
|
|
13706
13952
|
plain = text === void 0 ? void 0 : JSON.parse(text);
|
|
13707
13953
|
} catch (err) {
|
|
13708
|
-
return JSON.stringify(errorReply("EIO",
|
|
13954
|
+
return JSON.stringify(errorReply("EIO", lineOf2(err)));
|
|
13709
13955
|
}
|
|
13710
13956
|
if (!isRecord(plain)) return JSON.stringify(errorReply("EIO", "the vault gave no reply"));
|
|
13711
13957
|
const { v: _, ...rest2 } = plain;
|
|
13712
13958
|
return JSON.stringify({ v: 1, ...rest2 });
|
|
13713
13959
|
}
|
|
13714
|
-
function
|
|
13960
|
+
function lineOf2(err) {
|
|
13715
13961
|
const text = err instanceof Error ? err.message : String(err);
|
|
13716
13962
|
return (text.split("\n")[0] ?? "").slice(0, 200);
|
|
13717
13963
|
}
|
|
@@ -13741,7 +13987,7 @@ var SealedChannel = class {
|
|
|
13741
13987
|
gone.removeEventListener("abort", withdraw);
|
|
13742
13988
|
if (this.inflight !== flight) return;
|
|
13743
13989
|
this.inflight = void 0;
|
|
13744
|
-
if (reply) this.reply =
|
|
13990
|
+
if (reply) this.reply = encoder4.encode(serialized(reply));
|
|
13745
13991
|
});
|
|
13746
13992
|
this.inflight = flight;
|
|
13747
13993
|
return length2;
|
|
@@ -13755,7 +14001,7 @@ var SealedChannel = class {
|
|
|
13755
14001
|
if (signal.aborted) return void 0;
|
|
13756
14002
|
const asked = Promise.resolve().then(() => hook(req, { signal })).then(
|
|
13757
14003
|
(reply2) => isRecord(reply2) ? reply2 : errorReply("EIO", "the vault gave no reply"),
|
|
13758
|
-
(err) => errorReply("EIO",
|
|
14004
|
+
(err) => errorReply("EIO", lineOf2(err))
|
|
13759
14005
|
);
|
|
13760
14006
|
const reply = await Promise.race([asked, stopped]);
|
|
13761
14007
|
if (!signal.aborted) return reply;
|
|
@@ -14478,13 +14724,13 @@ var HostfsClient = class {
|
|
|
14478
14724
|
const response = await this.send("/api/hostfs/watch", { method: "POST", signal });
|
|
14479
14725
|
onOpen();
|
|
14480
14726
|
const reader = response.body.getReader();
|
|
14481
|
-
const
|
|
14727
|
+
const decoder4 = new TextDecoder();
|
|
14482
14728
|
let buffered = "";
|
|
14483
14729
|
try {
|
|
14484
14730
|
for (; ; ) {
|
|
14485
14731
|
const { done: done4, value } = await reader.read();
|
|
14486
14732
|
if (done4) return;
|
|
14487
|
-
buffered +=
|
|
14733
|
+
buffered += decoder4.decode(value, { stream: true });
|
|
14488
14734
|
let end = buffered.indexOf("\n");
|
|
14489
14735
|
while (end >= 0) {
|
|
14490
14736
|
const line = buffered.slice(0, end).trim();
|
|
@@ -15863,7 +16109,7 @@ var LAYOUT = [
|
|
|
15863
16109
|
];
|
|
15864
16110
|
var LOAD_SAMPLE_MS = 5e3;
|
|
15865
16111
|
var LOAD_WINDOWS_MS = [6e4, 3e5, 9e5];
|
|
15866
|
-
var
|
|
16112
|
+
var encoder5 = new TextEncoder();
|
|
15867
16113
|
var USERS_MODES = ["off", "report", "enforce"];
|
|
15868
16114
|
function usersMode(mode = "off") {
|
|
15869
16115
|
if (USERS_MODES.includes(mode)) return mode;
|
|
@@ -16020,6 +16266,7 @@ var Launcher = class {
|
|
|
16020
16266
|
usersMode;
|
|
16021
16267
|
dac;
|
|
16022
16268
|
sealed;
|
|
16269
|
+
clipboard;
|
|
16023
16270
|
rootFs;
|
|
16024
16271
|
fileWatches;
|
|
16025
16272
|
nextPid = 1e3;
|
|
@@ -16030,6 +16277,12 @@ var Launcher = class {
|
|
|
16030
16277
|
this.rootFs = options.fs;
|
|
16031
16278
|
this.dac = this.usersMode === "off" ? void 0 : new Dac(this.usersMode, options.onDenial);
|
|
16032
16279
|
if (this.dac) this.dac.tagOf = (pid) => this.tagOf(pid);
|
|
16280
|
+
this.clipboard = new ClipboardBroker(
|
|
16281
|
+
options.clipboard,
|
|
16282
|
+
this.usersMode,
|
|
16283
|
+
void 0,
|
|
16284
|
+
(pid) => this.tagOf(pid)
|
|
16285
|
+
);
|
|
16033
16286
|
this.sealed = new SealedBroker(
|
|
16034
16287
|
options.sealed,
|
|
16035
16288
|
this.usersMode,
|
|
@@ -16446,6 +16699,7 @@ var Launcher = class {
|
|
|
16446
16699
|
cred: req.cred,
|
|
16447
16700
|
...this.owners ? { owners: true } : {},
|
|
16448
16701
|
sealed: this.sealed,
|
|
16702
|
+
clipboard: this.clipboard,
|
|
16449
16703
|
...this.dac ? { dac: this.dac } : {},
|
|
16450
16704
|
program: req.program,
|
|
16451
16705
|
argv0: req.argv0,
|
|
@@ -17206,7 +17460,7 @@ var Launcher = class {
|
|
|
17206
17460
|
},
|
|
17207
17461
|
account
|
|
17208
17462
|
);
|
|
17209
|
-
const report = (message3) => options.onData(
|
|
17463
|
+
const report = (message3) => options.onData(encoder5.encode(`${message3}\r
|
|
17210
17464
|
`));
|
|
17211
17465
|
const handle = await this.launch(planned, {
|
|
17212
17466
|
env,
|
|
@@ -17243,7 +17497,7 @@ var Launcher = class {
|
|
|
17243
17497
|
};
|
|
17244
17498
|
const stdout = collect(out, options.onStdout);
|
|
17245
17499
|
const stderr = collect(err, options.onStderr);
|
|
17246
|
-
const report = (message3) => stderr(
|
|
17500
|
+
const report = (message3) => stderr(encoder5.encode(`${message3}
|
|
17247
17501
|
`));
|
|
17248
17502
|
if (!planned) {
|
|
17249
17503
|
report(`${file}: command not found`);
|
|
@@ -17431,13 +17685,14 @@ function usersOp(l, req) {
|
|
|
17431
17685
|
if (req.op === "groups-remove-member") return l.users.removeMember(req.group, req.user);
|
|
17432
17686
|
return l.users.list();
|
|
17433
17687
|
}
|
|
17434
|
-
function hostOptions(req, port, sealed) {
|
|
17688
|
+
function hostOptions(req, port, sealed, clipboard) {
|
|
17435
17689
|
return {
|
|
17436
17690
|
...req.hostname !== void 0 ? { hostname: req.hostname } : {},
|
|
17437
17691
|
...req.users !== void 0 ? { users: req.users } : {},
|
|
17438
17692
|
...req.denials ? { onDenial: (denial) => port.postMessage({ denial }) } : {},
|
|
17439
17693
|
...req.protect ? { protect: protectInit(req.protect) } : {},
|
|
17440
|
-
...req.sealed ? { sealed } : {}
|
|
17694
|
+
...req.sealed ? { sealed } : {},
|
|
17695
|
+
...req.clipboard ? { clipboard } : {}
|
|
17441
17696
|
};
|
|
17442
17697
|
}
|
|
17443
17698
|
function policyAsker(port) {
|
|
@@ -17467,6 +17722,7 @@ function serveKernel(port, deps) {
|
|
|
17467
17722
|
const reply = (id2, body, transfer) => port.postMessage({ id: id2, ...body }, transfer);
|
|
17468
17723
|
const clients = /* @__PURE__ */ new Set();
|
|
17469
17724
|
const sealed = sealedAsker(port);
|
|
17725
|
+
const clipboard = clipboardAsker(port);
|
|
17470
17726
|
const approvals = sudoApprovals(port);
|
|
17471
17727
|
const locks = deps.locks === null ? void 0 : deps.locks ?? locksOf();
|
|
17472
17728
|
let holding;
|
|
@@ -17531,7 +17787,7 @@ function serveKernel(port, deps) {
|
|
|
17531
17787
|
...deps.createDriverWorker ? { createDriverWorker: deps.createDriverWorker } : {},
|
|
17532
17788
|
...req.modules ? { modules: req.modules } : {},
|
|
17533
17789
|
...req.env ? { env: req.env } : {},
|
|
17534
|
-
...hostOptions(req, port, sealed.hook),
|
|
17790
|
+
...hostOptions(req, port, sealed.hook, clipboard.hook),
|
|
17535
17791
|
...req.approve ? { approve: approvals.ask } : {},
|
|
17536
17792
|
...net,
|
|
17537
17793
|
...media ? { media } : {},
|
|
@@ -17600,6 +17856,8 @@ function serveKernel(port, deps) {
|
|
|
17600
17856
|
port.addEventListener("message", (event) => {
|
|
17601
17857
|
const sealedReply = event.data.sealedReply;
|
|
17602
17858
|
if (sealedReply) return sealed.answer(sealedReply);
|
|
17859
|
+
const clipboardReply = event.data.clipboardReply;
|
|
17860
|
+
if (clipboardReply) return clipboard.answer(clipboardReply);
|
|
17603
17861
|
const grant = event.data.hostfsGrant;
|
|
17604
17862
|
if (grant) return granted2(grant);
|
|
17605
17863
|
if (allowed(event.data)) return;
|