@simantic/cloud-sim 0.1.0 → 0.3.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.
package/README.md CHANGED
@@ -9,7 +9,7 @@ after.
9
9
  The package is ESM with no dependencies, built on `fetch`, so it runs on
10
10
  Node 18 and later, in browsers, and on Deno, Bun and edge runtimes. Its
11
11
  wire types are generated from the server's OpenAPI document
12
- (`openapi/v1.json` in the cloud-sim repo), so they change only when the
12
+ (https://cloud.simantic.com/v1/openapi.json), so they change only when the
13
13
  contract does.
14
14
 
15
15
  ## Install
@@ -18,10 +18,8 @@ contract does.
18
18
  npm install @simantic/cloud-sim
19
19
  ```
20
20
 
21
- The package is private to the `simantic` npm organization for now: installing
22
- it needs an npm token of an org member (`npm login`, or `NPM_TOKEN` on CI and
23
- Vercel, which writes the `.npmrc` itself). The API it calls is open to any
24
- simantic account; its contract is at https://cloud.simantic.com/v1/openapi.json.
21
+ The API it calls is open to any simantic account; its contract is at
22
+ https://cloud.simantic.com/v1/openapi.json.
25
23
 
26
24
  ## Quick start
27
25
 
@@ -71,6 +69,7 @@ const run = await sim.runs.create({ mcu: 'STM32F072RB', firmware: input.files![0
71
69
  new CloudSim({
72
70
  apiKey, // an smtc_ API key or a dashboard session token, or a function giving one
73
71
  version, // Cloud-Sim-Version, such as '1.0'; the server's newest stable without it
72
+ client, // your app or tool, such as 'my-tool/1.2', named on the runs it makes
74
73
  baseUrl, // default https://cloud.simantic.com
75
74
  fetch, // default the global fetch
76
75
  onUnauthorized, // called once on a 401, before the request is sent again
@@ -85,6 +84,14 @@ release, so results do not change under you when the server's default
85
84
  moves on. A call that makes runs or reads a catalog can name another
86
85
  version for itself (`version`), so one client can serve a version picker.
87
86
 
87
+ Every run the client makes names it in `Cloud-Sim-Client`, and keeps
88
+ that as its `client`: `cloud-sim-ts/<version>` (the SDK's version, also
89
+ exported as `SDK_VERSION`), after `client` when it is given, so a run made
90
+ by `my-tool` reads `my-tool/1.2 cloud-sim-ts/0.3.0`. `client` is
91
+ `product/version` tokens separated by single spaces, in printable ASCII;
92
+ the constructor throws a `TypeError` for anything else. Only creates
93
+ carry the header.
94
+
88
95
  Every call takes an optional `signal`. Aborting it ends the call, its
89
96
  retries and the waits between them, and the call throws the signal's
90
97
  reason.
@@ -100,15 +107,38 @@ sim.runs.result(id, { signal? }) // { exit_code, output, log, truncated } of a
100
107
  sim.runs.output(id, { signal? }) // the UART output, as text
101
108
  sim.runs.log(id, { signal? }) // the engine's console, as text
102
109
  sim.runs.cancel(id, { signal? })
110
+ sim.runs.firmware(id, { signal? }) // the stored ELF, as a Uint8Array
111
+ sim.runs.deleteFirmware(id, { signal? }) // the run, its firmware no longer stored
103
112
  sim.runs.wait(id, { signal?, intervalMs? })
104
113
  sim.runs.follow(id, { signal?, transport?, intervalMs?, from? })
105
114
  sim.runs.compare({ engines, mcu, firmware, ...options, version?, timeoutMs?, signal? })
106
115
  ```
107
116
 
108
117
  `firmware` is a `Blob` (a `File` included), a `Uint8Array`, an
109
- `ArrayBuffer`, `{ url }` or `{ base64 }`. Bytes are uploaded as
118
+ `ArrayBuffer`, `{ url }`, `{ base64 }` or `{ run }`. Bytes are uploaded as
110
119
  multipart/form-data; a URL is fetched by the server (https only, at most
111
- 24 MiB); base64 goes in JSON.
120
+ 24 MiB); base64 goes in JSON; `run` is the id of one of your runs whose
121
+ firmware the server stores, which this run runs again.
122
+
123
+ ### Stored firmware
124
+
125
+ The server keeps each run's ELF, encrypted, after the run
126
+ (`run.firmware.stored`), so you can download it and run it again, with
127
+ other options or on a newer version:
128
+
129
+ ```ts
130
+ const elf = await sim.runs.firmware(run.id)
131
+ const again = await sim.runs.create({ mcu: run.mcu, firmware: { run: run.id }, simulatedSeconds: 30 })
132
+ await sim.runs.deleteFirmware(run.id) // gone; firmware.deleted_at says when
133
+ ```
134
+
135
+ Storage is on unless you turn it off, which deletes every run's stored
136
+ firmware:
137
+
138
+ ```ts
139
+ await sim.settings.get() // { store_firmware, stored_firmware: { runs, bytes } }
140
+ await sim.settings.update({ storeFirmware: false })
141
+ ```
112
142
 
113
143
  Every create carries an `Idempotency-Key`, generated when you give none, so
114
144
  a create the SDK retries after a lost answer gets the run it already made
package/dist/client.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Mcus, Versions } from './catalog.js';
2
2
  import { type ApiKey, type RetryInfo } from './http.js';
3
3
  import { Runs } from './runs.js';
