rightsize 0.5.0 → 0.6.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 (144) hide show
  1. package/README.md +14 -9
  2. package/dist/backend-msb/agent-endpoint.d.ts +25 -0
  3. package/dist/backend-msb/agent-endpoint.d.ts.map +1 -0
  4. package/dist/backend-msb/agent-endpoint.js +27 -0
  5. package/dist/backend-msb/agent-endpoint.js.map +1 -0
  6. package/dist/backend-msb/backend.d.ts +47 -6
  7. package/dist/backend-msb/backend.d.ts.map +1 -1
  8. package/dist/backend-msb/backend.js +123 -10
  9. package/dist/backend-msb/backend.js.map +1 -1
  10. package/dist/backend-msb/commands.d.ts +2 -2
  11. package/dist/backend-msb/commands.d.ts.map +1 -1
  12. package/dist/backend-msb/commands.js +22 -7
  13. package/dist/backend-msb/commands.js.map +1 -1
  14. package/dist/backend-msb/platform.js +2 -2
  15. package/dist/backend-msb/provisioner.d.ts +1 -1
  16. package/dist/backend-msb/provisioner.js +1 -1
  17. package/dist/backend-msb/snapshot-import.d.ts +4 -4
  18. package/dist/backend-msb/snapshot-import.js +4 -4
  19. package/dist/backend-msb/snapshot-list.d.ts +3 -3
  20. package/dist/backend-msb/snapshot-list.js +3 -3
  21. package/dist/backend-msb/snapshot-not-found.d.ts +1 -1
  22. package/dist/backend-msb/snapshot-not-found.js +1 -1
  23. package/dist/backend-msb/snapshot-save-fsync.d.ts +55 -0
  24. package/dist/backend-msb/snapshot-save-fsync.d.ts.map +1 -0
  25. package/dist/backend-msb/snapshot-save-fsync.js +79 -0
  26. package/dist/backend-msb/snapshot-save-fsync.js.map +1 -0
  27. package/dist/core/backend.d.ts +2 -2
  28. package/dist/core/checkpoint/api.d.ts +1 -1
  29. package/dist/core/checkpoint/api.js +1 -1
  30. package/dist/core/docker-image-name.d.ts +58 -0
  31. package/dist/core/docker-image-name.d.ts.map +1 -0
  32. package/dist/core/docker-image-name.js +127 -0
  33. package/dist/core/docker-image-name.js.map +1 -0
  34. package/dist/core/errors.d.ts +21 -0
  35. package/dist/core/errors.d.ts.map +1 -1
  36. package/dist/core/errors.js +26 -0
  37. package/dist/core/errors.js.map +1 -1
  38. package/dist/core/generic-container.d.ts +7 -1
  39. package/dist/core/generic-container.d.ts.map +1 -1
  40. package/dist/core/generic-container.js +8 -2
  41. package/dist/core/generic-container.js.map +1 -1
  42. package/dist/core/model.d.ts +9 -4
  43. package/dist/core/model.d.ts.map +1 -1
  44. package/dist/index.d.ts +8 -5
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +8 -5
  47. package/dist/index.js.map +1 -1
  48. package/dist/modules/arango.d.ts +11 -3
  49. package/dist/modules/arango.d.ts.map +1 -1
  50. package/dist/modules/arango.js +14 -4
  51. package/dist/modules/arango.js.map +1 -1
  52. package/dist/modules/cassandra.d.ts +12 -3
  53. package/dist/modules/cassandra.d.ts.map +1 -1
  54. package/dist/modules/cassandra.js +15 -4
  55. package/dist/modules/cassandra.js.map +1 -1
  56. package/dist/modules/clickhouse.d.ts +8 -2
  57. package/dist/modules/clickhouse.d.ts.map +1 -1
  58. package/dist/modules/clickhouse.js +11 -3
  59. package/dist/modules/clickhouse.js.map +1 -1
  60. package/dist/modules/elasticsearch.d.ts +49 -0
  61. package/dist/modules/elasticsearch.d.ts.map +1 -0
  62. package/dist/modules/elasticsearch.js +66 -0
  63. package/dist/modules/elasticsearch.js.map +1 -0
  64. package/dist/modules/flink.d.ts +8 -2
  65. package/dist/modules/flink.d.ts.map +1 -1
  66. package/dist/modules/flink.js +13 -4
  67. package/dist/modules/flink.js.map +1 -1
  68. package/dist/modules/floci.d.ts +30 -6
  69. package/dist/modules/floci.d.ts.map +1 -1
  70. package/dist/modules/floci.js +39 -9
  71. package/dist/modules/floci.js.map +1 -1
  72. package/dist/modules/index.d.ts +3 -1
  73. package/dist/modules/index.d.ts.map +1 -1
  74. package/dist/modules/index.js +3 -1
  75. package/dist/modules/index.js.map +1 -1
  76. package/dist/modules/kafka.d.ts +10 -3
  77. package/dist/modules/kafka.d.ts.map +1 -1
  78. package/dist/modules/kafka.js +13 -4
  79. package/dist/modules/kafka.js.map +1 -1
  80. package/dist/modules/keycloak.d.ts +10 -2
  81. package/dist/modules/keycloak.d.ts.map +1 -1
  82. package/dist/modules/keycloak.js +13 -3
  83. package/dist/modules/keycloak.js.map +1 -1
  84. package/dist/modules/mariadb.d.ts +8 -2
  85. package/dist/modules/mariadb.d.ts.map +1 -1
  86. package/dist/modules/mariadb.js +11 -3
  87. package/dist/modules/mariadb.js.map +1 -1
  88. package/dist/modules/memcached.d.ts +12 -3
  89. package/dist/modules/memcached.d.ts.map +1 -1
  90. package/dist/modules/memcached.js +15 -4
  91. package/dist/modules/memcached.js.map +1 -1
  92. package/dist/modules/minio.d.ts +7 -2
  93. package/dist/modules/minio.d.ts.map +1 -1
  94. package/dist/modules/minio.js +10 -3
  95. package/dist/modules/minio.js.map +1 -1
  96. package/dist/modules/mongodb.d.ts +7 -2
  97. package/dist/modules/mongodb.d.ts.map +1 -1
  98. package/dist/modules/mongodb.js +16 -4
  99. package/dist/modules/mongodb.js.map +1 -1
  100. package/dist/modules/mysql.d.ts +8 -2
  101. package/dist/modules/mysql.d.ts.map +1 -1
  102. package/dist/modules/mysql.js +11 -3
  103. package/dist/modules/mysql.js.map +1 -1
  104. package/dist/modules/neo4j.d.ts +7 -2
  105. package/dist/modules/neo4j.d.ts.map +1 -1
  106. package/dist/modules/neo4j.js +10 -3
  107. package/dist/modules/neo4j.js.map +1 -1
  108. package/dist/modules/pinot.d.ts +7 -2
  109. package/dist/modules/pinot.d.ts.map +1 -1
  110. package/dist/modules/pinot.js +10 -3
  111. package/dist/modules/pinot.js.map +1 -1
  112. package/dist/modules/postgres.d.ts +11 -2
  113. package/dist/modules/postgres.d.ts.map +1 -1
  114. package/dist/modules/postgres.js +14 -3
  115. package/dist/modules/postgres.js.map +1 -1
  116. package/dist/modules/qdrant.d.ts +26 -0
  117. package/dist/modules/qdrant.d.ts.map +1 -0
  118. package/dist/modules/qdrant.js +38 -0
  119. package/dist/modules/qdrant.js.map +1 -0
  120. package/dist/modules/rabbitmq.d.ts +11 -2
  121. package/dist/modules/rabbitmq.d.ts.map +1 -1
  122. package/dist/modules/rabbitmq.js +14 -3
  123. package/dist/modules/rabbitmq.js.map +1 -1
  124. package/dist/modules/redis.d.ts +8 -2
  125. package/dist/modules/redis.d.ts.map +1 -1
  126. package/dist/modules/redis.js +11 -3
  127. package/dist/modules/redis.js.map +1 -1
  128. package/dist/modules/redpanda.d.ts +15 -6
  129. package/dist/modules/redpanda.d.ts.map +1 -1
  130. package/dist/modules/redpanda.js +18 -7
  131. package/dist/modules/redpanda.js.map +1 -1
  132. package/dist/modules/spring-cloud-config.d.ts +7 -2
  133. package/dist/modules/spring-cloud-config.d.ts.map +1 -1
  134. package/dist/modules/spring-cloud-config.js +10 -3
  135. package/dist/modules/spring-cloud-config.js.map +1 -1
  136. package/dist/modules/valkey.d.ts +9 -2
  137. package/dist/modules/valkey.d.ts.map +1 -1
  138. package/dist/modules/valkey.js +12 -3
  139. package/dist/modules/valkey.js.map +1 -1
  140. package/dist/modules/wiremock.d.ts +7 -2
  141. package/dist/modules/wiremock.d.ts.map +1 -1
  142. package/dist/modules/wiremock.js +10 -3
  143. package/dist/modules/wiremock.js.map +1 -1
  144. package/package.json +1 -1
