@theholocron/cli 2.0.0-alpha.5 → 2.0.0-alpha.50

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
@@ -1,3 +1,5 @@
1
+ <!-- editorconfig-checker-disable-file -->
2
+
1
3
  # `@theholocron/cli`
2
4
 
3
5
  The Holocron CLI — a pluggable, capability-based orchestrator for
@@ -5,24 +7,106 @@ spinning up and operating software projects.
5
7
 
6
8
  ## Install
7
9
 
10
+ <!-- prettier-ignore -->
8
11
  ```bash
9
12
  npm i -g @theholocron/cli@alpha
10
13
  holocron --help
14
+
15
+ ```
16
+
17
+ ## Config file
18
+
19
+ Holocron reads `holocron.config.{json,js,ts}` from the project root
20
+ (priority: json → js → ts).
21
+
22
+ **JSON** (simplest):
23
+
24
+ <!-- prettier-ignore -->
25
+ ```jsonc
26
+ // holocron.config.json
27
+ {
28
+ "project": { "name": "my-app" },
29
+ "providers": {
30
+ "vault": ["1password", { "vault": "my-app" }],
31
+ "source": "github",
32
+ },
33
+ }
34
+
35
+ ```
36
+
37
+ **JS/TS** — use `defineConfig` for autocomplete and type-checking:
38
+
39
+ <!-- prettier-ignore -->
40
+ ```ts
41
+ // holocron.config.ts
42
+ import { defineConfig } from "@theholocron/cli";
43
+
44
+ export default defineConfig({
45
+ project: { name: "my-app" },
46
+ providers: {
47
+ vault: ["1password", { vault: "my-app" }],
48
+ source: "github",
49
+ },
50
+ });
51
+
52
+ ```
53
+
54
+ ### Shareable configs
55
+
56
+ **Level 1 — per-capability config packages.** Reference a published
57
+ package in any provider slot and Holocron resolves its bundled
58
+ `{ provider, options }` automatically. Per-project options merge on
59
+ top (project wins):
60
+
61
+ <!-- prettier-ignore -->
62
+ ```ts
63
+ providers: {
64
+ vault: '@acme/holocron-vault', // preset only
65
+ source: ['@acme/holocron-github', { repo: 'x' }], // preset + override
66
+ }
67
+
68
+ ```
69
+
70
+ A capability config package exports a `CapabilityConfigPackage` default:
71
+
72
+ <!-- prettier-ignore -->
73
+ ```ts
74
+ import type { CapabilityConfigPackage } from "@theholocron/cli";
75
+ export default {
76
+ provider: "1password",
77
+ options: { vault: "acme-app" },
78
+ } satisfies CapabilityConfigPackage;
79
+
80
+ ```
81
+
82
+ **Level 2 — whole-config presets.** Because the config file can be
83
+ JS/TS, a shared base is just an import:
84
+
85
+ <!-- prettier-ignore -->
86
+ ```ts
87
+ // holocron.config.ts
88
+ import { acmeConfig } from "@acme/holocron-config";
89
+ export default acmeConfig;
90
+
11
91
  ```
12
92
 
13
93
  ## What's in here
14
94
 
15
95
  - `src/capabilities/` — the 14 capability interfaces that providers
16
- implement
17
- - `src/config.ts` — `holocron.config.json` parser + plugin resolution
96
+ implement
97
+ - `src/config.ts` — config schema, `defineConfig`, `resolveConfig`,
98
+ `CapabilityConfigPackage`
99
+ - `src/load-config.ts` — `loadConfig` — reads JSON/JS/TS config files
100
+ - `src/define-config.ts` — `defineConfig` typed pass-through
101
+ - `src/loader.ts` — `PluginLoader` — dynamic-imports plugins, resolves
102
+ capability config packages, builds the capability registry
18
103
  - `src/cli.ts` — yargs entry, dispatches subcommands
19
- - `src/commands/` — `setup`, `doctor`, `deploy`, `secret set`,
20
- `secrets sync`, `npm publish-initial`
104
+ - `src/commands/` — `setup`, `sync`, `doctor`, `deploy`, `secret set`,
105
+ `secrets sync`, `npm publish-initial`, `sync-github`, `upgrade node`,
106
+ `plugin create`, `auth`
21
107
 
22
108
  ## Status
23
109
 
