@knpkv/codecommit-core 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (192) hide show
  1. package/dist/AwsClient/getDifferences.d.ts +16 -0
  2. package/dist/AwsClient/getDifferences.d.ts.map +1 -0
  3. package/dist/AwsClient/getDifferences.js +22 -0
  4. package/dist/AwsClient/getDifferences.js.map +1 -0
  5. package/dist/AwsClient/getPullRequest.d.ts.map +1 -1
  6. package/dist/AwsClient/getPullRequest.js +28 -7
  7. package/dist/AwsClient/getPullRequest.js.map +1 -1
  8. package/dist/AwsClient/getPullRequests.d.ts.map +1 -1
  9. package/dist/AwsClient/getPullRequests.js +31 -13
  10. package/dist/AwsClient/getPullRequests.js.map +1 -1
  11. package/dist/AwsClient/index.d.ts +21 -13
  12. package/dist/AwsClient/index.d.ts.map +1 -1
  13. package/dist/AwsClient/index.js +3 -1
  14. package/dist/AwsClient/index.js.map +1 -1
  15. package/dist/AwsClient/internal.d.ts +23 -0
  16. package/dist/AwsClient/internal.d.ts.map +1 -1
  17. package/dist/AwsClient/internal.js +4 -1
  18. package/dist/AwsClient/internal.js.map +1 -1
  19. package/dist/CacheService/Database.d.ts.map +1 -1
  20. package/dist/CacheService/Database.js +13 -1
  21. package/dist/CacheService/Database.js.map +1 -1
  22. package/dist/CacheService/EventsHub.d.ts +24 -4
  23. package/dist/CacheService/EventsHub.d.ts.map +1 -1
  24. package/dist/CacheService/EventsHub.js.map +1 -1
  25. package/dist/CacheService/index.d.ts +5 -2
  26. package/dist/CacheService/index.d.ts.map +1 -1
  27. package/dist/CacheService/index.js +3 -1
  28. package/dist/CacheService/index.js.map +1 -1
  29. package/dist/CacheService/migrations/0005_add_sandboxes.d.ts +5 -0
  30. package/dist/CacheService/migrations/0005_add_sandboxes.d.ts.map +1 -0
  31. package/dist/CacheService/migrations/0005_add_sandboxes.js +23 -0
  32. package/dist/CacheService/migrations/0005_add_sandboxes.js.map +1 -0
  33. package/dist/CacheService/migrations/0006_sandbox_logs.d.ts +5 -0
  34. package/dist/CacheService/migrations/0006_sandbox_logs.d.ts.map +1 -0
  35. package/dist/CacheService/migrations/0006_sandbox_logs.js +7 -0
  36. package/dist/CacheService/migrations/0006_sandbox_logs.js.map +1 -0
  37. package/dist/CacheService/migrations/0007_stats_columns.d.ts +5 -0
  38. package/dist/CacheService/migrations/0007_stats_columns.d.ts.map +1 -0
  39. package/dist/CacheService/migrations/0007_stats_columns.js +13 -0
  40. package/dist/CacheService/migrations/0007_stats_columns.js.map +1 -0
  41. package/dist/CacheService/migrations/0008_merged_by.d.ts +5 -0
  42. package/dist/CacheService/migrations/0008_merged_by.d.ts.map +1 -0
  43. package/dist/CacheService/migrations/0008_merged_by.js +4 -0
  44. package/dist/CacheService/migrations/0008_merged_by.js.map +1 -0
  45. package/dist/CacheService/migrations/0009_approved_by.d.ts +5 -0
  46. package/dist/CacheService/migrations/0009_approved_by.d.ts.map +1 -0
  47. package/dist/CacheService/migrations/0009_approved_by.js +4 -0
  48. package/dist/CacheService/migrations/0009_approved_by.js.map +1 -0
  49. package/dist/CacheService/migrations/0010_commented_by.d.ts +5 -0
  50. package/dist/CacheService/migrations/0010_commented_by.d.ts.map +1 -0
  51. package/dist/CacheService/migrations/0010_commented_by.js +4 -0
  52. package/dist/CacheService/migrations/0010_commented_by.js.map +1 -0
  53. package/dist/CacheService/repos/PullRequestRepo/index.d.ts +132 -0
  54. package/dist/CacheService/repos/PullRequestRepo/index.d.ts.map +1 -0
  55. package/dist/CacheService/repos/PullRequestRepo/index.js +35 -0
  56. package/dist/CacheService/repos/PullRequestRepo/index.js.map +1 -0
  57. package/dist/CacheService/repos/PullRequestRepo/internal.d.ts +72 -0
  58. package/dist/CacheService/repos/PullRequestRepo/internal.d.ts.map +1 -0
  59. package/dist/CacheService/repos/PullRequestRepo/internal.js +69 -0
  60. package/dist/CacheService/repos/PullRequestRepo/internal.js.map +1 -0
  61. package/dist/CacheService/repos/PullRequestRepo/mutations.d.ts +24 -0
  62. package/dist/CacheService/repos/PullRequestRepo/mutations.d.ts.map +1 -0
  63. package/dist/CacheService/repos/PullRequestRepo/mutations.js +94 -0
  64. package/dist/CacheService/repos/PullRequestRepo/mutations.js.map +1 -0
  65. package/dist/CacheService/repos/PullRequestRepo/queries.d.ts +113 -0
  66. package/dist/CacheService/repos/PullRequestRepo/queries.d.ts.map +1 -0
  67. package/dist/CacheService/repos/PullRequestRepo/queries.js +89 -0
  68. package/dist/CacheService/repos/PullRequestRepo/queries.js.map +1 -0
  69. package/dist/CacheService/repos/SandboxRepo.d.ts +116 -0
  70. package/dist/CacheService/repos/SandboxRepo.d.ts.map +1 -0
  71. package/dist/CacheService/repos/SandboxRepo.js +88 -0
  72. package/dist/CacheService/repos/SandboxRepo.js.map +1 -0
  73. package/dist/CacheService/repos/StatsRepo/index.d.ts +93 -0
  74. package/dist/CacheService/repos/StatsRepo/index.d.ts.map +1 -0
  75. package/dist/CacheService/repos/StatsRepo/index.js +41 -0
  76. package/dist/CacheService/repos/StatsRepo/index.js.map +1 -0
  77. package/dist/CacheService/repos/StatsRepo/internal.d.ts +120 -0
  78. package/dist/CacheService/repos/StatsRepo/internal.d.ts.map +1 -0
  79. package/dist/CacheService/repos/StatsRepo/internal.js +34 -0
  80. package/dist/CacheService/repos/StatsRepo/internal.js.map +1 -0
  81. package/dist/CacheService/repos/StatsRepo/queries.d.ts +42 -0
  82. package/dist/CacheService/repos/StatsRepo/queries.d.ts.map +1 -0
  83. package/dist/CacheService/repos/StatsRepo/queries.js +182 -0
  84. package/dist/CacheService/repos/StatsRepo/queries.js.map +1 -0
  85. package/dist/CacheService/repos/StatsRepo/reviewerData.d.ts +47 -0
  86. package/dist/CacheService/repos/StatsRepo/reviewerData.d.ts.map +1 -0
  87. package/dist/CacheService/repos/StatsRepo/reviewerData.js +172 -0
  88. package/dist/CacheService/repos/StatsRepo/reviewerData.js.map +1 -0
  89. package/dist/ConfigService/index.d.ts +1 -1
  90. package/dist/ConfigService/index.d.ts.map +1 -1
  91. package/dist/ConfigService/index.js +1 -1
  92. package/dist/ConfigService/index.js.map +1 -1
  93. package/dist/ConfigService/internal.d.ts +98 -0
  94. package/dist/ConfigService/internal.d.ts.map +1 -1
  95. package/dist/ConfigService/internal.js +16 -1
  96. package/dist/ConfigService/internal.js.map +1 -1
  97. package/dist/ConfigService/load.d.ts +15 -0
  98. package/dist/ConfigService/load.d.ts.map +1 -1
  99. package/dist/ConfigService/load.js +10 -3
  100. package/dist/ConfigService/load.js.map +1 -1
  101. package/dist/ConfigService/reset.d.ts.map +1 -1
  102. package/dist/ConfigService/reset.js +3 -2
  103. package/dist/ConfigService/reset.js.map +1 -1
  104. package/dist/DateUtils.d.ts +28 -0
  105. package/dist/DateUtils.d.ts.map +1 -1
  106. package/dist/DateUtils.js +69 -1
  107. package/dist/DateUtils.js.map +1 -1
  108. package/dist/Domain.d.ts +87 -5
  109. package/dist/Domain.d.ts.map +1 -1
  110. package/dist/Domain.js +38 -5
  111. package/dist/Domain.js.map +1 -1
  112. package/dist/Errors.d.ts +28 -1
  113. package/dist/Errors.d.ts.map +1 -1
  114. package/dist/Errors.js +22 -1
  115. package/dist/Errors.js.map +1 -1
  116. package/dist/PRService/index.d.ts +3 -3
  117. package/dist/PRService/index.d.ts.map +1 -1
  118. package/dist/PRService/index.js +1 -1
  119. package/dist/PRService/index.js.map +1 -1
  120. package/dist/PRService/internal.d.ts +24 -2
  121. package/dist/PRService/internal.d.ts.map +1 -1
  122. package/dist/PRService/internal.js +23 -8
  123. package/dist/PRService/internal.js.map +1 -1
  124. package/dist/PRService/refresh.d.ts +1 -1
  125. package/dist/PRService/refresh.d.ts.map +1 -1
  126. package/dist/PRService/refresh.js +2 -0
  127. package/dist/PRService/refresh.js.map +1 -1
  128. package/dist/PRService/refreshDiffs.d.ts +16 -0
  129. package/dist/PRService/refreshDiffs.d.ts.map +1 -0
  130. package/dist/PRService/refreshDiffs.js +54 -0
  131. package/dist/PRService/refreshDiffs.js.map +1 -0
  132. package/dist/PRService/refreshEnrich.d.ts +1 -1
  133. package/dist/PRService/refreshEnrich.d.ts.map +1 -1
  134. package/dist/PRService/refreshEnrich.js +3 -1
  135. package/dist/PRService/refreshEnrich.js.map +1 -1
  136. package/dist/PRService/refreshFetch.d.ts +1 -1
  137. package/dist/PRService/refreshFetch.d.ts.map +1 -1
  138. package/dist/PRService/refreshFetch.js +12 -3
  139. package/dist/PRService/refreshFetch.js.map +1 -1
  140. package/dist/PRService/refreshHistory.d.ts +19 -0
  141. package/dist/PRService/refreshHistory.d.ts.map +1 -0
  142. package/dist/PRService/refreshHistory.js +110 -0
  143. package/dist/PRService/refreshHistory.js.map +1 -0
  144. package/dist/PRService/refreshResolve.d.ts +1 -1
  145. package/dist/PRService/refreshResolve.d.ts.map +1 -1
  146. package/dist/PRService/refreshResolve.js +1 -1
  147. package/dist/PRService/refreshResolve.js.map +1 -1
  148. package/dist/PRService/refreshScore.d.ts +1 -1
  149. package/dist/PRService/refreshScore.d.ts.map +1 -1
  150. package/dist/PRService/refreshScore.js +1 -1
  151. package/dist/PRService/refreshScore.js.map +1 -1
  152. package/dist/PRService/refreshSinglePR.d.ts +1 -1
  153. package/dist/PRService/refreshSinglePR.d.ts.map +1 -1
  154. package/dist/PRService/refreshSinglePR.js +4 -3
  155. package/dist/PRService/refreshSinglePR.js.map +1 -1
  156. package/dist/SandboxService/DockerService.d.ts +57 -0
  157. package/dist/SandboxService/DockerService.d.ts.map +1 -0
  158. package/dist/SandboxService/DockerService.js +83 -0
  159. package/dist/SandboxService/DockerService.js.map +1 -0
  160. package/dist/SandboxService/PluginService.d.ts +38 -0
  161. package/dist/SandboxService/PluginService.d.ts.map +1 -0
  162. package/dist/SandboxService/PluginService.js +26 -0
  163. package/dist/SandboxService/PluginService.js.map +1 -0
  164. package/dist/SandboxService/SandboxService.d.ts +102 -0
  165. package/dist/SandboxService/SandboxService.d.ts.map +1 -0
  166. package/dist/SandboxService/SandboxService.js +272 -0
  167. package/dist/SandboxService/SandboxService.js.map +1 -0
  168. package/dist/SandboxService/index.d.ts +13 -0
  169. package/dist/SandboxService/index.d.ts.map +1 -0
  170. package/dist/SandboxService/index.js +10 -0
  171. package/dist/SandboxService/index.js.map +1 -0
  172. package/dist/SandboxService/plugins/ClaudeCodePlugin.d.ts +18 -0
  173. package/dist/SandboxService/plugins/ClaudeCodePlugin.d.ts.map +1 -0
  174. package/dist/SandboxService/plugins/ClaudeCodePlugin.js +24 -0
  175. package/dist/SandboxService/plugins/ClaudeCodePlugin.js.map +1 -0
  176. package/dist/StatsService/WeeklyStats.d.ts +174 -0
  177. package/dist/StatsService/WeeklyStats.d.ts.map +1 -0
  178. package/dist/StatsService/WeeklyStats.js +110 -0
  179. package/dist/StatsService/WeeklyStats.js.map +1 -0
  180. package/dist/StatsService/index.d.ts +45 -0
  181. package/dist/StatsService/index.d.ts.map +1 -0
  182. package/dist/StatsService/index.js +145 -0
  183. package/dist/StatsService/index.js.map +1 -0
  184. package/dist/index.d.ts +2 -0
  185. package/dist/index.d.ts.map +1 -1
  186. package/dist/index.js +2 -0
  187. package/dist/index.js.map +1 -1
  188. package/package.json +1 -1
  189. package/dist/CacheService/repos/PullRequestRepo.d.ts +0 -112
  190. package/dist/CacheService/repos/PullRequestRepo.d.ts.map +0 -1
  191. package/dist/CacheService/repos/PullRequestRepo.js +0 -146
  192. package/dist/CacheService/repos/PullRequestRepo.js.map +0 -1
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Docker interaction via `docker` CLI.
3
+ *
4
+ * Shells out to the docker binary — avoids unix socket HTTP complexity.
5
+ *
6
+ * @module
7
+ */
8
+ import { CommandExecutor } from "@effect/platform";
9
+ import { Effect } from "effect";
10
+ import { DockerError } from "../Errors.js";
11
+ export interface ContainerConfig {
12
+ readonly Image: string;
13
+ readonly Cmd: ReadonlyArray<string>;
14
+ readonly ExposedPorts: Record<string, Record<string, never>>;
15
+ readonly HostConfig: {
16
+ readonly Binds: ReadonlyArray<string>;
17
+ readonly PortBindings: Record<string, ReadonlyArray<{
18
+ HostPort: string;
19
+ }>>;
20
+ readonly NetworkMode?: string;
21
+ };
22
+ readonly Env?: ReadonlyArray<string>;
23
+ readonly Labels?: Record<string, string>;
24
+ }
25
+ export interface ContainerInfo {
26
+ readonly Id: string;
27
+ readonly State: {
28
+ readonly Status: string;
29
+ readonly Running: boolean;
30
+ };
31
+ readonly NetworkSettings: {
32
+ readonly Ports: Record<string, ReadonlyArray<{
33
+ HostPort: string;
34
+ }> | null>;
35
+ };
36
+ }
37
+ declare const DockerService_base: Effect.Service.Class<DockerService, "DockerService", {
38
+ readonly effect: Effect.Effect<{
39
+ readonly isAvailable: () => Effect.Effect<boolean, never, never>;
40
+ readonly pullImage: (image: string) => Effect.Effect<void, DockerError, never>;
41
+ readonly createContainer: (config: ContainerConfig) => Effect.Effect<string, DockerError, never>;
42
+ readonly startContainer: (containerId: string) => Effect.Effect<void, DockerError, never>;
43
+ readonly stopContainer: (containerId: string, timeout?: number) => Effect.Effect<void, DockerError, never>;
44
+ readonly removeContainer: (containerId: string) => Effect.Effect<void, DockerError, never>;
45
+ readonly inspectContainer: (containerId: string) => Effect.Effect<ContainerInfo, DockerError, never>;
46
+ readonly exec: (containerId: string, cmd: ReadonlyArray<string>) => Effect.Effect<string, DockerError, never>;
47
+ readonly listContainersByLabel: (label: string, value: string) => Effect.Effect<{
48
+ Id: string;
49
+ State: string;
50
+ Labels: any;
51
+ }[], DockerError, never>;
52
+ }, never, CommandExecutor.CommandExecutor>;
53
+ }>;
54
+ export declare class DockerService extends DockerService_base {
55
+ }
56
+ export {};
57
+ //# sourceMappingURL=DockerService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DockerService.d.ts","sourceRoot":"","sources":["../../src/SandboxService/DockerService.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAW,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAE1C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,GAAG,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACnC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;IAC5D,QAAQ,CAAC,UAAU,EAAE;QACnB,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;QACrC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC,CAAA;QAC1E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAC9B,CAAA;IACD,QAAQ,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAA;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACzC;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE;QACd,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;QACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;KAC1B,CAAA;IACD,QAAQ,CAAC,eAAe,EAAE;QACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;YAAE,QAAQ,EAAE,MAAM,CAAA;SAAE,CAAC,GAAG,IAAI,CAAC,CAAA;KAC3E,CAAA;CACF;;;;oCA4BwB,MAAM;2CAEC,eAAe;+CAqCX,MAAM;8CAGP,MAAM;gDAGJ,MAAM;iDAGL,MAAM;qCAelB,MAAM,OAAO,aAAa,CAAC,MAAM,CAAC;gDAGvB,MAAM,SAAS,MAAM;;;;;;;AA/E1D,qBAAa,aAAc,SAAQ,kBAqGjC;CAAG"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Docker interaction via `docker` CLI.
3
+ *
4
+ * Shells out to the docker binary — avoids unix socket HTTP complexity.
5
+ *
6
+ * @module
7
+ */
8
+ import { Command, CommandExecutor } from "@effect/platform";
9
+ import { Effect } from "effect";
10
+ import { DockerError } from "../Errors.js";
11
+ const dockerError = (operation) => (effect) => effect.pipe(Effect.mapError((cause) => new DockerError({ operation, cause })), Effect.withSpan(`DockerService.${operation}`, { captureStackTrace: false }));
12
+ const shellEscape = (s) => `'${s.replace(/'/g, "'\\''")}'`;
13
+ const run = (...args) => Command.make("sh", "-c", `docker ${args.map(shellEscape).join(" ")} 2>&1`).pipe(Command.string);
14
+ export class DockerService extends Effect.Service()("DockerService", {
15
+ effect: Effect.gen(function* () {
16
+ const executor = yield* CommandExecutor.CommandExecutor;
17
+ const docker = (...args) => run(...args).pipe(Effect.provideService(CommandExecutor.CommandExecutor, executor));
18
+ return {
19
+ isAvailable: () => docker("info").pipe(Effect.map(() => true), Effect.catchAll(() => Effect.succeed(false))),
20
+ pullImage: (image) => docker("pull", image).pipe(Effect.asVoid, dockerError("pullImage")),
21
+ createContainer: (config) => {
22
+ const args = ["create"];
23
+ // Port bindings
24
+ for (const [containerPort, bindings] of Object.entries(config.HostConfig.PortBindings)) {
25
+ for (const b of bindings) {
26
+ args.push("-p", `${b.HostPort}:${containerPort.replace("/tcp", "")}`);
27
+ }
28
+ }
29
+ // Volume binds
30
+ for (const bind of config.HostConfig.Binds) {
31
+ args.push("-v", bind);
32
+ }
33
+ // Network mode
34
+ if (config.HostConfig.NetworkMode) {
35
+ args.push("--network", config.HostConfig.NetworkMode);
36
+ }
37
+ // Env vars
38
+ for (const env of config.Env ?? []) {
39
+ args.push("-e", env);
40
+ }
41
+ // Labels
42
+ for (const [k, v] of Object.entries(config.Labels ?? {})) {
43
+ args.push("-l", `${k}=${v}`);
44
+ }
45
+ // Image + command
46
+ args.push(config.Image);
47
+ for (const c of config.Cmd)
48
+ args.push(c);
49
+ return docker(...args).pipe(dockerError("createContainer"));
50
+ },
51
+ startContainer: (containerId) => docker("start", containerId).pipe(Effect.asVoid, dockerError("startContainer")),
52
+ stopContainer: (containerId, timeout = 10) => docker("stop", "-t", String(timeout), containerId).pipe(Effect.asVoid, dockerError("stopContainer")),
53
+ removeContainer: (containerId) => docker("rm", "-f", containerId).pipe(Effect.asVoid, dockerError("removeContainer")),
54
+ inspectContainer: (containerId) => docker("inspect", containerId).pipe(Effect.flatMap((output) => Effect.try({
55
+ try: () => {
56
+ const arr = JSON.parse(output);
57
+ if (arr.length === 0)
58
+ throw new Error("Empty inspect result");
59
+ return arr[0];
60
+ },
61
+ catch: (e) => e
62
+ })), dockerError("inspectContainer")),
63
+ exec: (containerId, cmd) => docker("exec", containerId, ...cmd).pipe(dockerError("exec")),
64
+ listContainersByLabel: (label, value) => docker("ps", "-a", "--filter", `label=${label}=${value}`, "--format", "{{json .}}").pipe(Effect.flatMap((output) => Effect.try({
65
+ try: () => {
66
+ if (!output)
67
+ return [];
68
+ return output.split("\n").map((line) => {
69
+ const obj = JSON.parse(line);
70
+ return {
71
+ Id: obj.ID,
72
+ State: obj.State,
73
+ Labels: Object.fromEntries(obj.Labels.split(",").map((l) => l.split("=")))
74
+ };
75
+ });
76
+ },
77
+ catch: (e) => e
78
+ })), dockerError("listContainersByLabel"))
79
+ };
80
+ })
81
+ }) {
82
+ }
83
+ //# sourceMappingURL=DockerService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DockerService.js","sourceRoot":"","sources":["../../src/SandboxService/DockerService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Sandbox plugin system — extensible lifecycle hooks.
3
+ *
4
+ * Plugins execute sequentially in registration order.
5
+ * Plugin failures are non-fatal (logged, sandbox continues).
6
+ *
7
+ * @module
8
+ */
9
+ import { Effect } from "effect";
10
+ import type { PullRequestId, RepositoryName, SandboxId } from "../Domain.js";
11
+ export interface SandboxContext {
12
+ readonly sandboxId: SandboxId;
13
+ readonly containerId: string;
14
+ readonly workspacePath: string;
15
+ readonly port: number;
16
+ readonly pr: {
17
+ readonly id: PullRequestId;
18
+ readonly repositoryName: RepositoryName;
19
+ readonly sourceBranch: string;
20
+ };
21
+ }
22
+ export interface SandboxPlugin {
23
+ readonly name: string;
24
+ readonly onSandboxCreate?: (ctx: SandboxContext) => Effect.Effect<void>;
25
+ readonly onSandboxReady?: (ctx: SandboxContext) => Effect.Effect<void>;
26
+ readonly onSandboxDestroy?: (ctx: SandboxContext) => Effect.Effect<void>;
27
+ }
28
+ declare const PluginService_base: Effect.Service.Class<PluginService, "PluginService", {
29
+ readonly effect: Effect.Effect<{
30
+ readonly register: (plugin: SandboxPlugin) => Effect.Effect<void, never, never>;
31
+ readonly executeHook: (hook: "onSandboxCreate" | "onSandboxReady" | "onSandboxDestroy", ctx: SandboxContext) => Effect.Effect<void, never, never>;
32
+ readonly listPlugins: () => Effect.Effect<string[], never, never>;
33
+ }, never, never>;
34
+ }>;
35
+ export declare class PluginService extends PluginService_base {
36
+ }
37
+ export {};
38
+ //# sourceMappingURL=PluginService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginService.d.ts","sourceRoot":"","sources":["../../src/SandboxService/PluginService.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,MAAM,EAAO,MAAM,QAAQ,CAAA;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE5E,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAA;IAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE;QACX,QAAQ,CAAC,EAAE,EAAE,aAAa,CAAA;QAC1B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAA;QACvC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;KAC9B,CAAA;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACvE,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IACtE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;CACzE;;;oCAOwB,aAAa;qCAKZ,iBAAiB,GAAG,gBAAgB,GAAG,kBAAkB,OAAO,cAAc;;;;AAVxG,qBAAa,aAAc,SAAQ,kBA2BjC;CAAG"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Sandbox plugin system — extensible lifecycle hooks.
3
+ *
4
+ * Plugins execute sequentially in registration order.
5
+ * Plugin failures are non-fatal (logged, sandbox continues).
6
+ *
7
+ * @module
8
+ */
9
+ import { Effect, Ref } from "effect";
10
+ export class PluginService extends Effect.Service()("PluginService", {
11
+ effect: Effect.gen(function* () {
12
+ const plugins = yield* Ref.make([]);
13
+ return {
14
+ register: (plugin) => Ref.update(plugins, (ps) => [...ps, plugin]).pipe(Effect.tap(() => Effect.logInfo(`Plugin registered: ${plugin.name}`))),
15
+ executeHook: (hook, ctx) => Ref.get(plugins).pipe(Effect.flatMap((ps) => Effect.forEach(ps, (p) => {
16
+ const fn = p[hook];
17
+ if (!fn)
18
+ return Effect.void;
19
+ return fn(ctx).pipe(Effect.catchAllCause((cause) => Effect.logWarning(`Plugin ${p.name} ${hook} failed`, cause)));
20
+ }, { discard: true })), Effect.withSpan(`PluginService.${hook}`, { captureStackTrace: false })),
21
+ listPlugins: () => Ref.get(plugins).pipe(Effect.map((ps) => ps.map((p) => p.name)))
22
+ };
23
+ })
24
+ }) {
25
+ }
26
+ //# sourceMappingURL=PluginService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PluginService.js","sourceRoot":"","sources":["../../src/SandboxService/PluginService.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,QAAQ,CAAA;AAsBpC,MAAM,OAAO,aAAc,SAAQ,MAAM,CAAC,OAAO,EAAiB,CAAC,eAAe,EAAE;IAClF,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,IAAI,CAA+B,EAAE,CAAC,CAAA;QAEjE,OAAO;YACL,QAAQ,EAAE,CAAC,MAAqB,EAAE,EAAE,CAClC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAC/C,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,sBAAsB,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CACtE;YAEH,WAAW,EAAE,CAAC,IAA+D,EAAE,GAAmB,EAAE,EAAE,CACpG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CACnB,MAAM,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CACpB,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE;gBACvB,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,CAAA;gBAClB,IAAI,CAAC,EAAE;oBAAE,OAAO,MAAM,CAAC,IAAI,CAAA;gBAC3B,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CACjB,MAAM,CAAC,aAAa,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,IAAI,IAAI,IAAI,SAAS,EAAE,KAAK,CAAC,CAAC,CAC7F,CAAA;YACH,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CACtB,EACD,MAAM,CAAC,QAAQ,CAAC,iBAAiB,IAAI,EAAE,EAAE,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC,CACvE;YAEH,WAAW,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;SAC3E,CAAA;IACZ,CAAC,CAAC;CACH,CAAC;CAAG"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Sandbox orchestration service.
3
+ *
4
+ * Ties together DockerService, PluginService, SandboxRepo
5
+ * to manage sandbox lifecycle: create → run → stop → cleanup.
6
+ *
7
+ * @module
8
+ */
9
+ import { FileSystem } from "@effect/platform";
10
+ import { Duration, Effect } from "effect";
11
+ import { SandboxRepo } from "../CacheService/repos/SandboxRepo.js";
12
+ import { ConfigService } from "../ConfigService/index.js";
13
+ import { SandboxId } from "../Domain.js";
14
+ import { SandboxError } from "../Errors.js";
15
+ import { DockerService } from "./DockerService.js";
16
+ import { PluginService } from "./PluginService.js";
17
+ export interface CreateSandboxParams {
18
+ readonly pullRequestId: string;
19
+ readonly awsAccountId: string;
20
+ readonly repositoryName: string;
21
+ readonly sourceBranch: string;
22
+ readonly profile: string;
23
+ readonly region: string;
24
+ }
25
+ declare const SandboxService_base: Effect.Service.Class<SandboxService, "SandboxService", {
26
+ readonly dependencies: readonly [import("effect/Layer").Layer<SandboxRepo, import("@effect/sql/SqlError").SqlError | import("effect/ConfigError").ConfigError | import("@effect/sql/Migrator").MigrationError, FileSystem.FileSystem>, import("effect/Layer").Layer<DockerService, never, import("@effect/platform/CommandExecutor").CommandExecutor>, import("effect/Layer").Layer<PluginService, never, never>];
27
+ readonly effect: Effect.Effect<{
28
+ readonly create: (params: CreateSandboxParams) => Effect.Effect<{
29
+ readonly error: string | null;
30
+ readonly awsAccountId: string;
31
+ readonly id: string;
32
+ readonly repositoryName: string;
33
+ readonly status: string;
34
+ readonly sourceBranch: string;
35
+ readonly pullRequestId: string;
36
+ readonly workspacePath: string;
37
+ readonly createdAt: string;
38
+ readonly lastActivityAt: string;
39
+ readonly containerId: string | null;
40
+ readonly port: number | null;
41
+ readonly statusDetail: string | null;
42
+ readonly logs: string | null;
43
+ }, SandboxError, FileSystem.FileSystem | import("effect/Scope").Scope | import("@effect/platform/CommandExecutor").CommandExecutor>;
44
+ readonly get: (id: SandboxId) => Effect.Effect<{
45
+ readonly error: string | null;
46
+ readonly awsAccountId: string;
47
+ readonly id: string;
48
+ readonly repositoryName: string;
49
+ readonly status: string;
50
+ readonly sourceBranch: string;
51
+ readonly pullRequestId: string;
52
+ readonly workspacePath: string;
53
+ readonly createdAt: string;
54
+ readonly lastActivityAt: string;
55
+ readonly containerId: string | null;
56
+ readonly port: number | null;
57
+ readonly statusDetail: string | null;
58
+ readonly logs: string | null;
59
+ }, SandboxError, never>;
60
+ readonly list: () => Effect.Effect<readonly {
61
+ readonly error: string | null;
62
+ readonly awsAccountId: string;
63
+ readonly id: string;
64
+ readonly repositoryName: string;
65
+ readonly status: string;
66
+ readonly sourceBranch: string;
67
+ readonly pullRequestId: string;
68
+ readonly workspacePath: string;
69
+ readonly createdAt: string;
70
+ readonly lastActivityAt: string;
71
+ readonly containerId: string | null;
72
+ readonly port: number | null;
73
+ readonly statusDetail: string | null;
74
+ readonly logs: string | null;
75
+ }[], import("../CacheService/CacheError.ts").CacheError, never>;
76
+ readonly listAll: () => Effect.Effect<readonly {
77
+ readonly error: string | null;
78
+ readonly awsAccountId: string;
79
+ readonly id: string;
80
+ readonly repositoryName: string;
81
+ readonly status: string;
82
+ readonly sourceBranch: string;
83
+ readonly pullRequestId: string;
84
+ readonly workspacePath: string;
85
+ readonly createdAt: string;
86
+ readonly lastActivityAt: string;
87
+ readonly containerId: string | null;
88
+ readonly port: number | null;
89
+ readonly statusDetail: string | null;
90
+ readonly logs: string | null;
91
+ }[], import("../CacheService/CacheError.ts").CacheError, never>;
92
+ readonly stop: (id: SandboxId) => Effect.Effect<void, SandboxError, never>;
93
+ readonly restart: (id: SandboxId) => Effect.Effect<undefined, SandboxError, never>;
94
+ readonly cleanup: (id: SandboxId) => Effect.Effect<void, SandboxError, FileSystem.FileSystem>;
95
+ readonly reconcile: () => Effect.Effect<void, never, never>;
96
+ readonly gcIdle: (idleTimeout?: Duration.Duration, cleanupDelay?: Duration.Duration) => Effect.Effect<void, never, FileSystem.FileSystem>;
97
+ }, never, SandboxRepo | ConfigService | DockerService | PluginService>;
98
+ }>;
99
+ export declare class SandboxService extends SandboxService_base {
100
+ }
101
+ export {};
102
+ //# sourceMappingURL=SandboxService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SandboxService.d.ts","sourceRoot":"","sources":["../../src/SandboxService/SandboxService.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAW,UAAU,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EAAwB,QAAQ,EAAE,MAAM,EAAoC,MAAM,QAAQ,CAAA;AACjG,OAAO,EAAE,WAAW,EAAmB,MAAM,sCAAsC,CAAA;AACnF,OAAO,EAAE,aAAa,EAA4C,MAAM,2BAA2B,CAAA;AACnG,OAAO,EAAiC,SAAS,EAAsB,MAAM,cAAc,CAAA;AAC3F,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAwB,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAExE,OAAO,EAAE,aAAa,EAAuB,MAAM,oBAAoB,CAAA;AAEvE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAA;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;;;;kCA6GsB,mBAAmB;;;;;;;;;;;;;;;;2BAgL1B,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BASR,SAAS;+BAiBN,SAAS;+BAiCT,SAAS;;;;;AAvS7B,qBAAa,cAAe,SAAQ,mBAyXlC;CAAG"}
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Sandbox orchestration service.
3
+ *
4
+ * Ties together DockerService, PluginService, SandboxRepo
5
+ * to manage sandbox lifecycle: create → run → stop → cleanup.
6
+ *
7
+ * @module
8
+ */
9
+ import { Command, FileSystem } from "@effect/platform";
10
+ import { Cause, Clock, Config, Duration, Effect, Option, Random, Schedule, Stream } from "effect";
11
+ import { SandboxRepo } from "../CacheService/repos/SandboxRepo.js";
12
+ import { ConfigService, defaultSandboxConfig } from "../ConfigService/index.js";
13
+ import { PullRequestId, RepositoryName, SandboxId } from "../Domain.js";
14
+ import { SandboxError } from "../Errors.js";
15
+ import { DockerService } from "./DockerService.js";
16
+ import { makeClaudeCodePlugin } from "./plugins/ClaudeCodePlugin.js";
17
+ import { PluginService } from "./PluginService.js";
18
+ const SANDBOX_BASE_PORT = 18080;
19
+ const homeDir = Config.string("HOME").pipe(Config.orElse(() => Config.string("USERPROFILE")));
20
+ const sandboxesDir = homeDir.pipe(Config.map((h) => `${h}/.codecommit/sandboxes`));
21
+ const anthropicApiKey = Config.string("ANTHROPIC_API_KEY").pipe(Config.option);
22
+ const expandHome = (p, home) => p.startsWith("~/") ? `${home}${p.slice(1)}` : p;
23
+ const makeContainerConfig = (workspacePath, port, sandboxId, pullRequestId, apiKey, sandboxConfig, homePath) => ({
24
+ Image: sandboxConfig.image,
25
+ Cmd: ["--bind-addr", "0.0.0.0:8080", "--auth", "none", "/workspace"],
26
+ ExposedPorts: { "8080/tcp": {} },
27
+ HostConfig: {
28
+ Binds: [
29
+ `${workspacePath}:/workspace`,
30
+ ...sandboxConfig.volumeMounts.map((m) => `${expandHome(m.hostPath, homePath)}:${m.containerPath}${m.readonly ? ":ro" : ""}`)
31
+ ],
32
+ PortBindings: { "8080/tcp": [{ HostPort: String(port) }] }
33
+ },
34
+ Env: [
35
+ ...(Option.isSome(apiKey) ? [`ANTHROPIC_API_KEY=${apiKey.value}`] : []),
36
+ ...Object.entries(sandboxConfig.env).map(([k, v]) => `${k}=${v}`)
37
+ ],
38
+ Labels: {
39
+ "codecommit.sandbox.id": sandboxId,
40
+ "codecommit.sandbox.pr": pullRequestId
41
+ }
42
+ });
43
+ export class SandboxService extends Effect.Service()("SandboxService", {
44
+ dependencies: [SandboxRepo.Default, DockerService.Default, PluginService.Default],
45
+ effect: Effect.gen(function* () {
46
+ const repo = yield* SandboxRepo;
47
+ const docker = yield* DockerService;
48
+ const plugins = yield* PluginService;
49
+ const configService = yield* ConfigService;
50
+ const homePath = yield* homeDir.pipe(Effect.orDie);
51
+ const basePath = yield* sandboxesDir.pipe(Effect.orDie);
52
+ const apiKey = yield* anthropicApiKey.pipe(Effect.orDie);
53
+ const loadSandboxConfig = configService.load.pipe(Effect.map((c) => c.sandbox), Effect.catchAll(() => Effect.succeed(defaultSandboxConfig)));
54
+ // Register built-in plugins conditionally based on config
55
+ yield* Effect.gen(function* () {
56
+ const sandboxCfg = yield* loadSandboxConfig;
57
+ if (sandboxCfg.enableClaudeCode) {
58
+ yield* plugins.register(makeClaudeCodePlugin((containerId, cmd) => docker.exec(containerId, cmd).pipe(Effect.catchAll(() => Effect.void))));
59
+ }
60
+ });
61
+ const updateStatus = (id, status, extra) => repo.updateStatus(id, status, extra);
62
+ const progress = (id, detail) => Clock.currentTimeMillis.pipe(Effect.flatMap((ms) => {
63
+ const ts = new Date(ms).toISOString();
64
+ return repo.updateDetail(id, detail).pipe(Effect.tap(() => repo.appendLog(id, `[${ts}] ${detail}`)), Effect.tap(() => Effect.logInfo(`Sandbox ${id}: ${detail}`)));
65
+ }));
66
+ const allocatePort = () => Random.nextIntBetween(SANDBOX_BASE_PORT, SANDBOX_BASE_PORT + 1000);
67
+ const makeSandboxContext = (row) => ({
68
+ sandboxId: SandboxId.make(row.id),
69
+ containerId: row.containerId ?? "",
70
+ workspacePath: row.workspacePath,
71
+ port: row.port ?? 0,
72
+ pr: {
73
+ id: PullRequestId.make(row.pullRequestId),
74
+ repositoryName: RepositoryName.make(row.repositoryName),
75
+ sourceBranch: row.sourceBranch
76
+ }
77
+ });
78
+ return {
79
+ create: (params) => Effect.gen(function* () {
80
+ // Singleton check — one active sandbox per PR
81
+ const existing = yield* repo.findByPr(params.awsAccountId, params.pullRequestId);
82
+ if (Option.isSome(existing)) {
83
+ return existing.value;
84
+ }
85
+ const nowMs = yield* Clock.currentTimeMillis;
86
+ const rand = yield* Random.nextIntBetween(0, 2176782336);
87
+ const id = SandboxId.make(`sbx-${nowMs}-${rand.toString(36).padStart(6, "0")}`);
88
+ const port = yield* allocatePort();
89
+ const workspacePath = `${basePath}/${id}`;
90
+ const now = new Date(nowMs).toISOString();
91
+ yield* repo.insert({
92
+ id,
93
+ pullRequestId: params.pullRequestId,
94
+ awsAccountId: params.awsAccountId,
95
+ repositoryName: params.repositoryName,
96
+ sourceBranch: params.sourceBranch,
97
+ workspacePath,
98
+ status: "creating",
99
+ createdAt: now,
100
+ lastActivityAt: now
101
+ });
102
+ // Fork daemon for async lifecycle
103
+ yield* Effect.forkDaemon(Effect.gen(function* () {
104
+ const fs = yield* FileSystem.FileSystem;
105
+ const log = (detail) => progress(id, detail);
106
+ // Load config at creation time
107
+ yield* log("Loading sandbox config");
108
+ const sandboxCfg = yield* loadSandboxConfig;
109
+ // Clone via HTTPS + AWS credential helper
110
+ yield* updateStatus(id, "cloning");
111
+ yield* fs.makeDirectory(workspacePath, { recursive: true });
112
+ const cloneUrl = `https://git-codecommit.${params.region}.amazonaws.com/v1/repos/${params.repositoryName}`;
113
+ const branch = params.sourceBranch.replace(/^refs\/heads\//, "");
114
+ const depthLabel = sandboxCfg.cloneDepth > 0 ? ` (depth ${sandboxCfg.cloneDepth})` : "";
115
+ yield* log(`Cloning ${params.repositoryName}/${branch}${depthLabel}`);
116
+ const depthArgs = sandboxCfg.cloneDepth > 0
117
+ ? ["--depth", String(sandboxCfg.cloneDepth)]
118
+ : [];
119
+ const cloneProc = yield* Command.make("git", "-c", "credential.helper=!aws codecommit credential-helper $@", "-c", "credential.UseHttpPath=true", "clone", ...depthArgs, "-b", branch, cloneUrl, workspacePath).pipe(Command.env({ AWS_PROFILE: params.profile, AWS_DEFAULT_REGION: params.region }), Command.stderr("pipe"), Command.start);
120
+ const exitCode = yield* cloneProc.exitCode;
121
+ if (exitCode !== 0) {
122
+ const stderrChunks = yield* Stream.runCollect(cloneProc.stderr);
123
+ const stderrText = Array.from(stderrChunks).map((c) => new TextDecoder().decode(c)).join("").trim();
124
+ yield* log(`Clone failed: ${stderrText}`);
125
+ yield* updateStatus(id, "error", { error: stderrText || `git clone failed (exit ${exitCode})` });
126
+ return;
127
+ }
128
+ yield* log("Clone complete");
129
+ // Pull image
130
+ yield* updateStatus(id, "starting");
131
+ yield* log(`Pulling image ${sandboxCfg.image}`);
132
+ yield* docker.pullImage(sandboxCfg.image).pipe(Effect.tap(() => log("Image ready")), Effect.catchAll(() => log("Image pull skipped (using cached)")));
133
+ // Create + start container
134
+ yield* log("Creating container");
135
+ const containerConfig = makeContainerConfig(workspacePath, port, id, params.pullRequestId, apiKey, sandboxCfg, homePath);
136
+ const containerId = yield* docker.createContainer(containerConfig);
137
+ const cid = containerId.trim();
138
+ yield* log(`Container ${cid.slice(0, 12)} created, starting`);
139
+ yield* docker.startContainer(cid);
140
+ yield* updateStatus(id, "starting", { containerId: cid, port });
141
+ yield* log(`Container started on port ${port}`);
142
+ // Fix ownership of dirs that Docker may have created as root (from volume mounts)
143
+ yield* docker.exec(cid, [
144
+ "sudo",
145
+ "chown",
146
+ "-R",
147
+ "coder:coder",
148
+ "/home/coder/.local"
149
+ ]).pipe(Effect.catchAll(() => Effect.void));
150
+ // Wait for code-server to be ready (poll health)
151
+ yield* log("Waiting for code-server health check");
152
+ yield* docker.exec(cid, ["curl", "-sf", "http://localhost:8080/healthz"]).pipe(Effect.retry(Schedule.recurs(30).pipe(Schedule.intersect(Schedule.spaced(Duration.seconds(1))))), Effect.tap(() => log("code-server ready")), Effect.tapError((e) => log(`Health check failed: ${String(e)}`)), Effect.catchAll(() => Effect.void));
153
+ // Run plugin hooks
154
+ const row = yield* repo.findById(id);
155
+ const ctx = makeSandboxContext(row);
156
+ yield* log("Running plugin hooks");
157
+ yield* plugins.executeHook("onSandboxCreate", ctx);
158
+ yield* plugins.executeHook("onSandboxReady", ctx);
159
+ // Install configured extensions
160
+ if (sandboxCfg.extensions.length > 0) {
161
+ yield* log(`Installing ${sandboxCfg.extensions.length} extension(s)`);
162
+ yield* Effect.forEach(sandboxCfg.extensions, (ext) => log(`Installing extension: ${ext}`).pipe(Effect.zipRight(docker.exec(cid, ["code-server", "--install-extension", ext])), Effect.tap((output) => log(`Extension installed: ${ext}${output ? `\n${output.trim()}` : ""}`)), Effect.tapError((e) => log(`Extension failed: ${ext} — ${String(e)}`)), Effect.catchAll(() => Effect.void)), { discard: true });
163
+ }
164
+ // Run configured setup commands
165
+ if (sandboxCfg.setupCommands.length > 0) {
166
+ yield* log(`Running ${sandboxCfg.setupCommands.length} setup command(s)`);
167
+ yield* Effect.forEach(sandboxCfg.setupCommands, (cmd, i) => log(`[${i + 1}/${sandboxCfg.setupCommands.length}] ${cmd}`).pipe(Effect.zipRight(docker.exec(cid, ["sh", "-c", cmd])), Effect.tap((output) => log(`Command done: ${cmd.slice(0, 60)}${output ? `\n${output.trim()}` : ""}`)), Effect.tapError((e) => log(`Command failed: ${cmd.slice(0, 60)} — ${String(e)}`)), Effect.catchAll(() => Effect.void)), { discard: true });
168
+ }
169
+ yield* updateStatus(id, "running");
170
+ yield* log("Sandbox ready");
171
+ }).pipe(Effect.catchAllCause((cause) => Effect.gen(function* () {
172
+ yield* Effect.logError(`Sandbox ${id} creation failed`, cause);
173
+ const squashed = Cause.squash(cause);
174
+ const errorDetail = squashed instanceof Error ? squashed.message : String(squashed);
175
+ yield* updateStatus(id, "error", { error: errorDetail.slice(0, 500) }).pipe(Effect.catchAll((statusErr) => Effect.logError("Failed to update sandbox error status", statusErr)));
176
+ }))));
177
+ return yield* repo.findById(id);
178
+ }).pipe(Effect.mapError((cause) => new SandboxError({ message: "Failed to create sandbox", cause }))),
179
+ get: (id) => repo.findById(id).pipe(Effect.mapError((cause) => new SandboxError({ sandboxId: id, message: "Sandbox not found", cause }))),
180
+ list: () => repo.findActive(),
181
+ listAll: () => repo.findAll(),
182
+ stop: (id) => Effect.gen(function* () {
183
+ const row = yield* repo.findById(id);
184
+ yield* updateStatus(id, "stopping");
185
+ if (row.containerId) {
186
+ const ctx = makeSandboxContext(row);
187
+ yield* plugins.executeHook("onSandboxDestroy", ctx);
188
+ yield* docker.stopContainer(row.containerId).pipe(Effect.catchAll(() => Effect.void));
189
+ }
190
+ yield* updateStatus(id, "stopped");
191
+ yield* Effect.logInfo(`Sandbox ${id} stopped`);
192
+ }).pipe(Effect.mapError((cause) => new SandboxError({ sandboxId: id, message: "Failed to stop sandbox", cause }))),
193
+ restart: (id) => Effect.gen(function* () {
194
+ const row = yield* repo.findById(id);
195
+ if (!row.containerId) {
196
+ return yield* Effect.fail(new SandboxError({ sandboxId: id, message: "No container to restart" }));
197
+ }
198
+ yield* updateStatus(id, "starting");
199
+ yield* progress(id, "Restarting container");
200
+ yield* docker.startContainer(row.containerId);
201
+ yield* updateStatus(id, "starting", row.port ? { port: row.port } : {});
202
+ yield* progress(id, "Waiting for code-server health check");
203
+ yield* docker.exec(row.containerId, ["curl", "-sf", "http://localhost:8080/healthz"]).pipe(Effect.retry(Schedule.recurs(30).pipe(Schedule.intersect(Schedule.spaced(Duration.seconds(1))))), Effect.tap(() => progress(id, "code-server ready")), Effect.catchAll(() => Effect.void));
204
+ const ctx = makeSandboxContext(yield* repo.findById(id));
205
+ yield* plugins.executeHook("onSandboxReady", ctx);
206
+ yield* updateStatus(id, "running");
207
+ yield* progress(id, "Sandbox restarted");
208
+ yield* Effect.logInfo(`Sandbox ${id} restarted`);
209
+ }).pipe(Effect.mapError((cause) => new SandboxError({ sandboxId: id, message: "Failed to restart sandbox", cause }))),
210
+ cleanup: (id) => Effect.gen(function* () {
211
+ const row = yield* repo.findById(id);
212
+ const fs = yield* FileSystem.FileSystem;
213
+ if (row.containerId) {
214
+ yield* docker.removeContainer(row.containerId).pipe(Effect.catchAll(() => Effect.void));
215
+ }
216
+ yield* fs.remove(row.workspacePath, { recursive: true }).pipe(Effect.catchAll(() => Effect.void));
217
+ yield* repo.delete(id);
218
+ yield* Effect.logInfo(`Sandbox ${id} cleaned up`);
219
+ }).pipe(Effect.mapError((cause) => new SandboxError({ sandboxId: id, message: "Failed to cleanup sandbox", cause }))),
220
+ reconcile: () => Effect.gen(function* () {
221
+ const active = yield* repo.findActive();
222
+ yield* Effect.forEach(active, (row) => Effect.gen(function* () {
223
+ if (!row.containerId) {
224
+ yield* updateStatus(SandboxId.make(row.id), "error", { error: "Orphaned (no container)" });
225
+ return;
226
+ }
227
+ const info = yield* docker.inspectContainer(row.containerId).pipe(Effect.catchAll(() => Effect.succeed(null)));
228
+ if (!info || !info.State.Running) {
229
+ yield* updateStatus(SandboxId.make(row.id), "stopped");
230
+ yield* Effect.logInfo(`Reconciled orphaned sandbox ${row.id}`);
231
+ }
232
+ }), { discard: true });
233
+ }).pipe(Effect.catchAllCause((cause) => Effect.logWarning("Sandbox reconcile failed", cause))),
234
+ gcIdle: (idleTimeout = Duration.minutes(30), cleanupDelay = Duration.hours(24)) => Effect.gen(function* () {
235
+ const all = yield* repo.findAll();
236
+ const now = yield* Clock.currentTimeMillis;
237
+ // Stop idle running sandboxes
238
+ yield* Effect.forEach(all.filter((r) => r.status === "running"), (row) => {
239
+ const lastActivity = new Date(row.lastActivityAt).getTime();
240
+ if (now - lastActivity > Duration.toMillis(idleTimeout)) {
241
+ return Effect.gen(function* () {
242
+ yield* Effect.logInfo(`GC: stopping idle sandbox ${row.id}`);
243
+ if (row.containerId) {
244
+ yield* docker.stopContainer(row.containerId).pipe(Effect.catchAll(() => Effect.void));
245
+ }
246
+ yield* updateStatus(SandboxId.make(row.id), "stopped");
247
+ });
248
+ }
249
+ return Effect.void;
250
+ }, { discard: true });
251
+ // Cleanup stopped sandboxes past delay
252
+ const fs = yield* FileSystem.FileSystem;
253
+ yield* Effect.forEach(all.filter((r) => r.status === "stopped" || r.status === "error"), (row) => {
254
+ const lastActivity = new Date(row.lastActivityAt).getTime();
255
+ if (now - lastActivity > Duration.toMillis(cleanupDelay)) {
256
+ return Effect.gen(function* () {
257
+ yield* Effect.logInfo(`GC: cleaning up sandbox ${row.id}`);
258
+ if (row.containerId) {
259
+ yield* docker.removeContainer(row.containerId).pipe(Effect.catchAll(() => Effect.void));
260
+ }
261
+ yield* fs.remove(row.workspacePath, { recursive: true }).pipe(Effect.catchAll(() => Effect.void));
262
+ yield* repo.delete(SandboxId.make(row.id));
263
+ });
264
+ }
265
+ return Effect.void;
266
+ }, { discard: true });
267
+ }).pipe(Effect.catchAllCause((cause) => Effect.logWarning("Sandbox GC failed", cause)))
268
+ };
269
+ })
270
+ }) {
271
+ }
272
+ //# sourceMappingURL=SandboxService.js.map