@figulus/schema 0.4.0-alpha-dev → 0.5.0-alpha-dev-2

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/.openapi-meta/index.ts +12 -0
  2. package/dist/core/entities/figspec.d.ts +659 -0
  3. package/dist/core/entities/figspec.d.ts.map +1 -0
  4. package/dist/core/entities/figspec.js +73 -0
  5. package/dist/core/entities/figspec.js.map +1 -0
  6. package/dist/core/entities/figstack.d.ts +139 -0
  7. package/dist/core/entities/figstack.d.ts.map +1 -0
  8. package/dist/core/entities/figstack.js +79 -0
  9. package/dist/core/entities/figstack.js.map +1 -0
  10. package/dist/core/entities/index.d.ts +3 -0
  11. package/dist/core/entities/index.d.ts.map +1 -0
  12. package/dist/core/entities/index.js +3 -0
  13. package/dist/core/entities/index.js.map +1 -0
  14. package/dist/core/execute.d.ts +64 -0
  15. package/dist/core/execute.d.ts.map +1 -0
  16. package/dist/core/execute.js +45 -0
  17. package/dist/core/execute.js.map +1 -0
  18. package/dist/core/generic.d.ts +35 -0
  19. package/dist/core/generic.d.ts.map +1 -0
  20. package/dist/core/generic.js +28 -0
  21. package/dist/core/generic.js.map +1 -0
  22. package/dist/core/index.d.ts +6 -0
  23. package/dist/core/index.d.ts.map +1 -0
  24. package/dist/core/index.js +6 -0
  25. package/dist/core/index.js.map +1 -0
  26. package/dist/core/primitives.d.ts +3 -0
  27. package/dist/core/primitives.d.ts.map +1 -0
  28. package/dist/core/primitives.js +11 -0
  29. package/dist/core/primitives.js.map +1 -0
  30. package/dist/core/volumes.d.ts +160 -0
  31. package/dist/core/volumes.d.ts.map +1 -0
  32. package/dist/core/volumes.js +100 -0
  33. package/dist/core/volumes.js.map +1 -0
  34. package/dist/engine/deprecated/index.d.ts +2 -0
  35. package/dist/engine/deprecated/index.d.ts.map +1 -0
  36. package/dist/engine/deprecated/index.js +2 -0
  37. package/dist/engine/deprecated/index.js.map +1 -0
  38. package/dist/engine/deprecated/run-request.d.ts +73 -0
  39. package/dist/engine/deprecated/run-request.d.ts.map +1 -0
  40. package/dist/engine/deprecated/run-request.js +47 -0
  41. package/dist/engine/deprecated/run-request.js.map +1 -0
  42. package/dist/engine/dry-run.d.ts +15 -0
  43. package/dist/engine/dry-run.d.ts.map +1 -0
  44. package/dist/engine/dry-run.js +12 -0
  45. package/dist/engine/dry-run.js.map +1 -0
  46. package/dist/engine/health.d.ts +11 -0
  47. package/dist/engine/health.d.ts.map +1 -0
  48. package/dist/engine/health.js +9 -0
  49. package/dist/engine/health.js.map +1 -0
  50. package/dist/engine/images.d.ts +49 -0
  51. package/dist/engine/images.d.ts.map +1 -0
  52. package/dist/engine/images.js +44 -0
  53. package/dist/engine/images.js.map +1 -0
  54. package/dist/engine/index.d.ts +9 -0
  55. package/dist/engine/index.d.ts.map +1 -0
  56. package/dist/engine/index.js +9 -0
  57. package/dist/engine/index.js.map +1 -0
  58. package/dist/engine/paths/containers.d.ts +8 -0
  59. package/dist/engine/paths/containers.d.ts.map +1 -0
  60. package/dist/engine/paths/containers.js +297 -0
  61. package/dist/engine/paths/containers.js.map +1 -0
  62. package/dist/engine/paths/dry-run.d.ts +3 -0
  63. package/dist/engine/paths/dry-run.d.ts.map +1 -0
  64. package/dist/engine/paths/dry-run.js +43 -0
  65. package/dist/engine/paths/dry-run.js.map +1 -0
  66. package/dist/engine/paths/health.d.ts +3 -0
  67. package/dist/engine/paths/health.d.ts.map +1 -0
  68. package/dist/engine/paths/health.js +36 -0
  69. package/dist/engine/paths/health.js.map +1 -0
  70. package/dist/engine/paths/images.d.ts +5 -0
  71. package/dist/engine/paths/images.d.ts.map +1 -0
  72. package/dist/engine/paths/images.js +144 -0
  73. package/dist/engine/paths/images.js.map +1 -0
  74. package/dist/engine/paths/index.d.ts +8 -0
  75. package/dist/engine/paths/index.d.ts.map +1 -0
  76. package/dist/engine/paths/index.js +8 -0
  77. package/dist/engine/paths/index.js.map +1 -0
  78. package/dist/engine/paths/sessions.d.ts +10 -0
  79. package/dist/engine/paths/sessions.d.ts.map +1 -0
  80. package/dist/engine/paths/sessions.js +484 -0
  81. package/dist/engine/paths/sessions.js.map +1 -0
  82. package/dist/engine/paths/stacks.d.ts +6 -0
  83. package/dist/engine/paths/stacks.d.ts.map +1 -0
  84. package/dist/engine/paths/stacks.js +175 -0
  85. package/dist/engine/paths/stacks.js.map +1 -0
  86. package/dist/engine/paths/volumes.d.ts +6 -0
  87. package/dist/engine/paths/volumes.d.ts.map +1 -0
  88. package/dist/engine/paths/volumes.js +164 -0
  89. package/dist/engine/paths/volumes.js.map +1 -0
  90. package/dist/engine/response.d.ts +8 -0
  91. package/dist/engine/response.d.ts.map +1 -0
  92. package/dist/engine/response.js +9 -0
  93. package/dist/engine/response.js.map +1 -0
  94. package/dist/engine/sessions.d.ts +343 -0
  95. package/dist/engine/sessions.d.ts.map +1 -0
  96. package/dist/engine/sessions.js +118 -0
  97. package/dist/engine/sessions.js.map +1 -0
  98. package/dist/engine/volumes.d.ts +52 -0
  99. package/dist/engine/volumes.d.ts.map +1 -0
  100. package/dist/engine/volumes.js +39 -0
  101. package/dist/engine/volumes.js.map +1 -0
  102. package/dist/index.d.ts +2 -8
  103. package/dist/index.d.ts.map +1 -1
  104. package/dist/index.js +2 -8
  105. package/dist/index.js.map +1 -1
  106. package/dist/registry/index.d.ts +3 -0
  107. package/dist/registry/index.d.ts.map +1 -0
  108. package/dist/registry/index.js +3 -0
  109. package/dist/registry/index.js.map +1 -0
  110. package/dist/registry/json-schema.d.ts +82 -0
  111. package/dist/registry/json-schema.d.ts.map +1 -0
  112. package/dist/registry/json-schema.js +149 -0
  113. package/dist/registry/json-schema.js.map +1 -0
  114. package/dist/registry/metadata.d.ts +216 -0
  115. package/dist/registry/metadata.d.ts.map +1 -0
  116. package/dist/registry/metadata.js +143 -0
  117. package/dist/registry/metadata.js.map +1 -0
  118. package/package.json +12 -4
  119. package/scripts/fix-openapi-3.1.ts +183 -0
  120. package/scripts/generate-openapi-data.ts +51 -0
  121. package/scripts/generate-openapi-meta.ts +540 -0
  122. package/src/core/entities/figspec.ts +94 -0
  123. package/src/core/entities/figstack.ts +101 -0
  124. package/src/core/entities/index.ts +2 -0
  125. package/src/core/execute.ts +66 -0
  126. package/src/core/generic.ts +38 -0
  127. package/src/core/index.ts +5 -0
  128. package/src/core/primitives.ts +12 -0
  129. package/src/core/volumes.ts +124 -0
  130. package/src/engine/deprecated/index.ts +1 -0
  131. package/src/engine/deprecated/run-request.ts +53 -0
  132. package/src/engine/dry-run.ts +14 -0
  133. package/src/engine/health.ts +11 -0
  134. package/src/engine/images.ts +58 -0
  135. package/src/engine/index.ts +8 -0
  136. package/src/engine/paths/containers.ts +304 -0
  137. package/src/engine/paths/dry-run.ts +44 -0
  138. package/src/engine/paths/health.ts +37 -0
  139. package/src/engine/paths/images.ts +148 -0
  140. package/src/engine/paths/index.ts +7 -0
  141. package/src/engine/paths/sessions.ts +493 -0
  142. package/src/engine/paths/stacks.ts +179 -0
  143. package/src/engine/paths/volumes.ts +168 -0
  144. package/src/engine/response.ts +11 -0
  145. package/src/engine/sessions.ts +146 -0
  146. package/src/engine/volumes.ts +53 -0
  147. package/src/index.ts +3 -8
  148. package/src/registry/index.ts +2 -0
  149. package/src/registry/json-schema.ts +178 -0
  150. package/src/registry/metadata.ts +181 -0
  151. package/tests/figspec.test.ts +1 -1
  152. package/tests/schemas.test.ts +31 -31
  153. package/dist/figspec.d.ts +0 -4394
  154. package/dist/figspec.d.ts.map +0 -1
  155. package/dist/figspec.js +0 -214
  156. package/dist/figspec.js.map +0 -1
  157. package/dist/figstack.d.ts +0 -419
  158. package/dist/figstack.d.ts.map +0 -1
  159. package/dist/figstack.js +0 -72
  160. package/dist/figstack.js.map +0 -1
  161. package/dist/health.d.ts +0 -16
  162. package/dist/health.d.ts.map +0 -1
  163. package/dist/health.js +0 -7
  164. package/dist/health.js.map +0 -1
  165. package/dist/image.d.ts +0 -115
  166. package/dist/image.d.ts.map +0 -1
  167. package/dist/image.js +0 -42
  168. package/dist/image.js.map +0 -1
  169. package/dist/run-request.d.ts +0 -244
  170. package/dist/run-request.d.ts.map +0 -1
  171. package/dist/run-request.js +0 -43
  172. package/dist/run-request.js.map +0 -1
  173. package/dist/session.d.ts +0 -447
  174. package/dist/session.d.ts.map +0 -1
  175. package/dist/session.js +0 -78
  176. package/dist/session.js.map +0 -1
  177. package/dist/shared.d.ts +0 -90
  178. package/dist/shared.d.ts.map +0 -1
  179. package/dist/shared.js +0 -41
  180. package/dist/shared.js.map +0 -1
  181. package/dist/volume.d.ts +0 -140
  182. package/dist/volume.d.ts.map +0 -1
  183. package/dist/volume.js +0 -37
  184. package/dist/volume.js.map +0 -1
  185. package/src/figspec.ts +0 -279
  186. package/src/figstack.ts +0 -92
  187. package/src/health.ts +0 -8
  188. package/src/image.ts +0 -55
  189. package/src/run-request.ts +0 -55
  190. package/src/session.ts +0 -101
  191. package/src/shared.ts +0 -56
  192. package/src/volume.ts +0 -50
