@parcha/pi-durable-disk 0.1.0-beta.14 → 0.1.0-beta.16

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.
@@ -15,11 +15,38 @@
15
15
  // in it, the commands a crashed incarnation detached included. The supervisor holds the API key; boxes never do.
16
16
  import { DEFAULT_MOUNT_ROOT, runPath } from "../claim.js";
17
17
  import { PdaError } from "../errors.js";
18
+ import { attemptEnv } from "./attempt-env.js";
18
19
  import { LAUNCH_DIR } from "./daytona-launch.js";
19
20
  import { TERMINAL_EXITS } from "./local-host.js";
20
21
  export const DAYTONA_API_URL = "https://app.daytona.io/api";
21
22
  export const LABEL_FLEET = "pda-fleet";
22
23
  export const LABEL_RUN = "pda-run";
24
+ /**
25
+ * Daytona's published list prices, as its pricing page gave them on `readOn`: billed per second, quoted per hour, for
26
+ * each vCPU, each GiB of memory and each GiB of disk. The page's footnote "Price per GiB after first 5 free" is applied
27
+ * to storage (disk) only: its placement is ambiguous and could be read as covering memory too, so this is a choice.
28
+ * The page lists GPU rates per GPU model; this driver does not map a box's GPU to a model, so a GPU box is unpriced.
29
+ */
30
+ export const DAYTONA_LIST_PRICES = {
31
+ page: "https://www.daytona.io/pricing",
32
+ readOn: "2026-10-16",
33
+ vcpuPerHour: 0.0504,
34
+ memoryGiBPerHour: 0.0162,
35
+ diskGiBPerHour: 0.000108,
36
+ freeDiskGiB: 5,
37
+ };
38
+ /**
39
+ * A box's list price per second at DAYTONA_LIST_PRICES, from its size; null when its size is unknown or it has a GPU
40
+ * (priced per GPU model on the page, which this driver does not map).
41
+ */
42
+ export function daytonaUsdPerSecond(size) {
43
+ const { cpu, memory, disk, gpu } = size;
44
+ const count = (n) => typeof n === "number" && Number.isFinite(n) && n >= 0;
45
+ if (!count(cpu) || !count(memory) || !count(disk) || (gpu !== undefined && gpu !== 0))
46
+ return null;
47
+ const p = DAYTONA_LIST_PRICES;
48
+ return (cpu * p.vcpuPerHour + memory * p.memoryGiBPerHour + Math.max(0, disk - p.freeDiskGiB) * p.diskGiBPerHour) / 3600;
49
+ }
23
50
  export class DaytonaApiError extends PdaError {
24
51
  /** The HTTP status, null when no response arrived (network failure or timeout). */
25
52
  status;
@@ -255,13 +282,22 @@ export function daytonaHost(opts) {
255
282
  throw new DaytonaHostError("START_FAILED", `${what} in ${box.name} exited ${r.exitCode}: ${r.result.trim().split("\n").at(-1)?.slice(0, 300) ?? ""}`);
256
283
  return r;
257
284
  }
285
+ /** Each box this driver made, for its receipt: what Daytona said of it, when it was made, and when it ended. */
286
+ const made = new Map();
287
+ const ended = (id) => {
288
+ const m = made.get(id);
289
+ if (m && m.to === null)
290
+ m.to = new Date(now()).toISOString();
291
+ };
258
292
  return {
259
293
  fleet,
260
294
  mountRoot,
261
- async start(ref, mountToken) {
295
+ kills: "box",
296
+ async start(ref, mountToken, attempt) {
262
297
  runPath(ref.id);
263
298
  if (!mountToken)
264
299
  throw new DaytonaHostError("INVALID_ARGUMENT", "no mount token");
300
+ const extra = attemptEnv(attempt, (m) => new DaytonaHostError("INVALID_ARGUMENT", m));
265
301
  const t0 = now();
266
302
  const name = sandboxName(prefix, ref.id, t0);
267
303
  const body = {
@@ -276,8 +312,10 @@ export function daytonaHost(opts) {
276
312
  ...(opts.network?.allowList ? { networkAllowList: opts.network.allowList } : {}),
277
313
  };
278
314
  let box = await createOrAdopt(body, ref.id);
315
+ made.set(box.id, { info: box, from: box.createdAt ?? new Date(t0).toISOString(), to: null });
279
316
  try {
280
317
  box = await waitStarted(box, t0 + startTimeoutMs);
318
+ made.get(box.id).info = { ...made.get(box.id).info, ...box };
281
319
  const prepared = now();
282
320
  if (opts.prepare)
283
321
  await opts.prepare(box, client);
@@ -285,7 +323,7 @@ export function daytonaHost(opts) {
285
323
  const handle = { driver: "daytona", fleet, target: box.target ?? opts.target ?? null, sandboxId: box.id, name, mountpoint: `${mountRoot}/${runPath(ref.id)}` };
286
324
  const spec = {
287
325
  argv: [...command, "run", "--disk", ref.disk, "--region", ref.region, "--id", ref.id, "--mount-root", mountRoot, "--archil", archil, ...(opts.runArgs ?? []), "--token-stdin"],
288
- env: { ...opts.env, PDA_HOLDER: JSON.stringify(handle) },
326
+ env: { ...opts.env, ...extra, PDA_HOLDER: JSON.stringify(handle) },
289
327
  user,
290
328
  group,
291
329
  restart: opts.restart !== false,
@@ -305,7 +343,9 @@ export function daytonaHost(opts) {
305
343
  return handle;
306
344
  }
307
345
  catch (err) {
346
+ // The box was made and is deleted now: its receipt ends here, deleted or not (a delete that failed is the sweep's).
308
347
  await client.remove(box.id).catch(() => { });
348
+ ended(box.id);
309
349
  if (err instanceof DaytonaHostError)
310
350
  throw err;
311
351
  throw new DaytonaHostError("START_FAILED", `starting an instance in ${name} failed: ${err.message}`, { cause: err });
@@ -321,8 +361,10 @@ export function daytonaHost(opts) {
321
361
  catch (err) {
322
362
  throw new DaytonaHostError("DAYTONA_API_FAILED", `reading sandbox ${h.name}: ${err.message}`, { cause: err });
323
363
  }
324
- if (!box)
364
+ if (!box) {
365
+ ended(h.sandboxId);
325
366
  return "gone";
367
+ }
326
368
  // The label, not the id in a handle, decides whose box this is: a handle can never reach another fleet's box.
327
369
  if (box.labels?.[LABEL_FLEET] !== fleet)
328
370
  return "unknown";
@@ -363,6 +405,53 @@ export function daytonaHost(opts) {
363
405
  throw new DaytonaHostError("STOP_FAILED", `deleting sandbox ${h.name} failed: ${err.message}`, { cause: first });
364
406
  }
365
407
  }
408
+ ended(id);
409
+ },
410
+ async kill(h) {
411
+ if (!mine(h))
412
+ return;
413
+ const id = h.sandboxId;
414
+ let box;
415
+ try {
416
+ box = await client.get(id);
417
+ }
418
+ catch (err) {
419
+ throw new DaytonaHostError("KILL_FAILED", `reading sandbox ${h.name} to confirm it is this fleet's: ${err.message}`, { cause: err });
420
+ }
421
+ if (box === null || box.labels?.[LABEL_FLEET] !== fleet)
422
+ return;
423
+ // The box goes with everything in it, the instance and its mount: no launcher stop, no drain.
424
+ try {
425
+ await client.remove(id);
426
+ }
427
+ catch (first) {
428
+ // A delete refused mid-transition: power the box off, then delete again.
429
+ await client.stop(id, true).catch(() => { });
430
+ try {
431
+ await client.remove(id);
432
+ }
433
+ catch (err) {
434
+ throw new DaytonaHostError("KILL_FAILED", `deleting sandbox ${h.name} failed: ${err.message}`, { cause: first });
435
+ }
436
+ }
437
+ ended(id);
438
+ },
439
+ box(h) {
440
+ if (!mine(h))
441
+ return null;
442
+ const m = made.get(h.sandboxId);
443
+ if (!m)
444
+ return null;
445
+ const { cpu, gpu, memory, disk } = m.info;
446
+ const usdPerSecond = daytonaUsdPerSecond(m.info);
447
+ const size = [cpu, memory, disk].every((n) => typeof n === "number") ? ` ${cpu} vCPU, ${memory} GiB memory, ${disk} GiB disk${gpu ? `, ${gpu} GPU` : ""}` : "";
448
+ return {
449
+ provider: "daytona",
450
+ kind: `sandbox${size}`,
451
+ from: m.from,
452
+ to: m.to,
453
+ ...(usdPerSecond === null ? {} : { usdPerSecond, source: DAYTONA_LIST_PRICES.page, priced: DAYTONA_LIST_PRICES.readOn }),
454
+ };
366
455
  },
367
456
  };
368
457
  }
@@ -58,7 +58,7 @@ export interface DockerHostOptions {
58
58
  exec?: DockerRunner;
59
59
  }
60
60
  export declare class DockerHostError extends PdaError {
61
- constructor(code: "INVALID_ARGUMENT" | "START_FAILED" | "STOP_FAILED" | "DOCKER_FAILED", message: string, options?: {
61
+ constructor(code: "INVALID_ARGUMENT" | "START_FAILED" | "STOP_FAILED" | "KILL_FAILED" | "DOCKER_FAILED", message: string, options?: {
62
62
  cause?: unknown;
63
63
  });
64
64
  }
@@ -95,7 +95,7 @@ export type ContainerInfo = {
95
95
  startedAt: string | null;
96
96
  finishedAt: string | null;
97
97
  };
98
- export declare function dockerHost(opts?: DockerHostOptions): HostDriver & {
98
+ export declare function dockerHost(opts?: DockerHostOptions): HostDriver & Required<Pick<HostDriver, "kill" | "kills">> & {
99
99
  readonly fleet: string;
100
100
  readonly mountRoot: string;
101
101
  describe(handle: HostHandle): Promise<ContainerInfo | null>;
@@ -23,6 +23,7 @@ import { dirname, isAbsolute, join, posix, relative, resolve, sep } from "node:p
23
23
  import { DEFAULT_MOUNT_ROOT, runPath } from "../claim.js";
24
24
  import { PdaError } from "../errors.js";
25
25
  import { LOCAL_PARK_THRESHOLD_MS } from "../park.js";
26
+ import { attemptEnv } from "./attempt-env.js";
26
27
  export class DockerHostError extends PdaError {
27
28
  constructor(code, message, options = {}) {
28
29
  super(code, message, options);
@@ -191,8 +192,8 @@ export function dockerHost(opts = {}) {
191
192
  if (!ok(r) && !NO_SUCH.test(r.stderr))
192
193
  throw new DockerHostError("DOCKER_FAILED", `docker rm -f ${ref}: ${firstLine(r)}`);
193
194
  }
194
- function createArgs(ref, name, attempt, holder, profile) {
195
- const env = { ...opts.env, PDA_HOLDER: JSON.stringify(holder) };
195
+ function createArgs(ref, name, attempt, holder, profile, extra) {
196
+ const env = { ...opts.env, ...extra, PDA_HOLDER: JSON.stringify(holder) };
196
197
  const runFlags = [
197
198
  "run",
198
199
  "--disk", ref.disk,
@@ -228,10 +229,10 @@ export function dockerHost(opts = {}) {
228
229
  ...runFlags,
229
230
  ];
230
231
  }
231
- async function create(ref, name, attempt, holder) {
232
+ async function create(ref, name, attempt, holder, extra) {
232
233
  const profile = await apparmor();
233
234
  for (let tries = 0;; tries++) {
234
- const r = await run(createArgs(ref, name, attempt, holder, profile), { timeoutMs: 120_000 });
235
+ const r = await run(createArgs(ref, name, attempt, holder, profile, extra), { timeoutMs: 120_000 });
235
236
  if (ok(r))
236
237
  return { id: r.stdout.trim().split("\n").at(-1).trim() };
237
238
  if (!IN_USE.test(r.stderr) || tries > 0)
@@ -270,6 +271,7 @@ export function dockerHost(opts = {}) {
270
271
  return {
271
272
  fleet,
272
273
  mountRoot,
274
+ kills: "box",
273
275
  async start(ref, token, attemptInfo) {
274
276
  runPath(ref.id);
275
277
  if (!opts.image)
@@ -277,11 +279,12 @@ export function dockerHost(opts = {}) {
277
279
  if (!token || /[\r\n]/.test(token))
278
280
  throw new DockerHostError("INVALID_ARGUMENT", "no mount token, or one with a line break");
279
281
  const attempt = attemptInfo?.attempt ?? null;
282
+ const extra = attemptEnv(attemptInfo, (m) => new DockerHostError("INVALID_ARGUMENT", m));
280
283
  const name = `${prefix}${ref.id}-${diskKey(ref)}-${attempt === null ? `t${Date.now().toString(36)}` : `g${attempt}`}`;
281
284
  if (!NAME_SAFE.test(name))
282
285
  throw new DockerHostError("INVALID_ARGUMENT", `container name ${name} is not safe`);
283
286
  const handle = { driver: "docker", fleet, daemon: await daemon(), name, mountpoint: posix.join(mountRoot, runPath(ref.id)), image: opts.image };
284
- const made = await create(ref, name, attempt, handle);
287
+ const made = await create(ref, name, attempt, handle, extra);
285
288
  // An earlier start of this attempt made the container: the token given to this start was not used.
286
289
  if ("adopted" in made)
287
290
  return { ...handle, adopted: true };
@@ -336,6 +339,20 @@ export function dockerHost(opts = {}) {
336
339
  if (await inspect(ref))
337
340
  throw new DockerHostError("STOP_FAILED", `container ${ref} is still there after docker rm -f`);
338
341
  },
342
+ async kill(h) {
343
+ if (!(await mine(h)))
344
+ return;
345
+ const ref = target(h);
346
+ const found = await inspect(ref);
347
+ if (!found || found.labels["pda.fleet"] !== fleet)
348
+ return;
349
+ // SIGKILL, no stop timeout: the container dies with its mount (its own namespace), as on a power loss. It is left
350
+ // exited, not removed; the next start of the run removes it.
351
+ const r = await run(["kill", ref], { timeoutMs: 30_000 });
352
+ if (!ok(r) && !NO_SUCH.test(r.stderr) && (await inspect(ref).then((f) => f && containerStatus(f.state) === "running"))) {
353
+ throw new DockerHostError("KILL_FAILED", `docker kill ${ref}: ${firstLine(r)}`);
354
+ }
355
+ },
339
356
  };
340
357
  }
341
358
  /** The bind mount of the app's directory (read-only, its node_modules hidden) and the module's path inside the container. */
@@ -50,7 +50,7 @@ export interface LocalHostOptions {
50
50
  procMounts?: string;
51
51
  }
52
52
  export declare class LocalHostError extends PdaError {
53
- constructor(code: "INVALID_ARGUMENT" | "START_FAILED" | "STOP_FAILED", message: string, options?: {
53
+ constructor(code: "INVALID_ARGUMENT" | "START_FAILED" | "STOP_FAILED" | "KILL_FAILED", message: string, options?: {
54
54
  cause?: unknown;
55
55
  });
56
56
  }
@@ -66,7 +66,7 @@ export declare function currentBootId(): string;
66
66
  export declare function parseShow(text: string): Record<string, string>;
67
67
  /** A unit's state as a host status: loaded and active (or restarting) is running; unloaded is gone. */
68
68
  export declare function unitStatus(show: Record<string, string>): HostStatus;
69
- export declare function localHost(opts?: LocalHostOptions): HostDriver & {
69
+ export declare function localHost(opts?: LocalHostOptions): HostDriver & Required<Pick<HostDriver, "kill" | "kills">> & {
70
70
  readonly hostName: string;
71
71
  readonly mountRoot: string;
72
72
  };
@@ -20,6 +20,7 @@ import { ARCHIL_SCOPED, DEAD_CONNECTION, DEFAULT_MOUNT_ROOT, runPath } from "../
20
20
  import { EXIT_FENCED, EXIT_HELD, PdaError } from "../errors.js";
21
21
  import { LOCAL_PARK_THRESHOLD_MS } from "../park.js";
22
22
  import { processTable, thisBoot } from "../process-table.js";
23
+ import { attemptEnv } from "./attempt-env.js";
23
24
  export class LocalHostError extends PdaError {
24
25
  constructor(code, message, options = {}) {
25
26
  super(code, message, options);
@@ -161,13 +162,13 @@ export function localHost(opts = {}) {
161
162
  return rm(path, { force: true });
162
163
  await root(["/bin/rm", "-f", path]);
163
164
  }
164
- async function startUnit(ref, token) {
165
+ async function startUnit(ref, token, extra) {
165
166
  const unit = `${prefix}${ref.id}-${Date.now().toString(36)}${Math.floor(Math.random() * 1296).toString(36).padStart(2, "0")}`;
166
167
  if (!UNIT_SAFE.test(unit))
167
168
  throw new LocalHostError("INVALID_ARGUMENT", `unit name ${unit} is not safe`);
168
169
  const mountpoint = mountpointOf(ref);
169
170
  const handle = { driver: "local", mode: "systemd", host: hostName, bootId, unit, mountpoint };
170
- const env = { ...opts.env, PATH: SAFE_PATH, PDA_HOLDER: holderEnv(handle) };
171
+ const env = { ...opts.env, ...extra, PATH: SAFE_PATH, PDA_HOLDER: holderEnv(handle) };
171
172
  const path = tokenFile(unit);
172
173
  await writeTokenFile(path, token);
173
174
  let started = false;
@@ -202,7 +203,7 @@ export function localHost(opts = {}) {
202
203
  await (started ? emptyTokenFile(path) : removeFile(path)).catch(() => { });
203
204
  }
204
205
  }
205
- async function startChild(ref, token) {
206
+ async function startChild(ref, token, extra) {
206
207
  const tag = `${prefix}${ref.id}-${Date.now().toString(36)}`;
207
208
  const mountpoint = mountpointOf(ref);
208
209
  const base = { driver: "local", mode: "child", host: hostName, bootId, mountpoint, tag };
@@ -211,7 +212,7 @@ export function localHost(opts = {}) {
211
212
  const { openSync } = await import("node:fs");
212
213
  out = openSync(join(opts.logDir, `${tag}.log`), "a", 0o600);
213
214
  }
214
- const env = { ...opts.env, PATH: SAFE_PATH, LANG: "C.UTF-8", HOME: process.env.HOME ?? "/", PDA_HOLDER: holderEnv(base) };
215
+ const env = { ...opts.env, ...extra, PATH: SAFE_PATH, LANG: "C.UTF-8", HOME: process.env.HOME ?? "/", PDA_HOLDER: holderEnv(base) };
215
216
  const child = spawn(command[0], [...command.slice(1), ...runFlags(ref), "--token-stdin"], { detached: true, stdio: ["pipe", out, out], env });
216
217
  await new Promise((resolve, reject) => {
217
218
  child.once("spawn", resolve);
@@ -288,9 +289,11 @@ export function localHost(opts = {}) {
288
289
  return {
289
290
  hostName,
290
291
  mountRoot,
291
- start(ref, token) {
292
+ kills: "process",
293
+ async start(ref, token, attempt) {
292
294
  runPath(ref.id);
293
- return mode === "systemd" ? startUnit(ref, token) : startChild(ref, token);
295
+ const extra = attemptEnv(attempt, (m) => new LocalHostError("INVALID_ARGUMENT", m));
296
+ return mode === "systemd" ? startUnit(ref, token, extra) : startChild(ref, token, extra);
294
297
  },
295
298
  async status(h) {
296
299
  if (!mine(h))
@@ -347,6 +350,31 @@ export function localHost(opts = {}) {
347
350
  await root([SYSTEMCTL, "reset-failed", `${h.unit}.service`]);
348
351
  await removeFile(tokenFile(h.unit));
349
352
  },
353
+ async kill(h) {
354
+ if (!mine(h) || h.bootId !== bootId)
355
+ return;
356
+ if (h.mode === "child") {
357
+ const pid = Number(h.pid);
358
+ // As in stop: only a real process, and only the one the handle names (its start ticks).
359
+ if (!Number.isInteger(pid) || pid <= 1 || typeof h.startTicks !== "number" || procStartTicks(pid) !== h.startTicks)
360
+ return;
361
+ try {
362
+ process.kill(-pid, "SIGKILL");
363
+ }
364
+ catch {
365
+ // gone already
366
+ }
367
+ return;
368
+ }
369
+ if (typeof h.unit !== "string" || !UNIT_SAFE.test(h.unit))
370
+ return;
371
+ // Every process of the unit's control group at once, the instance's commands included. Its FUSE daemon runs in a
372
+ // scope of its own and keeps the mount, as after an OOM kill; the unit restarts in place unless restart is off.
373
+ const r = await root([SYSTEMCTL, "kill", "--signal=SIGKILL", "--kill-whom=all", `${h.unit}.service`]);
374
+ if (!okRan(r) && unitStatus(await show(h.unit)) === "running") {
375
+ throw new LocalHostError("KILL_FAILED", `systemctl kill ${h.unit}: ${r.timedOut ? "timed out" : firstLine(r)}`);
376
+ }
377
+ },
350
378
  };
351
379
  }
352
380
  /**
package/dist/index.d.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  export { openDurableRun, RunError, StoreBehindSealError } from "./run.js";
2
- export type { BeforeResume, DurableRun, LeaseOptions, OpenDurableRunOptions, OpenRunLeaseOptions, OpenStep, ReleaseStep, RunClaim, RunEnv, RunErrorCode, RunLease, StoreHeadSource, StoreReads } from "./run.js";
2
+ export type { AfterClaim, BeforeResume, DurableRun, LeaseOptions, OpenDurableRunOptions, OpenRunLeaseOptions, OpenStep, ReleaseStep, RunClaim, RunEnv, RunErrorCode, RunLease, RunSeed, RunSeedDir, StoreHeadSource, StoreReads } from "./run.js";
3
3
  export { localRun } from "./local-run.js";
4
4
  export type { LocalRun } from "./local-run.js";
5
- export { CLAIM_UNMOUNTED, openClaimDir, RunRecordError } from "./status.js";
5
+ export { CLAIM_UNMOUNTED, openClaimDir, readLocalRunStatus, RunRecordError } from "./status.js";
6
6
  export type { ClaimDir, OpenClaimDirOptions, PersistRecord, RunHolder, RunRecord, RunStatus } from "./status.js";
7
7
  export { ArchilCodingTools, archilEnv, EnvClosedError, EnvError, markReadOnlySafe } from "./env.js";
8
8
  export type { ArchilEnvClaim, ArchilEnvFactory, ArchilEnvOptions, EnvErrorCode } from "./env.js";
9
9
  export { acquire, ARCHIL_SCOPED, revoke, takeOver } from "./claim.js";
10
10
  export type { AcquireOptions, ArchilHost, Claim, ControlApi, PathlessResolver, RunRef, Unmounted } from "./claim.js";
11
+ export { readReleased, withClaim } from "./with-claim.js";
12
+ export type { ClaimControl, HeldPath, WithClaimOptions } from "./with-claim.js";
11
13
  export { PathOutsideWorkError } from "./confine.js";
12
14
  export type { RefusalReason } from "./confine.js";
13
15
  export { ClaimError, FencedError, HeldError, PdaError } from "./errors.js";
@@ -16,15 +18,17 @@ export type { ArchilStore, Fence, FencedDatabase, Profile, StoreFencedError } fr
16
18
  export { AppError } from "./app.js";
17
19
  export type { AppContext, AppModule, AppOptions } from "./app.js";
18
20
  export { archilControl, deleteRunTree, ensureRunning, readRunStatus, SuperviseError } from "./supervise.js";
19
- export type { ArchilControl, CheckControl, CheckOptions, DiskClient, EnsureOptions, EnsureResult, HostDriver, HostHandle, HostStatus, Json, StartAttempt, StartReason, StonithOutcome, SuperviseErrorCode, SupervisorControl, TokenUser } from "./supervise.js";
21
+ export type { ArchilControl, CheckControl, CheckOptions, DiskClient, EnsureOptions, EnsureResult, HostBox, HostDriver, HostHandle, HostStatus, Json, StartAttempt, StartReason, StonithOutcome, SuperviseErrorCode, SupervisorControl, TokenUser } from "./supervise.js";
20
22
  export { localHost, LocalHostError } from "./hosts/local-host.js";
21
23
  export type { LocalHostOptions, Ran, Runner } from "./hosts/local-host.js";
22
24
  export { dockerHost, DockerHostError } from "./hosts/docker.js";
23
25
  export type { ContainerInfo, DockerHostOptions, DockerRan, DockerRunner } from "./hosts/docker.js";
24
- export { DaytonaApiError, daytonaHost, DaytonaHostError, daytonaRest } from "./hosts/daytona.js";
26
+ export { DAYTONA_LIST_PRICES, DaytonaApiError, daytonaHost, DaytonaHostError, daytonaRest, daytonaUsdPerSecond } from "./hosts/daytona.js";
25
27
  export type { CreateSandboxBody, DaytonaClient, DaytonaHostErrorCode, DaytonaHostOptions, DaytonaRestOptions, ExecResult, SandboxInfo } from "./hosts/daytona.js";
26
28
  export { requestRun, ServeConfigError, ServeError, serveRun } from "./serve.js";
27
29
  export type { RequestOptions, RunResponse, RunServer, ServeConfigErrorCode, ServeErrorCode, ServeOptions } from "./serve.js";
30
+ export { archilPlace, localPlace, PlaceError } from "./place.js";
31
+ export type { ArchilPlaceOptions, LocalPlaceOptions, Place, PlaceContext, PlaceErrorCode, PlaceExit, PlaceKill, PlaceOutcome, PlacePaths, PlaceRecordHome, PlaceRecovery, PlaceRunner, PlaceSignal, PlaceStatus, RecordControl, RunnerOrder } from "./place.js";
28
32
  export { busyState, drain, leaseParkTarget, watchParking } from "./park.js";
29
33
  export type { BusyState, DrainOptions, Parking, ParkOptions, ParkResult, ParkTarget } from "./park.js";
30
34
  export { fork, ForkError, forkLocal } from "./fork.js";
package/dist/index.js CHANGED
@@ -4,9 +4,11 @@
4
4
  // The run: open it, on a disk or a local directory, with its environment, its claim and the errors it fails with.
5
5
  export { openDurableRun, RunError, StoreBehindSealError } from "./run.js";
6
6
  export { localRun } from "./local-run.js";
7
- export { CLAIM_UNMOUNTED, openClaimDir, RunRecordError } from "./status.js";
7
+ export { CLAIM_UNMOUNTED, openClaimDir, readLocalRunStatus, RunRecordError } from "./status.js";
8
8
  export { ArchilCodingTools, archilEnv, EnvClosedError, EnvError, markReadOnlySafe } from "./env.js";
9
9
  export { acquire, ARCHIL_SCOPED, revoke, takeOver } from "./claim.js";
10
+ // A short exclusive claim on any path of a disk, and a consistent S3 read of a released file (an agent home beside the runs).
11
+ export { readReleased, withClaim } from "./with-claim.js";
10
12
  export { PathOutsideWorkError } from "./confine.js";
11
13
  export { ClaimError, FencedError, HeldError, PdaError } from "./errors.js";
12
14
  // The app contract: what `--app` names.
@@ -15,9 +17,11 @@ export { AppError } from "./app.js";
15
17
  export { archilControl, deleteRunTree, ensureRunning, readRunStatus, SuperviseError } from "./supervise.js";
16
18
  export { localHost, LocalHostError } from "./hosts/local-host.js";
17
19
  export { dockerHost, DockerHostError } from "./hosts/docker.js";
18
- export { DaytonaApiError, daytonaHost, DaytonaHostError, daytonaRest } from "./hosts/daytona.js";
20
+ export { DAYTONA_LIST_PRICES, DaytonaApiError, daytonaHost, DaytonaHostError, daytonaRest, daytonaUsdPerSecond } from "./hosts/daytona.js";
19
21
  // Serve: one HTTP request runs one turn.
20
22
  export { requestRun, ServeConfigError, ServeError, serveRun } from "./serve.js";
23
+ // Places: where a run lives for a supervisor that kills it and brings it back (agentrun's reference place seam).
24
+ export { archilPlace, localPlace, PlaceError } from "./place.js";
21
25
  // Parking: an idle run gives its host back.
22
26
  export { busyState, drain, leaseParkTarget, watchParking } from "./park.js";
23
27
  // Fork: a new run from a run's committed state.
package/dist/lease.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { EXIT_DATAERR, EXIT_SOFTWARE, LeaseLapsedError, openRunLease, OWNER_LOCK, RunError, StoreBehindSealError, storeHead, STORE_FILE } from "./run.js";
2
- export type { LeaseOptions, OpenRunLeaseOptions, OpenStep, ReleaseStep, RunClaim, RunErrorCode, RunLease, StoreHeadSource } from "./run.js";
2
+ export type { LeaseOptions, OpenRunLeaseOptions, OpenStep, ReleaseStep, RunClaim, RunErrorCode, RunLease, RunSeed, RunSeedDir, StoreHeadSource } from "./run.js";
3
3
  export * from "./errors.js";
4
4
  export { assertPragmas, Fence, FencedDatabase, openArchilStore, PROFILE_PRAGMAS, StoreBusyError, StoreFencedError, StorePragmaError } from "./store.js";
5
5
  export type { ArchilStore, OpenOptions, PragmaMismatch, Profile } from "./store.js";