@hoststack.dev/sdk 0.4.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/dist/index.js CHANGED
@@ -196,11 +196,9 @@ var DeploysResource = class {
196
196
  teamId: tid,
197
197
  serviceId: sid
198
198
  });
199
- return this.client.request(
200
- "POST",
201
- `/api/services/${tid}/${sid}/deploys/${did}/promote`,
202
- { targetEnvironmentId }
203
- );
199
+ return this.client.request("POST", `/api/services/${tid}/${sid}/deploys/${did}/promote`, {
200
+ targetEnvironmentId
201
+ });
204
202
  }
205
203
  /**
206
204
  * Get build logs for a deploy.
@@ -354,6 +352,65 @@ var EnvVarsResource = class {
354
352
  }
355
353
  };
356
354
 
355
+ // src/resources/notifications.ts
356
+ var NotificationsResource = class {
357
+ constructor(client) {
358
+ this.client = client;
359
+ }
360
+ /**
361
+ * List notification channels for the team. Webhook URLs are
362
+ * server-side masked in the response so this is safe to log.
363
+ */
364
+ async listChannels(teamId) {
365
+ const tid = await this.client.resolveId(teamId, { kind: "team" });
366
+ return this.client.request("GET", `/api/notifications/${tid}/channels`);
367
+ }
368
+ /**
369
+ * Create a Slack/Discord/email notification channel.
370
+ *
371
+ * For type=email, `webhookUrl` is the recipient email address; for
372
+ * type=slack/discord it's the incoming webhook URL.
373
+ *
374
+ * `events` is the explicit subscription list — an empty array means
375
+ * "receive nothing". The platform pre-selects the critical-event
376
+ * set on the dashboard, but SDK callers must pass the list
377
+ * explicitly so behaviour is deterministic.
378
+ */
379
+ async createChannel(teamId, data) {
380
+ const tid = await this.client.resolveId(teamId, { kind: "team" });
381
+ return this.client.request("POST", `/api/notifications/${tid}/channels`, data);
382
+ }
383
+ /**
384
+ * Update a channel's name / active state / event subscriptions. The
385
+ * webhook URL and type are immutable — create a new channel if
386
+ * those need to change.
387
+ */
388
+ async updateChannel(teamId, channelId, data) {
389
+ const tid = await this.client.resolveId(teamId, { kind: "team" });
390
+ return this.client.request(
391
+ "PATCH",
392
+ `/api/notifications/${tid}/channels/${channelId}`,
393
+ data
394
+ );
395
+ }
396
+ /** Delete a notification channel. */
397
+ async deleteChannel(teamId, channelId) {
398
+ const tid = await this.client.resolveId(teamId, { kind: "team" });
399
+ return this.client.request("DELETE", `/api/notifications/${tid}/channels/${channelId}`);
400
+ }
401
+ /**
402
+ * Fire a test event to the channel so the user can confirm the
403
+ * webhook is wired correctly. Returns the dispatch outcome.
404
+ */
405
+ async testChannel(teamId, channelId) {
406
+ const tid = await this.client.resolveId(teamId, { kind: "team" });
407
+ return this.client.request(
408
+ "POST",
409
+ `/api/notifications/${tid}/channels/${channelId}/test`
410
+ );
411
+ }
412
+ };
413
+
357
414
  // src/resources/projects.ts
