@elie-laloum/outpost 2.0.0 → 3.0.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 (300) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/adapters/agents/claude-diagnostics.d.ts +1 -0
  3. package/dist/adapters/agents/claude-diagnostics.js +7 -0
  4. package/dist/adapters/agents/claude-diagnostics.js.map +1 -0
  5. package/dist/adapters/agents/cli-diagnostics.constants.d.ts +12 -0
  6. package/dist/adapters/agents/cli-diagnostics.constants.js +24 -0
  7. package/dist/adapters/agents/cli-diagnostics.constants.js.map +1 -0
  8. package/dist/adapters/agents/cli-diagnostics.d.ts +4 -0
  9. package/dist/adapters/agents/cli-diagnostics.js +14 -0
  10. package/dist/adapters/agents/cli-diagnostics.js.map +1 -0
  11. package/dist/adapters/agents/cli-diagnostics.types.d.ts +13 -0
  12. package/dist/adapters/agents/cli-diagnostics.types.js +2 -0
  13. package/dist/adapters/agents/cli-diagnostics.types.js.map +1 -0
  14. package/dist/adapters/agents/codex-diagnostics.d.ts +1 -0
  15. package/dist/adapters/agents/codex-diagnostics.js +7 -0
  16. package/dist/adapters/agents/codex-diagnostics.js.map +1 -0
  17. package/dist/adapters/agents/protocol-fixtures.constants.d.ts +2 -0
  18. package/dist/adapters/agents/protocol-fixtures.constants.js +68 -0
  19. package/dist/adapters/agents/protocol-fixtures.constants.js.map +1 -0
  20. package/dist/adapters/agents/protocol-fixtures.types.d.ts +6 -0
  21. package/dist/adapters/agents/protocol-fixtures.types.js +2 -0
  22. package/dist/adapters/agents/protocol-fixtures.types.js.map +1 -0
  23. package/dist/application/diagnostic-probe.d.ts +3 -0
  24. package/dist/application/diagnostic-probe.js +55 -0
  25. package/dist/application/diagnostic-probe.js.map +1 -0
  26. package/dist/application/doctor-agent.constants.d.ts +9 -0
  27. package/dist/application/doctor-agent.constants.js +8 -0
  28. package/dist/application/doctor-agent.constants.js.map +1 -0
  29. package/dist/application/doctor-agent.d.ts +3 -0
  30. package/dist/application/doctor-agent.js +53 -0
  31. package/dist/application/doctor-agent.js.map +1 -0
  32. package/dist/application/doctor-image.d.ts +3 -0
  33. package/dist/application/doctor-image.js +133 -0
  34. package/dist/application/doctor-image.js.map +1 -0
  35. package/dist/application/doctor-protocol.d.ts +3 -0
  36. package/dist/application/doctor-protocol.js +29 -0
  37. package/dist/application/doctor-protocol.js.map +1 -0
  38. package/dist/application/doctor-protocol.types.d.ts +10 -0
  39. package/dist/application/doctor-protocol.types.js +2 -0
  40. package/dist/application/doctor-protocol.types.js.map +1 -0
  41. package/dist/application/doctor-sandbox.constants.d.ts +5 -0
  42. package/dist/application/doctor-sandbox.constants.js +6 -0
  43. package/dist/application/doctor-sandbox.constants.js.map +1 -0
  44. package/dist/application/doctor-sandbox.d.ts +4 -0
  45. package/dist/application/doctor-sandbox.js +133 -0
  46. package/dist/application/doctor-sandbox.js.map +1 -0
  47. package/dist/application/doctor-sandbox.types.d.ts +23 -0
  48. package/dist/application/doctor-sandbox.types.js +2 -0
  49. package/dist/application/doctor-sandbox.types.js.map +1 -0
  50. package/dist/application/doctor-transfers.constants.d.ts +6 -0
  51. package/dist/application/doctor-transfers.constants.js +7 -0
  52. package/dist/application/doctor-transfers.constants.js.map +1 -0
  53. package/dist/application/doctor-transfers.d.ts +4 -0
  54. package/dist/application/doctor-transfers.js +88 -0
  55. package/dist/application/doctor-transfers.js.map +1 -0
  56. package/dist/application/doctor.constants.d.ts +9 -0
  57. package/dist/application/doctor.constants.js +15 -0
  58. package/dist/application/doctor.constants.js.map +1 -0
  59. package/dist/application/doctor.d.ts +3 -0
  60. package/dist/application/doctor.js +106 -0
  61. package/dist/application/doctor.js.map +1 -0
  62. package/dist/application/doctor.types.d.ts +39 -0
  63. package/dist/application/doctor.types.js +2 -0
  64. package/dist/application/doctor.types.js.map +1 -0
  65. package/dist/application/execution.js +1 -0
  66. package/dist/application/execution.js.map +1 -1
  67. package/dist/application/outpost.types.d.ts +2 -0
  68. package/dist/application/outpost.types.js.map +1 -1
  69. package/dist/application/recovery-checksum-capture.d.ts +2 -0
  70. package/dist/application/recovery-checksum-capture.js +32 -0
  71. package/dist/application/recovery-checksum-capture.js.map +1 -0
  72. package/dist/application/recovery-checksum-paths.d.ts +2 -0
  73. package/dist/application/recovery-checksum-paths.js +11 -0
  74. package/dist/application/recovery-checksum-paths.js.map +1 -0
  75. package/dist/application/recovery-checksums.constants.d.ts +5 -0
  76. package/dist/application/recovery-checksums.constants.js +6 -0
  77. package/dist/application/recovery-checksums.constants.js.map +1 -0
  78. package/dist/application/recovery-checksums.d.ts +3 -0
  79. package/dist/application/recovery-checksums.js +94 -0
  80. package/dist/application/recovery-checksums.js.map +1 -0
  81. package/dist/application/recovery-checksums.types.d.ts +17 -0
  82. package/dist/application/recovery-checksums.types.js +2 -0
  83. package/dist/application/recovery-checksums.types.js.map +1 -0
  84. package/dist/application/recovery-inspection.d.ts +2 -0
  85. package/dist/application/recovery-inspection.js +27 -0
  86. package/dist/application/recovery-inspection.js.map +1 -0
  87. package/dist/application/recovery-inspection.types.d.ts +15 -0
  88. package/dist/application/recovery-inspection.types.js +2 -0
  89. package/dist/application/recovery-inspection.types.js.map +1 -0
  90. package/dist/application/recovery-restorability.d.ts +2 -0
  91. package/dist/application/recovery-restorability.js +137 -0
  92. package/dist/application/recovery-restorability.js.map +1 -0
  93. package/dist/application/recovery-retention-policy.d.ts +2 -0
  94. package/dist/application/recovery-retention-policy.js +21 -0
  95. package/dist/application/recovery-retention-policy.js.map +1 -0
  96. package/dist/application/recovery-retention.constants.d.ts +3 -0
  97. package/dist/application/recovery-retention.constants.js +2 -0
  98. package/dist/application/recovery-retention.constants.js.map +1 -0
  99. package/dist/application/recovery-retention.d.ts +4 -0
  100. package/dist/application/recovery-retention.js +202 -0
  101. package/dist/application/recovery-retention.js.map +1 -0
  102. package/dist/application/recovery-retention.types.d.ts +52 -0
  103. package/dist/application/recovery-retention.types.js +2 -0
  104. package/dist/application/recovery-retention.types.js.map +1 -0
  105. package/dist/application/recovery-verification.constants.d.ts +5 -0
  106. package/dist/application/recovery-verification.constants.js +10 -0
  107. package/dist/application/recovery-verification.constants.js.map +1 -0
  108. package/dist/application/recovery-verification.d.ts +2 -0
  109. package/dist/application/recovery-verification.js +107 -0
  110. package/dist/application/recovery-verification.js.map +1 -0
  111. package/dist/application/recovery-verification.types.d.ts +26 -0
  112. package/dist/application/recovery-verification.types.js +2 -0
  113. package/dist/application/recovery-verification.types.js.map +1 -0
  114. package/dist/application/remote-apply.js +24 -6
  115. package/dist/application/remote-apply.js.map +1 -1
  116. package/dist/application/remote-backup.js +6 -2
  117. package/dist/application/remote-backup.js.map +1 -1
  118. package/dist/application/remote-download.js +36 -4
  119. package/dist/application/remote-download.js.map +1 -1
  120. package/dist/application/remote-validation.js +19 -0
  121. package/dist/application/remote-validation.js.map +1 -1
  122. package/dist/application/remote-workspace.constants.d.ts +3 -0
  123. package/dist/application/remote-workspace.constants.js +2 -0
  124. package/dist/application/remote-workspace.constants.js.map +1 -0
  125. package/dist/application/remote-workspace.js +6 -0
  126. package/dist/application/remote-workspace.js.map +1 -1
  127. package/dist/application/remote-workspace.types.d.ts +3 -1
  128. package/dist/application/remote-workspace.types.js.map +1 -1
  129. package/dist/application/sandbox.js +11 -0
  130. package/dist/application/sandbox.js.map +1 -1
  131. package/dist/application/task-usage.d.ts +4 -0
  132. package/dist/application/task-usage.js +41 -0
  133. package/dist/application/task-usage.js.map +1 -0
  134. package/dist/application/task-usage.types.d.ts +5 -0
  135. package/dist/application/task-usage.types.js +2 -0
  136. package/dist/application/task-usage.types.js.map +1 -0
  137. package/dist/application/tasks.js +23 -2
  138. package/dist/application/tasks.js.map +1 -1
  139. package/dist/cli/commands.js +4 -0
  140. package/dist/cli/commands.js.map +1 -1
  141. package/dist/cli/doctor-command.d.ts +3 -0
  142. package/dist/cli/doctor-command.js +38 -0
  143. package/dist/cli/doctor-command.js.map +1 -0
  144. package/dist/cli/main.constants.d.ts +28 -1
  145. package/dist/cli/main.constants.js +15 -0
  146. package/dist/cli/main.constants.js.map +1 -1
  147. package/dist/cli/recovery-command.d.ts +2 -0
  148. package/dist/cli/recovery-command.js +69 -0
  149. package/dist/cli/recovery-command.js.map +1 -0
  150. package/dist/cli/recovery-prune-command.d.ts +2 -0
  151. package/dist/cli/recovery-prune-command.js +34 -0
  152. package/dist/cli/recovery-prune-command.js.map +1 -0
  153. package/dist/cli/recovery-verify-command.d.ts +2 -0
  154. package/dist/cli/recovery-verify-command.js +43 -0
  155. package/dist/cli/recovery-verify-command.js.map +1 -0
  156. package/dist/domain/ports.d.ts +1 -1
  157. package/dist/domain/ports.js.map +1 -1
  158. package/dist/domain/sandbox.types.d.ts +12 -0
  159. package/dist/domain/sandbox.types.js.map +1 -1
  160. package/dist/domain/workflow/budget.constants.d.ts +1 -0
  161. package/dist/domain/workflow/budget.constants.js +7 -0
  162. package/dist/domain/workflow/budget.constants.js.map +1 -0
  163. package/dist/domain/workflow/budget.d.ts +9 -0
  164. package/dist/domain/workflow/budget.js +67 -0
  165. package/dist/domain/workflow/budget.js.map +1 -0
  166. package/dist/domain/workflow/budget.types.d.ts +15 -0
  167. package/dist/domain/workflow/budget.types.js +2 -0
  168. package/dist/domain/workflow/budget.types.js.map +1 -0
  169. package/dist/domain/workflow/task-runner.js +8 -1
  170. package/dist/domain/workflow/task-runner.js.map +1 -1
  171. package/dist/domain/workflow/workflow-scheduler.js +4 -2
  172. package/dist/domain/workflow/workflow-scheduler.js.map +1 -1
  173. package/dist/domain/workflow/workflow-state.js +29 -0
  174. package/dist/domain/workflow/workflow-state.js.map +1 -1
  175. package/dist/domain/workflow.d.ts +2 -0
  176. package/dist/domain/workflow.js +1 -0
  177. package/dist/domain/workflow.js.map +1 -1
  178. package/dist/domain/workflow.types.d.ts +10 -1
  179. package/dist/domain/workflow.types.js.map +1 -1
  180. package/dist/index.d.ts +12 -3
  181. package/dist/index.js +5 -1
  182. package/dist/index.js.map +1 -1
  183. package/dist/infrastructure/file-manifest.d.ts +6 -0
  184. package/dist/infrastructure/file-manifest.js +91 -0
  185. package/dist/infrastructure/file-manifest.js.map +1 -0
  186. package/dist/infrastructure/git/lock-inspection.constants.d.ts +4 -0
  187. package/dist/infrastructure/git/lock-inspection.constants.js +5 -0
  188. package/dist/infrastructure/git/lock-inspection.constants.js.map +1 -0
  189. package/dist/infrastructure/git/lock-inspection.d.ts +3 -0
  190. package/dist/infrastructure/git/lock-inspection.js +64 -0
  191. package/dist/infrastructure/git/lock-inspection.js.map +1 -0
  192. package/dist/infrastructure/git/lock-inspection.types.d.ts +27 -0
  193. package/dist/infrastructure/git/lock-inspection.types.js +2 -0
  194. package/dist/infrastructure/git/lock-inspection.types.js.map +1 -0
  195. package/dist/infrastructure/git/lock.d.ts +1 -0
  196. package/dist/infrastructure/git/lock.js +70 -29
  197. package/dist/infrastructure/git/lock.js.map +1 -1
  198. package/dist/infrastructure/git/process-identity.d.ts +3 -0
  199. package/dist/infrastructure/git/process-identity.js +73 -0
  200. package/dist/infrastructure/git/process-identity.js.map +1 -0
  201. package/dist/infrastructure/git/process-identity.types.d.ts +10 -0
  202. package/dist/infrastructure/git/process-identity.types.js +2 -0
  203. package/dist/infrastructure/git/process-identity.types.js.map +1 -0
  204. package/dist/infrastructure/git/verification-command.d.ts +1 -0
  205. package/dist/infrastructure/git/verification-command.js +36 -0
  206. package/dist/infrastructure/git/verification-command.js.map +1 -0
  207. package/dist/infrastructure/git/workspace-lease.js +1 -0
  208. package/dist/infrastructure/git/workspace-lease.js.map +1 -1
  209. package/dist/infrastructure/git/workspace.js +5 -1
  210. package/dist/infrastructure/git/workspace.js.map +1 -1
  211. package/dist/infrastructure/git/worktree-inspection.constants.d.ts +5 -0
  212. package/dist/infrastructure/git/worktree-inspection.constants.js +16 -0
  213. package/dist/infrastructure/git/worktree-inspection.constants.js.map +1 -0
  214. package/dist/infrastructure/git/worktree-inspection.d.ts +3 -0
  215. package/dist/infrastructure/git/worktree-inspection.js +120 -0
  216. package/dist/infrastructure/git/worktree-inspection.js.map +1 -0
  217. package/dist/infrastructure/git/worktree-inspection.types.d.ts +19 -0
  218. package/dist/infrastructure/git/worktree-inspection.types.js +2 -0
  219. package/dist/infrastructure/git/worktree-inspection.types.js.map +1 -0
  220. package/dist/infrastructure/inspection-file.constants.d.ts +1 -0
  221. package/dist/infrastructure/inspection-file.constants.js +5 -0
  222. package/dist/infrastructure/inspection-file.constants.js.map +1 -0
  223. package/dist/infrastructure/inspection-file.d.ts +1 -0
  224. package/dist/infrastructure/inspection-file.js +43 -0
  225. package/dist/infrastructure/inspection-file.js.map +1 -0
  226. package/dist/infrastructure/inspection-hash.constants.d.ts +1 -0
  227. package/dist/infrastructure/inspection-hash.constants.js +2 -0
  228. package/dist/infrastructure/inspection-hash.constants.js.map +1 -0
  229. package/dist/infrastructure/inspection-hash.d.ts +2 -0
  230. package/dist/infrastructure/inspection-hash.js +62 -0
  231. package/dist/infrastructure/inspection-hash.js.map +1 -0
  232. package/dist/infrastructure/inspection-hash.types.d.ts +5 -0
  233. package/dist/infrastructure/inspection-hash.types.js +2 -0
  234. package/dist/infrastructure/inspection-hash.types.js.map +1 -0
  235. package/dist/infrastructure/journal-retention.constants.d.ts +4 -0
  236. package/dist/infrastructure/journal-retention.constants.js +5 -0
  237. package/dist/infrastructure/journal-retention.constants.js.map +1 -0
  238. package/dist/infrastructure/journal-retention.d.ts +2 -0
  239. package/dist/infrastructure/journal-retention.js +51 -0
  240. package/dist/infrastructure/journal-retention.js.map +1 -0
  241. package/dist/infrastructure/journal.js +37 -13
  242. package/dist/infrastructure/journal.js.map +1 -1
  243. package/dist/infrastructure/opentelemetry.constants.d.ts +10 -0
  244. package/dist/infrastructure/opentelemetry.constants.js +11 -0
  245. package/dist/infrastructure/opentelemetry.constants.js.map +1 -0
  246. package/dist/infrastructure/opentelemetry.d.ts +3 -0
  247. package/dist/infrastructure/opentelemetry.js +131 -0
  248. package/dist/infrastructure/opentelemetry.js.map +1 -0
  249. package/dist/infrastructure/opentelemetry.types.d.ts +21 -0
  250. package/dist/infrastructure/opentelemetry.types.js +2 -0
  251. package/dist/infrastructure/opentelemetry.types.js.map +1 -0
  252. package/dist/infrastructure/storage-inventory.constants.d.ts +6 -0
  253. package/dist/infrastructure/storage-inventory.constants.js +11 -0
  254. package/dist/infrastructure/storage-inventory.constants.js.map +1 -0
  255. package/dist/infrastructure/storage-inventory.d.ts +2 -0
  256. package/dist/infrastructure/storage-inventory.js +123 -0
  257. package/dist/infrastructure/storage-inventory.js.map +1 -0
  258. package/dist/infrastructure/storage-inventory.types.d.ts +38 -0
  259. package/dist/infrastructure/storage-inventory.types.js +2 -0
  260. package/dist/infrastructure/storage-inventory.types.js.map +1 -0
  261. package/dist/infrastructure/transfer.js +12 -0
  262. package/dist/infrastructure/transfer.js.map +1 -1
  263. package/dist/providers/cloud.constants.d.ts +1 -0
  264. package/dist/providers/cloud.constants.js +1 -0
  265. package/dist/providers/cloud.constants.js.map +1 -1
  266. package/dist/providers/container-cache.constants.d.ts +11 -0
  267. package/dist/providers/container-cache.constants.js +9 -0
  268. package/dist/providers/container-cache.constants.js.map +1 -0
  269. package/dist/providers/container-cache.d.ts +4 -0
  270. package/dist/providers/container-cache.js +48 -0
  271. package/dist/providers/container-cache.js.map +1 -0
  272. package/dist/providers/container-cache.types.d.ts +8 -0
  273. package/dist/providers/container-cache.types.js +2 -0
  274. package/dist/providers/container-cache.types.js.map +1 -0
  275. package/dist/providers/container-plan.js +3 -1
  276. package/dist/providers/container-plan.js.map +1 -1
  277. package/dist/providers/container.js +31 -1
  278. package/dist/providers/container.js.map +1 -1
  279. package/dist/providers/container.types.d.ts +2 -0
  280. package/dist/providers/container.types.js.map +1 -1
  281. package/dist/providers/daytona-command.js +7 -4
  282. package/dist/providers/daytona-command.js.map +1 -1
  283. package/dist/providers/daytona.js +13 -4
  284. package/dist/providers/daytona.js.map +1 -1
  285. package/dist/providers/diagnostic-container.d.ts +4 -0
  286. package/dist/providers/diagnostic-container.js +15 -0
  287. package/dist/providers/diagnostic-container.js.map +1 -0
  288. package/dist/providers/docker.d.ts +1 -0
  289. package/dist/providers/docker.js.map +1 -1
  290. package/dist/providers/file-batches.constants.d.ts +10 -0
  291. package/dist/providers/file-batches.constants.js +57 -0
  292. package/dist/providers/file-batches.constants.js.map +1 -0
  293. package/dist/providers/file-batches.d.ts +2 -0
  294. package/dist/providers/file-batches.js +151 -0
  295. package/dist/providers/file-batches.js.map +1 -0
  296. package/dist/providers/podman.d.ts +1 -0
  297. package/dist/providers/podman.js.map +1 -1
  298. package/dist/providers/vercel.js +3 -1
  299. package/dist/providers/vercel.js.map +1 -1
  300. package/package.json +12 -1
