@moldesk/registry 0.0.4 → 0.0.6

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/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { MoldeskError } from "./errors.js";
2
2
  export type { MoldeskErrorData } from "./errors.js";
3
- export { allowedCommandTokens, commandSpecSchema, modelManifestV1Schema, } from "./schema.js";
4
- export type { AssetSpec, CommandSpec, HardwareRequirements, InputSpec, ModelCategory, ModelManifestV1, ModelStatus, OutputSpec, PlatformId, PostInstallHook, PythonRequirement, PythonRuntimeSpec, DockerRuntimeSpec, RequirementLevel, RuntimeKind, RuntimeSpec, SourceSpec, } from "./schema.js";
3
+ export { allowedCommandTokens, commandSpecSchema, isCloudEligible, modelManifestV1Schema, } from "./schema.js";
4
+ export type { AssetSpec, CloudSpec, CommandSpec, HardwareRequirements, InputSpec, ModelCategory, ModelManifestV1, ModelStatus, OutputSpec, PlatformId, PostInstallHook, PythonRequirement, PythonRuntimeSpec, DockerRuntimeSpec, RequirementLevel, RuntimeKind, RuntimeSpec, SourceSpec, } from "./schema.js";
5
5
  export type { LegacyModelManifest } from "./types.js";
6
6
  export { getModelsDir, listAvailableModels, loadManifestFile, parseManifest, } from "./loader.js";
7
7
  export { detectHostPlatformId, effectiveRuntimeSource, selectPythonRuntime, } from "./runtime-selection.js";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { MoldeskError } from "./errors.js";
2
- export { allowedCommandTokens, commandSpecSchema, modelManifestV1Schema, } from "./schema.js";
2
+ export { allowedCommandTokens, commandSpecSchema, isCloudEligible, modelManifestV1Schema, } from "./schema.js";
3
3
  export { getModelsDir, listAvailableModels, loadManifestFile, parseManifest, } from "./loader.js";
4
4
  export { detectHostPlatformId, effectiveRuntimeSource, selectPythonRuntime, } from "./runtime-selection.js";
@@ -5,7 +5,7 @@ modelVersion: 1.0.3
5
5
  adapterVersion: 0.1.0
6
6
  description: Bounded local protein-binder campaigns with AlphaFold and ProteinMPNN/HyperMPNN.
7
7
  category: sequence-design
8
- status: planned
8
+ status: beta
9
9
  license: BindCraft2 Source-Available License (Hosting-Restricted)
10
10
  homepage: https://github.com/PacesaLab/BindCraft2
11
11
  source:
@@ -5,7 +5,7 @@ modelVersion: "1.1"
5
5
  adapterVersion: 0.2.0
6
6
  description: Blind small-molecule docking against a protein, returning ranked poses and confidence (not binding affinity).
7
7
  category: docking
8
- status: planned
8
+ status: beta
9
9
  license: MIT
10
10
  homepage: https://github.com/gcorso/DiffDock
11
11
  source:
package/dist/schema.d.ts CHANGED
@@ -463,6 +463,64 @@ declare const assetSchema: z.ZodObject<{
463
463
  sizeBytes?: number | undefined;
464
464
  archive?: "none" | "tar.gz" | "tar" | "zip" | undefined;
465
465
  }>;