24
- **`v2.0.0-alpha.0`** — published on npm under the `alpha` dist-tag.
25
- [Release notes](https://github.com/theholocron/holocron/releases/tag/v2.0.0-alpha.0).
26
- Design in
27
- [`.notes/tech-architecture.spec.md`](../../.notes/tech-architecture.spec.md).
28
- APIs may still shift before stable v2.0.0.
110
+ Published on npm under the `alpha` dist-tag. APIs may still shift before
111
+ stable v2.0.0. Design in
112
+ [`.notes/archive/tech-architecture.spec.md`](../../.notes/archive/tech-architecture.spec.md).
@@ -1,2 +1,552 @@
1
- import { $ as StorageBranch, A as EnvironmentReviewer, B as ParseWebhookInput, C as DeploymentTarget, D as DnsRecordType, E as DnsRecord, F as LifecycleResult, G as RepoSettings, H as ProviderIdentity, I as LifecycleSlot, J as SecretScope, K as ResolvedCapability, L as NormalizedAuthUser, M as Issue, N as IssueSearchFilter, O as EnsureResult, P as Issues, Q as Storage, R as Notifications, S as DeploymentRecord, T as Dns, U as REQUIRED_CAPABILITIES, V as ProviderApiError, W as RepoRef, X as Source, Y as Secrets, Z as StatusCategory, _ as ConnectionStringOptions, a as AuthEventType, at as WebhookDashboardInfo, b as DeploymentProject, c as CARDINALITY, d as Cardinality, et as Tooling, f as CardinalityFor, g as CiRunStatus, h as CiRunFilter, i as AuthEvent, it as Vault, j as Environments, k as Environment, l as CapabilityImpls, m as CiRun, n as Auth, nt as TrackerDoctorReport, o as AuthIdentity, ot as WebhookVerificationError, p as Ci, q as Ruleset, r as AuthDescription, rt as TrackerUser, s as AuthUser, st as isMulti, t as Analytics, tt as ToolingDoctorReport, u as CapabilityKey, v as CreateAuthUserInput, w as DeploymentTrigger, x as DeploymentProjectSettings, y as Deployment, z as Observability } from "../index-jxPVFH7-.mjs";
2
- 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, Issue, IssueSearchFilter, Issues, LifecycleResult, LifecycleSlot, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, isMulti };
1
+ import { ProviderApiError } from "@theholocron/http-client";
2
+
3
+ //#region src/capabilities/index.d.ts
4
+ /**
5
+ * Capability interfaces — the contracts that providers implement.
6
+ *
7
+ * Each capability has a stable key (`'source'`, `'ci'`, …) and a
8
+ * cardinality (`'single'` = one provider; `'many'` = several active
9
+ * at once). The cardinality is part of the type contract via
10
+ * `CardinalityFor<K>` so config resolution + command code can branch
11
+ * statically.
12
+ *
13
+ * See `.notes/tech-architecture.spec.md` for the design narrative
14
+ * (status: proposed, issue: #74).
15
+ */
16
+ type CapabilityKey = "source" | "ci" | "secrets" | "environments" | "issues" | "deployment" | "storage" | "auth" | "vault" | "dns" | "tooling" | "notifications" | "analytics" | "observability";
17
+ type Cardinality = "single" | "many";
18
+ declare const CARDINALITY: {
19
+ readonly source: "single";
20
+ readonly ci: "single";
21
+ readonly secrets: "single";
22
+ readonly environments: "single";
23
+ readonly issues: "single";
24
+ readonly deployment: "single";
25
+ readonly storage: "single";
26
+ readonly auth: "single";
27
+ readonly vault: "single";
28
+ readonly dns: "single";
29
+ readonly tooling: "many";
30
+ readonly notifications: "many";
31
+ readonly analytics: "many";
32
+ readonly observability: "many";
33
+ };
34
+ /**
35
+ * No capabilities are strictly required — repos without secrets (e.g. org
36
+ * community health repos) legitimately omit vault. Plugins validate their
37
+ * own requirements at call time.
38
+ */
39
+ declare const REQUIRED_CAPABILITIES: readonly CapabilityKey[];
40
+ interface ProviderIdentity {
41
+ readonly key: CapabilityKey;
42
+ readonly providerName: string;
43
+ }
44
+ interface Ruleset {
45
+ id: number;
46
+ name: string;
47
+ enforcement: "active" | "evaluate" | "disabled";
48
+ target?: string;
49
+ }
50
+ interface RepoSettings {
51
+ allow_squash_merge?: boolean;
52
+ allow_merge_commit?: boolean;
53
+ allow_rebase_merge?: boolean;
54
+ allow_auto_merge?: boolean;
55
+ /** Always suggest updating PR branches when the base branch has new commits. */
56
+ allow_update_branch?: boolean;
57
+ delete_branch_on_merge?: boolean;
58
+ default_branch?: string;
59
+ has_issues?: boolean;
60
+ has_discussions?: boolean;
61
+ has_projects?: boolean;
62
+ has_wiki?: boolean;
63
+ }
64
+ interface RepoRef {
65
+ owner: string;
66
+ name: string;
67
+ defaultBranch: string;
68
+ }
69
+ interface LabelDef {
70
+ readonly name: string;
71
+ readonly color: string;
72
+ readonly description: string;
73
+ }
74
+ interface Source extends ProviderIdentity {
75
+ readonly key: "source";
76
+ /** Auth sanity-check. Throws ProviderApiError on auth failure. */
77
+ whoami(): Promise<{
78
+ login: string;
79
+ }>;
80
+ getRepo(): Promise<RepoRef>;
81
+ listRulesets(): Promise<Ruleset[]>;
82
+ createRuleset(payload: Record<string, unknown>): Promise<Ruleset>;
83
+ updateRuleset(id: number, payload: Record<string, unknown>): Promise<Ruleset>;
84
+ updateRepoSettings(settings: RepoSettings): Promise<void>;
85
+ /**
86
+ * Classic branch protection — fallback for private repos on free plans
87
+ * where the Rulesets API (requires Team+) returns 403.
88
+ */
89
+ protectBranch(branch: string, payload: Record<string, unknown>): Promise<void>;
90
+ enableVulnerabilityAlerts(): Promise<void>;
91
+ enableAutomatedSecurityFixes(): Promise<void>;
92
+ enableSecretScanning(): Promise<void>;
93
+ enablePrivateVulnerabilityReporting(): Promise<void>;
94
+ /**
95
+ * Enables the dependency graph and automatic dependency snapshot
96
+ * submission. Also enables secret-scanning validity checks and
97
+ * non-provider pattern detection — these require GitHub Advanced
98
+ * Security at the org level; the call is accepted but may be a no-op
99
+ * until that is configured.
100
+ */
101
+ enableDependencyGraph(): Promise<void>;
102
+ /**
103
+ * Enables CodeQL default setup with the extended query suite and
104
+ * `threat_model: all` (scans both remote and local exploit paths).
105
+ * Triggers a new analysis run; returns the run id.
106
+ */
107
+ enableCodeScanning(): Promise<string>;
108
+ /**
109
+ * Disables CodeQL default setup. Required when the repo uses an advanced
110
+ * CodeQL workflow instead — GitHub rejects SARIF from advanced workflows
111
+ * while default setup is active.
112
+ */
113
+ disableDefaultCodeScanning(): Promise<void>;
114
+ listWorkflowFiles(): Promise<string[]>;
115
+ readWorkflowFile(name: string): Promise<string | null>;
116
+ writeWorkflowFile(name: string, contents: string): Promise<void>;
117
+ removeWorkflowFile(name: string): Promise<void>;
118
+ /**
119
+ * Write an arbitrary file relative to the repo root. Used for
120
+ * provisioning config files that live outside `.github/workflows/`
121
+ * (e.g. `.github/dependabot.yml`).
122
+ */
123
+ writeRepoFile(path: string, contents: string): Promise<void>;
124
+ /**
125
+ * Idempotently sync repo labels to a canonical set. Creates missing
126
+ * labels, patches color/description drift, deletes stale labels.
127
+ * Optional — providers that have no label concept omit this.
128
+ */
129
+ syncLabels?(canonical: ReadonlyArray<LabelDef>, stale: ReadonlyArray<string>): Promise<string>;
130
+ /**
131
+ * Set org-level custom property values on the repo.
132
+ * Optional — providers that don't support custom properties omit this.
133
+ */
134
+ syncProperties?(values: Record<string, string>): Promise<string>;
135
+ /**
136
+ * Replace the repo's topic set with the supplied list.
137
+ * Optional — providers that don't support topics omit this.
138
+ */
139
+ syncTopics?(topics: string[]): Promise<string>;
140
+ }
141
+ type CiRunStatus = "queued" | "in_progress" | "completed" | "cancelled" | "failure" | "success" | "skipped";
142
+ interface CiRun {
143
+ id: string | number;
144
+ workflowName: string;
145
+ branch: string;
146
+ sha: string;
147
+ status: CiRunStatus;
148
+ url: string;
149
+ startedAt: string;
150
+ completedAt?: string;
151
+ }
152
+ interface CiRunFilter {
153
+ branch?: string;
154
+ status?: CiRunStatus;
155
+ limit?: number;
156
+ }
157
+ interface Ci extends ProviderIdentity {
158
+ readonly key: "ci";
159
+ listRuns(filter?: CiRunFilter): Promise<CiRun[]>;
160
+ getRun(id: string | number): Promise<CiRun>;
161
+ }
162
+ type SecretScope = {
163
+ kind: "repo";
164
+ } | {
165
+ kind: "environment";
166
+ name: string;
167
+ } | {
168
+ kind: "organization";
169
+ name: string;
170
+ };
171
+ interface Secrets extends ProviderIdentity {
172
+ readonly key: "secrets";
173
+ /** List secret NAMES (not values) at the given scope. */
174
+ listSecrets(scope: SecretScope): Promise<string[]>;
175
+ /** Idempotent upsert. Adapter handles encryption. */
176
+ setSecret(scope: SecretScope, name: string, value: string): Promise<void>;
177
+ deleteSecret(scope: SecretScope, name: string): Promise<void>;
178
+ }
179
+ interface EnvironmentReviewer {
180
+ type: "User" | "Team";
181
+ /** Numeric id — GitHub's reviewer API silently ignores login strings. */
182
+ id: number;
183
+ }
184
+ interface Environment {
185
+ name: string;
186
+ reviewers?: EnvironmentReviewer[];
187
+ waitTimer?: number;
188
+ preventSelfReview?: boolean;
189
+ }
190
+ interface Environments extends ProviderIdentity {
191
+ readonly key: "environments";
192
+ listEnvironments(): Promise<Environment[]>;
193
+ upsertEnvironment(env: Environment): Promise<void>;
194
+ deleteEnvironment(name: string): Promise<void>;
195
+ }
196
+ type LifecycleSlot = "inProgress" | "inReview" | "done";
197
+ type StatusCategory = "open" | "in-progress" | "in-review" | "done" | "other";
198
+ interface TrackerUser {
199
+ id: string;
200
+ displayName: string;
201
+ emailAddress?: string;
202
+ }
203
+ interface Issue {
204
+ /** Human-readable key — "#42" for GitHub, "RANDO-42" for Jira. */
205
+ key: string;
206
+ /** Internal opaque id. */
207
+ id: string;
208
+ summary: string;
209
+ body?: string;
210
+ status: string;
211
+ statusCategory: StatusCategory;
212
+ assignee: TrackerUser | null;
213
+ updated: string;
214
+ url?: string;
215
+ }
216
+ interface IssueSearchFilter {
217
+ /** Restrict to issues assigned to a specific id, or 'currentUser'. */
218
+ assignee?: string | "currentUser";
219
+ /** Exclude issues in the `done` category. */
220
+ openOnly?: boolean;
221
+ /** Max number of issues to return. Adapters apply a sensible default. */
222
+ limit?: number;
223
+ }
224
+ interface LifecycleResult {
225
+ /** False when no API write happened (already at target state). */
226
+ transitioned: boolean;
227
+ /** Status name the issue is in after this call. */
228
+ status: string;
229
+ /** Adapter-specific note (e.g., "label set" / "closed (completed)"). */
230
+ via?: string;
231
+ }
232
+ interface TrackerDoctorReport {
233
+ /** "Authenticated as ..." subject for the spinner. */
234
+ authedAs: string;
235
+ /** Free-form "Project: RANDO" / "Repo: rando-id/rando" identifier. */
236
+ projectLabel: string;
237
+ /** Status values the adapter exposes. */
238
+ statuses: Array<{
239
+ name: string;
240
+ category: StatusCategory;
241
+ }>;
242
+ /**
243
+ * Per-lifecycle-slot readiness check. `resolved` indicates whether
244
+ * the configured value actually maps to something the tracker
245
+ * recognizes; the `note` is the rendered explanation.
246
+ */
247
+ lifecycle: Array<{
248
+ slot: LifecycleSlot;
249
+ value: string | null;
250
+ resolved: boolean;
251
+ note: string;
252
+ }>;
253
+ }
254
+ interface Issues extends ProviderIdentity {
255
+ readonly key: "issues";
256
+ /** Currently-authenticated user. */
257
+ getMyself(): Promise<TrackerUser>;
258
+ search(filter: IssueSearchFilter): Promise<Issue[]>;
259
+ get(key: string): Promise<Issue>;
260
+ create(input: {
261
+ summary: string;
262
+ body?: string;
263
+ labels?: string[]; /** Numeric id or exact title (case-insensitive). */
264
+ milestone?: string;
265
+ }): Promise<{
266
+ key: string;
267
+ }>;
268
+ /** Idempotent — `transitioned: false` if the issue is already at the target. */
269
+ transition(key: string, slot: LifecycleSlot): Promise<LifecycleResult>;
270
+ comment(key: string, body: string): Promise<void>;
271
+ doctor(): Promise<TrackerDoctorReport>;
272
+ }
273
+ /** Env-var scope on the deploy platform. */
274
+ type DeploymentTarget = "development" | "preview" | "production";
275
+ /**
276
+ * Named deployment trigger target — `undefined` means a branch
277
+ * preview (no named environment).
278
+ */
279
+ type DeploymentTrigger = "production" | "staging";
280
+ interface DeploymentProject {
281
+ id: string;
282
+ name: string;
283
+ framework?: string;
284
+ /** True when the project is linked to a Git provider. */
285
+ gitLinked?: boolean;
286
+ rootDirectory?: string | null;
287
+ }
288
+ interface DeploymentProjectSettings {
289
+ previewDeploymentsDisabled?: boolean;
290
+ /** Vercel-specific: whether the GitHub integration creates deployments
291
+ * for every push (false → only on-demand triggers). */
292
+ gitProviderCreateDeployments?: boolean;
293
+ }
294
+ interface DeploymentRecord {
295
+ id: string;
296
+ url: string;
297
+ /** Branch this deployment was made from (null if not git-sourced). */
298
+ branch: string | null;
299
+ /** Named environment if one was targeted; undefined for branch previews. */
300
+ target?: DeploymentTrigger;
301
+ status: "queued" | "building" | "ready" | "error" | "cancelled";
302
+ }
303
+ interface Deployment extends ProviderIdentity {
304
+ readonly key: "deployment";
305
+ listProjects(): Promise<DeploymentProject[]>;
306
+ /** Create if missing, otherwise return existing. Idempotent. */
307
+ ensureProject(input: {
308
+ name: string;
309
+ framework?: string; /** "owner/repo" — passed when linking to a Git provider. */
310
+ repo?: string;
311
+ rootDirectory?: string;
312
+ }): Promise<DeploymentProject>;
313
+ updateProjectSettings(projectId: string, settings: DeploymentProjectSettings): Promise<DeploymentProject>;
314
+ listEnvVars(projectId: string, target: DeploymentTarget): Promise<string[]>;
315
+ setEnvVar(projectId: string, target: DeploymentTarget, name: string, value: string): Promise<void>;
316
+ /**
317
+ * Kick off a deployment of the given branch. Omit `target` for a
318
+ * branch preview; pass `'production'` / `'staging'` to deploy into
319
+ * a named environment.
320
+ */
321
+ triggerDeployment(input: {
322
+ projectId: string;
323
+ branch: string;
324
+ target?: DeploymentTrigger;
325
+ }): Promise<DeploymentRecord>;
326
+ getDeployment(deploymentId: string): Promise<DeploymentRecord>;
327
+ }
328
+ interface StorageBranch {
329
+ id: string;
330
+ name: string;
331
+ /** Parent branch id; null for the root/main branch. */
332
+ parentId: string | null;
333
+ createdAt: string;
334
+ }
335
+ interface ConnectionStringOptions {
336
+ /** Use the pooled (PgBouncer) URL when available. Defaults to false. */
337
+ pooled?: boolean;
338
+ }
339
+ interface Storage extends ProviderIdentity {
340
+ readonly key: "storage";
341
+ /**
342
+ * Connection string for the given scope. Scope is provider-specific:
343
+ *
344
+ * - branch-based providers (Neon, PlanetScale): scope = branch
345
+ * name or id
346
+ * - flat providers (single Postgres instance): scope is ignored
347
+ *
348
+ * Callers (or the orchestrator) decide how a deploy target maps to
349
+ * a scope; the storage plugin doesn't own that mapping.
350
+ */
351
+ getConnectionString(scope: string, options?: ConnectionStringOptions): Promise<string>;
352
+ listBranches?(): Promise<StorageBranch[]>;
353
+ createBranch?(input: {
354
+ name: string;
355
+ from?: string;
356
+ }): Promise<StorageBranch>;
357
+ destroyBranch?(branch: string): Promise<void>;
358
+ /** Restore one branch to match another (e.g., reset preview → main). */
359
+ resetBranch?(input: {
360
+ branch: string;
361
+ from: string;
362
+ }): Promise<void>;
363
+ /**
364
+ * Provider-specific feature toggle. For Postgres providers this is
365
+ * `CREATE EXTENSION IF NOT EXISTS ...` per branch.
366
+ */
367
+ enableExtension?(input: {
368
+ branch: string;
369
+ extension: string;
370
+ }): Promise<void>;
371
+ }
372
+ interface AuthDescription {
373
+ provider: string;
374
+ /** Env-var names the app needs at runtime (CLERK_PUBLISHABLE_KEY, etc.). */
375
+ envKeys: string[];
376
+ }
377
+ interface AuthIdentity {
378
+ provider: string;
379
+ /** Provider-specific health signal (user count, role, account name, etc.). */
380
+ details?: Record<string, unknown>;
381
+ }
382
+ interface AuthUser {
383
+ id: string;
384
+ email: string;
385
+ }
386
+ interface CreateAuthUserInput {
387
+ email: string;
388
+ password: string;
389
+ firstName?: string;
390
+ lastName?: string;
391
+ }
392
+ interface WebhookDashboardInfo {
393
+ url: string;
394
+ }
395
+ interface Auth extends ProviderIdentity {
396
+ readonly key: "auth";
397
+ /** Env-var keys the runtime app needs. */
398
+ describe(): Promise<AuthDescription>;
399
+ /** Reachability probe — proves the configured key works. */
400
+ whoami(): Promise<AuthIdentity>;
401
+ /** Idempotent webhook backend provisioning (Clerk: Svix app). */
402
+ ensureWebhookApp?(): Promise<{
403
+ alreadyExists: boolean;
404
+ }>;
405
+ /** Deep-link to the provider's webhook config dashboard. */
406
+ getWebhookDashboardUrl?(): Promise<WebhookDashboardInfo>;
407
+ /** Seed a user (test fixtures, admin bootstrap). */
408
+ createUser?(input: CreateAuthUserInput): Promise<AuthUser>;
409
+ /** Wire the auth provider's webhook into the project's repo. */
410
+ syncWebhook?(input: {
411
+ repo: string;
412
+ secretRef: string;
413
+ }): Promise<void>;
414
+ }
415
+ type AuthEventType = "user.created" | "user.updated" | "user.deleted";
416
+ interface NormalizedAuthUser {
417
+ id: string;
418
+ email: string;
419
+ firstName?: string | null;
420
+ lastName?: string | null;
421
+ /** Provider-native fields preserved verbatim for consumers that need them. */
422
+ raw?: Record<string, unknown>;
423
+ }
424
+ interface AuthEvent {
425
+ type: AuthEventType;
426
+ user: NormalizedAuthUser;
427
+ /** ISO timestamp of when the event occurred. */
428
+ occurredAt: string;
429
+ }
430
+ interface ParseWebhookInput {
431
+ /** Raw request body (string or Buffer). */
432
+ body: string | Buffer;
433
+ /** Incoming HTTP headers — needed for signature verification. */
434
+ headers: Record<string, string | string[] | undefined>;
435
+ /** The signing secret the auth provider issued for this webhook endpoint. */
436
+ signingSecret: string;
437
+ }
438
+ declare class WebhookVerificationError extends Error {
439
+ name: string;
440
+ }
441
+ interface EnsureResult {
442
+ /** True when the resource already existed (idempotent no-op). */
443
+ alreadyExists: boolean;
444
+ }
445
+ interface Vault extends ProviderIdentity {
446
+ readonly key: "vault";
447
+ /**
448
+ * Read a secret by reference. The reference format is
449
+ * provider-specific (1P: "op://Vault/Item/field"; HashiCorp Vault:
450
+ * "kv/path#field"; etc.). Adapters validate the reference shape.
451
+ */
452
+ read(reference: string): Promise<string>;
453
+ /** Write or update a secret. */
454
+ write(reference: string, value: string): Promise<void>;
455
+ /** List secret keys available to the project. */
456
+ list(): Promise<string[]>;
457
+ /**
458
+ * Optional environment notion within the vault (e.g., 1P
459
+ * Environments — named KEY=VALUE bundles). Adapters without
460
+ * environments return [].
461
+ */
462
+ environments?(): Promise<string[]>;
463
+ /**
464
+ * Optional bulk read of an environment's KEY=VALUE pairs. Powers
465
+ * the `holocron secrets sync` flow where the orchestrator pulls a
466
+ * whole environment from the vault then fans the values out to
467
+ * destinations (CI secrets, deployment env vars, local .env).
468
+ */
469
+ readEnvironment?(environmentId: string): Promise<Record<string, string>>;
470
+ /**
471
+ * Optional — create the top-level project container in the vault
472
+ * if it does not exist. Idempotent: `alreadyExists: true` when the
473
+ * project was already there. Providers whose data model has no
474
+ * project notion (or that gate this behind a paid tier) omit this.
475
+ */
476
+ ensureProject?(name: string): Promise<EnsureResult>;
477
+ /**
478
+ * Optional — create a named environment/config inside a project
479
+ * (e.g., Doppler config `dev` / `stg` / `prd`). Idempotent.
480
+ * Providers whose data model has a single flat namespace omit this.
481
+ */
482
+ ensureEnvironment?(project: string, name: string): Promise<EnsureResult>;
483
+ }
484
+ type DnsRecordType = "A" | "AAAA" | "CNAME" | "TXT" | "MX" | "NS" | "SRV" | "CAA";
485
+ interface DnsRecord {
486
+ id?: string;
487
+ type: DnsRecordType;
488
+ name: string;
489
+ content: string;
490
+ ttl?: number;
491
+ priority?: number;
492
+ }
493
+ interface Dns extends ProviderIdentity {
494
+ readonly key: "dns";
495
+ listRecords(domain: string): Promise<DnsRecord[]>;
496
+ upsertRecord(domain: string, record: DnsRecord): Promise<DnsRecord>;
497
+ deleteRecord(domain: string, id: string): Promise<void>;
498
+ }
499
+ interface ToolingDoctorReport {
500
+ ok: boolean;
501
+ message: string;
502
+ }
503
+ interface Tooling extends ProviderIdentity {
504
+ readonly key: "tooling";
505
+ /** Sync the tool's authoritative state from the repo. */
506
+ sync(): Promise<void>;
507
+ doctor(): Promise<ToolingDoctorReport>;
508
+ }
509
+ interface Notifications extends ProviderIdentity {
510
+ readonly key: "notifications";
511
+ /**
512
+ * Send a message. `channel` is provider-specific (Slack channel id,
513
+ * Discord webhook url-name, etc.); adapters resolve from config.
514
+ */
515
+ send(channel: string, message: string): Promise<void>;
516
+ }
517
+ interface Analytics extends ProviderIdentity {
518
+ readonly key: "analytics";
519
+ describe(): Promise<{
520
+ provider: string;
521
+ dsnEnvKey: string;
522
+ }>;
523
+ }
524
+ interface Observability extends ProviderIdentity {
525
+ readonly key: "observability";
526
+ describe(): Promise<{
527
+ provider: string;
528
+ dsnEnvKey: string;
529
+ }>;
530
+ }
531
+ interface CapabilityImpls {
532
+ source: Source;
533
+ ci: Ci;
534
+ secrets: Secrets;
535
+ environments: Environments;
536
+ issues: Issues;
537
+ deployment: Deployment;
538
+ storage: Storage;
539
+ auth: Auth;
540
+ vault: Vault;
541
+ dns: Dns;
542
+ tooling: Tooling;
543
+ notifications: Notifications;
544
+ analytics: Analytics;
545
+ observability: Observability;
546
+ }
547
+ type CardinalityFor<K extends CapabilityKey> = (typeof CARDINALITY)[K];
548
+ /** Resolved runtime shape: single → one impl; many → array. */
549
+ type ResolvedCapability<K extends CapabilityKey> = CardinalityFor<K> extends "many" ? CapabilityImpls[K][] : CapabilityImpls[K];
550
+ declare function isMulti<K extends CapabilityKey>(key: K): CardinalityFor<K> extends "many" ? true : false;
551
+ //#endregion
552
+ 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, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, isMulti };