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