@mintmcp/hosted-cli 0.0.17 → 0.0.19
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.
- package/dist/api.d.ts +87 -5
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +65 -4
- package/dist/api.js.map +1 -1
- package/dist/containerBuilder.d.ts +48 -0
- package/dist/containerBuilder.d.ts.map +1 -0
- package/dist/containerBuilder.js +301 -0
- package/dist/containerBuilder.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +400 -38
- package/dist/index.js.map +1 -1
- package/dist/updateBuilder.d.ts +28 -3
- package/dist/updateBuilder.d.ts.map +1 -1
- package/dist/updateBuilder.js +8 -3
- package/dist/updateBuilder.js.map +1 -1
- package/package.json +3 -2
- package/src/api.ts +87 -4
- package/src/containerBuilder.ts +531 -0
- package/src/index.ts +633 -63
- package/src/updateBuilder.ts +10 -3
package/dist/api.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import z from "zod";
|
|
|
2
2
|
export declare const HOSTED_ID_PREFIX: "hosted-";
|
|
3
3
|
export declare const HostedIdSchema: z.ZodBranded<z.ZodEffects<z.ZodString, string, string>, "HostedId">;
|
|
4
4
|
export type HostedId = z.infer<typeof HostedIdSchema>;
|
|
5
|
+
export declare const GatewayIdSchema: z.ZodString;
|
|
6
|
+
export type GatewayId = z.infer<typeof GatewayIdSchema>;
|
|
7
|
+
export declare const ManagedImageTagSchema: z.ZodString;
|
|
8
|
+
export type ManagedImageTag = z.infer<typeof ManagedImageTagSchema>;
|
|
5
9
|
export declare const EnvUpdateValueSchema: z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
6
10
|
type: z.ZodLiteral<"copyPreviousValue">;
|
|
7
11
|
fromName: z.ZodString;
|
|
@@ -45,7 +49,7 @@ export type HostedTransport = z.infer<typeof HostedTransportSchema>;
|
|
|
45
49
|
export declare const UserConfigUpdateSchema: z.ZodObject<{
|
|
46
50
|
userGivenName: z.ZodString;
|
|
47
51
|
image: z.ZodDefault<z.ZodString>;
|
|
48
|
-
command: z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]
|
|
52
|
+
command: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
|
|
49
53
|
transport: z.ZodDefault<z.ZodDiscriminatedUnion<"type", [z.ZodObject<{
|
|
50
54
|
type: z.ZodLiteral<"http">;
|
|
51
55
|
path: z.ZodString;
|
|
@@ -115,7 +119,6 @@ export declare const UserConfigUpdateSchema: z.ZodObject<{
|
|
|
115
119
|
}, "strip", z.ZodTypeAny, {
|
|
116
120
|
userGivenName: string;
|
|
117
121
|
image: string;
|
|
118
|
-
command: string | string[];
|
|
119
122
|
transport: {
|
|
120
123
|
path: string;
|
|
121
124
|
type: "http";
|
|
@@ -137,10 +140,10 @@ export declare const UserConfigUpdateSchema: z.ZodObject<{
|
|
|
137
140
|
};
|
|
138
141
|
name: string;
|
|
139
142
|
}[];
|
|
143
|
+
command?: string | string[] | undefined;
|
|
140
144
|
secretDataZipGcsPath?: string | undefined;
|
|
141
145
|
}, {
|
|
142
146
|
userGivenName: string;
|
|
143
|
-
command: string | string[];
|
|
144
147
|
replaceEnv: {
|
|
145
148
|
value: {
|
|
146
149
|
type: "copyPreviousValue";
|
|
@@ -153,6 +156,7 @@ export declare const UserConfigUpdateSchema: z.ZodObject<{
|
|
|
153
156
|
name: string;
|
|
154
157
|
}[];
|
|
155
158
|
image?: string | undefined;
|
|
159
|
+
command?: string | string[] | undefined;
|
|
156
160
|
transport?: {
|
|
157
161
|
path: string;
|
|
158
162
|
type: "http";
|
|
@@ -181,7 +185,6 @@ declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
181
185
|
input: {
|
|
182
186
|
config: {
|
|
183
187
|
userGivenName: string;
|
|
184
|
-
command: string | string[];
|
|
185
188
|
replaceEnv: {
|
|
186
189
|
value: {
|
|
187
190
|
type: "copyPreviousValue";
|
|
@@ -194,6 +197,7 @@ declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
194
197
|
name: string;
|
|
195
198
|
}[];
|
|
196
199
|
image?: string | undefined;
|
|
200
|
+
command?: string | string[] | undefined;
|
|
197
201
|
transport?: {
|
|
198
202
|
path: string;
|
|
199
203
|
type: "http";
|
|
@@ -209,6 +213,7 @@ declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
209
213
|
};
|
|
210
214
|
output: {
|
|
211
215
|
hostedId: string & z.BRAND<"HostedId">;
|
|
216
|
+
gatewayId: string;
|
|
212
217
|
};
|
|
213
218
|
meta: object;
|
|
214
219
|
}>;
|
|
@@ -217,6 +222,75 @@ declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
217
222
|
hostedId: string;
|
|
218
223
|
update: {
|
|
219
224
|
userGivenName?: string | undefined;
|
|
225
|
+
image?: string | undefined;
|
|
226
|
+
command?: string | string[] | null | undefined;
|
|
227
|
+
transport?: {
|
|
228
|
+
path: string;
|
|
229
|
+
type: "http";
|
|
230
|
+
} | {
|
|
231
|
+
type: "stdio";
|
|
232
|
+
} | undefined;
|
|
233
|
+
secretDataZipGcsPath?: string | undefined;
|
|
234
|
+
};
|
|
235
|
+
};
|
|
236
|
+
output: {
|
|
237
|
+
hostedId: string & z.BRAND<"HostedId">;
|
|
238
|
+
gatewayId?: string | undefined;
|
|
239
|
+
};
|
|
240
|
+
meta: object;
|
|
241
|
+
}>;
|
|
242
|
+
createRegistryPushSession: import("@trpc/server").TRPCMutationProcedure<{
|
|
243
|
+
input: {
|
|
244
|
+
hostedId: string;
|
|
245
|
+
};
|
|
246
|
+
output: {
|
|
247
|
+
registryHost: string;
|
|
248
|
+
repository: string;
|
|
249
|
+
username: string;
|
|
250
|
+
password: string;
|
|
251
|
+
imageTag: string;
|
|
252
|
+
finalizeToken: string;
|
|
253
|
+
};
|
|
254
|
+
meta: object;
|
|
255
|
+
}>;
|
|
256
|
+
createRegistryPushSessionForCreate: import("@trpc/server").TRPCMutationProcedure<{
|
|
257
|
+
input: {};
|
|
258
|
+
output: {
|
|
259
|
+
registryHost: string;
|
|
260
|
+
repository: string;
|
|
261
|
+
username: string;
|
|
262
|
+
password: string;
|
|
263
|
+
imageTag: string;
|
|
264
|
+
finalizeToken: string;
|
|
265
|
+
};
|
|
266
|
+
meta: object;
|
|
267
|
+
}>;
|
|
268
|
+
finalizeRegistryPush: import("@trpc/server").TRPCMutationProcedure<{
|
|
269
|
+
input: {
|
|
270
|
+
hostedId: string;
|
|
271
|
+
finalizeToken: string;
|
|
272
|
+
};
|
|
273
|
+
output: {
|
|
274
|
+
hostedId: string & z.BRAND<"HostedId">;
|
|
275
|
+
gatewayId?: string | undefined;
|
|
276
|
+
};
|
|
277
|
+
meta: object;
|
|
278
|
+
}>;
|
|
279
|
+
finalizeRegistryPushCreate: import("@trpc/server").TRPCMutationProcedure<{
|
|
280
|
+
input: {
|
|
281
|
+
config: {
|
|
282
|
+
userGivenName: string;
|
|
283
|
+
replaceEnv: {
|
|
284
|
+
value: {
|
|
285
|
+
type: "copyPreviousValue";
|
|
286
|
+
fromName: string;
|
|
287
|
+
} | {
|
|
288
|
+
value: string;
|
|
289
|
+
type: "set";
|
|
290
|
+
isSecret: boolean;
|
|
291
|
+
};
|
|
292
|
+
name: string;
|
|
293
|
+
}[];
|
|
220
294
|
command?: string | string[] | undefined;
|
|
221
295
|
transport?: {
|
|
222
296
|
path: string;
|
|
@@ -224,10 +298,18 @@ declare const appRouter: import("@trpc/server").TRPCBuiltRouter<{
|
|
|
224
298
|
} | {
|
|
225
299
|
type: "stdio";
|
|
226
300
|
} | undefined;
|
|
301
|
+
cpu?: number | undefined;
|
|
302
|
+
memoryMiB?: number | undefined;
|
|
303
|
+
urlPath?: string | undefined;
|
|
227
304
|
secretDataZipGcsPath?: string | undefined;
|
|
305
|
+
startupProbe?: "startupProbeOn" | "startupProbeOff" | undefined;
|
|
228
306
|
};
|
|
307
|
+
finalizeToken: string;
|
|
308
|
+
};
|
|
309
|
+
output: {
|
|
310
|
+
hostedId: string & z.BRAND<"HostedId">;
|
|
311
|
+
gatewayId: string;
|
|
229
312
|
};
|
|
230
|
-
output: unknown;
|
|
231
313
|
meta: object;
|
|
232
314
|
}>;
|
|
233
315
|
makeSignedUploadUrl: import("@trpc/server").TRPCMutationProcedure<{
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAQA,OAAO,CAAC,MAAM,KAAK,CAAC;AAKpB,eAAO,MAAM,gBAAgB,EAAG,SAAkB,CAAC;AACnD,eAAO,MAAM,cAAc,qEAOL,CAAC;AACvB,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;IAU/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;KAWQ,CAAC;AAC3C,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmBjC,CAAC;AAKH,QAAA,MAAM,SAAS
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAQA,OAAO,CAAC,MAAM,KAAK,CAAC;AAKpB,eAAO,MAAM,gBAAgB,EAAG,SAAkB,CAAC;AACnD,eAAO,MAAM,cAAc,qEAOL,CAAC;AACvB,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,eAAO,MAAM,eAAe,aAAoB,CAAC;AACjD,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,qBAAqB,aAA2C,CAAC;AAC9E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;IAU/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;KAWQ,CAAC;AAC3C,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmBjC,CAAC;AAKH,QAAA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkHb,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC"}
|
package/dist/api.js
CHANGED
|
@@ -13,6 +13,8 @@ export const HostedIdSchema = z
|
|
|
13
13
|
.refine((v) => v.startsWith(HOSTED_ID_PREFIX) &&
|
|
14
14
|
UUID_RE.test(v.substring(HOSTED_ID_PREFIX.length)))
|
|
15
15
|
.brand();
|
|
16
|
+
export const GatewayIdSchema = z.string().min(1);
|
|
17
|
+
export const ManagedImageTagSchema = z.string().regex(/^mi_[0-9A-Za-z]{22}$/);
|
|
16
18
|
export const EnvUpdateValueSchema = z.discriminatedUnion("type", [
|
|
17
19
|
z.object({
|
|
18
20
|
type: z.literal("copyPreviousValue"),
|
|
@@ -41,7 +43,7 @@ export const UserConfigUpdateSchema = z.object({
|
|
|
41
43
|
image: z
|
|
42
44
|
.string()
|
|
43
45
|
.default("nikolaik/python-nodejs@sha256:1dcaf296688723fbe79c400e1dda743c8e8a8b2812ef4b0af70779914a9cec3c"),
|
|
44
|
-
command: z.union([z.string(), z.array(z.string())]),
|
|
46
|
+
command: z.union([z.string(), z.array(z.string())]).optional(),
|
|
45
47
|
transport: HostedTransportSchema,
|
|
46
48
|
cpu: z.number().default(1),
|
|
47
49
|
memoryMiB: z.number().default(4096),
|
|
@@ -58,7 +60,7 @@ const appRouter = router({
|
|
|
58
60
|
mcpHost: router({
|
|
59
61
|
createServer: procedure
|
|
60
62
|
.input(z.object({ config: UserConfigUpdateSchema }))
|
|
61
|
-
.output(z.object({ hostedId: HostedIdSchema }))
|
|
63
|
+
.output(z.object({ hostedId: HostedIdSchema, gatewayId: GatewayIdSchema }))
|
|
62
64
|
.mutation(() => {
|
|
63
65
|
throw new Error("stub");
|
|
64
66
|
}),
|
|
@@ -67,12 +69,71 @@ const appRouter = router({
|
|
|
67
69
|
hostedId: HostedIdSchema,
|
|
68
70
|
update: z.object({
|
|
69
71
|
userGivenName: z.string().optional(),
|
|
70
|
-
|
|
72
|
+
image: z.string().optional(),
|
|
73
|
+
command: z
|
|
74
|
+
.union([z.string(), z.array(z.string())])
|
|
75
|
+
.optional()
|
|
76
|
+
.nullable(),
|
|
71
77
|
transport: HostedTransportSchema.optional(),
|
|
72
78
|
secretDataZipGcsPath: z.string().optional(),
|
|
73
79
|
}),
|
|
74
80
|
}))
|
|
75
|
-
.output(z.
|
|
81
|
+
.output(z.object({
|
|
82
|
+
hostedId: HostedIdSchema,
|
|
83
|
+
gatewayId: GatewayIdSchema.optional(),
|
|
84
|
+
}))
|
|
85
|
+
.mutation(() => {
|
|
86
|
+
throw new Error("stub");
|
|
87
|
+
}),
|
|
88
|
+
createRegistryPushSession: procedure
|
|
89
|
+
.input(z.object({
|
|
90
|
+
hostedId: HostedIdSchema,
|
|
91
|
+
}))
|
|
92
|
+
.output(z.object({
|
|
93
|
+
registryHost: z.string(),
|
|
94
|
+
repository: z.string(),
|
|
95
|
+
username: z.string(),
|
|
96
|
+
password: z.string(),
|
|
97
|
+
imageTag: ManagedImageTagSchema,
|
|
98
|
+
finalizeToken: z.string(),
|
|
99
|
+
}))
|
|
100
|
+
.mutation(() => {
|
|
101
|
+
throw new Error("stub");
|
|
102
|
+
}),
|
|
103
|
+
createRegistryPushSessionForCreate: procedure
|
|
104
|
+
.input(z.object({}))
|
|
105
|
+
.output(z.object({
|
|
106
|
+
registryHost: z.string(),
|
|
107
|
+
repository: z.string(),
|
|
108
|
+
username: z.string(),
|
|
109
|
+
password: z.string(),
|
|
110
|
+
imageTag: ManagedImageTagSchema,
|
|
111
|
+
finalizeToken: z.string(),
|
|
112
|
+
}))
|
|
113
|
+
.mutation(() => {
|
|
114
|
+
throw new Error("stub");
|
|
115
|
+
}),
|
|
116
|
+
finalizeRegistryPush: procedure
|
|
117
|
+
.input(z.object({
|
|
118
|
+
hostedId: HostedIdSchema,
|
|
119
|
+
finalizeToken: z.string(),
|
|
120
|
+
}))
|
|
121
|
+
.output(z.object({
|
|
122
|
+
hostedId: HostedIdSchema,
|
|
123
|
+
gatewayId: GatewayIdSchema.optional(),
|
|
124
|
+
}))
|
|
125
|
+
.mutation(() => {
|
|
126
|
+
throw new Error("stub");
|
|
127
|
+
}),
|
|
128
|
+
finalizeRegistryPushCreate: procedure
|
|
129
|
+
.input(z.object({
|
|
130
|
+
finalizeToken: z.string(),
|
|
131
|
+
config: UserConfigUpdateSchema.omit({ image: true }),
|
|
132
|
+
}))
|
|
133
|
+
.output(z.object({
|
|
134
|
+
hostedId: HostedIdSchema,
|
|
135
|
+
gatewayId: GatewayIdSchema,
|
|
136
|
+
}))
|
|
76
137
|
.mutation(() => {
|
|
77
138
|
throw new Error("stub");
|
|
78
139
|
}),
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,uFAAuF;AACvF,EAAE;AACF,kGAAkG;AAClG,oBAAoB;AAEpB,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,SAAS,MAAM,WAAW,CAAC;AAClC,OAAO,CAAC,MAAM,KAAK,CAAC;AAEpB,MAAM,OAAO,GACX,iEAAiE,CAAC;AAEpE,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAkB,CAAC;AACnD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC;KAC5B,MAAM,EAAE;KACR,MAAM,CACL,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,UAAU,CAAC,gBAAgB,CAAC;IAC9B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,CACrD;KACA,KAAK,EAAc,CAAC;AAGvB,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAC/D,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAAC;QACpC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;KACrB,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;QACtB,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;KACtB,CAAC;CACH,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC;KACnC,kBAAkB,CAAC,MAAM,EAAE;IAC1B,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC;QACvB,gCAAgC;QAChC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;KACjB,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;KACzB,CAAC;CACH,CAAC;KACD,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;AAG3C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,aAAa,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAChC,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,OAAO,CACN,gGAAgG,CACjG;IACH,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
2
|
+
export interface BuildOptions {
|
|
3
|
+
dockerfile?: string;
|
|
4
|
+
context?: string;
|
|
5
|
+
buildArgs?: string[];
|
|
6
|
+
target?: string;
|
|
7
|
+
platform?: string;
|
|
8
|
+
imageRef: string;
|
|
9
|
+
}
|
|
10
|
+
export interface BuildResult {
|
|
11
|
+
imageRef: string;
|
|
12
|
+
platform?: string;
|
|
13
|
+
sizeBytes: number;
|
|
14
|
+
sizeHuman: string;
|
|
15
|
+
}
|
|
16
|
+
export interface SmokeTestOptions {
|
|
17
|
+
imageRef: string;
|
|
18
|
+
probePath?: string;
|
|
19
|
+
probeTimeoutMs?: number;
|
|
20
|
+
env?: string[];
|
|
21
|
+
envFile?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface SmokeTestResult {
|
|
24
|
+
containerName: string;
|
|
25
|
+
imageRef: string;
|
|
26
|
+
url: string;
|
|
27
|
+
}
|
|
28
|
+
interface ContainerBuilderDeps {
|
|
29
|
+
execFileSync: typeof execFileSync;
|
|
30
|
+
spawn: typeof spawn;
|
|
31
|
+
fetch: typeof fetch;
|
|
32
|
+
getAvailablePort: () => Promise<number>;
|
|
33
|
+
sleep: (ms: number) => Promise<void>;
|
|
34
|
+
now: () => number;
|
|
35
|
+
}
|
|
36
|
+
export declare function defaultBuildPlatform(): string;
|
|
37
|
+
export declare function defaultLocalTestImageRef(now?: number): string;
|
|
38
|
+
export declare function ensureDockerAvailable(deps?: ContainerBuilderDeps): void;
|
|
39
|
+
export declare function ensureDockerBuildxAvailable(deps?: ContainerBuilderDeps): void;
|
|
40
|
+
export declare function buildImage(options: BuildOptions, deps?: ContainerBuilderDeps): BuildResult;
|
|
41
|
+
export declare function pushImage(imageRef: string, deps?: ContainerBuilderDeps): void;
|
|
42
|
+
export declare function loginToRegistry(registryHost: string, username: string, password: string, deps?: ContainerBuilderDeps): void;
|
|
43
|
+
export declare function tagImage(sourceImageRef: string, targetImageRef: string, deps?: ContainerBuilderDeps): void;
|
|
44
|
+
export declare function ensureImageAvailableLocally(imageRef: string, deps?: ContainerBuilderDeps): void;
|
|
45
|
+
export declare function assertImageSupportsPlatform(imageRef: string, platform: string, deps?: ContainerBuilderDeps): void;
|
|
46
|
+
export declare function smokeTestImage(options: SmokeTestOptions, deps?: ContainerBuilderDeps): Promise<SmokeTestResult>;
|
|
47
|
+
export {};
|
|
48
|
+
//# sourceMappingURL=containerBuilder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"containerBuilder.d.ts","sourceRoot":"","sources":["../src/containerBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAQ5E,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;CACb;AAYD,UAAU,oBAAoB;IAC5B,YAAY,EAAE,OAAO,YAAY,CAAC;IAClC,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,gBAAgB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,GAAG,EAAE,MAAM,MAAM,CAAC;CACnB;AAmBD,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAgB,wBAAwB,CAAC,GAAG,SAAoB,GAAG,MAAM,CAExE;AAED,wBAAgB,qBAAqB,CACnC,IAAI,GAAE,oBAAkC,GACvC,IAAI,CAQN;AAED,wBAAgB,2BAA2B,CACzC,IAAI,GAAE,oBAAkC,GACvC,IAAI,CAQN;AAED,wBAAgB,UAAU,CACxB,OAAO,EAAE,YAAY,EACrB,IAAI,GAAE,oBAAkC,GACvC,WAAW,CA+Db;AAED,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,oBAAkC,GACvC,IAAI,CAIN;AAED,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,oBAAkC,GACvC,IAAI,CAUN;AAED,wBAAgB,QAAQ,CACtB,cAAc,EAAE,MAAM,EACtB,cAAc,EAAE,MAAM,EACtB,IAAI,GAAE,oBAAkC,GACvC,IAAI,CAIN;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,oBAAkC,GACvC,IAAI,CAUN;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE,oBAAkC,GACvC,IAAI,CAYN;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,gBAAgB,EACzB,IAAI,GAAE,oBAAkC,GACvC,OAAO,CAAC,eAAe,CAAC,CA4C1B"}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import net from "node:net";
|
|
4
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
5
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
6
|
+
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
7
|
+
const defaultDeps = {
|
|
8
|
+
execFileSync,
|
|
9
|
+
spawn,
|
|
10
|
+
fetch,
|
|
11
|
+
getAvailablePort,
|
|
12
|
+
sleep: (ms) => delay(ms),
|
|
13
|
+
now: () => Date.now(),
|
|
14
|
+
};
|
|
15
|
+
const SIZE_WARN_BYTES = 250 * 1024 * 1024;
|
|
16
|
+
const SIZE_REJECT_BYTES = 1024 * 1024 * 1024;
|
|
17
|
+
const DEFAULT_BUILD_PLATFORM = "linux/amd64";
|
|
18
|
+
const DEFAULT_PROBE_PATH = "/mcp";
|
|
19
|
+
const DEFAULT_PROBE_TIMEOUT_MS = 30_000;
|
|
20
|
+
const LOCAL_TEST_IMAGE_PREFIX = "mintmcp-local-test";
|
|
21
|
+
const LOCAL_TEST_POLL_INTERVAL_MS = 500;
|
|
22
|
+
export function defaultBuildPlatform() {
|
|
23
|
+
return DEFAULT_BUILD_PLATFORM;
|
|
24
|
+
}
|
|
25
|
+
export function defaultLocalTestImageRef(now = defaultDeps.now()) {
|
|
26
|
+
return `${LOCAL_TEST_IMAGE_PREFIX}:${now}`;
|
|
27
|
+
}
|
|
28
|
+
export function ensureDockerAvailable(deps = defaultDeps) {
|
|
29
|
+
try {
|
|
30
|
+
deps.execFileSync("docker", ["info"], { stdio: "ignore" });
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
throw new Error("Docker is not running or not installed. Install Docker Desktop or start the Docker daemon.");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export function ensureDockerBuildxAvailable(deps = defaultDeps) {
|
|
37
|
+
try {
|
|
38
|
+
deps.execFileSync("docker", ["buildx", "version"], { stdio: "ignore" });
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new Error("Docker Buildx is required. Install a Docker version with buildx support and try again.");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function buildImage(options, deps = defaultDeps) {
|
|
45
|
+
const { dockerfile = "Dockerfile", context = ".", buildArgs = [], target, platform, imageRef, } = options;
|
|
46
|
+
if (platform != undefined) {
|
|
47
|
+
assertSinglePlatform(platform);
|
|
48
|
+
}
|
|
49
|
+
const args = [
|
|
50
|
+
"buildx",
|
|
51
|
+
"build",
|
|
52
|
+
"--load",
|
|
53
|
+
...(platform != undefined ? ["--platform", platform] : []),
|
|
54
|
+
"-f",
|
|
55
|
+
dockerfile,
|
|
56
|
+
"-t",
|
|
57
|
+
imageRef,
|
|
58
|
+
...(target ? ["--target", target] : []),
|
|
59
|
+
...buildArgs.flatMap((arg) => ["--build-arg", arg]),
|
|
60
|
+
context,
|
|
61
|
+
];
|
|
62
|
+
console.log(`Building image: docker ${args.join(" ")}`);
|
|
63
|
+
deps.execFileSync("docker", args, { stdio: "inherit" });
|
|
64
|
+
const inspectOutput = deps
|
|
65
|
+
.execFileSync("docker", ["image", "inspect", imageRef, "--format", "{{.Size}}"], { encoding: "utf8" })
|
|
66
|
+
.trim();
|
|
67
|
+
const sizeBytes = Number.parseInt(inspectOutput, 10);
|
|
68
|
+
const sizeHuman = formatBytes(sizeBytes);
|
|
69
|
+
if (!Number.isFinite(sizeBytes)) {
|
|
70
|
+
throw new Error(`Could not determine built image size for ${imageRef}.`);
|
|
71
|
+
}
|
|
72
|
+
if (sizeBytes > SIZE_REJECT_BYTES) {
|
|
73
|
+
throw new Error(`Image size ${sizeHuman} exceeds maximum of 1 GB. Reduce layers or use a smaller base image.`);
|
|
74
|
+
}
|
|
75
|
+
if (sizeBytes > SIZE_WARN_BYTES) {
|
|
76
|
+
console.warn(`Image size ${sizeHuman} exceeds 250 MB. Consider optimizing.`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(`Image size: ${sizeHuman}`);
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
imageRef,
|
|
83
|
+
...(platform != undefined ? { platform } : {}),
|
|
84
|
+
sizeBytes,
|
|
85
|
+
sizeHuman,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
export function pushImage(imageRef, deps = defaultDeps) {
|
|
89
|
+
console.log(`Pushing image: ${imageRef}`);
|
|
90
|
+
deps.execFileSync("docker", ["push", imageRef], { stdio: "inherit" });
|
|
91
|
+
console.log("Image pushed.");
|
|
92
|
+
}
|
|
93
|
+
export function loginToRegistry(registryHost, username, password, deps = defaultDeps) {
|
|
94
|
+
console.log(`Logging in to registry: ${registryHost}`);
|
|
95
|
+
deps.execFileSync("docker", ["login", registryHost, "-u", username, "--password-stdin"], {
|
|
96
|
+
input: password,
|
|
97
|
+
stdio: "pipe",
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
export function tagImage(sourceImageRef, targetImageRef, deps = defaultDeps) {
|
|
101
|
+
deps.execFileSync("docker", ["tag", sourceImageRef, targetImageRef], {
|
|
102
|
+
stdio: "inherit",
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
export function ensureImageAvailableLocally(imageRef, deps = defaultDeps) {
|
|
106
|
+
try {
|
|
107
|
+
deps.execFileSync("docker", ["image", "inspect", imageRef], {
|
|
108
|
+
stdio: "ignore",
|
|
109
|
+
});
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
console.log(`Pulling image: ${imageRef}`);
|
|
114
|
+
deps.execFileSync("docker", ["pull", imageRef], { stdio: "inherit" });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function assertImageSupportsPlatform(imageRef, platform, deps = defaultDeps) {
|
|
118
|
+
assertSinglePlatform(platform);
|
|
119
|
+
const manifestOutput = deps.execFileSync("docker", ["manifest", "inspect", imageRef, "-v"], { encoding: "utf8" });
|
|
120
|
+
if (!manifestSupportsPlatform(manifestOutput, platform)) {
|
|
121
|
+
throw new Error(`Pushed image ${imageRef} does not advertise support for ${platform}. Build and push a compatible image before deploying to the hosted runtime.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export async function smokeTestImage(options, deps = defaultDeps) {
|
|
125
|
+
const probePath = options.probePath ?? DEFAULT_PROBE_PATH;
|
|
126
|
+
const probeTimeoutMs = options.probeTimeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS;
|
|
127
|
+
const hostPort = await deps.getAvailablePort();
|
|
128
|
+
const containerName = `${LOCAL_TEST_IMAGE_PREFIX}-${randomUUID().slice(0, 12)}`;
|
|
129
|
+
const url = `http://127.0.0.1:${hostPort}${probePath}`;
|
|
130
|
+
console.log(`Running image locally: ${options.imageRef}`);
|
|
131
|
+
const container = deps.spawn("docker", [
|
|
132
|
+
"run",
|
|
133
|
+
"--rm",
|
|
134
|
+
"--name",
|
|
135
|
+
containerName,
|
|
136
|
+
"-p",
|
|
137
|
+
`${hostPort}:${hostPort}`,
|
|
138
|
+
"-e",
|
|
139
|
+
`PORT=${hostPort}`,
|
|
140
|
+
...(options.envFile ? ["--env-file", options.envFile] : []),
|
|
141
|
+
...(options.env ?? []).flatMap((entry) => ["-e", entry]),
|
|
142
|
+
options.imageRef,
|
|
143
|
+
], { stdio: ["ignore", "pipe", "pipe"] });
|
|
144
|
+
const { logs, exitInfo } = captureContainerLogs(container);
|
|
145
|
+
try {
|
|
146
|
+
await mcpSmokeTest({ url, probeTimeoutMs }, {
|
|
147
|
+
fetch: deps.fetch,
|
|
148
|
+
sleep: deps.sleep,
|
|
149
|
+
getExitInfo: () => exitInfo.value,
|
|
150
|
+
});
|
|
151
|
+
return { containerName, imageRef: options.imageRef, url };
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
const message = formatSmokeTestFailureMessage(error, logs.join(""));
|
|
155
|
+
throw new Error(message);
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
await stopContainer(containerName, deps);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function assertSinglePlatform(platform) {
|
|
162
|
+
if (platform.includes(",")) {
|
|
163
|
+
throw new Error("Multiple platforms are not supported yet. Pass a single platform such as linux/amd64.");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
function formatBytes(bytes) {
|
|
167
|
+
if (bytes < 1024) {
|
|
168
|
+
return `${bytes} B`;
|
|
169
|
+
}
|
|
170
|
+
if (bytes < 1024 * 1024) {
|
|
171
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
172
|
+
}
|
|
173
|
+
if (bytes < 1024 * 1024 * 1024) {
|
|
174
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
175
|
+
}
|
|
176
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
177
|
+
}
|
|
178
|
+
function manifestSupportsPlatform(rawManifest, platform) {
|
|
179
|
+
const parsed = parsePlatform(platform);
|
|
180
|
+
const manifest = JSON.parse(rawManifest);
|
|
181
|
+
if (!manifest) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
if (platformMatches(manifest.Descriptor?.platform, parsed)) {
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
187
|
+
if (manifest.os != undefined &&
|
|
188
|
+
manifest.architecture != undefined &&
|
|
189
|
+
platformMatches({
|
|
190
|
+
os: manifest.os,
|
|
191
|
+
architecture: manifest.architecture,
|
|
192
|
+
...(manifest.variant != undefined ? { variant: manifest.variant } : {}),
|
|
193
|
+
}, parsed)) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
return (manifest.manifests?.some((entry) => platformMatches(entry.platform, parsed)) ?? false);
|
|
197
|
+
}
|
|
198
|
+
function parsePlatform(platform) {
|
|
199
|
+
const [os, architecture, variant, ...rest] = platform.split("/");
|
|
200
|
+
if (!os || !architecture || rest.length > 0) {
|
|
201
|
+
throw new Error(`Unsupported platform "${platform}". Use a single platform like linux/amd64.`);
|
|
202
|
+
}
|
|
203
|
+
return { os, architecture, ...(variant ? { variant } : {}) };
|
|
204
|
+
}
|
|
205
|
+
function platformMatches(actual, expected) {
|
|
206
|
+
return (actual?.os === expected.os &&
|
|
207
|
+
actual?.architecture === expected.architecture &&
|
|
208
|
+
(expected.variant == undefined || actual?.variant === expected.variant));
|
|
209
|
+
}
|
|
210
|
+
function captureContainerLogs(container) {
|
|
211
|
+
const logs = [];
|
|
212
|
+
const exitInfo = {};
|
|
213
|
+
for (const stream of [container.stdout, container.stderr]) {
|
|
214
|
+
stream?.on("data", (chunk) => {
|
|
215
|
+
logs.push(typeof chunk === "string" ? chunk : chunk.toString("utf8"));
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
container.on("exit", (code, signal) => {
|
|
219
|
+
exitInfo.value = { code, signal };
|
|
220
|
+
});
|
|
221
|
+
return { logs, exitInfo };
|
|
222
|
+
}
|
|
223
|
+
async function mcpSmokeTest(params, deps) {
|
|
224
|
+
const deadline = Date.now() + params.probeTimeoutMs;
|
|
225
|
+
while (Date.now() < deadline) {
|
|
226
|
+
const exitInfo = deps.getExitInfo();
|
|
227
|
+
if (exitInfo) {
|
|
228
|
+
throw new Error(`Container exited before responding to the MCP probe (code=${String(exitInfo.code)}, signal=${String(exitInfo.signal)}).`);
|
|
229
|
+
}
|
|
230
|
+
const client = new Client({
|
|
231
|
+
name: "MintMCP Local Probe",
|
|
232
|
+
version: "1.0.0",
|
|
233
|
+
});
|
|
234
|
+
const transport = new StreamableHTTPClientTransport(new URL(params.url), {
|
|
235
|
+
fetch: deps.fetch,
|
|
236
|
+
});
|
|
237
|
+
try {
|
|
238
|
+
await client.connect(transport);
|
|
239
|
+
console.log(`Local MCP initialize succeeded: ${params.url}`);
|
|
240
|
+
const toolsResult = await client.listTools();
|
|
241
|
+
console.log(`Local tools/list succeeded: ${toolsResult.tools.length} tool(s) found`);
|
|
242
|
+
await client.close();
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
try {
|
|
247
|
+
await client.close();
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
// Ignore close errors during retry
|
|
251
|
+
}
|
|
252
|
+
const exitInfo = deps.getExitInfo();
|
|
253
|
+
if (exitInfo) {
|
|
254
|
+
throw new Error(`Container exited before responding to the MCP probe (code=${String(exitInfo.code)}, signal=${String(exitInfo.signal)}).`);
|
|
255
|
+
}
|
|
256
|
+
await deps.sleep(LOCAL_TEST_POLL_INTERVAL_MS);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
throw new Error(`Timed out waiting ${params.probeTimeoutMs}ms for the local MCP probe to succeed.`);
|
|
260
|
+
}
|
|
261
|
+
async function stopContainer(containerName, deps) {
|
|
262
|
+
try {
|
|
263
|
+
deps.execFileSync("docker", ["rm", "-f", containerName], {
|
|
264
|
+
stdio: "ignore",
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
// Ignore cleanup failures: the container may have already exited.
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async function getAvailablePort() {
|
|
272
|
+
return await new Promise((resolve, reject) => {
|
|
273
|
+
const server = net.createServer();
|
|
274
|
+
server.unref();
|
|
275
|
+
server.on("error", reject);
|
|
276
|
+
server.listen(0, "127.0.0.1", () => {
|
|
277
|
+
const address = server.address();
|
|
278
|
+
if (address == null || typeof address === "string") {
|
|
279
|
+
server.close(() => reject(new Error("Could not determine a free port")));
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
const { port } = address;
|
|
283
|
+
server.close((error) => {
|
|
284
|
+
if (error) {
|
|
285
|
+
reject(error);
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
resolve(port);
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function formatSmokeTestFailureMessage(error, logs) {
|
|
294
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
295
|
+
const trimmedLogs = logs.trim();
|
|
296
|
+
if (trimmedLogs.length === 0) {
|
|
297
|
+
return `Local image smoke test failed: ${message}`;
|
|
298
|
+
}
|
|
299
|
+
return `Local image smoke test failed: ${message}\n\nContainer logs:\n${trimmedLogs}`;
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=containerBuilder.js.map
|