@pantechdynamic/sdk 0.0.0-stage → 0.2.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/CHANGELOG.md +39 -0
- package/LICENSE +21 -0
- package/README.md +200 -2
- package/dist/index.cjs +1661 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +20315 -0
- package/dist/index.d.ts +20315 -0
- package/dist/index.js +1634 -0
- package/dist/index.js.map +1 -0
- package/package.json +72 -4
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
A new version heading here (`## X.Y.Z (YYYY-MM-DD)`) pushed to main is
|
|
4
|
+
tagged `vX.Y.Z` and published to npm. Add changes under `## Unreleased`
|
|
5
|
+
until then.
|
|
6
|
+
|
|
7
|
+
## 0.2.0 (2026-10-28)
|
|
8
|
+
|
|
9
|
+
- `client.migrate`, for moving from another cloud: `migrate.connections`
|
|
10
|
+
(`list`, `listAll`, `create`, `get`, `update` for the name, role ARN or a retry, `delete`)
|
|
11
|
+
connects an AWS account through a read-only role; `migrate.scans` (`list`, `listAll`,
|
|
12
|
+
`start`, `get` with `{ include: "inventory" }`) reads what runs there; `migrate.migrations`
|
|
13
|
+
(`list`, `listAll`, `create`, `get`, `rename`, `delete`, `updateItem`, `exportTerraform`) plans
|
|
14
|
+
the move with a price comparison and a Terraform export.
|
|
15
|
+
- `migrate.connections.wait()` and `migrate.scans.wait()`, which reject with the new
|
|
16
|
+
`CloudFailedError` when the connection or scan fails.
|
|
17
|
+
- `examples/migrate-from-aws.ts`.
|
|
18
|
+
- Types follow the current public API, including Kubernetes clusters' `node_health`,
|
|
19
|
+
`ready_nodes` and `nodes_checked_at`, and a load balancer's `protocol`.
|
|
20
|
+
- Fixed: request fields the API defaults (`endpoint_access`, `control_nodes`, a firewall rule's
|
|
21
|
+
`direction` and `action`, `algorithm`, `purpose`, `admin_username`, `retention_count`,
|
|
22
|
+
`enabled`, `autoscaling_enabled`) are optional in the types, as they are in the API.
|
|
23
|
+
- Agent sessions, which answer only AI apps connected through Pantech's MCP server, have no
|
|
24
|
+
methods yet; `scripts/not-in-sdk.json` lists them and `npm run generate` leaves out their types.
|
|
25
|
+
|
|
26
|
+
## 0.1.0 (2026-10-24)
|
|
27
|
+
|
|
28
|
+
- First release: a client for every operation in the Pantech Dynamics public API
|
|
29
|
+
(`/public/v1`), with types generated from its OpenAPI spec.
|
|
30
|
+
- Automatic `Idempotency-Key` on every write, reused across retries; retries with
|
|
31
|
+
backoff and `Retry-After` on network errors, 429, 500, 502, 503 and 504, except for
|
|
32
|
+
writes the API does not replay.
|
|
33
|
+
- `PantechError` and its subclasses, carrying `status`, `code`, `title`, `detail`,
|
|
34
|
+
`requestId` and field `errors`.
|
|
35
|
+
- `listAll()` async iterators, `operations.wait()`, `operations.stream()`, and
|
|
36
|
+
`instanceOrders.wait()` / `databaseOrders.wait()`.
|
|
37
|
+
- `me.revoke()` (`DELETE /me`), and `kind` / `client_name` on `me.get()`.
|
|
38
|
+
- `verifyWebhook()` and `signWebhook()` on Web Crypto.
|
|
39
|
+
- ESM and CommonJS builds; Node.js 18+, Bun, Deno and edge runtimes.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Pantech Dynamics
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,201 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Pantech Dynamics SDK for TypeScript and JavaScript
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://github.com/Pantech-Dynamics/pantech-js/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@pantechdynamic/sdk)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
The official client for the [Pantech Dynamics public API](https://docs.pantechdynamics.com/api):
|
|
8
|
+
instances, VPC networks, firewalls, public IPs, load balancers, volumes, snapshots, managed
|
|
9
|
+
databases, managed Kubernetes, SSH keys, webhooks, billing, usage, and moving from another cloud.
|
|
10
|
+
|
|
11
|
+
- Zero runtime dependencies. Uses the global `fetch` and Web Crypto, so it runs on Node.js 18+,
|
|
12
|
+
Bun, Deno and edge runtimes.
|
|
13
|
+
- ESM and CommonJS, with types generated from the API's OpenAPI spec.
|
|
14
|
+
- Retries, idempotency keys, typed errors, pagination, operation waiting and webhook
|
|
15
|
+
verification built in.
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install @pantechdynamic/sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Quickstart
|
|
22
|
+
|
|
23
|
+
Create an API key in the console under **Organization › API keys**, then:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Pantech } from "@pantechdynamic/sdk";
|
|
27
|
+
|
|
28
|
+
const client = new Pantech({ apiKey: process.env.PANTECH_API_KEY });
|
|
29
|
+
|
|
30
|
+
// Order an instance and wait until it is running.
|
|
31
|
+
const order = await client.instances.create({
|
|
32
|
+
name: "web-1",
|
|
33
|
+
plan_slug: "starter",
|
|
34
|
+
image_slug: "ubuntu-24-04",
|
|
35
|
+
ssh_key_id: "sshk_…",
|
|
36
|
+
});
|
|
37
|
+
await client.instanceOrders.wait(order);
|
|
38
|
+
|
|
39
|
+
const vm = await client.instances.get(order.instance_id);
|
|
40
|
+
console.log(vm.public_ipv4);
|
|
41
|
+
|
|
42
|
+
// Every write returns an operation; wait for it.
|
|
43
|
+
await client.operations.wait(await client.instances.stop(vm.id));
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`apiKey` defaults to `PANTECH_API_KEY`, and `baseURL` to `PANTECH_API_URL`, else
|
|
47
|
+
`https://api.pantechdynamics.com/public/v1`. Request and response fields are exactly as the API
|
|
48
|
+
sends them (`plan_slug`, `next_cursor`); methods are camelCase.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
new Pantech({
|
|
52
|
+
apiKey: "PAN_…",
|
|
53
|
+
baseURL: "https://api.example.com/public/v1",
|
|
54
|
+
maxRetries: 3, // default
|
|
55
|
+
timeout: 60_000, // per attempt, in ms (default)
|
|
56
|
+
fetch: customFetch, // default: globalThis.fetch
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Every method takes per-call options last: `{ signal, timeout, maxRetries, idempotencyKey, headers }`.
|
|
61
|
+
|
|
62
|
+
## Resources
|
|
63
|
+
|
|
64
|
+
| Family | Examples |
|
|
65
|
+
|---|---|
|
|
66
|
+
| Account | `me.get()`, `me.revoke()` |
|
|
67
|
+
| Catalogue | `regions.list()`, `plans.list({ placement })`, `images.list()`, `diskOfferings.quote(slug, { size_gb })`, `databaseEngines.list()`, `kubernetesVersions.list()`, `pricing.estimate({...})` |
|
|
68
|
+
| Instances | `instances.list/listAll/create/get/delete/start/stop/reboot/rebuild/rename/resize/restore/…`, `instances.snapshots.*`, `instances.sshAccess.*`, `instanceOrders.wait(order)` |
|
|
69
|
+
| Networks | `networks.*`, `networks.subnets.list(networkId)`, `networks.firewallRules.create(subnetId, {...})` |
|
|
70
|
+
| Public IPs | `publicIps.*`, `publicIps.portForwardingRules.*` |
|
|
71
|
+
| Load balancers | `loadBalancers.*` |
|
|
72
|
+
| Kubernetes | `kubernetes.*`, `kubernetes.nodePools.*`, `kubernetes.addons.toggle(...)` |
|
|
73
|
+
| Security groups | `securityGroups.*`, `securityGroups.replaceRules(id, {...})` |
|
|
74
|
+
| Volumes and snapshots | `volumes.*`, `volumes.snapshots.*`, `snapshots.*` |
|
|
75
|
+
| Databases | `databases.*`, `databases.snapshots.*`, `databaseOrders.wait(order)` |
|
|
76
|
+
| SSH keys, webhooks | `sshKeys.*`, `webhooks.*`, `webhooks.deliveries.*` |
|
|
77
|
+
| Billing and usage | `billing.credit.get()`, `billing.credit.purchases.create(...)`, `billing.receipts.getPdf(id)`, `usage.get()` |
|
|
78
|
+
| Move from another cloud | `migrate.connections.*`, `migrate.scans.*`, `migrate.migrations.*` |
|
|
79
|
+
| Operations | `operations.get(id)`, `operations.wait(id)`, `operations.stream(id)` |
|
|
80
|
+
|
|
81
|
+
Every operation in the public spec has a method, except agent sessions, which answer only AI apps
|
|
82
|
+
connected through Pantech's MCP server; `test/coverage.test.ts` fails if one is missing.
|
|
83
|
+
For anything else, `client.request({ method, path, query, body })` sends a raw request with the
|
|
84
|
+
same auth, retries and errors.
|
|
85
|
+
|
|
86
|
+
## Pagination
|
|
87
|
+
|
|
88
|
+
`list()` returns one page (`{ data, next_cursor }`). `listAll()` follows the cursor for you:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
for await (const vm of client.instances.listAll({ observed_state: "running" })) {
|
|
92
|
+
console.log(vm.id, vm.name);
|
|
93
|
+
}
|
|
94
|
+
for await (const subnet of client.networks.subnets.listAll("net_…")) console.log(subnet.cidr);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Waiting
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const op = await client.operations.wait(accepted, { timeoutMs: 10 * 60_000, onUpdate: (o) => console.log(o.status) });
|
|
101
|
+
await client.databaseOrders.wait(order); // paid, provisioned, and its create operation done
|
|
102
|
+
for await (const o of client.operations.stream("op_…")) console.log(o.status); // Server-Sent Events
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
A failed operation rejects with `OperationFailedError` (`code`, `detail` from its `failure`), a
|
|
106
|
+
failed or unpaid order with `OrderFailedError`, a failed cloud connection or scan with
|
|
107
|
+
`CloudFailedError`, and a wait that runs out with `WaitTimeoutError`.
|
|
108
|
+
|
|
109
|
+
## Move from another cloud
|
|
110
|
+
|
|
111
|
+
`client.migrate` connects an AWS account through a read-only role that only Pantech can assume (no
|
|
112
|
+
access keys are shared), scans what runs there, and plans the move with a price comparison and a
|
|
113
|
+
Terraform export. Nothing is created on Pantech until you apply the Terraform.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
let conn = await client.migrate.connections.create({ name: "Production AWS" });
|
|
117
|
+
console.log(conn.setup.quick_create_url); // create the stack in AWS, then send its RoleArn output
|
|
118
|
+
await client.migrate.connections.update(conn.id, { role_arn: "arn:aws:iam::…:role/…" });
|
|
119
|
+
conn = await client.migrate.connections.wait(conn.id); // CloudFailedError if the role cannot be assumed
|
|
120
|
+
|
|
121
|
+
const scan = await client.migrate.scans.wait((await client.migrate.scans.start(conn.id)).id);
|
|
122
|
+
const plan = await client.migrate.migrations.create({ scan_id: scan.id, name: "Move to Pantech" });
|
|
123
|
+
console.log(plan.estimate?.pantech_monthly_minor, plan.estimate?.source_monthly_minor);
|
|
124
|
+
const { files } = await client.migrate.migrations.exportTerraform(plan.id); // main.tf, variables.tf
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`migrate.migrations.updateItem(id, { key, excluded: true })` leaves an item out, or changes its
|
|
128
|
+
plan, size, address range or role, before you export. See `examples/migrate-from-aws.ts`.
|
|
129
|
+
|
|
130
|
+
## Errors
|
|
131
|
+
|
|
132
|
+
Every error is a `PantechError` carrying `status`, `code`, `title`, `detail`, `requestId` and
|
|
133
|
+
`errors` (the field errors of a `422`). Branch on `code`, never on the message:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { isPantechError, NotFoundError } from "@pantechdynamic/sdk";
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
await client.instances.get("vm_…");
|
|
140
|
+
} catch (err) {
|
|
141
|
+
if (err instanceof NotFoundError) { /* gone */ }
|
|
142
|
+
else if (isPantechError(err, "INSUFFICIENT_SCOPE")) { /* key lacks a scope */ }
|
|
143
|
+
else throw err;
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Subclasses: `BadRequestError` (400), `AuthenticationError` (401), `PaymentRequiredError` (402),
|
|
148
|
+
`PermissionDeniedError` (403), `NotFoundError` (404), `ConflictError` (409), `ValidationError`
|
|
149
|
+
(422), `RateLimitError` (429), `InternalServerError` (5xx), `APIConnectionError` and
|
|
150
|
+
`APITimeoutError` (no answer).
|
|
151
|
+
|
|
152
|
+
## Retries and idempotency
|
|
153
|
+
|
|
154
|
+
Every POST, PUT, PATCH and DELETE carries an `Idempotency-Key` (a random UUID, or yours via
|
|
155
|
+
`idempotencyKey`), and every retry of that call reuses it, so a change cannot happen twice.
|
|
156
|
+
Network errors, timeouts, `429` (after `Retry-After`), `500`, `502`, `503` and `504` are retried up
|
|
157
|
+
to `maxRetries` times with jittered backoff.
|
|
158
|
+
|
|
159
|
+
The API does not replay some writes by key, so the SDK sends them once (except after a `429`,
|
|
160
|
+
which is refused before anything runs): creating and deleting SSH keys, opening an instance
|
|
161
|
+
console, issuing a volume key credential, testing, deleting, rotating the secret of and
|
|
162
|
+
redelivering webhooks, and `DELETE /me`.
|
|
163
|
+
|
|
164
|
+
## Webhooks
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import { verifyWebhook } from "@pantechdynamic/sdk";
|
|
168
|
+
|
|
169
|
+
await verifyWebhook({
|
|
170
|
+
secret: process.env.PANTECH_WEBHOOK_SECRET!,
|
|
171
|
+
header: request.headers.get("Pantech-Signature"),
|
|
172
|
+
body: await request.text(), // the raw body, not re-serialised JSON
|
|
173
|
+
toleranceSeconds: 300, // default
|
|
174
|
+
});
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
It rejects with `WebhookVerificationError` when the signature does not match any `v1` value or
|
|
178
|
+
the timestamp is too old. On Node.js 18, start node with `--experimental-global-webcrypto`
|
|
179
|
+
(Node.js 20+ needs nothing). See `examples/webhook-receiver.ts`.
|
|
180
|
+
|
|
181
|
+
## Examples
|
|
182
|
+
|
|
183
|
+
`examples/` has `create-instance.ts`, `list.ts`, `webhook-receiver.ts` and `migrate-from-aws.ts`. With Node.js 23.6+:
|
|
184
|
+
`npm run build && PANTECH_API_KEY=PAN_… node examples/list.ts`.
|
|
185
|
+
|
|
186
|
+
## Development
|
|
187
|
+
|
|
188
|
+
```sh
|
|
189
|
+
npm ci
|
|
190
|
+
npm run spec:sync # download the published spec into spec/public-api.yaml (or pass a path/URL)
|
|
191
|
+
npm run generate # regenerate src/types.gen.ts and src/models.gen.ts from it
|
|
192
|
+
npm run typecheck && npm run lint && npm test && npm run build
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The SDK is generated from the published [OpenAPI spec](https://docs.pantechdynamics.com/openapi/public-api.yaml).
|
|
196
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the workflow and how releases are made. Report security
|
|
197
|
+
issues as described in [SECURITY.md](SECURITY.md).
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT, see [LICENSE](LICENSE).
|