@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,168 @@
1
+ import { RouteConfig } from "@asteasolutions/zod-to-openapi";
2
+ import { deleteVolumeResponseSchema, ensureVolumeRequestSchema, ensureVolumeResponseSchema, listVolumesResponseSchema, pruneVolumesRequestSchema, pruneVolumesResponseSchema } from "../volumes.js";
3
+ import { errorResponseSchema } from "../response.js";
4
+
5
+ export const ensureVolumeRouteSchema: RouteConfig = {
6
+ method: "post",
7
+ path: "/api/v1/volumes/ensure",
8
+ operationId: "ensureVolume",
9
+ summary: "Ensure a Docker volume exists",
10
+ description: "Creates the volume if it does not exist; no-ops if it does.",
11
+ tags: [
12
+ "volumes"
13
+ ],
14
+ request: {
15
+ body: {
16
+ description: undefined,
17
+ content: {
18
+ "application/json": {
19
+ schema: ensureVolumeRequestSchema
20
+ }
21
+ },
22
+ required: true
23
+ }
24
+ },
25
+ responses: {
26
+ 200: {
27
+ description: "Volume exists (created or pre-existing)",
28
+ content: {
29
+ "application/json": {
30
+ schema: ensureVolumeResponseSchema
31
+ }
32
+ }
33
+ },
34
+ 400: {
35
+ description: "Missing volume_name",
36
+ content: {
37
+ "application/json": {
38
+ schema: errorResponseSchema
39
+ }
40
+ }
41
+ },
42
+ 500: {
43
+ description: "Failed to ensure volume",
44
+ content: {
45
+ "application/json": {
46
+ schema: errorResponseSchema
47
+ }
48
+ }
49
+ }
50
+ }
51
+ };
52
+
53
+ export const listVolumesRouteSchema: RouteConfig = {
54
+ method: "get",
55
+ path: "/api/v1/volumes",
56
+ operationId: "listVolumes",
57
+ summary: "List Figulus-managed volumes",
58
+ description: "Returns all Docker volumes managed by this Figulus engine instance.",
59
+ tags: [
60
+ "volumes"
61
+ ],
62
+ responses: {
63
+ 200: {
64
+ description: "Volume list",
65
+ content: {
66
+ "application/json": {
67
+ schema: listVolumesResponseSchema
68
+ }
69
+ }
70
+ },
71
+ 500: {
72
+ description: "Failed to list volumes",
73
+ content: {
74
+ "application/json": {
75
+ schema: errorResponseSchema
76
+ }
77
+ }
78
+ }
79
+ }
80
+ };
81
+
82
+ export const deleteVolumeRouteSchema: RouteConfig = {
83
+ method: "delete",
84
+ path: "/api/v1/volumes/{name}",
85
+ operationId: "deleteVolume",
86
+ summary: "Delete a volume",
87
+ description: "Deletes the named Docker volume. Returns 500 if the volume is in use.",
88
+ tags: [
89
+ "volumes"
90
+ ],
91
+ parameters: [
92
+ {
93
+ name: "name",
94
+ in: "path",
95
+ required: true,
96
+ schema: {
97
+ type: "string"
98
+ },
99
+ example: "figulus-cache-nodejs"
100
+ }
101
+ ],
102
+ responses: {
103
+ 200: {
104
+ description: "Volume deleted",
105
+ content: {
106
+ "application/json": {
107
+ schema: deleteVolumeResponseSchema
108
+ }
109
+ }
110
+ },
111
+ 400: {
112
+ description: "Missing volume name",
113
+ content: {
114
+ "application/json": {
115
+ schema: errorResponseSchema
116
+ }
117
+ }
118
+ },
119
+ 500: {
120
+ description: "Delete failed",
121
+ content: {
122
+ "application/json": {
123
+ schema: errorResponseSchema
124
+ }
125
+ }
126
+ }
127
+ }
128
+ };
129
+
130
+ export const pruneVolumesRouteSchema: RouteConfig = {
131
+ method: "post",
132
+ path: "/api/v1/volumes/prune",
133
+ operationId: "pruneVolumes",
134
+ summary: "Prune unused volumes",
135
+ description: "Removes unused Docker volumes matching the given filters. Body is optional.",
136
+ tags: [
137
+ "volumes"
138
+ ],
139
+ request: {
140
+ body: {
141
+ description: undefined,
142
+ content: {
143
+ "application/json": {
144
+ schema: pruneVolumesRequestSchema
145
+ }
146
+ },
147
+ required: false
148
+ }
149
+ },
150
+ responses: {
151
+ 200: {
152
+ description: "Volumes pruned",
153
+ content: {
154
+ "application/json": {
155
+ schema: pruneVolumesResponseSchema
156
+ }
157
+ }
158
+ },
159
+ 500: {
160
+ description: "Prune failed",
161
+ content: {
162
+ "application/json": {
163
+ schema: errorResponseSchema
164
+ }
165
+ }
166
+ }
167
+ }
168
+ };
@@ -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 errorResponseSchema = z.object({
7
+ error: z.string().describe("Machine-readable error code").openapi({ example: "job_not_found" }),
8
+ message: z.string().describe("Human-readable error detail").openapi({ example: "Job abc123 not found" }),
9
+ code: z.int().describe("HTTP status code mirrored in body").openapi({ example: 404 }),
10
+ }).openapi("ErrorResponse");
11
+ export type ErrorResponse = z.infer<typeof errorResponseSchema>;
@@ -0,0 +1,146 @@
1
+ import { z } from 'zod';
2
+ import {
3
+ namedVolumeMountSchema,
4
+ volumeMountSchema
5
+ } from '../core/volumes.js';
6
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
7
+ import { figStackSchema } from '../core/entities/figstack.js';
8
+ import { figSpecExecuteSchema } from '../core/entities/figspec.js';
9
+ import { portMappingSchema } from '../core/generic.js';
10
+ extendZodWithOpenApi(z);
11
+
12
+ export const sessionStatusSchema = z.enum([
13
+ 'active', 'idle', 'expired'
14
+ ]).openapi("SessionStatus");
15
+ export type SessionStatus = z.infer<typeof sessionStatusSchema>;
16
+
17
+ export const sessionConfigSchema = z.object({
18
+ working_dir: z.string().optional().describe("Default working directory for commands in this session").openapi({ example: "/app" }),
19
+ environment: z.record(z.string(), z.string()).optional().describe("Environment variables for the session container"),
20
+ parser_script: z.string().optional().describe("Default JavaScript parser script for command output"),
21
+ volumes: z.array(volumeMountSchema).optional(),
22
+ named_volumes: z.array(namedVolumeMountSchema).optional(),
23
+ init_cmd: z.array(z.string()).optional().describe("Command to run once after container creation"),
24
+ destroy_cmd: z.array(z.string()).optional().describe("Command to run before container destruction"),
25
+ execute: figSpecExecuteSchema.optional().describe("Figspec execute configuration (alternative to start_cmd for Figspec-driven sessions)"),
26
+ ports: z.array(portMappingSchema).optional(),
27
+ start_cmd: z.array(z.string()).optional().describe("Main container process command (default [\"sleep\", \"infinity\"])"),
28
+ auto_cleanup: z.boolean().optional().describe("If true, the session is destroyed automatically after the main command (start_cmd or execute.main) completes, regardless of exit code. This is the flag that distinguishes a one-shot execution from a persistent session.").openapi({ example: false }),
29
+ block_until_complete: z.boolean().optional().describe("If true (on CreateSessionRequest), the HTTP handler blocks until the session reaches a terminal state and returns the full result (exit_code, stdout, stderr) in the response. Intended for use with auto_cleanup: true to implement synchronous one-shot execution.").openapi({ example: false })
30
+ }).openapi("SessionConfig");
31
+ export type SessionConfig = z.infer<typeof sessionConfigSchema>;
32
+
33
+ export const createSessionRequestSchema = z.object({
34
+ image_tag: z.string().min(1).describe("Docker image to use for the session container").openapi({ example: "localhost:5000/figulus:sha256-abc123" }),
35
+ ttl: z.int().positive().optional().describe("Session TTL in seconds (default 600)").openapi({ example: 600 }),
36
+ config: sessionConfigSchema.optional(),
37
+ execute: figSpecExecuteSchema.optional().describe("Figspec execute configuration (preferred over config.start_cmd for Figspec-driven sessions)"),
38
+ block_until_complete: z.boolean().optional().describe("If true, the HTTP handler blocks until the session reaches a terminal state and returns the full result (exit_code, stdout, stderr) synchronously. Use with config.auto_cleanup: true to implement one-shot execution semantics (replaces the removed /containers/run endpoint).").openapi({ example: false }),
39
+ }).openapi("CreateSessionRequest");
40
+ export type CreateSessionRequest = z.infer<typeof createSessionRequestSchema>;
41
+
42
+ export const createSessionResponseSchema = z.object({
43
+ session_id: z.string().openapi({ example: "sess-abc123" }),
44
+ container_id: z.string().openapi({ example: "a1b2c3d4e5f6" }),
45
+ status: sessionStatusSchema,
46
+ created_at: z.iso.datetime().openapi({ format: "date-time" }),
47
+ ports: z.array(portMappingSchema).optional(),
48
+ exit_code: z.int().optional().describe("Container exit code. Populated when block_until_complete: true and the session ran to completion (i.e., auto_cleanup: true and the main command finished).").openapi({ example: 0 }),
49
+ stdout: z.string().optional().describe("Main command stdout. Populated when block_until_complete is true and session ran to completion.").openapi({ example: "Hello, world!\n" }),
50
+ stderr: z.string().optional().describe("Main command stderr. Populated when block_until_complete is true and session ran to completion.").openapi({ example: "" }),
51
+ }).openapi("CreateSessionResponse");
52
+ export type CreateSessionResponse = z.infer<typeof createSessionResponseSchema>;
53
+
54
+ export const getSessionResponseSchema = z.object({
55
+ session_id: z.string(),
56
+ container_id: z.string(),
57
+ image_tag: z.string(),
58
+ status: sessionStatusSchema,
59
+ created_at: z.iso.datetime().openapi({ format: "date-time" }),
60
+ last_used_at: z.iso.datetime().openapi({ format: "date-time" }),
61
+ ttl: z.int().describe("TTL in seconds"),
62
+ ports: z.array(portMappingSchema).optional(),
63
+ }).openapi("GetSessionResponse");
64
+ export type GetSessionResponse = z.infer<typeof getSessionResponseSchema>;
65
+
66
+ export const runInSessionRequestSchema = z.object({
67
+ command: z.array(z.string()).min(1).describe("Command to execute inside the session container").openapi({ example: ["npm","test"] }),
68
+ timeout: z.int().positive().max(86400).optional().describe("Timeout in seconds (default 300)").openapi({ example: 300 }),
69
+ parser_script: z.string().optional().describe("Optional JavaScript parser for structured output"),
70
+ working_dir: z.string().optional().describe("Override the session working directory for this run").openapi({ example: "/app/packages/core" }),
71
+ }).openapi("RunInSessionRequest");
72
+ export type RunInSessionRequest = z.infer<typeof runInSessionRequestSchema>;
73
+
74
+ export const runInSessionResponseSchema = z.object({
75
+ run_id: z.string().openapi({ example: "run-abc123" }),
76
+ exit_code: z.int(),
77
+ stdout: z.string(),
78
+ stderr: z.string(),
79
+ parsed_output: z.record(z.string(), z.unknown()).optional().describe("Structured output from the parser script, if provided"),
80
+ duration: z.number().describe("Duration in seconds").openapi({ format: "double" }),
81
+ }).openapi("RunInSessionResponse");
82
+ export type RunInSessionResponse = z.infer<typeof runInSessionResponseSchema>;
83
+
84
+ export const getRunResponseSchema = z.object({
85
+ run_id: z.string(),
86
+ session_id: z.string(),
87
+ command: z.array(z.string()),
88
+ exit_code: z.int(),
89
+ stdout: z.string(),
90
+ stderr: z.string(),
91
+ parsed_output: z.record(z.string(), z.unknown()).optional(),
92
+ started_at: z.iso.datetime().openapi({ format: "date-time" }),
93
+ completed_at: z.iso.datetime().openapi({ format: "date-time" }),
94
+ duration: z.number().openapi({ format: "double" }),
95
+ }).openapi("GetRunResponse");
96
+ export type GetRunResponse = z.infer<typeof getRunResponseSchema>;
97
+
98
+ export const listRunsResponseSchema = z.object({
99
+ runs: z.array(getRunResponseSchema),
100
+ count: z.int()
101
+ }).openapi("ListRunsResponse");
102
+
103
+ export const syncFileRequestSchema = z.object({
104
+ targetPath: z.string().min(1).describe("Absolute path inside the container where the file will be written").openapi({ example: "/app/src/index.ts" }),
105
+ content: z.string().min(1).describe("File content, encoded as specified by the encoding field"),
106
+ encoding: z.enum(['base64', 'utf8']).default('base64').describe("Encoding of the content field (default base64)"),
107
+ }).openapi("SyncFileRequest");
108
+ export type SyncFileRequest = z.infer<typeof syncFileRequestSchema>;
109
+
110
+ export const syncFileResponseSchema = z.object({
111
+ success: z.boolean(),
112
+ path: z.string().describe("Path written to (echoes targetPath)"),
113
+ bytes_written: z.int().openapi({ format: "int64" }),
114
+ message: z.string().optional(),
115
+ }).openapi("SyncFileResponse");
116
+ export type SyncFileResponse = z.infer<typeof syncFileResponseSchema>;
117
+
118
+ export const startStackRequestSchema = z.object({
119
+ spec: figStackSchema,
120
+ build: z.boolean().optional().default(false).describe("If true, build all required images before starting."),
121
+ images: z.object({}).optional().describe("Pre-resolved image tags keyed by container entry name."),
122
+ }).openapi("StartStackRequest");
123
+
124
+ export const startStackResponseSchema = z.object({
125
+ stack_name: z.string(),
126
+ sessions: z.object({}).describe("Map of container name → session ID."),
127
+ network_id: z.string()
128
+ }).openapi("StartStackResponse");
129
+
130
+ export const stackContainerStatusSchema = z.object({
131
+ container_name: z.string(),
132
+ session_id: z.string(),
133
+ status: z.string()
134
+ }).openapi("StackContainerStatus");
135
+
136
+ export const getStackResponseSchema = z.object({
137
+ stack_name: z.string(),
138
+ network_id: z.string(),
139
+ containers: z.array(stackContainerStatusSchema),
140
+ created_at: z.iso.datetime().openapi({ format: "date-time" }),
141
+ }).openapi("GetStackResponse");
142
+
143
+ export const listStacksResponseSchema = z.object({
144
+ stacks: z.array(getRunResponseSchema),
145
+ count: z.int()
146
+ }).openapi("ListStacksResponse");
@@ -0,0 +1,53 @@
1
+ import { z } from 'zod';
2
+ import { extendZodWithOpenApi } from "@asteasolutions/zod-to-openapi";
3
+
4
+ extendZodWithOpenApi(z);
5
+
6
+ export const ensureVolumeRequestSchema = z.object({
7
+ volume_name: z.string().min(1).describe("Docker volume name to ensure exists").openapi({ example: "figulus-cache-nodejs" }),
8
+ driver: z.string().optional().describe("Volume driver (default \"local\")").openapi({ example: "local" }),
9
+ labels: z.record(z.string(), z.string()).optional().describe("Labels to apply when creating the volume"),
10
+ }).openapi("EnsureVolumeRequest");
11
+ export type EnsureVolumeRequest = z.infer<typeof ensureVolumeRequestSchema>;
12
+
13
+ export const ensureVolumeResponseSchema = z.object({
14
+ volume_name: z.string(),
15
+ created: z.boolean().describe("true if the volume was newly created, false if it already existed"),
16
+ message: z.string().optional(),
17
+ }).openapi("EnsureVolumeResponse");
18
+ export type EnsureVolumeResponse = z.infer<typeof ensureVolumeResponseSchema>;
19
+
20
+ export const volumeInfoSchema = z.object({
21
+ name: z.string(),
22
+ driver: z.string(),
23
+ mountpoint: z.string(),
24
+ created_at: z.string(),
25
+ labels: z.record(z.string(), z.string()).optional(),
26
+ scope: z.string(),
27
+ size_bytes: z.int().optional().describe("Volume size; may not be available on all Docker versions").openapi({ format: "int64" }),
28
+ }).openapi("VolumeInfo");
29
+ export type VolumeInfo = z.infer<typeof volumeInfoSchema>;
30
+
31
+ export const listVolumesResponseSchema = z.object({
32
+ volumes: z.array(volumeInfoSchema),
33
+ count: z.int(),
34
+ }).openapi("ListVolumesResponse");
35
+ export type ListVolumesResponse = z.infer<typeof listVolumesResponseSchema>;
36
+
37
+ export const deleteVolumeResponseSchema = z.object({
38
+ volume_name: z.string(),
39
+ message: z.string(),
40
+ }).openapi("DeleteVolumeResponse");
41
+ export type DeleteVolumeResponse = z.infer<typeof deleteVolumeResponseSchema>;
42
+
43
+ export const pruneVolumesRequestSchema = z.object({
44
+ filters: z.record(z.string(), z.array(z.string())).optional().describe("Docker filter map, e.g. {\"label\": [\"figulus.cache.language=nodejs\"]}"),
45
+ }).openapi("PruneVolumesRequest");
46
+ export type PruneVolumesRequest = z.infer<typeof pruneVolumesRequestSchema>;
47
+
48
+ export const pruneVolumesResponseSchema = z.object({
49
+ volumes_deleted: z.array(z.string()).describe("Names of volumes that were deleted"),
50
+ space_reclaimed: z.int().describe("Bytes freed").openapi({ format: "int64" }),
51
+ message: z.string(),
52
+ }).openapi("PruneVolumesResponse");
53
+ export type PruneVolumesResponse = z.infer<typeof pruneVolumesResponseSchema>;
package/src/index.ts CHANGED
@@ -1,8 +1,3 @@
1
- export * from './shared.js';
2
- export * from './health.js';
3
- export * from './run-request.js';
4
- export * from './image.js';
5
- export * from './volume.js';
6
- export * from './session.js';
7
- export * from './figspec.js';
8
- export * from './figstack.js';
1
+ export * from './core/index.js';
2
+ export * from './engine/index.js';
3
+ export * from './registry/index.js';
@@ -0,0 +1,2 @@
1
+ export * from "./json-schema.js";
2
+ export * from "./metadata.js";
@@ -0,0 +1,178 @@
1
+ import { z } from "zod";
2
+
3
+ const JsonSchemaTypeNameSchema = z.enum([
4
+ "string",
5
+ "number",
6
+ "integer",
7
+ "boolean",
8
+ "object",
9
+ "array",
10
+ "null",
11
+ ]).describe("JSON Schema type name");
12
+
13
+ const JsonValueSchema: z.ZodType = z.lazy(() =>
14
+ z.union([
15
+ z.null(),
16
+ z.boolean(),
17
+ z.number(),
18
+ z.string(),
19
+ z.array(JsonValueSchema),
20
+ z.record(z.string(), JsonValueSchema),
21
+ ]),
22
+ );
23
+
24
+ // Reusable: array of strings with no duplicates
25
+ const uniqueStringArray = () =>
26
+ z.array(z.string()).superRefine((arr, ctx) => {
27
+ const seen = new Set<string>();
28
+ arr.forEach((el, i) => {
29
+ if (seen.has(el)) {
30
+ ctx.addIssue({
31
+ code: "custom",
32
+ message: `Duplicate value "${el}" at index ${i}`,
33
+ path: [i],
34
+ });
35
+ }
36
+ seen.add(el);
37
+ });
38
+ });
39
+
40
+ // Array of unique type name values
41
+ const typeNameArray = z
42
+ .array(JsonSchemaTypeNameSchema)
43
+ .superRefine((arr, ctx) => {
44
+ const seen = new Set<string>();
45
+ arr.forEach((el, i) => {
46
+ if (seen.has(el)) {
47
+ ctx.addIssue({
48
+ code: "custom",
49
+ message: `Duplicate type "${el}" at index ${i}`,
50
+ path: [i],
51
+ });
52
+ }
53
+ seen.add(el);
54
+ });
55
+ });
56
+
57
+ // Declared before JsonSchemaSchema so all lazy references below are genuinely
58
+ // necessary to defer evaluation until JsonSchemaSchema is assigned.
59
+ const JsonSchemaObjectSchema = z
60
+ .object({
61
+ // ── Core ────────────────────────────────────────────────────────────
62
+ $schema: z.string().optional()
63
+ .describe("§7: MUST be a URI (RFC 3986) with a scheme — not necessarily an http URL"),
64
+ $id: z.string().optional()
65
+ .describe("§8.2: MUST be a URI-reference — includes relative refs like \"#foo\", \"other.json\""),
66
+ $ref: z.string().optional()
67
+ .describe("§8.3: MUST be a URI Reference"),
68
+ $comment: z.string().optional()
69
+ .describe("§9: MUST be a string"),
70
+
71
+ // ── Metadata ────────────────────────────────────────────────────────
72
+ title: z.string().optional(),
73
+ description: z.string().optional(),
74
+ default: JsonValueSchema.optional()
75
+ .describe("§10.2: no restrictions on value"),
76
+ examples: z.array(JsonValueSchema).optional()
77
+ .describe("§10.4: MUST be an array, no restrictions on elements"),
78
+ readOnly: z.boolean().optional(),
79
+ writeOnly: z.boolean().optional(),
80
+
81
+ // ── Type ────────────────────────────────────────────────────────────
82
+ type: z.union([JsonSchemaTypeNameSchema, typeNameArray]).optional()
83
+ .describe("§6.1.1: string or array of unique type names; no minimum length on array form"),
84
+
85
+ // ── Enum / Const ────────────────────────────────────────────────────
86
+ enum: z.array(JsonValueSchema).optional()
87
+ .describe("§6.1.2: SHOULD have ≥1 element (SHOULD not MUST — no .min(1))"),
88
+ const: JsonValueSchema.optional()
89
+ .describe("§6.1.3: MAY be any type including null"),
90
+
91
+ // ── Numeric ─────────────────────────────────────────────────────────
92
+ multipleOf: z.number().positive().optional()
93
+ .describe("§6.2.1: MUST be a number strictly greater than 0"),
94
+ maximum: z.number().optional(),
95
+ exclusiveMaximum: z.number().optional()
96
+ .describe("§6.2.3: MUST be a number (exclusive bounds are numbers in Draft-07, not booleans)"),
97
+ minimum: z.number().optional(),
98
+ exclusiveMinimum: z.number().optional(),
99
+
100
+ // ── String ──────────────────────────────────────────────────────────
101
+ maxLength: z.int().nonnegative().optional()
102
+ .describe("§6.3.1–6.3.2: MUST be a non-negative integer"),
103
+ minLength: z.int().nonnegative().optional()
104
+ .describe("§6.3.1–6.3.2: MUST be a non-negative integer"),
105
+ pattern: z.string().optional(),
106
+ format: z.string().optional(),
107
+ contentMediaType: z.string().optional(),
108
+ contentEncoding: z.string().optional(),
109
+
110
+ // ── Array ───────────────────────────────────────────────────────────
111
+ items: z
112
+ .lazy(() => z.union([JsonSchemaSchema, z.array(JsonSchemaSchema)]))
113
+ .optional()
114
+ .describe("§6.4.1: MUST be a valid JSON Schema or an array of valid JSON Schemas"),
115
+ additionalItems: z.lazy(() => JsonSchemaSchema).optional()
116
+ .describe("§6.4.2: MUST be a valid JSON Schema"),
117
+ maxItems: z.int().nonnegative().optional()
118
+ .describe("§6.4.3–6.4.4: MUST be a non-negative integer"),
119
+ minItems: z.int().nonnegative().optional()
120
+ .describe("§6.4.3–6.4.4: MUST be a non-negative integer"),
121
+ uniqueItems: z.boolean().optional(),
122
+ contains: z.lazy(() => JsonSchemaSchema).optional()
123
+ .describe("§6.4.6: MUST be a valid JSON Schema"),
124
+
125
+ // ── Object ──────────────────────────────────────────────────────────
126
+ maxProperties: z.int().nonnegative().optional()
127
+ .describe("§6.5.1–6.5.2: MUST be a non-negative integer"),
128
+ minProperties: z.int().nonnegative().optional()
129
+ .describe("§6.5.1–6.5.2: MUST be a non-negative integer"),
130
+ required: uniqueStringArray().optional()
131
+ .describe("§6.5.3: elements MUST be unique; empty array is explicitly valid"),
132
+ properties: z.lazy(() => z.record(z.string(), JsonSchemaSchema)).optional(),
133
+ patternProperties: z
134
+ .lazy(() => z.record(z.string(), JsonSchemaSchema))
135
+ .optional(),
136
+ additionalProperties: z.lazy(() => JsonSchemaSchema).optional(),
137
+ dependencies: z
138
+ .lazy(() =>
139
+ z.record(z.string(), z.union([JsonSchemaSchema, uniqueStringArray()])),
140
+ )
141
+ .optional()
142
+ .describe("§6.5.7: each value is a subschema OR an array of unique strings"),
143
+ propertyNames: z.lazy(() => JsonSchemaSchema).optional(),
144
+
145
+ // ── Conditional ─────────────────────────────────────────────────────
146
+ if: z.lazy(() => JsonSchemaSchema).optional(),
147
+ then: z.lazy(() => JsonSchemaSchema).optional(),
148
+ else: z.lazy(() => JsonSchemaSchema).optional(),
149
+
150
+ // ── Combiners ───────────────────────────────────────────────────────
151
+ allOf: z.lazy(() => z.array(JsonSchemaSchema).min(1)).optional()
152
+ .describe("§6.7.1–6.7.3: MUST be a non-empty array (min(1) is correct per spec)"),
153
+ anyOf: z.lazy(() => z.array(JsonSchemaSchema).min(1)).optional()
154
+ .describe("§6.7.1–6.7.3: MUST be a non-empty array (min(1) is correct per spec)"),
155
+ oneOf: z.lazy(() => z.array(JsonSchemaSchema).min(1)).optional()
156
+ .describe("§6.7.1–6.7.3: MUST be a non-empty array (min(1) is correct per spec)"),
157
+ not: z.lazy(() => JsonSchemaSchema).optional(),
158
+
159
+ // ── Definitions ─────────────────────────────────────────────────────
160
+ definitions: z
161
+ .lazy(() => z.record(z.string(), JsonSchemaSchema))
162
+ .optional()
163
+ .describe("§9: MUST be an object; each value MUST be a valid JSON Schema"),
164
+ })
165
+ // §4.3.1: unknown keywords SHOULD be ignored — loose preserves them
166
+ .loose();
167
+
168
+ // Top-level: a schema is either a boolean or an object (§4.3.1)
169
+ export const JsonSchemaSchema: z.ZodType = z.lazy(() =>
170
+ z.union([z.boolean(), JsonSchemaObjectSchema]),
171
+ );
172
+
173
+ // ── Inferred TypeScript types ──────────────────────────────────────────────────
174
+
175
+ export type JsonSchemaTypeName = z.infer<typeof JsonSchemaTypeNameSchema>;
176
+ export type JsonValue = z.infer<typeof JsonValueSchema>;
177
+ export type JsonSchemaObject = z.infer<typeof JsonSchemaObjectSchema>;
178
+ export type JsonSchema = z.infer<typeof JsonSchemaSchema>;