466
+ /**
467
+ * Opt-in remote execution capability. Versioned independently of the manifest so the cloud contract
468
+ * can evolve without a manifest schema bump. Every value here must come from a live validation of
469
+ * that model on that image; a manifest without this block is never run in the cloud. Reuses the
470
+ * manifest's own `assets`, `runtimes` and `source`; it adds only what the cloud path cannot derive.
471
+ */
472
+ declare const cloudSpecSchema: z.ZodObject<{
473
+ schemaVersion: z.ZodLiteral<1>;
474
+ platform: z.ZodLiteral<"linux-x64">;
475
+ images: z.ZodObject<{
476
+ /** Generic CUDA worker image used for inference. */
477
+ gpu: z.ZodString;
478
+ /** Generic CPU worker image used only to prepare assets on a network volume. */
479
+ cpu: z.ZodString;
480
+ }, "strict", z.ZodTypeAny, {
481
+ cpu: string;
482
+ gpu: string;
483
+ }, {
484
+ cpu: string;
485
+ gpu: string;
486
+ }>;
487
+ minVramGb: z.ZodNumber;
488
+ minRamGb: z.ZodNumber;
489
+ /** Container disk for one ephemeral job: runtime environment, inputs and outputs (and assets when ephemeral). */
490
+ ephemeralDiskGb: z.ZodNumber;
491
+ /** Persistent network volume holding the raw object cache plus materialized assets. */
492
+ volumeGb: z.ZodNumber;
493
+ /** Bump when the on-volume asset layout changes; invalidates persistent caches. */
494
+ assetLayoutRevision: z.ZodString;
495
+ /** True when assets can be prepared on a CPU-only Pod without Python or CUDA. */
496
+ cpuAssetPreparation: z.ZodBoolean;
497
+ }, "strict", z.ZodTypeAny, {
498
+ minVramGb: number;
499
+ minRamGb: number;
500
+ schemaVersion: 1;
501
+ platform: "linux-x64";
502
+ images: {
503
+ cpu: string;
504
+ gpu: string;
505
+ };
506
+ ephemeralDiskGb: number;
507
+ volumeGb: number;
508
+ assetLayoutRevision: string;
509
+ cpuAssetPreparation: boolean;
510
+ }, {
511
+ minVramGb: number;
512
+ minRamGb: number;
513
+ schemaVersion: 1;
514
+ platform: "linux-x64";
515
+ images: {
516
+ cpu: string;
517
+ gpu: string;
518
+ };
519
+ ephemeralDiskGb: number;
520
+ volumeGb: number;
521
+ assetLayoutRevision: string;
522
+ cpuAssetPreparation: boolean;
523
+ }>;
466
524
  declare const inputSpecSchema: z.ZodObject<{
467
525
  formats: z.ZodArray<z.ZodString, "many">;
468
526
  required: z.ZodBoolean;
@@ -786,6 +844,58 @@ export declare const modelManifestV1Schema: z.ZodEffects<z.ZodObject<{
786
844
  sizeBytes?: number | undefined;
787
845
  archive?: "none" | "tar.gz" | "tar" | "zip" | undefined;
788
846
  }>, "many">>;
847
+ cloud: z.ZodOptional<z.ZodObject<{
848
+ schemaVersion: z.ZodLiteral<1>;
849
+ platform: z.ZodLiteral<"linux-x64">;
850
+ images: z.ZodObject<{
851
+ /** Generic CUDA worker image used for inference. */
852
+ gpu: z.ZodString;
853
+ /** Generic CPU worker image used only to prepare assets on a network volume. */
854
+ cpu: z.ZodString;
855
+ }, "strict", z.ZodTypeAny, {
856
+ cpu: string;
857
+ gpu: string;
858
+ }, {
859
+ cpu: string;
860
+ gpu: string;
861
+ }>;
862
+ minVramGb: z.ZodNumber;
863
+ minRamGb: z.ZodNumber;
864
+ /** Container disk for one ephemeral job: runtime environment, inputs and outputs (and assets when ephemeral). */
865
+ ephemeralDiskGb: z.ZodNumber;
866
+ /** Persistent network volume holding the raw object cache plus materialized assets. */
867
+ volumeGb: z.ZodNumber;
868
+ /** Bump when the on-volume asset layout changes; invalidates persistent caches. */
869
+ assetLayoutRevision: z.ZodString;
870
+ /** True when assets can be prepared on a CPU-only Pod without Python or CUDA. */
871
+ cpuAssetPreparation: z.ZodBoolean;
872
+ }, "strict", z.ZodTypeAny, {
873
+ minVramGb: number;
874
+ minRamGb: number;
875
+ schemaVersion: 1;
876
+ platform: "linux-x64";
877
+ images: {
878
+ cpu: string;
879
+ gpu: string;
880
+ };
881
+ ephemeralDiskGb: number;
882
+ volumeGb: number;
883
+ assetLayoutRevision: string;
884
+ cpuAssetPreparation: boolean;
885
+ }, {
886
+ minVramGb: number;
887
+ minRamGb: number;
888
+ schemaVersion: 1;
889
+ platform: "linux-x64";
890
+ images: {
891
+ cpu: string;
892
+ gpu: string;
893
+ };
894
+ ephemeralDiskGb: number;
895
+ volumeGb: number;
896
+ assetLayoutRevision: string;
897
+ cpuAssetPreparation: boolean;
898
+ }>>;
789
899
  input: z.ZodObject<{
790
900
  formats: z.ZodArray<z.ZodString, "many">;
791
901
  required: z.ZodBoolean;
@@ -914,6 +1024,20 @@ export declare const modelManifestV1Schema: z.ZodEffects<z.ZodObject<{
914
1024
  sizeBytes?: number | undefined;
915
1025
  archive?: "none" | "tar.gz" | "tar" | "zip" | undefined;
916
1026
  }[] | undefined;
1027
+ cloud?: {
1028
+ minVramGb: number;
1029
+ minRamGb: number;
1030
+ schemaVersion: 1;
1031
+ platform: "linux-x64";
1032
+ images: {
1033
+ cpu: string;
1034
+ gpu: string;
1035
+ };
1036
+ ephemeralDiskGb: number;
1037
+ volumeGb: number;
1038
+ assetLayoutRevision: string;
1039
+ cpuAssetPreparation: boolean;
1040
+ } | undefined;
917
1041
  }, {
918
1042
  status: "available" | "beta" | "planned";
919
1043
  name: string;
@@ -1011,6 +1135,20 @@ export declare const modelManifestV1Schema: z.ZodEffects<z.ZodObject<{
1011
1135
  sizeBytes?: number | undefined;
1012
1136
  archive?: "none" | "tar.gz" | "tar" | "zip" | undefined;
1013
1137
  }[] | undefined;
1138
+ cloud?: {
1139
+ minVramGb: number;
1140
+ minRamGb: number;
1141
+ schemaVersion: 1;
1142
+ platform: "linux-x64";
1143
+ images: {
1144
+ cpu: string;
1145
+ gpu: string;
1146
+ };
1147
+ ephemeralDiskGb: number;
1148
+ volumeGb: number;
1149
+ assetLayoutRevision: string;
1150
+ cpuAssetPreparation: boolean;
1151
+ } | undefined;
1014
1152
  }>, {
1015
1153
  status: "available" | "beta" | "planned";
1016
1154
  name: string;
@@ -1108,6 +1246,20 @@ export declare const modelManifestV1Schema: z.ZodEffects<z.ZodObject<{
1108
1246
  sizeBytes?: number | undefined;
1109
1247
  archive?: "none" | "tar.gz" | "tar" | "zip" | undefined;
1110
1248
  }[] | undefined;
1249
+ cloud?: {
1250
+ minVramGb: number;
1251
+ minRamGb: number;
1252
+ schemaVersion: 1;
1253
+ platform: "linux-x64";
1254
+ images: {
1255
+ cpu: string;
1256
+ gpu: string;
1257
+ };
1258
+ ephemeralDiskGb: number;
1259
+ volumeGb: number;
1260
+ assetLayoutRevision: string;
1261
+ cpuAssetPreparation: boolean;
1262
+ } | undefined;
1111
1263
  }, {
1112
1264
  status: "available" | "beta" | "planned";
1113
1265
  name: string;
@@ -1205,6 +1357,20 @@ export declare const modelManifestV1Schema: z.ZodEffects<z.ZodObject<{
1205
1357
  sizeBytes?: number | undefined;
1206
1358
  archive?: "none" | "tar.gz" | "tar" | "zip" | undefined;
1207
1359
  }[] | undefined;
1360
+ cloud?: {
1361
+ minVramGb: number;
1362
+ minRamGb: number;
1363
+ schemaVersion: 1;
1364
+ platform: "linux-x64";
1365
+ images: {
1366
+ cpu: string;
1367
+ gpu: string;
1368
+ };
1369
+ ephemeralDiskGb: number;
1370
+ volumeGb: number;
1371
+ assetLayoutRevision: string;
1372
+ cpuAssetPreparation: boolean;
1373
+ } | undefined;
1208
1374
  }>;
1209
1375
  export type ModelStatus = z.infer<typeof modelStatus>;
1210
1376
  export type RuntimeKind = z.infer<typeof runtimeKind>;
@@ -1218,9 +1384,14 @@ export type DockerRuntimeSpec = z.infer<typeof dockerRuntimeSchema>;
1218
1384
  export type RuntimeSpec = z.infer<typeof runtimeSpecSchema>;
1219
1385
  export type HardwareRequirements = z.infer<typeof hardwareSchema>;
1220
1386
  export type AssetSpec = z.infer<typeof assetSchema>;
1387
+ export type CloudSpec = z.infer<typeof cloudSpecSchema>;
1221
1388
  export type InputSpec = z.infer<typeof inputSpecSchema>;
1222
1389
  export type OutputSpec = z.infer<typeof outputSpecSchema>;
1223
1390
  export type SourceSpec = z.infer<typeof sourceSchema>;
1224
1391
  export type ModelManifestV1 = z.infer<typeof modelManifestV1Schema>;
1225
1392
  export type CommandSpec = z.infer<typeof commandSpecSchema>;
1393
+ /** A manifest is cloud-eligible only by declaring a `cloud` block; there is no implicit eligibility. */
1394
+ export declare function isCloudEligible(manifest: ModelManifestV1): manifest is ModelManifestV1 & {
1395
+ cloud: CloudSpec;
1396
+ };
1226
1397
  export {};
package/dist/schema.js CHANGED
@@ -183,6 +183,43 @@ const assetSchema = z
183
183
  archive: z.enum(["none", "tar.gz", "tar", "zip"]).optional(),
184
184
  })
