@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/dist/index.js
CHANGED
|
@@ -182,6 +182,24 @@ var DeploysResource = class {
|
|
|
182
182
|
});
|
|
183
183
|
return this.client.request("POST", `/api/services/${tid}/${sid}/deploys/${did}/rollback`);
|
|
184
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* v66 P5: promote a built deploy to a sibling service in another
|
|
187
|
+
* environment. Reuses the source deploy's docker image (no rebuild).
|
|
188
|
+
* If no sibling service exists in the target env yet, the API auto-
|
|
189
|
+
* creates one by cloning the source service's config.
|
|
190
|
+
*/
|
|
191
|
+
async promote(teamId, serviceId, deployId, targetEnvironmentId) {
|
|
192
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
193
|
+
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
194
|
+
const did = await this.client.resolveId(deployId, {
|
|
195
|
+
kind: "deploy",
|
|
196
|
+
teamId: tid,
|
|
197
|
+
serviceId: sid
|
|
198
|
+
});
|
|
199
|
+
return this.client.request("POST", `/api/services/${tid}/${sid}/deploys/${did}/promote`, {
|
|
200
|
+
targetEnvironmentId
|
|
201
|
+
});
|
|
202
|
+
}
|
|
185
203
|
/**
|
|
186
204
|
* Get build logs for a deploy.
|
|
187
205
|
*
|
|
@@ -243,6 +261,50 @@ var DomainsResource = class {
|
|
|
243
261
|
}
|
|
244
262
|
};
|
|
245
263
|
|
|
264
|
+
// src/resources/environments.ts
|
|
265
|
+
var EnvironmentsResource = class {
|
|
266
|
+
constructor(client) {
|
|
267
|
+
this.client = client;
|
|
268
|
+
}
|
|
269
|
+
/** List all environments for a project. */
|
|
270
|
+
async list(teamId, projectId) {
|
|
271
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
272
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
273
|
+
return this.client.request("GET", `/api/environments/${tid}/${pid}`);
|
|
274
|
+
}
|
|
275
|
+
/** Get a single environment by id. */
|
|
276
|
+
async get(teamId, projectId, envId) {
|
|
277
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
278
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
279
|
+
const eid = await this.client.resolveId(envId, { kind: "environment", teamId: tid });
|
|
280
|
+
return this.client.request("GET", `/api/environments/${tid}/${pid}/${eid}`);
|
|
281
|
+
}
|
|
282
|
+
/** Create a new environment in the given project. */
|
|
283
|
+
async create(teamId, projectId, data) {
|
|
284
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
285
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
286
|
+
return this.client.request("POST", `/api/environments/${tid}/${pid}`, data);
|
|
287
|
+
}
|
|
288
|
+
/** Update environment metadata (name, default flag, protected flag). */
|
|
289
|
+
async update(teamId, projectId, envId, data) {
|
|
290
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
291
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
292
|
+
const eid = await this.client.resolveId(envId, { kind: "environment", teamId: tid });
|
|
293
|
+
return this.client.request("PATCH", `/api/environments/${tid}/${pid}/${eid}`, data);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Delete an environment. The API blocks delete when the env still
|
|
297
|
+
* has services or databases attached — destroy them first or move
|
|
298
|
+
* them to another env. Cannot delete the project's default env.
|
|
299
|
+
*/
|
|
300
|
+
async delete(teamId, projectId, envId) {
|
|
301
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
302
|
+
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
303
|
+
const eid = await this.client.resolveId(envId, { kind: "environment", teamId: tid });
|
|
304
|
+
return this.client.request("DELETE", `/api/environments/${tid}/${pid}/${eid}`);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
|
|
246
308
|
// src/resources/env-vars.ts
|
|
247
309
|
var EnvVarsResource = class {
|
|
248
310
|
constructor(client) {
|
|
@@ -290,6 +352,65 @@ var EnvVarsResource = class {
|
|
|
290
352
|
}
|
|
291
353
|
};
|
|
292
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
|
+
|
|
293
414
|
// src/resources/projects.ts
|
|
294
415
|
var ProjectsResource = class {
|
|
295
416
|
constructor(client) {
|
|
@@ -333,7 +454,7 @@ async function* streamLogsViaPolling(fetch2, basePath, options = {}) {
|
|
|
333
454
|
const buildPath = (lines, since) => {
|
|
334
455
|
const params = new URLSearchParams();
|
|
335
456
|
if (options.stream) params.set("stream", options.stream);
|
|
336
|
-
if (lines !== void 0) params.set("
|
|
457
|
+
if (lines !== void 0) params.set("limit", String(lines));
|
|
337
458
|
if (since) params.set("since", since);
|
|
338
459
|
const qs = params.toString();
|
|
339
460
|
return `${basePath}${qs ? `?${qs}` : ""}`;
|
|
@@ -387,10 +508,23 @@ var ServicesResource = class {
|
|
|
387
508
|
constructor(client) {
|
|
388
509
|
this.client = client;
|
|
389
510
|
}
|
|
390
|
-
/**
|
|
391
|
-
|
|
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) {
|
|
392
519
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
393
|
-
|
|
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}` : ""}`);
|
|
394
528
|
}
|
|
395
529
|
/** Get a single service by ID. */
|
|
396
530
|
async get(teamId, serviceId) {
|
|
@@ -433,6 +567,25 @@ var ServicesResource = class {
|
|
|
433
567
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
434
568
|
return this.client.request("GET", `/api/services/${tid}/${sid}/metrics`);
|
|
435
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
|
+
}
|
|
436
589
|
/** Get service configuration. */
|
|
437
590
|
async getConfig(teamId, serviceId) {
|
|
438
591
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
@@ -445,14 +598,30 @@ var ServicesResource = class {
|
|
|
445
598
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
446
599
|
return this.client.request("PATCH", `/api/services/${tid}/${sid}/config`, data);
|
|
447
600
|
}
|
|
448
|
-
/**
|
|
601
|
+
/**
|
|
602
|
+
* Get runtime logs for a service.
|
|
603
|
+
*
|
|
604
|
+
* `since`/`until` accept either an ISO-8601 timestamp or a short
|
|
605
|
+
* relative offset like `-5m`, `-1h`, `-2d`.
|
|
606
|
+
*
|
|
607
|
+
* `search` does case-insensitive substring filtering server-side
|
|
608
|
+
* (≤100 chars). `countOnly` returns just `{ count: N }` for cheap
|
|
609
|
+
* polling — useful when you want to know "how many error lines in the
|
|
610
|
+
* last 5 minutes" without paying the bytes.
|
|
611
|
+
*/
|
|
449
612
|
async getRuntimeLogs(teamId, serviceId, options) {
|
|
450
613
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
451
614
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
452
615
|
const params = new URLSearchParams();
|
|
453
|
-
|
|
616
|
+
const lim = options?.limit ?? options?.lines;
|
|
617
|
+
if (lim != null) params.set("limit", String(lim));
|
|
454
618
|
if (options?.since) params.set("since", options.since);
|
|
619
|
+
if (options?.until) params.set("until", options.until);
|
|
455
620
|
if (options?.stream) params.set("stream", options.stream);
|
|
621
|
+
if (options?.level) params.set("level", options.level);
|
|
622
|
+
const grep = options?.grep ?? options?.search;
|
|
623
|
+
if (grep) params.set("search", grep);
|
|
624
|
+
if (options?.countOnly) params.set("count_only", "1");
|
|
456
625
|
const qs = params.toString();
|
|
457
626
|
return this.client.request(
|
|
458
627
|
"GET",
|
|
@@ -475,11 +644,7 @@ var ServicesResource = class {
|
|
|
475
644
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
476
645
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
477
646
|
const basePath = `/api/services/${tid}/${sid}/runtime-logs`;
|
|
478
|
-
yield* streamLogsViaPolling(
|
|
479
|
-
(path) => this.client.request("GET", path),
|
|
480
|
-
basePath,
|
|
481
|
-
options
|
|
482
|
-
);
|
|
647
|
+
yield* streamLogsViaPolling((path) => this.client.request("GET", path), basePath, options);
|
|
483
648
|
}
|
|
484
649
|
};
|
|
485
650
|
|
|
@@ -541,6 +706,7 @@ var PREFIX = {
|
|
|
541
706
|
domain: "dom_",
|
|
542
707
|
volume: "vol_",
|
|
543
708
|
envVar: "env_",
|
|
709
|
+
environment: "environment_",
|
|
544
710
|
cronExecution: "cjob_"
|
|
545
711
|
};
|
|
546
712
|
var HostStack = class {
|
|
@@ -560,10 +726,18 @@ var HostStack = class {
|
|
|
560
726
|
domains;
|
|
561
727
|
/** Manage service environment variables. */
|
|
562
728
|
envVars;
|
|
729
|
+
/** v66: manage environments (production/staging/development/preview) per project. */
|
|
730
|
+
environments;
|
|
563
731
|
/** Manage cron job executions. */
|
|
564
732
|
cron;
|
|
565
733
|
/** Manage persistent disks attached to services. */
|
|
566
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;
|
|
567
741
|
constructor(options) {
|
|
568
742
|
if (!options.apiKey) {
|
|
569
743
|
throw new Error("apiKey is required");
|
|
@@ -576,8 +750,10 @@ var HostStack = class {
|
|
|
576
750
|
this.databases = new DatabasesResource(this);
|
|
577
751
|
this.domains = new DomainsResource(this);
|
|
578
752
|
this.envVars = new EnvVarsResource(this);
|
|
753
|
+
this.environments = new EnvironmentsResource(this);
|
|
579
754
|
this.cron = new CronResource(this);
|
|
580
755
|
this.volumes = new VolumesResource(this);
|
|
756
|
+
this.notifications = new NotificationsResource(this);
|
|
581
757
|
}
|
|
582
758
|
/**
|
|
583
759
|
* Make an authenticated request to the HostStack API.
|
|
@@ -669,17 +845,11 @@ var HostStack = class {
|
|
|
669
845
|
return r.services ?? [];
|
|
670
846
|
}
|
|
671
847
|
case "deploy": {
|
|
672
|
-
const r = await this.request(
|
|
673
|
-
"GET",
|
|
674
|
-
`/api/services/${scope.teamId}/${scope.serviceId}/deploys?perPage=100`
|
|
675
|
-
);
|
|
848
|
+
const r = await this.request("GET", `/api/services/${scope.teamId}/${scope.serviceId}/deploys?perPage=100`);
|
|
676
849
|
return r.data ?? [];
|
|
677
850
|
}
|
|
678
851
|
case "database": {
|
|
679
|
-
const r = await this.request(
|
|
680
|
-
"GET",
|
|
681
|
-
`/api/databases/${scope.teamId}`
|
|
682
|
-
);
|
|
852
|
+
const r = await this.request("GET", `/api/databases/${scope.teamId}`);
|
|
683
853
|
return r.databases ?? [];
|
|
684
854
|
}
|
|
685
855
|
case "domain": {
|
|
@@ -704,6 +874,15 @@ var HostStack = class {
|
|
|
704
874
|
const r = await this.request("GET", `/api/services/${scope.teamId}/${scope.serviceId}/cron-executions`);
|
|
705
875
|
return r.executions ?? [];
|
|
706
876
|
}
|
|
877
|
+
case "environment": {
|
|
878
|
+
const projectsRes = await this.request("GET", `/api/projects/${scope.teamId}`);
|
|
879
|
+
const envs = [];
|
|
880
|
+
for (const p of projectsRes.projects ?? []) {
|
|
881
|
+
const r = await this.request("GET", `/api/environments/${scope.teamId}/${p.id}`);
|
|
882
|
+
envs.push(...r.environments ?? []);
|
|
883
|
+
}
|
|
884
|
+
return envs;
|
|
885
|
+
}
|
|
707
886
|
}
|
|
708
887
|
}
|
|
709
888
|
};
|
|
@@ -715,6 +894,7 @@ function cacheScope(scope) {
|
|
|
715
894
|
case "service":
|
|
716
895
|
case "database":
|
|
717
896
|
case "domain":
|
|
897
|
+
case "environment":
|
|
718
898
|
return `${scope.kind}:${scope.teamId}`;
|
|
719
899
|
case "deploy":
|
|
720
900
|
case "volume":
|