@theholocron/cli 5.0.0-alpha.19 → 5.0.0-alpha.190

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.
@@ -1,5 +1,5 @@
1
1
  import { ProviderApiError } from "@theholocron/http-client";
2
- //#region src/plugin/capabilities.d.ts
2
+ //#region src/plugin/capabilities/capabilities.type.d.ts
3
3
  /**
4
4
  * Capability interfaces — the contracts that providers implement.
5
5
  *
@@ -12,33 +12,8 @@ import { ProviderApiError } from "@theholocron/http-client";
12
12
  * See `docs/wiki/specifications/tech-architecture.spec.md` for the design narrative
13
13
  * (issue: #74).
14
14
  */
15
- type CapabilityKey = "source" | "ci" | "secrets" | "environments" | "issues" | "deployment" | "storage" | "auth" | "vault" | "dns" | "tooling" | "notifications" | "analytics" | "errors" | "logs" | "wiki" | "workers";
15
+ type CapabilityKey = "source" | "ci" | "secrets" | "environments" | "issues" | "deployment" | "storage" | "auth" | "vault" | "dns" | "tooling" | "notifications" | "analytics" | "errors" | "logs" | "monitoring" | "wiki" | "workers";
16
16
  type Cardinality = "single" | "many";
17
- declare const CARDINALITY: {
18
- readonly source: "single";
19
- readonly ci: "single";
20
- readonly secrets: "single";
21
- readonly environments: "single";
22
- readonly issues: "single";
23
- readonly deployment: "single";
24
- readonly storage: "single";
25
- readonly auth: "single";
26
- readonly vault: "single";
27
- readonly dns: "single";
28
- readonly tooling: "many";
29
- readonly notifications: "many";
30
- readonly analytics: "many";
31
- readonly errors: "single";
32
- readonly logs: "single";
33
- readonly wiki: "single";
34
- readonly workers: "single";
35
- };
36
- /**
37
- * No capabilities are strictly required — repos without secrets (e.g. org
38
- * community health repos) legitimately omit vault. Plugins validate their
39
- * own requirements at call time.
40
- */
41
- declare const REQUIRED_CAPABILITIES: readonly CapabilityKey[];
42
17
  interface ProviderIdentity {
43
18
  readonly key: CapabilityKey;
44
19
  readonly providerName: string;
@@ -364,9 +339,35 @@ interface DeploymentRecord {
364
339
  target?: DeploymentTrigger;
365
340
  status: "queued" | "building" | "ready" | "error" | "cancelled";
366
341
  createdAt?: string;
342
+ /** The provider's own reason when `status` is `"error"` (e.g. Vercel's `Command "npm install" exited with 1`). */
343
+ errorMessage?: string;
344
+ }
345
+ /** Config for `Deployment.deployFunction()` — source files, not a Git ref. */
346
+ interface DeployFunctionConfig {
347
+ /**
348
+ * Files to deploy, keyed by path relative to the project root (e.g.
349
+ * `"api/webhook.js"`, `"package.json"`). Content is plain text — the
350
+ * provider handles any wire-format encoding.
351
+ */
352
+ files: Record<string, string>;
353
+ /**
354
+ * Named deployment target. Omit for a preview deployment — same
355
+ * semantics as `triggerDeployment`'s `target`.
356
+ */
357
+ target?: DeploymentTrigger;
358
+ }
359
+ interface DeployFunctionResult {
360
+ deploymentId: string;
361
+ url: string;
367
362
  }
368
363
  interface Deployment extends ProviderIdentity {
369
364
  readonly key: "deployment";
365
+ /**
366
+ * Custom domain declared in the deployment provider's configuration.
367
+ * `holocron repo setup` attaches it to the configured project and hands any
368
+ * returned verification record to the `dns` capability.
369
+ */
370
+ readonly domain?: string;
370
371
  listProjects(): Promise<DeploymentProject[]>;
371
372
  /** Create if missing, otherwise return existing. Idempotent. */
372
373
  ensureProject(input: {
@@ -390,6 +391,16 @@ interface Deployment extends ProviderIdentity {
390
391
  target?: DeploymentTrigger;
391
392
  }): Promise<DeploymentRecord>;
392
393
  getDeployment(deploymentId: string): Promise<DeploymentRecord>;
394
+ /**
395
+ * Deploy (or update) a project directly from source files, bypassing
396
+ * Git entirely — the non-git counterpart to `triggerDeployment`, for a
397
+ * consumer with no repo to link (a webhook receiver shipped as an npm
398
+ * package, for instance, rather than deployed from its own Git
399
+ * history). `projectId` may be a project name for providers that
400
+ * accept either. Optional — providers without a files-based deploy API
401
+ * omit this (e.g. Cloudflare Pages, git-source only today).
402
+ */
403
+ deployFunction?(projectId: string, config: DeployFunctionConfig): Promise<DeployFunctionResult>;
393
404
  /**
394
405
  * List preview deployments for a specific branch alias (e.g. "repo-pr-42").
395
406
  * Optional — providers that don't support listing by branch omit this.
@@ -402,10 +413,18 @@ interface Deployment extends ProviderIdentity {
402
413
  deletePreviewDeployments?(projectId: string, deploymentIds: string[]): Promise<number>;
403
414
  /**
404
415
  * Add a custom domain (or wildcard) to the project. Idempotent — no-op
405
- * when the domain is already present. Optional: providers without a custom
406
- * domain API omit this (e.g. Vercel manages domains separately).
416
+ * when the domain is already present. Optional: providers without a
417
+ * custom domain API omit this.
418
+ *
419
+ * Returns the DNS record needed to finish verification — `null` when
420
+ * the domain is already verified, or when the provider's API doesn't
421
+ * surface DNS-challenge details at all (e.g. Cloudflare Pages, whose
422
+ * custom-domain flow doesn't expose the same per-request verification
423
+ * handshake Vercel's does). Callers hand a non-null result straight
424
+ * to `dns.upsertRecord(result.zone, result.record)` — the exact
425
+ * pattern `setup` already uses for `Wiki.dnsRecord()`.
407
426
  */
408
- ensureCustomDomain?(projectId: string, hostname: string): Promise<void>;
427
+ ensureCustomDomain?(projectId: string, hostname: string): Promise<DnsRecordRequest | null>;
409
428
  }
410
429
  interface StorageBranch {
411
430
  id: string;
@@ -517,9 +536,6 @@ interface ParseWebhookInput {
517
536
  /** The signing secret the auth provider issued for this webhook endpoint. */
518
537
  signingSecret: string;
519
538
  }
520
- declare class WebhookVerificationError extends Error {
521
- name: string;
522
- }
523
539
  interface EnsureResult {
524
540
  /** True when the resource already existed (idempotent no-op). */
525
541
  alreadyExists: boolean;
@@ -573,11 +589,31 @@ interface DnsRecord {
573
589
  priority?: number;
574
590
  proxied?: boolean;
575
591
  }
592
+ /**
593
+ * A DNS record a capability wants created, plus which zone it belongs
594
+ * to — the shape `Wiki.dnsRecord()` and `Deployment.ensureCustomDomain()`
595
+ * both hand off to `Dns.upsertRecord()`. Not tied to either capability;
596
+ * any provider that needs "here's a record, go create it" returns this.
597
+ */
598
+ interface DnsRecordRequest {
599
+ /** Zone apex passed as the first argument to `dns.upsertRecord`. */
600
+ zone: string;
601
+ record: DnsRecord;
602
+ }
576
603
  interface Dns extends ProviderIdentity {
577
604
  readonly key: "dns";
578
605
  listRecords(domain: string): Promise<DnsRecord[]>;
579
606
  upsertRecord(domain: string, record: DnsRecord): Promise<DnsRecord>;
580
607
  deleteRecord(domain: string, id: string): Promise<void>;
608
+ /**
609
+ * A `holocron doctor` smoke check with no specific domain to ask about —
610
+ * `listRecords` needs one, but doctor doesn't have one on hand. Verifies
611
+ * the token and reports how many zones it can see.
612
+ */
613
+ whoami?(): Promise<{
614
+ ok: boolean;
615
+ zones: number;
616
+ }>;
581
617
  }
582
618
  interface Errors extends ProviderIdentity {
583
619
  readonly key: "errors";
@@ -586,9 +622,17 @@ interface Errors extends ProviderIdentity {
586
622
  provider: string;
587
623
  envKeys: string[];
588
624
  }>;
625
+ /**
626
+ * Reachability probe. `null` means the check genuinely can't run right
627
+ * now on an otherwise-valid configuration (e.g. Sentry's optional `org`
628
+ * setting is unset — error ingestion still works from the DSN alone,
629
+ * `org` is only needed for the management-API calls `whoami` makes) —
630
+ * callers should fall back to `describe()`, not treat it as a failure.
631
+ * Still throws for a genuine failure (bad token, network error, etc.).
632
+ */
589
633
  whoami?(): Promise<{
590
634
  org: string;
591
- }>;
635
+ } | null>;
592
636
  ensureProject?(input: {
593
637
  name: string;
594
638
  platform?: string;
@@ -614,6 +658,74 @@ interface Logs extends ProviderIdentity {
614
658
  alreadyExists: boolean;
615
659
  }>;
616
660
  }
661
+ type MonitoringAssertionSource = "status_code" | "response_time" | "body" | "header";
662
+ type MonitoringAssertionComparison = "equals" | "not_equals" | "contains" | "not_contains" | "greater_than" | "less_than";
663
+ interface MonitoringAssertion {
664
+ source: MonitoringAssertionSource;
665
+ comparison: MonitoringAssertionComparison;
666
+ /** Header name / JSON path — required when `source` needs one (e.g. `"header"`). */
667
+ property?: string;
668
+ target?: string;
669
+ }
670
+ type MonitoringHttpMethod = "GET" | "POST" | "PUT" | "HEAD" | "DELETE" | "PATCH";
671
+ interface MonitoringCheckInput {
672
+ /** Also the idempotency key — `ensureCheck` finds-or-creates by this name. */
673
+ name: string;
674
+ url: string;
675
+ method?: MonitoringHttpMethod;
676
+ /** Minutes between runs. Adapters round to their own supported values. */
677
+ frequency?: number;
678
+ assertions?: MonitoringAssertion[];
679
+ activated?: boolean;
680
+ /**
681
+ * Provider-specific region/datacenter ids to run the check from (e.g.
682
+ * Checkly's `"us-east-1"`). Required by some providers — there's no
683
+ * universal default; an account with none configured rejects a check
684
+ * that omits this entirely.
685
+ */
686
+ locations?: string[];
687
+ /**
688
+ * Inverts pass/fail: set this when the check's expected-healthy
689
+ * response is itself an HTTP error status (e.g. asserting a 404/405
690
+ * on an endpoint that should reject certain requests). Without it,
691
+ * most providers classify any 4xx/5xx response as a failure at the
692
+ * platform level *before* evaluating `assertions` at all — a custom
693
+ * assertion that matches the "expected" error status does not
694
+ * override that baseline classification on its own.
695
+ */
696
+ shouldFail?: boolean;
697
+ /**
698
+ * Raw request body to send with the check — e.g. simulating a webhook
699
+ * delivery to verify signature/auth checking is wired correctly
700
+ * end-to-end, rather than just confirming the endpoint is reachable.
701
+ * Omitted entirely by default (no body sent).
702
+ */
703
+ body?: string;
704
+ }
705
+ interface MonitoringCheck {
706
+ id: string;
707
+ name: string;
708
+ }
709
+ interface Monitoring extends ProviderIdentity {
710
+ readonly key: "monitoring";
711
+ /**
712
+ * Checks this provider is configured to ensure exist — populated from
713
+ * the provider's own config (`holocron.config.ts`'s provider tuple),
714
+ * the same place `Deployment.domain` comes from. `holocron repo setup`
715
+ * loops this list and calls `ensureCheck` for each; empty when none
716
+ * are configured.
717
+ */
718
+ readonly checks: readonly MonitoringCheckInput[];
719
+ /** Auth sanity-check. Throws ProviderApiError on auth failure. */
720
+ whoami?(): Promise<{
721
+ ok: boolean;
722
+ }>;
723
+ /** Idempotent find-or-create-or-update by `input.name`. */
724
+ ensureCheck(input: MonitoringCheckInput): Promise<{
725
+ check: MonitoringCheck;
726
+ alreadyExists: boolean;
727
+ }>;
728
+ }
617
729
  interface ToolingDoctorReport {
618
730
  ok: boolean;
619
731
  message: string;
@@ -624,6 +736,114 @@ interface Tooling extends ProviderIdentity {
624
736
  sync(): Promise<void>;
625
737
  doctor(): Promise<ToolingDoctorReport>;
626
738
  }
739
+ /** What an `ensure*` call found or made. `created: false` means it already existed. */
740
+ interface EnsuredResource {
741
+ id: string;
742
+ created: boolean;
743
+ /** For something that already existed: the settings that disagreed with the layout and were changed. */
744
+ updated?: string[];
745
+ }
746
+ /** A webhook (or equivalent inbound URL) for a channel. */
747
+ interface EnsureWebhookResult {
748
+ created: boolean;
749
+ /**
750
+ * The posting URL — a credential. Absent when the webhook already existed and
751
+ * the provider will not reveal it again (Discord returns the token only to
752
+ * the application that created the webhook, and only on creation).
753
+ */
754
+ url?: string;
755
+ }
756
+ /**
757
+ * Who may see a channel and post in it. Roles are named as in the layout's `roles`.
758
+ * A channel inherits its category's access; stating `view` or `post` on the channel
759
+ * replaces that field only.
760
+ */
761
+ interface NotificationsAccess {
762
+ /** `"everyone"` (the default), or the roles that can see it: everyone else cannot. */
763
+ view?: "everyone" | string[];
764
+ /** `"everyone"` (the default), or the roles that can post; `[]` means only bots and webhooks. */
765
+ post?: "everyone" | string[];
766
+ }
767
+ /** One channel in a {@link NotificationsLayout}. */
768
+ interface NotificationsChannelLayout {
769
+ name: string;
770
+ topic?: string;
771
+ /** Who may see and post. Overrides the category's, field by field. */
772
+ access?: NotificationsAccess;
773
+ /**
774
+ * Create an inbound webhook for the channel and store its URL in a secret.
775
+ * `true` uses the defaults; the object form overrides the webhook's display
776
+ * name and the secret's name.
777
+ */
778
+ webhook?: boolean | {
779
+ name?: string;
780
+ secret?: string;
781
+ };
782
+ }
783
+ /** A declared server layout, applied idempotently by `holocron notifications setup`. */
784
+ interface NotificationsLayout {
785
+ categories?: Array<{
786
+ name: string;
787
+ access?: NotificationsAccess;
788
+ channels?: NotificationsChannelLayout[];
789
+ }>;
790
+ roles?: Array<{
791
+ name: string;
792
+ mentionable?: boolean;
793
+ /** `#rrggbb`. Omit to leave the colour alone. */
794
+ color?: string;
795
+ /** Show the role's members as their own group in the member list. */
796
+ hoist?: boolean;
797
+ /**
798
+ * The permissions the role holds, by the provider's own names (for Discord, `ViewChannel`,
799
+ * `SendMessages`, …). The list is exact: a permission not named is not granted.
800
+ */
801
+ permissions?: string[];
802
+ /**
803
+ * Store the role's id in this secret, so a workflow can ping the role
804
+ * without anyone copying the id by hand. The id is not sensitive; a secret
805
+ * is used because the same token that writes the webhook secrets can write
806
+ * it, with no extra permission.
807
+ */
808
+ secret?: string;
809
+ }>;
810
+ }
811
+ /**
812
+ * What a provider found on the server, limited to what a {@link NotificationsLayout}
813
+ * asks about. Read-only: this is what `holocron doctor notifications` compares the
814
+ * layout against.
815
+ */
816
+ interface ServerInspection {
817
+ categories: string[];
818
+ /**
819
+ * Who can see and post in each category. `"custom"` means its overwrites are not an access the layout
820
+ * can state (a role-specific deny, a member overwrite, only some of the posting permissions).
821
+ */
822
+ categoryAccess?: Record<string, Required<NotificationsAccess> | "custom">;
823
+ channels: Array<{
824
+ name: string;
825
+ /** The category it sits under, if any. */
826
+ category?: string;
827
+ topic?: string;
828
+ /** Names of the webhooks on the channel. Only filled in for channels the layout gives a webhook. */
829
+ webhooks: string[];
830
+ /** Set when listing this channel's webhooks failed; `webhooks` is then empty and means nothing. */
831
+ webhooksError?: string;
832
+ /** Who can see and post; `"custom"` as for `categoryAccess`. */
833
+ access?: Required<NotificationsAccess> | "custom";
834
+ }>;
835
+ roles: Array<{
836
+ name: string;
837
+ id: string;
838
+ mentionable: boolean;
839
+ /** `#rrggbb`, or absent for the default colour. */
840
+ color?: string;
841
+ hoist?: boolean;
842
+ permissions?: string[];
843
+ }>;
844
+ /** Set when reading the roles failed; `roles` is then empty and means nothing. Not read at all when the layout declares none. */
845
+ rolesError?: string;
846
+ }
627
847
  interface Notifications extends ProviderIdentity {
628
848
  readonly key: "notifications";
629
849
  /**
@@ -631,6 +851,58 @@ interface Notifications extends ProviderIdentity {
631
851
  * Discord webhook url-name, etc.); adapters resolve from config.
632
852
  */
633
853
  send(channel: string, message: string): Promise<void>;
854
+ /**
855
+ * The server layout the owner declared for this provider, if any. Read by
856
+ * `holocron notifications setup`; the provider just carries it from its options.
857
+ */
858
+ readonly layout?: NotificationsLayout;
859
+ /**
860
+ * Provisioning. All optional, all idempotent (look up by name, create only
861
+ * what is missing), and all need provider credentials beyond the posting
862
+ * URL — so a provider that only posts simply omits them.
863
+ */
864
+ ensureCategory?(name: string, opts?: {
865
+ access?: NotificationsAccess;
866
+ }): Promise<EnsuredResource>;
867
+ ensureChannel?(input: {
868
+ name: string;
869
+ category?: string;
870
+ topic?: string;
871
+ access?: NotificationsAccess;
872
+ }): Promise<EnsuredResource>;
873
+ ensureWebhook?(input: {
874
+ channel: string;
875
+ name: string;
876
+ category?: string;
877
+ }): Promise<EnsureWebhookResult>;
878
+ ensureRole?(input: {
879
+ name: string;
880
+ mentionable?: boolean;
881
+ color?: string;
882
+ hoist?: boolean;
883
+ permissions?: string[];
884
+ }): Promise<EnsuredResource>;
885
+ /**
886
+ * Check the provider-specific parts of the layout (colours, permission names, …) without
887
+ * touching the server, so `setup` can refuse a bad layout before it creates anything.
888
+ * Returns the first problem, or `undefined` when the layout is fine.
889
+ */
890
+ validateLayout?(layout: NotificationsLayout): string | undefined;
891
+ /**
892
+ * Smoke check for `holocron doctor`: prove the provisioning credentials work
893
+ * and name what they reach. Returns `null` when there is nothing to check (a
894
+ * config that only posts has no provisioning credentials), which doctor shows
895
+ * as "loaded" rather than as a failure. Throws when it is configured to
896
+ * provision but cannot (missing token, no access).
897
+ */
898
+ whoami?(): Promise<{
899
+ target: string;
900
+ } | null>;
901
+ /**
902
+ * Read what exists on the server, for the parts the layout cares about, without
903
+ * changing anything. Backs `holocron doctor notifications`.
904
+ */
905
+ inspect?(layout: NotificationsLayout): Promise<ServerInspection>;
634
906
  }
635
907
  interface Analytics extends ProviderIdentity {
636
908
  readonly key: "analytics";
@@ -651,18 +923,6 @@ interface WikiProvisionOpts {
651
923
  /** Project display name used in the wiki title (e.g. "Holocron"). */
652
924
  name?: string;
653
925
  }
654
- /**
655
- * DNS record that `setup` should create for a wiki custom domain.
656
- * Returned by `Wiki.dnsRecord()` when a custom domain is configured.
657
- */
658
- interface WikiDnsRecord {
659
- /** Zone apex passed as the first argument to `dns.upsertRecord`. */
660
- zone: string;
661
- /** Full hostname for the CNAME (e.g. "wiki.theholocron.dev"). */
662
- cname: string;
663
- /** CNAME target (e.g. "holocron.docs.buildwithfern.com"). */
664
- target: string;
665
- }
666
926
  /**
667
927
  * Reverse-proxy configuration returned by `Wiki.proxyConfig()`.
668
928
  * Used by `setup` to deploy a Worker that forwards traffic to the wiki
@@ -691,20 +951,53 @@ interface Wiki extends ProviderIdentity {
691
951
  * Returns null when no custom domain is set.
692
952
  * Called by `setup` to provision the CNAME via the `dns` capability.
693
953
  */
694
- dnsRecord?(): WikiDnsRecord | null;
954
+ dnsRecord?(): DnsRecordRequest | null;
695
955
  /**
696
956
  * Reverse-proxy config when the wiki provider requires a Worker-level
697
957
  * proxy in addition to the CNAME. Returns null when no proxy is needed.
698
958
  * Called by `setup` to deploy the proxy via the `workers` capability.
699
959
  */
700
960
  proxyConfig?(): WikiProxyConfig | null;
961
+ /**
962
+ * Confirms a provider token resolves. Unlike `dns`/`workers`/`monitoring`'s
963
+ * `whoami()`, this is NOT a live reachability check — Fern (the only
964
+ * current `wiki` provider) exposes no lightweight endpoint to verify a
965
+ * token actually works; the only token-consuming operation is a full
966
+ * `fern generate` (holocron#1079). `ok: true` means "a token was found",
967
+ * not "the token works".
968
+ */
969
+ whoami?(): Promise<{
970
+ ok: boolean;
971
+ }>;
972
+ }
973
+ /** Config for `Workers.deployScript()` — an arbitrary Worker, not a generated reverse-proxy. */
974
+ interface DeployScriptConfig {
975
+ /** The full Worker script source — a single ES module (`export default { fetch(...) {...} }`). */
976
+ code: string;
977
+ /**
978
+ * Secret bindings — `env.<name>` in the deployed Worker. Values are
979
+ * write-only; the provider never echoes them back, here or anywhere else.
980
+ */
981
+ secrets?: Record<string, string>;
982
+ /**
983
+ * Route pattern(s) this Worker should serve (e.g.
984
+ * `"sentinel.theholocron.dev/*"`). Omit for a Worker reached only via
985
+ * its `*.workers.dev` URL.
986
+ */
987
+ routes?: string[];
988
+ }
989
+ interface DeployScriptResult {
990
+ /** The deployed script's name at the provider. */
991
+ scriptName: string;
701
992
  }
702
993
  /**
703
- * Edge Worker / reverse-proxy management capability.
994
+ * Edge Worker management capability.
704
995
  *
705
- * Deploys and manages Worker scripts that proxy traffic to a configured
706
- * target. Used by `setup` to wire wiki custom-domain proxies when the
707
- * wiki provider requires a Worker in addition to the CNAME.
996
+ * Deploys and manages Worker scripts. `upsertProxy` is the existing,
997
+ * narrower case `setup` uses to wire wiki custom-domain reverse-proxies;
998
+ * `deployScript` is the general case — any Worker, any code, its own
999
+ * secrets — for a consumer that isn't a reverse-proxy (a webhook
1000
+ * receiver, for instance).
708
1001
  */
709
1002
  interface Workers extends ProviderIdentity {
710
1003
  readonly key: "workers";
@@ -714,6 +1007,17 @@ interface Workers extends ProviderIdentity {
714
1007
  * `config.headers` on each request.
715
1008
  */
716
1009
  upsertProxy(hostname: string, config: WikiProxyConfig): Promise<void>;
1010
+ /** Deploy (or update) an arbitrary Worker script, optionally with secrets and routes. */
1011
+ deployScript(name: string, config: DeployScriptConfig): Promise<DeployScriptResult>;
1012
+ /**
1013
+ * A `holocron doctor` smoke check — every other method either mutates or
1014
+ * needs a specific script/zone doctor has no way to supply up front.
1015
+ * Reports how many Worker scripts the token can see.
1016
+ */
1017
+ whoami?(): Promise<{
1018
+ ok: boolean;
1019
+ scripts: number;
1020
+ }>;
717
1021
  }
718
1022
  interface CapabilityImpls {
719
1023
  source: Source;
@@ -731,12 +1035,44 @@ interface CapabilityImpls {
731
1035
  analytics: Analytics;
732
1036
  errors: Errors;
733
1037
  logs: Logs;
1038
+ monitoring: Monitoring;
734
1039
  wiki: Wiki;
735
1040
  workers: Workers;
736
1041
  }
737
1042
  type CardinalityFor<K extends CapabilityKey> = (typeof CARDINALITY)[K];
738
1043
  /** Resolved runtime shape: single → one impl; many → array. */
739
1044
  type ResolvedCapability<K extends CapabilityKey> = CardinalityFor<K> extends "many" ? CapabilityImpls[K][] : CapabilityImpls[K];
1045
+ //#endregion
1046
+ //#region src/plugin/capabilities/capabilities.d.ts
1047
+ declare const CARDINALITY: {
1048
+ readonly source: "single";
1049
+ readonly ci: "single";
1050
+ readonly secrets: "single";
1051
+ readonly environments: "single";
1052
+ readonly issues: "single";
1053
+ readonly deployment: "single";
1054
+ readonly storage: "single";
1055
+ readonly auth: "single";
1056
+ readonly vault: "single";
1057
+ readonly dns: "single";
1058
+ readonly tooling: "many";
1059
+ readonly notifications: "many";
1060
+ readonly analytics: "many";
1061
+ readonly errors: "single";
1062
+ readonly logs: "single";
1063
+ readonly monitoring: "single";
1064
+ readonly wiki: "single";
1065
+ readonly workers: "single";
1066
+ };
1067
+ /**
1068
+ * No capabilities are strictly required — repos without secrets (e.g. org
1069
+ * community health repos) legitimately omit vault. Plugins validate their
1070
+ * own requirements at call time.
1071
+ */
1072
+ declare const REQUIRED_CAPABILITIES: readonly CapabilityKey[];
1073
+ declare class WebhookVerificationError extends Error {
1074
+ name: string;
1075
+ }
740
1076
  declare function isMulti<K extends CapabilityKey>(key: K): CardinalityFor<K> extends "many" ? true : false;
741
1077
  //#endregion
742
- export { Analytics, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, EnsureResult, Environment, EnvironmentReviewer, Environments, Errors, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, Logs, NormalizedAuthUser, Notifications, PagesConfig, ParseWebhookInput, ProviderApiError, ProviderIdentity, PullRequest, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, TeamEntry, TeamPermission, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, Wiki, WikiDnsRecord, WikiProvisionOpts, WikiProxyConfig, Workers, isMulti };
1078
+ export { Analytics, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConnectionStringOptions, CreateAuthUserInput, DeployFunctionConfig, DeployFunctionResult, DeployScriptConfig, DeployScriptResult, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordRequest, DnsRecordType, EnsureResult, EnsureWebhookResult, EnsuredResource, Environment, EnvironmentReviewer, Environments, Errors, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, Logs, Monitoring, MonitoringAssertion, MonitoringAssertionComparison, MonitoringAssertionSource, MonitoringCheck, MonitoringCheckInput, MonitoringHttpMethod, NormalizedAuthUser, Notifications, NotificationsAccess, NotificationsChannelLayout, NotificationsLayout, PagesConfig, ParseWebhookInput, ProviderApiError, ProviderIdentity, PullRequest, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, ServerInspection, Source, StatusCategory, Storage, StorageBranch, TeamEntry, TeamPermission, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, Wiki, WikiProvisionOpts, WikiProxyConfig, Workers, isMulti };
@@ -1,5 +1,5 @@
1
1
  import { ProviderApiError } from "@theholocron/http-client";
2
- //#region src/plugin/capabilities.ts
2
+ //#region src/plugin/capabilities/capabilities.ts
3
3
  const CARDINALITY = {
4
4
  source: "single",
5
5
  ci: "single",
@@ -16,6 +16,7 @@ const CARDINALITY = {
16
16
  analytics: "many",
17
17
  errors: "single",
18
18
  logs: "single",
19
+ monitoring: "single",
19
20
  wiki: "single",
20
21
  workers: "single"
21
22
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/cli",
3
- "version": "5.0.0-alpha.19",
3
+ "version": "5.0.0-alpha.190",
4
4
  "description": "The Holocron CLI - a pluggable, capability-based orchestrator for spinning up and operating software projects.",
5
5
  "keywords": [
6
6
  "cli",
@@ -46,24 +46,31 @@
46
46
  "@napi-rs/keyring": "^1.3.0",
47
47
  "@sentry/node": "^10.70.0",
48
48
  "@theholocron/components-doc": "^1.6.0",
49
- "@theholocron/env-utils": "^1.6.0",
50
- "@theholocron/github-client": "^1.19.0",
51
- "@theholocron/http-client": "^1.19.0",
52
- "@theholocron/observability": "^0.3.0",
49
+ "@theholocron/env-utils": "^1.16.0",
50
+ "@theholocron/fs-utils": "^1.16.0",
51
+ "@theholocron/git-utils": "^1.16.0",
52
+ "@theholocron/github-client": "^1.43.1",
53
+ "@theholocron/http-client": "^1.43.1",
54
+ "@theholocron/json-utils": "^1.16.0",
55
+ "@theholocron/misc-utils": "^1.16.0",
56
+ "@theholocron/observability": "^0.6.0",
57
+ "@theholocron/pkg-utils": "^1.16.0",
53
58
  "@theholocron/registry-doc": "^1.12.0",
54
59
  "chalk": "^6.0.0",
60
+ "dotenv": "^17.4.2",
55
61
  "ora": "^9.4.1",
56
62
  "pino": "^10.3.1",
57
63
  "pino-pretty": "^13.1.3",
58
64
  "posthog-node": "^5.51.6",
59
65
  "yargs": "^18.1.0",
60
- "@theholocron/astromech": "5.0.0-alpha.19",
61
- "@theholocron/datapad": "5.0.0-alpha.19"
66
+ "@theholocron/astromech": "5.0.0-alpha.190",
67
+ "@theholocron/datapad": "5.0.0-alpha.190"
62
68
  },
63
69
  "devDependencies": {
64
- "@theholocron/eslint-config": "^8.0.0",
65
- "@theholocron/tsconfig": "^8.0.0",
66
- "@theholocron/vitest-config": "^8.0.0",
70
+ "@theholocron/eslint-config": "^8.8.2",
71
+ "@theholocron/rollup-plugin-transform-template": "^8.8.2",
72
+ "@theholocron/tsconfig": "^8.8.2",
73
+ "@theholocron/vitest-config": "^8.8.2",
67
74
  "@types/node": "^26",
68
75
  "@types/yargs": "^17.0.35",
69
76
  "@vitest/coverage-v8": "^4.1.11",
@@ -72,10 +79,9 @@
72
79
  "eslint-plugin-n": "^18.3.0",
73
80
  "globals": "^17.11.0",
74
81
  "tsdown": "^0.22.14",
75
- "tsx": "4.23.12",
82
+ "tsx": "4.23.15",
76
83
  "typescript": "^5.9.3",
77
- "vitest": "^4.1.11",
78
- "@theholocron/rollup-plugin-transform-template": "5.0.0-alpha.19"
84
+ "vitest": "^4.1.11"
79
85
  },
80
86
  "engines": {
81
87
  "node": ">=22.0.0"