@@ -0,0 +1,123 @@
1
+ import { lstat, opendir } from "node:fs/promises";
2
+ import { basename, join } from "node:path";
3
+ import { positive } from "../domain/errors.js";
4
+ import { storageCategories, storageInventoryDefaults, } from "./storage-inventory.constants.js";
5
+ function usage() {
6
+ return { bytes: 0, files: 0, directories: 0, symlinks: 0, other: 0 };
7
+ }
8
+ function addUsage(target, incoming) {
9
+ target.bytes += incoming.bytes;
10
+ target.files += incoming.files;
11
+ target.directories += incoming.directories;
12
+ target.symlinks += incoming.symlinks;
13
+ target.other += incoming.other;
14
+ }
15
+ function errorCode(error) {
16
+ if (error &&
17
+ typeof error === "object" &&
18
+ "code" in error &&
19
+ typeof error.code === "string")
20
+ return error.code;
21
+ return "UNAVAILABLE";
22
+ }
23
+ async function availableDirectory(path, scan) {
24
+ try {
25
+ const info = await lstat(path);
26
+ if (info.isDirectory() && !info.isSymbolicLink())
27
+ return true;
28
+ scan.issues.push({
29
+ path,
30
+ code: info.isSymbolicLink() ? "SYMLINK_ROOT" : "NOT_DIRECTORY",
31
+ });
32
+ }
33
+ catch (error) {
34
+ if (errorCode(error) !== "ENOENT")
35
+ scan.issues.push({ path, code: errorCode(error) });
36
+ }
37
+ return false;
38
+ }
39
+ async function measure(path, scan, depth) {
40
+ const startIssues = scan.issues.length;
41
+ const entry = {
42
+ ...usage(),
43
+ name: basename(path),
44
+ path,
45
+ kind: "unknown",
46
+ complete: true,
47
+ };
48
+ if (scan.remaining === 0) {
49
+ scan.issues.push({ path, code: "ENTRY_LIMIT" });
50
+ return { ...entry, complete: false };
51
+ }
52
+ scan.remaining--;
53
+ try {
54
+ const info = await lstat(path);
55
+ entry.modifiedAt = info.mtime.toISOString();
56
+ if (info.isSymbolicLink())
57
+ return { ...entry, kind: "symlink", symlinks: 1 };
58
+ if (info.isFile())
59
+ return { ...entry, kind: "file", files: 1, bytes: info.size };
60
+ if (!info.isDirectory()) {
61
+ scan.issues.push({ path, code: "UNSUPPORTED_TYPE" });
62
+ return { ...entry, kind: "other", other: 1, complete: false };
63
+ }
64
+ entry.kind = "directory";
65
+ entry.directories = 1;
66
+ if (depth >= storageInventoryDefaults.maxDepth) {
67
+ scan.issues.push({ path, code: "DEPTH_LIMIT" });
68
+ return { ...entry, complete: false };
69
+ }
70
+ for await (const child of await opendir(path)) {
71
+ const nested = await measure(join(path, child.name), scan, depth + 1);
72
+ addUsage(entry, nested);
73
+ if (nested.modifiedAt && nested.modifiedAt > entry.modifiedAt)
74
+ entry.modifiedAt = nested.modifiedAt;
75
+ if (!nested.complete)
76
+ entry.complete = false;
77
+ if (scan.remaining === 0 && !nested.complete)
78
+ break;
79
+ }
80
+ }
81
+ catch (error) {
82
+ scan.issues.push({ path, code: errorCode(error) });
83
+ }
84
+ entry.complete = entry.complete && scan.issues.length === startIssues;
85
+ return entry;
86
+ }
87
+ export async function storageInventory(root, maxEntries = storageInventoryDefaults.maxEntries) {
88
+ positive(maxEntries, "maxEntries");
89
+ const scan = { remaining: maxEntries, issues: [] };
90
+ const categories = [];
91
+ const total = usage();
92
+ const available = await availableDirectory(root, scan);
93
+ for (const name of storageCategories) {
94
+ const path = join(root, name);
95
+ const entries = [];
96
+ if (available && (await availableDirectory(path, scan))) {
97
+ try {
98
+ for await (const child of await opendir(path)) {
99
+ const entry = await measure(join(path, child.name), scan, 0);
100
+ entries.push(entry);
101
+ addUsage(total, entry);
102
+ if (scan.remaining === 0 && !entry.complete)
103
+ break;
104
+ }
105
+ }
106
+ catch (error) {
107
+ scan.issues.push({ path, code: errorCode(error) });
108
+ }
109
+ }
110
+ entries.sort((left, right) => left.name < right.name ? -1 : Number(left.name > right.name));
111
+ categories.push({ name, path, entries });
112
+ }
113
+ return {
114
+ root,
115
+ categories,
116
+ usage: total,
117
+ issues: scan.issues,
118
+ complete: scan.issues.length === 0,
119
+ scannedEntries: maxEntries - scan.remaining,
120
+ maxEntries,
121
+ };
122
+ }
123
+ //# sourceMappingURL=storage-inventory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-inventory.js","sourceRoot":"","sources":["../../src/infrastructure/storage-inventory.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, opendir } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport { positive } from \"../domain/errors.ts\";\nimport {\n storageCategories,\n storageInventoryDefaults,\n} from \"./storage-inventory.constants.ts\";\nimport type {\n StorageCategory,\n StorageEntry,\n StorageInventory,\n StorageScan,\n StorageUsage,\n} from \"./storage-inventory.types.ts\";\n\nfunction usage(): StorageUsage {\n return { bytes: 0, files: 0, directories: 0, symlinks: 0, other: 0 };\n}\nfunction addUsage(\n target: StorageUsage,\n incoming: Readonly<StorageUsage>,\n): void {\n target.bytes += incoming.bytes;\n target.files += incoming.files;\n target.directories += incoming.directories;\n target.symlinks += incoming.symlinks;\n target.other += incoming.other;\n}\nfunction errorCode(error: unknown): string {\n if (\n error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n typeof error.code === \"string\"\n )\n return error.code;\n return \"UNAVAILABLE\";\n}\nasync function availableDirectory(\n path: string,\n scan: StorageScan,\n): Promise<boolean> {\n try {\n const info = await lstat(path);\n if (info.isDirectory() && !info.isSymbolicLink()) return true;\n scan.issues.push({\n path,\n code: info.isSymbolicLink() ? \"SYMLINK_ROOT\" : \"NOT_DIRECTORY\",\n });\n } catch (error) {\n if (errorCode(error) !== \"ENOENT\")\n scan.issues.push({ path, code: errorCode(error) });\n }\n return false;\n}\nasync function measure(\n path: string,\n scan: StorageScan,\n depth: number,\n): Promise<StorageEntry> {\n const startIssues = scan.issues.length;\n const entry: StorageEntry = {\n ...usage(),\n name: basename(path),\n path,\n kind: \"unknown\",\n complete: true,\n };\n if (scan.remaining === 0) {\n scan.issues.push({ path, code: \"ENTRY_LIMIT\" });\n return { ...entry, complete: false };\n }\n scan.remaining--;\n try {\n const info = await lstat(path);\n entry.modifiedAt = info.mtime.toISOString();\n if (info.isSymbolicLink())\n return { ...entry, kind: \"symlink\", symlinks: 1 };\n if (info.isFile())\n return { ...entry, kind: \"file\", files: 1, bytes: info.size };\n if (!info.isDirectory()) {\n scan.issues.push({ path, code: \"UNSUPPORTED_TYPE\" });\n return { ...entry, kind: \"other\", other: 1, complete: false };\n }\n entry.kind = \"directory\";\n entry.directories = 1;\n if (depth >= storageInventoryDefaults.maxDepth) {\n scan.issues.push({ path, code: \"DEPTH_LIMIT\" });\n return { ...entry, complete: false };\n }\n for await (const child of await opendir(path)) {\n const nested = await measure(join(path, child.name), scan, depth + 1);\n addUsage(entry, nested);\n if (nested.modifiedAt && nested.modifiedAt > entry.modifiedAt)\n entry.modifiedAt = nested.modifiedAt;\n if (!nested.complete) entry.complete = false;\n if (scan.remaining === 0 && !nested.complete) break;\n }\n } catch (error) {\n scan.issues.push({ path, code: errorCode(error) });\n }\n entry.complete = entry.complete && scan.issues.length === startIssues;\n return entry;\n}\n\nexport async function storageInventory(\n root: string,\n maxEntries: number = storageInventoryDefaults.maxEntries,\n): Promise<StorageInventory> {\n positive(maxEntries, \"maxEntries\");\n const scan: StorageScan = { remaining: maxEntries, issues: [] };\n const categories: StorageCategory[] = [];\n const total = usage();\n const available = await availableDirectory(root, scan);\n for (const name of storageCategories) {\n const path = join(root, name);\n const entries: StorageEntry[] = [];\n if (available && (await availableDirectory(path, scan))) {\n try {\n for await (const child of await opendir(path)) {\n const entry = await measure(join(path, child.name), scan, 0);\n entries.push(entry);\n addUsage(total, entry);\n if (scan.remaining === 0 && !entry.complete) break;\n }\n } catch (error) {\n scan.issues.push({ path, code: errorCode(error) });\n }\n }\n entries.sort((left, right) =>\n left.name < right.name ? -1 : Number(left.name > right.name),\n );\n categories.push({ name, path, entries });\n }\n return {\n root,\n categories,\n usage: total,\n issues: scan.issues,\n complete: scan.issues.length === 0,\n scannedEntries: maxEntries - scan.remaining,\n maxEntries,\n };\n}\n"]}
@@ -0,0 +1,38 @@
1
+ export type StorageCategoryName = "recovery" | "logs" | "locks" | "workspaces";
2
+ export type StorageEntryKind = "file" | "directory" | "symlink" | "other" | "unknown";
3
+ export interface StorageIssue {
4
+ readonly path: string;
5
+ readonly code: string;
6
+ }
7
+ export interface StorageUsage {
8
+ bytes: number;
9
+ files: number;
10
+ directories: number;
11
+ symlinks: number;
12
+ other: number;
13
+ }
14
+ export interface StorageEntry extends StorageUsage {
15
+ readonly name: string;
16
+ readonly path: string;
17
+ kind: StorageEntryKind;
18
+ modifiedAt?: string;
19
+ complete: boolean;
20
+ }
21
+ export interface StorageCategory {
22
+ readonly name: StorageCategoryName;
23
+ readonly path: string;
24
+ readonly entries: readonly StorageEntry[];
25
+ }
26
+ export interface StorageInventory {
27
+ readonly root: string;
28
+ readonly categories: readonly StorageCategory[];
29
+ readonly usage: Readonly<StorageUsage>;
30
+ readonly issues: readonly StorageIssue[];
31
+ readonly complete: boolean;
32
+ readonly scannedEntries: number;
33
+ readonly maxEntries: number;
34
+ }
35
+ export interface StorageScan {
36
+ remaining: number;
37
+ readonly issues: StorageIssue[];
38
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=storage-inventory.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-inventory.types.js","sourceRoot":"","sources":["../../src/infrastructure/storage-inventory.types.ts"],"names":[],"mappings":"","sourcesContent":["export type StorageCategoryName = \"recovery\" | \"logs\" | \"locks\" | \"workspaces\";\nexport type StorageEntryKind =\n \"file\" | \"directory\" | \"symlink\" | \"other\" | \"unknown\";\n\nexport interface StorageIssue {\n readonly path: string;\n readonly code: string;\n}\nexport interface StorageUsage {\n bytes: number;\n files: number;\n directories: number;\n symlinks: number;\n other: number;\n}\nexport interface StorageEntry extends StorageUsage {\n readonly name: string;\n readonly path: string;\n kind: StorageEntryKind;\n modifiedAt?: string;\n complete: boolean;\n}\nexport interface StorageCategory {\n readonly name: StorageCategoryName;\n readonly path: string;\n readonly entries: readonly StorageEntry[];\n}\nexport interface StorageInventory {\n readonly root: string;\n readonly categories: readonly StorageCategory[];\n readonly usage: Readonly<StorageUsage>;\n readonly issues: readonly StorageIssue[];\n readonly complete: boolean;\n readonly scannedEntries: number;\n readonly maxEntries: number;\n}\nexport interface StorageScan {\n remaining: number;\n readonly issues: StorageIssue[];\n}\n"]}
@@ -25,6 +25,18 @@ export function boundedTransfers(lease, defaults) {
25
25
  release: lease.release.bind(lease),
26
26
  upload: copy("upload"),
27
27
  download: copy("download"),
28
+ ...(lease.fileTransfers
29
+ ? {
30
+ fileTransfers: {
31
+ manifest: (source, paths, options = {}) => transfer({ ...defaults, ...options }, (signal) => lease.fileTransfers.manifest(source, paths, {
32
+ ...defaults,
33
+ ...options,
34
+ signal,
35
+ })),
36
+ downloadBatch: (source, entries, destination, options = {}) => transfer({ ...defaults, ...options }, (signal) => lease.fileTransfers.downloadBatch(source, entries, destination, { ...defaults, ...options, signal })),
37
+ },
38
+ }
39
+ : {}),
28
40
  };
