rightsize 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +4 -0
  3. package/README.md +255 -0
  4. package/dist/backend-docker/backend.d.ts +53 -0
  5. package/dist/backend-docker/backend.d.ts.map +1 -0
  6. package/dist/backend-docker/backend.js +336 -0
  7. package/dist/backend-docker/backend.js.map +1 -0
  8. package/dist/backend-docker/client.d.ts +82 -0
  9. package/dist/backend-docker/client.d.ts.map +1 -0
  10. package/dist/backend-docker/client.js +190 -0
  11. package/dist/backend-docker/client.js.map +1 -0
  12. package/dist/backend-docker/frames.d.ts +44 -0
  13. package/dist/backend-docker/frames.d.ts.map +1 -0
  14. package/dist/backend-docker/frames.js +105 -0
  15. package/dist/backend-docker/frames.js.map +1 -0
  16. package/dist/backend-docker/index.d.ts +5 -0
  17. package/dist/backend-docker/index.d.ts.map +1 -0
  18. package/dist/backend-docker/index.js +20 -0
  19. package/dist/backend-docker/index.js.map +1 -0
  20. package/dist/backend-docker/json.d.ts +13 -0
  21. package/dist/backend-docker/json.d.ts.map +1 -0
  22. package/dist/backend-docker/json.js +47 -0
  23. package/dist/backend-docker/json.js.map +1 -0
  24. package/dist/backend-docker/labels.d.ts +11 -0
  25. package/dist/backend-docker/labels.d.ts.map +1 -0
  26. package/dist/backend-docker/labels.js +13 -0
  27. package/dist/backend-docker/labels.js.map +1 -0
  28. package/dist/backend-docker/port-conflict.d.ts +9 -0
  29. package/dist/backend-docker/port-conflict.d.ts.map +1 -0
  30. package/dist/backend-docker/port-conflict.js +12 -0
  31. package/dist/backend-docker/port-conflict.js.map +1 -0
  32. package/dist/backend-docker/provider.d.ts +22 -0
  33. package/dist/backend-docker/provider.d.ts.map +1 -0
  34. package/dist/backend-docker/provider.js +36 -0
  35. package/dist/backend-docker/provider.js.map +1 -0
  36. package/dist/backend-msb/backend.d.ts +169 -0
  37. package/dist/backend-msb/backend.d.ts.map +1 -0
  38. package/dist/backend-msb/backend.js +664 -0
  39. package/dist/backend-msb/backend.js.map +1 -0
  40. package/dist/backend-msb/commands.d.ts +28 -0
  41. package/dist/backend-msb/commands.d.ts.map +1 -0
  42. package/dist/backend-msb/commands.js +67 -0
  43. package/dist/backend-msb/commands.js.map +1 -0
  44. package/dist/backend-msb/exec-tunnel.d.ts +33 -0
  45. package/dist/backend-msb/exec-tunnel.d.ts.map +1 -0
  46. package/dist/backend-msb/exec-tunnel.js +163 -0
  47. package/dist/backend-msb/exec-tunnel.js.map +1 -0
  48. package/dist/backend-msb/follow-replay.d.ts +14 -0
  49. package/dist/backend-msb/follow-replay.d.ts.map +1 -0
  50. package/dist/backend-msb/follow-replay.js +20 -0
  51. package/dist/backend-msb/follow-replay.js.map +1 -0
  52. package/dist/backend-msb/image-cache.d.ts +25 -0
  53. package/dist/backend-msb/image-cache.d.ts.map +1 -0
  54. package/dist/backend-msb/image-cache.js +27 -0
  55. package/dist/backend-msb/image-cache.js.map +1 -0
  56. package/dist/backend-msb/index.d.ts +6 -0
  57. package/dist/backend-msb/index.d.ts.map +1 -0
  58. package/dist/backend-msb/index.js +21 -0
  59. package/dist/backend-msb/index.js.map +1 -0
  60. package/dist/backend-msb/invoke.d.ts +24 -0
  61. package/dist/backend-msb/invoke.d.ts.map +1 -0
  62. package/dist/backend-msb/invoke.js +73 -0
  63. package/dist/backend-msb/invoke.js.map +1 -0
  64. package/dist/backend-msb/ls-json.d.ts +23 -0
  65. package/dist/backend-msb/ls-json.d.ts.map +1 -0
  66. package/dist/backend-msb/ls-json.js +113 -0
  67. package/dist/backend-msb/ls-json.js.map +1 -0
  68. package/dist/backend-msb/network-links.d.ts +7 -0
  69. package/dist/backend-msb/network-links.d.ts.map +1 -0
  70. package/dist/backend-msb/network-links.js +31 -0
  71. package/dist/backend-msb/network-links.js.map +1 -0
  72. package/dist/backend-msb/platform.d.ts +29 -0
  73. package/dist/backend-msb/platform.d.ts.map +1 -0
  74. package/dist/backend-msb/platform.js +128 -0
  75. package/dist/backend-msb/platform.js.map +1 -0
  76. package/dist/backend-msb/port-conflict.d.ts +10 -0
  77. package/dist/backend-msb/port-conflict.d.ts.map +1 -0
  78. package/dist/backend-msb/port-conflict.js +16 -0
  79. package/dist/backend-msb/port-conflict.js.map +1 -0
  80. package/dist/backend-msb/provider.d.ts +10 -0
  81. package/dist/backend-msb/provider.d.ts.map +1 -0
  82. package/dist/backend-msb/provider.js +40 -0
  83. package/dist/backend-msb/provider.js.map +1 -0
  84. package/dist/backend-msb/provisioner.d.ts +19 -0
  85. package/dist/backend-msb/provisioner.d.ts.map +1 -0
  86. package/dist/backend-msb/provisioner.js +332 -0
  87. package/dist/backend-msb/provisioner.js.map +1 -0
  88. package/dist/backend-msb/reaper.d.ts +10 -0
  89. package/dist/backend-msb/reaper.d.ts.map +1 -0
  90. package/dist/backend-msb/reaper.js +73 -0
  91. package/dist/backend-msb/reaper.js.map +1 -0
  92. package/dist/core/backend.d.ts +98 -0
  93. package/dist/core/backend.d.ts.map +1 -0
  94. package/dist/core/backend.js +2 -0
  95. package/dist/core/backend.js.map +1 -0
  96. package/dist/core/backends.d.ts +37 -0
  97. package/dist/core/backends.d.ts.map +1 -0
  98. package/dist/core/backends.js +127 -0
  99. package/dist/core/backends.js.map +1 -0
  100. package/dist/core/cleanup.d.ts +37 -0
  101. package/dist/core/cleanup.d.ts.map +1 -0
  102. package/dist/core/cleanup.js +86 -0
  103. package/dist/core/cleanup.js.map +1 -0
  104. package/dist/core/errors.d.ts +51 -0
  105. package/dist/core/errors.d.ts.map +1 -0
  106. package/dist/core/errors.js +66 -0
  107. package/dist/core/errors.js.map +1 -0
  108. package/dist/core/free-ports.d.ts +23 -0
  109. package/dist/core/free-ports.d.ts.map +1 -0
  110. package/dist/core/free-ports.js +63 -0
  111. package/dist/core/free-ports.js.map +1 -0
  112. package/dist/core/generic-container.d.ts +145 -0
  113. package/dist/core/generic-container.d.ts.map +1 -0
  114. package/dist/core/generic-container.js +385 -0
  115. package/dist/core/generic-container.js.map +1 -0
  116. package/dist/core/model.d.ts +60 -0
  117. package/dist/core/model.d.ts.map +1 -0
  118. package/dist/core/model.js +2 -0
  119. package/dist/core/model.js.map +1 -0
  120. package/dist/core/mountable-file.d.ts +25 -0
  121. package/dist/core/mountable-file.d.ts.map +1 -0
  122. package/dist/core/mountable-file.js +64 -0
  123. package/dist/core/mountable-file.js.map +1 -0
  124. package/dist/core/network.d.ts +58 -0
  125. package/dist/core/network.d.ts.map +1 -0
  126. package/dist/core/network.js +84 -0
  127. package/dist/core/network.js.map +1 -0
  128. package/dist/core/run-id.d.ts +18 -0
  129. package/dist/core/run-id.d.ts.map +1 -0
  130. package/dist/core/run-id.js +18 -0
  131. package/dist/core/run-id.js.map +1 -0
  132. package/dist/core/wait.d.ts +59 -0
  133. package/dist/core/wait.d.ts.map +1 -0
  134. package/dist/core/wait.js +239 -0
  135. package/dist/core/wait.js.map +1 -0
  136. package/dist/index.d.ts +26 -0
  137. package/dist/index.d.ts.map +1 -0
  138. package/dist/index.js +22 -0
  139. package/dist/index.js.map +1 -0
  140. package/dist/modules/arango.d.ts +14 -0
  141. package/dist/modules/arango.d.ts.map +1 -0
  142. package/dist/modules/arango.js +37 -0
  143. package/dist/modules/arango.js.map +1 -0
  144. package/dist/modules/clickhouse.d.ts +37 -0
  145. package/dist/modules/clickhouse.d.ts.map +1 -0
  146. package/dist/modules/clickhouse.js +69 -0
  147. package/dist/modules/clickhouse.js.map +1 -0
  148. package/dist/modules/flink.d.ts +65 -0
  149. package/dist/modules/flink.d.ts.map +1 -0
  150. package/dist/modules/flink.js +125 -0
  151. package/dist/modules/flink.js.map +1 -0
  152. package/dist/modules/floci.d.ts +46 -0
  153. package/dist/modules/floci.d.ts.map +1 -0
  154. package/dist/modules/floci.js +62 -0
  155. package/dist/modules/floci.js.map +1 -0
  156. package/dist/modules/index.d.ts +31 -0
  157. package/dist/modules/index.d.ts.map +1 -0
  158. package/dist/modules/index.js +31 -0
  159. package/dist/modules/index.js.map +1 -0
  160. package/dist/modules/kafka.d.ts +11 -0
  161. package/dist/modules/kafka.d.ts.map +1 -0
  162. package/dist/modules/kafka.js +41 -0
  163. package/dist/modules/kafka.js.map +1 -0
  164. package/dist/modules/keycloak.d.ts +40 -0
  165. package/dist/modules/keycloak.d.ts.map +1 -0
  166. package/dist/modules/keycloak.js +67 -0
  167. package/dist/modules/keycloak.js.map +1 -0
  168. package/dist/modules/mariadb.d.ts +56 -0
  169. package/dist/modules/mariadb.d.ts.map +1 -0
  170. package/dist/modules/mariadb.js +89 -0
  171. package/dist/modules/mariadb.js.map +1 -0
  172. package/dist/modules/memcached.d.ts +24 -0
  173. package/dist/modules/memcached.d.ts.map +1 -0
  174. package/dist/modules/memcached.js +105 -0
  175. package/dist/modules/memcached.js.map +1 -0
  176. package/dist/modules/mongodb.d.ts +19 -0
  177. package/dist/modules/mongodb.d.ts.map +1 -0
  178. package/dist/modules/mongodb.js +69 -0
  179. package/dist/modules/mongodb.js.map +1 -0
  180. package/dist/modules/mysql.d.ts +61 -0
  181. package/dist/modules/mysql.d.ts.map +1 -0
  182. package/dist/modules/mysql.js +101 -0
  183. package/dist/modules/mysql.js.map +1 -0
  184. package/dist/modules/neo4j.d.ts +60 -0
  185. package/dist/modules/neo4j.d.ts.map +1 -0
  186. package/dist/modules/neo4j.js +83 -0
  187. package/dist/modules/neo4j.js.map +1 -0
  188. package/dist/modules/pinot.d.ts +30 -0
  189. package/dist/modules/pinot.d.ts.map +1 -0
  190. package/dist/modules/pinot.js +49 -0
  191. package/dist/modules/pinot.js.map +1 -0
  192. package/dist/modules/postgres.d.ts +29 -0
  193. package/dist/modules/postgres.d.ts.map +1 -0
  194. package/dist/modules/postgres.js +73 -0
  195. package/dist/modules/postgres.js.map +1 -0
  196. package/dist/modules/rabbitmq.d.ts +46 -0
  197. package/dist/modules/rabbitmq.d.ts.map +1 -0
  198. package/dist/modules/rabbitmq.js +68 -0
  199. package/dist/modules/rabbitmq.js.map +1 -0
  200. package/dist/modules/redis.d.ts +15 -0
  201. package/dist/modules/redis.d.ts.map +1 -0
  202. package/dist/modules/redis.js +24 -0
  203. package/dist/modules/redis.js.map +1 -0
  204. package/dist/modules/redpanda.d.ts +27 -0
  205. package/dist/modules/redpanda.d.ts.map +1 -0
  206. package/dist/modules/redpanda.js +58 -0
  207. package/dist/modules/redpanda.js.map +1 -0
  208. package/dist/modules/spring-cloud-config.d.ts +18 -0
  209. package/dist/modules/spring-cloud-config.d.ts.map +1 -0
  210. package/dist/modules/spring-cloud-config.js +28 -0
  211. package/dist/modules/spring-cloud-config.js.map +1 -0
  212. package/dist/modules/wiremock.d.ts +21 -0
  213. package/dist/modules/wiremock.d.ts.map +1 -0
  214. package/dist/modules/wiremock.js +32 -0
  215. package/dist/modules/wiremock.js.map +1 -0
  216. package/package.json +104 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The Docker daemon's log-stream multiplexing frame format, present whenever
