@hoststack.dev/sdk 0.3.0 → 0.5.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 +17 -12
- package/dist/index.cjs +199 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +287 -9
- package/dist/index.d.ts +287 -9
- package/dist/index.js +199 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,17 +53,21 @@ Generate an API key from your [HostStack dashboard → Settings → API Keys](ht
|
|
|
53
53
|
|
|
54
54
|
## Resources
|
|
55
55
|
|
|
56
|
-
| Resource
|
|
57
|
-
|
|
|
58
|
-
| `client.projects`
|
|
59
|
-
| `client.services`
|
|
60
|
-
| `client.deploys`
|
|
61
|
-
| `client.databases`
|
|
62
|
-
| `client.domains`
|
|
63
|
-
| `client.envVars`
|
|
64
|
-
| `client.
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
| Resource | Methods |
|
|
57
|
+
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
+
| `client.projects` | `list`, `get`, `create`, `update`, `delete` |
|
|
59
|
+
| `client.services` | `list`, `get`, `create`, `update`, `delete`, `suspend`, `resume`, `getMetrics`, `getConfig`, `updateConfig`, `getRuntimeLogs`, `streamLogs` |
|
|
60
|
+
| `client.deploys` | `list`, `get`, `trigger`, `cancel`, `rollback`, `promote`, `getLogs` |
|
|
61
|
+
| `client.databases` | `list`, `get`, `create`, `update`, `delete`, `suspend`, `resume`, `getCredentials`, `resetPassword` |
|
|
62
|
+
| `client.domains` | `list`, `add`, `update`, `remove`, `verify` |
|
|
63
|
+
| `client.envVars` | `list`, `create`, `update`, `delete`, `bulkSet` |
|
|
64
|
+
| `client.volumes` | `list`, `create`, `update`, `delete` |
|
|
65
|
+
| `client.environments` | `list`, `get`, `create`, `update`, `delete` |
|
|
66
|
+
| `client.cron` | `list`, `get`, `trigger` |
|
|
67
|
+
|
|
68
|
+
Every method's first argument is the team id — accepts either the numeric id or the `team_…` publicId (the SDK resolves publicIds to numeric ids internally and caches the lookup). Full API reference: **[hoststack.dev/docs/sdk](https://hoststack.dev/docs/sdk)**.
|
|
69
|
+
|
|
70
|
+
`client.deploys.promote(teamId, serviceId, deployId, targetEnvironmentId)` performs image-based promotion: it pins the same built image into a sibling environment without rebuilding.
|
|
67
71
|
|
|
68
72
|
## Error handling
|
|
69
73
|
|
|
@@ -94,7 +98,8 @@ try {
|
|
|
94
98
|
## Related packages
|
|
95
99
|
|
|
96
100
|
- **[@hoststack.dev/cli](https://www.npmjs.com/package/@hoststack.dev/cli)** — command-line interface for HostStack
|
|
97
|
-
- **[
|
|
101
|
+
- **[@hoststack.dev/mcp](https://www.npmjs.com/package/@hoststack.dev/mcp)** — MCP server for Claude, Cursor, and other AI agents
|
|
102
|
+
- **Terraform provider** — see [hoststack.dev/docs](https://hoststack.dev/docs) for installation and resource reference
|
|
98
103
|
|
|
99
104
|
## Support
|
|
100
105
|
|
package/dist/index.cjs
CHANGED
|
@@ -184,6 +184,24 @@ var DeploysResource = class {
|
|
|
184
184
|
});
|
|
185
185
|
return this.client.request("POST", `/api/services/${tid}/${sid}/deploys/${did}/rollback`);
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* v66 P5: promote a built deploy to a sibling service in another
|
|
189
|
+
* environment. Reuses the source deploy's docker image (no rebuild).
|
|
190
|
+
* If no sibling service exists in the target env yet, the API auto-
|
|
191
|
+
* creates one by cloning the source service's config.
|
|
192
|
+
*/
|
|
193
|
+
async promote(teamId, serviceId, deployId, targetEnvironmentId) {
|
|
194
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
195
|
+
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
196
|
+
const did = await this.client.resolveId(deployId, {
|
|
197
|
+
kind: "deploy",
|
|
198
|
+
teamId: tid,
|
|
199
|
+
serviceId: sid
|
|
200
|
+
});
|
|
201
|
+
return this.client.request("POST", `/api/services/${tid}/${sid}/deploys/${did}/promote`, {
|
|
202
|
+
targetEnvironmentId
|
|
203
|
+
});
|
|
204
|
+
}
|
|
187
205
|
/**
|
|
188
206
|
* Get build logs for a deploy.
|
|
189
207
|
*
|
|
@@ -245,6 +263,50 @@ var DomainsResource = class {
|
|
|
245
263
|
}
|
|
246
264
|
};
|
|
247
265
|
|
|
266
|
+
// src/resources/environments.ts
|
|
267
|
+
var EnvironmentsResource = class {
|
|
268
|
+
constructor(client) {
|
|
269
|
+
this.client = client;
|
|
270
|
+
}
|
|
271
|
+
/** List all environments for a project. */
|
|
272
|
+
async list(teamId, projectId) {
|
|
273
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
274
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
275
|
+
return this.client.request("GET", `/api/environments/${tid}/${pid}`);
|
|
276
|
+
}
|
|
277
|
+
/** Get a single environment by id. */
|
|
278
|
+
async get(teamId, projectId, envId) {
|
|
279
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
280
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
281
|
+
const eid = await this.client.resolveId(envId, { kind: "environment", teamId: tid });
|
|
282
|
+
return this.client.request("GET", `/api/environments/${tid}/${pid}/${eid}`);
|
|
283
|
+
}
|
|
284
|
+
/** Create a new environment in the given project. */
|
|
285
|
+
async create(teamId, projectId, data) {
|
|
286
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
287
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
288
|
+
return this.client.request("POST", `/api/environments/${tid}/${pid}`, data);
|
|
289
|
+
}
|
|
290
|
+
/** Update environment metadata (name, default flag, protected flag). */
|
|
291
|
+
async update(teamId, projectId, envId, data) {
|
|
292
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
293
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
294
|
+
const eid = await this.client.resolveId(envId, { kind: "environment", teamId: tid });
|
|
295
|
+
return this.client.request("PATCH", `/api/environments/${tid}/${pid}/${eid}`, data);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Delete an environment. The API blocks delete when the env still
|
|
299
|
+
* has services or databases attached — destroy them first or move
|
|
300
|
+
* them to another env. Cannot delete the project's default env.
|
|
301
|
+
*/
|
|
302
|
+
async delete(teamId, projectId, envId) {
|
|
303
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
304
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
305
|
+
const eid = await this.client.resolveId(envId, { kind: "environment", teamId: tid });
|
|
306
|
+
return this.client.request("DELETE", `/api/environments/${tid}/${pid}/${eid}`);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
|
|
248
310
|
// src/resources/env-vars.ts
|
|
249
311
|
var EnvVarsResource = class {
|
|
250
312
|
constructor(client) {
|
|
@@ -292,6 +354,65 @@ var EnvVarsResource = class {
|
|
|
292
354
|
}
|
|
293
355
|
};
|
|
294
356
|
|
|
357
|
+
// src/resources/notifications.ts
|
|
358
|
+
var NotificationsResource = class {
|
|
359
|
+
constructor(client) {
|
|
360
|
+
this.client = client;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* List notification channels for the team. Webhook URLs are
|
|
364
|
+
* server-side masked in the response so this is safe to log.
|
|
365
|
+
*/
|
|
366
|
+
async listChannels(teamId) {
|
|
367
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
368
|
+
return this.client.request("GET", `/api/notifications/${tid}/channels`);
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Create a Slack/Discord/email notification channel.
|
|
372
|
+
*
|
|
373
|
+
* For type=email, `webhookUrl` is the recipient email address; for
|
|
374
|
+
* type=slack/discord it's the incoming webhook URL.
|
|
375
|
+
*
|
|
376
|
+
* `events` is the explicit subscription list — an empty array means
|
|
377
|
+
* "receive nothing". The platform pre-selects the critical-event
|
|
378
|
+
* set on the dashboard, but SDK callers must pass the list
|
|
379
|
+
* explicitly so behaviour is deterministic.
|
|
380
|
+
*/
|
|
381
|
+
async createChannel(teamId, data) {
|
|
382
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
383
|
+
return this.client.request("POST", `/api/notifications/${tid}/channels`, data);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Update a channel's name / active state / event subscriptions. The
|
|
387
|
+
* webhook URL and type are immutable — create a new channel if
|
|
388
|
+
* those need to change.
|
|
389
|
+
*/
|
|
390
|
+
async updateChannel(teamId, channelId, data) {
|
|
391
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
392
|
+
return this.client.request(
|
|
393
|
+
"PATCH",
|
|
394
|
+
`/api/notifications/${tid}/channels/${channelId}`,
|
|
395
|
+
data
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
/** Delete a notification channel. */
|
|
399
|
+
async deleteChannel(teamId, channelId) {
|
|
400
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
401
|
+
return this.client.request("DELETE", `/api/notifications/${tid}/channels/${channelId}`);
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Fire a test event to the channel so the user can confirm the
|
|
405
|
+
* webhook is wired correctly. Returns the dispatch outcome.
|
|
406
|
+
*/
|
|
407
|
+
async testChannel(teamId, channelId) {
|
|
408
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
409
|
+
return this.client.request(
|
|
410
|
+
"POST",
|
|
411
|
+
`/api/notifications/${tid}/channels/${channelId}/test`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
295
416
|
// src/resources/projects.ts
|
|
296
417
|
var ProjectsResource = class {
|
|
297
418
|
constructor(client) {
|
|
@@ -335,7 +456,7 @@ async function* streamLogsViaPolling(fetch2, basePath, options = {}) {
|
|
|
335
456
|
const buildPath = (lines, since) => {
|
|
336
457
|
const params = new URLSearchParams();
|
|
337
458
|
if (options.stream) params.set("stream", options.stream);
|
|
338
|
-
if (lines !== void 0) params.set("
|
|
459
|
+
if (lines !== void 0) params.set("limit", String(lines));
|
|
339
460
|
if (since) params.set("since", since);
|
|
340
461
|
const qs = params.toString();
|
|
341
462
|
return `${basePath}${qs ? `?${qs}` : ""}`;
|
|
@@ -389,10 +510,23 @@ var ServicesResource = class {
|
|
|
389
510
|
constructor(client) {
|
|
390
511
|
this.client = client;
|
|
391
512
|
}
|
|
392
|
-
/**
|
|
393
|
-
|
|
513
|
+
/**
|
|
514
|
+
* List services for the active team.
|
|
515
|
+
*
|
|
516
|
+
* Optional filters narrow by project, environment, status, or type.
|
|
517
|
+
* The server treats unknown enum values as no match (returns empty)
|
|
518
|
+
* rather than 400-ing.
|
|
519
|
+
*/
|
|
520
|
+
async list(teamId, filters) {
|
|
394
521
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
395
|
-
|
|
522
|
+
const params = new URLSearchParams();
|
|
523
|
+
if (filters?.projectId !== void 0) params.set("projectId", String(filters.projectId));
|
|
524
|
+
if (filters?.environmentId !== void 0)
|
|
525
|
+
params.set("environmentId", String(filters.environmentId));
|
|
526
|
+
if (filters?.status) params.set("status", filters.status);
|
|
527
|
+
if (filters?.type) params.set("type", filters.type);
|
|
528
|
+
const qs = params.toString();
|
|
529
|
+
return this.client.request("GET", `/api/services/${tid}${qs ? `?${qs}` : ""}`);
|
|
396
530
|
}
|
|
397
531
|
/** Get a single service by ID. */
|
|
398
532
|
async get(teamId, serviceId) {
|
|
@@ -435,6 +569,25 @@ var ServicesResource = class {
|
|
|
435
569
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
436
570
|
return this.client.request("GET", `/api/services/${tid}/${sid}/metrics`);
|
|
437
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Get a metrics time series for a service.
|
|
574
|
+
*
|
|
575
|
+
* `from`/`to` accept ISO-8601 timestamps. Omit both for the trailing
|
|
576
|
+
* hour. Server picks the resolution: raw samples ≤7d, hourly pre-
|
|
577
|
+
* aggregates ≤30d, daily beyond that. Up to ~500 points returned.
|
|
578
|
+
*/
|
|
579
|
+
async getMetricsHistory(teamId, serviceId, options) {
|
|
580
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
581
|
+
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
582
|
+
const params = new URLSearchParams();
|
|
583
|
+
if (options?.from) params.set("from", options.from);
|
|
584
|
+
if (options?.to) params.set("to", options.to);
|
|
585
|
+
const qs = params.toString();
|
|
586
|
+
return this.client.request(
|
|
587
|
+
"GET",
|
|
588
|
+
`/api/services/${tid}/${sid}/metrics/history${qs ? `?${qs}` : ""}`
|
|
589
|
+
);
|
|
590
|
+
}
|
|
438
591
|
/** Get service configuration. */
|
|
439
592
|
async getConfig(teamId, serviceId) {
|
|
440
593
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
@@ -447,14 +600,30 @@ var ServicesResource = class {
|
|
|
447
600
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
448
601
|
return this.client.request("PATCH", `/api/services/${tid}/${sid}/config`, data);
|
|
449
602
|
}
|
|
450
|
-
/**
|
|
603
|
+
/**
|
|
604
|
+
* Get runtime logs for a service.
|
|
605
|
+
*
|
|
606
|
+
* `since`/`until` accept either an ISO-8601 timestamp or a short
|
|
607
|
+
* relative offset like `-5m`, `-1h`, `-2d`.
|
|
608
|
+
*
|
|
609
|
+
* `search` does case-insensitive substring filtering server-side
|
|
610
|
+
* (≤100 chars). `countOnly` returns just `{ count: N }` for cheap
|
|
611
|
+
* polling — useful when you want to know "how many error lines in the
|
|
612
|
+
* last 5 minutes" without paying the bytes.
|
|
613
|
+
*/
|
|
451
614
|
async getRuntimeLogs(teamId, serviceId, options) {
|
|
452
615
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
453
616
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
454
617
|
const params = new URLSearchParams();
|
|
455
|
-
|
|
618
|
+
const lim = options?.limit ?? options?.lines;
|
|
619
|
+
if (lim != null) params.set("limit", String(lim));
|
|
456
620
|
if (options?.since) params.set("since", options.since);
|
|
621
|
+
if (options?.until) params.set("until", options.until);
|
|
457
622
|
if (options?.stream) params.set("stream", options.stream);
|
|
623
|
+
if (options?.level) params.set("level", options.level);
|
|
624
|
+
const grep = options?.grep ?? options?.search;
|
|
625
|
+
if (grep) params.set("search", grep);
|
|
626
|
+
if (options?.countOnly) params.set("count_only", "1");
|
|
458
627
|
const qs = params.toString();
|
|
459
628
|
return this.client.request(
|
|
460
629
|
"GET",
|
|
@@ -477,11 +646,7 @@ var ServicesResource = class {
|
|
|
477
646
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
478
647
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
479
648
|
const basePath = `/api/services/${tid}/${sid}/runtime-logs`;
|
|
480
|
-
yield* streamLogsViaPolling(
|
|
481
|
-
(path) => this.client.request("GET", path),
|
|
482
|
-
basePath,
|
|
483
|
-
options
|
|
484
|
-
);
|
|
649
|
+
yield* streamLogsViaPolling((path) => this.client.request("GET", path), basePath, options);
|
|
485
650
|
}
|
|
486
651
|
};
|
|
487
652
|
|
|
@@ -543,6 +708,7 @@ var PREFIX = {
|
|
|
543
708
|
domain: "dom_",
|
|
544
709
|
volume: "vol_",
|
|
545
710
|
envVar: "env_",
|
|
711
|
+
environment: "environment_",
|
|
546
712
|
cronExecution: "cjob_"
|
|
547
713
|
};
|
|
548
714
|
var HostStack = class {
|
|
@@ -562,10 +728,18 @@ var HostStack = class {
|
|
|
562
728
|
domains;
|
|
563
729
|
/** Manage service environment variables. */
|
|
564
730
|
envVars;
|
|
731
|
+
/** v66: manage environments (production/staging/development/preview) per project. */
|
|
732
|
+
environments;
|
|
565
733
|
/** Manage cron job executions. */
|
|
566
734
|
cron;
|
|
567
735
|
/** Manage persistent disks attached to services. */
|
|
568
736
|
volumes;
|
|
737
|
+
/**
|
|
738
|
+
* Manage notification channels — Slack/Discord webhooks + email
|
|
739
|
+
* recipients with per-channel event filters. Used for deploy
|
|
740
|
+
* failures, restart loops, ACME failures, git auth losses, etc.
|
|
741
|
+
*/
|
|
742
|
+
notifications;
|
|
569
743
|
constructor(options) {
|
|
570
744
|
if (!options.apiKey) {
|
|
571
745
|
throw new Error("apiKey is required");
|
|
@@ -578,8 +752,10 @@ var HostStack = class {
|
|
|
578
752
|
this.databases = new DatabasesResource(this);
|
|
579
753
|
this.domains = new DomainsResource(this);
|
|
580
754
|
this.envVars = new EnvVarsResource(this);
|
|
755
|
+
this.environments = new EnvironmentsResource(this);
|
|
581
756
|
this.cron = new CronResource(this);
|
|
582
757
|
this.volumes = new VolumesResource(this);
|
|
758
|
+
this.notifications = new NotificationsResource(this);
|
|
583
759
|
}
|
|
584
760
|
/**
|
|
585
761
|
* Make an authenticated request to the HostStack API.
|
|
@@ -671,17 +847,11 @@ var HostStack = class {
|
|
|
671
847
|
return r.services ?? [];
|
|
672
848
|
}
|
|
673
849
|
case "deploy": {
|
|
674
|
-
const r = await this.request(
|
|
675
|
-
"GET",
|
|
676
|
-
`/api/services/${scope.teamId}/${scope.serviceId}/deploys?perPage=100`
|
|
677
|
-
);
|
|
850
|
+
const r = await this.request("GET", `/api/services/${scope.teamId}/${scope.serviceId}/deploys?perPage=100`);
|
|
678
851
|
return r.data ?? [];
|
|
679
852
|
}
|
|
680
853
|
case "database": {
|
|
681
|
-
const r = await this.request(
|
|
682
|
-
"GET",
|
|
683
|
-
`/api/databases/${scope.teamId}`
|
|
684
|
-
);
|
|
854
|
+
const r = await this.request("GET", `/api/databases/${scope.teamId}`);
|
|
685
855
|
return r.databases ?? [];
|
|
686
856
|
}
|
|
687
857
|
case "domain": {
|
|
@@ -706,6 +876,15 @@ var HostStack = class {
|
|
|
706
876
|
const r = await this.request("GET", `/api/services/${scope.teamId}/${scope.serviceId}/cron-executions`);
|
|
707
877
|
return r.executions ?? [];
|
|
708
878
|
}
|
|
879
|
+
case "environment": {
|
|
880
|
+
const projectsRes = await this.request("GET", `/api/projects/${scope.teamId}`);
|
|
881
|
+
const envs = [];
|
|
882
|
+
for (const p of projectsRes.projects ?? []) {
|
|
883
|
+
const r = await this.request("GET", `/api/environments/${scope.teamId}/${p.id}`);
|
|
884
|
+
envs.push(...r.environments ?? []);
|
|
885
|
+
}
|
|
886
|
+
return envs;
|
|
887
|
+
}
|
|
709
888
|
}
|
|
710
889
|
}
|
|
711
890
|
};
|
|
@@ -717,6 +896,7 @@ function cacheScope(scope) {
|
|
|
717
896
|
case "service":
|
|
718
897
|
case "database":
|
|
719
898
|
case "domain":
|
|
899
|
+
case "environment":
|
|
720
900
|
return `${scope.kind}:${scope.teamId}`;
|
|
721
901
|
case "deploy":
|
|
722
902
|
case "volume":
|