29
41
  }
30
42
  //# sourceMappingURL=transfer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../src/infrastructure/transfer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAwB,EACxB,OAA4C;IAE5C,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,gBAAgB,CAAC,UAAU,CAAC;IACrE,SAAS,CACP,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAC7C,oCAAoC,CACrC,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;QAC3B,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAChD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IAChB,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CACH,IAAI,CAAC,KAAK,CACR,IAAI,YAAY,CAAC,SAAS,EAAE,yBAAyB,EAAE,EAAE,UAAU,EAAE,CAAC,CACvE,EACH,UAAU,CACX,CAAC;IACF,IAAI,CAAC;QACH,OAAO,MAAM,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,KAAmB,EACnB,QAAyB;IAEzB,MAAM,IAAI,GACR,CAAC,MAA6B,EAAE,EAAE,CAClC,CAAC,MAAc,EAAE,WAAmB,EAAE,UAA2B,EAAE,EAAE,EAAE,CACrE,QAAQ,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAC/C,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,CACxE,CAAC;IACN,OAAO;QACL,GAAG,KAAK;QACR,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QAChC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAClC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;QACtB,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC;KAC3B,CAAC;AACJ,CAAC","sourcesContent":["import { invariant, OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease, TransferOptions } from \"../domain/sandbox.types.ts\";\nimport { interruptible } from \"./abort.ts\";\nimport { transferDefaults } from \"./transfer.constants.ts\";\n\nexport async function transfer<T>(\n options: TransferOptions,\n perform: (signal: AbortSignal) => Promise<T>,\n): Promise<T> {\n options.signal?.throwIfAborted();\n const deadlineMs = options.deadlineMs ?? transferDefaults.deadlineMs;\n invariant(\n Number.isFinite(deadlineMs) && deadlineMs > 0,\n \"Transfer deadline must be positive\",\n );\n const stop = new AbortController();\n const signal = options.signal\n ? AbortSignal.any([options.signal, stop.signal])\n : stop.signal;\n const timer = setTimeout(\n () =>\n stop.abort(\n new OutpostError(\"timeout\", \"File transfer timed out\", { deadlineMs }),\n ),\n deadlineMs,\n );\n try {\n return await interruptible(perform(signal), signal);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function boundedTransfers(\n lease: SandboxLease,\n defaults: TransferOptions,\n): SandboxLease {\n const copy =\n (method: \"upload\" | \"download\") =>\n (source: string, destination: string, options: TransferOptions = {}) =>\n transfer({ ...defaults, ...options }, (signal) =>\n lease[method](source, destination, { ...defaults, ...options, signal }),\n );\n return {\n ...lease,\n invoke: lease.invoke.bind(lease),\n release: lease.release.bind(lease),\n upload: copy(\"upload\"),\n download: copy(\"download\"),\n };\n}\n"]}
