@shardflux/sdk 0.6.2 → 0.7.0

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.
@@ -1,17 +1,57 @@
1
1
  /**
2
- * Template registry, custom template builds, the version file tree and diff, and template dev mode (drafts and test
3
- * instances) over the application API (/v1). Registry and build types are written by hand and checked against the
4
- * generated contract in type-checks.ts; the Templates v2 types (contracts §19) alias the generated schemas.
2
+ * Template registry, custom template builds (recipe v1 Dockerfiles and recipe v2, contracts §24.1), build uploads
3
+ * (§24.2), recipe export, version test instances, package search, the version file tree and diff, and template dev
4
+ * mode (drafts and test instances) over the application API (/v1). Registry and build types are written by hand and
5
+ * checked against the generated contract in type-checks.ts; the Templates v2 and template editor types alias the
6
+ * generated schemas.
5
7
  *
6
8
  * Publishing/archiving an organization template version is a browser-only
7
9
  * owner/admin action (it changes what `open` resolves for every project), so
8
10
  * it is not exposed here.
9
11
  */
10
12
  import type { components } from './generated/app-api.js';
11
- import type { ClientContext, Operation, Page, WaitOptions } from './client.js';
13
+ import type { Caps, ClientContext, Operation, Page, WaitOptions } from './client.js';
14
+ import type { YamlParser } from './template-file.js';
12
15
  import type { ToolName } from './tokens.js';
13
16
  import { Workspace } from './workspace.js';
14
17
  type S = components['schemas'];
18
+ /** Recipe v2 (contracts §24.1): the `recipe` of a build, the export's `recipe` and the document of template.yaml. */
19
+ export type TemplateRecipeV2 = S['TemplateRecipeV2'];
20
+ /** One `build.files[]` entry: an upload (`upload: "sha256:<hex>"`), or in template.yaml a local path (`from`). */
21
+ export type TemplateRecipeV2File = NonNullable<TemplateRecipeV2['build']['files']>[number];
22
+ /** What a workspace of a version gets when it opens (§24.3): env, inputs, start commands, services, defaults. */
23
+ export type TemplateSettings = S['TemplateSettings'];
24
+ /** Settings as a recipe v2, a draft publish or save-as-template take them (omitted fields: empty, or carried forward). */
25
+ export type TemplateSettingsInput = S['TemplateSettingsInput'];
26
+ /** An open-time input: `text` (a value passed to open) or `secret` (a stored secret of the same name). */
27
+ export type TemplateInput = S['TemplateInput'];
28
+ /** A start command (§24.4): create, boot or resume. */
29
+ export type TemplateStartCommand = S['TemplateStartCommand'];
30
+ /** A process the guest keeps running (§24.4), ready before open() returns. */
31
+ export type TemplateService = S['TemplateService'];
32
+ /** The template's workspace network ceiling (§24.3). */
33
+ export type TemplateEgressDefault = S['TemplateEgressDefault'];
34
+ /** An uploaded file or folder (tar) of the organization (§24.2). */
35
+ export type TemplateUpload = S['TemplateUpload'];
36
+ export type TemplateUploadRequest = S['TemplateUploadRequest'];
37
+ export type TemplateUploadResponse = S['TemplateUploadResponse'];
38
+ /** The recipe and settings a version was built from (GET …/versions/{v}/recipe): ready to build again. */
39
+ export type TemplateVersionRecipe = S['TemplateVersionRecipe'];
40
+ export type TemplatePackage = S['TemplatePackage'];
41
+ export type TemplatePackagePage = S['TemplatePackagePage'];
42
+ export type TemplatePackageEcosystem = 'apt' | 'pip' | 'npm';
43
+ /** The languages a base offers a recipe v2 (contracts §24.6 `GET …/template-languages`): the language table for its platform base. */
44
+ export type TemplateLanguages = S['TemplateLanguages'];
45
+ export type TemplateLanguage = TemplateLanguages['data'][number];
46
+ export type CreateVersionTestInstanceBody = S['CreateVersionTestInstanceBody'];
47
+ /** Platform templates: `os` (a bare operating system) or `stack`; organization templates are null. */
48
+ export type TemplateCategory = 'os' | 'stack';
49
+ /** The stored recipe v2 of a build (GET of one build): the build, the settings and the compiled steps. */
50
+ export type TemplateBuildRecipeV2 = Extract<NonNullable<S['TemplateBuild']['recipe']>, {
51
+ schema: string;
52
+ }>;
53
+ /** Start commands and services of a workspace's version (§24.4); null when it has none. */
54
+ export type WorkspaceStartup = S['WorkspaceStartup'];
15
55
  /** Manifest v2 `defaults` of a version (contracts §19.7). */