@@ -0,0 +1,101 @@
1
+ import { z } from 'zod';
2
+ import { portMappingSchema } from '../generic.js';
3
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
4
+
5
+ extendZodWithOpenApi(z);
6
+
7
+ // ---------------------------------------------------------------------------
8
+ // FigStackDependency
9
+ // ---------------------------------------------------------------------------
10
+
11
+ export const figStackDependencySchema = z.object({
12
+ name: z.string().min(1).describe("References another entry's name in this stack."),
13
+ condition: z.enum(['started', 'healthy', 'completed']).default('started').optional().describe("started — wait for the container to be running; healthy — wait for the health check to pass; completed — wait for the container to exit successfully."),
14
+ }).openapi("FigStackDependency");
15
+ export type FigStackDependency = z.infer<typeof figStackDependencySchema>;
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // FigStackHealthCheck (discriminated union flattened into one object)
19
+ // ---------------------------------------------------------------------------
20
+
21
+ export const figStackHealthCheckSchema = z.object({
22
+ type: z.enum(['exec', 'http', 'tcp']),
23
+ // exec
24
+ cmd: z.string().optional().describe("Shell command to run inside the container (exec type only)."),
25
+ // http
26
+ path: z.string().optional().describe("HTTP path to check (http type only). Example: /health"),
27
+ port: z.int().optional().describe("Container port to check (http and tcp types)."),
28
+ method: z.enum(['GET', 'HEAD']).optional().describe("HTTP method (http type only)."),
29
+ expectedStatus: z.int().optional().describe("Expected HTTP status code (http type only)."),
30
+ // shared timing
31
+ intervalSeconds: z.int().optional(),
32
+ timeoutSeconds: z.int().optional(),
33
+ retries: z.int().optional(),
34
+ startPeriodSeconds: z.int().optional().describe("Grace period before failures count against the retry limit."),
35
+ }).openapi("FigStackHealthCheck");
36
+ export type FigStackHealthCheck = z.infer<typeof figStackHealthCheckSchema>;
37
+
38
+ // ---------------------------------------------------------------------------
39
+ // FigStackEntry
40
+ // ---------------------------------------------------------------------------
41
+
42
+ // Inline FigSpec reference: either an object or a string reference
43
+ export const figStackSpecSchema = z.union([
44
+ z.object({}).passthrough().openapi({ type: "object" }),
45
+ z.string()
46
+ ]).openapi("FigStackSpec");
47
+
48
+ export const figStackEntrySchema = z.object({
49
+ name: z.string().min(1).describe("Unique container name within the stack; also the hostname on the shared network.").openapi({ example: "postgres" }),
50
+ spec: figStackSpecSchema.describe("Inline FigSpec object or string reference (e.g. \"postgres#default\")."),
51
+ ports: z.array(portMappingSchema).optional().describe("Overrides spec.run.ports for this deployment."),
52
+ dependsOn: z.array(figStackDependencySchema).optional(),
53
+ healthCheck: figStackHealthCheckSchema.optional(),
54
+ env: z.record(z.string(), z.string()).optional().describe("Entry-level environment variable overrides (run-time only)."),
55
+ }).openapi("FigStackEntry");
56
+ export type FigStackEntry = z.infer<typeof figStackEntrySchema>;
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // FigStackNetworkConfig
60
+ // ---------------------------------------------------------------------------
61
+
62
+ export const figStackNetworkConfigSchema = z.object({
63
+ name: z.string().optional().describe("Custom Docker network name. Defaults to {stackName}_network."),
64
+ subnet: z.string().optional().describe("Optional CIDR for predictable container IPs. Example: 172.20.0.0/16"),
65
+ external: z.string().optional().describe("Join an existing external Docker network by name instead of creating one."),
66
+ }).openapi("FigStackNetworkConfig");
67
+ export type FigStackNetworkConfig = z.infer<typeof figStackNetworkConfigSchema>;
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // FigStackVolumeDeclaration
71
+ // ---------------------------------------------------------------------------
72
+
73
+ export const figStackVolumeDeclarationSchema = z.object({
74
+ name: z.string().min(1).describe("Exact Docker volume name — no stack prefix is applied."),
75
+ external: z.boolean().optional().describe("If true, the volume must already exist; the engine will not create it."),
76
+ }).openapi("FigStackVolumeDeclaration");
77
+ export type FigStackVolumeDeclaration = z.infer<typeof figStackVolumeDeclarationSchema>;
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // FigStackEnvGroup
81
+ // ---------------------------------------------------------------------------
82
+
83
+ export const figStackEnvGroupSchema = z.object({
84
+ vars: z.record(z.string(), z.string()),
85
+ inject: z.array(z.string()).optional().describe("Container names to inject into. Omit to inject into all containers."),
86
+ }).openapi("FigStackEnvGroup");
87
+ export type FigStackEnvGroup = z.infer<typeof figStackEnvGroupSchema>;
88
+
89
+ // ---------------------------------------------------------------------------
90
+ // FigStack (top-level)
91
+ // ---------------------------------------------------------------------------
92
+
93
+ export const figStackSchema = z.object({
94
+ schemaVersion: z.string().openapi({ example: "0.1" }),
95
+ stackName: z.string().min(1).openapi({ example: "blog" }),
96
+ containers: z.array(figStackEntrySchema).min(1),
97
+ env: z.array(figStackEnvGroupSchema).optional(),
98
+ network: figStackNetworkConfigSchema.optional(),
99
+ volumes: z.array(figStackVolumeDeclarationSchema).optional(),
100
+ }).openapi("FigStack");
101
+ export type FigStack = z.infer<typeof figStackSchema>;
@@ -0,0 +1,2 @@
1
+ export * from './figspec.js';
2
+ export * from './figstack.js';
@@ -0,0 +1,66 @@
1
+ import z from "zod";
2
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
3
+
4
+ extendZodWithOpenApi(z);
5
+
6
+ export const mainCmdOnExitWithCodeSchema = z.object({
7
+ cmd: z.union([z.string(), z.null()]).optional().describe("Action: a command string, \"persist\", \"shutdown\", or null"),
8
+ exitCode: z.int(),
9
+ }).openapi("MainCmdOnExitWithCode");
10
+ export type MainCmdOnExitWithCode = z.infer<typeof mainCmdOnExitWithCodeSchema>;
11
+
12
+ export const mainCmdOnExitListSchema = z.array(
13
+ mainCmdOnExitWithCodeSchema
14
+ );
15
+
16
+ // onExit accepts: null | "persist" | "shutdown" | command-string | dispatch-table
17
+ export const onExitBehaviorSchema = z.union([
18
+ z.string(),
19
+ z.null(),
20
+ z.array(mainCmdOnExitWithCodeSchema),
21
+ ]).openapi("OnExitBehavior");
22
+ export type OnExitBehavior = z.infer<typeof onExitBehaviorSchema>;
23
+
24
+ export const executeStartupCmdSchema = z.object({
25
+ cmd: z.string().describe("Shell command to run before the main process"),
26
+ async: z.boolean().optional().describe("Fire-and-forget if true; blocks main start if false"),
27
+ }).openapi("ExecuteStartupCmd");
28
+ export type ExecuteStartupCmd = z.infer<typeof executeStartupCmdSchema>;
29
+
30
+ export const executeMainCmdSchema = z.object({
31
+ cmd: z.string().describe("Primary command the container runs"),
32
+ onExit: onExitBehaviorSchema.optional(),
33
+ parser: z.string().optional().describe("JavaScript parser source or URI for structured output"),
34
+ }).openapi("ExecuteMainCmd");
35
+ export type ExecuteMainCmd = z.infer<typeof executeMainCmdSchema>;
36
+
37
+ export const executeShutdownCmdSchema = z.object({
38
+ cmd: z.string().describe("Shell command to run before the container stops"),
39
+ }).openapi("ExecuteShutdownCmd");
40
+ export type ExecuteShutdownCmd = z.infer<typeof executeShutdownCmdSchema>;
41
+
42
+ export const scheduleExpressionStructSchema = z.object({
43
+ interval: z.int().optional().describe("Seconds after startup, or repeat interval"),
44
+ time: z.string().optional().describe("Daily run time in \"HH:MM\" format"),
45
+ cron: z.string().optional().describe("Cron expression (e.g. \"0 9 * * *\")"),
46
+ }).openapi("ScheduleExpressionStruct");
47
+ export type ScheduleExpressionStruct = z.infer<typeof scheduleExpressionStructSchema>;
48
+
49
+ export const scheduleExpressionSchema = z.union([
50
+ z.string(),
51
+ scheduleExpressionStructSchema,
52
+ ]).openapi("ScheduleExpression");
53
+ export type ScheduleExpression = z.infer<typeof scheduleExpressionSchema>;
54
+
55
+ export const scheduledCmdSchema = z.object({
56
+ cmd: z.string(),
57
+ runAt: scheduleExpressionSchema,
58
+ onExit: onExitBehaviorSchema.optional(),
59
+ parser: z.string().optional(),
60
+ }).openapi("ScheduledCmd");
61
+ export type ScheduledCmd = z.infer<typeof scheduledCmdSchema>;
62
+
63
+ export const buildExecuteStepSchema = z.object({
64
+ cmd: z.string().min(1).describe("Shell command to run as a separate RUN instruction (cacheable layer)").openapi({ example: "apt-get install -y curl" }),
65
+ }).openapi("BuildExecuteStep");
66
+ export type BuildExecuteStep = z.infer<typeof buildExecuteStepSchema>;
@@ -0,0 +1,38 @@
1
+ import z from "zod";
2
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
3
+
4
+ extendZodWithOpenApi(z);
5
+
6
+ export const resourceLimitsSchema = z.object({
7
+ memory_limit: z.string().optional().describe("e.g. \"2G\", \"512M\"").openapi({ example: "2G" }),
8
+ memory_reservation: z.string().optional().describe("Soft memory limit, e.g. \"1G\"").openapi({ example: "1G" }),
9
+ cpu_limit: z.string().optional().describe("e.g. \"2\", \"0.5\"").openapi({ example: "2" }),
10
+ cpu_reservation: z.string().optional().describe("Soft CPU limit, e.g. \"1\"").openapi({ example: "1" }),
11
+ }).openapi("ResourceLimits");
12
+ export type ResourceLimits = z.infer<typeof resourceLimitsSchema>;
13
+
14
+ export const portMappingSchema = z.object({
15
+ container: z.int().min(1).max(65535).describe("Container port").openapi({ example: 3000 }),
16
+ host: z.int().min(0).max(65535).optional().describe("Host port. 0 or omitted means same as container port.").openapi({ example: 8080 }),
17
+ }).openapi("PortMapping");
18
+ export type PortMapping = z.infer<typeof portMappingSchema>;
19
+
20
+ export const parsedOutputSchema = z.object({
21
+ success: z.boolean(),
22
+ data: z.record(z.string(), z.unknown()).optional().describe("Arbitrary structured data produced by the parser script"),
23
+ }).openapi("ParsedOutput");
24
+ export type ParsedOutput = z.infer<typeof parsedOutputSchema>;
25
+
26
+ export const packageSpecSchema = z.object({
27
+ name: z.string(),
28
+ version: z.string().optional(),
29
+ args: z.array(z.string()).optional(),
30
+ }).openapi("PackageSpec");
31
+ export type PackageSpec = z.infer<typeof packageSpecSchema>;
32
+
33
+ export const installGroupSchema = z.object({
34
+ packageManager: z.string().min(1).describe("Package manager to use (e.g. \"apt\", \"npm\", \"pip\")"),
35
+ packages: z.array(z.union([z.string(), packageSpecSchema])),
36
+ globalArgs: z.array(z.string()).optional().describe("Arguments passed to every package install invocation"),
37
+ }).openapi("InstallGroup");
38
+ export type InstallGroup = z.infer<typeof installGroupSchema>;
@@ -0,0 +1,5 @@
1
+ export * from './primitives.js';
2
+ export * from './generic.js';
3
+ export * from './execute.js';
4
+ export * from './volumes.js';
5
+ export * from './entities/index.js';
@@ -0,0 +1,12 @@
1
+ import z from "zod";
2
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
3
+
4
+ extendZodWithOpenApi(z);
5
+
6
+ export const bigIntSchema = z
7
+ .bigint()
8
+ .describe("An integer between 3n and 5n")
9
+ .openapi("BigInt", {
10
+ format: "int64",
11
+ type: "integer",
12
+ });
@@ -0,0 +1,124 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Build-time volume mounts
3
+ // ---------------------------------------------------------------------------
4
+
5
+ import z from "zod";
6
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
7
+
8
+ extendZodWithOpenApi(z);
9
+
10
+ export const fileContentSchema = z.object({
11
+ data: z.string().describe("File payload"),
12
+ encoding: z.string().optional().describe("Encoding of the data field (default \"utf-8\")"),
13
+ }).openapi("FileContent");
14
+ export type FileContent = z.infer<typeof fileContentSchema>;
15
+
16
+ export const filePermissionsSchema = z.object({
17
+ read: z.int(),
18
+ write: z.int(),
19
+ execute: z.int(),
20
+ }).openapi("FilePermissions");
21
+ export type FilePermissions = z.infer<typeof filePermissionsSchema>;
22
+
23
+ export const buildVolumeMountRegularSchema = z.object({
24
+ type: z.literal('regular'),
25
+ host_path: z.string().optional().describe("Absolute path on the host"),
26
+ container_path: z.string().optional().describe("Absolute path inside the container"),
27
+ read_only: z.boolean().optional(),
28
+ }).openapi("BuildVolumeMountRegular");
29
+
30
+ export const buildVolumeMountNewFileSchema = z.object({
31
+ type: z.literal('new-file'),
32
+ container_path: z.string().describe("Destination path inside the image (COPY semantics)"),
33
+ content: fileContentSchema,
34
+ owner: z.string().optional().describe("Unix owner string, e.g. \"root:root\""),
35
+ permissions: filePermissionsSchema.optional(),
36
+ }).openapi("BuildVolumeMountNewFile");
37
+
38
+ export const buildVolumeMountSchema = z.discriminatedUnion('type', [
39
+ buildVolumeMountRegularSchema,
40
+ buildVolumeMountNewFileSchema,
41
+ ]).openapi("BuildVolumeMount");
42
+ export type BuildVolumeMount = z.infer<typeof buildVolumeMountSchema>;
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Run-time volume mounts
46
+ // ---------------------------------------------------------------------------
47
+
48
+ export const runVolumeMountRegularSchema = z.object({
49
+ type: z.literal('regular'),
50
+ host_path: z.string().optional(),
51
+ container_path: z.string().optional(),
52
+ read_only: z.boolean().optional(),
53
+ }).openapi("RunVolumeMountRegular");
54
+
55
+ export const runVolumeMountDockerSocketSchema = z.object({
56
+ type: z.literal('docker-socket'),
57
+ container_path: z.string().optional().describe("Mount point inside the container (defaults to /var/run/docker.sock)"),
58
+ }).openapi("RunVolumeMountDockerSocket");
59
+
60
+ export const runVolumeMountNewFileSchema = z.object({
61
+ type: z.literal('new-file'),
62
+ container_path: z.string(),
63
+ content: fileContentSchema,
64
+ owner: z.string().optional(),
65
+ permissions: filePermissionsSchema.optional(),
66
+ }).openapi("RunVolumeMountNewFile");
67
+
68
+ export const runVolumeMountNamedSchema = z.object({
69
+ type: z.literal('named'),
70
+ name: z.string().describe("Docker named volume name"),
71
+ container_path: z.string(),
72
+ read_only: z.boolean().optional(),
73
+ }).openapi("RunVolumeMountNamed");
74
+
75
+ export const runVolumeMountAnonymousSchema = z.object({
76
+ type: z.literal('anonymous'),
77
+ container_path: z.string(),
78
+ }).openapi("RunVolumeMountAnonymous");
79
+
80
+ export const runVolumeMountTmpfsSchema = z.object({
81
+ type: z.literal('tmpfs'),
82
+ container_path: z.string(),
83
+ size_mb: z.int().optional().describe("Size limit in MiB; absent means no limit"),
84
+ }).openapi("RunVolumeMountTmpfs");
85
+
86
+ export const runVolumeMountCopyInSchema = z.object({
87
+ type: z.literal('copy-in'),
88
+ host_path: z.string(),
89
+ container_path: z.string(),
90
+ }).openapi("RunVolumeMountCopyIn");
91
+
92
+ export const runVolumeMountCopyOutSchema = z.object({
93
+ type: z.literal('copy-out'),
94
+ container_path: z.string(),
95
+ host_path: z.string(),
96
+ changed_only: z.boolean().optional().describe("Copy only files changed since container start (uses docker diff)"),
97
+ }).openapi("RunVolumeMountCopyOut");
98
+
99
+ export const runVolumeMountSchema = z.discriminatedUnion('type', [
100
+ runVolumeMountRegularSchema,
101
+ runVolumeMountDockerSocketSchema,
102
+ runVolumeMountNewFileSchema,
103
+ runVolumeMountNamedSchema,
104
+ runVolumeMountAnonymousSchema,
105
+ runVolumeMountTmpfsSchema,
106
+ runVolumeMountCopyInSchema,
107
+ runVolumeMountCopyOutSchema,
108
+ ]).openapi("RunVolumeMount");
109
+ export type RunVolumeMount = z.infer<typeof runVolumeMountSchema>;
110
+
111
+ export const volumeMountSchema = z.object({
112
+ type: z.string().optional().describe("Optional mount type. Use \"docker-socket\" for Docker socket mounts.").openapi({ example: "docker-socket" }),
113
+ host_path: z.string().describe("Absolute path on the host").openapi({ example: "/var/run/docker.sock" }),
114
+ container_path: z.string().optional().describe("Absolute path inside the container (may be omitted for docker-socket mounts)").openapi({ example: "/var/run/docker.sock" }),
115
+ read_only: z.boolean().describe("Whether the mount is read-only"),
116
+ }).openapi("VolumeMount");
117
+ export type VolumeMount = z.infer<typeof volumeMountSchema>;
118
+
119
+ export const namedVolumeMountSchema = z.object({
120
+ volume_name: z.string().min(1).describe("Docker named volume name").openapi({ example: "figulus-cache-nodejs" }),
121
+ container_path: z.string().min(1).describe("Mount point inside the container").openapi({ example: "/root/.npm" }),
122
+ read_only: z.boolean(),
123
+ }).openapi("NamedVolumeMount");
124
+ export type NamedVolumeMount = z.infer<typeof namedVolumeMountSchema>;
@@ -0,0 +1 @@
1
+ export * from './run-request.js';
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
3
+ import { namedVolumeMountSchema, volumeMountSchema } from '../../core/volumes.js';
4
+ import { parsedOutputSchema, portMappingSchema, resourceLimitsSchema } from '../../core/generic.js';
5
+
6
+ extendZodWithOpenApi(z);
7
+
8
+ export const jobStatusSchema = z.enum([
9
+ 'pending',
10
+ 'running',
11
+ 'completed',
12
+ 'failed',
13
+ 'timeout',
14
+ 'awaiting_cleanup',
15
+ ]).openapi("JobStatus");
16
+ export type JobStatus = z.infer<typeof jobStatusSchema>;
17
+
18
+ export const runRequestSchema = z.object({
19
+ job_id: z.string().min(1).describe("Unique identifier for this job").openapi({ example: "job-abc123" }),
20
+ image: z.string().min(1).describe("Docker image to run").openapi({ example: "node:20-alpine" }),
21
+ command: z.array(z.string()).optional().describe("Command and arguments to execute").openapi({ example: ["node","index.js"] }),
22
+ environment: z.record(z.string(), z.string()).optional().describe("Environment variables"),
23
+ volumes: z.array(volumeMountSchema).optional().describe("Bind mounts (host path → container path)"),
24
+ named_volumes: z.array(namedVolumeMountSchema).optional().describe("Docker named volume mounts"),
25
+ resources: resourceLimitsSchema.optional(),
26
+ timeout: z.int().positive().max(86400).optional().describe("Execution timeout in seconds (default 300)").openapi({ example: 300 }),
27
+ parser_script: z.string().optional().describe("Full JavaScript parser source code for structured output parsing"),
28
+ root_dir: z.string().optional().describe("Root directory for file:// parser resolution"),
29
+ env_files: z.array(z.string()).optional().describe("Paths to .env files to load"),
30
+ secrets: z.record(z.string(), z.string()).optional().describe("Secret references: env_name → \"vault:path#field\""),
31
+ extra_hosts: z.array(z.string()).optional().describe("Extra /etc/hosts entries, e.g. \"host.docker.internal:host-gateway\""),
32
+ network_mode: z.string().optional().describe("Docker network mode: \"host\", \"bridge\", \"none\", etc.").openapi({ example: "bridge" }),
33
+ working_dir: z.string().optional().describe("Working directory inside the container (overrides Dockerfile WORKDIR)").openapi({ example: "/app" }),
34
+ ports: z.array(portMappingSchema).optional().describe("Port mappings (container port → host port)"),
35
+ }).openapi("RunRequest");
36
+ export type RunRequest = z.infer<typeof runRequestSchema>;
37
+
38
+ export const runResponseSchema = z.object({
39
+ job_id: z.string().openapi({ example: "job-abc123" }),
40
+ status: jobStatusSchema,
41
+ message: z.string().optional().openapi({ example: "Job accepted and queued" }),
42
+ container_id: z.string().optional().describe("Docker container ID, present once container is created"),
43
+ exit_code: z.int().nullable().optional().describe("Container exit code, present on completion"),
44
+ stdout: z.string().optional(),
45
+ stderr: z.string().optional(),
46
+ parsed_output: parsedOutputSchema.optional(),
47
+ started_at: z.iso.datetime().optional().openapi({ format: "date-time" }),
48
+ completed_at: z.iso.datetime().optional().openapi({ format: "date-time" }),
49
+ duration: z.number().optional().describe("Execution duration in seconds").openapi({ format: "double" }),
50
+ command: z.array(z.string()).optional().describe("Original command from the request"),
51
+ final_command: z.array(z.string()).optional().describe("Actual command executed after any wrapping or modification"),
52
+ }).openapi("RunResponse");
53
+ export type RunResponse = z.infer<typeof runResponseSchema>;
@@ -0,0 +1,14 @@
1
+ import z from "zod";
2
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
3
+ import { volumeMountSchema } from "../core/volumes.js";
4
+
5
+ extendZodWithOpenApi(z);
6
+
7
+ export const dryRunResponseSchema = z.object({
8
+ command: z.array(z.string()).describe("Original command from the request"),
9
+ final_command: z.array(z.string()).describe("Actual command that would be executed"),
10
+ image: z.string(),
11
+ working_dir: z.string(),
12
+ volumes: z.array(volumeMountSchema).optional().describe("Volume mounts with computed container paths"),
13
+ }).openapi("DryRunResponse");
14
+ export type DryRunResponse = z.infer<typeof dryRunResponseSchema>;
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod';
2
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
3
+
4
+ extendZodWithOpenApi(z);
5
+
6
+ export const healthResponseSchema = z.object({
7
+ status: z.enum(['healthy', 'unhealthy']),
8
+ docker_api: z.string().optional().describe("Docker daemon info string, present when healthy").openapi({ example: "Docker v29.3.0, 12 containers" }),
9
+ message: z.string().optional().describe("Error detail, present when unhealthy").openapi({ example: "Docker daemon unreachable: connection refused" }),
10
+ }).openapi("HealthResponse");
11
+ export type HealthResponse = z.infer<typeof healthResponseSchema>;
@@ -0,0 +1,58 @@
1
+ import { z } from 'zod';
2
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
3
+
4
+ extendZodWithOpenApi(z);
5
+
6
+ export const buildImageRequestSchema = z.object({
7
+ spec: z.record(z.string(), z.unknown()).describe("Figspec build specification describing the image to build"),
8
+ platform: z.string().optional().describe("Target platform, e.g. \"linux/amd64\", \"linux/arm64\"").openapi({ example: "linux/amd64" }),
9
+ parser_script: z.string().optional().describe("JavaScript parser source for build output (resolved by server)"),
10
+ root_dir: z.string().optional().describe("Root directory for file:// parser resolution"),
11
+ }).openapi("BuildImageRequest");
12
+ export type BuildImageRequest = z.infer<typeof buildImageRequestSchema>;
13
+
14
+ export const buildImageResponseSchema = z.object({
15
+ image_tag: z.string().min(1).describe("Fully qualified image tag").openapi({ example: "localhost:5000/figulus:sha256-abc123de" }),
16
+ hash: z.string().min(1).describe("Content-addressable hash of the spec").openapi({ example: "abc123defgh456" }),
17
+ cached: z.boolean().describe("true if the image was already in cache (no build needed)"),
18
+ message: z.string().optional().openapi({ example: "Image built successfully" }),
19
+ }).openapi("BuildImageResponse");
20
+ export type BuildImageResponse = z.infer<typeof buildImageResponseSchema>;
21
+
22
+ export const pruneImagesRequestSchema = z.object({
23
+ retention_days: z.int().min(1).optional().describe("Images unused for longer than this many days are removed (overrides query param)"),
24
+ all: z.boolean().optional().describe("When true, also prune all unused Docker images (not just Figulus-managed)"),
25
+ }).openapi("PruneImagesRequest");
26
+ export type PruneImagesRequest = z.infer<typeof pruneImagesRequestSchema>;
27
+
28
+ export const pruneImagesResponseSchema = z.object({
29
+ removed: z.int().describe("Number of Figulus-managed images removed"),
30
+ bytes_freed: z.int().describe("Bytes freed by Figulus GC").openapi({ format: "int64" }),
31
+ mb_freed: z.number().describe("Megabytes freed by Figulus GC").openapi({ format: "double" }),
32
+ message: z.string(),
33
+ docker_images_pruned: z.int().optional().describe("Number of Docker-native images pruned (only present when all=true)"),
34
+ docker_bytes_freed: z.int().optional().describe("Bytes freed by Docker-native image prune (only present when all=true)").openapi({ format: "int64" }),
35
+ }).openapi("PruneImagesResponse");
36
+ export type PruneImagesResponse = z.infer<typeof pruneImagesResponseSchema>;
37
+
38
+ export const pruneCacheResponseSchema = z.object({
39
+ freed_bytes: z.int(),
40
+ });
41
+ export type PruneCacheResponse = z.infer<typeof pruneCacheResponseSchema>;
42
+
43
+ export const extractArtifactsRequestSchema = z.object({
44
+ container_id: z.string().min(1).describe("Docker container ID to extract from").openapi({ example: "a1b2c3d4e5f6" }),
45
+ container_path: z.string().min(1).describe("Absolute path inside the container to extract").openapi({ example: "/app/dist" }),
46
+ host_path: z.string().min(1).describe("Absolute path on the host to extract files into").openapi({ example: "/tmp/artifacts" }),
47
+ }).openapi("ExtractArtifactsRequest");
48
+ export type ExtractArtifactsRequest = z.infer<typeof extractArtifactsRequestSchema>;
49
+
50
+ export const extractArtifactsResponseSchema = z.object({
51
+ container_id: z.string(),
52
+ container_path: z.string(),
53
+ host_path: z.string(),
54
+ files_count: z.int().optional().describe("Number of files extracted"),
55
+ total_bytes: z.int().optional().describe("Total bytes extracted").openapi({ format: "int64" }),
56
+ message: z.string(),
57
+ }).openapi("ExtractArtifactsResponse");
58
+ export type ExtractArtifactsResponse = z.infer<typeof extractArtifactsResponseSchema>;
@@ -0,0 +1,8 @@
1
+ export * from './response.js';
2
+ export * from './dry-run.js';
3
+ export * from './health.js';
4
+ export * from './images.js';
5
+ export * from './sessions.js';
6
+ export * from './volumes.js';
7
+ export * from './paths/index.js';
8
+ export * from './deprecated/index.js';