185
185
  .strict();
186
+ /** Registry/name reference pinned by digest (`ghcr.io/org/img@sha256:<64 hex>`); a bare tag is never accepted. */
187
+ const digestPinnedImage = z
188
+ .string()
189
+ .regex(/^[a-z0-9]+(?:[._-][a-z0-9]+)*(?::\d+)?(?:\/[a-z0-9]+(?:[._-][a-z0-9]+)*)+@sha256:[0-9a-f]{64}$/, "must be a lowercase image reference pinned by digest, e.g. registry.example/org/image@sha256:<64 hex>");
190
+ const cloudAssetLayoutRevision = z
191
+ .string()
192
+ .regex(/^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$/, "must be a short safe token, e.g. \"1\"");
193
+ /**
194
+ * Opt-in remote execution capability. Versioned independently of the manifest so the cloud contract
195
+ * can evolve without a manifest schema bump. Every value here must come from a live validation of
196
+ * that model on that image; a manifest without this block is never run in the cloud. Reuses the
197
+ * manifest's own `assets`, `runtimes` and `source`; it adds only what the cloud path cannot derive.
198
+ */
199
+ const cloudSpecSchema = z
200
+ .object({
201
+ schemaVersion: z.literal(1),
202
+ platform: z.literal("linux-x64"),
203
+ images: z
204
+ .object({
205
+ /** Generic CUDA worker image used for inference. */
206
+ gpu: digestPinnedImage,
207
+ /** Generic CPU worker image used only to prepare assets on a network volume. */
208
+ cpu: digestPinnedImage,
209
+ })
210
+ .strict(),
211
+ minVramGb: z.number().positive(),
212
+ minRamGb: z.number().positive(),
213
+ /** Container disk for one ephemeral job: runtime environment, inputs and outputs (and assets when ephemeral). */
214
+ ephemeralDiskGb: z.number().int().positive(),
215
+ /** Persistent network volume holding the raw object cache plus materialized assets. */
216
+ volumeGb: z.number().int().positive(),
217
+ /** Bump when the on-volume asset layout changes; invalidates persistent caches. */
218
+ assetLayoutRevision: cloudAssetLayoutRevision,
219
+ /** True when assets can be prepared on a CPU-only Pod without Python or CUDA. */
220
+ cpuAssetPreparation: z.boolean(),
221
+ })
222
+ .strict();
186
223
  const inputSpecSchema = z
