@hoststack.dev/sdk 0.4.0 → 0.6.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
@@ -53,19 +53,22 @@ Generate an API key from your [HostStack dashboard → Settings → API Keys](ht
53
53
 
54
54
  ## Resources
55
55
 
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`, `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.environments` | `list`, `get`, `create`, `update`, `delete` |
65
- | `client.cron` | `list`, `get`, `trigger` |
66
- | `client.deploys` | `…`, `promote(teamId, serviceId, deployId, targetEnvId)` — image-based promotion across envs |
67
-
68
- Every method's first argument is `teamId: number`. Full API reference: **[hoststack.dev/docs/sdk](https://hoststack.dev/docs/sdk)**.
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
+ | `client.notifications` | `listChannels`, `createChannel`, `updateChannel`, `deleteChannel`, `testChannel` |
68
+
69
+ 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)**.
70
+
71
+ `client.deploys.promote(teamId, serviceId, deployId, targetEnvironmentId)` performs image-based promotion: it pins the same built image into a sibling environment without rebuilding.
69
72
 
70
73
  ## Error handling
71
74
 
@@ -96,7 +99,8 @@ try {
96
99
  ## Related packages
97
100
 
98
101
  - **[@hoststack.dev/cli](https://www.npmjs.com/package/@hoststack.dev/cli)** — command-line interface for HostStack
99
- - **[Terraform provider](https://github.com/gethoststack/terraform-provider-hoststack)** — manage HostStack resources as IaC
102
+ - **[@hoststack.dev/mcp](https://www.npmjs.com/package/@hoststack.dev/mcp)** — MCP server for Claude, Cursor, and other AI agents
103
+ - **Terraform provider** — see [hoststack.dev/docs](https://hoststack.dev/docs) for installation and resource reference
100
104
 
101
105
  ## Support
102
106
 
package/dist/index.cjs CHANGED
@@ -198,11 +198,9 @@ var DeploysResource = class {
198
198
  teamId: tid,
199
199
  serviceId: sid
200
200
  });
201
- return this.client.request(
202
- "POST",
203
- `/api/services/${tid}/${sid}/deploys/${did}/promote`,
204
- { targetEnvironmentId }
205
- );
201
+ return this.client.request("POST", `/api/services/${tid}/${sid}/deploys/${did}/promote`, {
202
+ targetEnvironmentId
203
+ });
206
204
  }
207
205
  /**
208
206
  * Get build logs for a deploy.
@@ -356,6 +354,62 @@ var EnvVarsResource = class {
356
354
  }
357
355
  };
358
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("POST", `/api/notifications/${tid}/channels/${channelId}/test`);
410
+ }
411
+ };
412
+
359
413
  // src/resources/projects.ts
360
414
  var ProjectsResource = class {
361
415
  constructor(client) {
@@ -453,10 +507,23 @@ var ServicesResource = class {
453
507
  constructor(client) {
454
508
  this.client = client;
455
509
  }
456
- /** List all services for the active team. */
457
- async list(teamId) {
510
+ /**
511
+ * List services for the active team.
512
+ *
513
+ * Optional filters narrow by project, environment, status, or type.
514
+ * The server treats unknown enum values as no match (returns empty)
515
+ * rather than 400-ing.
516
+ */
517
+ async list(teamId, filters) {
458
518
  const tid = await this.client.resolveId(teamId, { kind: "team" });
459
- return this.client.request("GET", `/api/services/${tid}`);
519
+ const params = new URLSearchParams();
520
+ if (filters?.projectId !== void 0) params.set("projectId", String(filters.projectId));
521
+ if (filters?.environmentId !== void 0)
522
+ params.set("environmentId", String(filters.environmentId));
523
+ if (filters?.status) params.set("status", filters.status);
524
+ if (filters?.type) params.set("type", filters.type);
525
+ const qs = params.toString();
526
+ return this.client.request("GET", `/api/services/${tid}${qs ? `?${qs}` : ""}`);
460
527
  }
461
528
  /** Get a single service by ID. */
462
529
  async get(teamId, serviceId) {
@@ -499,6 +566,25 @@ var ServicesResource = class {
499
566
  const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
500
567
  return this.client.request("GET", `/api/services/${tid}/${sid}/metrics`);
501
568
  }
569
+ /**
570
+ * Get a metrics time series for a service.
571
+ *
572
+ * `from`/`to` accept ISO-8601 timestamps. Omit both for the trailing
573
+ * hour. Server picks the resolution: raw samples ≤7d, hourly pre-
574
+ * aggregates ≤30d, daily beyond that. Up to ~500 points returned.
575
+ */
576
+ async getMetricsHistory(teamId, serviceId, options) {
577
+ const tid = await this.client.resolveId(teamId, { kind: "team" });
578
+ const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
579
+ const params = new URLSearchParams();
580
+ if (options?.from) params.set("from", options.from);
581
+ if (options?.to) params.set("to", options.to);
582
+ const qs = params.toString();
583
+ return this.client.request(
584
+ "GET",
585
+ `/api/services/${tid}/${sid}/metrics/history${qs ? `?${qs}` : ""}`
586
+ );
587
+ }
502
588
  /** Get service configuration. */
503
589
  async getConfig(teamId, serviceId) {
504
590
  const tid = await this.client.resolveId(teamId, { kind: "team" });
@@ -557,11 +643,7 @@ var ServicesResource = class {
557
643
  const tid = await this.client.resolveId(teamId, { kind: "team" });
558
644
  const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
559
645
  const basePath = `/api/services/${tid}/${sid}/runtime-logs`;
560
- yield* streamLogsViaPolling(
561
- (path) => this.client.request("GET", path),
562
- basePath,
563
- options
564
- );
646
+ yield* streamLogsViaPolling((path) => this.client.request("GET", path), basePath, options);
565
647
  }
566
648
  };
567
649
 
@@ -649,6 +731,12 @@ var HostStack = class {
649
731
  cron;
650
732
  /** Manage persistent disks attached to services. */
651
733
  volumes;
734
+ /**
735
+ * Manage notification channels — Slack/Discord webhooks + email
736
+ * recipients with per-channel event filters. Used for deploy
737
+ * failures, restart loops, ACME failures, git auth losses, etc.
738
+ */
739
+ notifications;
652
740
  constructor(options) {
653
741
  if (!options.apiKey) {
654
742
  throw new Error("apiKey is required");
@@ -664,6 +752,7 @@ var HostStack = class {
664
752
  this.environments = new EnvironmentsResource(this);
665
753
  this.cron = new CronResource(this);
666
754
  this.volumes = new VolumesResource(this);
755
+ this.notifications = new NotificationsResource(this);
667
756
  }
668
757
  /**
669
758
  * Make an authenticated request to the HostStack API.
@@ -755,17 +844,11 @@ var HostStack = class {
755
844
  return r.services ?? [];
756
845
  }
757
846
  case "deploy": {
758
- const r = await this.request(
759
- "GET",
760
- `/api/services/${scope.teamId}/${scope.serviceId}/deploys?perPage=100`
761
- );
847
+ const r = await this.request("GET", `/api/services/${scope.teamId}/${scope.serviceId}/deploys?perPage=100`);
762
848
  return r.data ?? [];
763
849
  }
764
850
  case "database": {
765
- const r = await this.request(
766
- "GET",
767
- `/api/databases/${scope.teamId}`
768
- );
851
+ const r = await this.request("GET", `/api/databases/${scope.teamId}`);
769
852
  return r.databases ?? [];
770
853
  }
771
854
  case "domain": {