358
415
  var ProjectsResource = class {
359
416
  constructor(client) {
@@ -451,10 +508,23 @@ var ServicesResource = class {
451
508
  constructor(client) {
452
509
  this.client = client;
453
510
  }
454
- /** List all services for the active team. */
455
- async list(teamId) {
511
+ /**
512
+ * List services for the active team.
513
+ *
514
+ * Optional filters narrow by project, environment, status, or type.
515
+ * The server treats unknown enum values as no match (returns empty)
516
+ * rather than 400-ing.
517
+ */
518
+ async list(teamId, filters) {
456
519
  const tid = await this.client.resolveId(teamId, { kind: "team" });
457
- return this.client.request("GET", `/api/services/${tid}`);
520
+ const params = new URLSearchParams();
521
+ if (filters?.projectId !== void 0) params.set("projectId", String(filters.projectId));
522
+ if (filters?.environmentId !== void 0)
523
+ params.set("environmentId", String(filters.environmentId));
524
+ if (filters?.status) params.set("status", filters.status);
525
+ if (filters?.type) params.set("type", filters.type);
526
+ const qs = params.toString();
527
+ return this.client.request("GET", `/api/services/${tid}${qs ? `?${qs}` : ""}`);
458
528
  }
459
529
  /** Get a single service by ID. */
460
530
  async get(teamId, serviceId) {
@@ -497,6 +567,25 @@ var ServicesResource = class {
497
567
  const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
498
568
  return this.client.request("GET", `/api/services/${tid}/${sid}/metrics`);
499
569
  }
570
+ /**
571
+ * Get a metrics time series for a service.
572
+ *
573
+ * `from`/`to` accept ISO-8601 timestamps. Omit both for the trailing
574
+ * hour. Server picks the resolution: raw samples ≤7d, hourly pre-
575
+ * aggregates ≤30d, daily beyond that. Up to ~500 points returned.
576
+ */
577
+ async getMetricsHistory(teamId, serviceId, options) {
578
+ const tid = await this.client.resolveId(teamId, { kind: "team" });
579
+ const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
580
+ const params = new URLSearchParams();
581
+ if (options?.from) params.set("from", options.from);
582
+ if (options?.to) params.set("to", options.to);
583
+ const qs = params.toString();
584
+ return this.client.request(
585
+ "GET",
586
+ `/api/services/${tid}/${sid}/metrics/history${qs ? `?${qs}` : ""}`
587
+ );
588
+ }
500
589
  /** Get service configuration. */
501
590
  async getConfig(teamId, serviceId) {
502
591
  const tid = await this.client.resolveId(teamId, { kind: "team" });
@@ -555,11 +644,7 @@ var ServicesResource = class {
555
644
  const tid = await this.client.resolveId(teamId, { kind: "team" });
556
645
  const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
557
646
  const basePath = `/api/services/${tid}/${sid}/runtime-logs`;
558
- yield* streamLogsViaPolling(
559
- (path) => this.client.request("GET", path),
560
- basePath,
561
- options
562
- );
647
+ yield* streamLogsViaPolling((path) => this.client.request("GET", path), basePath, options);
563
648
  }
564
649
  };
565
650
 
@@ -647,6 +732,12 @@ var HostStack = class {
647
732
  cron;
648
733
  /** Manage persistent disks attached to services. */
649
734
  volumes;
735
+ /**
736
+ * Manage notification channels — Slack/Discord webhooks + email
737
+ * recipients with per-channel event filters. Used for deploy
738
+ * failures, restart loops, ACME failures, git auth losses, etc.
739
+ */
740
+ notifications;
650
741
  constructor(options) {
651
742
  if (!options.apiKey) {
652
743
  throw new Error("apiKey is required");
@@ -662,6 +753,7 @@ var HostStack = class {
662
753
  this.environments = new EnvironmentsResource(this);
663
754
  this.cron = new CronResource(this);
664
755
  this.volumes = new VolumesResource(this);
756
+ this.notifications = new NotificationsResource(this);
665
757
  }
666
758
  /**
667
759
  * Make an authenticated request to the HostStack API.
@@ -753,17 +845,11 @@ var HostStack = class {
753
845
  return r.services ?? [];
754
846
  }
755
847
  case "deploy": {
756
- const r = await this.request(
757
- "GET",
758
- `/api/services/${scope.teamId}/${scope.serviceId}/deploys?perPage=100`
759
- );
848
+ const r = await this.request("GET", `/api/services/${scope.teamId}/${scope.serviceId}/deploys?perPage=100`);
760
849
  return r.data ?? [];
761
850
  }
762
851
  case "database": {
763
- const r = await this.request(
764
- "GET",
765
- `/api/databases/${scope.teamId}`
766
- );
852
+ const r = await this.request("GET", `/api/databases/${scope.teamId}`);
767
853
  return r.databases ?? [];
768
854
  }
769
855
  case "domain": {