@theholocron/cli 1.0.0 → 1.1.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/LICENSE +21 -674
- package/README.md +19 -8
- package/dist/capabilities/index.d.mts +2 -0
- package/dist/capabilities/index.mjs +2 -0
- package/dist/capabilities-QjjhVlDd.mjs +43 -0
- package/dist/cli.d.mts +1 -0
- package/dist/cli.mjs +852 -0
- package/dist/config-DWlIfFZm.mjs +113 -0
- package/dist/index-CSxf0Yc7.d.mts +485 -0
- package/dist/index.d.mts +58 -0
- package/dist/index.mjs +3 -0
- package/package.json +46 -53
- package/.alexignore +0 -3
- package/.editorconfig +0 -24
- package/.editorconfig-checker.json +0 -20
- package/.env +0 -3
- package/.gitattributes +0 -12
- package/.github/CODEOWNERS +0 -1
- package/.github/dependabot.yml +0 -6
- package/.github/labeler.yml +0 -21
- package/.github/workflows/bookkeeping-pr.yml +0 -32
- package/.github/workflows/greetings.yml +0 -32
- package/.github/workflows/lint.yml +0 -96
- package/.github/workflows/publish.yml +0 -71
- package/.github/workflows/review.yml +0 -72
- package/.github/workflows/stale.yml +0 -25
- package/.husky/commit-msg +0 -1
- package/.husky/pre-commit +0 -13
- package/.husky/prepare-commit-msg +0 -18
- package/commitlint.config.js +0 -9
- package/eslint.config.js +0 -9
- package/media/README.md +0 -11
- package/media/error.mp3 +0 -0
- package/media/success.mp3 +0 -0
- package/media/warning.mp3 +0 -0
- package/prettier.config.js +0 -11
- package/src/cli.ts +0 -65
- package/src/commands/README.md +0 -13
- package/src/commands/bootstrap.ts +0 -102
- package/src/commands/conf/README.md +0 -45
- package/src/commands/conf/add.ts +0 -49
- package/src/commands/conf/edit.ts +0 -12
- package/src/commands/conf/view.ts +0 -40
- package/src/commands/conf.ts +0 -10
- package/src/commands/log.ts +0 -91
- package/src/const.ts +0 -19
- package/src/tasks/README.md +0 -33
- package/src/tasks/find/README.md +0 -49
- package/src/tasks/find/find-project.example.ts +0 -23
- package/src/tasks/find/find-project.ts +0 -76
- package/src/tasks/find/index.ts +0 -1
- package/src/tasks/index.ts +0 -2
- package/src/tasks/replace/README.md +0 -61
- package/src/tasks/replace/index.ts +0 -1
- package/src/tasks/replace/replace.example.ts +0 -31
- package/src/tasks/replace/replace.ts +0 -95
- package/src/ui/README.md +0 -8
- package/src/ui/index.ts +0 -2
- package/src/ui/open/README.md +0 -58
- package/src/ui/open/index.ts +0 -7
- package/src/ui/open/open-browser.ts +0 -25
- package/src/ui/open/open-editor.example.ts +0 -14
- package/src/ui/open/open-editor.ts +0 -30
- package/src/ui/prompts/README.md +0 -36
- package/src/ui/prompts/autocomplete.prompt.ts +0 -18
- package/src/ui/prompts/confirm.prompt.ts +0 -21
- package/src/ui/prompts/index.ts +0 -17
- package/src/ui/prompts/input.prompt.ts +0 -15
- package/src/ui/prompts/search.prompt.ts +0 -33
- package/src/ui/prompts/select.prompt.ts +0 -17
- package/src/ui/prompts/types.ts +0 -8
- package/src/ui/prompts/utils.ts +0 -11
- package/src/utils/$/README.md +0 -87
- package/src/utils/$/command.test.ts +0 -48
- package/src/utils/$/command.ts +0 -21
- package/src/utils/$/directory.ts +0 -108
- package/src/utils/$/file.ts +0 -98
- package/src/utils/$/index.example.ts +0 -37
- package/src/utils/$/index.ts +0 -22
- package/src/utils/$/path.ts +0 -13
- package/src/utils/$/remove.ts +0 -47
- package/src/utils/$/spawn.ts +0 -26
- package/src/utils/$/utils.ts +0 -21
- package/src/utils/README.md +0 -15
- package/src/utils/config/README.md +0 -41
- package/src/utils/config/config.ts +0 -26
- package/src/utils/config/index.ts +0 -1
- package/src/utils/config/preferences.conf.ts +0 -45
- package/src/utils/env/README.md +0 -52
- package/src/utils/env/env.example.ts +0 -21
- package/src/utils/env/env.ts +0 -57
- package/src/utils/env/index.ts +0 -1
- package/src/utils/index.ts +0 -5
- package/src/utils/log/README.md +0 -117
- package/src/utils/log/index.ts +0 -2
- package/src/utils/log/log.example.ts +0 -13
- package/src/utils/log/log.ts +0 -58
- package/src/utils/log/logger.ts +0 -28
- package/src/utils/log/sound.ts +0 -35
- package/src/utils/node/README.md +0 -23
- package/src/utils/node/index.ts +0 -5
- package/src/utils/node/package.ts +0 -12
- package/tsconfig.json +0 -15
- package/yamllint.config.yml +0 -20
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { r as REQUIRED_CAPABILITIES, t as CARDINALITY } from "./capabilities-QjjhVlDd.mjs";
|
|
2
|
+
//#region src/config.ts
|
|
3
|
+
/**
|
|
4
|
+
* `holocron.config.json` schema, parser, and provider resolution.
|
|
5
|
+
*
|
|
6
|
+
* ESLint-style entry forms:
|
|
7
|
+
*
|
|
8
|
+
* "source": "github" ← single, short
|
|
9
|
+
* "deployment": ["vercel", { team: "rando" }] ← single, with options
|
|
10
|
+
* "notifications": ["slack", "discord"] ← multi, short
|
|
11
|
+
* "notifications": [
|
|
12
|
+
* ["slack", { channel: "#ops" }],
|
|
13
|
+
* ["discord", { webhook: "env:HOOK" }]
|
|
14
|
+
* ] ← multi, with options
|
|
15
|
+
*
|
|
16
|
+
* Discriminator: an array entry is a `[provider, options]` tuple when
|
|
17
|
+
* the length is 2 AND element[1] is a non-array, non-null object.
|
|
18
|
+
* Otherwise it's a multi-provider list (string[] or tuple[]).
|
|
19
|
+
*
|
|
20
|
+
* Validation rules:
|
|
21
|
+
* - `vault` is REQUIRED (every project has secrets somewhere)
|
|
22
|
+
* - Entries for `'many'` capabilities are normalized to an array of
|
|
23
|
+
* normalized tuples; entries for `'single'` capabilities are
|
|
24
|
+
* normalized to one tuple
|
|
25
|
+
* - Tokens / secret values never appear in config — providers read
|
|
26
|
+
* them from env (or pull from `vault` at runtime)
|
|
27
|
+
*/
|
|
28
|
+
var ConfigError = class extends Error {
|
|
29
|
+
name = "ConfigError";
|
|
30
|
+
};
|
|
31
|
+
const PLUGIN_PREFIX = "@theholocron/holocron-plugin-";
|
|
32
|
+
const COMMUNITY_PREFIX = "holocron-plugin-";
|
|
33
|
+
/**
|
|
34
|
+
* Resolve `"github"` → `"@theholocron/holocron-plugin-github"`.
|
|
35
|
+
* Fully-qualified names (scoped or not) are honored verbatim, which
|
|
36
|
+
* is how third-party plugins published outside the org work.
|
|
37
|
+
*/
|
|
38
|
+
function resolvePluginPackage(provider) {
|
|
39
|
+
if (!provider) throw new ConfigError("provider name is empty");
|
|
40
|
+
if (provider.startsWith("@")) return provider;
|
|
41
|
+
if (provider.startsWith(COMMUNITY_PREFIX)) return provider;
|
|
42
|
+
if (provider.includes("/")) return provider;
|
|
43
|
+
return PLUGIN_PREFIX + provider;
|
|
44
|
+
}
|
|
45
|
+
/** A bare `[provider, options]` tuple, with both elements present? */
|
|
46
|
+
function isOptionsTuple(value) {
|
|
47
|
+
if (!Array.isArray(value)) return false;
|
|
48
|
+
if (value.length !== 2) return false;
|
|
49
|
+
if (typeof value[0] !== "string") return false;
|
|
50
|
+
const opt = value[1];
|
|
51
|
+
return typeof opt === "object" && opt !== null && !Array.isArray(opt);
|
|
52
|
+
}
|
|
53
|
+
function normalizeEntry(entry) {
|
|
54
|
+
if (typeof entry === "string") return {
|
|
55
|
+
provider: entry,
|
|
56
|
+
packageName: resolvePluginPackage(entry),
|
|
57
|
+
options: {}
|
|
58
|
+
};
|
|
59
|
+
const [provider, options] = entry;
|
|
60
|
+
return {
|
|
61
|
+
provider,
|
|
62
|
+
packageName: resolvePluginPackage(provider),
|
|
63
|
+
options
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function resolveEntry(key, raw) {
|
|
67
|
+
const cardinality = CARDINALITY[key];
|
|
68
|
+
if (typeof raw === "string") {
|
|
69
|
+
if (cardinality === "many") throw new ConfigError(`\`${key}\` accepts multiple providers; wrap a single one in an array: ["${raw}"]`);
|
|
70
|
+
return {
|
|
71
|
+
cardinality: "single",
|
|
72
|
+
tuple: normalizeEntry(raw)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (!Array.isArray(raw)) throw new ConfigError(`\`${key}\` entry must be a string or array, got ${typeof raw}`);
|
|
76
|
+
if (isOptionsTuple(raw)) {
|
|
77
|
+
if (cardinality === "many") return {
|
|
78
|
+
cardinality: "many",
|
|
79
|
+
tuples: [normalizeEntry(raw)]
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
cardinality: "single",
|
|
83
|
+
tuple: normalizeEntry(raw)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (cardinality === "single") throw new ConfigError(`\`${key}\` accepts exactly one provider; got a multi-provider list with ${raw.length} entries`);
|
|
87
|
+
return {
|
|
88
|
+
cardinality: "many",
|
|
89
|
+
tuples: raw.map((entry, idx) => {
|
|
90
|
+
if (typeof entry === "string") return normalizeEntry(entry);
|
|
91
|
+
if (isOptionsTuple(entry)) return normalizeEntry(entry);
|
|
92
|
+
throw new ConfigError(`\`${key}[${idx}]\` must be a provider string or [provider, options] tuple`);
|
|
93
|
+
})
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function resolveConfig(raw) {
|
|
97
|
+
if (!raw.project?.name) throw new ConfigError("`project.name` is required");
|
|
98
|
+
if (!raw.providers || typeof raw.providers !== "object") throw new ConfigError("`providers` block is required");
|
|
99
|
+
const providers = {};
|
|
100
|
+
for (const [key, entry] of Object.entries(raw.providers)) {
|
|
101
|
+
if (entry === void 0) continue;
|
|
102
|
+
providers[key] = resolveEntry(key, entry);
|
|
103
|
+
}
|
|
104
|
+
for (const required of REQUIRED_CAPABILITIES) if (!providers[required]) throw new ConfigError(`required capability \`${required}\` is missing from providers`);
|
|
105
|
+
return {
|
|
106
|
+
project: raw.project,
|
|
107
|
+
providers,
|
|
108
|
+
apps: raw.apps ?? [],
|
|
109
|
+
doctor: raw.doctor ?? {}
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { resolvePluginPackage as i, resolveConfig as n, resolveEntry as r, ConfigError as t };
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
//#region src/capabilities/index.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Capability interfaces — the contracts that providers implement.
|
|
4
|
+
*
|
|
5
|
+
* Each capability has a stable key (`'source'`, `'ci'`, …) and a
|
|
6
|
+
* cardinality (`'single'` = one provider; `'many'` = several active
|
|
7
|
+
* at once). The cardinality is part of the type contract via
|
|
8
|
+
* `CardinalityFor<K>` so config resolution + command code can branch
|
|
9
|
+
* statically.
|
|
10
|
+
*
|
|
11
|
+
* See `.notes/tech-architecture.spec.md` for the design narrative
|
|
12
|
+
* (status: proposed, issue: #74).
|
|
13
|
+
*/
|
|
14
|
+
type CapabilityKey = "source" | "ci" | "secrets" | "environments" | "issues" | "deployment" | "storage" | "auth" | "vault" | "dns" | "tooling" | "notifications" | "analytics" | "observability";
|
|
15
|
+
type Cardinality = "single" | "many";
|
|
16
|
+
declare const CARDINALITY: {
|
|
17
|
+
readonly source: "single";
|
|
18
|
+
readonly ci: "single";
|
|
19
|
+
readonly secrets: "single";
|
|
20
|
+
readonly environments: "single";
|
|
21
|
+
readonly issues: "single";
|
|
22
|
+
readonly deployment: "single";
|
|
23
|
+
readonly storage: "single";
|
|
24
|
+
readonly auth: "single";
|
|
25
|
+
readonly vault: "single";
|
|
26
|
+
readonly dns: "single";
|
|
27
|
+
readonly tooling: "many";
|
|
28
|
+
readonly notifications: "many";
|
|
29
|
+
readonly analytics: "many";
|
|
30
|
+
readonly observability: "many";
|
|
31
|
+
};
|
|
32
|
+
/** Vault is required; everything else is optional in the config. */
|
|
33
|
+
declare const REQUIRED_CAPABILITIES: readonly CapabilityKey[];
|
|
34
|
+
interface ProviderIdentity {
|
|
35
|
+
readonly key: CapabilityKey;
|
|
36
|
+
readonly providerName: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Surfaced from every capability call that hits a vendor API. Wraps
|
|
40
|
+
* the underlying error with `status` (HTTP) and `details` so
|
|
41
|
+
* orchestrators (`holocron setup`, `doctor`) can soft-skip rather
|
|
42
|
+
* than abort.
|
|
43
|
+
*/
|
|
44
|
+
declare class ProviderApiError extends Error {
|
|
45
|
+
readonly status: number | undefined;
|
|
46
|
+
readonly details?: unknown | undefined;
|
|
47
|
+
name: string;
|
|
48
|
+
constructor(message: string, status: number | undefined, details?: unknown | undefined);
|
|
49
|
+
}
|
|
50
|
+
interface Ruleset {
|
|
51
|
+
id: number;
|
|
52
|
+
name: string;
|
|
53
|
+
enforcement: "active" | "evaluate" | "disabled";
|
|
54
|
+
target?: string;
|
|
55
|
+
}
|
|
56
|
+
interface RepoSettings {
|
|
57
|
+
allow_squash_merge?: boolean;
|
|
58
|
+
allow_merge_commit?: boolean;
|
|
59
|
+
allow_rebase_merge?: boolean;
|
|
60
|
+
allow_auto_merge?: boolean;
|
|
61
|
+
delete_branch_on_merge?: boolean;
|
|
62
|
+
default_branch?: string;
|
|
63
|
+
has_issues?: boolean;
|
|
64
|
+
has_discussions?: boolean;
|
|
65
|
+
}
|
|
66
|
+
interface RepoRef {
|
|
67
|
+
owner: string;
|
|
68
|
+
name: string;
|
|
69
|
+
defaultBranch: string;
|
|
70
|
+
}
|
|
71
|
+
interface Source extends ProviderIdentity {
|
|
72
|
+
readonly key: "source";
|
|
73
|
+
/** Auth sanity-check. Throws ProviderApiError on auth failure. */
|
|
74
|
+
whoami(): Promise<{
|
|
75
|
+
login: string;
|
|
76
|
+
}>;
|
|
77
|
+
getRepo(): Promise<RepoRef>;
|
|
78
|
+
listRulesets(): Promise<Ruleset[]>;
|
|
79
|
+
createRuleset(payload: Record<string, unknown>): Promise<Ruleset>;
|
|
80
|
+
updateRuleset(id: number, payload: Record<string, unknown>): Promise<Ruleset>;
|
|
81
|
+
updateRepoSettings(settings: RepoSettings): Promise<void>;
|
|
82
|
+
enableVulnerabilityAlerts(): Promise<void>;
|
|
83
|
+
enableAutomatedSecurityFixes(): Promise<void>;
|
|
84
|
+
enableSecretScanning(): Promise<void>;
|
|
85
|
+
enablePrivateVulnerabilityReporting(): Promise<void>;
|
|
86
|
+
listWorkflowFiles(): Promise<string[]>;
|
|
87
|
+
readWorkflowFile(name: string): Promise<string | null>;
|
|
88
|
+
writeWorkflowFile(name: string, contents: string): Promise<void>;
|
|
89
|
+
removeWorkflowFile(name: string): Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
type CiRunStatus = "queued" | "in_progress" | "completed" | "cancelled" | "failure" | "success" | "skipped";
|
|
92
|
+
interface CiRun {
|
|
93
|
+
id: string | number;
|
|
94
|
+
workflowName: string;
|
|
95
|
+
branch: string;
|
|
96
|
+
sha: string;
|
|
97
|
+
status: CiRunStatus;
|
|
98
|
+
url: string;
|
|
99
|
+
startedAt: string;
|
|
100
|
+
completedAt?: string;
|
|
101
|
+
}
|
|
102
|
+
interface CiRunFilter {
|
|
103
|
+
branch?: string;
|
|
104
|
+
status?: CiRunStatus;
|
|
105
|
+
limit?: number;
|
|
106
|
+
}
|
|
107
|
+
interface Ci extends ProviderIdentity {
|
|
108
|
+
readonly key: "ci";
|
|
109
|
+
listRuns(filter?: CiRunFilter): Promise<CiRun[]>;
|
|
110
|
+
getRun(id: string | number): Promise<CiRun>;
|
|
111
|
+
}
|
|
112
|
+
type SecretScope = {
|
|
113
|
+
kind: "repo";
|
|
114
|
+
} | {
|
|
115
|
+
kind: "environment";
|
|
116
|
+
name: string;
|
|
117
|
+
} | {
|
|
118
|
+
kind: "organization";
|
|
119
|
+
name: string;
|
|
120
|
+
};
|
|
121
|
+
interface Secrets extends ProviderIdentity {
|
|
122
|
+
readonly key: "secrets";
|
|
123
|
+
/** List secret NAMES (not values) at the given scope. */
|
|
124
|
+
listSecrets(scope: SecretScope): Promise<string[]>;
|
|
125
|
+
/** Idempotent upsert. Adapter handles encryption. */
|
|
126
|
+
setSecret(scope: SecretScope, name: string, value: string): Promise<void>;
|
|
127
|
+
deleteSecret(scope: SecretScope, name: string): Promise<void>;
|
|
128
|
+
}
|
|
129
|
+
interface EnvironmentReviewer {
|
|
130
|
+
type: "User" | "Team";
|
|
131
|
+
/** Numeric id — GitHub's reviewer API silently ignores login strings. */
|
|
132
|
+
id: number;
|
|
133
|
+
}
|
|
134
|
+
interface Environment {
|
|
135
|
+
name: string;
|
|
136
|
+
reviewers?: EnvironmentReviewer[];
|
|
137
|
+
waitTimer?: number;
|
|
138
|
+
preventSelfReview?: boolean;
|
|
139
|
+
}
|
|
140
|
+
interface Environments extends ProviderIdentity {
|
|
141
|
+
readonly key: "environments";
|
|
142
|
+
listEnvironments(): Promise<Environment[]>;
|
|
143
|
+
upsertEnvironment(env: Environment): Promise<void>;
|
|
144
|
+
deleteEnvironment(name: string): Promise<void>;
|
|
145
|
+
}
|
|
146
|
+
type LifecycleSlot = "inProgress" | "inReview" | "done";
|
|
147
|
+
type StatusCategory = "open" | "in-progress" | "in-review" | "done" | "other";
|
|
148
|
+
interface TrackerUser {
|
|
149
|
+
id: string;
|
|
150
|
+
displayName: string;
|
|
151
|
+
emailAddress?: string;
|
|
152
|
+
}
|
|
153
|
+
interface Issue {
|
|
154
|
+
/** Human-readable key — "#42" for GitHub, "RANDO-42" for Jira. */
|
|
155
|
+
key: string;
|
|
156
|
+
/** Internal opaque id. */
|
|
157
|
+
id: string;
|
|
158
|
+
summary: string;
|
|
159
|
+
body?: string;
|
|
160
|
+
status: string;
|
|
161
|
+
statusCategory: StatusCategory;
|
|
162
|
+
assignee: TrackerUser | null;
|
|
163
|
+
updated: string;
|
|
164
|
+
url?: string;
|
|
165
|
+
}
|
|
166
|
+
interface IssueSearchFilter {
|
|
167
|
+
/** Restrict to issues assigned to a specific id, or 'currentUser'. */
|
|
168
|
+
assignee?: string | "currentUser";
|
|
169
|
+
/** Exclude issues in the `done` category. */
|
|
170
|
+
openOnly?: boolean;
|
|
171
|
+
/** Max number of issues to return. Adapters apply a sensible default. */
|
|
172
|
+
limit?: number;
|
|
173
|
+
}
|
|
174
|
+
interface LifecycleResult {
|
|
175
|
+
/** False when no API write happened (already at target state). */
|
|
176
|
+
transitioned: boolean;
|
|
177
|
+
/** Status name the issue is in after this call. */
|
|
178
|
+
status: string;
|
|
179
|
+
/** Adapter-specific note (e.g., "label set" / "closed (completed)"). */
|
|
180
|
+
via?: string;
|
|
181
|
+
}
|
|
182
|
+
interface TrackerDoctorReport {
|
|
183
|
+
/** "Authenticated as ..." subject for the spinner. */
|
|
184
|
+
authedAs: string;
|
|
185
|
+
/** Free-form "Project: RANDO" / "Repo: rando-id/rando" identifier. */
|
|
186
|
+
projectLabel: string;
|
|
187
|
+
/** Status values the adapter exposes. */
|
|
188
|
+
statuses: Array<{
|
|
189
|
+
name: string;
|
|
190
|
+
category: StatusCategory;
|
|
191
|
+
}>;
|
|
192
|
+
/**
|
|
193
|
+
* Per-lifecycle-slot readiness check. `resolved` indicates whether
|
|
194
|
+
* the configured value actually maps to something the tracker
|
|
195
|
+
* recognizes; the `note` is the rendered explanation.
|
|
196
|
+
*/
|
|
197
|
+
lifecycle: Array<{
|
|
198
|
+
slot: LifecycleSlot;
|
|
199
|
+
value: string | null;
|
|
200
|
+
resolved: boolean;
|
|
201
|
+
note: string;
|
|
202
|
+
}>;
|
|
203
|
+
}
|
|
204
|
+
interface Issues extends ProviderIdentity {
|
|
205
|
+
readonly key: "issues";
|
|
206
|
+
/** Currently-authenticated user. */
|
|
207
|
+
getMyself(): Promise<TrackerUser>;
|
|
208
|
+
search(filter: IssueSearchFilter): Promise<Issue[]>;
|
|
209
|
+
get(key: string): Promise<Issue>;
|
|
210
|
+
create(input: {
|
|
211
|
+
summary: string;
|
|
212
|
+
body?: string;
|
|
213
|
+
labels?: string[]; /** Numeric id or exact title (case-insensitive). */
|
|
214
|
+
milestone?: string;
|
|
215
|
+
}): Promise<{
|
|
216
|
+
key: string;
|
|
217
|
+
}>;
|
|
218
|
+
/** Idempotent — `transitioned: false` if the issue is already at the target. */
|
|
219
|
+
transition(key: string, slot: LifecycleSlot): Promise<LifecycleResult>;
|
|
220
|
+
comment(key: string, body: string): Promise<void>;
|
|
221
|
+
doctor(): Promise<TrackerDoctorReport>;
|
|
222
|
+
}
|
|
223
|
+
/** Env-var scope on the deploy platform. */
|
|
224
|
+
type DeploymentTarget = "development" | "preview" | "production";
|
|
225
|
+
/**
|
|
226
|
+
* Named deployment trigger target — `undefined` means a branch
|
|
227
|
+
* preview (no named environment).
|
|
228
|
+
*/
|
|
229
|
+
type DeploymentTrigger = "production" | "staging";
|
|
230
|
+
interface DeploymentProject {
|
|
231
|
+
id: string;
|
|
232
|
+
name: string;
|
|
233
|
+
framework?: string;
|
|
234
|
+
/** True when the project is linked to a Git provider. */
|
|
235
|
+
gitLinked?: boolean;
|
|
236
|
+
rootDirectory?: string | null;
|
|
237
|
+
}
|
|
238
|
+
interface DeploymentProjectSettings {
|
|
239
|
+
previewDeploymentsDisabled?: boolean;
|
|
240
|
+
/** Vercel-specific: whether the GitHub integration creates deployments
|
|
241
|
+
* for every push (false → only on-demand triggers). */
|
|
242
|
+
gitProviderCreateDeployments?: boolean;
|
|
243
|
+
}
|
|
244
|
+
interface DeploymentRecord {
|
|
245
|
+
id: string;
|
|
246
|
+
url: string;
|
|
247
|
+
/** Branch this deployment was made from (null if not git-sourced). */
|
|
248
|
+
branch: string | null;
|
|
249
|
+
/** Named environment if one was targeted; undefined for branch previews. */
|
|
250
|
+
target?: DeploymentTrigger;
|
|
251
|
+
status: "queued" | "building" | "ready" | "error" | "cancelled";
|
|
252
|
+
}
|
|
253
|
+
interface Deployment extends ProviderIdentity {
|
|
254
|
+
readonly key: "deployment";
|
|
255
|
+
listProjects(): Promise<DeploymentProject[]>;
|
|
256
|
+
/** Create if missing, otherwise return existing. Idempotent. */
|
|
257
|
+
ensureProject(input: {
|
|
258
|
+
name: string;
|
|
259
|
+
framework?: string; /** "owner/repo" — passed when linking to a Git provider. */
|
|
260
|
+
repo?: string;
|
|
261
|
+
rootDirectory?: string;
|
|
262
|
+
}): Promise<DeploymentProject>;
|
|
263
|
+
updateProjectSettings(projectId: string, settings: DeploymentProjectSettings): Promise<DeploymentProject>;
|
|
264
|
+
listEnvVars(projectId: string, target: DeploymentTarget): Promise<string[]>;
|
|
265
|
+
setEnvVar(projectId: string, target: DeploymentTarget, name: string, value: string): Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Kick off a deployment of the given branch. Omit `target` for a
|
|
268
|
+
* branch preview; pass `'production'` / `'staging'` to deploy into
|
|
269
|
+
* a named environment.
|
|
270
|
+
*/
|
|
271
|
+
triggerDeployment(input: {
|
|
272
|
+
projectId: string;
|
|
273
|
+
branch: string;
|
|
274
|
+
target?: DeploymentTrigger;
|
|
275
|
+
}): Promise<DeploymentRecord>;
|
|
276
|
+
getDeployment(deploymentId: string): Promise<DeploymentRecord>;
|
|
277
|
+
}
|
|
278
|
+
interface StorageBranch {
|
|
279
|
+
id: string;
|
|
280
|
+
name: string;
|
|
281
|
+
/** Parent branch id; null for the root/main branch. */
|
|
282
|
+
parentId: string | null;
|
|
283
|
+
createdAt: string;
|
|
284
|
+
}
|
|
285
|
+
interface ConnectionStringOptions {
|
|
286
|
+
/** Use the pooled (PgBouncer) URL when available. Defaults to false. */
|
|
287
|
+
pooled?: boolean;
|
|
288
|
+
}
|
|
289
|
+
interface Storage extends ProviderIdentity {
|
|
290
|
+
readonly key: "storage";
|
|
291
|
+
/**
|
|
292
|
+
* Connection string for the given scope. Scope is provider-specific:
|
|
293
|
+
*
|
|
294
|
+
* - branch-based providers (Neon, PlanetScale): scope = branch
|
|
295
|
+
* name or id
|
|
296
|
+
* - flat providers (single Postgres instance): scope is ignored
|
|
297
|
+
*
|
|
298
|
+
* Callers (or the orchestrator) decide how a deploy target maps to
|
|
299
|
+
* a scope; the storage plugin doesn't own that mapping.
|
|
300
|
+
*/
|
|
301
|
+
getConnectionString(scope: string, options?: ConnectionStringOptions): Promise<string>;
|
|
302
|
+
listBranches?(): Promise<StorageBranch[]>;
|
|
303
|
+
createBranch?(input: {
|
|
304
|
+
name: string;
|
|
305
|
+
from?: string;
|
|
306
|
+
}): Promise<StorageBranch>;
|
|
307
|
+
destroyBranch?(branch: string): Promise<void>;
|
|
308
|
+
/** Restore one branch to match another (e.g., reset preview → main). */
|
|
309
|
+
resetBranch?(input: {
|
|
310
|
+
branch: string;
|
|
311
|
+
from: string;
|
|
312
|
+
}): Promise<void>;
|
|
313
|
+
/**
|
|
314
|
+
* Provider-specific feature toggle. For Postgres providers this is
|
|
315
|
+
* `CREATE EXTENSION IF NOT EXISTS ...` per branch.
|
|
316
|
+
*/
|
|
317
|
+
enableExtension?(input: {
|
|
318
|
+
branch: string;
|
|
319
|
+
extension: string;
|
|
320
|
+
}): Promise<void>;
|
|
321
|
+
}
|
|
322
|
+
interface AuthDescription {
|
|
323
|
+
provider: string;
|
|
324
|
+
/** Env-var names the app needs at runtime (CLERK_PUBLISHABLE_KEY, etc.). */
|
|
325
|
+
envKeys: string[];
|
|
326
|
+
}
|
|
327
|
+
interface AuthIdentity {
|
|
328
|
+
provider: string;
|
|
329
|
+
/** Provider-specific health signal (user count, role, account name, etc.). */
|
|
330
|
+
details?: Record<string, unknown>;
|
|
331
|
+
}
|
|
332
|
+
interface AuthUser {
|
|
333
|
+
id: string;
|
|
334
|
+
email: string;
|
|
335
|
+
}
|
|
336
|
+
interface CreateAuthUserInput {
|
|
337
|
+
email: string;
|
|
338
|
+
password: string;
|
|
339
|
+
firstName?: string;
|
|
340
|
+
lastName?: string;
|
|
341
|
+
}
|
|
342
|
+
interface WebhookDashboardInfo {
|
|
343
|
+
url: string;
|
|
344
|
+
}
|
|
345
|
+
interface Auth extends ProviderIdentity {
|
|
346
|
+
readonly key: "auth";
|
|
347
|
+
/** Env-var keys the runtime app needs. */
|
|
348
|
+
describe(): Promise<AuthDescription>;
|
|
349
|
+
/** Reachability probe — proves the configured key works. */
|
|
350
|
+
whoami(): Promise<AuthIdentity>;
|
|
351
|
+
/** Idempotent webhook backend provisioning (Clerk: Svix app). */
|
|
352
|
+
ensureWebhookApp?(): Promise<{
|
|
353
|
+
alreadyExists: boolean;
|
|
354
|
+
}>;
|
|
355
|
+
/** Deep-link to the provider's webhook config dashboard. */
|
|
356
|
+
getWebhookDashboardUrl?(): Promise<WebhookDashboardInfo>;
|
|
357
|
+
/** Seed a user (test fixtures, admin bootstrap). */
|
|
358
|
+
createUser?(input: CreateAuthUserInput): Promise<AuthUser>;
|
|
359
|
+
/** Wire the auth provider's webhook into the project's repo. */
|
|
360
|
+
syncWebhook?(input: {
|
|
361
|
+
repo: string;
|
|
362
|
+
secretRef: string;
|
|
363
|
+
}): Promise<void>;
|
|
364
|
+
}
|
|
365
|
+
type AuthEventType = "user.created" | "user.updated" | "user.deleted";
|
|
366
|
+
interface NormalizedAuthUser {
|
|
367
|
+
id: string;
|
|
368
|
+
email: string;
|
|
369
|
+
firstName?: string | null;
|
|
370
|
+
lastName?: string | null;
|
|
371
|
+
/** Provider-native fields preserved verbatim for consumers that need them. */
|
|
372
|
+
raw?: Record<string, unknown>;
|
|
373
|
+
}
|
|
374
|
+
interface AuthEvent {
|
|
375
|
+
type: AuthEventType;
|
|
376
|
+
user: NormalizedAuthUser;
|
|
377
|
+
/** ISO timestamp of when the event occurred. */
|
|
378
|
+
occurredAt: string;
|
|
379
|
+
}
|
|
380
|
+
interface ParseWebhookInput {
|
|
381
|
+
/** Raw request body (string or Buffer). */
|
|
382
|
+
body: string | Buffer;
|
|
383
|
+
/** Incoming HTTP headers — needed for signature verification. */
|
|
384
|
+
headers: Record<string, string | string[] | undefined>;
|
|
385
|
+
/** The signing secret the auth provider issued for this webhook endpoint. */
|
|
386
|
+
signingSecret: string;
|
|
387
|
+
}
|
|
388
|
+
declare class WebhookVerificationError extends Error {
|
|
389
|
+
name: string;
|
|
390
|
+
}
|
|
391
|
+
interface Vault extends ProviderIdentity {
|
|
392
|
+
readonly key: "vault";
|
|
393
|
+
/**
|
|
394
|
+
* Read a secret by reference. The reference format is
|
|
395
|
+
* provider-specific (1P: "op://Vault/Item/field"; HashiCorp Vault:
|
|
396
|
+
* "kv/path#field"; etc.). Adapters validate the reference shape.
|
|
397
|
+
*/
|
|
398
|
+
read(reference: string): Promise<string>;
|
|
399
|
+
/** Write or update a secret. */
|
|
400
|
+
write(reference: string, value: string): Promise<void>;
|
|
401
|
+
/** List secret keys available to the project. */
|
|
402
|
+
list(): Promise<string[]>;
|
|
403
|
+
/**
|
|
404
|
+
* Optional environment notion within the vault (e.g., 1P
|
|
405
|
+
* Environments — named KEY=VALUE bundles). Adapters without
|
|
406
|
+
* environments return [].
|
|
407
|
+
*/
|
|
408
|
+
environments?(): Promise<string[]>;
|
|
409
|
+
/**
|
|
410
|
+
* Optional bulk read of an environment's KEY=VALUE pairs. Powers
|
|
411
|
+
* the `holocron secrets sync` flow where the orchestrator pulls a
|
|
412
|
+
* whole environment from the vault then fans the values out to
|
|
413
|
+
* destinations (CI secrets, deployment env vars, local .env).
|
|
414
|
+
*/
|
|
415
|
+
readEnvironment?(environmentId: string): Promise<Record<string, string>>;
|
|
416
|
+
}
|
|
417
|
+
type DnsRecordType = "A" | "AAAA" | "CNAME" | "TXT" | "MX" | "NS" | "SRV" | "CAA";
|
|
418
|
+
interface DnsRecord {
|
|
419
|
+
id?: string;
|
|
420
|
+
type: DnsRecordType;
|
|
421
|
+
name: string;
|
|
422
|
+
content: string;
|
|
423
|
+
ttl?: number;
|
|
424
|
+
priority?: number;
|
|
425
|
+
}
|
|
426
|
+
interface Dns extends ProviderIdentity {
|
|
427
|
+
readonly key: "dns";
|
|
428
|
+
listRecords(domain: string): Promise<DnsRecord[]>;
|
|
429
|
+
upsertRecord(domain: string, record: DnsRecord): Promise<DnsRecord>;
|
|
430
|
+
deleteRecord(domain: string, id: string): Promise<void>;
|
|
431
|
+
}
|
|
432
|
+
interface ToolingDoctorReport {
|
|
433
|
+
ok: boolean;
|
|
434
|
+
message: string;
|
|
435
|
+
}
|
|
436
|
+
interface Tooling extends ProviderIdentity {
|
|
437
|
+
readonly key: "tooling";
|
|
438
|
+
/** Sync the tool's authoritative state from the repo. */
|
|
439
|
+
sync(): Promise<void>;
|
|
440
|
+
doctor(): Promise<ToolingDoctorReport>;
|
|
441
|
+
}
|
|
442
|
+
interface Notifications extends ProviderIdentity {
|
|
443
|
+
readonly key: "notifications";
|
|
444
|
+
/**
|
|
445
|
+
* Send a message. `channel` is provider-specific (Slack channel id,
|
|
446
|
+
* Discord webhook url-name, etc.); adapters resolve from config.
|
|
447
|
+
*/
|
|
448
|
+
send(channel: string, message: string): Promise<void>;
|
|
449
|
+
}
|
|
450
|
+
interface Analytics extends ProviderIdentity {
|
|
451
|
+
readonly key: "analytics";
|
|
452
|
+
describe(): Promise<{
|
|
453
|
+
provider: string;
|
|
454
|
+
dsnEnvKey: string;
|
|
455
|
+
}>;
|
|
456
|
+
}
|
|
457
|
+
interface Observability extends ProviderIdentity {
|
|
458
|
+
readonly key: "observability";
|
|
459
|
+
describe(): Promise<{
|
|
460
|
+
provider: string;
|
|
461
|
+
dsnEnvKey: string;
|
|
462
|
+
}>;
|
|
463
|
+
}
|
|
464
|
+
interface CapabilityImpls {
|
|
465
|
+
source: Source;
|
|
466
|
+
ci: Ci;
|
|
467
|
+
secrets: Secrets;
|
|
468
|
+
environments: Environments;
|
|
469
|
+
issues: Issues;
|
|
470
|
+
deployment: Deployment;
|
|
471
|
+
storage: Storage;
|
|
472
|
+
auth: Auth;
|
|
473
|
+
vault: Vault;
|
|
474
|
+
dns: Dns;
|
|
475
|
+
tooling: Tooling;
|
|
476
|
+
notifications: Notifications;
|
|
477
|
+
analytics: Analytics;
|
|
478
|
+
observability: Observability;
|
|
479
|
+
}
|
|
480
|
+
type CardinalityFor<K extends CapabilityKey> = (typeof CARDINALITY)[K];
|
|
481
|
+
/** 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;
|
|
484
|
+
//#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 };
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
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-CSxf0Yc7.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/config.d.ts
|
|
4
|
+
type ProviderOptions = Record<string, unknown>;
|
|
5
|
+
type SingleEntry = string | [provider: string, options: ProviderOptions];
|
|
6
|
+
type MultiEntry = Array<string | [provider: string, options: ProviderOptions]>;
|
|
7
|
+
type RawProviderEntry = SingleEntry | MultiEntry;
|
|
8
|
+
type RawProvidersConfig = Partial<Record<CapabilityKey, RawProviderEntry>>;
|
|
9
|
+
interface AppConfig {
|
|
10
|
+
name: string;
|
|
11
|
+
path: string;
|
|
12
|
+
kind?: string;
|
|
13
|
+
}
|
|
14
|
+
interface DoctorConfig {
|
|
15
|
+
checks?: string[];
|
|
16
|
+
}
|
|
17
|
+
interface HolocronConfig {
|
|
18
|
+
project: {
|
|
19
|
+
name: string;
|
|
20
|
+
description?: string;
|
|
21
|
+
};
|
|
22
|
+
providers: RawProvidersConfig;
|
|
23
|
+
apps?: AppConfig[];
|
|
24
|
+
doctor?: DoctorConfig;
|
|
25
|
+
}
|
|
26
|
+
interface ResolvedTuple {
|
|
27
|
+
provider: string;
|
|
28
|
+
/** Resolved package name (`@theholocron/holocron-plugin-<provider>` for short refs). */
|
|
29
|
+
packageName: string;
|
|
30
|
+
options: ProviderOptions;
|
|
31
|
+
}
|
|
32
|
+
type ResolvedProviderEntry = {
|
|
33
|
+
cardinality: "single";
|
|
34
|
+
tuple: ResolvedTuple;
|
|
35
|
+
} | {
|
|
36
|
+
cardinality: "many";
|
|
37
|
+
tuples: ResolvedTuple[];
|
|
38
|
+
};
|
|
39
|
+
type ResolvedProvidersConfig = Partial<Record<CapabilityKey, ResolvedProviderEntry>>;
|
|
40
|
+
interface ResolvedHolocronConfig {
|
|
41
|
+
project: HolocronConfig["project"];
|
|
42
|
+
providers: ResolvedProvidersConfig;
|
|
43
|
+
apps: AppConfig[];
|
|
44
|
+
doctor: DoctorConfig;
|
|
45
|
+
}
|
|
46
|
+
declare class ConfigError extends Error {
|
|
47
|
+
name: string;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Resolve `"github"` → `"@theholocron/holocron-plugin-github"`.
|
|
51
|
+
* Fully-qualified names (scoped or not) are honored verbatim, which
|
|
52
|
+
* is how third-party plugins published outside the org work.
|
|
53
|
+
*/
|
|
54
|
+
declare function resolvePluginPackage(provider: string): string;
|
|
55
|
+
declare function resolveEntry(key: CapabilityKey, raw: RawProviderEntry): ResolvedProviderEntry;
|
|
56
|
+
declare function resolveConfig(raw: HolocronConfig): ResolvedHolocronConfig;
|
|
57
|
+
//#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 };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +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 };
|