4
+ import { Settings } from './settings.js';
4
5
  export interface CloudSimOptions {
5
6
  /**
6
7
  * An `smtc_` API key, or a simantic dashboard session's access token. A
@@ -14,6 +15,14 @@ export interface CloudSimOptions {
14
15
  * stable version, which can change under a long-lived client.
15
16
  */
16
17
  version?: string;
18
+ /**
19
+ * The app or tool built on the SDK, as `product/version` (several
20
+ * separated by spaces), such as `my-tool/1.2`. A run it makes keeps it,
21
+ * followed by the SDK's own `cloud-sim-ts/<version>`, as its `client`
22
+ * (`Cloud-Sim-Client`); without it, the run names the SDK alone. Printable
23
+ * ASCII; the constructor throws a TypeError for anything else.
24
+ */
25
+ client?: string;
17
26
  /** Default `https://cloud.simantic.com`. */
18
27
  baseUrl?: string;
19
28
  /** Default the global fetch. */
@@ -49,5 +58,6 @@ export declare class CloudSim {
49
58
  readonly runs: Runs;
50
59
  readonly versions: Versions;
51
60
  readonly mcus: Mcus;
61
+ readonly settings: Settings;
52
62
  constructor(options: CloudSimOptions);
53
63
  }
package/dist/client.js CHANGED
@@ -1,15 +1,18 @@
1
1
  import { Mcus, Versions } from './catalog.js';
2
2
  import { Transport } from './http.js';
3
3
  import { Runs } from './runs.js';
4
+ import { Settings } from './settings.js';
4
5
  /** A client of the Cloud Sim API. */
5
6
  export class CloudSim {
6
7
  runs;
7
8
  versions;
8
9
  mcus;
10
+ settings;
9
11
  constructor(options) {
10
12
  const http = new Transport(options);
11
13
  this.runs = new Runs(http);
12
14
  this.versions = new Versions(http);
13
15
  this.mcus = new Mcus(http);
16
+ this.settings = new Settings(http);
14
17
  }
15
18
  }
@@ -86,6 +86,24 @@ export interface paths {
86
86
  patch?: never;
87
87
  trace?: never;
88
88
  };
89
+ "/v1/runs/{id}/firmware": {
90
+ parameters: {
91
+ query?: never;
92
+ header?: never;
93
+ path?: never;
94
+ cookie?: never;
95
+ };
96
+ /** @description The ELF the run simulated, as it was given, while the server stores it (`firmware.stored`): from the run's creation until its owner deletes it or turns firmware storage off. */
97
+ get: operations["firmware"];
98
+ put?: never;
99
+ post?: never;
100
+ /** @description Deletes the run's stored firmware: at once, or for a run that has not ended, when it does (it runs on it until then). Either way the run answers with `firmware.stored` false from now on, and `firmware.deleted_at` says when. A run whose firmware is not stored answers as it is. */
101
+ delete: operations["delete_firmware"];
102
+ options?: never;
103
+ head?: never;
104
+ patch?: never;
105
+ trace?: never;
106
+ };
89
107
  "/v1/runs/{id}/live": {
90
108
  parameters: {
91
109
  query?: never;
@@ -153,6 +171,23 @@ export interface paths {
153
171
  patch?: never;
154
172
  trace?: never;
155
173
  };
174
+ "/v1/settings": {
175
+ parameters: {
176
+ query?: never;
177
+ header?: never;
178
+ path?: never;
179
+ cookie?: never;
180
+ };
181
+ get: operations["get_settings"];
182
+ put?: never;
183
+ post?: never;
184
+ delete?: never;
185
+ options?: never;
186
+ head?: never;
187
+ /** @description Changes the settings given, and answers with all of them. Turning `store_firmware` off deletes every run's stored firmware. */
188
+ patch: operations["update_settings"];
189
+ trace?: never;
190
+ };
156
191
  "/v1/versions": {
157
192
  parameters: {
158
193
  query?: never;
@@ -279,6 +314,11 @@ export interface components {
279
314
  /** @description Repeat the field for each name. */
280
315
  usb_cdc_consoles?: string[] | null;
281
316
  };
317
+ /**
318
+ * Format: binary
319
+ * @description An ELF file, as `GET /v1/runs/{id}/firmware` answers it.
320
+ */
321
+ ElfFile: string;
282
322
  /** @description An engine as a version pins it. */
283
323
  EngineView: {
284
324
  build: components["schemas"]["Build"];
@@ -313,15 +353,20 @@ export interface components {
313
353
  * version), `engine_unknown`, `engine_not_in_version`,
314
354
  * `mcu_not_supported`, `mcu_not_runnable`, `option_not_supported`,
315
355
  * `firmware_missing`, `firmware_invalid`, `firmware_url_refused`,
316
- * `firmware_fetch_failed`, `invalid_request`;
317
- * - 401 `unauthorized`; 403 `forbidden`;
318
- * - 404 `run_not_found`, `version_unknown` (in the path), `not_found`;
356
+ * `firmware_fetch_failed`, `firmware_not_stored` (`firmware.run`
357
+ * names no run of yours whose firmware is stored),
358
+ * `invalid_request`;
359
+ * - 401 `unauthorized`; 403 `forbidden`, `guest_firmware_only` (the
360
+ * demo server runs only its examples, uploaded);
361
+ * - 404 `run_not_found`, `firmware_not_stored` (the run's firmware is
362
+ * not stored), `version_unknown` (in the path), `not_found`;
319
363
  * - 409 `idempotency_key_reused`, `run_not_finished`, `run_finished`;
320
364
  * - 410 `version_retired`;
321
365
  * - 413 `firmware_too_large`, `payload_too_large`;
322
366
  * - 415 `unsupported_media_type`;
323
367
  * - 429 `too_many_runs` (the caller's unfinished runs are at the
324
- * limit; `Retry-After` says when to look again);
368
+ * limit; `Retry-After` says when to look again), `demo_busy` (the
369
+ * demo server's queue is full);
325
370
  * - 503 `unavailable` (a service the API relies on; `Retry-After`).
326
371
  */
327
372
  code: string;
@@ -334,6 +379,17 @@ export interface components {
334
379
  };
335
380
  /** @description The firmware a run simulates. */
336
381
  Firmware: {
382
+ /**
383
+ * Format: date-time
384
+ * @description When its owner deleted the ELF, or turned storage off while the
385
+ * server kept it.
386
+ */
387
+ deleted_at: string | null;
388
+ /**
389
+ * Format: uuid
390
+ * @description The run whose stored firmware it ran again, for `run`.
391
+ */
392
+ run: string | null;
337
393
  /** @description Hex SHA-256 of the ELF. */
338
394
  sha256: string;
339
395
  /**
@@ -342,13 +398,27 @@ export interface components {
342
398
  */
343
399
  size: number;
344
400
  source: components["schemas"]["FirmwareSource"];
401
+ /**
402
+ * @description Whether the server keeps the ELF, encrypted, after the run: `GET
403
+ * /v1/runs/{id}/firmware` downloads it, and a run with `firmware:
404
+ * {"run": id}` runs it again. It does unless its owner turned firmware
405
+ * storage off (`PATCH /v1/settings`) or deleted it (`DELETE
406
+ * /v1/runs/{id}/firmware`), or the run is from before storage.
407
+ */
408
+ stored: boolean;
345
409
  /** @description The URL it was fetched from, for `url`. */
346
410
  url: string | null;
347
411
  };
348
- /** @description The firmware: exactly one of `base64` and `url`. */
412
+ /** @description The firmware: exactly one of `base64`, `url` and `run`. */
349
413
  FirmwareInput: {
350
414
  /** @description The ELF, base64-encoded (standard alphabet, padded). */
351
415
  base64?: string | null;
416
+ /**
417
+ * Format: uuid
418
+ * @description The id of one of your runs whose firmware the server stores
419
+ * (`firmware.stored`): this run runs that ELF again.
420
+ */
421
+ run?: string | null;
352
422
  /**
353
423
  * @description An https URL the server fetches the ELF from when the run is
354
424
  * submitted (at most 24 MiB, within 20 seconds; never a private
@@ -357,11 +427,11 @@ export interface components {
357
427
  url?: string | null;
358
428
  };
359
429
  /**
360
- * @description How a run's firmware came: a multipart file part, base64 in JSON, or
361
- * fetched by the server from a URL.
430
+ * @description How a run's firmware came: a multipart file part, base64 in JSON,
431
+ * fetched by the server from a URL, or another run's stored firmware.
362
432
  * @enum {string}
363
433
  */
364
- FirmwareSource: "upload" | "base64" | "url";
434
+ FirmwareSource: "upload" | "base64" | "url" | "run";
365
435
  /**
366
436
  * @description Where a version is in its life (index.toml `status`).
367
437
  * @enum {string}
@@ -475,6 +545,14 @@ export interface components {
475
545
  * @description When its owner asked to cancel it, while it is being stopped.
476
546
  */
477
547
  cancel_requested_at: string | null;
548
+ /**
549
+ * @description The client it was made with, as the `Cloud-Sim-Client` header of the
550
+ * request that made it named it: `cloud-sim-cli/0.1.0`, say, or
551
+ * `cloud-sim-mcp/0.2.1 cloud-sim-ts/0.2.1` for one client built on
552
+ * another. Null when that request named none, and for a run submitted
553
+ * through the legacy /jobs API.
554
+ */
555
+ client: string | null;
478
556
  /** Format: date-time */
479
557
  created_at: string;
480
558
  engine: null | components["schemas"]["RunEngine"];
@@ -556,16 +634,52 @@ export interface components {
556
634
  * @enum {string}
557
635
  */
558
636
  RunStatus: "queued" | "starting" | "running" | "succeeded" | "failed" | "canceled";
637
+ /** @description The caller's settings. */
638
+ Settings: {
639
+ /**
640
+ * @description Whether the server keeps each run's firmware after the run, so it
641
+ * can be downloaded and run again. On unless turned off. Off, a run's
642
+ * firmware is deleted when the run ends.
643
+ */
644
+ store_firmware: boolean;
645
+ /** @description What the server keeps now. */
646
+ stored_firmware: components["schemas"]["StoredFirmware"];
647
+ };
559
648
  StatusChange: {
560
649
  from: string;
561
650
  mcu: string;
562
651
  to: string;
563
652
  };
653
+ /** @description The firmware the server keeps for the caller. */
654
+ StoredFirmware: {
655
+ /**
656
+ * Format: int64
657
+ * @description Their size in all, in bytes.
658
+ */
659
+ bytes: number;
660
+ /**
661
+ * Format: int64
662
+ * @description How many runs' firmware.
663
+ */
664
+ runs: number;
665
+ };
564
666
  /**
565
667
  * @description How UART bytes are written: hex rows (every engine's default) or text.
566
668
  * @enum {string}
567
669
  */
568
670
  UartEncoding: "hex" | "ascii";
671
+ /**
672
+ * @description `PATCH /v1/settings`: the settings to change. One left out stays as it
673
+ * is.
674
+ */
675
+ UpdateSettings: {
676
+ /**
677
+ * @description Turning it off deletes every run's stored firmware, at once (or, for
678
+ * a run that has not ended, when it does), and keeps none from then
679
+ * on. Turning it on keeps the firmware of runs that end from then on.
680
+ */
681
+ store_firmware?: boolean | null;
682
+ };
569
683
  /** @description Every version this server serves. */
570
684
  VersionList: {
571
685
  /** @description What a request without `Cloud-Sim-Version` gets. */
@@ -766,11 +880,13 @@ export interface operations {
766
880
  "Cloud-Sim-Version"?: string | null;
767
881
  /** @description Up to 255 printable ASCII characters: the same request again with it gets the same run. */
768
882
  "Idempotency-Key"?: string | null;
883
+ /** @description The client making the run, as `product/version` tokens separated by spaces (`cloud-sim-mcp/0.2.1 cloud-sim-ts/0.2.1`): the run keeps it as `client`. Up to 200 printable ASCII characters; any other value is left out, not refused. */
884
+ "Cloud-Sim-Client"?: string | null;
769
885
  };
770
886
  path?: never;
771
887
  cookie?: never;
772
888
  };
773
- /** @description The run: JSON (firmware as base64 or a URL) or multipart/form-data (firmware as the file part `firmware`). */
889
+ /** @description The run: JSON (firmware as base64, a URL, or a run whose stored firmware to run again) or multipart/form-data (firmware as the file part `firmware`). */
774
890
  requestBody: {
775
891
  content: {
776
892
  "application/json": components["schemas"]["CreateRun"];
@@ -1281,6 +1397,203 @@ export interface operations {
1281
1397
  };
1282
1398
  };
1283
1399
  };
1400
+ firmware: {
1401
+ parameters: {
1402
+ query?: never;
1403
+ header?: never;
1404
+ path: {
1405
+ id: string;
1406
+ };
1407
+ cookie?: never;
1408
+ };
1409
+ requestBody?: never;
1410
+ responses: {
1411
+ 200: {
1412
+ headers: {
1413
+ /** @description The API version that answered. */
1414
+ "Cloud-Sim-Version"?: string;
1415
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1416
+ Deprecation?: string;
1417
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1418
+ Sunset?: string;
1419
+ /** @description The request's id, which an error's `request_id` repeats. */
1420
+ "X-Request-Id"?: string;
1421
+ [name: string]: unknown;
1422
+ };
1423
+ content: {
1424
+ "application/octet-stream": components["schemas"]["ElfFile"];
1425
+ };
1426
+ };
1427
+ /** @description The request is not one the API takes (see `code`). */
1428
+ 400: {
1429
+ headers: {
1430
+ /** @description The API version that answered. */
1431
+ "Cloud-Sim-Version"?: string;
1432
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1433
+ Deprecation?: string;
1434
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1435
+ Sunset?: string;
1436
+ /** @description The request's id, which an error's `request_id` repeats. */
1437
+ "X-Request-Id"?: string;
1438
+ [name: string]: unknown;
1439
+ };
1440
+ content: {
1441
+ "application/json": components["schemas"]["Error"];
1442
+ };
1443
+ };
1444
+ /** @description No valid credential. */
1445
+ 401: {
1446
+ headers: {
1447
+ /** @description The API version that answered. */
1448
+ "Cloud-Sim-Version"?: string;
1449
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1450
+ Deprecation?: string;
1451
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1452
+ Sunset?: string;
1453
+ /** @description The request's id, which an error's `request_id` repeats. */
1454
+ "X-Request-Id"?: string;
1455
+ [name: string]: unknown;
1456
+ };
1457
+ content: {
1458
+ "application/json": components["schemas"]["Error"];
1459
+ };
1460
+ };
1461
+ /** @description No such run (`run_not_found`), or its firmware is not stored (`firmware_not_stored`). */
1462
+ 404: {
1463
+ headers: {
1464
+ /** @description The API version that answered. */
1465
+ "Cloud-Sim-Version"?: string;
1466
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1467
+ Deprecation?: string;
1468
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1469
+ Sunset?: string;
1470
+ /** @description The request's id, which an error's `request_id` repeats. */
1471
+ "X-Request-Id"?: string;
1472
+ [name: string]: unknown;
1473
+ };
1474
+ content: {
1475
+ "application/json": components["schemas"]["Error"];
1476
+ };
1477
+ };
1478
+ /** @description A service the API relies on is unavailable. */
1479
+ 503: {
1480
+ headers: {
1481
+ /** @description The API version that answered. */
1482
+ "Cloud-Sim-Version"?: string;
1483
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1484
+ Deprecation?: string;
1485
+ /** @description Seconds to wait before trying again. */
1486
+ "Retry-After"?: string;
1487
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1488
+ Sunset?: string;
1489
+ /** @description The request's id, which an error's `request_id` repeats. */
1490
+ "X-Request-Id"?: string;
1491
+ [name: string]: unknown;
1492
+ };
1493
+ content: {
1494
+ "application/json": components["schemas"]["Error"];
1495
+ };
1496
+ };
1497
+ };
1498
+ };
1499
+ delete_firmware: {
1500
+ parameters: {
1501
+ query?: never;
1502
+ header?: never;
1503
+ path: {
1504
+ id: string;
1505
+ };
1506
+ cookie?: never;
1507
+ };
1508
+ requestBody?: never;
1509
+ responses: {
1510
+ 200: {
1511
+ headers: {
1512
+ /** @description The API version that answered. */
1513
+ "Cloud-Sim-Version"?: string;
1514
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1515
+ Deprecation?: string;
1516
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1517
+ Sunset?: string;
1518
+ /** @description The request's id, which an error's `request_id` repeats. */
1519
+ "X-Request-Id"?: string;
1520
+ [name: string]: unknown;
1521
+ };
1522
+ content: {
1523
+ "application/json": components["schemas"]["Run"];
1524
+ };
1525
+ };
1526
+ /** @description The request is not one the API takes (see `code`). */
1527
+ 400: {
1528
+ headers: {
1529
+ /** @description The API version that answered. */
1530
+ "Cloud-Sim-Version"?: string;
1531
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1532
+ Deprecation?: string;
1533
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1534
+ Sunset?: string;
1535
+ /** @description The request's id, which an error's `request_id` repeats. */
1536
+ "X-Request-Id"?: string;
1537
+ [name: string]: unknown;
1538
+ };
1539
+ content: {
1540
+ "application/json": components["schemas"]["Error"];
1541
+ };
1542
+ };
1543
+ /** @description No valid credential. */
1544
+ 401: {
1545
+ headers: {
1546
+ /** @description The API version that answered. */
1547
+ "Cloud-Sim-Version"?: string;
1548
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1549
+ Deprecation?: string;
1550
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1551
+ Sunset?: string;
1552
+ /** @description The request's id, which an error's `request_id` repeats. */
1553
+ "X-Request-Id"?: string;
1554
+ [name: string]: unknown;
1555
+ };
1556
+ content: {
1557
+ "application/json": components["schemas"]["Error"];
1558
+ };
1559
+ };
1560
+ 404: {
1561
+ headers: {
1562
+ /** @description The API version that answered. */
1563
+ "Cloud-Sim-Version"?: string;
1564
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1565
+ Deprecation?: string;
1566
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1567
+ Sunset?: string;
1568
+ /** @description The request's id, which an error's `request_id` repeats. */
1569
+ "X-Request-Id"?: string;
1570
+ [name: string]: unknown;
1571
+ };
1572
+ content: {
1573
+ "application/json": components["schemas"]["Error"];
1574
+ };
1575
+ };
1576
+ /** @description A service the API relies on is unavailable. */
1577
+ 503: {
1578
+ headers: {
1579
+ /** @description The API version that answered. */
1580
+ "Cloud-Sim-Version"?: string;
1581
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
1582
+ Deprecation?: string;
1583
+ /** @description Seconds to wait before trying again. */
1584
+ "Retry-After"?: string;
1585
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
1586
+ Sunset?: string;
1587
+ /** @description The request's id, which an error's `request_id` repeats. */
1588
+ "X-Request-Id"?: string;
1589
+ [name: string]: unknown;
1590
+ };
1591
+ content: {
1592
+ "application/json": components["schemas"]["Error"];
1593
+ };
1594
+ };
1595
+ };
1596
+ };
1284
1597
  live: {
1285
1598
  parameters: {
1286
1599
  query?: {
@@ -1726,6 +2039,170 @@ export interface operations {
1726
2039
  };
1727
2040
  };
1728
2041
  };
2042
+ get_settings: {
2043
+ parameters: {
2044
+ query?: never;
2045
+ header?: never;
2046
+ path?: never;
2047
+ cookie?: never;
2048
+ };
2049
+ requestBody?: never;
2050
+ responses: {
2051
+ 200: {
2052
+ headers: {
2053
+ /** @description The API version that answered. */
2054
+ "Cloud-Sim-Version"?: string;
2055
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
2056
+ Deprecation?: string;
2057
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
2058
+ Sunset?: string;
2059
+ /** @description The request's id, which an error's `request_id` repeats. */
2060
+ "X-Request-Id"?: string;
2061
+ [name: string]: unknown;
2062
+ };
2063
+ content: {
2064
+ "application/json": components["schemas"]["Settings"];
2065
+ };
2066
+ };
2067
+ /** @description The request is not one the API takes (see `code`). */
2068
+ 400: {
2069
+ headers: {
2070
+ /** @description The API version that answered. */
2071
+ "Cloud-Sim-Version"?: string;
2072
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
2073
+ Deprecation?: string;
2074
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
2075
+ Sunset?: string;
2076
+ /** @description The request's id, which an error's `request_id` repeats. */
2077
+ "X-Request-Id"?: string;
2078
+ [name: string]: unknown;
2079
+ };
2080
+ content: {
2081
+ "application/json": components["schemas"]["Error"];
2082
+ };
2083
+ };
2084
+ /** @description No valid credential. */
2085
+ 401: {
2086
+ headers: {
2087
+ /** @description The API version that answered. */
2088
+ "Cloud-Sim-Version"?: string;
2089
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
2090
+ Deprecation?: string;
2091
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
2092
+ Sunset?: string;
2093
+ /** @description The request's id, which an error's `request_id` repeats. */
2094
+ "X-Request-Id"?: string;
2095
+ [name: string]: unknown;
2096
+ };
2097
+ content: {
2098
+ "application/json": components["schemas"]["Error"];
2099
+ };
2100
+ };
2101
+ /** @description A service the API relies on is unavailable. */
2102
+ 503: {
2103
+ headers: {
2104
+ /** @description The API version that answered. */
2105
+ "Cloud-Sim-Version"?: string;
2106
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
2107
+ Deprecation?: string;
2108
+ /** @description Seconds to wait before trying again. */
2109
+ "Retry-After"?: string;
2110
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
2111
+ Sunset?: string;
2112
+ /** @description The request's id, which an error's `request_id` repeats. */
2113
+ "X-Request-Id"?: string;
2114
+ [name: string]: unknown;
2115
+ };
2116
+ content: {
2117
+ "application/json": components["schemas"]["Error"];
2118
+ };
2119
+ };
2120
+ };
2121
+ };
2122
+ update_settings: {
2123
+ parameters: {
2124
+ query?: never;
2125
+ header?: never;
2126
+ path?: never;
2127
+ cookie?: never;
2128
+ };
2129
+ requestBody: {
2130
+ content: {
2131
+ "application/json": components["schemas"]["UpdateSettings"];
2132
+ };
2133
+ };
2134
+ responses: {
2135
+ 200: {
2136
+ headers: {
2137
+ /** @description The API version that answered. */
2138
+ "Cloud-Sim-Version"?: string;
2139
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
2140
+ Deprecation?: string;
2141
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
2142
+ Sunset?: string;
2143
+ /** @description The request's id, which an error's `request_id` repeats. */
2144
+ "X-Request-Id"?: string;
2145
+ [name: string]: unknown;
2146
+ };
2147
+ content: {
2148
+ "application/json": components["schemas"]["Settings"];
2149
+ };
2150
+ };
2151
+ /** @description The request is not one the API takes (see `code`). */
2152
+ 400: {
2153
+ headers: {
2154
+ /** @description The API version that answered. */
2155
+ "Cloud-Sim-Version"?: string;
2156
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
2157
+ Deprecation?: string;
2158
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
2159
+ Sunset?: string;
2160
+ /** @description The request's id, which an error's `request_id` repeats. */
2161
+ "X-Request-Id"?: string;
2162
+ [name: string]: unknown;
2163
+ };
2164
+ content: {
2165
+ "application/json": components["schemas"]["Error"];
2166
+ };
2167
+ };
2168
+ /** @description No valid credential. */
2169
+ 401: {
2170
+ headers: {
2171
+ /** @description The API version that answered. */
2172
+ "Cloud-Sim-Version"?: string;
2173
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
2174
+ Deprecation?: string;
2175
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
2176
+ Sunset?: string;
2177
+ /** @description The request's id, which an error's `request_id` repeats. */
2178
+ "X-Request-Id"?: string;
2179
+ [name: string]: unknown;
2180
+ };
2181
+ content: {
2182
+ "application/json": components["schemas"]["Error"];
2183
+ };
2184
+ };
2185
+ /** @description A service the API relies on is unavailable. */
2186
+ 503: {
2187
+ headers: {
2188
+ /** @description The API version that answered. */
2189
+ "Cloud-Sim-Version"?: string;
2190
+ /** @description For a deprecated version: `@` and the Unix time it was deprecated (RFC 9745). */
2191
+ Deprecation?: string;
2192
+ /** @description Seconds to wait before trying again. */
2193
+ "Retry-After"?: string;
2194
+ /** @description For a deprecated version with a sunset date: when it retires (RFC 8594). */
2195
+ Sunset?: string;
2196
+ /** @description The request's id, which an error's `request_id` repeats. */
2197
+ "X-Request-Id"?: string;
2198
+ [name: string]: unknown;
2199
+ };
2200
+ content: {
2201
+ "application/json": components["schemas"]["Error"];
2202
+ };
2203
+ };
2204
+ };
2205
+ };
1729
2206
  versions: {
1730
2207
  parameters: {
1731
2208
  query?: never;
package/dist/http.d.ts CHANGED
@@ -13,6 +13,7 @@ export interface RetryInfo {
13
13
  export interface TransportOptions {
14
14
  apiKey: ApiKey;
15
15
  version?: string;
16
+ client?: string;
16
17
  baseUrl?: string;
17
18
  fetch?: typeof fetch;
18
19
  onUnauthorized?: () => Promise<void> | void;
@@ -22,7 +23,7 @@ export interface TransportOptions {
22
23
  }
23
24
  export type Query = Record<string, string | number | boolean | undefined>;
24
25
  export interface Call {
25
- method: 'GET' | 'POST';
26
+ method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
26
27
  path: string;
27
28
  query?: Query;
28
29
  /** A JSON body. */
@@ -40,13 +41,16 @@ export interface Call {
40
41
  /**
41
42
  * Whether a transient failure may be sent again: true for reads, for a
42
43
  * create that carries an Idempotency-Key, which the server answers with
43
- * the run it already made, and for a cancel, which it answers with the
44
- * run as it is.
44
+ * the run it already made, for a cancel or a firmware deletion, which it
45
+ * answers with the run as it is, and for a settings change, which sets
46
+ * what it sets again.
45
47
  */
46
48
  retry: boolean;
47
49
  }
48
50
  export declare class Transport {
49
51
  readonly version: string | undefined;
52
+ /** `Cloud-Sim-Client`, as a create sends it. */
53
+ readonly client: string;
50
54
  readonly baseUrl: string;
51
55
  /** How many times a transient failure is tried again. */
52
56
  readonly maxRetries: number;
@@ -60,6 +64,8 @@ export declare class Transport {
60
64
  json<T>(call: Call): Promise<T>;
61
65
  /** The answer as text. */
62
66
  text(call: Call): Promise<string>;
67
+ /** The answer's bytes. */
68
+ bytes(call: Call): Promise<Uint8Array>;
63
69
  /**
64
70
  * The answer itself, its body unread (an event stream). Aborting the
65
71
  * call's signal later still ends the body.
package/dist/http.js CHANGED
@@ -2,6 +2,7 @@
2
2
  // version header, a time limit on the answer, retries of what is safe to
3
3
  // retry, and the server's error body turned into a CloudSimError.
4
4
  import { CloudSimError, generalCode } from './errors.js';
5
+ import { SDK_VERSION } from './version.js';
5
6
  export const DEFAULT_BASE_URL = 'https://cloud.simantic.com';
6
7
  const DEFAULT_MAX_RETRIES = 3;
7
8
  // The CLI's and the dashboard's own limit: a submit can take a while, as the
@@ -14,8 +15,14 @@ const MAX_BACKOFF_MS = 8_000;
14
15
  // A Retry-After longer than this is not waited out: a call that would sit
15
16
  // for minutes is the caller's to decide on, so it fails with the error.
16
17
  const MAX_RETRY_AFTER_MS = 60_000;
18
+ // The SDK as `Cloud-Sim-Client` names it, after any client built on it.
19
+ const SDK_CLIENT = `cloud-sim-ts/${SDK_VERSION}`;
20
+ // The longest `Cloud-Sim-Client` the server keeps; it drops a longer one.
21
+ const MAX_CLIENT_LENGTH = 200;
17
22
  export class Transport {
18
23
  version;
24
+ /** `Cloud-Sim-Client`, as a create sends it. */
25
+ client;
19
26
  baseUrl;
20
27
  /** How many times a transient failure is tried again. */
21
28
  maxRetries;
@@ -27,6 +34,7 @@ export class Transport {
27
34
  constructor(options) {
28
35
  this.apiKey = options.apiKey;
29
36
  this.version = options.version;
37
+ this.client = clientHeader(options.client);
30
38
  this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
31
39
  // Called as a plain function: a browser's fetch throws "Illegal
32
40
  // invocation" when called as a method of anything but the window.
@@ -54,6 +62,13 @@ export class Transport {
54
62
  const headers = { Accept: 'text/plain', ...call.headers };
55
63
  return this.request({ ...call, headers }, (response) => response.text());
56
64
  }
65
+ /** The answer's bytes. */
66
+ bytes(call) {
67
+ const headers = { Accept: 'application/octet-stream', ...call.headers };
68
+ return this.request({ ...call, headers }, async (response) => {
69
+ return new Uint8Array(await response.arrayBuffer());
70
+ });
71
+ }
57
72
  /**
58
73
  * The answer itself, its body unread (an event stream). Aborting the
59
74
  * call's signal later still ends the body.
@@ -282,3 +297,21 @@ export function sleep(ms, signal) {
282
297
  export function isRecord(v) {
283
298
  return typeof v === 'object' && v !== null && !Array.isArray(v);
284
299
  }
300
+ /**
301
+ * `Cloud-Sim-Client` for a client built on the SDK as `client` (none for the
302
+ * SDK used as it is): its products, then the SDK's. Checked here rather than
303
+ * left to the server, which keeps only 1-200 printable ASCII characters and
304
+ * otherwise drops it, and to fetch, which refuses other characters in a
305
+ * header only when a request is sent.
306
+ */
307
+ function clientHeader(client) {
308
+ const named = client?.trim();
309
+ if (!named)
310
+ return SDK_CLIENT;
311
+ const header = `${named} ${SDK_CLIENT}`;
312
+ if (!/^[\x21-\x7e]+( [\x21-\x7e]+)*$/.test(named) || header.length > MAX_CLIENT_LENGTH) {
313
+ throw new TypeError(`client must be printable ASCII products separated by single spaces, such as "my-tool/1.2", ` +
314
+ `of at most ${MAX_CLIENT_LENGTH - SDK_CLIENT.length - 1} characters: got ${JSON.stringify(client)}`);
315
+ }
316
+ return header;
317
+ }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  export { CloudSim, type CloudSimOptions } from './client.js';
2
+ export { SDK_VERSION } from './version.js';
2
3
  export { CloudSimError, type CloudSimErrorInit, CompareError, isTransient, } from './errors.js';
3
4
  export type { ApiKey, RetryInfo } from './http.js';
4
5
  export { type FollowEvent, type FollowOptions, type FollowTransport, type Offsets, type StreamName, POLL_INTERVAL_MS, } from './follow.js';
5
6
  export type { CallOptions, Comparison, CompareParams, CreateOptions, CreateRunParams, FirmwareInput, ListRunsParams, RunListing, RunOptionsInput, Runs, WaitOptions, } from './runs.js';
6
7
  export type { McuFilter, McuListOptions, Mcus, Versions } from './catalog.js';
8
+ export type { Settings as SettingsClient, UpdateSettingsParams } from './settings.js';
7
9
  export { type Difference, type ResultComparison, compareResults } from './compare.js';
8
- export { type Changes, type Engine, type EngineOption, type ErrorBody, type Firmware, type Lifecycle, type Live, type LiveStream, type Mcu, type McuEngine, type McuList, type Run, type RunEngine, type RunList, type RunOptions, type RunResult, type RunStatus, type UartEncoding, type Version, type VersionList, FINISHED, isFinished, } from './types.js';
10
+ export { type Changes, type Engine, type EngineOption, type ErrorBody, type Firmware, type FirmwareSource, type Lifecycle, type Live, type LiveStream, type Mcu, type McuEngine, type McuList, type Run, type RunEngine, type RunList, type RunOptions, type RunResult, type RunStatus, type Settings, type StoredFirmware, type UartEncoding, type Version, type VersionList, FINISHED, isFinished, } from './types.js';
9
11
  export type { components, operations, paths } from './generated/v1.js';
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  // fetch does: Node 18 and later, browsers, Deno, Bun, edge runtimes.
4
4
  // Anything that needs Node lives in '@simantic/cloud-sim/node'.
5
5
  export { CloudSim } from './client.js';
6
+ export { SDK_VERSION } from './version.js';
6
7
  export { CloudSimError, CompareError, isTransient, } from './errors.js';
7
8
  export { POLL_INTERVAL_MS, } from './follow.js';
8
9
  export { compareResults } from './compare.js';
package/dist/runs.d.ts CHANGED
@@ -3,13 +3,16 @@ import { type Transport } from './http.js';
3
3
  import { type Run, type RunList, type RunResult, type UartEncoding, type Version } from './types.js';
4
4
  /**
5
5
  * A run's firmware: the ELF's bytes (sent as a multipart upload), an https
6
- * URL the server fetches it from, or the ELF already base64-encoded (sent
7
- * in JSON).
6
+ * URL the server fetches it from, the ELF already base64-encoded (sent in
7
+ * JSON), or the id of one of your runs whose firmware the server stores
8
+ * (`run.firmware.stored`), to run it again.
8
9
  */
9
10
  export type FirmwareInput = Blob | Uint8Array | ArrayBuffer | {
10
11
  url: string;
11
12
  } | {
12
13
  base64: string;
14
+ } | {
15
+ run: string;
13
16
  };
14
17
  /** What every call takes. */
15
18
  export interface CallOptions {
@@ -105,6 +108,19 @@ export declare class Runs {
105
108
  * run that finished meanwhile gets the 409, which is not retried.
106
109
  */
107
110
  cancel(id: string, options?: CallOptions): Promise<Run>;
111
+ /**
112
+ * The ELF the run simulated, as it was given, while the server stores it
113
+ * (`run.firmware.stored`). One it does not store is 404
114
+ * `firmware_not_stored`.
115
+ */
116
+ firmware(id: string, options?: CallOptions): Promise<Uint8Array>;
117
+ /**
118
+ * Deletes the run's stored firmware: at once, or for a run that has not
119
+ * ended, when it does. Answers with the run, `firmware.stored` false and
120
+ * `firmware.deleted_at` saying when. Retried like a read: the server
121
+ * answers a repeated deletion with the run as it is.
122
+ */
123
+ deleteFirmware(id: string, options?: CallOptions): Promise<Run>;
108
124
  /** The run once it has finished: succeeded, failed or canceled. */
109
125
  wait(id: string, options?: WaitOptions): Promise<Run>;
110
126
  /**
package/dist/runs.js CHANGED
@@ -74,6 +74,33 @@ export class Runs {
74
74
  retry: true,
75
75
  });
76
76
  }
77
+ /**
78
+ * The ELF the run simulated, as it was given, while the server stores it
79
+ * (`run.firmware.stored`). One it does not store is 404
80
+ * `firmware_not_stored`.
81
+ */
82
+ firmware(id, options = {}) {
83
+ return this.http.bytes({
84
+ method: 'GET',
85
+ path: `/v1/runs/${segment(id)}/firmware`,
86
+ signal: options.signal,
87
+ retry: true,
88
+ });
89
+ }
90
+ /**
91
+ * Deletes the run's stored firmware: at once, or for a run that has not
92
+ * ended, when it does. Answers with the run, `firmware.stored` false and
93
+ * `firmware.deleted_at` saying when. Retried like a read: the server
94
+ * answers a repeated deletion with the run as it is.
95
+ */
96
+ deleteFirmware(id, options = {}) {
97
+ return this.http.json({
98
+ method: 'DELETE',
99
+ path: `/v1/runs/${segment(id)}/firmware`,
100
+ signal: options.signal,
101
+ retry: true,
102
+ });
103
+ }
77
104
  /** The run once it has finished: succeeded, failed or canceled. */
78
105
  async wait(id, options = {}) {
79
106
  const { signal, intervalMs = WAIT_INTERVAL_MS } = options;
@@ -142,7 +169,7 @@ export class Runs {
142
169
  const call = {
143
170
  method: 'POST',
144
171
  path: '/v1/runs',
145
- headers: { 'Idempotency-Key': key },
172
+ headers: { 'Idempotency-Key': key, 'Cloud-Sim-Client': this.http.client },
146
173
  version: params.version,
147
174
  signal: params.signal,
148
175
  timeoutMs: params.timeoutMs,
@@ -250,8 +277,11 @@ function firmwareBody(firmware) {
250
277
  if ('base64' in firmware && typeof firmware.base64 === 'string') {
251
278
  return { kind: 'json', json: { base64: firmware.base64 } };
252
279
  }
280
+ if ('run' in firmware && typeof firmware.run === 'string') {
281
+ return { kind: 'json', json: { run: firmware.run } };
282
+ }
253
283
  }
254
- throw new TypeError('firmware must be a Blob, a Uint8Array, an ArrayBuffer, { url } or { base64 }');
284
+ throw new TypeError('firmware must be a Blob, a Uint8Array, an ArrayBuffer, { url }, { base64 } or { run }');
255
285
  }
256
286
  /** The run's fields other than its firmware, as the API names them. */
257
287
  function optionFields(params) {
@@ -0,0 +1,21 @@
1
+ import type { CallOptions } from './runs.js';
2
+ import type { Transport } from './http.js';
3
+ import type { Settings as SettingsBody } from './types.js';
4
+ /** The settings to change. One left out stays as it is. */
5
+ export interface UpdateSettingsParams extends CallOptions {
6
+ /**
7
+ * Whether the server stores runs' firmware. Turning it off deletes every
8
+ * run's stored firmware (an unfinished run's when it ends) and stores
9
+ * none from then on; turning it on stores the firmware of runs that end
10
+ * from then on.
11
+ */
12
+ storeFirmware?: boolean;
13
+ }
14
+ export declare class Settings {
15
+ private readonly http;
16
+ constructor(http: Transport);
17
+ /** Your settings, and how much firmware the server stores for you. */
18
+ get(options?: CallOptions): Promise<SettingsBody>;
19
+ /** Changes the settings given, and answers with all of them. */
20
+ update(params: UpdateSettingsParams): Promise<SettingsBody>;
21
+ }
@@ -0,0 +1,32 @@
1
+ // `client.settings`: your settings. One so far: whether the server stores
2
+ // each run's firmware after the run, so you can download it
3
+ // (`runs.firmware`) and run it again (`firmware: { run: id }`).
4
+ export class Settings {
5
+ http;
6
+ constructor(http) {
7
+ this.http = http;
8
+ }
9
+ /** Your settings, and how much firmware the server stores for you. */
10
+ get(options = {}) {
11
+ return this.http.json({
12
+ method: 'GET',
13
+ path: '/v1/settings',
14
+ signal: options.signal,
15
+ retry: true,
16
+ });
17
+ }
18
+ /** Changes the settings given, and answers with all of them. */
19
+ update(params) {
20
+ const json = {};
21
+ if (params.storeFirmware !== undefined)
22
+ json['store_firmware'] = params.storeFirmware;
23
+ return this.http.json({
24
+ method: 'PATCH',
25
+ path: '/v1/settings',
26
+ json,
27
+ signal: params.signal,
28
+ // Setting the same value again changes nothing more.
29
+ retry: true,
30
+ });
31
+ }
32
+ }
package/dist/types.d.ts CHANGED
@@ -12,8 +12,14 @@ export type RunOptions = Schemas['RunOptions'];
12
12
  export type RunResult = Schemas['RunResult'];
13
13
  /** A page of the caller's runs, newest first. */
14
14
  export type RunList = Schemas['RunList'];
15
- /** The firmware a run simulates, as the server received it. */
15
+ /** The firmware a run simulates, as the server received it, and whether it stores it. */
16
16
  export type Firmware = Schemas['Firmware'];
17
+ /** How a run's firmware came: uploaded, base64, from a URL, or another run's stored firmware. */
18
+ export type FirmwareSource = Schemas['FirmwareSource'];
19
+ /** Your settings: whether the server stores runs' firmware, and how much it stores. */
20
+ export type Settings = Schemas['Settings'];
21
+ /** The firmware the server stores for you. */
22
+ export type StoredFirmware = Schemas['StoredFirmware'];
17
23
  /** A run and a page of each of its streams (`GET /v1/runs/{id}/live`). */
18
24
  export type Live = Schemas['Live'];
19
25
  /** A page of one stream. */
@@ -0,0 +1 @@
1
+ export declare const SDK_VERSION = "0.3.0";
@@ -0,0 +1,4 @@
1
+ // The SDK's own version, as a run's `Cloud-Sim-Client` names it
2
+ // (`cloud-sim-ts/<version>`). A test keeps it equal to package.json's, so a
3
+ // release that bumps one bumps the other.
4
+ export const SDK_VERSION = '0.3.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simantic/cloud-sim",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "TypeScript client for the Cloud Sim API: run MCU firmware in the cloud, follow its output live, and compare simulation engines.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -24,12 +24,7 @@
24
24
  "node": ">=18"
25
25
  },
26
26
  "license": "UNLICENSED",
27
- "repository": {
28
- "type": "git",
29
- "url": "git+https://github.com/simantic-dev/cloud-sim.git",
30
- "directory": "sdk/typescript"
31
- },
32
- "homepage": "https://github.com/simantic-dev/cloud-sim/tree/main/sdk/typescript#readme",
27
+ "homepage": "https://simantic.com/cloud-sim",
33
28
  "keywords": [
34
29
  "simantic",
35
30
  "cloud-sim",
@@ -52,8 +47,5 @@
52
47
  "openapi-typescript": "^7.13.0",
53
48
  "typescript": "~5.9.3",
54
49
  "vitest": "^4.1.11"
55
- },
56
- "publishConfig": {
57
- "access": "restricted"
58
50
  }
59
51
  }