@@ -20,14 +20,29 @@ export const MsbCommands = {
20
20
  for (const [key, value] of spec.env) {
21
21
  argv.push("-e", `${key}=${value}`);
22
22
  }
23
+ // The option block is always spelled out, never left to msb's defaults, for two
24
+ // reasons on top of each other. The access token (`ro`/`rw`) carries
25
+ // FileMount.readOnly, which msb enforces as a genuine guest-side write block — and it
26
+ // keeps OUR spec parseable on Windows: msb stages each mount into a temp directory
27
+ // and canonicalizes it, which there yields the extended-length `\\?\C:\...` form, and
28
+ // its splitter skips a drive prefix only for a bare drive letter, so a spec with no
29
+ // option block splits at the drive's colon and rejects the path tail as options.
30
+ // `nodev` exists because msb then rebuilds an INTERNAL `tag:staged_path[:opts]` spec
31
+ // for the same mount, carrying over only NON-DEFAULT option tokens — `rw` is its
32
+ // default and is dropped, which on Windows strips the internal spec's option block
33
+ // and re-creates the same misparse one layer down (captured: `--mount
34
+ // "fm_…:\\?\C:\…": expected flag or key=value option`). `nodev` always survives the
35
+ // carry-over, and for a single-file mount it is meaningless (no device nodes to
36
+ // block): verified against a real msb 0.6.8 — `rw,nodev` mounts `rw,nodev` and
37
+ // accepts an in-guest write, `ro,nodev` rejects one with `Read-only file system`.
23
38
  for (const mount of spec.mounts) {
24
- argv.push("--mount-file", `${mount.hostPath}:${mount.guestPath}`);
39
+ argv.push("--mount-file", `${mount.hostPath}:${mount.guestPath}:${mount.readOnly ? "ro" : "rw"},nodev`);
25
40
  }
26
41
  if (spec.checkpointRef !== undefined) {
27
- // `--snapshot` is mutually exclusive with the IMAGE positional arg —
42
+ // `--from-snapshot` is mutually exclusive with the IMAGE positional arg —
28
43
  // the snapshot itself pins the image (see MsbCliBackend's own doc on
29
44
  // fromCheckpoint/checkpointRef).
30
- argv.push("--snapshot", spec.checkpointRef);
45
+ argv.push("--from-snapshot", spec.checkpointRef);
31
46
  }
32
47
  else {
33
48
  argv.push(spec.image);
@@ -50,13 +65,13 @@ export const MsbCommands = {
50
65
  snapshotInspect(name) {
51
66
  return ["snapshot", "inspect", name];
52
67
  },
53
- /** `msb snapshot export <ref> <dest>` — writes a `.tar.zst` artifact archive; deliberately never `--with-image` (its import fails an integrity check in 0.6.6, see the checkpoints guide). `exportCheckpoint`'s backend call. */
68
+ /** `msb snapshot save <ref> <dest>` — writes a `.tar.zst` artifact archive; deliberately never `--with-image` (its import fails an integrity check in 0.6.6, see the checkpoints guide). `exportCheckpoint`'s backend call. */
54
69
  snapshotExport(ref, dest) {
55
- return ["snapshot", "export", ref, dest];
70
+ return ["snapshot", "save", ref, dest];
56
71
  },
57
- /** `msb snapshot import <archive>` — unpacks into a digest-derived directory under `~/.microsandbox/snapshots/`, never the original name; `importCheckpoint`'s backend call. */
72
+ /** `msb snapshot load <archive>` — unpacks into a digest-derived directory under `~/.microsandbox/snapshots/`, never the original name; `importCheckpoint`'s backend call. */
58
73
  snapshotImport(archive) {
59
- return ["snapshot", "import", archive];
74
+ return ["snapshot", "load", archive];
60
75
  },
61
76
  /** `msb snapshot list --format json` — full `digest`/`name`/`artifact_path`/`image_ref` entries, used to CONFIRM an imported snapshot's digest-dir basename is present (the basename itself, not the `digest` field, is the effective ref — the full digest does not resolve as a snapshot ref). */
62
77
  snapshotList() {
@@ -1 +1 @@
1
- {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/backend-msb/commands.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/backend-msb/commands.ts"],"names":[],"mappings":"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"}
@@ -23,8 +23,8 @@ const KRUN_ASSETS = {
23
23
  // re-verify all names when bumping the pin.
24
24
  const KRUN_INSTALL_NAMES = {
25
25
  "darwin-arm64": "libkrunfw.5.dylib",
26
- "linux-x64": "libkrunfw.so.5.5.0",
27
- "linux-arm64": "libkrunfw.so.5.5.0",
26
+ "linux-x64": "libkrunfw.so.5.6.1",
27
+ "linux-arm64": "libkrunfw.so.5.6.1",
28
28
  "win32-x64": "libkrunfw.dll",
29
29
  "win32-arm64": "libkrunfw.dll",
30
30
  };
@@ -1,5 +1,5 @@
1
1
  import type { Platform } from "./platform.js";
2
- export declare const MSB_VERSION = "0.6.6";
2
+ export declare const MSB_VERSION = "0.6.8";
3
3
  /** Test seam: the full resolution logic parameterized over the release base URL, cache dir, and environment. */
4
4
  export declare function ensureInstalledAt(baseUrl: string, cacheDir: string, env: Record<string, string | undefined>): Promise<string>;
5
5
  /** Resolves (downloading and verifying if needed) a runnable `msb` binary path, using the real GitHub release base, `RIGHTSIZE_CACHE_DIR`, and `process.env`. */
@@ -7,7 +7,7 @@ import { createHash } from "node:crypto";
7
7
  import { ProvisionError } from "../core/errors.js";
8
8
  import { cacheDir as defaultCacheDir } from "../core/cache-dir.js";
9
9
  import { PlatformInfo } from "./platform.js";
10
- export const MSB_VERSION = "0.6.6";
10
+ export const MSB_VERSION = "0.6.8";
11
11
  const DEFAULT_BASE = `https://github.com/superradcompany/microsandbox/releases/download/v${MSB_VERSION}`;
12
12
  const CONNECT_READ_TIMEOUT_MS = 300_000;
13
13
  const STALE_LOCK_AGE_MS = 5 * 60 * 1000;
@@ -1,7 +1,7 @@
1
1
  /**
2
- * True if `stderr` (an `msb snapshot import` non-zero exit) names msb's own
2
+ * True if `stderr` (an `msb snapshot load` non-zero exit) names msb's own
3
3
  * "this digest is already imported" signal, as opposed to some other import
4
- * failure. Observed verbatim against the real msb 0.6.6 binary:
4
+ * failure. Observed verbatim against the real msb 0.6.8 binary:
5
5
  *
6
6
  * ```
7
7
  * error: snapshot already exists: <path>
@@ -16,8 +16,8 @@
16
16
  */
17
17
  export declare function isSnapshotAlreadyExistsError(stderr: string): boolean;
18
18
  /**
19
- * Extracts the digest-dir basename from one `msb snapshot import`
20
- * invocation's output. Verified against the real msb 0.6.6 binary: on both a
19
+ * Extracts the digest-dir basename from one `msb snapshot load`
20
+ * invocation's output. Verified against the real msb 0.6.8 binary: on both a
21
21
  * success (the printed stdout line) and an already-exists failure (the
22
22
  * `error: snapshot already exists: <path>` stderr line), the relevant line
23
23
  * ENDS with the artifact path under `~/.microsandbox/snapshots/`, whose
@@ -1,8 +1,8 @@
1
1
  import * as path from "node:path";
2
2
  /**
3
- * True if `stderr` (an `msb snapshot import` non-zero exit) names msb's own
3
+ * True if `stderr` (an `msb snapshot load` non-zero exit) names msb's own
4
4
  * "this digest is already imported" signal, as opposed to some other import
5
- * failure. Observed verbatim against the real msb 0.6.6 binary:
5
+ * failure. Observed verbatim against the real msb 0.6.8 binary:
6
6
  *
7
7
  * ```
8
8
  * error: snapshot already exists: <path>
@@ -19,8 +19,8 @@ export function isSnapshotAlreadyExistsError(stderr) {
19
19
  return stderr.includes("snapshot already exists");
20
20
  }
21
21
  /**
22
- * Extracts the digest-dir basename from one `msb snapshot import`
23
- * invocation's output. Verified against the real msb 0.6.6 binary: on both a
22
+ * Extracts the digest-dir basename from one `msb snapshot load`
23
+ * invocation's output. Verified against the real msb 0.6.8 binary: on both a
24
24
  * success (the printed stdout line) and an already-exists failure (the
25
25
  * `error: snapshot already exists: <path>` stderr line), the relevant line
26
26
  * ENDS with the artifact path under `~/.microsandbox/snapshots/`, whose
@@ -23,18 +23,18 @@ export interface SnapshotListEntry {
23
23
  export declare function parseSnapshotList(json: string): SnapshotListEntry[];
24
24
  /**
25
25
  * Confirms an imported snapshot's digest-dir basename (e.g.
26
- * `sha256-b9c0448ee9d54e33`, parsed from `msb snapshot import`'s own
26
+ * `sha256-b9c0448ee9d54e33`, parsed from `msb snapshot load`'s own
27
27
  * output — see `snapshot-import.ts`) actually appears in `msb snapshot
28
28
  * list`: an entry whose `name` equals `digestDirName` outright, or whose
29
29
  * `artifact_path`'s basename does (`msb snapshot list` was verified to
30
30
  * report both shapes depending on how the snapshot was created).
31
31
  *
32
32
  * Returns `digestDirName` itself once confirmed present — NOT the entry's
33
- * `digest` field. Live-verified against msb 0.6.6: the full
33
+ * `digest` field. Live-verified against msb 0.6.8: the full
34
34
  * `sha256:<64hex>` digest does not resolve as a snapshot ref at all (`msb
35
35
  * snapshot inspect sha256:<full>` fails "snapshot not found" — msb treats
36
36
  * it as a literal path). Only the digest-dir name resolves for
37
- * `inspect`/`rm`/`run --snapshot`, so it — not the `digest` field — is the
37
+ * `inspect`/`rm`/`run --from-snapshot`, so it — not the `digest` field — is the
38
38
  * effective ref `importCheckpoint` must hand back. `undefined` means no
39
39
  * entry matched — the caller throws rather than returning an unconfirmed
40
40
  * ref.
@@ -29,18 +29,18 @@ export function parseSnapshotList(json) {
29
29
  }
30
30
  /**
31
31
  * Confirms an imported snapshot's digest-dir basename (e.g.
32
- * `sha256-b9c0448ee9d54e33`, parsed from `msb snapshot import`'s own
32
+ * `sha256-b9c0448ee9d54e33`, parsed from `msb snapshot load`'s own
33
33
  * output — see `snapshot-import.ts`) actually appears in `msb snapshot
34
34
  * list`: an entry whose `name` equals `digestDirName` outright, or whose
35
35
  * `artifact_path`'s basename does (`msb snapshot list` was verified to
36
36
  * report both shapes depending on how the snapshot was created).
37
37
  *
38
38
  * Returns `digestDirName` itself once confirmed present — NOT the entry's
39
- * `digest` field. Live-verified against msb 0.6.6: the full
39
+ * `digest` field. Live-verified against msb 0.6.8: the full
40
40
  * `sha256:<64hex>` digest does not resolve as a snapshot ref at all (`msb
41
41
  * snapshot inspect sha256:<full>` fails "snapshot not found" — msb treats
42
42
  * it as a literal path). Only the digest-dir name resolves for
43
- * `inspect`/`rm`/`run --snapshot`, so it — not the `digest` field — is the
43
+ * `inspect`/`rm`/`run --from-snapshot`, so it — not the `digest` field — is the
44
44
  * effective ref `importCheckpoint` must hand back. `undefined` means no
45
45
  * entry matched — the caller throws rather than returning an unconfirmed
46
46
  * ref.
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * True if `output` (an `msb snapshot inspect <ref>` non-zero exit's stderr)
3
3
  * names msb's own "this snapshot does not exist" signal, as opposed to some
4
- * other probe failure. Observed verbatim against the real msb 0.6.6 binary:
4
+ * other probe failure. Observed verbatim against the real msb 0.6.8 binary:
5
5
  *
6
6
  * ```
7
7
  * error: snapshot not found: <path>
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * True if `output` (an `msb snapshot inspect <ref>` non-zero exit's stderr)
3
3
  * names msb's own "this snapshot does not exist" signal, as opposed to some
4
- * other probe failure. Observed verbatim against the real msb 0.6.6 binary:
4
+ * other probe failure. Observed verbatim against the real msb 0.6.8 binary:
5
5
  *
6
6
  * ```
7
7
  * error: snapshot not found: <path>
@@ -0,0 +1,55 @@
1
+ /**
2
+ * True if `stderr` (an `msb snapshot save` non-zero exit) carries a Windows
3
+ * `ERROR_ACCESS_DENIED`, which on that platform is how EVERY `snapshot save`
4
+ * in msb 0.6.7 and 0.6.8 ends. Captured verbatim from a windows-2025 hosted
5
+ * runner:
6
+ *
7
+ * ```text
8
+ * msb snapshot save rz-ckpt-fccd7568-archive C:\...\artifact failed (exit 1):
9
+ * error: io error: Access is denied. (os error 5)
10
+ * ```
11
+ *
12
+ * The cause is in msb's own archive writer: having finished the archive it
13
+ * reopens the staging file READ-ONLY, then calls `sync_all()` on that handle
14
+ * before moving it onto the destination. On Windows `sync_all` is
15
+ * `FlushFileBuffers`, which requires write access, so it fails with error 5
16
+ * every time and the move on the next line never runs. On Unix an `fsync` of a
17
+ * read-only descriptor is legal, so the same code path is fine there — which
18
+ * is why `exportCheckpoint` consults this only on Windows. 0.6.6 wrote
19
+ * straight to the destination with no fsync step at all, hence the failure
20
+ * being new in 0.6.7.
21
+ *
22
+ * Matches the `(os error 5)` suffix rather than the `Access is denied.` text:
23
+ * that text comes from Windows' own `FormatMessage` and is localized to the
24
+ * machine's display language, while the numeric suffix is appended by Rust
25
+ * itself and reads the same on every host. The closing parenthesis is part of
26
+ * the needle so that codes in the fifties — `os error 53` (network path not
27
+ * found), `55`, `59` — cannot match on their leading digit.
28
+ */
29
+ export declare function isSnapshotSaveAccessDeniedFailure(stderr: string): boolean;
30
+ /**
31
+ * Completes the move msb's archive writer failed to reach (see
32
+ * `isSnapshotSaveAccessDeniedFailure`), resolving `true` if `destFile` now
33
+ * holds the finished archive.
34
+ *
35
+ * msb stages the archive beside its destination under
36
+ * `.<destination file name>.tmp.<msb pid>.<unix nanos>` and unlinks that
37
+ * staging file only when the archive WRITE fails — not when the trailing fsync
38
+ * does. So on the failure this handles, a complete, valid archive is sitting
39
+ * next to the destination and the only step left is the rename msb's own
40
+ * `replace_archive` would have performed on the very next line.
41
+ *
42
+ * Requires EXACTLY ONE candidate in the destination's parent directory. This
43
+ * library exports into a staging directory it creates fresh and owns for the
44
+ * one export, so a single candidate is the ordinary case; zero means the
45
+ * archive never got written and this is a different failure, and several means
46
+ * something else is writing there and picking one would be a guess. Both
47
+ * resolve `false`, which leaves the caller surfacing msb's own error unchanged
48
+ * — as does any filesystem error along the way, since msb's error describes
49
+ * the actual problem better than a failure to clean up after it would.
50
+ *
51
+ * Nothing here is Windows-specific by construction; the platform check lives
52
+ * at the call site so this stays exercisable by unit tests on any host.
53
+ */
54
+ export declare function salvageStagedArchive(destFile: string): Promise<boolean>;
55
+ //# sourceMappingURL=snapshot-save-fsync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-save-fsync.d.ts","sourceRoot":"","sources":["../../src/backend-msb/snapshot-save-fsync.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEzE;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAmB7E"}
@@ -0,0 +1,79 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ /**
4
+ * True if `stderr` (an `msb snapshot save` non-zero exit) carries a Windows
5
+ * `ERROR_ACCESS_DENIED`, which on that platform is how EVERY `snapshot save`
6
+ * in msb 0.6.7 and 0.6.8 ends. Captured verbatim from a windows-2025 hosted
7
+ * runner:
8
+ *
9
+ * ```text
10
+ * msb snapshot save rz-ckpt-fccd7568-archive C:\...\artifact failed (exit 1):
11
+ * error: io error: Access is denied. (os error 5)
12
+ * ```
13
+ *
14
+ * The cause is in msb's own archive writer: having finished the archive it
15
+ * reopens the staging file READ-ONLY, then calls `sync_all()` on that handle
16
+ * before moving it onto the destination. On Windows `sync_all` is
17
+ * `FlushFileBuffers`, which requires write access, so it fails with error 5
18
+ * every time and the move on the next line never runs. On Unix an `fsync` of a
19
+ * read-only descriptor is legal, so the same code path is fine there — which
20
+ * is why `exportCheckpoint` consults this only on Windows. 0.6.6 wrote
21
+ * straight to the destination with no fsync step at all, hence the failure
22
+ * being new in 0.6.7.
23
+ *
24
+ * Matches the `(os error 5)` suffix rather than the `Access is denied.` text:
25
+ * that text comes from Windows' own `FormatMessage` and is localized to the
26
+ * machine's display language, while the numeric suffix is appended by Rust
27
+ * itself and reads the same on every host. The closing parenthesis is part of
28
+ * the needle so that codes in the fifties — `os error 53` (network path not
29
+ * found), `55`, `59` — cannot match on their leading digit.
30
+ */
31
+ export function isSnapshotSaveAccessDeniedFailure(stderr) {
32
+ return stderr.includes("(os error 5)");
33
+ }
34
+ /**
35
+ * Completes the move msb's archive writer failed to reach (see
36
+ * `isSnapshotSaveAccessDeniedFailure`), resolving `true` if `destFile` now
37
+ * holds the finished archive.
38
+ *
39
+ * msb stages the archive beside its destination under
40
+ * `.<destination file name>.tmp.<msb pid>.<unix nanos>` and unlinks that
41
+ * staging file only when the archive WRITE fails — not when the trailing fsync
42
+ * does. So on the failure this handles, a complete, valid archive is sitting
43
+ * next to the destination and the only step left is the rename msb's own
44
+ * `replace_archive` would have performed on the very next line.
45
+ *
46
+ * Requires EXACTLY ONE candidate in the destination's parent directory. This
47
+ * library exports into a staging directory it creates fresh and owns for the
48
+ * one export, so a single candidate is the ordinary case; zero means the
49
+ * archive never got written and this is a different failure, and several means
50
+ * something else is writing there and picking one would be a guess. Both
51
+ * resolve `false`, which leaves the caller surfacing msb's own error unchanged
52
+ * — as does any filesystem error along the way, since msb's error describes
53
+ * the actual problem better than a failure to clean up after it would.
54
+ *
55
+ * Nothing here is Windows-specific by construction; the platform check lives
56
+ * at the call site so this stays exercisable by unit tests on any host.
57
+ */
58
+ export async function salvageStagedArchive(destFile) {
59
+ const parent = path.dirname(destFile);
60
+ const stagingPrefix = `.${path.basename(destFile)}.tmp.`;
61
+ try {
62
+ const candidates = (await fs.readdir(parent, { withFileTypes: true }))
63
+ // Regular files only: msb writes a file under the staging name, and a directory
64
+ // carrying it would otherwise consume the single-candidate slot and then be
65
+ // renamed onto the destination, where the archive step would bundle a directory.
66
+ .filter((entry) => entry.name.startsWith(stagingPrefix) && entry.isFile())
67
+ .map((entry) => entry.name);
68
+ const only = candidates.length === 1 ? candidates[0] : undefined;
69
+ if (only === undefined) {
70
+ return false;
71
+ }
72
+ await fs.rename(path.join(parent, only), destFile);
73
+ return true;
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }
79
+ //# sourceMappingURL=snapshot-save-fsync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot-save-fsync.js","sourceRoot":"","sources":["../../src/backend-msb/snapshot-save-fsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,iCAAiC,CAAC,MAAc;IAC9D,OAAO,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAgB;IACzD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,aAAa,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;IACzD,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;YACpE,gFAAgF;YAChF,4EAA4E;YAC5E,iFAAiF;aAChF,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;aACzE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QACjE,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACf,CAAC;QACD,MAAM,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;QACnD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -139,7 +139,7 @@ export interface SandboxBackend {
139
139
  * Writes this checkpoint's backend payload to `destFile` — the artifact
140
140
  * step of `Checkpoints.exportTo`, byte-for-byte what the backend's own CLI
141
141
  * produces (docker: `docker save -o <destFile> <ref>`; microsandbox:
142
- * `msb snapshot export <ref> <destFile>`, never `--with-image` — see the
142
+ * `msb snapshot save <ref> <destFile>`, never `--with-image` — see the
143
143
  * checkpoints guide for why). Only ever called once the generic layer has
144
144
  * already confirmed `ref` belongs to the ACTIVE backend and probed it
145
145
  * exists via `hasCheckpoint`; a failure surfaces the underlying tool's
@@ -150,7 +150,7 @@ export interface SandboxBackend {
150
150
  * Materializes an exported archive's backend payload from `srcFile` and
151
151
  * returns the EFFECTIVE ref to use from here on — not necessarily `ref`
152
152
  * itself. docker preserves the original tag on `docker load`, so its
153
- * effective ref IS `ref`; microsandbox's `snapshot import` writes under a
153
+ * effective ref IS `ref`; microsandbox's `snapshot load` writes under a
154
154
  * digest-derived DIRECTORY NAME (e.g. `sha256-b9c0448ee9d54e33`) it never
155
155
  * lets the caller choose, so its effective ref is that directory name,
156
156
  * confirmed present via `snapshot list --format json` — never the full
@@ -91,7 +91,7 @@ export declare function exportTo(cp: Checkpoint, destPath: string): Promise<void
91
91
  * either way, before any backend call or registry write), then hands the
92
92
  * extracted `artifact` to `SandboxBackend.importCheckpoint`, which
93
93
  * materializes it and returns the EFFECTIVE ref (docker: the same `ref` the
94
- * archive recorded; microsandbox: the digest `snapshot import` actually
94
+ * archive recorded; microsandbox: the digest `snapshot load` actually
95
95
  * assigned it — never necessarily the archive's own `ref`).
96
96
  *
97
97
  * A NAMED archive (`name` non-null) gets replace semantics matching
@@ -189,7 +189,7 @@ export async function exportTo(cp, destPath) {
189
189
  * either way, before any backend call or registry write), then hands the
190
190
  * extracted `artifact` to `SandboxBackend.importCheckpoint`, which
191
191
  * materializes it and returns the EFFECTIVE ref (docker: the same `ref` the
192
- * archive recorded; microsandbox: the digest `snapshot import` actually
192
+ * archive recorded; microsandbox: the digest `snapshot load` actually
193
193
  * assigned it — never necessarily the archive's own `ref`).
194
194
  *
195
195
  * A NAMED archive (`name` non-null) gets replace semantics matching
@@ -0,0 +1,58 @@
1
+ /**
2
+ * A parsed `[registry/]repository[:tag][@digest]` image reference, plus the
3
+ * Testcontainers-style compatibility override `asCompatibleSubstituteFor`
4
+ * sets. Every module constructor accepts `string | DockerImageName` and
5
+ * resolves it through `requireCompatible` before calling `super()`:
6
+ *
7
+ * ```ts
8
+ * new PostgresContainer("postgres:16") // checked, common case
9
+ * new PostgresContainer(DockerImageName.parse("mycorp/pg-hardened:16")
10
+ * .asCompatibleSubstituteFor("postgres")) // explicit override
11
+ * ```
12
+ *
13
+ * An explicitly supplied image is always used verbatim — `requireCompatible`
14
+ * only ever checks it and returns it unchanged; it never rewrites a tag or
15
+ * substitutes an image.
16
+ */
17
+ export declare class DockerImageName {
18
+ /** The image reference exactly as given to `parse()` — what a module passes to the backend, unmodified. */
19
+ readonly raw: string;
20
+ /** The registry host, if the first path segment looked like one (contains `.` or `:`, or is `localhost`). */
21
+ readonly registry: string | undefined;
22
+ /** The repository component: registry, tag, and digest all stripped. */
23
+ readonly repository: string;
24
+ /** The tag component, if the reference carried one. */
25
+ readonly tag: string | undefined;
26
+ /** The digest component (everything after `@`), if the reference carried one. */
27
+ readonly digest: string | undefined;
28
+ private readonly substituteRepository;
29
+ private constructor();
30
+ /**
31
+ * Parses a full image reference into its registry/repository/tag/digest
32
+ * components. A pure string decomposition — never validates against a
33
+ * real registry, never performs I/O.
34
+ */
35
+ static parse(image: string): DockerImageName;
36
+ /**
37
+ * Declares this image a compatible drop-in for `repository` — the escape
38
+ * hatch for a fork, mirror, or hardened rebuild that doesn't share the
39
+ * expected module's own repository name. From here on,
40
+ * `requireCompatible` treats `repository` as this image's declared
41
+ * identity instead of its own parsed `repository`. Returns a new
42
+ * `DockerImageName`; the original is left untouched.
43
+ */
44
+ asCompatibleSubstituteFor(repository: string): DockerImageName;
45
+ /** The exact reference passed to `parse()`, unmodified. */
46
+ toString(): string;
47
+ /**
48
+ * Resolves `image` (parsing it first if it's a plain string) and checks
49
+ * that it names `expectedRepository` — either directly via its own parsed
50
+ * `repository`, or via a prior `asCompatibleSubstituteFor(expectedRepository)`
51
+ * override. Returns the exact image string to pass to `super()`: never
52
+ * rewritten, tag and all. Throws `IncompatibleImageError` — before any
53
+ * backend call, before the module's own constructor body runs — when
54
+ * neither matches.
55
+ */
56
+ static requireCompatible(image: string | DockerImageName, expectedRepository: string): string;
57
+ }
58
+ //# sourceMappingURL=docker-image-name.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-image-name.d.ts","sourceRoot":"","sources":["../../src/core/docker-image-name.ts"],"names":[],"mappings":"AAwDA;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,eAAe;IAExB,2GAA2G;IAC3G,QAAQ,CAAC,GAAG,EAAE,MAAM;IACpB,6GAA6G;IAC7G,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS;IACrC,wEAAwE;IACxE,QAAQ,CAAC,UAAU,EAAE,MAAM;IAC3B,uDAAuD;IACvD,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IAChC,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB;IAXvC,OAAO;IAcP;;;;OAIG;IACH,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe;IAK5C;;;;;;;OAOG;IACH,yBAAyB,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe;IAI9D,2DAA2D;IAC3D,QAAQ,IAAI,MAAM;IAIlB;;;;;;;;OAQG;IACH,MAAM,CAAC,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,EAAE,kBAAkB,EAAE,MAAM,GAAG,MAAM;CAQ9F"}
@@ -0,0 +1,127 @@
1
+ import { IncompatibleImageError } from "./errors.js";
2
+ /** Splits off a trailing `@sha256:...` digest, if present — always the last `@` in the reference. */
3
+ function splitDigest(ref) {
4
+ const at = ref.lastIndexOf("@");
5
+ if (at === -1) {
6
+ return [ref, undefined];
7
+ }
8
+ return [ref.slice(0, at), ref.slice(at + 1)];
9
+ }
10
+ /**
11
+ * Splits a registry host off the front, following the Docker convention:
12
+ * the first path segment is a registry only if it contains a `.` (a domain)
13
+ * or a `:` (an explicit port), or is the literal `localhost` — otherwise
14
+ * it's a namespace segment and stays part of the repository. This is why
15
+ * `quay.io/keycloak/keycloak` strips to repository `keycloak/keycloak`
16
+ * while `floci/floci` stays whole.
17
+ */
18
+ function splitRegistry(ref) {
19
+ const slash = ref.indexOf("/");
20
+ if (slash === -1) {
21
+ return [undefined, ref];
22
+ }
23
+ const first = ref.slice(0, slash);
24
+ if (first.includes(".") || first.includes(":") || first === "localhost") {
25
+ return [first, ref.slice(slash + 1)];
26
+ }
27
+ return [undefined, ref];
28
+ }
29
+ /** Splits a trailing `:tag` off the repository — the last `:` counts only when it falls after the last `/`, so a registry-host port (already stripped by `splitRegistry`) is never mistaken for one. */
30
+ function splitTag(ref) {
31
+ const lastSlash = ref.lastIndexOf("/");
32
+ const lastColon = ref.lastIndexOf(":");
33
+ if (lastColon > lastSlash) {
34
+ return [ref.slice(0, lastColon), ref.slice(lastColon + 1)];
35
+ }
36
+ return [ref, undefined];
37
+ }
38
+ function parseReference(image) {
39
+ const [withoutDigest, digest] = splitDigest(image);
40
+ const [registry, rest] = splitRegistry(withoutDigest);
41
+ const [repository, tag] = splitTag(rest);
42
+ return { registry, repository, tag, digest };
43
+ }
44
+ /**
45
+ * A parsed `[registry/]repository[:tag][@digest]` image reference, plus the
46
+ * Testcontainers-style compatibility override `asCompatibleSubstituteFor`
47
+ * sets. Every module constructor accepts `string | DockerImageName` and
48
+ * resolves it through `requireCompatible` before calling `super()`:
49
+ *
50
+ * ```ts
51
+ * new PostgresContainer("postgres:16") // checked, common case
52
+ * new PostgresContainer(DockerImageName.parse("mycorp/pg-hardened:16")
53
+ * .asCompatibleSubstituteFor("postgres")) // explicit override
54
+ * ```
55
+ *
56
+ * An explicitly supplied image is always used verbatim — `requireCompatible`
57
+ * only ever checks it and returns it unchanged; it never rewrites a tag or
58
+ * substitutes an image.
59
+ */
60
+ export class DockerImageName {
61
+ raw;
62
+ registry;
63
+ repository;
64
+ tag;
65
+ digest;
66
+ substituteRepository;
67
+ constructor(
68
+ /** The image reference exactly as given to `parse()` — what a module passes to the backend, unmodified. */
69
+ raw,
70
+ /** The registry host, if the first path segment looked like one (contains `.` or `:`, or is `localhost`). */
71
+ registry,
72
+ /** The repository component: registry, tag, and digest all stripped. */
73
+ repository,
74
+ /** The tag component, if the reference carried one. */
75
+ tag,
76
+ /** The digest component (everything after `@`), if the reference carried one. */
77
+ digest, substituteRepository) {
78
+ this.raw = raw;
79
+ this.registry = registry;
80
+ this.repository = repository;
81
+ this.tag = tag;
82
+ this.digest = digest;
83
+ this.substituteRepository = substituteRepository;
84
+ }
85
+ /**
86
+ * Parses a full image reference into its registry/repository/tag/digest
87
+ * components. A pure string decomposition — never validates against a
88
+ * real registry, never performs I/O.
89
+ */
90
+ static parse(image) {
91
+ const { registry, repository, tag, digest } = parseReference(image);
92
+ return new DockerImageName(image, registry, repository, tag, digest, undefined);
93
+ }
94
+ /**
95
+ * Declares this image a compatible drop-in for `repository` — the escape
96
+ * hatch for a fork, mirror, or hardened rebuild that doesn't share the
97
+ * expected module's own repository name. From here on,
98
+ * `requireCompatible` treats `repository` as this image's declared
99
+ * identity instead of its own parsed `repository`. Returns a new
100
+ * `DockerImageName`; the original is left untouched.
101
+ */
102
+ asCompatibleSubstituteFor(repository) {
103
+ return new DockerImageName(this.raw, this.registry, this.repository, this.tag, this.digest, repository);
104
+ }
105
+ /** The exact reference passed to `parse()`, unmodified. */
106
+ toString() {
107
+ return this.raw;
108
+ }
109
+ /**
110
+ * Resolves `image` (parsing it first if it's a plain string) and checks
111
+ * that it names `expectedRepository` — either directly via its own parsed
112
+ * `repository`, or via a prior `asCompatibleSubstituteFor(expectedRepository)`
113
+ * override. Returns the exact image string to pass to `super()`: never
114
+ * rewritten, tag and all. Throws `IncompatibleImageError` — before any
115
+ * backend call, before the module's own constructor body runs — when
116
+ * neither matches.
117
+ */
118
+ static requireCompatible(image, expectedRepository) {
119
+ const name = typeof image === "string" ? DockerImageName.parse(image) : image;
120
+ const declaredRepository = name.substituteRepository ?? name.repository;
121
+ if (declaredRepository !== expectedRepository) {
122
+ throw new IncompatibleImageError(name.repository, expectedRepository);
123
+ }
124
+ return name.raw;
125
+ }
126
+ }
127
+ //# sourceMappingURL=docker-image-name.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-image-name.js","sourceRoot":"","sources":["../../src/core/docker-image-name.ts"],"names":[],"mappings":"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"}
@@ -163,6 +163,27 @@ export declare class CheckpointArtifactMissingError extends Error {
163
163
  /** The backend this ref was supposed to exist on. */
164
164
  backend: string);
165
165
  }
166
+ /**
167
+ * Thrown by `DockerImageName.requireCompatible` — called by a module
168
+ * constructor before its own `super()` — when an explicitly supplied
169
+ * image's repository (registry host, tag, and digest all stripped) doesn't
170
+ * match the repository that module declares it understands, and no
171
+ * `asCompatibleSubstituteFor` override says otherwise. Thrown before any
172
+ * backend call and before the module's own builder calls ever run — never a
173
+ * bare wait-strategy timeout against a workload that was never going to
174
+ * answer on the expected protocol.
175
+ */
176
+ export declare class IncompatibleImageError extends Error {
177
+ /** The supplied image's own parsed repository (after registry/tag/digest stripping). */
178
+ readonly suppliedRepository: string;
179
+ /** The repository this module declares it understands. */
180
+ readonly expectedRepository: string;
181
+ constructor(
182
+ /** The supplied image's own parsed repository (after registry/tag/digest stripping). */
183
+ suppliedRepository: string,
184
+ /** The repository this module declares it understands. */
185
+ expectedRepository: string);
186
+ }
166
187
  /**
167
188
  * Thrown by `Checkpoints.importFrom` when `srcPath` isn't a well-formed
168
189
  * rightsize checkpoint archive: the file doesn't exist, it isn't a valid tar,