187
224
  .object({
188
225
  formats: z.array(z.string().min(1)).min(1),
@@ -247,6 +284,7 @@ export const modelManifestV1Schema = z
247
284
  runtimes: z.array(runtimeSpecSchema).min(1),
248
285
  hardware: hardwareSchema,
249
286
  assets: z.array(assetSchema).optional(),
287
+ cloud: cloudSpecSchema.optional(),
250
288
  input: inputSpecSchema,
251
289
  outputs: z.array(outputSpecSchema).min(1),
252
290
  })
@@ -282,6 +320,39 @@ export const modelManifestV1Schema = z
282
320
  }
283
321
  }
284
322
  }
323
+ if (val.cloud) {
324
+ const cloud = val.cloud;
325
+ const remoteRuntimes = val.runtimes.filter((runtime) => runtime.kind === "python" && (runtime.platforms === undefined || runtime.platforms.includes(cloud.platform)));
326
+ if (remoteRuntimes.length === 0) {
327
+ ctx.addIssue({
328
+ code: z.ZodIssueCode.custom,
329
+ message: `cloud requires a python runtime entry that applies to ${cloud.platform}`,
330
+ path: ["cloud", "platform"],
331
+ });
332
+ }
333
+ for (const runtime of remoteRuntimes) {
334
+ if (runtime.minVramGb !== undefined && cloud.minVramGb < runtime.minVramGb) {
335
+ ctx.addIssue({
336
+ code: z.ZodIssueCode.custom,
337
+ message: `cloud.minVramGb (${cloud.minVramGb}) must be at least the ${cloud.platform} runtime's minVramGb (${runtime.minVramGb})`,
338
+ path: ["cloud", "minVramGb"],
339
+ });
340
+ }
341
+ }
342
+ // Lower bound only: compressed asset bytes alone must fit. Extraction and runtime headroom
343
+ // come from live measurement, not from this check.
344
+ const assetBytes = (val.assets ?? []).reduce((total, asset) => total + (asset.sizeBytes ?? 0), 0);
345
+ const GIB = 1024 ** 3;
346
+ for (const field of ["ephemeralDiskGb", "volumeGb"]) {
347
+ if (cloud[field] * GIB < assetBytes) {
348
+ ctx.addIssue({
349
+ code: z.ZodIssueCode.custom,
350
+ message: `cloud.${field} (${cloud[field]} GB) is smaller than the manifest's declared asset bytes (${(assetBytes / GIB).toFixed(2)} GiB)`,
351
+ path: ["cloud", field],
352
+ });
353
+ }
354
+ }
355
+ }
285
356
  const names = new Set();