16
56
  export type TemplateDefaults = S['TemplateDefaults'];
17
57
  /** How a version was produced: a recipe build, a saved workspace (or draft publish), or git (reserved). */
@@ -124,6 +164,8 @@ export interface TemplateVersion {
124
164
  rootfs_sha256: string | null;
125
165
  } | null;
126
166
  defaults: TemplateDefaults;
167
+ /** What a workspace of this version gets when it opens (0.7.0; contracts §24.3). `defaults` equals `settings.defaults`. */
168
+ settings: TemplateSettings;
127
169
  /** The file list the tree and diff read (`loaded` when files() and diff() can answer). */
128
170
  files: TemplateFilesSummary;
129
171
  rootfs_bytes: number | null;
@@ -167,6 +209,8 @@ export interface TemplateSummary {
167
209
  draft: TemplateDraftSummary | null;
168
210
  /** Reserved (T2): always `pinned` in T1. */
169
211
  update_policy: 'pinned' | 'auto';
212
+ /** Platform templates: `os` or `stack` (0.7.0); organization templates: null. */
213
+ category: TemplateCategory | null;
170
214
  }
171
215
  export interface TemplateDetail extends TemplateSummary {
172
216
  plan: {
@@ -267,10 +311,12 @@ export interface TemplateBuild {
267
311
  builder_id: string | null;
268
312
  attempt: number;
269
313
  };
270
- /** Recipe builds only, on create and get of one build. */
314
+ /** Recipe builds only, on create and get of one build: v1 `{dockerfile}`, or the stored recipe v2 (0.7.0). */
271
315
  recipe?: {
272
316
  dockerfile: string;
273
- };
317
+ } | TemplateBuildRecipeV2;
318
+ /** Host names the builder refused during the build (0.7.0; at most 50): rebuild with them in `build.network.extra_hosts`. */
319
+ denied_hosts: string[];
274
320
  result: {
275
321
  artifact_sha256: string | null;
276
322
  scan: ({
@@ -334,12 +380,21 @@ export interface TemplateRecipe {
334
380
  };
335
381
  }
336
382
  export interface CreateTemplateBuildParams {
337
- /** Organization template slug (created by the first build; platform slugs are refused). */
383
+ /** Organization template slug (created by the first build; platform slugs and the reserved `new`/`edit` are refused). */
338
384
  templateSlug: string;
339
385
  displayName?: string;
340
- recipe: TemplateRecipe;
386
+ /**
387
+ * Recipe v1 (a Dockerfile) or recipe v2 (0.7.0; `schema: "shardflux.template-recipe.v2"`: languages, packages,
388
+ * uploaded files, steps, auto network and settings; contracts §24.1). Recipe v2 files reference uploads
389
+ * (`templates.uploads.put()`); `buildFromFile()` / `buildFromRecipe()` upload local `from` paths for you.
390
+ */
391
+ recipe: TemplateRecipe | TemplateRecipeV2;
341
392
  /** Publish the produced version as soon as it is registered (default true); false leaves it for the owner/admin publish route. */
342
393
  autoPublish?: boolean;
394
+ /** The version description (1..2000 characters; 0.7.0). */
395
+ description?: string;
396
+ /** Recipe v2 only (0.7.0): up to 200 absolute paths the credential scan may report without failing the build. */
397
+ acknowledgedScanFindings?: string[];
343
398
  /** Defaults to a random key so retries never create a second build. */
344
399
  idempotencyKey?: string;
345
400
  }
@@ -359,6 +414,8 @@ export interface WaitForBuildOptions {
359
414
  /** Ask the server to hold each poll until the build changes (`Prefer: wait`, contracts §3; default true). */
360
415
  serverWait?: boolean;
361
416
  signal?: AbortSignal;
417
+ /** Called with each build view the wait observes whose state or registration changed (0.7.0), the settled one included. */
418
+ onChange?: (build: TemplateBuild) => void;
362
419
  }
363
420
  /** Finished from the caller's point of view: failed/canceled/legacy succeeded, or published and its registration settled. */
364
421
  export declare function buildSettled(build: Pick<TemplateBuild, 'state' | 'registration'>): boolean;
@@ -382,6 +439,11 @@ export interface SaveAsTemplateParams {
382
439
  description?: string;
383
440
  /** Defaults of the new version (default: the source version's, else persistent). */
384
441
  defaults?: TemplateDefaultsInput;
442
+ /**
443
+ * Settings of the new version (0.7.0; contracts §24.3): each field given replaces that field of the source version's
444
+ * settings, each field left out is carried forward. `settings.defaults` together with `defaults` is 422 invalid_settings.
445
+ */
446
+ settings?: TemplateSettingsInput;
385
447
  /** A committed checkpoint of this workspace to save instead of its current state. */
386
448
  checkpointId?: string;
387
449
  /** Publish the produced version once registered (default true). */
@@ -420,6 +482,10 @@ export interface TemplateDiffParams {
420
482
  export interface CreateDraftParams {
421
483
  /** `<slug>@<version>`: a published, layered-capable version (default: the template's latest published version; required when it has none). */
422
484
  base?: string;
485
+ /** The template's name when this draft creates the template (0.7.0). */
486
+ displayName?: string;
487
+ /** Text inputs of the base version (0.7.0; `{NAME: value}`, as open() takes them). */
488
+ inputs?: Record<string, string>;
423
489
  caps?: {
424
490
  cpu_millis?: number;
425
491
  memory_mib?: number;
@@ -446,6 +512,8 @@ export interface OpenTestInstanceParams {
446
512
  };
447
513
  agentLabel?: string;
448
514
  tools?: ToolName[];
515
+ /** Text inputs (0.7.0; `{NAME: value}`, as open() takes them). */
516
+ inputs?: Record<string, string>;
449
517
  /** `false`: return at once. Default: wait until the test instance is ready. */
450
518
  wait?: false | WaitOptions;
451
519
  idempotencyKey?: string;
@@ -455,6 +523,8 @@ export interface PublishDraftParams {
455
523
  stateId?: string;
456
524
  description?: string;
457
525
  defaults?: TemplateDefaultsInput;
526
+ /** Settings of the new version (0.7.0): each field given replaces the draft base version's, the rest carries forward. */
527
+ settings?: TemplateSettingsInput;
458
528
  autoPublish?: boolean;
459
529
  acknowledgedScanFindings?: string[];
460
530
  idempotencyKey?: string;
@@ -521,7 +591,12 @@ export declare class TemplateDraftApi {
521
591
  export declare class TemplateBuildsApi {
522
592
  #private;
523
593
  constructor(ctx: () => ClientContext);
524
- /** Queues a build (202). Poll with get()/waitForBuild(); `builder_availability` says whether a builder runs. */
594
+ /**
595
+ * Queues a build (202) of a recipe v1 (Dockerfile) or recipe v2 (0.7.0). Poll with get()/waitForBuild();
596
+ * `builder_availability` says whether a builder runs. A recipe v2 is validated at once: 422 validation_failed with
597
+ * details.reason (invalid_recipe, base_not_layered, language_unavailable, upload_missing, invalid_settings, ...) and
598
+ * details.field (the JSON path). A file entry still carrying `from` is 422 upload_required.
599
+ */
525
600
  create(organizationId: string, params: CreateTemplateBuildParams): Promise<TemplateBuild>;
526
601
  get(organizationId: string, buildId: string): Promise<TemplateBuild>;
527
602
  list(organizationId: string, params?: {
@@ -546,10 +621,244 @@ export declare class TemplateBuildsApi {
546
621
  */
547
622
  waitForBuild(organizationId: string, buildId: string, opts?: WaitForBuildOptions): Promise<TemplateBuild>;
548
623
  }
624
+ /** Bytes to upload: in memory, a Blob (a File in browsers, fs.openAsBlob in Node) or a stream. */
625
+ export type UploadData = Uint8Array | ArrayBuffer | Blob | ReadableStream<Uint8Array>;
626
+ export interface PutUploadOptions {
627
+ /** `file` (copied to `to`) or `tar` (an uncompressed ustar/pax archive of a folder, extracted into `to`). */
628
+ kind: 'file' | 'tar';
629
+ /**
630
+ * The bytes' SHA-256 (lower-case hex) and size, when known. Required to send a stream without holding it in memory;
631
+ * otherwise the SDK reads the data once to hash it.
632
+ */
633
+ sha256?: string;
634
+ size?: number;
635
+ /** Use the organization form of the route (default: the API key's organization, the SDK form). */
636
+ organizationId?: string;
637
+ signal?: AbortSignal;
638
+ }
639
+ /** An upload the organization has: reference it in a recipe v2 file entry as `upload: ref`. */
640
+ export interface TemplateUploadResult {
641
+ upload: TemplateUpload;
642
+ /** `sha256:<hex>`, the recipe's `upload` value. */
643
+ ref: string;
644
+ /** False when the organization already had these bytes (no PUT was sent). */
645
+ uploaded: boolean;
646
+ }
647
+ /** A presigned PUT the storage refused (the URL is a bearer credential and never part of the message). */
648
+ export declare class TemplateUploadError extends Error {
649
+ /** HTTP status of the storage's answer (0: no answer, a network failure). */
650
+ readonly status: number;
651
+ /** The storage's error code: BadDigest (other bytes), SignatureDoesNotMatch (another length or header), ... */
652
+ readonly code: string | null;
653
+ readonly sha256: string;
654
+ constructor(sha256: string, status: number, code: string | null, detail?: string);
655
+ }
656
+ /**
657
+ * Build uploads (contracts §24.2): files and folders a recipe v2 copies into the template, stored once per
658
+ * organization and content (SHA-256). Needs build access (API keys with a tool permission). Uploads count toward the
659
+ * organization's template storage while they exist; one nothing references is deleted 7 days later.
660
+ */
661
+ export declare class TemplateUploadsApi {
662
+ #private;
663
+ constructor(ctx: () => ClientContext);
664
+ /**
665
+ * POST …/template-uploads alone: `status` 200 (the organization has the bytes; `put` null) or 201 (`put`: the
666
+ * presigned PUT, valid 900 s). 422 upload_too_large (over 5 GiB), upload_digest_mismatch (the same SHA-256 with
667
+ * another size); 503 dependency_unavailable (uploads_not_configured).
668
+ */
669
+ request(meta: TemplateUploadRequest, opts?: {
670
+ organizationId?: string;
671
+ signal?: AbortSignal;
672
+ }): Promise<{
673
+ status: number;
674
+ } & TemplateUploadResponse>;
675
+ /**
676
+ * Uploads bytes unless the organization already has them, and returns the `sha256:<hex>` reference for a recipe v2
677
+ * file entry. The PUT carries exactly the presigned headers (the storage checks the SHA-256 and the length). A stream
678
+ * is sent as it is when `sha256` and `size` are given (it cannot be retried); otherwise it is read into memory first.
679
+ * Throws TemplateUploadError when the storage refuses the bytes.
680
+ */
681
+ put(data: UploadData, opts: PutUploadOptions): Promise<TemplateUploadResult>;
682
+ /**
683
+ * Node only: uploads a local file, or a folder packed as the reproducible tar (the same bytes as the Python SDK's
684
+ * for the same folder). `kind` omitted: a folder is `tar`, a file `file`; a file with kind `tar` is a prepared,
685
+ * uncompressed archive.
686
+ */
687
+ putPath(path: string, opts?: {
688
+ kind?: 'file' | 'tar';
689
+ organizationId?: string;
690
+ signal?: AbortSignal;
691
+ }): Promise<TemplateUploadResult & {
692
+ path: string;
693
+ kind: 'file' | 'tar';
694
+ size: number;
695
+ entries: number | null;
696
+ }>;
697
+ }
698
+ /** One distinct local `from` source of a template file (a path used by several entries is one row), uploaded or already there. */
699
+ export interface LocalUpload {
700
+ /** `from` as written in the file (its first entry). */
701
+ from: string;
702
+ /** The resolved local path. */
703
+ path: string;
704
+ /** The destination of its first entry in the template. */
705
+ to: string;
706
+ kind: 'file' | 'tar';
707
+ sha256: string;
708
+ size: number;
709
+ /** Members of a packed folder; null for a file. */
710
+ entries: number | null;
711
+ /** False when the organization already had these bytes. */
712
+ uploaded: boolean;
713
+ }
714
+ export type BuildFromFileEvent = {
715
+ type: 'pack';
716
+ from: string;
717
+ path: string;
718
+ kind: 'file' | 'tar';
719
+ sha256: string;
720
+ size: number;
721
+ entries: number | null;
722
+ ms: number;
723
+ } | {
724
+ type: 'upload';
725
+ from: string;
726
+ sha256: string;
727
+ size: number;
728
+ uploaded: boolean;
729
+ ms: number;
730
+ } | {
731
+ type: 'build';
732
+ build: TemplateBuild;
733
+ };
734
+ export interface BuildFromRecipeOptions {
735
+ /** Organization template to build into (created by the first build). */
736
+ templateSlug: string;
737
+ /** Template name when this build creates the template. */
738
+ displayName?: string;
739
+ /** The version description. */
740
+ description?: string;
741
+ /** Publish the version once registered (API default true); false leaves it unpublished for test instances. */
742
+ autoPublish?: boolean;
743
+ /** Up to 200 absolute paths the credential scan may report without failing the build. */
744
+ acknowledgedScanFindings?: string[];
745
+ /** Default: the API key's organization (GET /v1/me). */
746
+ organizationId?: string;
747
+ /** Directory the `from` paths are relative to (buildFromFile: the template file's directory; default the process's). */
748
+ baseDir?: string;
749
+ /** Refuse every local path (the template file and each `from`) that resolves, after symlinks, outside this directory. */
750
+ root?: string;
751
+ /** Wait until the build settles (true: 30 minutes; or the wait's options). Default: return the queued build. */
752
+ wait?: boolean | WaitForBuildOptions;
753
+ /** Packing, each upload, the created build and each change while waiting. */
754
+ onProgress?: (event: BuildFromFileEvent) => void;
755
+ idempotencyKey?: string;
756
+ signal?: AbortSignal;
757
+ }
758
+ export interface BuildFromFileOptions extends Omit<BuildFromRecipeOptions, 'baseDir'> {
759
+ /** YAML parser (default: the optional `yaml` package's parse). */
760
+ parseYaml?: YamlParser;
761
+ }
762
+ export interface BuildFromFileResult {
763
+ /** The created build, or the settled one with `wait`. */
764
+ build: TemplateBuild;
765
+ /** The recipe v2 as sent (every `from` replaced by its `upload`). */
766
+ recipe: TemplateRecipeV2;
767
+ uploads: LocalUpload[];
768
+ }
769
+ export declare class TemplateVersionsApi {
770
+ #private;
771
+ constructor(ctx: () => ClientContext);
772
+ /**
773
+ * The recipe and settings a version was built from, in request form (`recipe` null for versions saved from a
774
+ * workspace and platform versions). Building the recipe again (same base, uploads still present) gives the same
775
+ * recipe_sha256. Same visibility as the version.
776
+ */
777
+ recipe(slug: string, version: number, params?: {
778
+ owner?: TemplateOwner;
779
+ organizationId?: string;
780
+ }): Promise<TemplateVersionRecipe>;
781
+ }
782
+ export interface CreateVersionTestInstanceParams {
783
+ /** Workspace key (default sf:test:<slug>:<8 hex>); keys starting with `sf:` are reserved. */
784
+ key?: string;
785
+ caps?: Caps;
786
+ agentLabel?: string;
787
+ tools?: ToolName[];
788
+ /** The version's text inputs `{NAME: value}` (422 input_unknown, input_invalid, input_required). */
789
+ inputs?: Record<string, string>;
790
+ /** Browser/session principals only. */
791
+ projectId?: string;
792
+ /** `false`: return at once. Default: wait until the test instance is ready (its startup ran). */
793
+ wait?: false | WaitOptions;
794
+ organizationId?: string;
795
+ idempotencyKey?: string;
796
+ }
797
+ /**
798
+ * Test instances of a version (contracts §24.6): a session workspace on a registered version of the organization's
799
+ * template, published or not, so a build can be tried before it is published. Owners, admins and API keys with a
800
+ * tool permission (403 template_dev_mode_role otherwise).
801
+ */
802
+ export declare class TemplateVersionTestInstancesApi {
803
+ #private;
804
+ constructor(ctx: () => ClientContext);
805
+ /** Opens one (202; waits until ready unless `wait: false`). It ends with close() or when idle. */
806
+ create(slug: string, version: number, params?: CreateVersionTestInstanceParams): Promise<Workspace>;
807
+ }
808
+ /** Package names for the editor's pickers (contracts §24.6): apt (a base's index), pip (names only) and npm. */
809
+ export declare class TemplatePackagesApi {
810
+ #private;
811
+ constructor(ctx: () => ClientContext);
812
+ /**
813
+ * Searches an ecosystem (`query` 1..100 characters; `limit` 1..50, default 20). apt needs `base` (`<slug>@<version>`);
814
+ * a base without an index is 409 package_index_unavailable. pip returns names only (fetch one package for versions).
815
+ * 503 dependency_unavailable (package_search_unavailable, package_index_loading) and 429 are retryable.
816
+ */
817
+ search(ecosystem: TemplatePackageEcosystem, query: string, params?: {
818
+ base?: string;
819
+ limit?: number;
820
+ organizationId?: string;
821
+ }): Promise<TemplatePackagePage>;
822
+ /** One package: its latest version, summary and known versions (404 package_not_found). */
823
+ get(ecosystem: TemplatePackageEcosystem, name: string, params?: {
824
+ base?: string;
825
+ organizationId?: string;
826
+ }): Promise<TemplatePackage>;
827
+ }
549
828
  export declare class TemplatesApi {
550
829
  #private;
551
830
  readonly builds: TemplateBuildsApi;
831
+ /** Build uploads (0.7.0): files and folders for recipe v2. */
832
+ readonly uploads: TemplateUploadsApi;
833
+ /** Recipe export of a version (0.7.0). */
834
+ readonly versions: TemplateVersionsApi;
835
+ /** Test instances of a registered version, published or not (0.7.0). */
836
+ readonly versionTestInstances: TemplateVersionTestInstancesApi;
837
+ /** Package search for recipes (0.7.0). */
838
+ readonly packages: TemplatePackagesApi;
552
839
  constructor(ctx: () => ClientContext);
840
+ /**
841
+ * The languages `base` (`<slug>@<version>`) offers a recipe v2's `build.languages` (0.7.0): the platform's table for
842
+ * the chain's platform base, in table order. `included`: the base already has that version (nothing is installed and
843
+ * no host is needed); a version the base has another version of is left out (a build would refuse it with
844
+ * language_conflict). `hosts` and `apt` are what the language adds to an `auto` build network. An unknown or
845
+ * archived base is 422 validation_failed with details.field `base`. Build access (API keys with a tool permission).
846
+ */
847
+ languages(base: string, params?: {
848
+ organizationId?: string;
849
+ }): Promise<TemplateLanguages>;
850
+ /**
851
+ * Node only: builds a template from template.yaml (or a .json file with the same document; contracts §24.1). The
852
+ * file is recipe v2; each `build.files[]` entry may name a local `from` path (relative to the file): folders are
853
+ * packed as the reproducible tar, files are sent as they are, both uploaded unless the organization already has
854
+ * them, and the build is created (and awaited with `wait`). YAML needs the optional `yaml` package or `parseYaml`.
855
+ */
856
+ buildFromFile(file: string, opts: BuildFromFileOptions): Promise<BuildFromFileResult>;
857
+ /**
858
+ * Builds a recipe v2 document whose file entries may name local `from` paths (Node only when one does): the same
859
+ * as buildFromFile() for a document already in memory, `from` relative to `baseDir`.
860
+ */
861
+ buildFromRecipe(recipe: TemplateRecipeV2 | Record<string, unknown>, opts: BuildFromRecipeOptions): Promise<BuildFromFileResult>;
553
862
  /** Templates the API key's organization can use (platform + its own), with the version `open` picks. */
554
863
  list(params?: {
555
864
  organizationId?: string;