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

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.
@@ -11,8 +11,8 @@
11
11
  * See `.notes/tech-architecture.spec.md` for the design narrative
12
12
  * (status: proposed, issue: #74).
13
13
  */
14
- type CapabilityKey = 'source' | 'ci' | 'secrets' | 'environments' | 'issues' | 'deployment' | 'storage' | 'auth' | 'vault' | 'dns' | 'tooling' | 'notifications' | 'analytics' | 'observability';
15
- type Cardinality = 'single' | 'many';
14
+ type CapabilityKey = "source" | "ci" | "secrets" | "environments" | "issues" | "deployment" | "storage" | "auth" | "vault" | "dns" | "tooling" | "notifications" | "analytics" | "observability";
15
+ type Cardinality = "single" | "many";
16
16
  declare const CARDINALITY: {
17
17
  readonly source: "single";
18
18
  readonly ci: "single";
@@ -29,7 +29,11 @@ declare const CARDINALITY: {
29
29
  readonly analytics: "many";
30
30
  readonly observability: "many";
31
31
  };
32
- /** Vault is required; everything else is optional in the config. */
32
+ /**
33
+ * No capabilities are strictly required — repos without secrets (e.g. org
34
+ * community health repos) legitimately omit vault. Plugins validate their
35
+ * own requirements at call time.
36
+ */
33
37
  declare const REQUIRED_CAPABILITIES: readonly CapabilityKey[];
34
38
  interface ProviderIdentity {
35
39
  readonly key: CapabilityKey;
@@ -50,7 +54,7 @@ declare class ProviderApiError extends Error {
50
54
  interface Ruleset {
51
55
  id: number;
52
56
  name: string;
53
- enforcement: 'active' | 'evaluate' | 'disabled';
57
+ enforcement: "active" | "evaluate" | "disabled";
54
58
  target?: string;
55
59
  }
56
60
  interface RepoSettings {
@@ -58,10 +62,14 @@ interface RepoSettings {
58
62
  allow_merge_commit?: boolean;
59
63
  allow_rebase_merge?: boolean;
60
64
  allow_auto_merge?: boolean;
65
+ /** Always suggest updating PR branches when the base branch has new commits. */
66
+ allow_update_branch?: boolean;
61
67
  delete_branch_on_merge?: boolean;
62
68
  default_branch?: string;
63
69
  has_issues?: boolean;
64
70
  has_discussions?: boolean;
71
+ has_projects?: boolean;
72
+ has_wiki?: boolean;
65
73
  }
66
74
  interface RepoRef {
67
75
  owner: string;
@@ -69,7 +77,7 @@ interface RepoRef {
69
77
  defaultBranch: string;
70
78
  }
71
79
  interface Source extends ProviderIdentity {
72
- readonly key: 'source';
80
+ readonly key: "source";
73
81
  /** Auth sanity-check. Throws ProviderApiError on auth failure. */
74
82
  whoami(): Promise<{
75
83
  login: string;
@@ -79,16 +87,41 @@ interface Source extends ProviderIdentity {
79
87
  createRuleset(payload: Record<string, unknown>): Promise<Ruleset>;
80
88
  updateRuleset(id: number, payload: Record<string, unknown>): Promise<Ruleset>;
81
89
  updateRepoSettings(settings: RepoSettings): Promise<void>;
90
+ /**
91
+ * Classic branch protection — fallback for private repos on free plans
92
+ * where the Rulesets API (requires Team+) returns 403.
93
+ */
94
+ protectBranch(branch: string, payload: Record<string, unknown>): Promise<void>;
82
95
  enableVulnerabilityAlerts(): Promise<void>;
83
96
  enableAutomatedSecurityFixes(): Promise<void>;
84
97
  enableSecretScanning(): Promise<void>;
85
98
  enablePrivateVulnerabilityReporting(): Promise<void>;
99
+ /**
100
+ * Enables the dependency graph and automatic dependency snapshot
101
+ * submission. Also enables secret-scanning validity checks and
102
+ * non-provider pattern detection — these require GitHub Advanced
103
+ * Security at the org level; the call is accepted but may be a no-op
104
+ * until that is configured.
105
+ */
106
+ enableDependencyGraph(): Promise<void>;
107
+ /**
108
+ * Enables CodeQL default setup with the extended query suite and
109
+ * `threat_model: all` (scans both remote and local exploit paths).
110
+ * Triggers a new analysis run; returns the run id.
111
+ */
112
+ enableCodeScanning(): Promise<string>;
86
113
  listWorkflowFiles(): Promise<string[]>;
87
114
  readWorkflowFile(name: string): Promise<string | null>;
88
115
  writeWorkflowFile(name: string, contents: string): Promise<void>;
89
116
  removeWorkflowFile(name: string): Promise<void>;
117
+ /**
118
+ * Write an arbitrary file relative to the repo root. Used for
119
+ * provisioning config files that live outside `.github/workflows/`
120
+ * (e.g. `.github/dependabot.yml`).
121
+ */
122
+ writeRepoFile(path: string, contents: string): Promise<void>;
90
123
  }
91
- type CiRunStatus = 'queued' | 'in_progress' | 'completed' | 'cancelled' | 'failure' | 'success' | 'skipped';
124
+ type CiRunStatus = "queued" | "in_progress" | "completed" | "cancelled" | "failure" | "success" | "skipped";
92
125
  interface CiRun {
93
126
  id: string | number;
94
127
  workflowName: string;
@@ -105,21 +138,21 @@ interface CiRunFilter {
105
138
  limit?: number;
106
139
  }
107
140
  interface Ci extends ProviderIdentity {
108
- readonly key: 'ci';
141
+ readonly key: "ci";
109
142
  listRuns(filter?: CiRunFilter): Promise<CiRun[]>;
110
143
  getRun(id: string | number): Promise<CiRun>;
111
144
  }
112
145
  type SecretScope = {
113
- kind: 'repo';
146
+ kind: "repo";
114
147
  } | {
115
- kind: 'environment';
148
+ kind: "environment";
116
149
  name: string;
117
150
  } | {
118
- kind: 'organization';
151
+ kind: "organization";
119
152
  name: string;
120
153
  };
121
154
  interface Secrets extends ProviderIdentity {
122
- readonly key: 'secrets';
155
+ readonly key: "secrets";
123
156
  /** List secret NAMES (not values) at the given scope. */
124
157
  listSecrets(scope: SecretScope): Promise<string[]>;
125
158
  /** Idempotent upsert. Adapter handles encryption. */
@@ -127,7 +160,7 @@ interface Secrets extends ProviderIdentity {
127
160
  deleteSecret(scope: SecretScope, name: string): Promise<void>;
128
161
  }
129
162
  interface EnvironmentReviewer {
130
- type: 'User' | 'Team';
163
+ type: "User" | "Team";
131
164
  /** Numeric id — GitHub's reviewer API silently ignores login strings. */
132
165
  id: number;
133
166
  }
@@ -138,13 +171,13 @@ interface Environment {
138
171
  preventSelfReview?: boolean;
139
172
  }
140
173
  interface Environments extends ProviderIdentity {
141
- readonly key: 'environments';
174
+ readonly key: "environments";
142
175
  listEnvironments(): Promise<Environment[]>;
143
176
  upsertEnvironment(env: Environment): Promise<void>;
144
177
  deleteEnvironment(name: string): Promise<void>;
145
178
  }
146
- type LifecycleSlot = 'inProgress' | 'inReview' | 'done';
147
- type StatusCategory = 'open' | 'in-progress' | 'in-review' | 'done' | 'other';
179
+ type LifecycleSlot = "inProgress" | "inReview" | "done";
180
+ type StatusCategory = "open" | "in-progress" | "in-review" | "done" | "other";
148
181
  interface TrackerUser {
149
182
  id: string;
150
183
  displayName: string;
@@ -165,7 +198,7 @@ interface Issue {
165
198
  }
166
199
  interface IssueSearchFilter {
167
200
  /** Restrict to issues assigned to a specific id, or 'currentUser'. */
168
- assignee?: string | 'currentUser';
201
+ assignee?: string | "currentUser";
169
202
  /** Exclude issues in the `done` category. */
170
203
  openOnly?: boolean;
171
204
  /** Max number of issues to return. Adapters apply a sensible default. */
@@ -202,7 +235,7 @@ interface TrackerDoctorReport {
202
235
  }>;
203
236
  }
204
237
  interface Issues extends ProviderIdentity {
205
- readonly key: 'issues';
238
+ readonly key: "issues";
206
239
  /** Currently-authenticated user. */
207
240
  getMyself(): Promise<TrackerUser>;
208
241
  search(filter: IssueSearchFilter): Promise<Issue[]>;
@@ -221,12 +254,12 @@ interface Issues extends ProviderIdentity {
221
254
  doctor(): Promise<TrackerDoctorReport>;
222
255
  }
223
256
  /** Env-var scope on the deploy platform. */
224
- type DeploymentTarget = 'development' | 'preview' | 'production';
257
+ type DeploymentTarget = "development" | "preview" | "production";
225
258
  /**
226
259
  * Named deployment trigger target — `undefined` means a branch
227
260
  * preview (no named environment).
228
261
  */
229
- type DeploymentTrigger = 'production' | 'staging';
262
+ type DeploymentTrigger = "production" | "staging";
230
263
  interface DeploymentProject {
231
264
  id: string;
232
265
  name: string;
@@ -248,10 +281,10 @@ interface DeploymentRecord {
248
281
  branch: string | null;
249
282
  /** Named environment if one was targeted; undefined for branch previews. */
250
283
  target?: DeploymentTrigger;
251
- status: 'queued' | 'building' | 'ready' | 'error' | 'cancelled';
284
+ status: "queued" | "building" | "ready" | "error" | "cancelled";
252
285
  }
253
286
  interface Deployment extends ProviderIdentity {
254
- readonly key: 'deployment';
287
+ readonly key: "deployment";
255
288
  listProjects(): Promise<DeploymentProject[]>;
256
289
  /** Create if missing, otherwise return existing. Idempotent. */
257
290
  ensureProject(input: {
@@ -287,7 +320,7 @@ interface ConnectionStringOptions {
287
320
  pooled?: boolean;
288
321
  }
289
322
  interface Storage extends ProviderIdentity {
290
- readonly key: 'storage';
323
+ readonly key: "storage";
291
324
  /**
292
325
  * Connection string for the given scope. Scope is provider-specific:
293
326
  *
@@ -343,7 +376,7 @@ interface WebhookDashboardInfo {
343
376
  url: string;
344
377
  }
345
378
  interface Auth extends ProviderIdentity {
346
- readonly key: 'auth';
379
+ readonly key: "auth";
347
380
  /** Env-var keys the runtime app needs. */
348
381
  describe(): Promise<AuthDescription>;
349
382
  /** Reachability probe — proves the configured key works. */
@@ -362,7 +395,7 @@ interface Auth extends ProviderIdentity {
362
395
  secretRef: string;
363
396
  }): Promise<void>;
364
397
  }
365
- type AuthEventType = 'user.created' | 'user.updated' | 'user.deleted';
398
+ type AuthEventType = "user.created" | "user.updated" | "user.deleted";
366
399
  interface NormalizedAuthUser {
367
400
  id: string;
368
401
  email: string;
@@ -388,8 +421,12 @@ interface ParseWebhookInput {
388
421
  declare class WebhookVerificationError extends Error {
389
422
  name: string;
390
423
  }
424
+ interface EnsureResult {
425
+ /** True when the resource already existed (idempotent no-op). */
426
+ alreadyExists: boolean;
427
+ }
391
428
  interface Vault extends ProviderIdentity {
392
- readonly key: 'vault';
429
+ readonly key: "vault";
393
430
  /**
394
431
  * Read a secret by reference. The reference format is
395
432
  * provider-specific (1P: "op://Vault/Item/field"; HashiCorp Vault:
@@ -413,8 +450,21 @@ interface Vault extends ProviderIdentity {
413
450
  * destinations (CI secrets, deployment env vars, local .env).
414
451
  */
415
452
  readEnvironment?(environmentId: string): Promise<Record<string, string>>;
453
+ /**
454
+ * Optional — create the top-level project container in the vault
455
+ * if it does not exist. Idempotent: `alreadyExists: true` when the
456
+ * project was already there. Providers whose data model has no
457
+ * project notion (or that gate this behind a paid tier) omit this.
458
+ */
459
+ ensureProject?(name: string): Promise<EnsureResult>;
460
+ /**
461
+ * Optional — create a named environment/config inside a project
462
+ * (e.g., Doppler config `dev` / `stg` / `prd`). Idempotent.
463
+ * Providers whose data model has a single flat namespace omit this.
464
+ */
465
+ ensureEnvironment?(project: string, name: string): Promise<EnsureResult>;
416
466
  }
417
- type DnsRecordType = 'A' | 'AAAA' | 'CNAME' | 'TXT' | 'MX' | 'NS' | 'SRV' | 'CAA';
467
+ type DnsRecordType = "A" | "AAAA" | "CNAME" | "TXT" | "MX" | "NS" | "SRV" | "CAA";
418
468
  interface DnsRecord {
419
469
  id?: string;
420
470
  type: DnsRecordType;
@@ -424,7 +474,7 @@ interface DnsRecord {
424
474
  priority?: number;
425
475
  }
426
476
  interface Dns extends ProviderIdentity {
427
- readonly key: 'dns';
477
+ readonly key: "dns";
428
478
  listRecords(domain: string): Promise<DnsRecord[]>;
429
479
  upsertRecord(domain: string, record: DnsRecord): Promise<DnsRecord>;
430
480
  deleteRecord(domain: string, id: string): Promise<void>;
@@ -434,13 +484,13 @@ interface ToolingDoctorReport {
434
484
  message: string;
435
485
  }
436
486
  interface Tooling extends ProviderIdentity {
437
- readonly key: 'tooling';
487
+ readonly key: "tooling";
438
488
  /** Sync the tool's authoritative state from the repo. */
439
489
  sync(): Promise<void>;
440
490
  doctor(): Promise<ToolingDoctorReport>;
441
491
  }
442
492
  interface Notifications extends ProviderIdentity {
443
- readonly key: 'notifications';
493
+ readonly key: "notifications";
444
494
  /**
445
495
  * Send a message. `channel` is provider-specific (Slack channel id,
446
496
  * Discord webhook url-name, etc.); adapters resolve from config.
@@ -448,14 +498,14 @@ interface Notifications extends ProviderIdentity {
448
498
  send(channel: string, message: string): Promise<void>;
449
499
  }
450
500
  interface Analytics extends ProviderIdentity {
451
- readonly key: 'analytics';
501
+ readonly key: "analytics";
452
502
  describe(): Promise<{
453
503
  provider: string;
454
504
  dsnEnvKey: string;
455
505
  }>;
456
506
  }
457
507
  interface Observability extends ProviderIdentity {
458
- readonly key: 'observability';
508
+ readonly key: "observability";
459
509
  describe(): Promise<{
460
510
  provider: string;
461
511
  dsnEnvKey: string;
@@ -479,7 +529,7 @@ interface CapabilityImpls {
479
529
  }
480
530
  type CardinalityFor<K extends CapabilityKey> = (typeof CARDINALITY)[K];
481
531
  /** Resolved runtime shape: single → one impl; many → array. */
482
- type ResolvedCapability<K extends CapabilityKey> = CardinalityFor<K> extends 'many' ? CapabilityImpls[K][] : CapabilityImpls[K];
483
- declare function isMulti<K extends CapabilityKey>(key: K): CardinalityFor<K> extends 'many' ? true : false;
532
+ type ResolvedCapability<K extends CapabilityKey> = CardinalityFor<K> extends "many" ? CapabilityImpls[K][] : CapabilityImpls[K];
533
+ declare function isMulti<K extends CapabilityKey>(key: K): CardinalityFor<K> extends "many" ? true : false;
484
534
  //#endregion
485
- export { Tooling as $, Environments as A, ProviderApiError as B, DeploymentTarget as C, DnsRecordType as D, DnsRecord as E, LifecycleSlot as F, ResolvedCapability as G, REQUIRED_CAPABILITIES as H, NormalizedAuthUser as I, Secrets as J, Ruleset as K, Notifications as L, IssueSearchFilter as M, Issues as N, Environment as O, LifecycleResult as P, StorageBranch as Q, Observability as R, DeploymentRecord as S, Dns as T, RepoRef as U, ProviderIdentity as V, RepoSettings as W, StatusCategory as X, Source as Y, Storage as Z, ConnectionStringOptions as _, AuthEventType as a, WebhookVerificationError as at, DeploymentProject as b, CARDINALITY as c, Cardinality as d, ToolingDoctorReport as et, CardinalityFor as f, CiRunStatus as g, CiRunFilter as h, AuthEvent as i, WebhookDashboardInfo as it, Issue as j, EnvironmentReviewer as k, CapabilityImpls as l, CiRun as m, Auth as n, TrackerUser as nt, AuthIdentity as o, isMulti as ot, Ci as p, SecretScope as q, AuthDescription as r, Vault as rt, AuthUser as s, Analytics as t, TrackerDoctorReport as tt, CapabilityKey as u, CreateAuthUserInput as v, DeploymentTrigger as w, DeploymentProjectSettings as x, Deployment as y, ParseWebhookInput as z };
535
+ export { StorageBranch as $, EnvironmentReviewer as A, ParseWebhookInput as B, DeploymentTarget as C, DnsRecordType as D, DnsRecord as E, LifecycleResult as F, RepoSettings as G, ProviderIdentity as H, LifecycleSlot as I, SecretScope as J, ResolvedCapability as K, NormalizedAuthUser as L, Issue as M, IssueSearchFilter as N, EnsureResult as O, Issues as P, Storage as Q, Notifications as R, DeploymentRecord as S, Dns as T, REQUIRED_CAPABILITIES as U, ProviderApiError as V, RepoRef as W, Source as X, Secrets as Y, StatusCategory as Z, ConnectionStringOptions as _, AuthEventType as a, WebhookDashboardInfo as at, DeploymentProject as b, CARDINALITY as c, Cardinality as d, Tooling as et, CardinalityFor as f, CiRunStatus as g, CiRunFilter as h, AuthEvent as i, Vault as it, Environments as j, Environment as k, CapabilityImpls as l, CiRun as m, Auth as n, TrackerDoctorReport as nt, AuthIdentity as o, WebhookVerificationError as ot, Ci as p, Ruleset as q, AuthDescription as r, TrackerUser as rt, AuthUser as s, isMulti as st, Analytics as t, ToolingDoctorReport as tt, CapabilityKey as u, CreateAuthUserInput as v, DeploymentTrigger as w, DeploymentProjectSettings as x, Deployment as y, Observability as z };
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as Tooling, A as Environments, B as ProviderApiError, C as DeploymentTarget, D as DnsRecordType, E as DnsRecord, F as LifecycleSlot, G as ResolvedCapability, H as REQUIRED_CAPABILITIES, I as NormalizedAuthUser, J as Secrets, K as Ruleset, L as Notifications, M as IssueSearchFilter, N as Issues, O as Environment, P as LifecycleResult, Q as StorageBranch, R as Observability, S as DeploymentRecord, T as Dns, U as RepoRef, V as ProviderIdentity, W as RepoSettings, X as StatusCategory, Y as Source, Z as Storage, _ as ConnectionStringOptions, a as AuthEventType, at as WebhookVerificationError, b as DeploymentProject, c as CARDINALITY, d as Cardinality, et as ToolingDoctorReport, f as CardinalityFor, g as CiRunStatus, h as CiRunFilter, i as AuthEvent, it as WebhookDashboardInfo, j as Issue, k as EnvironmentReviewer, l as CapabilityImpls, m as CiRun, n as Auth, nt as TrackerUser, o as AuthIdentity, ot as isMulti, p as Ci, q as SecretScope, r as AuthDescription, rt as Vault, s as AuthUser, t as Analytics, tt as TrackerDoctorReport, u as CapabilityKey, v as CreateAuthUserInput, w as DeploymentTrigger, x as DeploymentProjectSettings, y as Deployment, z as ParseWebhookInput } from "./index-DDxKXqtR.mjs";
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
2
 
3
3
  //#region src/config.d.ts
4
4
  type ProviderOptions = Record<string, unknown>;
@@ -6,6 +6,22 @@ type SingleEntry = string | [provider: string, options: ProviderOptions];
6
6
  type MultiEntry = Array<string | [provider: string, options: ProviderOptions]>;
7
7
  type RawProviderEntry = SingleEntry | MultiEntry;
8
8
  type RawProvidersConfig = Partial<Record<CapabilityKey, RawProviderEntry>>;
9
+ interface RepoPolicyConfig {
10
+ /**
11
+ * "balanced" — squash-only merges, delete-branch-on-merge, issues/discussions/projects
12
+ * enabled, auto-merge enabled, web sign-off required, always suggest updating, no wiki;
13
+ * plus a ruleset that blocks force-push + deletion and requires a pull request (0 reviews).
14
+ *
15
+ * "strict" — everything in "balanced" plus required status checks from `requiredChecks`.
16
+ *
17
+ * "none" — skips repo settings + ruleset entirely.
18
+ *
19
+ * @default "balanced"
20
+ */
21
+ preset?: "balanced" | "strict" | "none";
22
+ /** CI check context names required on the default branch (used by "strict"). */
23
+ requiredChecks?: string[];
24
+ }
9
25
  interface AppConfig {
10
26
  name: string;
11
27
  path: string;
@@ -18,6 +34,31 @@ interface HolocronConfig {
18
34
  project: {
19
35
  name: string;
20
36
  description?: string;
37
+ /**
38
+ * Repo coord — `"owner/name"`. When set, `PluginLoader` injects
39
+ * it into every plugin's `RuntimeContext.repo` so plugins that
40
+ * need a repo (github, etc.) don't require `--repo` on every
41
+ * invocation. `--repo` on the command line still overrides.
42
+ */
43
+ repo?: string;
44
+ /**
45
+ * Repo-level policy applied by `holocron setup`. Defines merge
46
+ * strategy, branch protection rulesets, and security defaults.
47
+ * Requires `source` capability to be configured.
48
+ */
49
+ repoPolicy?: RepoPolicyConfig;
50
+ /**
51
+ * CI workflow names to install as thin wrappers during `holocron setup`.
52
+ * Each name maps to a reusable workflow in `theholocron/.github`.
53
+ *
54
+ * Supported values: "lint" | "test" | "typecheck" | "codeql" | "review" |
55
+ * "release" | "stale" | "greetings" | "dependencies" | "bookkeeping-pr" | "audit"
56
+ *
57
+ * `holocron setup` writes `.github/workflows/<name>.yml` for each entry,
58
+ * calling the corresponding `ci-<name>.yml@main` reusable workflow.
59
+ * Files are overwritten on each run — they are generated artifacts.
60
+ */
61
+ workflows?: string[];
21
62
  };
22
63
  providers: RawProvidersConfig;
23
64
  apps?: AppConfig[];
@@ -30,15 +71,15 @@ interface ResolvedTuple {
30
71
  options: ProviderOptions;
31
72
  }
32
73
  type ResolvedProviderEntry = {
33
- cardinality: 'single';
74
+ cardinality: "single";
34
75
  tuple: ResolvedTuple;
35
76
  } | {
36
- cardinality: 'many';
77
+ cardinality: "many";
37
78
  tuples: ResolvedTuple[];
38
79
  };
39
80
  type ResolvedProvidersConfig = Partial<Record<CapabilityKey, ResolvedProviderEntry>>;
40
81
  interface ResolvedHolocronConfig {
41
- project: HolocronConfig['project'];
82
+ project: HolocronConfig["project"];
42
83
  providers: ResolvedProvidersConfig;
43
84
  apps: AppConfig[];
44
85
  doctor: DoctorConfig;
@@ -55,4 +96,48 @@ declare function resolvePluginPackage(provider: string): string;
55
96
  declare function resolveEntry(key: CapabilityKey, raw: RawProviderEntry): ResolvedProviderEntry;
56
97
  declare function resolveConfig(raw: HolocronConfig): ResolvedHolocronConfig;
57
98
  //#endregion
58
- export { Analytics, AppConfig, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConfigError, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, DoctorConfig, Environment, EnvironmentReviewer, Environments, HolocronConfig, Issue, IssueSearchFilter, Issues, LifecycleResult, LifecycleSlot, MultiEntry, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, ProviderOptions, REQUIRED_CAPABILITIES, RawProviderEntry, RawProvidersConfig, RepoRef, RepoSettings, ResolvedCapability, ResolvedHolocronConfig, ResolvedProviderEntry, ResolvedProvidersConfig, ResolvedTuple, Ruleset, SecretScope, Secrets, SingleEntry, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, isMulti, resolveConfig, resolveEntry, resolvePluginPackage };
99
+ //#region src/keyring.d.ts
100
+ /**
101
+ * Keyring-backed bootstrap credential store.
102
+ *
103
+ * Every holocron plugin's bootstrap token (the one it needs before it
104
+ * can talk to its vendor's API) can be stored in the OS keyring under
105
+ * a single reverse-DNS service scope. Managed via `holocron auth`
106
+ * subcommands; consulted at position 4 in every plugin's auth
107
+ * precedence chain (after --token / HOLOCRON_<X>_TOKEN / <native>_TOKEN).
108
+ *
109
+ * See `.notes/tech-auth-bootstrap.spec.md` for the design rationale.
110
+ *
111
+ * Failure model: keyring access is best-effort. Platforms without a
112
+ * supported credential store (some Linux CI images, sandboxed
113
+ * environments) will throw from the underlying library. Every export
114
+ * here catches and returns a null/empty result rather than propagating
115
+ * — the plugin's precedence chain then falls through to
116
+ * env-var-only paths, which is exactly how CI is meant to work.
117
+ */
118
+ /**
119
+ * Store or overwrite a bootstrap token for a provider. Returns true on
120
+ * success, false when the underlying keyring is unsupported or errored.
121
+ */
122
+ declare function setToken(provider: string, token: string): boolean;
123
+ /**
124
+ * Read the bootstrap token for a provider. Returns `null` for both
125
+ * "not stored" and "keyring unavailable" — callers can treat them the
126
+ * same way (fall through to env-var precedence).
127
+ */
128
+ declare function getToken(provider: string): string | null;
129
+ /**
130
+ * Delete a stored token. Returns true when a token was removed, false
131
+ * when there was nothing to delete or the keyring is unavailable.
132
+ * Distinguishing the two cases isn't worth the surface area — the
133
+ * command output makes the situation clear either way.
134
+ */
135
+ declare function deleteToken(provider: string): boolean;
136
+ /**
137
+ * List provider slugs with a stored token in this service scope.
138
+ * Uses the library's `findCredentials(service)` — supported on all
139
+ * platforms the underlying credential store supports.
140
+ */
141
+ declare function listStoredProviders(): string[];
142
+ //#endregion
143
+ export { Analytics, AppConfig, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConfigError, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, DoctorConfig, EnsureResult, Environment, EnvironmentReviewer, Environments, HolocronConfig, Issue, IssueSearchFilter, Issues, LifecycleResult, LifecycleSlot, MultiEntry, NormalizedAuthUser, Notifications, Observability, ParseWebhookInput, ProviderApiError, ProviderIdentity, ProviderOptions, REQUIRED_CAPABILITIES, RawProviderEntry, RawProvidersConfig, RepoPolicyConfig, RepoRef, RepoSettings, ResolvedCapability, ResolvedHolocronConfig, ResolvedProviderEntry, ResolvedProvidersConfig, ResolvedTuple, Ruleset, SecretScope, Secrets, SingleEntry, Source, StatusCategory, Storage, StorageBranch, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, deleteToken, getToken, isMulti, listStoredProviders, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { a as isMulti, i as WebhookVerificationError, n as ProviderApiError, r as REQUIRED_CAPABILITIES, t as CARDINALITY } from "./capabilities-QjjhVlDd.mjs";
2
- import { i as resolvePluginPackage, n as resolveConfig, r as resolveEntry, t as ConfigError } from "./config-DWlIfFZm.mjs";
3
- export { CARDINALITY, ConfigError, ProviderApiError, REQUIRED_CAPABILITIES, WebhookVerificationError, isMulti, resolveConfig, resolveEntry, resolvePluginPackage };
1
+ import { a as isMulti, i as WebhookVerificationError, n as ProviderApiError, r as REQUIRED_CAPABILITIES, t as CARDINALITY } from "./capabilities-DapaKOlX.mjs";
2
+ import { a as ConfigError, c as resolvePluginPackage, i as setToken, n as getToken, o as resolveConfig, r as listStoredProviders, s as resolveEntry, t as deleteToken } from "./keyring-DwNEmrBc.mjs";
3
+ export { CARDINALITY, ConfigError, ProviderApiError, REQUIRED_CAPABILITIES, WebhookVerificationError, deleteToken, getToken, isMulti, listStoredProviders, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
@@ -1,4 +1,5 @@
1
- import { r as REQUIRED_CAPABILITIES, t as CARDINALITY } from "./capabilities-QjjhVlDd.mjs";
1
+ import { r as REQUIRED_CAPABILITIES, t as CARDINALITY } from "./capabilities-DapaKOlX.mjs";
2
+ import { Entry, findCredentials } from "@napi-rs/keyring";
2
3
  //#region src/config.ts
3
4
  /**
4
5
  * `holocron.config.json` schema, parser, and provider resolution.
@@ -110,4 +111,74 @@ function resolveConfig(raw) {
110
111
  };
111
112
  }
112
113
  //#endregion
113
- export { resolvePluginPackage as i, resolveConfig as n, resolveEntry as r, ConfigError as t };
114
+ //#region src/keyring.ts
115
+ /**
116
+ * Keyring-backed bootstrap credential store.
117
+ *
118
+ * Every holocron plugin's bootstrap token (the one it needs before it
119
+ * can talk to its vendor's API) can be stored in the OS keyring under
120
+ * a single reverse-DNS service scope. Managed via `holocron auth`
121
+ * subcommands; consulted at position 4 in every plugin's auth
122
+ * precedence chain (after --token / HOLOCRON_<X>_TOKEN / <native>_TOKEN).
123
+ *
124
+ * See `.notes/tech-auth-bootstrap.spec.md` for the design rationale.
125
+ *
126
+ * Failure model: keyring access is best-effort. Platforms without a
127
+ * supported credential store (some Linux CI images, sandboxed
128
+ * environments) will throw from the underlying library. Every export
129
+ * here catches and returns a null/empty result rather than propagating
130
+ * — the plugin's precedence chain then falls through to
131
+ * env-var-only paths, which is exactly how CI is meant to work.
132
+ */
133
+ const SERVICE = "com.theholocron.cli";
134
+ /**
135
+ * Store or overwrite a bootstrap token for a provider. Returns true on
136
+ * success, false when the underlying keyring is unsupported or errored.
137
+ */
138
+ function setToken(provider, token) {
139
+ try {
140
+ new Entry(SERVICE, provider).setPassword(token);
141
+ return true;
142
+ } catch {
143
+ return false;
144
+ }
145
+ }
146
+ /**
147
+ * Read the bootstrap token for a provider. Returns `null` for both
148
+ * "not stored" and "keyring unavailable" — callers can treat them the
149
+ * same way (fall through to env-var precedence).
150
+ */
151
+ function getToken(provider) {
152
+ try {
153
+ return new Entry(SERVICE, provider).getPassword();
154
+ } catch {
155
+ return null;
156
+ }
157
+ }
158
+ /**
159
+ * Delete a stored token. Returns true when a token was removed, false
160
+ * when there was nothing to delete or the keyring is unavailable.
161
+ * Distinguishing the two cases isn't worth the surface area — the
162
+ * command output makes the situation clear either way.
163
+ */
164
+ function deleteToken(provider) {
165
+ try {
166
+ return new Entry(SERVICE, provider).deletePassword();
167
+ } catch {
168
+ return false;
169
+ }
170
+ }
171
+ /**
172
+ * List provider slugs with a stored token in this service scope.
173
+ * Uses the library's `findCredentials(service)` — supported on all
174
+ * platforms the underlying credential store supports.
175
+ */
176
+ function listStoredProviders() {
177
+ try {
178
+ return findCredentials(SERVICE).map((c) => c.account);
179
+ } catch {
180
+ return [];
181
+ }
182
+ }
183
+ //#endregion
184
+ export { ConfigError as a, resolvePluginPackage as c, setToken as i, getToken as n, resolveConfig as o, listStoredProviders as r, resolveEntry as s, deleteToken as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/cli",
3
- "version": "2.0.0-alpha.0",
3
+ "version": "2.0.0-alpha.5",
4
4
  "description": "The Holocron CLI — a pluggable, capability-based orchestrator for spinning up and operating software projects.",
5
5
  "homepage": "https://github.com/theholocron/holocron/tree/main/packages/cli#readme",
6
6
  "bugs": "https://github.com/theholocron/holocron/issues",
@@ -33,6 +33,7 @@
33
33
  "README.md"
34
34
  ],
35
35
  "dependencies": {
36
+ "@napi-rs/keyring": "^1.3.0",
36
37
  "yargs": "^18.0.0",
37
38
  "@theholocron/cli-utils": "0.0.0"
38
39
  },