286
357
  for (const out of val.outputs) {
287
358
  if (names.has(out.id)) {
@@ -335,3 +406,7 @@ export const modelManifestV1Schema = z
335
406
  }
336
407
  }
337
408
  });
409
+ /** A manifest is cloud-eligible only by declaring a `cloud` block; there is no implicit eligibility. */
410
+ export function isCloudEligible(manifest) {
411
+ return manifest.cloud !== undefined;
412
+ }
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
  import { MoldeskError } from "./errors.js";
3
3
  import { parseManifest } from "./loader.js";
4
- import { commandSpecSchema } from "./schema.js";
4
+ import { commandSpecSchema, isCloudEligible } from "./schema.js";
5
5
  function baseManifest() {
6
6
  return {
7
7
  schemaVersion: 1,
@@ -333,3 +333,60 @@ describe("parseManifest", () => {
333
333
  }
334
334
  });
335
335
  });
336
+ describe("cloud capability block", () => {
337
+ const DIGEST = "a".repeat(64);
338
+ const cloud = () => ({
339
+ schemaVersion: 1,
340
+ platform: "linux-x64",
341
+ images: {
342
+ gpu: `registry.example/moldesk/worker-gpu@sha256:${DIGEST}`,
343
+ cpu: `registry.example/moldesk/worker-cpu@sha256:${DIGEST}`,
344
+ },
345
+ minVramGb: 24,
346
+ minRamGb: 32,
347
+ ephemeralDiskGb: 40,
348
+ volumeGb: 40,
349
+ assetLayoutRevision: "1",
350
+ cpuAssetPreparation: true,
351
+ });
352
+ const withCloud = (block, extra = {}) => ({ ...baseManifest(), ...extra, cloud: block });
353
+ it("accepts a complete block and marks the model eligible; absence means ineligible", () => {
354
+ const eligible = parseManifest(withCloud(cloud()), "x/manifest.yaml");
355
+ expect(isCloudEligible(eligible)).toBe(true);
356
+ expect(eligible.cloud?.images.gpu).toContain("@sha256:");
357
+ expect(isCloudEligible(parseManifest(baseManifest(), "x/manifest.yaml"))).toBe(false);
358
+ });
359
+ it("rejects tag-only or malformed image references", () => {
360
+ for (const gpu of ["registry.example/moldesk/worker:latest", "worker@sha256:" + DIGEST, `Registry.Example/x@sha256:${DIGEST}`, `registry.example/x@sha256:${"a".repeat(63)}`]) {
361
+ expect(() => parseManifest(withCloud({ ...cloud(), images: { ...cloud().images, gpu } }), "x/manifest.yaml")).toThrow(/pinned by digest/);
362
+ }
363
+ });
364
+ it("is strict: unknown keys, other platforms and missing fields are rejected", () => {
365
+ expect(() => parseManifest(withCloud({ ...cloud(), command: "rm -rf /" }), "x/manifest.yaml")).toThrow();
366
+ expect(() => parseManifest(withCloud({ ...cloud(), platform: "darwin-arm64" }), "x/manifest.yaml")).toThrow();
367
+ const { volumeGb: _omit, ...missing } = cloud();
368
+ expect(() => parseManifest(withCloud(missing), "x/manifest.yaml")).toThrow();
369
+ expect(() => parseManifest(withCloud({ ...cloud(), minVramGb: 0 }), "x/manifest.yaml")).toThrow();
370
+ expect(() => parseManifest(withCloud({ ...cloud(), schemaVersion: 2 }), "x/manifest.yaml")).toThrow();
371
+ });
372
+ it("requires a python runtime entry that applies to linux-x64", () => {
373
+ const darwinOnly = {
374
+ runtimes: [{ kind: "python", python: "3.11", installer: "uv", platforms: ["darwin-arm64"], requirements: [{ name: "torch" }] }],
375
+ };
376
+ expect(() => parseManifest(withCloud(cloud(), darwinOnly), "x/manifest.yaml")).toThrow(/python runtime entry that applies to linux-x64/);
377
+ });
378
+ it("requires cloud.minVramGb to cover the linux runtime's own floor", () => {
379
+ const runtimes = [
380
+ { kind: "python", python: "3.11", installer: "uv", platforms: ["linux-x64"], nvidiaGpu: "required", minVramGb: 40, requirements: [{ name: "torch" }] },
381
+ ];
382
+ expect(() => parseManifest(withCloud(cloud(), { runtimes }), "x/manifest.yaml")).toThrow(/cloud\.minVramGb \(24\)/);
383
+ expect(parseManifest(withCloud({ ...cloud(), minVramGb: 40 }, { runtimes }), "x/manifest.yaml").cloud?.minVramGb).toBe(40);
384
+ });
385
+ it("rejects disk or volume sizes smaller than the declared asset bytes", () => {
386
+ const GIB = 1024 ** 3;
387
+ const assets = [{ id: "w", url: "https://example.com/w.bin", sha256: "b".repeat(64), target: "w.bin", sizeBytes: 50 * GIB }];
388
+ expect(() => parseManifest(withCloud({ ...cloud(), volumeGb: 60 }, { assets }), "x/manifest.yaml")).toThrow(/cloud\.ephemeralDiskGb \(40 GB\)/);
389
+ expect(() => parseManifest(withCloud({ ...cloud(), ephemeralDiskGb: 60 }, { assets }), "x/manifest.yaml")).toThrow(/cloud\.volumeGb \(40 GB\)/);
390
+ expect(parseManifest(withCloud({ ...cloud(), ephemeralDiskGb: 60, volumeGb: 60 }, { assets }), "x/manifest.yaml").cloud?.volumeGb).toBe(60);
391
+ });
392
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moldesk/registry",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Model manifest loading and resolution for MoleculeDesk",
5
5
  "license": "MIT",
6
6
  "type": "module",