1
+ {"version":3,"file":"transfer.js","sourceRoot":"","sources":["../../src/infrastructure/transfer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAwB,EACxB,OAA4C;IAE5C,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,gBAAgB,CAAC,UAAU,CAAC;IACrE,SAAS,CACP,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,UAAU,GAAG,CAAC,EAC7C,oCAAoC,CACrC,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,eAAe,EAAE,CAAC;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM;QAC3B,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAChD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;IAChB,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CACH,IAAI,CAAC,KAAK,CACR,IAAI,YAAY,CAAC,SAAS,EAAE,yBAAyB,EAAE,EAAE,UAAU,EAAE,CAAC,CACvE,EACH,UAAU,CACX,CAAC;IACF,IAAI,CAAC;QACH,OAAO,MAAM,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,KAAmB,EACnB,QAAyB;IAEzB,MAAM,IAAI,GACR,CAAC,MAA6B,EAAE,EAAE,CAClC,CAAC,MAAc,EAAE,WAAmB,EAAE,UAA2B,EAAE,EAAE,EAAE,CACrE,QAAQ,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAC/C,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC,CACxE,CAAC;IACN,OAAO;QACL,GAAG,KAAK;QACR,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;QAChC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;QAClC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;QACtB,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC;QAC1B,GAAG,CAAC,KAAK,CAAC,aAAa;YACrB,CAAC,CAAC;gBACE,aAAa,EAAE;oBACb,QAAQ,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CACxC,QAAQ,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAC/C,KAAK,CAAC,aAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE;wBAC3C,GAAG,QAAQ;wBACX,GAAG,OAAO;wBACV,MAAM;qBACP,CAAC,CACH;oBACH,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,GAAG,EAAE,EAAE,EAAE,CAC5D,QAAQ,CAAC,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,EAAE,CAC/C,KAAK,CAAC,aAAc,CAAC,aAAa,CAChC,MAAM,EACN,OAAO,EACP,WAAW,EACX,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CACpC,CACF;iBACJ;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC;AACJ,CAAC","sourcesContent":["import { invariant, OutpostError } from \"../domain/errors.ts\";\nimport type { SandboxLease, TransferOptions } from \"../domain/sandbox.types.ts\";\nimport { interruptible } from \"./abort.ts\";\nimport { transferDefaults } from \"./transfer.constants.ts\";\n\nexport async function transfer<T>(\n options: TransferOptions,\n perform: (signal: AbortSignal) => Promise<T>,\n): Promise<T> {\n options.signal?.throwIfAborted();\n const deadlineMs = options.deadlineMs ?? transferDefaults.deadlineMs;\n invariant(\n Number.isFinite(deadlineMs) && deadlineMs > 0,\n \"Transfer deadline must be positive\",\n );\n const stop = new AbortController();\n const signal = options.signal\n ? AbortSignal.any([options.signal, stop.signal])\n : stop.signal;\n const timer = setTimeout(\n () =>\n stop.abort(\n new OutpostError(\"timeout\", \"File transfer timed out\", { deadlineMs }),\n ),\n deadlineMs,\n );\n try {\n return await interruptible(perform(signal), signal);\n } finally {\n clearTimeout(timer);\n }\n}\n\nexport function boundedTransfers(\n lease: SandboxLease,\n defaults: TransferOptions,\n): SandboxLease {\n const copy =\n (method: \"upload\" | \"download\") =>\n (source: string, destination: string, options: TransferOptions = {}) =>\n transfer({ ...defaults, ...options }, (signal) =>\n lease[method](source, destination, { ...defaults, ...options, signal }),\n );\n return {\n ...lease,\n invoke: lease.invoke.bind(lease),\n release: lease.release.bind(lease),\n upload: copy(\"upload\"),\n download: copy(\"download\"),\n ...(lease.fileTransfers\n ? {\n fileTransfers: {\n manifest: (source, paths, options = {}) =>\n transfer({ ...defaults, ...options }, (signal) =>\n lease.fileTransfers!.manifest(source, paths, {\n ...defaults,\n ...options,\n signal,\n }),\n ),\n downloadBatch: (source, entries, destination, options = {}) =>\n transfer({ ...defaults, ...options }, (signal) =>\n lease.fileTransfers!.downloadBatch(\n source,\n entries,\n destination,\n { ...defaults, ...options, signal },\n ),\n ),\n },\n }\n : {}),\n };\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  export declare const cloudDefaults: Readonly<{
2
2
  deadlineMs: 600000;
3
3
  retainBytes: 65536;
4
+ pollMs: 100;
4
5
  cancellationMs: 15000;
5
6
  }>;
6
7
  export declare const cloudRoots: Readonly<{
@@ -1,6 +1,7 @@
1
1
  export const cloudDefaults = Object.freeze({
2
2
  deadlineMs: 600_000,
3
3
  retainBytes: 65_536,
4
+ pollMs: 100,
4
5
  cancellationMs: 15_000,
5
6
  });
6
7
  export const cloudRoots = Object.freeze({
@@ -1 +1 @@
1
- {"version":3,"file":"cloud.constants.js","sourceRoot":"","sources":["../../src/providers/cloud.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;IACzC,UAAU,EAAE,OAAO;IACnB,WAAW,EAAE,MAAM;IACnB,cAAc,EAAE,MAAM;CACvB,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IACtC,MAAM,EAAE,yBAAyB;IACjC,WAAW,EAAE,eAAe;CAC7B,CAAC,CAAC","sourcesContent":["export const cloudDefaults = Object.freeze({\n deadlineMs: 600_000,\n retainBytes: 65_536,\n cancellationMs: 15_000,\n});\nexport const cloudRoots = Object.freeze({\n vercel: \"/vercel/sandbox/outpost\",\n daytonaHome: \"/home/daytona\",\n});\n"]}
1
+ {"version":3,"file":"cloud.constants.js","sourceRoot":"","sources":["../../src/providers/cloud.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;IACzC,UAAU,EAAE,OAAO;IACnB,WAAW,EAAE,MAAM;IACnB,MAAM,EAAE,GAAG;IACX,cAAc,EAAE,MAAM;CACvB,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC;IACtC,MAAM,EAAE,yBAAyB;IACjC,WAAW,EAAE,eAAe;CAC7B,CAAC,CAAC","sourcesContent":["export const cloudDefaults = Object.freeze({\n deadlineMs: 600_000,\n retainBytes: 65_536,\n pollMs: 100,\n cancellationMs: 15_000,\n});\nexport const cloudRoots = Object.freeze({\n vercel: \"/vercel/sandbox/outpost\",\n daytonaHome: \"/home/daytona\",\n});\n"]}
@@ -0,0 +1,11 @@
1
+ export declare const cacheDefaults: Readonly<{
2
+ root: "/outpost/cache";
3
+ label: "io.outpost.cache";
4
+ version: 1;
5
+ namePattern: RegExp;
6
+ maximumKeyLength: 1024;
7
+ }>;
8
+ export declare const cacheCreateOptions: {
9
+ readonly docker: readonly [];
10
+ readonly podman: readonly ["--ignore"];
11
+ };
@@ -0,0 +1,9 @@
1
+ export const cacheDefaults = Object.freeze({
2
+ root: "/outpost/cache",
3
+ label: "io.outpost.cache",
4
+ version: 1,
5
+ namePattern: /^[a-z][a-z0-9-]{0,47}$/,
6
+ maximumKeyLength: 1024,
7
+ });
8
+ export const cacheCreateOptions = { docker: [], podman: ["--ignore"] };
9
+ //# sourceMappingURL=container-cache.constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-cache.constants.js","sourceRoot":"","sources":["../../src/providers/container-cache.constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE,kBAAkB;IACzB,OAAO,EAAE,CAAC;IACV,WAAW,EAAE,wBAAwB;IACrC,gBAAgB,EAAE,IAAI;CACvB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,UAAU,CAAC,EAAW,CAAC","sourcesContent":["export const cacheDefaults = Object.freeze({\n root: \"/outpost/cache\",\n label: \"io.outpost.cache\",\n version: 1,\n namePattern: /^[a-z][a-z0-9-]{0,47}$/,\n maximumKeyLength: 1024,\n});\n\nexport const cacheCreateOptions = { docker: [], podman: [\"--ignore\"] } as const;\n"]}
@@ -0,0 +1,4 @@
1
+ import type { DependencyCache, ContainerCacheMount } from "./container-cache.types.ts";
2
+ import type { ContainerOptions, ContainerUser } from "./container.types.ts";
3
+ export declare function validateCaches(config: ContainerOptions): void;
4
+ export declare function cacheMounts(caches: readonly DependencyCache[], repository: string, image: string, user: ContainerUser): Promise<ContainerCacheMount[]>;
@@ -0,0 +1,48 @@
1
+ import { createHash } from "node:crypto";
2
+ import { realpath } from "node:fs/promises";
3
+ import { posix } from "node:path";
4
+ import { invariant } from "../domain/errors.js";
5
+ import { cacheDefaults } from "./container-cache.constants.js";
6
+ export function validateCaches(config) {
7
+ const names = new Set();
8
+ for (const cache of config.caches ?? []) {
9
+ invariant(typeof cache.name === "string" &&
10
+ cacheDefaults.namePattern.test(cache.name), "Cache names must start with a lowercase letter and contain at most 48 lowercase letters, digits or hyphens");
11
+ invariant(!names.has(cache.name), `Duplicate cache name: ${cache.name}`);
12
+ invariant(typeof cache.key === "string" &&
13
+ cache.key.trim().length > 0 &&
14
+ cache.key.length <= cacheDefaults.maximumKeyLength, "Cache keys must be nonempty strings of at most 1024 characters");
15
+ names.add(cache.name);
16
+ }
17
+ if (!names.size)
18
+ return;
19
+ for (const volume of config.volumes ?? []) {
20
+ const target = posix.resolve("/workspace", volume.target.replaceAll("\\", "/"));
21
+ invariant(!(target === cacheDefaults.root ||
22
+ target.startsWith(cacheDefaults.root + "/") ||
23
+ cacheDefaults.root.startsWith(target.replace(/\/$/, "") + "/")), "Explicit volumes must not overlap dependency caches");
24
+ }
25
+ }
26
+ export async function cacheMounts(caches, repository, image, user) {
27
+ if (!caches.length)
28
+ return [];
29
+ const canonical = await realpath(repository);
30
+ return caches.map(({ name, key }) => {
31
+ const digest = createHash("sha256")
32
+ .update(JSON.stringify([
33
+ cacheDefaults.version,
34
+ canonical,
35
+ image,
36
+ user.uid,
37
+ user.gid,
38
+ name,
39
+ key,
40
+ ]))
41
+ .digest("hex");
42
+ return {
43
+ volume: `outpost-cache-${name}-${digest}`,
44
+ target: `${cacheDefaults.root}/${name}`,
45
+ };
46
+ });
47
+ }
48
+ //# sourceMappingURL=container-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-cache.js","sourceRoot":"","sources":["../../src/providers/container-cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAO/D,MAAM,UAAU,cAAc,CAAC,MAAwB;IACrD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,IAAI,EAAE,EAAE,CAAC;QACxC,SAAS,CACP,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ;YAC5B,aAAa,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAC5C,4GAA4G,CAC7G,CAAC;QACF,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,yBAAyB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACzE,SAAS,CACP,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ;YAC3B,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;YAC3B,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,aAAa,CAAC,gBAAgB,EACpD,gEAAgE,CACjE,CAAC;QACF,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,IAAI;QAAE,OAAO;IACxB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;QAC1C,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAC1B,YAAY,EACZ,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CACpC,CAAC;QACF,SAAS,CACP,CAAC,CACC,MAAM,KAAK,aAAa,CAAC,IAAI;YAC7B,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC,IAAI,GAAG,GAAG,CAAC;YAC3C,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAC/D,EACD,qDAAqD,CACtD,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAkC,EAClC,UAAkB,EAClB,KAAa,EACb,IAAmB;IAEnB,IAAI,CAAC,MAAM,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC7C,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,EAAE,EAAE;QAClC,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC;aAChC,MAAM,CACL,IAAI,CAAC,SAAS,CAAC;YACb,aAAa,CAAC,OAAO;YACrB,SAAS;YACT,KAAK;YACL,IAAI,CAAC,GAAG;YACR,IAAI,CAAC,GAAG;YACR,IAAI;YACJ,GAAG;SACJ,CAAC,CACH;aACA,MAAM,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO;YACL,MAAM,EAAE,iBAAiB,IAAI,IAAI,MAAM,EAAE;YACzC,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,IAAI,IAAI,EAAE;SACxC,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { realpath } from \"node:fs/promises\";\nimport { posix } from \"node:path\";\nimport { invariant } from \"../domain/errors.ts\";\nimport { cacheDefaults } from \"./container-cache.constants.ts\";\nimport type {\n DependencyCache,\n ContainerCacheMount,\n} from \"./container-cache.types.ts\";\nimport type { ContainerOptions, ContainerUser } from \"./container.types.ts\";\n\nexport function validateCaches(config: ContainerOptions): void {\n const names = new Set<string>();\n for (const cache of config.caches ?? []) {\n invariant(\n typeof cache.name === \"string\" &&\n cacheDefaults.namePattern.test(cache.name),\n \"Cache names must start with a lowercase letter and contain at most 48 lowercase letters, digits or hyphens\",\n );\n invariant(!names.has(cache.name), `Duplicate cache name: ${cache.name}`);\n invariant(\n typeof cache.key === \"string\" &&\n cache.key.trim().length > 0 &&\n cache.key.length <= cacheDefaults.maximumKeyLength,\n \"Cache keys must be nonempty strings of at most 1024 characters\",\n );\n names.add(cache.name);\n }\n if (!names.size) return;\n for (const volume of config.volumes ?? []) {\n const target = posix.resolve(\n \"/workspace\",\n volume.target.replaceAll(\"\\\\\", \"/\"),\n );\n invariant(\n !(\n target === cacheDefaults.root ||\n target.startsWith(cacheDefaults.root + \"/\") ||\n cacheDefaults.root.startsWith(target.replace(/\\/$/, \"\") + \"/\")\n ),\n \"Explicit volumes must not overlap dependency caches\",\n );\n }\n}\n\nexport async function cacheMounts(\n caches: readonly DependencyCache[],\n repository: string,\n image: string,\n user: ContainerUser,\n): Promise<ContainerCacheMount[]> {\n if (!caches.length) return [];\n const canonical = await realpath(repository);\n return caches.map(({ name, key }) => {\n const digest = createHash(\"sha256\")\n .update(\n JSON.stringify([\n cacheDefaults.version,\n canonical,\n image,\n user.uid,\n user.gid,\n name,\n key,\n ]),\n )\n .digest(\"hex\");\n return {\n volume: `outpost-cache-${name}-${digest}`,\n target: `${cacheDefaults.root}/${name}`,\n };\n });\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export interface DependencyCache {
2
+ readonly name: string;
3
+ readonly key: string;
4
+ }
5
+ export interface ContainerCacheMount {
6
+ readonly volume: string;
7
+ readonly target: string;
8
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=container-cache.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-cache.types.js","sourceRoot":"","sources":["../../src/providers/container-cache.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface DependencyCache {\n readonly name: string;\n readonly key: string;\n}\n\nexport interface ContainerCacheMount {\n readonly volume: string;\n readonly target: string;\n}\n"]}
@@ -21,7 +21,9 @@ export function containerPlan({ config, engine, user, root, name, image, volumes
21
21
  root,
22
22
  "--cap-drop",
23
23
  "ALL",
24
- ...(fileParents.size ? ["--cap-add", "CHOWN"] : []),
24
+ ...(fileParents.size || config.caches?.length
25
+ ? ["--cap-add", "CHOWN"]
26
+ : []),
25
27
  "--security-opt",
26
28
  "no-new-privileges",
27
29
  "--tmpfs",
@@ -1 +1 @@
1
- {"version":3,"file":"container-plan.js","sourceRoot":"","sources":["../../src/providers/container-plan.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,OAAO,EACP,WAAW,GACU;IACrB,MAAM,QAAQ,GACZ,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;QACjC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QACnB,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG;QACX,QAAQ;QACR,QAAQ;QACR,IAAI;QACJ,QAAQ;QACR,QAAQ;QACR,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;QACzB,GAAG,CAAC,MAAM,KAAK,QAAQ;YACvB,MAAM,CAAC,MAAM,KAAK,KAAK;YACvB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC;YACvD,CAAC,CAAC;gBACE,UAAU;gBACV,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,GAAG,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;aACpE;YACH,CAAC,CAAC,EAAE,CAAC;QACP,WAAW;QACX,IAAI;QACJ,YAAY;QACZ,KAAK;QACL,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,gBAAgB;QAChB,mBAAmB;QACnB,SAAS;QACT,MAAM,KAAK,QAAQ;YACjB,CAAC,CAAC,0BAA0B;YAC5B,CAAC,CAAC,sBAAsB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,QAAQ,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,YAAY;QAC5G,GAAG,OAAO;QACV,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACxD,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3E,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACnE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,cAAc;QACd,OAAO;QACP,KAAK;QACL,UAAU;KACX,CAAC;IAEF,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import type { ContainerPlanOptions } from \"./container.types.ts\";\n\nexport function containerPlan({\n config,\n engine,\n user,\n root,\n name,\n image,\n volumes,\n fileParents,\n}: ContainerPlanOptions): string[] {\n const networks =\n typeof config.networks === \"string\"\n ? [config.networks]\n : (config.networks ?? []);\n const args = [\n \"create\",\n \"--name\",\n name,\n \"--init\",\n \"--user\",\n `${user.uid}:${user.gid}`,\n ...(engine === \"podman\" &&\n config.userns !== false &&\n (process.getuid?.() !== 0 || config.userns === \"keep-id\")\n ? [\n \"--userns\",\n config.user ? `keep-id:uid=${user.uid},gid=${user.gid}` : \"keep-id\",\n ]\n : []),\n \"--workdir\",\n root,\n \"--cap-drop\",\n \"ALL\",\n ...(fileParents.size ? [\"--cap-add\", \"CHOWN\"] : []),\n \"--security-opt\",\n \"no-new-privileges\",\n \"--tmpfs\",\n engine === \"podman\"\n ? \"/home/agent:rw,mode=1777\"\n : `/home/agent:rw,uid=${fileParents.size ? 0 : user.uid},gid=${fileParents.size ? 0 : user.gid},mode=0700`,\n ...volumes,\n ...networks.flatMap((network) => [\"--network\", network]),\n ...(config.groups ?? []).flatMap((group) => [\"--group-add\", String(group)]),\n ...(config.devices ?? []).flatMap((device) => [\"--device\", device]),\n ...(config.cpus ? [\"--cpus\", String(config.cpus)] : []),\n ...(config.memoryMb ? [\"--memory\", `${config.memoryMb}m`] : []),\n \"--entrypoint\",\n \"sleep\",\n image,\n \"infinity\",\n ];\n\n return args;\n}\n"]}
1
+ {"version":3,"file":"container-plan.js","sourceRoot":"","sources":["../../src/providers/container-plan.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,aAAa,CAAC,EAC5B,MAAM,EACN,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,OAAO,EACP,WAAW,GACU;IACrB,MAAM,QAAQ,GACZ,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ;QACjC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;QACnB,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC;IAC9B,MAAM,IAAI,GAAG;QACX,QAAQ;QACR,QAAQ;QACR,IAAI;QACJ,QAAQ;QACR,QAAQ;QACR,GAAG,IAAI,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE;QACzB,GAAG,CAAC,MAAM,KAAK,QAAQ;YACvB,MAAM,CAAC,MAAM,KAAK,KAAK;YACvB,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC;YACvD,CAAC,CAAC;gBACE,UAAU;gBACV,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,GAAG,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS;aACpE;YACH,CAAC,CAAC,EAAE,CAAC;QACP,WAAW;QACX,IAAI;QACJ,YAAY;QACZ,KAAK;QACL,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM;YAC3C,CAAC,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC;YACxB,CAAC,CAAC,EAAE,CAAC;QACP,gBAAgB;QAChB,mBAAmB;QACnB,SAAS;QACT,MAAM,KAAK,QAAQ;YACjB,CAAC,CAAC,0BAA0B;YAC5B,CAAC,CAAC,sBAAsB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,QAAQ,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,YAAY;QAC5G,GAAG,OAAO;QACV,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACxD,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,aAAa,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3E,GAAG,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACnE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/D,cAAc;QACd,OAAO;QACP,KAAK;QACL,UAAU;KACX,CAAC;IAEF,OAAO,IAAI,CAAC;AACd,CAAC","sourcesContent":["import type { ContainerPlanOptions } from \"./container.types.ts\";\n\nexport function containerPlan({\n config,\n engine,\n user,\n root,\n name,\n image,\n volumes,\n fileParents,\n}: ContainerPlanOptions): string[] {\n const networks =\n typeof config.networks === \"string\"\n ? [config.networks]\n : (config.networks ?? []);\n const args = [\n \"create\",\n \"--name\",\n name,\n \"--init\",\n \"--user\",\n `${user.uid}:${user.gid}`,\n ...(engine === \"podman\" &&\n config.userns !== false &&\n (process.getuid?.() !== 0 || config.userns === \"keep-id\")\n ? [\n \"--userns\",\n config.user ? `keep-id:uid=${user.uid},gid=${user.gid}` : \"keep-id\",\n ]\n : []),\n \"--workdir\",\n root,\n \"--cap-drop\",\n \"ALL\",\n ...(fileParents.size || config.caches?.length\n ? [\"--cap-add\", \"CHOWN\"]\n : []),\n \"--security-opt\",\n \"no-new-privileges\",\n \"--tmpfs\",\n engine === \"podman\"\n ? \"/home/agent:rw,mode=1777\"\n : `/home/agent:rw,uid=${fileParents.size ? 0 : user.uid},gid=${fileParents.size ? 0 : user.gid},mode=0700`,\n ...volumes,\n ...networks.flatMap((network) => [\"--network\", network]),\n ...(config.groups ?? []).flatMap((group) => [\"--group-add\", String(group)]),\n ...(config.devices ?? []).flatMap((device) => [\"--device\", device]),\n ...(config.cpus ? [\"--cpus\", String(config.cpus)] : []),\n ...(config.memoryMb ? [\"--memory\", `${config.memoryMb}m`] : []),\n \"--entrypoint\",\n \"sleep\",\n image,\n \"infinity\",\n ];\n\n return args;\n}\n"]}
@@ -4,6 +4,8 @@ import { basename } from "node:path";
4
4
  import { invariant } from "../domain/errors.js";
5
5
  import { executeProcess, quote, requireSuccess, } from "../infrastructure/process.js";
6
6
  import { registerCleanup } from "../infrastructure/shutdown.js";
7
+ import { cacheMounts, validateCaches } from "./container-cache.js";
8
+ import { cacheCreateOptions, cacheDefaults, } from "./container-cache.constants.js";
7
9
  import { containerCommand } from "./container-command.js";
8
10
  import { containerFiles } from "./container-files.js";
9
11
  import { containerMounts } from "./container-mounts.js";
@@ -16,7 +18,11 @@ export function imageName(repository) {
16
18
  .replace(/[^a-z0-9_.-]/g, "-") || "workspace"}`;
17
19
  }
18
20
  export function containerProvider(engine, options = {}, executor = executeProcess, platform = process.platform) {
19
- const config = { ...options };
21
+ const config = {
22
+ ...options,
23
+ caches: (options.caches ?? []).map((cache) => ({ ...cache })),
24
+ };
25
+ validateCaches(config);
20
26
  if (config.cpus !== undefined)
21
27
  invariant(Number.isFinite(config.cpus) && config.cpus > 0, "cpus must be positive");
22
28
  if (config.memoryMb !== undefined)
@@ -47,6 +53,9 @@ export function containerProvider(engine, options = {}, executor = executeProces
47
53
  await containerPreflight(engine, platform, call, config, image, user);
48
54
  const root = containerDefaults.root, home = containerDefaults.home;
49
55
  const { env, volumes, fileParents } = await containerMounts(context, config, platform, root, home);
56
+ const caches = await cacheMounts(config.caches ?? [], context.repository, image, user);
57
+ for (const cache of caches)
58
+ volumes.push("--volume", `${cache.volume}:${cache.target}:nocopy`);
50
59
  const args = containerPlan({
51
60
  config,
52
61
  engine,
@@ -72,10 +81,31 @@ export function containerProvider(engine, options = {}, executor = executeProces
72
81
  return releasing;
73
82
  };
74
83
  try {
84
+ for (const cache of caches)
85
+ await call([
86
+ "volume",
87
+ "create",
88
+ ...cacheCreateOptions[engine],
89
+ "--label",
90
+ `${cacheDefaults.label}=true`,
91
+ cache.volume,
92
+ ], context.signal ? { signal: context.signal } : {});
75
93
  await call(args, {
76
94
  ...(context.signal ? { signal: context.signal } : {}),
77
95
  });
78
96
  await call(["start", name], context.signal ? { signal: context.signal } : {});
97
+ for (const cache of caches) {
98
+ await call([
99
+ "exec",
100
+ "--user",
101
+ "0:0",
102
+ name,
103
+ "chown",
104
+ `${user.uid}:${user.gid}`,
105
+ cache.target,
106
+ ], context.signal ? { signal: context.signal } : {});
107
+ await call(["exec", name, "chmod", "700", cache.target], context.signal ? { signal: context.signal } : {});
108
+ }
79
109
  for (const parent of fileParents)
80
110
  await call([
81
111
  "exec",
@@ -1 +1 @@
1
- {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/providers/container.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { basename } from \"node:path\";\nimport type { Command } from \"../domain/command.types.ts\";\nimport { invariant } from \"../domain/errors.ts\";\nimport type { SandboxProvider } from \"../domain/sandbox.types.ts\";\nimport {\n executeProcess,\n quote,\n requireSuccess,\n} from \"../infrastructure/process.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport { registerCleanup } from \"../infrastructure/shutdown.ts\";\nimport { containerCommand } from \"./container-command.ts\";\nimport { containerFiles } from \"./container-files.ts\";\nimport { containerMounts } from \"./container-mounts.ts\";\nimport { containerPlan } from \"./container-plan.ts\";\nimport { containerPreflight } from \"./container-preflight.ts\";\nimport { containerDefaults } from \"./container.constants.ts\";\nimport type { ContainerEngine, ContainerOptions } from \"./container.types.ts\";\n\nexport type { ContainerOptions } from \"./container.types.ts\";\n\nexport function imageName(repository: string): string {\n return `outpost:${\n basename(repository)\n .toLowerCase()\n .replace(/[^a-z0-9_.-]/g, \"-\") || \"workspace\"\n }`;\n}\n\nexport function containerProvider(\n engine: ContainerEngine,\n options: ContainerOptions = {},\n executor: Executor = executeProcess,\n platform: NodeJS.Platform = process.platform,\n): SandboxProvider {\n const config = { ...options };\n if (config.cpus !== undefined)\n invariant(\n Number.isFinite(config.cpus) && config.cpus > 0,\n \"cpus must be positive\",\n );\n if (config.memoryMb !== undefined)\n invariant(\n Number.isSafeInteger(config.memoryMb) &&\n config.memoryMb >= containerDefaults.minimumMemoryMb,\n \"memoryMb must be at least 64\",\n );\n return {\n name: engine,\n placement: \"mounted\",\n variables: { ...config.variables },\n async acquire(context) {\n context.signal?.throwIfAborted();\n const name = `outpost-${randomUUID()}`;\n const image = config.image ?? imageName(context.repository);\n const user = config.user ?? {\n uid: process.getuid?.() ?? containerDefaults.uid,\n gid: process.getgid?.() ?? containerDefaults.gid,\n };\n invariant(\n Number.isInteger(user.uid) &&\n user.uid >= 0 &&\n Number.isInteger(user.gid) &&\n user.gid >= 0,\n \"Invalid container user\",\n );\n const call = (args: readonly string[], extra: Partial<Command> = {}) =>\n requireSuccess(\n {\n executable: engine,\n arguments: args,\n deadlineMs: containerDefaults.deadlineMs,\n ...extra,\n },\n executor,\n );\n await containerPreflight(engine, platform, call, config, image, user);\n const root = containerDefaults.root,\n home = containerDefaults.home;\n const { env, volumes, fileParents } = await containerMounts(\n context,\n config,\n platform,\n root,\n home,\n );\n const args = containerPlan({\n config,\n engine,\n user,\n root,\n name,\n image,\n volumes,\n fileParents,\n });\n let closed = false,\n releasing: Promise<void> | undefined;\n let unregister = () => {};\n const release = (): Promise<void> => {\n closed = true;\n releasing ??= call([\"rm\", \"--force\", name])\n .then(() => {\n unregister();\n })\n .catch((error) => {\n releasing = undefined;\n throw error;\n });\n return releasing;\n };\n try {\n await call(args, {\n ...(context.signal ? { signal: context.signal } : {}),\n });\n await call(\n [\"start\", name],\n context.signal ? { signal: context.signal } : {},\n );\n for (const parent of fileParents)\n await call([\n \"exec\",\n \"--user\",\n \"0:0\",\n name,\n \"sh\",\n \"-c\",\n `mkdir -p ${quote(parent)} && chown ${user.uid}:${user.gid} ${quote(parent)}`,\n ]);\n if (fileParents.size)\n await call([\n \"exec\",\n \"--user\",\n \"0:0\",\n name,\n \"chown\",\n `${user.uid}:${user.gid}`,\n home,\n ]);\n await call([\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n \"command -v setsid >/dev/null && command -v kill >/dev/null && command -v tar >/dev/null && command -v cp >/dev/null\",\n ]);\n } catch (cause) {\n try {\n await release();\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Sandbox provisioning and cleanup failed\",\n );\n }\n throw cause;\n }\n unregister = registerCleanup(release, () => {\n if (!closed)\n execFileSync(engine, [\"rm\", \"--force\", name], {\n timeout: containerDefaults.cleanupMs,\n stdio: \"ignore\",\n windowsHide: true,\n });\n });\n return {\n root,\n home,\n invoke: containerCommand({\n engine,\n config,\n executor,\n root,\n name,\n env,\n call,\n isClosed: () => closed,\n }),\n ...containerFiles({\n engine,\n config,\n executor,\n root,\n name,\n env,\n call,\n isClosed: () => closed,\n }),\n release,\n };\n },\n };\n}\n"]}
1
+ {"version":3,"file":"container.js","sourceRoot":"","sources":["../../src/providers/container.ts"],"names":[],"mappings":"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","sourcesContent":["import { execFileSync } from \"node:child_process\";\nimport { randomUUID } from \"node:crypto\";\nimport { basename } from \"node:path\";\nimport type { Command } from \"../domain/command.types.ts\";\nimport { invariant } from \"../domain/errors.ts\";\nimport type { SandboxProvider } from \"../domain/sandbox.types.ts\";\nimport {\n executeProcess,\n quote,\n requireSuccess,\n} from \"../infrastructure/process.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport { registerCleanup } from \"../infrastructure/shutdown.ts\";\nimport { cacheMounts, validateCaches } from \"./container-cache.ts\";\nimport {\n cacheCreateOptions,\n cacheDefaults,\n} from \"./container-cache.constants.ts\";\nimport { containerCommand } from \"./container-command.ts\";\nimport { containerFiles } from \"./container-files.ts\";\nimport { containerMounts } from \"./container-mounts.ts\";\nimport { containerPlan } from \"./container-plan.ts\";\nimport { containerPreflight } from \"./container-preflight.ts\";\nimport { containerDefaults } from \"./container.constants.ts\";\nimport type { ContainerEngine, ContainerOptions } from \"./container.types.ts\";\n\nexport type { ContainerOptions } from \"./container.types.ts\";\n\nexport function imageName(repository: string): string {\n return `outpost:${\n basename(repository)\n .toLowerCase()\n .replace(/[^a-z0-9_.-]/g, \"-\") || \"workspace\"\n }`;\n}\n\nexport function containerProvider(\n engine: ContainerEngine,\n options: ContainerOptions = {},\n executor: Executor = executeProcess,\n platform: NodeJS.Platform = process.platform,\n): SandboxProvider {\n const config = {\n ...options,\n caches: (options.caches ?? []).map((cache) => ({ ...cache })),\n };\n validateCaches(config);\n if (config.cpus !== undefined)\n invariant(\n Number.isFinite(config.cpus) && config.cpus > 0,\n \"cpus must be positive\",\n );\n if (config.memoryMb !== undefined)\n invariant(\n Number.isSafeInteger(config.memoryMb) &&\n config.memoryMb >= containerDefaults.minimumMemoryMb,\n \"memoryMb must be at least 64\",\n );\n return {\n name: engine,\n placement: \"mounted\",\n variables: { ...config.variables },\n async acquire(context) {\n context.signal?.throwIfAborted();\n const name = `outpost-${randomUUID()}`;\n const image = config.image ?? imageName(context.repository);\n const user = config.user ?? {\n uid: process.getuid?.() ?? containerDefaults.uid,\n gid: process.getgid?.() ?? containerDefaults.gid,\n };\n invariant(\n Number.isInteger(user.uid) &&\n user.uid >= 0 &&\n Number.isInteger(user.gid) &&\n user.gid >= 0,\n \"Invalid container user\",\n );\n const call = (args: readonly string[], extra: Partial<Command> = {}) =>\n requireSuccess(\n {\n executable: engine,\n arguments: args,\n deadlineMs: containerDefaults.deadlineMs,\n ...extra,\n },\n executor,\n );\n await containerPreflight(engine, platform, call, config, image, user);\n const root = containerDefaults.root,\n home = containerDefaults.home;\n const { env, volumes, fileParents } = await containerMounts(\n context,\n config,\n platform,\n root,\n home,\n );\n const caches = await cacheMounts(\n config.caches ?? [],\n context.repository,\n image,\n user,\n );\n for (const cache of caches)\n volumes.push(\"--volume\", `${cache.volume}:${cache.target}:nocopy`);\n const args = containerPlan({\n config,\n engine,\n user,\n root,\n name,\n image,\n volumes,\n fileParents,\n });\n let closed = false,\n releasing: Promise<void> | undefined;\n let unregister = () => {};\n const release = (): Promise<void> => {\n closed = true;\n releasing ??= call([\"rm\", \"--force\", name])\n .then(() => {\n unregister();\n })\n .catch((error) => {\n releasing = undefined;\n throw error;\n });\n return releasing;\n };\n try {\n for (const cache of caches)\n await call(\n [\n \"volume\",\n \"create\",\n ...cacheCreateOptions[engine],\n \"--label\",\n `${cacheDefaults.label}=true`,\n cache.volume,\n ],\n context.signal ? { signal: context.signal } : {},\n );\n await call(args, {\n ...(context.signal ? { signal: context.signal } : {}),\n });\n await call(\n [\"start\", name],\n context.signal ? { signal: context.signal } : {},\n );\n for (const cache of caches) {\n await call(\n [\n \"exec\",\n \"--user\",\n \"0:0\",\n name,\n \"chown\",\n `${user.uid}:${user.gid}`,\n cache.target,\n ],\n context.signal ? { signal: context.signal } : {},\n );\n await call(\n [\"exec\", name, \"chmod\", \"700\", cache.target],\n context.signal ? { signal: context.signal } : {},\n );\n }\n for (const parent of fileParents)\n await call([\n \"exec\",\n \"--user\",\n \"0:0\",\n name,\n \"sh\",\n \"-c\",\n `mkdir -p ${quote(parent)} && chown ${user.uid}:${user.gid} ${quote(parent)}`,\n ]);\n if (fileParents.size)\n await call([\n \"exec\",\n \"--user\",\n \"0:0\",\n name,\n \"chown\",\n `${user.uid}:${user.gid}`,\n home,\n ]);\n await call([\n \"exec\",\n name,\n \"sh\",\n \"-c\",\n \"command -v setsid >/dev/null && command -v kill >/dev/null && command -v tar >/dev/null && command -v cp >/dev/null\",\n ]);\n } catch (cause) {\n try {\n await release();\n } catch (cleanup) {\n throw new AggregateError(\n [cause, cleanup],\n \"Sandbox provisioning and cleanup failed\",\n );\n }\n throw cause;\n }\n unregister = registerCleanup(release, () => {\n if (!closed)\n execFileSync(engine, [\"rm\", \"--force\", name], {\n timeout: containerDefaults.cleanupMs,\n stdio: \"ignore\",\n windowsHide: true,\n });\n });\n return {\n root,\n home,\n invoke: containerCommand({\n engine,\n config,\n executor,\n root,\n name,\n env,\n call,\n isClosed: () => closed,\n }),\n ...containerFiles({\n engine,\n config,\n executor,\n root,\n name,\n env,\n call,\n isClosed: () => closed,\n }),\n release,\n };\n },\n };\n}\n"]}
@@ -1,7 +1,9 @@
1
1
  import type { Command, CommandResult, Variables } from "../domain/command.types.ts";
2
2
  import type { Volume } from "../domain/sandbox.types.ts";
3
3
  import type { Executor } from "../infrastructure/process.types.ts";
4
+ import type { DependencyCache } from "./container-cache.types.ts";
4
5
  export interface ContainerOptions {
6
+ readonly caches?: readonly DependencyCache[];
5
7
  readonly image?: string;
6
8
  readonly user?: {
7
9
  readonly uid: number;
@@ -1 +1 @@
1
- {"version":3,"file":"container.types.js","sourceRoot":"","sources":["../../src/providers/container.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n Command,\n CommandResult,\n Variables,\n} from \"../domain/command.types.ts\";\nimport type { Volume } from \"../domain/sandbox.types.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\n\nexport interface ContainerOptions {\n readonly image?: string;\n readonly user?: { readonly uid: number; readonly gid: number };\n readonly volumes?: readonly Volume[];\n readonly variables?: Variables;\n readonly networks?: string | readonly string[];\n readonly groups?: readonly (string | number)[];\n readonly devices?: readonly string[];\n readonly cpus?: number;\n readonly memoryMb?: number;\n readonly label?: \"z\" | \"Z\" | false;\n readonly retain?: number;\n readonly userns?: \"keep-id\" | false;\n}\n\nexport type ContainerEngine = \"docker\" | \"podman\";\nexport type ContainerCall = (\n args: readonly string[],\n extra?: Partial<Command>,\n) => Promise<CommandResult>;\nexport interface ContainerRuntime {\n readonly engine: ContainerEngine;\n readonly config: ContainerOptions;\n readonly executor: Executor;\n readonly root: string;\n readonly name: string;\n readonly env: Record<string, string>;\n readonly call: ContainerCall;\n readonly isClosed: () => boolean;\n}\nexport interface ContainerMounts {\n readonly env: Record<string, string>;\n readonly volumes: string[];\n readonly fileParents: Set<string>;\n}\n\nexport interface ContainerUser {\n readonly uid: number;\n readonly gid: number;\n}\nexport interface ContainerPlanOptions {\n readonly config: ContainerOptions;\n readonly engine: ContainerEngine;\n readonly user: ContainerUser;\n readonly root: string;\n readonly name: string;\n readonly image: string;\n readonly volumes: readonly string[];\n readonly fileParents: ReadonlySet<string>;\n}\n"]}
1
+ {"version":3,"file":"container.types.js","sourceRoot":"","sources":["../../src/providers/container.types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n Command,\n CommandResult,\n Variables,\n} from \"../domain/command.types.ts\";\nimport type { Volume } from \"../domain/sandbox.types.ts\";\nimport type { Executor } from \"../infrastructure/process.types.ts\";\nimport type { DependencyCache } from \"./container-cache.types.ts\";\n\nexport interface ContainerOptions {\n readonly caches?: readonly DependencyCache[];\n readonly image?: string;\n readonly user?: { readonly uid: number; readonly gid: number };\n readonly volumes?: readonly Volume[];\n readonly variables?: Variables;\n readonly networks?: string | readonly string[];\n readonly groups?: readonly (string | number)[];\n readonly devices?: readonly string[];\n readonly cpus?: number;\n readonly memoryMb?: number;\n readonly label?: \"z\" | \"Z\" | false;\n readonly retain?: number;\n readonly userns?: \"keep-id\" | false;\n}\n\nexport type ContainerEngine = \"docker\" | \"podman\";\nexport type ContainerCall = (\n args: readonly string[],\n extra?: Partial<Command>,\n) => Promise<CommandResult>;\nexport interface ContainerRuntime {\n readonly engine: ContainerEngine;\n readonly config: ContainerOptions;\n readonly executor: Executor;\n readonly root: string;\n readonly name: string;\n readonly env: Record<string, string>;\n readonly call: ContainerCall;\n readonly isClosed: () => boolean;\n}\nexport interface ContainerMounts {\n readonly env: Record<string, string>;\n readonly volumes: string[];\n readonly fileParents: Set<string>;\n}\n\nexport interface ContainerUser {\n readonly uid: number;\n readonly gid: number;\n}\nexport interface ContainerPlanOptions {\n readonly config: ContainerOptions;\n readonly engine: ContainerEngine;\n readonly user: ContainerUser;\n readonly root: string;\n readonly name: string;\n readonly image: string;\n readonly volumes: readonly string[];\n readonly fileParents: ReadonlySet<string>;\n}\n"]}
@@ -1,3 +1,4 @@
1
+ import { setTimeout } from "node:timers/promises";
1
2
  import { randomUUID } from "node:crypto";
2
3
  import { OutpostError } from "../domain/errors.js";
3
4
  import { interruptible } from "../infrastructure/abort.js";
@@ -59,10 +60,12 @@ export function daytonaCommand(runtime) {
59
60
  if (cancellation)
60
61
  await cancellation;
61
62
  signal.throwIfAborted();
62
- const result = await sandbox.process.getSessionCommand(id, response.cmdId);
63
- if (result.exitCode === undefined)
64
- throw new OutpostError("provider", "Cloud command ended without an exit status");
65
- return { status: result.exitCode, ...output };
63
+ while (true) {
64
+ const result = await interruptible(sandbox.process.getSessionCommand(id, response.cmdId), signal);
65
+ if (result.exitCode !== undefined)
66
+ return { status: result.exitCode, ...output };
67
+ await setTimeout(cloudDefaults.pollMs, undefined, { signal });
68
+ }
66
69
  }
67
70
  catch (cause) {
68
71
  cancel();