@parcha/pi-durable-disk 0.1.0-beta.15 → 0.1.0-beta.17

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.
@@ -1,7 +1,7 @@
1
1
  import type { Delegation } from "disk";
2
2
  import { type ArchilHost, type ControlApi, type PathlessResolver, type RunRef } from "./claim.js";
3
3
  import { PdaError } from "./errors.js";
4
- import { type RunRecord } from "./status.js";
4
+ import { type RunRecord, type StartMark } from "./status.js";
5
5
  export type Json = null | boolean | number | string | Json[] | {
6
6
  [key: string]: Json;
7
7
  };
@@ -21,6 +21,22 @@ export type StartAttempt = {
21
21
  attempt: number;
22
22
  env?: Record<string, string>;
23
23
  };
24
+ /**
25
+ * A box a driver made for an instance, as a receipt: who provided it, what kind, when it was made and when it ended
26
+ * (null while it lives), and its published list price per second (`usdPerSecond`), with the page that price was read
27
+ * from (`source`) and the ISO date it was read (`priced`). A box with no price (a kind the provider's page does not
28
+ * price, or of unknown size) has none of the three: never a price of 0. No field holds the provider's id of the box:
29
+ * the supervisor labels boxes itself and computes their seconds and cost.
30
+ */
31
+ export type HostBox = {
32
+ provider: string;
33
+ kind: string;
34
+ from: string;
35
+ to: string | null;
36
+ usdPerSecond?: number;
37
+ source?: string;
38
+ priced?: string;
39
+ };
24
40
  /** Compute is disposable and pluggable: a driver is these three calls. */
25
41
  export interface HostDriver {
26
42
  /**
@@ -41,6 +57,8 @@ export interface HostDriver {
41
57
  kill?(handle: HostHandle): Promise<void>;
42
58
  /** What `kill` takes: `process`, the instance's processes while its machine stays (a restart policy may bring them back), or `box`, its machine. */
43
59
  readonly kills?: "process" | "box";
60
+ /** Optional, for a driver whose instances get machines of their own: the box `handle`'s instance runs in, as a receipt. */
61
+ box?(handle: HostHandle): HostBox | null;
44
62
  }
45
63
  /** The control-plane calls the supervisor uses; an archil `Disk` satisfies it. */
46
64
  export interface SupervisorControl extends ControlApi {
@@ -227,18 +245,7 @@ export type EnsureResult =
227
245
  * supervisors are safe: the mount admits one claimant and the other instance exits 76.
228
246
  */
229
247
  export declare function ensureRunning(ref: RunRef, host: HostDriver, options: EnsureOptions): Promise<EnsureResult>;
230
- /**
231
- * The supervisor's record of its last start of a run: the generation the instance will write, when, by whom, and the
232
- * failed starts right before it (`failures`; `lastExit`, what the decision saw of the last one).
233
- */
234
- export declare const START_MARK = "start.json";
235
- export type StartMark = {
236
- generation: number;
237
- at: string;
238
- by: string | null;
239
- failures: number;
240
- lastExit: string | null;
241
- };
248
+ export { START_MARK, type StartMark } from "./status.js";
242
249
  /** The grace of a start that follows `failures` failed ones: the base doubled per failure, capped, never below the base. */
243
250
  export declare function startGrace(baseMs: number, failures: number, maxMs?: number): number;
244
251
  /**
package/dist/supervise.js CHANGED
@@ -8,7 +8,7 @@ import { hostname } from "node:os";
8
8
  import { join } from "node:path";
9
9
  import { acquire, createRunDir, findDelegations, matchDelegations, pathlessResolver, revokeBestEffort, revokeCompanions, mintMountToken, MOUNT_TOKEN_TTL, parseTokenNickname, removeMountToken, runPath, unmountClaim, } from "./claim.js";
10
10
  import { FencedError, HeldError, PdaError } from "./errors.js";
11
- import { parseRunRecord, RUN_JSON } from "./status.js";
11
+ import { parseRunRecord, parseStartMark, RUN_JSON, START_MARK } from "./status.js";
12
12
  export const CONTROL_TIMEOUT_MS = 10_000;
13
13
  /** The same control API with every call bounded: one that does not settle in `ms` fails as CONTROL_API_FAILED. */
14
14
  export function withTimeouts(control, ms) {
@@ -160,11 +160,8 @@ export async function ensureRunning(ref, host, options) {
160
160
  await revokeListed(opts.control, ref.id, held, all);
161
161
  return start("lease-expired", held, stonith);
162
162
  }
163
- /**
164
- * The supervisor's record of its last start of a run: the generation the instance will write, when, by whom, and the
165
- * failed starts right before it (`failures`; `lastExit`, what the decision saw of the last one).
166
- */
167
- export const START_MARK = "start.json";
163
+ // The start mark (`start.json`): its name, shape and parser live in status.ts, which the instance also reads it with.
164
+ export { START_MARK } from "./status.js";
168
165
  /** The grace of a start that follows `failures` failed ones: the base doubled per failure, capped, never below the base. */
169
166
  export function startGrace(baseMs, failures, maxMs = START_BACKOFF_MAX_MS) {
170
167
  if (baseMs <= 0)
@@ -193,17 +190,7 @@ export async function readStartMark(control, id) {
193
190
  return null;
194
191
  throw new SuperviseError("CONTROL_API_FAILED", `GetObject ${runPath(id)}/${START_MARK} failed`, { cause: err });
195
192
  }
196
- try {
197
- const v = JSON.parse(text);
198
- if (!Number.isSafeInteger(v.generation) || v.generation < 1 || typeof v.at !== "string" || !Number.isFinite(Date.parse(v.at)))
199
- return null;
200
- const failures = Number.isSafeInteger(v.failures) && v.failures >= 0 ? v.failures : 0;
201
- const lastExit = typeof v.lastExit === "string" ? v.lastExit : null;
202
- return { generation: v.generation, at: v.at, by: typeof v.by === "string" ? v.by : null, failures, lastExit };
203
- }
204
- catch {
205
- return null;
206
- }
193
+ return parseStartMark(text);
207
194
  }
208
195
  /** Only a run with no run.json and no holder gets here, so the directory is either absent or idle. */
209
196
  async function ensureRunDir(control, id, owner) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parcha/pi-durable-disk",
3
- "version": "0.1.0-beta.15",
3
+ "version": "0.1.0-beta.17",
4
4
  "description": "A durable pi host on a network disk (Archil): one owner per run, a fence, durable commits, and a supervisor.",
5
5
  "keywords": [
6
6
  "pi",
@@ -56,7 +56,7 @@
56
56
  "test": "node --test --test-timeout=240000 'test/*.test.ts' 'test/examples/*.test.ts'",
57
57
  "test:acceptance": "node --test --test-concurrency=1 'test/acceptance/*.test.ts'",
58
58
  "test:live": "node --test --test-concurrency=1 'test/live/*.test.ts'",
59
- "test:live:claim": "node --test --test-concurrency=1 test/live/claim.live.test.ts",
59
+ "test:live:claim": "node --test --test-concurrency=1 test/live/claim.live.test.ts test/live/record-home.live.test.ts",
60
60
  "test:live:env": "node --test --test-concurrency=1 test/live/env.live.test.ts",
61
61
  "test:live:lifecycle": "node --test --test-concurrency=1 test/live/lifecycle.live.test.ts",
62
62
  "test:live:run": "node --test --test-concurrency=1 test/live/run.live.test.ts",