@impz/testing 6.0.0 → 6.0.1

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 (3) hide show
  1. package/README.md +4 -16
  2. package/dist/index.js +216 -25
  3. package/package.json +5 -5
package/README.md CHANGED
@@ -79,22 +79,10 @@ await forward.listening;
79
79
  const stream = await guest.connect(4000);
80
80
  ```
81
81
 
82
- The guest copies these behaviours of the real guest agent's reverse forwards:
83
-
84
- - Bytes relay both ways between the process and the forward's local end.
85
- - The process's end of its side reaches the local end as an EOF, and the local end's EOF ends what
86
- the process reads.
87
- - A process that resets its connection drops the relay with no EOF.
88
- - A forward that asks for port 0 gets a free port, and a second forward on a port in use fails with
89
- `LISTEN_FAILED`.
90
- - When the imp sleeps or goes away, the guest drops its connections, so a forward ends as `lost`. A
91
- forward opened after the wake listens on the guest again.
92
-
93
- It does not copy:
94
-
95
- - unix socket forwards and ssh-agent forwarding, which it answers with `UNKNOWN_OP`
96
- - the refusal of a port below 1024 to an image whose user is not root
97
- - the limit of 16 clients waiting for impd, and the 10 s after which an unpaired client closes
82
+ The guest relays bytes and EOFs as the real guest agent's reverse forwards do, and drops its
83
+ connections when the imp sleeps.
84
+ [Testing against impd](https://github.com/zgeoff/impz/blob/main/docs/guides/testing-impd.md) lists
85
+ what it copies and what it does not.
98
86
 
99
87
  ## What this impd does not do
100
88
 
package/dist/index.js CHANGED
@@ -21949,7 +21949,7 @@ import { join as join46 } from "path";
21949
21949
  // packages/daemon/package.json
21950
21950
  var package_default = {
21951
21951
  name: "@impz/daemon",
21952
- version: "6.0.0",
21952
+ version: "6.0.1",
21953
21953
  private: true,
21954
21954
  description: "impd: the imp host daemon",
21955
21955
  type: "module",
@@ -26652,6 +26652,12 @@ import { FFIType, dlopen, read } from "bun:ffi";
26652
26652
  var CORE_SYMBOLS = {
26653
26653
  ioctl: { args: [FFIType.i32, FFIType.u64, FFIType.ptr], returns: FFIType.i32 },
26654
26654
  lseek: { args: [FFIType.i32, FFIType.i64, FFIType.i32], returns: FFIType.i64 },
26655
+ mmap: {
26656
+ args: [FFIType.u64, FFIType.u64, FFIType.i32, FFIType.i32, FFIType.i32, FFIType.i64],
26657
+ returns: FFIType.i64
26658
+ },
26659
+ munmap: { args: [FFIType.u64, FFIType.u64], returns: FFIType.i32 },
26660
+ mlock: { args: [FFIType.u64, FFIType.u64], returns: FFIType.i32 },
26655
26661
  __errno_location: { args: [], returns: FFIType.ptr }
26656
26662
  };
26657
26663
  var PIDFD_SYMBOLS = {
@@ -37248,8 +37254,9 @@ function createRamGovernor(deps) {
37248
37254
  const listed = await deps.listAwake();
37249
37255
  const awake = listed.filter((imp) => imp.id !== excludeId);
37250
37256
  const byImp = new Map;
37251
- let usedMib = 0;
37252
- let effectiveMib = 0;
37257
+ const lockedMib = deps.readLockedMib?.() ?? 0;
37258
+ let usedMib = lockedMib;
37259
+ let effectiveMib = lockedMib;
37253
37260
  const readings = await Promise.all(awake.map((imp) => readVmMib(imp.pid, imp.apiSocket)));
37254
37261
  for (const [index, imp] of awake.entries()) {
37255
37262
  const measured = readings[index] ?? 0;
@@ -38947,6 +38954,89 @@ async function setupVm(api, config) {
38947
38954
  await api.putBalloon(VM_DEVICES.balloon);
38948
38955
  }
38949
38956
 
38957
+ // packages/daemon/src/vm/templates/template-locks.ts
38958
+ import { match as match6 } from "ts-pattern";
38959
+
38960
+ // packages/daemon/src/vm/templates/can-admit-lock.ts
38961
+ var MAX_LOCKED_PERCENT = 10;
38962
+ var MIB3 = 1024 * 1024;
38963
+ function canAdmitLock(admission) {
38964
+ const maxLockedBytes = Math.floor(admission.ramBudgetMib * MAX_LOCKED_PERCENT / 100) * MIB3;
38965
+ return admission.heldBytes + admission.lockBytes <= maxLockedBytes;
38966
+ }
38967
+
38968
+ // packages/daemon/src/vm/templates/pick-lock-outcome.ts
38969
+ import { P as P3, match as match5 } from "ts-pattern";
38970
+ var MIB4 = 1024 * 1024;
38971
+ function pickLockOutcome(result, isCurrent) {
38972
+ return match5(result).with({ errno: P3.number }, (failed) => ({ kind: "failed", errno: failed.errno })).with({ declinedBytes: P3.number }, (declined) => ({
38973
+ kind: "declined",
38974
+ lockMib: Math.ceil(declined.declinedBytes / MIB4)
38975
+ })).with({ lock: P3.select() }, (lock) => isCurrent ? { kind: "held", lock, lockMib: Math.ceil(lock.lockedBytes / MIB4) } : { kind: "stale", lock }).exhaustive();
38976
+ }
38977
+
38978
+ // packages/daemon/src/vm/templates/template-locks.ts
38979
+ var MIB5 = 1024 * 1024;
38980
+ function createTemplateLocks(deps) {
38981
+ const locks = new Map;
38982
+ const locking = createSingleFlight();
38983
+ const sizedBytes = new Map;
38984
+ const state = { isStopping: false };
38985
+ const readHeldBytes = () => [...locks.values()].reduce((total, held) => total + held.lockedBytes, 0) + [...sizedBytes.values()].reduce((total, bytes) => total + bytes, 0);
38986
+ const runLock = async (target) => {
38987
+ const key = target.key.slice(0, 12);
38988
+ try {
38989
+ const result = await deps.lockMemory(target.memFile, (lockBytes) => {
38990
+ const isAdmitted = canAdmitLock({
38991
+ heldBytes: readHeldBytes(),
38992
+ lockBytes,
38993
+ ramBudgetMib: deps.ramBudgetMib
38994
+ });
38995
+ if (isAdmitted) {
38996
+ sizedBytes.set(target.buildId, lockBytes);
38997
+ }
38998
+ return isAdmitted;
38999
+ });
39000
+ match6(pickLockOutcome(result, deps.probeCurrent(target))).with({ kind: "failed" }, (failed) => {
39001
+ deps.logger.warning("boot template {key} memory not locked (errno {errno}): a restore reads what the host dropped", { key, errno: String(failed.errno) });
39002
+ }).with({ kind: "declined" }, (declined) => {
39003
+ deps.logger.info("boot template {key} memory not locked: {lockMib} MiB would take the locks past a tenth of the RAM budget", { key, lockMib: String(declined.lockMib) });
39004
+ }).with({ kind: "stale" }, (stale) => {
39005
+ stale.lock.release();
39006
+ }).with({ kind: "held" }, (held) => {
39007
+ locks.set(target.key, held.lock);
39008
+ deps.logger.info("boot template {key} memory locked: {lockMib} MiB", {
39009
+ key,
39010
+ lockMib: String(held.lockMib)
39011
+ });
39012
+ }).exhaustive();
39013
+ } catch (error) {
39014
+ deps.logger.warning("boot template {key} memory not locked: {error}", {
39015
+ key,
39016
+ error: getErrorMessage(error)
39017
+ });
39018
+ } finally {
39019
+ sizedBytes.delete(target.buildId);
39020
+ }
39021
+ };
39022
+ return {
39023
+ lock: (target) => state.isStopping || locks.has(target.key) ? Promise.resolve() : locking.run(target.buildId, () => runLock(target)),
39024
+ release: (key) => {
39025
+ locks.get(key)?.release();
39026
+ locks.delete(key);
39027
+ },
39028
+ readLockedMib: () => Math.ceil(readHeldBytes() / MIB5),
39029
+ stop: async () => {
39030
+ state.isStopping = true;
39031
+ await locking.waitForAll();
39032
+ for (const held of locks.values()) {
39033
+ held.release();
39034
+ }
39035
+ locks.clear();
39036
+ }
39037
+ };
39038
+ }
39039
+
38950
39040
  // packages/daemon/src/vm/templates/boot-templates.ts
38951
39041
  var TEMPLATE_BOOT_ARGS = [...BASE_BOOT_ARGS, "imp.template=1"].join(" ");
38952
39042
  var TEMPLATE_ADDRESS = {
@@ -38965,7 +39055,7 @@ var BUILD_JAIL_ID = "tpl-build";
38965
39055
  var BUILD_CPU = { limit: null, weight: 100 };
38966
39056
  var PLACEHOLDER_BYTES = 1024 * 1024;
38967
39057
  var PLACEHOLDER_NAME = "placeholder.ext4";
38968
- var MIB3 = 1024 * 1024;
39058
+ var MIB6 = 1024 * 1024;
38969
39059
  var MIN_MISSES = 2;
38970
39060
  var MAX_TEMPLATES = 4;
38971
39061
  var MAX_BUILD_FAILURES = 3;
@@ -39039,6 +39129,7 @@ function createBootTemplates(deps) {
39039
39129
  };
39040
39130
  const listTemplateKeys = () => existsSync9(root) ? readdirSync3(root).filter((name) => name !== PLACEHOLDER_NAME && !name.startsWith(".")) : [];
39041
39131
  const removeDir = (name) => {
39132
+ templateLocks.release(name);
39042
39133
  const gone = join18(root, `.gone-${Bun.randomUUIDv7()}`);
39043
39134
  try {
39044
39135
  renameSync3(join18(root, name), gone);
@@ -39048,6 +39139,12 @@ function createBootTemplates(deps) {
39048
39139
  rmSync15(gone, { recursive: true, force: true });
39049
39140
  lastUsed.delete(name);
39050
39141
  };
39142
+ const templateLocks = createTemplateLocks({
39143
+ lockMemory: deps.lockMemory,
39144
+ probeCurrent: (target) => readMeta(target.key)?.buildId === target.buildId,
39145
+ ramBudgetMib: deps.ramBudgetMib,
39146
+ logger: getImpdLogger("boot")
39147
+ });
39051
39148
  const readLastUsed = (key) => {
39052
39149
  const used = lastUsed.get(key);
39053
39150
  if (used !== undefined) {
@@ -39138,7 +39235,7 @@ function createBootTemplates(deps) {
39138
39235
  key: key.slice(0, 12),
39139
39236
  durationMs: String(durationMs)
39140
39237
  });
39141
- return {
39238
+ const files = {
39142
39239
  key,
39143
39240
  buildId,
39144
39241
  vmstate: join18(root, key, "vmstate"),
@@ -39146,6 +39243,8 @@ function createBootTemplates(deps) {
39146
39243
  systemDrivePath: meta.systemDrivePath,
39147
39244
  placeholderPath: placeholder
39148
39245
  };
39246
+ templateLocks.lock(files);
39247
+ return files;
39149
39248
  };
39150
39249
  const runBuild = async (shape, key) => {
39151
39250
  const work = join18(root, `.build-${Bun.randomUUIDv7()}`);
@@ -39164,7 +39263,7 @@ function createBootTemplates(deps) {
39164
39263
  memoryMib: shape.memoryMib,
39165
39264
  maySleepImps: false
39166
39265
  });
39167
- return await (deps.diskBudget === undefined ? writeAdmitted() : deps.diskBudget.withRoom(shape.memoryMib * MIB3, writeAdmitted));
39266
+ return await (deps.diskBudget === undefined ? writeAdmitted() : deps.diskBudget.withRoom(shape.memoryMib * MIB6, writeAdmitted));
39168
39267
  } catch (error) {
39169
39268
  if (progress.isBuilding) {
39170
39269
  throw error;
@@ -39279,10 +39378,98 @@ function createBootTemplates(deps) {
39279
39378
  const meta = readMeta(key);
39280
39379
  return meta === null ? [] : [meta.systemDrivePath];
39281
39380
  }),
39282
- stop: () => building.waitForAll()
39381
+ lockKept: async () => {
39382
+ for (const key of listTemplateKeys()) {
39383
+ const ready = readReady(key);
39384
+ if (ready !== null) {
39385
+ await templateLocks.lock(ready);
39386
+ }
39387
+ }
39388
+ },
39389
+ readLockedMib: templateLocks.readLockedMib,
39390
+ stop: async () => {
39391
+ await Promise.all([templateLocks.stop(), building.waitForAll()]);
39392
+ }
39283
39393
  };
39284
39394
  }
39285
39395
 
39396
+ // packages/daemon/src/vm/templates/create-memory-lock.ts
39397
+ import { closeSync as closeSync8, fstatSync as fstatSync3, openSync as openSync6 } from "fs";
39398
+ var PROT_READ = 1;
39399
+ var MAP_SHARED = 1;
39400
+ async function createMemoryLock(path, onSized) {
39401
+ const libc = readLibc();
39402
+ if (libc === null) {
39403
+ return { errno: -1 };
39404
+ }
39405
+ const fd = openSync6(path, "r");
39406
+ try {
39407
+ const size = fstatSync3(fd).size;
39408
+ const runs = readDataRuns(libc, fd, size);
39409
+ if ("errno" in runs) {
39410
+ return runs;
39411
+ }
39412
+ const lockedBytes = runs.reduce((total, run) => total + run.length, 0);
39413
+ if (!onSized(lockedBytes)) {
39414
+ return { declinedBytes: lockedBytes };
39415
+ }
39416
+ const address = Number(libc.mmap(0, size, PROT_READ, MAP_SHARED, fd, 0));
39417
+ if (address === -1) {
39418
+ return { errno: readErrno() };
39419
+ }
39420
+ const mapping = { isMapped: true };
39421
+ const release = () => {
39422
+ if (mapping.isMapped) {
39423
+ mapping.isMapped = false;
39424
+ libc.munmap(address, size);
39425
+ }
39426
+ };
39427
+ for (const chunk of runs.flatMap((run) => splitRun(run))) {
39428
+ if (libc.mlock(address + chunk.offset, chunk.length) !== 0) {
39429
+ const errno = readErrno();
39430
+ release();
39431
+ return { errno };
39432
+ }
39433
+ await Bun.sleep(0);
39434
+ }
39435
+ return { lock: { lockedBytes, release } };
39436
+ } finally {
39437
+ closeSync8(fd);
39438
+ }
39439
+ }
39440
+ var SEEK_DATA2 = 3;
39441
+ var SEEK_HOLE2 = 4;
39442
+ var ENXIO2 = 6;
39443
+ function readDataRuns(libc, fd, size) {
39444
+ const runs = [];
39445
+ let offset = 0;
39446
+ while (offset < size) {
39447
+ const data = Number(libc.lseek(fd, offset, SEEK_DATA2));
39448
+ if (data < 0) {
39449
+ const errno = readErrno();
39450
+ if (errno === ENXIO2) {
39451
+ break;
39452
+ }
39453
+ return { errno };
39454
+ }
39455
+ const hole = Number(libc.lseek(fd, data, SEEK_HOLE2));
39456
+ if (hole < 0) {
39457
+ return { errno: readErrno() };
39458
+ }
39459
+ const end = Math.min(hole, size);
39460
+ runs.push({ offset: data, length: end - data });
39461
+ offset = end;
39462
+ }
39463
+ return runs;
39464
+ }
39465
+ var CHUNK_BYTES = 4 * 1024 * 1024;
39466
+ function splitRun(run) {
39467
+ return Array.from({ length: Math.ceil(run.length / CHUNK_BYTES) }, (_unused, index) => {
39468
+ const offset = run.offset + index * CHUNK_BYTES;
39469
+ return { offset, length: Math.min(CHUNK_BYTES, run.offset + run.length - offset) };
39470
+ });
39471
+ }
39472
+
39286
39473
  // packages/daemon/src/vm/vmm/vm-stats.ts
39287
39474
  import { readFileSync as readFileSync19, readdirSync as readdirSync4 } from "fs";
39288
39475
  import { readFile as readFile3 } from "fs/promises";
@@ -40855,7 +41042,9 @@ function createDefaultTemplates(deps, cgroups) {
40855
41042
  kernelPath: deps.config.kernelPath,
40856
41043
  minGuestUptimeMs: deps.config.sleepMinGuestUptimeMs,
40857
41044
  bootReservePercent: deps.config.bootReservePercent,
41045
+ ramBudgetMib: deps.config.ramBudgetMib,
40858
41046
  buildVm: deps.vms.createTemplateVm,
41047
+ lockMemory: createMemoryLock,
40859
41048
  taps: deps.taps,
40860
41049
  jail: deps.config.jailerBin === null ? null : { uid: TEMPLATE_BUILD_UID, gid: TEMPLATE_BUILD_UID },
40861
41050
  cgroups,
@@ -42464,12 +42653,12 @@ function readNumber(parsed, key) {
42464
42653
  // packages/daemon/src/vm/vmm/jail.ts
42465
42654
  import {
42466
42655
  chmodSync as chmodSync6,
42467
- closeSync as closeSync8,
42656
+ closeSync as closeSync9,
42468
42657
  existsSync as existsSync12,
42469
42658
  lchownSync as lchownSync2,
42470
42659
  lstatSync as lstatSync3,
42471
42660
  mkdirSync as mkdirSync14,
42472
- openSync as openSync6,
42661
+ openSync as openSync7,
42473
42662
  readdirSync as readdirSync7,
42474
42663
  rmSync as rmSync19,
42475
42664
  statSync as statSync9,
@@ -42558,7 +42747,7 @@ function createJails(deps) {
42558
42747
  };
42559
42748
  const setupReadOnlyBind = async (source, target) => {
42560
42749
  mkdirSync14(dirname9(target), { recursive: true });
42561
- closeSync8(openSync6(target, "a"));
42750
+ closeSync9(openSync7(target, "a"));
42562
42751
  await runMount(["mount", "--bind", source, target]);
42563
42752
  await runMount(["mount", "-o", "remount,bind,ro,nosuid,nodev", target]);
42564
42753
  };
@@ -42705,7 +42894,7 @@ function setupOwnership(paths, user, isDiskLate) {
42705
42894
  }
42706
42895
  function setupScratchFile(source, target, user) {
42707
42896
  mkdirSync14(dirname9(target), { recursive: true });
42708
- closeSync8(openSync6(target, "wx", 384));
42897
+ closeSync9(openSync7(target, "wx", 384));
42709
42898
  truncateSync3(target, statSync9(source).size);
42710
42899
  lchownSync2(target, user.uid, user.gid);
42711
42900
  }
@@ -44320,6 +44509,7 @@ function createImpdImps(config, deps, parts) {
44320
44509
  readRamMib: readRamMibAsync,
44321
44510
  readResidentMib,
44322
44511
  readHeadroomMib,
44512
+ readLockedMib: () => imps.bootTemplates?.readLockedMib() ?? 0,
44323
44513
  isBusy,
44324
44514
  trySleepImp: imps.trySleepImp,
44325
44515
  readDiskFullError: imps.readDiskFullError,
@@ -45796,7 +45986,7 @@ function readSettled2(row, imp) {
45796
45986
 
45797
45987
  // packages/daemon/src/imp-ops/moves/move-sender.ts
45798
45988
  import invariant4 from "tiny-invariant";
45799
- import { match as match5 } from "ts-pattern";
45989
+ import { match as match7 } from "ts-pattern";
45800
45990
 
45801
45991
  // packages/daemon/src/platform/db/move-send-steps.ts
45802
45992
  var MOVE_SEND_STEPS = [
@@ -46358,7 +46548,7 @@ function createMoveSender(deps) {
46358
46548
  };
46359
46549
  const recoverSend = async (imp, row) => {
46360
46550
  const log = getImpdLogger("move").with({ imp: imp.name });
46361
- await match5(pickSendRecovery(imp.moveState, row?.step)).with("unmark", () => removeMark(imp)).with("wait", () => {
46551
+ await match7(pickSendRecovery(imp.moveState, row?.step)).with("unmark", () => removeMark(imp)).with("wait", () => {
46362
46552
  log.info("a prepared send waits for its send after a restart");
46363
46553
  }).with("abort", async () => {
46364
46554
  const isUndone = await resetSend(imp, row?.peer_url ?? null, row?.ticket ?? null);
@@ -46480,7 +46670,7 @@ function buildPublicError(name) {
46480
46670
  }
46481
46671
  function pickSendRecovery(moveState, step) {
46482
46672
  if (moveState === "sending") {
46483
- return match5(step).with(undefined, () => "unmark").with("prepared", () => "wait").with("streaming", () => "abort").with("sent", () => "commit").exhaustive();
46673
+ return match7(step).with(undefined, () => "unmark").with("prepared", () => "wait").with("streaming", () => "abort").with("sent", () => "commit").exhaustive();
46484
46674
  }
46485
46675
  return moveState === "moved" && step === "sent" ? "commit" : "none";
46486
46676
  }
@@ -46532,7 +46722,7 @@ function createMoveService(deps) {
46532
46722
  // packages/daemon/src/surface/moves/create-move-peer-handler.ts
46533
46723
  import { OpenAPIHandler } from "@orpc/openapi/fetch";
46534
46724
  import { ORPCError as ORPCError7, implement as implement3, onError as onError3 } from "@orpc/server";
46535
- import { match as match6 } from "ts-pattern";
46725
+ import { match as match8 } from "ts-pattern";
46536
46726
  function createMovePeerHandler(deps) {
46537
46727
  const withTicket = async (step, context, call) => {
46538
46728
  const startedAt = deps.now();
@@ -46583,7 +46773,7 @@ function createMovePeerHandler(deps) {
46583
46773
  const respondReceive = async (context) => {
46584
46774
  try {
46585
46775
  const received = await withTicket("receive", context, (checked) => deps.calls.receive(checked, context.request));
46586
- return match6(received).with({ kind: "part" }, (sent) => Response.json({ part: sent.part }, { status: 202 })).with({ kind: "receipt" }, (sent) => Response.json(sent.receipt)).exhaustive();
46776
+ return match8(received).with({ kind: "part" }, (sent) => Response.json({ part: sent.part }, { status: 202 })).with({ kind: "receipt" }, (sent) => Response.json(sent.receipt)).exhaustive();
46587
46777
  } catch (error) {
46588
46778
  const failure = toPeerError(error);
46589
46779
  return Response.json(encodePeerError(failure), { status: failure.status });
@@ -47296,9 +47486,9 @@ function printSweep(logger, result, orphans) {
47296
47486
  }
47297
47487
  logger.info("kept {length} orphans the database does not name; `impz gc --orphans --dry-run` lists what `impz gc --orphans` would retire", { length: String(result.kept.length) });
47298
47488
  }
47299
- var MIB4 = 1024 ** 2;
47489
+ var MIB7 = 1024 ** 2;
47300
47490
  function formatOrphan(orphan) {
47301
- const size = `${(orphan.bytes / MIB4).toFixed(1)} MiB`;
47491
+ const size = `${(orphan.bytes / MIB7).toFixed(1)} MiB`;
47302
47492
  const created = orphan.createdAt?.toISOString() ?? "unknown";
47303
47493
  const snapshots = orphan.snapshots.length === 0 ? "none" : orphan.snapshots.join(", ");
47304
47494
  return `orphan ${orphan.kind} ${orphan.id} (${orphan.location}): ${size}, created ${created}, snapshots: ${snapshots}`;
@@ -48781,11 +48971,11 @@ class ImageLimitError extends ImpdError {
48781
48971
 
48782
48972
  // packages/daemon/src/imps/images/rootfs/rootfs-plan.ts
48783
48973
  var GIB3 = 1024 ** 3;
48784
- var MIB5 = 1024 ** 2;
48974
+ var MIB8 = 1024 ** 2;
48785
48975
  var ROOTFS_MIN_BYTES = 4 * GIB3;
48786
48976
  var ROOTFS_SPARE_BYTES = 2 * GIB3;
48787
48977
  var BYTES_PER_INODE = 16384;
48788
- var ROOTFS_OVERHEAD_MIN_BYTES = 128 * MIB5;
48978
+ var ROOTFS_OVERHEAD_MIN_BYTES = 128 * MIB8;
48789
48979
  var ROOTFS_OVERHEAD_SHARE = 64;
48790
48980
  function planRootfs(tree) {
48791
48981
  const wanted = Math.ceil((tree.bytes * 1.2 + ROOTFS_SPARE_BYTES) / GIB3) * GIB3;
@@ -49441,7 +49631,7 @@ async function readOciConfig(ref, signal, run) {
49441
49631
  }
49442
49632
 
49443
49633
  // packages/daemon/src/imps/images/rootfs/write-image-config.ts
49444
- import { closeSync as closeSync9, constants as constants5, lstatSync as lstatSync5, mkdirSync as mkdirSync21, openSync as openSync7, rmSync as rmSync25, writeSync as writeSync3 } from "fs";
49634
+ import { closeSync as closeSync10, constants as constants5, lstatSync as lstatSync5, mkdirSync as mkdirSync21, openSync as openSync8, rmSync as rmSync25, writeSync as writeSync3 } from "fs";
49445
49635
  import { join as join29 } from "path";
49446
49636
  function writeImageConfig(root, contents) {
49447
49637
  const etc = join29(root, "etc");
@@ -49450,11 +49640,11 @@ function writeImageConfig(root, contents) {
49450
49640
  createDirectory(directory, "/etc/imp");
49451
49641
  const file = join29(directory, "image.json");
49452
49642
  rmSync25(file, { recursive: true, force: true });
49453
- const fd = openSync7(file, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, 420);
49643
+ const fd = openSync8(file, constants5.O_WRONLY | constants5.O_CREAT | constants5.O_EXCL | constants5.O_NOFOLLOW, 420);
49454
49644
  try {
49455
49645
  writeSync3(fd, contents);
49456
49646
  } finally {
49457
- closeSync9(fd);
49647
+ closeSync10(fd);
49458
49648
  }
49459
49649
  }
49460
49650
  function createDirectory(path, name) {
@@ -50843,14 +51033,14 @@ function expandScopes(asked) {
50843
51033
  }
50844
51034
 
50845
51035
  // packages/daemon/src/platform/http/render-html.ts
50846
- import { P as P3, match as match7 } from "ts-pattern";
51036
+ import { P as P4, match as match9 } from "ts-pattern";
50847
51037
  var MARKUP = Symbol("markup");
50848
51038
  function renderHTML(strings, ...parts) {
50849
51039
  const html = parts.reduce((text, part, index) => `${text}${toHTML(part)}${strings[index + 1] ?? ""}`, strings[0] ?? "");
50850
51040
  return { [MARKUP]: true, html };
50851
51041
  }
50852
51042
  function toHTML(part) {
50853
- return match7(part).with(P3.string, (text) => escapeHTML(text)).with(P3.array(), (list) => list.map((each) => each.html).join("")).with({ html: P3.string }, (markup) => markup.html).exhaustive();
51043
+ return match9(part).with(P4.string, (text) => escapeHTML(text)).with(P4.array(), (list) => list.map((each) => each.html).join("")).with({ html: P4.string }, (markup) => markup.html).exhaustive();
50854
51044
  }
50855
51045
  function escapeHTML(text) {
50856
51046
  return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
@@ -57034,6 +57224,7 @@ async function startImpdParts(config, boundaries, plan) {
57034
57224
  key: key.slice(0, 12)
57035
57225
  });
57036
57226
  }
57227
+ imps.bootTemplates?.lockKept();
57037
57228
  });
57038
57229
  await runStep("system-drives", async () => {
57039
57230
  const removed = await removeUnusedDrives(db, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@impz/testing",
3
- "version": "6.0.0",
3
+ "version": "6.0.1",
4
4
  "description": "A real impd on stand-ins, for Bun tests of code that drives impd through @impz/client",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,7 +22,7 @@
22
22
  "@bufbuild/protobuf": "^2.16.0",
23
23
  "@logtape/logtape": "^2.4.2",
24
24
  "@modelcontextprotocol/server": "^2.3.1",
25
- "@opentelemetry/api": "^1.9.1",
25
+ "@opentelemetry/api": "^1.10.0",
26
26
  "@orpc/client": "^1.15.7",
27
27
  "@orpc/contract": "^1.15.7",
28
28
  "@orpc/openapi": "^1.15.7",
@@ -42,14 +42,14 @@
42
42
  "ts-pattern": "^5.9.2",
43
43
  "xstate": "^5.33.3",
44
44
  "zod": "^4.6.7",
45
- "@impz/client": "6.0.0"
45
+ "@impz/client": "6.0.1"
46
46
  },
47
47
  "engines": {
48
48
  "bun": ">=1.4.3"
49
49
  },
50
50
  "peerDependencies": {
51
- "@opentelemetry/exporter-metrics-otlp-proto": "^0.223.0",
52
- "@opentelemetry/exporter-trace-otlp-proto": "^0.223.0",
51
+ "@opentelemetry/exporter-metrics-otlp-proto": "^0.300.0",
52
+ "@opentelemetry/exporter-trace-otlp-proto": "^0.300.0",
53
53
  "@opentelemetry/resources": "^2.12.0",
54
54
  "@opentelemetry/sdk-metrics": "^2.12.0",
55
55
  "@opentelemetry/sdk-trace-base": "^2.12.0",