3
+ * a container is created without a TTY — this backend never allocates one,
4
+ * so this is the only shape `exec`/`logs`/`followLogs` ever see:
5
+ *
6
+ * frame = header ++ payload
7
+ * header = [ streamType: u8, 0u8, 0u8, 0u8, len: u32_be ] (8 bytes)
8
+ * payload = [u8; len]
9
+ * streamType: 0 = stdin, 1 = stdout, 2 = stderr
10
+ *
11
+ * `FrameDemuxer` accepts arbitrary Buffer chunks — frames straddle chunk
12
+ * boundaries with no relationship to how the daemon happened to flush its
13
+ * write buffer — and buffers a partial header/payload across `push()` calls,
14
+ * yielding only the frames a given chunk completes.
15
+ */
16
+ export type StreamType = "stdin" | "stdout" | "stderr";
17
+ export interface Frame {
18
+ readonly streamType: StreamType;
19
+ readonly payload: Buffer;
20
+ }
21
+ /** Stateful frame reassembler: feed it raw bytes as they arrive, get back every frame a call completes. */
22
+ export declare class FrameDemuxer {
23
+ private buffered;
24
+ /** Appends `chunk` to the pending buffer and returns every frame now fully available. */
25
+ push(chunk: Buffer): Frame[];
26
+ }
27
+ /**
28
+ * Reassembles complete lines out of a stream of text chunks whose
29
+ * boundaries are a chunking artifact, not a line break. A single log line
30
+ * can straddle two (or more) frames or Buffer reads with no relationship to
31
+ * where the actual `\n` falls; `feed` buffers a trailing partial line across
32
+ * calls and returns only the lines a given call completes. `flush` hands
33
+ * back that trailing fragment once at stream end — idempotent, since a
34
+ * terminal stream event (`end`/`close`) can legitimately fire more than once
35
+ * for the same close.
36
+ */
37
+ export declare class LineAssembler {
38
+ private pending;
39
+ private flushedOnce;
40
+ feed(text: string): string[];
41
+ /** Returns the trailing unterminated fragment exactly once, or undefined if there was none (or this was already called). */
42
+ flush(): string | undefined;
43
+ }
44
+ //# sourceMappingURL=frames.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frames.d.ts","sourceRoot":"","sources":["../../src/backend-docker/frames.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEvD,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAiBD,2GAA2G;AAC3G,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAA2B;IAE3C,yFAAyF;IACzF,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE;CAwB7B;AAED;;;;;;;;;GASG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,WAAW,CAAS;IAE5B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAqB5B,4HAA4H;IAC5H,KAAK,IAAI,MAAM,GAAG,SAAS;CAY5B"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * The Docker daemon's log-stream multiplexing frame format, present whenever
3
+ * a container is created without a TTY — this backend never allocates one,
4
+ * so this is the only shape `exec`/`logs`/`followLogs` ever see:
5
+ *
6
+ * frame = header ++ payload
7
+ * header = [ streamType: u8, 0u8, 0u8, 0u8, len: u32_be ] (8 bytes)
8
+ * payload = [u8; len]
9
+ * streamType: 0 = stdin, 1 = stdout, 2 = stderr
10
+ *
11
+ * `FrameDemuxer` accepts arbitrary Buffer chunks — frames straddle chunk
12
+ * boundaries with no relationship to how the daemon happened to flush its
13
+ * write buffer — and buffers a partial header/payload across `push()` calls,
14
+ * yielding only the frames a given chunk completes.
15
+ */
16
+ function streamTypeFromByte(b) {
17
+ switch (b) {
18
+ case 0:
19
+ return "stdin";
20
+ case 1:
21
+ return "stdout";
22
+ case 2:
23
+ return "stderr";
24
+ default:
25
+ return undefined;
26
+ }
27
+ }
28
+ const HEADER_LEN = 8;
29
+ /** Stateful frame reassembler: feed it raw bytes as they arrive, get back every frame a call completes. */
30
+ export class FrameDemuxer {
31
+ buffered = Buffer.alloc(0);
32
+ /** Appends `chunk` to the pending buffer and returns every frame now fully available. */
33
+ push(chunk) {
34
+ this.buffered = this.buffered.length === 0 ? Buffer.from(chunk) : Buffer.concat([this.buffered, chunk]);
35
+ const frames = [];
36
+ for (;;) {
37
+ if (this.buffered.length < HEADER_LEN) {
38
+ break;
39
+ }
40
+ const len = this.buffered.readUInt32BE(4);
41
+ if (this.buffered.length < HEADER_LEN + len) {
42
+ break;
43
+ }
44
+ const streamType = streamTypeFromByte(this.buffered.readUInt8(0));
45
+ const payload = this.buffered.subarray(HEADER_LEN, HEADER_LEN + len);
46
+ if (streamType !== undefined) {
47
+ frames.push({ streamType, payload: Buffer.from(payload) });
48
+ }
49
+ // An unrecognized stream-type byte is dropped rather than throwing —
50
+ // the frame's length prefix is still trustworthy, so skipping past it
51
+ // cleanly (instead of aborting the whole stream) is the same
52
+ // tolerant-of-drift posture as the msb ls-json scanner.
53
+ this.buffered = this.buffered.subarray(HEADER_LEN + len);
54
+ }
55
+ return frames;
56
+ }
57
+ }
58
+ /**
59
+ * Reassembles complete lines out of a stream of text chunks whose
60
+ * boundaries are a chunking artifact, not a line break. A single log line
61
+ * can straddle two (or more) frames or Buffer reads with no relationship to
62
+ * where the actual `\n` falls; `feed` buffers a trailing partial line across
63
+ * calls and returns only the lines a given call completes. `flush` hands
64
+ * back that trailing fragment once at stream end — idempotent, since a
65
+ * terminal stream event (`end`/`close`) can legitimately fire more than once
66
+ * for the same close.
67
+ */
68
+ export class LineAssembler {
69
+ pending = "";
70
+ flushedOnce = false;
71
+ feed(text) {
72
+ this.pending += text;
73
+ if (!this.pending.includes("\n")) {
74
+ return [];
75
+ }
76
+ const endsWithNewline = this.pending.endsWith("\n");
77
+ const parts = this.pending.split("\n");
78
+ // split("a\nb\n", "\n") => ["a","b",""] — the trailing "" is the
79
+ // "nothing pending" marker when the chunk ended exactly on a newline;
80
+ // split("a\nb", "\n") => ["a","b"], where "b" is genuinely pending.
81
+ // Either way the last element is never a completed line.
82
+ const tail = parts.pop() ?? "";
83
+ this.pending = endsWithNewline ? "" : tail;
84
+ // A genuinely-empty interior line (a blank line the workload printed) is
85
+ // real output and must be delivered — do not filter it out. Only the
86
+ // trailing "nothing pending" marker popped above is ever dropped; that's
87
+ // handled by the pop(), not by filtering here. Mirrors msb's replay
88
+ // contract in follow-replay.ts, so both backends agree on blank lines.
89
+ return parts;
90
+ }
91
+ /** Returns the trailing unterminated fragment exactly once, or undefined if there was none (or this was already called). */
92
+ flush() {
93
+ if (this.flushedOnce) {
94
+ return undefined;
95
+ }
96
+ this.flushedOnce = true;
97
+ if (this.pending.length === 0) {
98
+ return undefined;
99
+ }
100
+ const tail = this.pending;
101
+ this.pending = "";
102
+ return tail;
103
+ }
104
+ }
105
+ //# sourceMappingURL=frames.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frames.js","sourceRoot":"","sources":["../../src/backend-docker/frames.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { DockerBackend } from "./backend.js";
2
+ export { DockerBackendProvider } from "./provider.js";
3
+ export { DockerClient, socketPathFromDockerHost } from "./client.js";
4
+ export type { DockerResponse, DockerStreamResponse } from "./client.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backend-docker/index.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACrE,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * `rightsize/backend-docker` — the Docker daemon backend.
3
+ *
4
+ * Importing this module registers `DockerBackendProvider` with the core
5
+ * registry as a side effect (`priority: 10` — used when msb isn't supported,
6
+ * or when `RIGHTSIZE_BACKEND=docker` is set). Talks to the daemon over a
7
+ * hand-rolled unix-socket HTTP client (`node:http` with `socketPath`) rather
8
+ * than a general-purpose Docker SDK, so this backend can only ever dial the
9
+ * daemon's real unix socket — never a TCP host a dependency bump elsewhere
10
+ * could misroute it onto.
11
+ *
12
+ * @packageDocumentation
13
+ */
14
+ import { registerBackend } from "../core/backends.js";
15
+ import { DockerBackendProvider } from "./provider.js";
16
+ registerBackend(new DockerBackendProvider());
17
+ export { DockerBackend } from "./backend.js";
18
+ export { DockerBackendProvider } from "./provider.js";
19
+ export { DockerClient, socketPathFromDockerHost } from "./client.js";
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/backend-docker/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAEtD,eAAe,CAAC,IAAI,qBAAqB,EAAE,CAAC,CAAC;AAE7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Tolerant field extraction for daemon response bodies. Request bodies in
3
+ * this backend are fixed-shape and built with `JSON.stringify` — no need for
4
+ * tolerance there. Responses go through `JSON.parse` too, but wrapped so a
5
+ * daemon-version field addition or a genuinely malformed body degrades to
6
+ * "field not found" instead of throwing, matching the msb `ls` parser's own
7
+ * tolerance of schema drift (see `../backend-msb/ls-json.ts`).
8
+ */
9
+ export declare function extractString(body: string, field: string): string | undefined;
10
+ export declare function extractNumber(body: string, field: string): number | undefined;
11
+ /** Every `Id` field from a JSON array of objects — used for the container/network list endpoints. */
12
+ export declare function extractIds(body: string): string[];
13
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/backend-docker/json.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAUH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO7E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAO7E;AAED,qGAAqG;AACrG,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAYjD"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Tolerant field extraction for daemon response bodies. Request bodies in
3
+ * this backend are fixed-shape and built with `JSON.stringify` — no need for
4
+ * tolerance there. Responses go through `JSON.parse` too, but wrapped so a
5
+ * daemon-version field addition or a genuinely malformed body degrades to
6
+ * "field not found" instead of throwing, matching the msb `ls` parser's own
7
+ * tolerance of schema drift (see `../backend-msb/ls-json.ts`).
8
+ */
9
+ function parseOrUndefined(body) {
10
+ try {
11
+ return JSON.parse(body);
12
+ }
13
+ catch {
14
+ return undefined;
15
+ }
16
+ }
17
+ export function extractString(body, field) {
18
+ const parsed = parseOrUndefined(body);
19
+ if (parsed === undefined || parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
20
+ return undefined;
21
+ }
22
+ const value = parsed[field];
23
+ return typeof value === "string" ? value : undefined;
24
+ }
25
+ export function extractNumber(body, field) {
26
+ const parsed = parseOrUndefined(body);
27
+ if (parsed === undefined || parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
28
+ return undefined;
29
+ }
30
+ const value = parsed[field];
31
+ return typeof value === "number" ? value : undefined;
32
+ }
33
+ /** Every `Id` field from a JSON array of objects — used for the container/network list endpoints. */
34
+ export function extractIds(body) {
35
+ const parsed = parseOrUndefined(body);
36
+ if (!Array.isArray(parsed)) {
37
+ return [];
38
+ }
39
+ const ids = [];
40
+ for (const entry of parsed) {
41
+ if (entry !== null && typeof entry === "object" && typeof entry["Id"] === "string") {
42
+ ids.push(entry["Id"]);
43
+ }
44
+ }
45
+ return ids;
46
+ }
47
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/backend-docker/json.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,SAAS,gBAAgB,CAAC,IAAY;IACpC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAa;IACvD,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnG,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAI,MAAkC,CAAC,KAAK,CAAC,CAAC;IACzD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAa;IACvD,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnG,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,KAAK,GAAI,MAAkC,CAAC,KAAK,CAAC,CAAC;IACzD,OAAO,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACvD,CAAC;AAED,qGAAqG;AACrG,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,OAAQ,KAAiC,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;YAChH,GAAG,CAAC,IAAI,CAAE,KAAiC,CAAC,IAAI,CAAW,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The label key every container this backend creates carries, so `close()`
3
+ * and the reaper can find exactly this run's containers without touching
4
+ * anyone else's. The literal wire-format string is part of the library's
5
+ * external contract: any tool or process that filters on this label sees
6
+ * consistent labels regardless of which rightsize release created them.
7
+ */
8
+ export declare const RUN_ID_LABEL_KEY = "dev.rightsize.runId";
9
+ /** The `filters` query-string value for `GET /containers/json?filters=...`, scoped to one run id's label. */
10
+ export declare function labelFilterQuery(runId: string): string;
11
+ //# sourceMappingURL=labels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/backend-docker/labels.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,wBAAwB,CAAC;AAEtD,6GAA6G;AAC7G,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The label key every container this backend creates carries, so `close()`
3
+ * and the reaper can find exactly this run's containers without touching
4
+ * anyone else's. The literal wire-format string is part of the library's
5
+ * external contract: any tool or process that filters on this label sees
6
+ * consistent labels regardless of which rightsize release created them.
7
+ */
8
+ export const RUN_ID_LABEL_KEY = "dev.rightsize.runId";
9
+ /** The `filters` query-string value for `GET /containers/json?filters=...`, scoped to one run id's label. */
10
+ export function labelFilterQuery(runId) {
11
+ return JSON.stringify({ label: [`${RUN_ID_LABEL_KEY}=${runId}`] });
12
+ }
13
+ //# sourceMappingURL=labels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"labels.js","sourceRoot":"","sources":["../../src/backend-docker/labels.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,qBAAqB,CAAC;AAEtD,6GAA6G;AAC7G,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,gBAAgB,IAAI,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The daemon has no distinct exception type for a host-port bind conflict —
3
+ * only free-text in a 500 response body, such as "driver failed programming
4
+ * external connectivity: ... address already in use" or "Bind for
5
+ * 0.0.0.0:PORT failed: port is already allocated". Classify by message,
6
+ * case-insensitively.
7
+ */
8
+ export declare function isPortBindConflictMessage(message: string): boolean;
9
+ //# sourceMappingURL=port-conflict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port-conflict.d.ts","sourceRoot":"","sources":["../../src/backend-docker/port-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAGlE"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * The daemon has no distinct exception type for a host-port bind conflict —
3
+ * only free-text in a 500 response body, such as "driver failed programming
4
+ * external connectivity: ... address already in use" or "Bind for
5
+ * 0.0.0.0:PORT failed: port is already allocated". Classify by message,
6
+ * case-insensitively.
7
+ */
8
+ export function isPortBindConflictMessage(message) {
9
+ const m = message.toLowerCase();
10
+ return m.includes("already in use") || m.includes("already allocated");
11
+ }
12
+ //# sourceMappingURL=port-conflict.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port-conflict.js","sourceRoot":"","sources":["../../src/backend-docker/port-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAe;IACvD,MAAM,CAAC,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAChC,OAAO,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC;AACzE,CAAC"}
@@ -0,0 +1,22 @@
1
+ import type { BackendProvider, SandboxBackend } from "../core/backend.js";
2
+ /**
3
+ * `isSupported()` must be synchronous (the interface's contract — see
4
+ * `../core/backend.ts`), but the daemon's real reachability probe
5
+ * (`GET /_ping`) is inherently async over `node:http`. Rather than fake a
6
+ * synchronous HTTP round trip, this resolves to "the socket path exists and
7
+ * is a socket" via `fs.statSync().isSocket()`, deferring true reachability
8
+ * to first use — an existing-but-dead daemon surfaces a clear error the
9
+ * moment a real call is made. A blocking `/_ping` here would be the
10
+ * stronger check, but JS has no synchronous socket I/O to do it with, and
11
+ * the stat is behaviorally equivalent for backend resolution since a socket
12
+ * file present but genuinely unreachable is vanishingly rare.
13
+ */
14
+ export declare class DockerBackendProvider implements BackendProvider {
15
+ /** `"docker"` — matched case-insensitively against `RIGHTSIZE_BACKEND`. */
16
+ readonly name = "docker";
17
+ readonly priority = 10;
18
+ isSupported(): boolean;
19
+ unsupportedReason(): string;
20
+ create(): SandboxBackend;
21
+ }
22
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/backend-docker/provider.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAI1E;;;;;;;;;;;GAWG;AACH,qBAAa,qBAAsB,YAAW,eAAe;IAC3D,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,QAAQ,MAAM;IAEvB,WAAW,IAAI,OAAO;IAStB,iBAAiB,IAAI,MAAM;IAI3B,MAAM,IAAI,cAAc;CAGzB"}
@@ -0,0 +1,36 @@
1
+ import * as fs from "node:fs";
2
+ import { DockerBackend } from "./backend.js";
3
+ import { DockerClient, socketPathFromDockerHost } from "./client.js";
4
+ /**
5
+ * `isSupported()` must be synchronous (the interface's contract — see
6
+ * `../core/backend.ts`), but the daemon's real reachability probe
7
+ * (`GET /_ping`) is inherently async over `node:http`. Rather than fake a
8
+ * synchronous HTTP round trip, this resolves to "the socket path exists and
9
+ * is a socket" via `fs.statSync().isSocket()`, deferring true reachability
10
+ * to first use — an existing-but-dead daemon surfaces a clear error the
11
+ * moment a real call is made. A blocking `/_ping` here would be the
12
+ * stronger check, but JS has no synchronous socket I/O to do it with, and
13
+ * the stat is behaviorally equivalent for backend resolution since a socket
14
+ * file present but genuinely unreachable is vanishingly rare.
15
+ */
16
+ export class DockerBackendProvider {
17
+ /** `"docker"` — matched case-insensitively against `RIGHTSIZE_BACKEND`. */
18
+ name = "docker";
19
+ priority = 10;
20
+ isSupported() {
21
+ const socketPath = socketPathFromDockerHost(process.env["DOCKER_HOST"]);
22
+ try {
23
+ return fs.statSync(socketPath).isSocket();
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ unsupportedReason() {
30
+ return "no reachable Docker-API socket (Docker/Podman/Colima not running?)";
31
+ }
32
+ create() {
33
+ return new DockerBackend(DockerClient.fromEnv());
34
+ }
35
+ }
36
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/backend-docker/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,YAAY,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAErE;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,qBAAqB;IAChC,2EAA2E;IAClE,IAAI,GAAG,QAAQ,CAAC;IAChB,QAAQ,GAAG,EAAE,CAAC;IAEvB,WAAW;QACT,MAAM,UAAU,GAAG,wBAAwB,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC;QACxE,IAAI,CAAC;YACH,OAAO,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,iBAAiB;QACf,OAAO,oEAAoE,CAAC;IAC9E,CAAC;IAED,MAAM;QACJ,OAAO,IAAI,aAAa,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;CACF"}
@@ -0,0 +1,169 @@
1
+ import type { SandboxBackend, SandboxHandle, FollowHandle, NetworkLink } from "../core/backend.js";
2
+ import type { ContainerSpec, ExecResult } from "../core/model.js";
3
+ /**
4
+ * The attached-mode CLI driver: every sandbox this backend starts runs as a
5
+ * held child process (`msb run`, no `-d`) because detached mode never
6
+ * executes the image's own ENTRYPOINT/CMD — only attached mode does.
7
+ * Readiness is never inferred from that child's own output; it comes from
8
+ * polling `msb ls --format json` until the name shows `"Running"`. The
9
+ * attached child's stdout/stderr carries msb's own boot diagnostics and is
10
+ * kept only for pre-Running failure messages — it is not a dependable
11
+ * workload-log source (on Windows it does not relay guest stdout at all);
12
+ * workload logs are always fetched through the `msb logs` channel.
13
+ *
14
+ * `create()` on the `BackendProvider` interface is synchronous, but locating
15
+ * (and possibly downloading) the pinned `msb` binary is inherently async.
16
+ * The seam: this backend holds a memoized `ensureInstalled()` promise and
17
+ * every method awaits it once before doing anything else, so construction
18
+ * stays cheap and the actual provisioning happens lazily on first use.
19
+ */
20
+ export declare class MsbCliBackend implements SandboxBackend {
21
+ private readonly msbPathPromise;
22
+ /** `"microsandbox"` — matched against `RIGHTSIZE_BACKEND` and used in `dev.rightsize.runId`-style diagnostics. */
23
+ readonly name = "microsandbox";
24
+ readonly supportsNativeNetworks = false;
25
+ private readonly handles;
26
+ private readonly startedNames;
27
+ private resolvedMsbPath;
28
+ constructor(msbPathPromise: Promise<string>);
29
+ private msbPath;
30
+ create(spec: ContainerSpec): Promise<SandboxHandle>;
31
+ /**
32
+ * Boots via `bootOnce`; on a first failure carrying msb's image-cache-
33
+ * corruption signature (see `isImageCacheCorruption`), heals by removing
34
+ * just the affected image's cache entry (`msb image remove <image>`, result
35
+ * ignored — including "image not found", since the real signal is whether
36
+ * the retried boot succeeds, not whether removal reported success) and
37
+ * retries the boot exactly once. A second identical failure surfaces an
38
+ * error naming the image and the attempted heal instead of retrying
39
+ * further. The heal is scoped to the one image reference — never the whole
40
+ * cache directory, and never any sandbox state.
41
+ *
42
+ * The failed first attempt never reached Running, so `state.attached` and
43
+ * `startedNames` (both populated only on success, inside `bootOnce`) carry
44
+ * no state from it to double-register, and its child has already been
45
+ * reaped there.
46
+ *
47
+ * Two corruption shapes were found empirically and the same one command
48
+ * heals both: the failing image's manifest was never committed to msb's
49
+ * cache database (a concurrent pull lost the race for a shared base layer
50
+ * before its own manifest write landed) — `image remove` reports "image not
51
+ * found" and the retry succeeds anyway, because by then the concurrent
52
+ * winner has finished materializing the shared layer — or the manifest IS
53
+ * committed but the cache file backing one of its layers is gone, where
54
+ * `image remove` clears the stale entry and the retry re-pulls from
55
+ * scratch.
56
+ */
57
+ start(handle: SandboxHandle): Promise<void>;
58
+ /**
59
+ * One boot attempt: spawns the attached `msb run` child and polls until the
60
+ * sandbox reaches Running. `state.attached` and `startedNames` are
61
+ * populated only on success; on any failure the child is reaped here (for
62
+ * the classified early-exit failures it has already exited; a readiness
63
+ * timeout leaves it alive and it is hard-killed) so a failed attempt leaves
64
+ * no live process or registered cleanup state behind — the caller owns
65
+ * retry policy, never cleanup. An early exit is classified from the child's
66
+ * combined output: the image-cache-corruption signature throws
67
+ * `ImageCacheCorruptionError` (the one failure `start()` heals and
68
+ * retries), a host-port bind conflict throws `PortBindConflictError`, and
69
+ * anything else surfaces the raw output.
70
+ */
71
+ private bootOnce;
72
+ stop(handle: SandboxHandle): Promise<void>;
73
+ remove(handle: SandboxHandle): Promise<void>;
74
+ private runningSandboxNames;
75
+ exec(handle: SandboxHandle, cmd: ReadonlyArray<string>): Promise<ExecResult>;
76
+ /**
77
+ * A fresh `msb logs <name> --tail 1000` invocation, same on every platform.
78
+ * This is the workload's own output, as distinct from the attached
79
+ * `msb run` child's pipe (drained in start() into a tail kept only for
80
+ * pre-Running crash diagnostics): on Windows the attached process does not
81
+ * relay guest stdout at all, while `msb logs` does everywhere, so this is
82
+ * the only channel this method can source from. Never rejects on a
83
+ * missing/removed sandbox — invoke() only rejects on spawn failure or
84
+ * timeout, never on exit code, so a failing `msb logs` call resolves with
85
+ * whatever (possibly empty) stdout it produced.
86
+ */
87
+ logs(handle: SandboxHandle): Promise<string>;
88
+ /**
89
+ * `msb logs -f` never exits once the sandbox stops (confirmed against the
90
+ * real msb binary — it blocks on read forever instead of the documented
91
+ * clean exit). A watchdog polls `msb ls` in the background; the instant
92
+ * the sandbox leaves Running it quiesces the stuck follow child FIRST
93
+ * (kill it, wait for the reader to finish draining whatever was already
94
+ * buffered) so `delivered` reflects everything the live stream will ever
95
+ * produce, THEN does one authoritative non-follow `msb logs` fetch and
96
+ * replays only the lines after `delivered` — guarded so that replay can
97
+ * only ever happen once. An explicit `close()` never triggers a replay:
98
+ * closing means the caller asked delivery to stop, not "catch me up."
99
+ *
100
+ * On Windows hosts this routes to `followLogsByPolling` instead: there,
101
+ * `msb logs -f` stays alive for the sandbox's whole run but never relays a
102
+ * single line to its stdout pipe while the sandbox is Running (confirmed
103
+ * against the real binary on a hosted windows-2025 runner — the same lines
104
+ * are retrievable through non-follow `msb logs` the whole time), so a
105
+ * pipe-reading follow child can never deliver a live line on Windows.
106
+ */
107
+ followLogs(handle: SandboxHandle, consumer: (line: string) => void): Promise<FollowHandle>;
108
+ /**
109
+ * Windows follow-logs path: no follow child at all. One async loop polls
110
+ * the non-follow `msb logs` fetch and delivers each fetch's not-yet-
111
+ * delivered lines, tracked by a monotonic `delivered` index — the same
112
+ * index-based diffing the POSIX watchdog's one-shot replay uses, made
113
+ * continuous. Delivery contract is identical to the POSIX path: in order,
114
+ * each line at most once, nothing after `close()`.
115
+ *
116
+ * A fetch's last line is held back only while the sandbox is Running AND
117
+ * the fetched text does not end with a newline: an unterminated tail may
118
+ * have been read mid-write, and delivering it early would split one
119
+ * workload line into two deliveries (the next fetch's index-diff would then
120
+ * skip its completed form). A newline-terminated tail is complete and
121
+ * delivers immediately — a workload that trickles complete lines while
122
+ * staying alive must see each line arrive live, not stall behind a
123
+ * holdback. Once the sandbox leaves Running, a final fetch (see
124
+ * `deliverTerminalTail`) delivers everything outstanding, including a
125
+ * trailing unterminated line.
126
+ *
127
+ * Every msb invocation this poller makes runs to completion strictly before
128
+ * the next one starts — exactly one in-flight child at a time from this
129
+ * code path. A failed `msb ls`/`msb logs` invocation — spawn error,
130
+ * timeout, or msb exiting non-zero (msb's own internal errors print to
131
+ * stderr and exit non-zero with EMPTY stdout, indistinguishable from a
132
+ * genuinely-empty response unless the exit code is checked) — is never read
133
+ * as "the sandbox stopped" or "the log is empty"; it retries.
134
+ */
135
+ private followLogsByPolling;
136
+ /**
137
+ * Delivers everything outstanding once the sandbox is confirmed no longer
138
+ * Running: retries the `msb logs` fetch only while it keeps failing to
139
+ * invoke at all (bounded by `TERMINAL_FETCH_FAILURE_BUDGET_MS`), and
140
+ * delivers from the very first successful fetch — withholding nothing,
141
+ * since a stopped sandbox's log cannot grow, so there is no more mid-write
142
+ * risk. This is the one place a trailing unterminated line reaches the
143
+ * consumer on Windows.
144
+ */
145
+ private deliverTerminalTail;
146
+ ensureNetwork(_networkId: string): Promise<void>;
147
+ removeNetwork(_networkId: string): Promise<void>;
148
+ /**
149
+ * Networks are emulated because there is no bridge/subnet the current
150
+ * msb exposes on macOS — the only data path into a running sandbox is the
151
+ * exec channel. Four concerns, each its own guard: reject duplicate guest
152
+ * ports, validate every alias (they get shell-interpolated), probe for
153
+ * `nc`, then install `/etc/hosts` aliases and spawn one tunnel per link.
154
+ */
155
+ installNetworkLinks(handle: SandboxHandle, links: ReadonlyArray<NetworkLink>): Promise<void>;
156
+ close(): Promise<void>;
157
+ /**
158
+ * Synchronous, blocking teardown for the process-exit path, where the
159
+ * event loop is not available to await this backend's normal async
160
+ * stop/remove. `spawnSync` is the one primitive Node offers that actually
161
+ * blocks the process here; failures are swallowed because the process is
162
+ * exiting regardless and there is no caller left to report them to.
163
+ */
164
+ cleanupSync(id: string): void;
165
+ /** Remove leftover `rz-<other-runid>-*` sandboxes from a crashed prior run — never this run's own. */
166
+ sweepOrphans(): Promise<void>;
167
+ private silently;
168
+ }
169
+ //# sourceMappingURL=backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/backend-msb/backend.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACnG,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAwJlE;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,aAAc,YAAW,cAAc;IAatC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAZ3C,kHAAkH;IAClH,QAAQ,CAAC,IAAI,kBAAkB;IAC/B,QAAQ,CAAC,sBAAsB,SAAS;IAExC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAkC;IAC1D,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;IAKlD,OAAO,CAAC,eAAe,CAAqB;gBAEf,cAAc,EAAE,OAAO,CAAC,MAAM,CAAC;YAa9C,OAAO;IAIf,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC;IAKzD;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACG,KAAK,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAsCjD;;;;;;;;;;;;OAYG;YACW,QAAQ;IAyDhB,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAiD1C,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;YAOpC,mBAAmB;IAK3B,IAAI,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,UAAU,CAAC;IAKlF;;;;;;;;;;OAUG;IACG,IAAI,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC;IAKlD;;;;;;;;;;;;;;;;;;OAkBG;IACG,UAAU,CAAC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC;IA+DhG;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,CAAC,mBAAmB;IAyE3B;;;;;;;;OAQG;YACW,mBAAmB;IAmC3B,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhD,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD;;;;;;OAMG;IACG,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IA+B5F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAU5B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAsB7B,sGAAsG;IAChG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAQrB,QAAQ;CAIvB"}