@vibesdotdev/infra-core 0.0.1

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.
Files changed (133) hide show
  1. package/README.md +78 -0
  2. package/SPEC.md +169 -0
  3. package/dist/bootstrap/index.d.ts +5 -0
  4. package/dist/bootstrap/index.d.ts.map +1 -0
  5. package/dist/bootstrap/index.js +4 -0
  6. package/dist/bootstrap/index.js.map +1 -0
  7. package/dist/bootstrap/seed.descriptor.d.ts +31 -0
  8. package/dist/bootstrap/seed.descriptor.d.ts.map +1 -0
  9. package/dist/bootstrap/seed.descriptor.js +18 -0
  10. package/dist/bootstrap/seed.descriptor.js.map +1 -0
  11. package/dist/bootstrap/seed.impl-shape.d.ts +5 -0
  12. package/dist/bootstrap/seed.impl-shape.d.ts.map +1 -0
  13. package/dist/bootstrap/seed.impl-shape.js +2 -0
  14. package/dist/bootstrap/seed.impl-shape.js.map +1 -0
  15. package/dist/bootstrap/seed.kind.d.ts +5 -0
  16. package/dist/bootstrap/seed.kind.d.ts.map +1 -0
  17. package/dist/bootstrap/seed.kind.js +14 -0
  18. package/dist/bootstrap/seed.kind.js.map +1 -0
  19. package/dist/bootstrap/seed.runner.d.ts +18 -0
  20. package/dist/bootstrap/seed.runner.d.ts.map +1 -0
  21. package/dist/bootstrap/seed.runner.js +59 -0
  22. package/dist/bootstrap/seed.runner.js.map +1 -0
  23. package/dist/credentials/resolve.d.ts +80 -0
  24. package/dist/credentials/resolve.d.ts.map +1 -0
  25. package/dist/credentials/resolve.js +128 -0
  26. package/dist/credentials/resolve.js.map +1 -0
  27. package/dist/deployment.d.ts +9 -0
  28. package/dist/deployment.d.ts.map +1 -0
  29. package/dist/deployment.js +25 -0
  30. package/dist/deployment.js.map +1 -0
  31. package/dist/index.d.ts +32 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +21 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/infra.plugin.d.ts +18 -0
  36. package/dist/infra.plugin.d.ts.map +1 -0
  37. package/dist/infra.plugin.js +52 -0
  38. package/dist/infra.plugin.js.map +1 -0
  39. package/dist/kinds/alerts.kind.d.ts +192 -0
  40. package/dist/kinds/alerts.kind.d.ts.map +1 -0
  41. package/dist/kinds/alerts.kind.js +116 -0
  42. package/dist/kinds/alerts.kind.js.map +1 -0
  43. package/dist/kinds/artifact.kind.d.ts +96 -0
  44. package/dist/kinds/artifact.kind.d.ts.map +1 -0
  45. package/dist/kinds/artifact.kind.js +53 -0
  46. package/dist/kinds/artifact.kind.js.map +1 -0
  47. package/dist/kinds/cache.kind.d.ts +128 -0
  48. package/dist/kinds/cache.kind.d.ts.map +1 -0
  49. package/dist/kinds/cache.kind.js +90 -0
  50. package/dist/kinds/cache.kind.js.map +1 -0
  51. package/dist/kinds/database.kind.d.ts +141 -0
  52. package/dist/kinds/database.kind.d.ts.map +1 -0
  53. package/dist/kinds/database.kind.js +103 -0
  54. package/dist/kinds/database.kind.js.map +1 -0
  55. package/dist/kinds/discovery.d.ts +20 -0
  56. package/dist/kinds/discovery.d.ts.map +1 -0
  57. package/dist/kinds/discovery.js +25 -0
  58. package/dist/kinds/discovery.js.map +1 -0
  59. package/dist/kinds/git-hosting.kind.d.ts +98 -0
  60. package/dist/kinds/git-hosting.kind.d.ts.map +1 -0
  61. package/dist/kinds/git-hosting.kind.js +81 -0
  62. package/dist/kinds/git-hosting.kind.js.map +1 -0
  63. package/dist/kinds/index.d.ts +16 -0
  64. package/dist/kinds/index.d.ts.map +1 -0
  65. package/dist/kinds/index.js +16 -0
  66. package/dist/kinds/index.js.map +1 -0
  67. package/dist/kinds/logs.kind.d.ts +91 -0
  68. package/dist/kinds/logs.kind.d.ts.map +1 -0
  69. package/dist/kinds/logs.kind.js +73 -0
  70. package/dist/kinds/logs.kind.js.map +1 -0
  71. package/dist/kinds/object-storage.kind.d.ts +85 -0
  72. package/dist/kinds/object-storage.kind.d.ts.map +1 -0
  73. package/dist/kinds/object-storage.kind.js +81 -0
  74. package/dist/kinds/object-storage.kind.js.map +1 -0
  75. package/dist/kinds/observability.kind.d.ts +176 -0
  76. package/dist/kinds/observability.kind.d.ts.map +1 -0
  77. package/dist/kinds/observability.kind.js +120 -0
  78. package/dist/kinds/observability.kind.js.map +1 -0
  79. package/dist/kinds/package-registry.kind.d.ts +89 -0
  80. package/dist/kinds/package-registry.kind.d.ts.map +1 -0
  81. package/dist/kinds/package-registry.kind.js +78 -0
  82. package/dist/kinds/package-registry.kind.js.map +1 -0
  83. package/dist/kinds/queue.kind.d.ts +113 -0
  84. package/dist/kinds/queue.kind.d.ts.map +1 -0
  85. package/dist/kinds/queue.kind.js +86 -0
  86. package/dist/kinds/queue.kind.js.map +1 -0
  87. package/dist/kinds/rum.kind.d.ts +113 -0
  88. package/dist/kinds/rum.kind.d.ts.map +1 -0
  89. package/dist/kinds/rum.kind.js +80 -0
  90. package/dist/kinds/rum.kind.js.map +1 -0
  91. package/dist/kinds/sandbox.kind.d.ts +111 -0
  92. package/dist/kinds/sandbox.kind.d.ts.map +1 -0
  93. package/dist/kinds/sandbox.kind.js +94 -0
  94. package/dist/kinds/sandbox.kind.js.map +1 -0
  95. package/dist/kinds/web-app.kind.d.ts +99 -0
  96. package/dist/kinds/web-app.kind.d.ts.map +1 -0
  97. package/dist/kinds/web-app.kind.js +86 -0
  98. package/dist/kinds/web-app.kind.js.map +1 -0
  99. package/dist/kinds/worker.kind.d.ts +244 -0
  100. package/dist/kinds/worker.kind.d.ts.map +1 -0
  101. package/dist/kinds/worker.kind.js +171 -0
  102. package/dist/kinds/worker.kind.js.map +1 -0
  103. package/dist/schemas.d.ts +125 -0
  104. package/dist/schemas.d.ts.map +1 -0
  105. package/dist/schemas.js +102 -0
  106. package/dist/schemas.js.map +1 -0
  107. package/package.json +92 -0
  108. package/src/bootstrap/index.ts +15 -0
  109. package/src/bootstrap/seed.descriptor.ts +24 -0
  110. package/src/bootstrap/seed.impl-shape.ts +5 -0
  111. package/src/bootstrap/seed.kind.ts +26 -0
  112. package/src/bootstrap/seed.runner.ts +87 -0
  113. package/src/credentials/resolve.ts +205 -0
  114. package/src/deployment.ts +57 -0
  115. package/src/index.ts +149 -0
  116. package/src/infra.plugin.ts +54 -0
  117. package/src/kinds/alerts.kind.ts +164 -0
  118. package/src/kinds/artifact.kind.ts +96 -0
  119. package/src/kinds/cache.kind.ts +104 -0
  120. package/src/kinds/database.kind.ts +120 -0
  121. package/src/kinds/discovery.ts +26 -0
  122. package/src/kinds/git-hosting.kind.ts +100 -0
  123. package/src/kinds/index.ts +139 -0
  124. package/src/kinds/logs.kind.ts +104 -0
  125. package/src/kinds/object-storage.kind.ts +101 -0
  126. package/src/kinds/observability.kind.ts +178 -0
  127. package/src/kinds/package-registry.kind.ts +95 -0
  128. package/src/kinds/queue.kind.ts +102 -0
  129. package/src/kinds/rum.kind.ts +135 -0
  130. package/src/kinds/sandbox.kind.ts +115 -0
  131. package/src/kinds/web-app.kind.ts +103 -0
  132. package/src/kinds/worker.kind.ts +203 -0
  133. package/src/schemas.ts +127 -0
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Provider-agnostic credential resolution for infra/* adapters.
3
+ *
4
+ * Routes through the `secrets/store` runtime kind so every adapter
5
+ * (alerts, RUM, observability, …) reads from the same credential chain
6
+ * the rest of the platform uses. The chain (highest precedence first):
7
+ *
8
+ * 1. process.env (CI / explicit overrides)
9
+ * 2. The tier-resolved secrets/store implementation
10
+ * - local tier → encrypted-local → env-file
11
+ * - production → cloudflare-secrets-store → cloudflare-api
12
+ *
13
+ * Adapters declare the secret KEY NAMES (e.g. CLOUDFLARE_API_TOKEN) on
14
+ * their descriptor's `adapterConfig` and use this helper to resolve
15
+ * the values. The helper supports `keyCandidates` for fallback chains
16
+ * (e.g. prefer a scoped CLOUDFLARE_NOTIFICATIONS_API_TOKEN, fall back
17
+ * to the broad CLOUDFLARE_API_TOKEN).
18
+ *
19
+ * Why a helper instead of inlining the secrets/store query: provider
20
+ * adapters live in different packages and shouldn't all duplicate the
21
+ * tier-walk + env-overlay logic. Keeping it in infra-core makes the
22
+ * abstraction explicit ("infra/* adapters route credentials through
23
+ * vibes secrets") and gives us one place to evolve the chain (e.g.
24
+ * adding 1Password CLI as a backend).
25
+ */
26
+
27
+ import { getVibesRuntime } from '@vibesdotdev/runtime';
28
+
29
+ export type EnvironmentTier = 'local' | 'dev' | 'staging' | 'production';
30
+
31
+ export interface ResolveAdapterCredentialOptions {
32
+ /**
33
+ * Ordered list of candidate keys. First one that resolves to a
34
+ * non-empty value wins. Useful for scoped → fallback patterns:
35
+ * ['CLOUDFLARE_NOTIFICATIONS_API_TOKEN', 'CLOUDFLARE_API_TOKEN']
36
+ */
37
+ keyCandidates: readonly string[];
38
+ /**
39
+ * Environment name to read from the secrets store (default: 'local').
40
+ * Maps onto vault.environments[name] in encrypted-local.
41
+ */
42
+ environment?: string;
43
+ /**
44
+ * Tier hint for runtime store selection. Defaults to inferring from
45
+ * the environment name when it matches a known tier; falls back to
46
+ * 'local' otherwise.
47
+ */
48
+ tier?: EnvironmentTier;
49
+ /**
50
+ * Whether to consult process.env first. Defaults true — CI/CD and
51
+ * explicit shell overrides should win. Pass false to force
52
+ * store-only resolution.
53
+ */
54
+ includeProcessEnv?: boolean;
55
+ /**
56
+ * Optional human-readable name for the credential, used in error
57
+ * messages when no candidate resolves (e.g. "Cloudflare API token").
58
+ */
59
+ humanName?: string;
60
+ }
61
+
62
+ export interface ResolvedAdapterCredential {
63
+ /** The KEY that resolved (one of keyCandidates). Useful for audit. */
64
+ key: string;
65
+ /** The resolved secret value. Never logged. */
66
+ value: string;
67
+ /** Where the value came from: 'process.env' or the store backend id. */
68
+ source: string;
69
+ }
70
+
71
+ interface SecretsStoreImplShape {
72
+ id?: string;
73
+ get?(environment: string, key: string): Promise<string | undefined>;
74
+ }
75
+
76
+ interface SecretsStoreDescriptorShape {
77
+ id: string;
78
+ priority?: number;
79
+ tiers?: readonly EnvironmentTier[] | EnvironmentTier[];
80
+ }
81
+
82
+ /**
83
+ * Tier-aware backend selection. Mirrors the secrets/store kind's own
84
+ * resolver (`packages/secrets/src/kinds/store.kind.ts`) so callers
85
+ * don't need to import internal helpers.
86
+ */
87
+ function pickBackend(
88
+ descriptors: SecretsStoreDescriptorShape[],
89
+ tier: EnvironmentTier
90
+ ): SecretsStoreDescriptorShape | undefined {
91
+ if (descriptors.length === 0) return undefined;
92
+ const matches = descriptors.filter((d) => (d.tiers ?? []).includes(tier));
93
+ const pool = matches.length > 0 ? matches : descriptors;
94
+ pool.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
95
+ return pool[0];
96
+ }
97
+
98
+ function inferTier(environment: string, override?: EnvironmentTier): EnvironmentTier {
99
+ if (override) return override;
100
+ if (environment === 'local' || environment === 'dev' || environment === 'staging' || environment === 'production') {
101
+ return environment;
102
+ }
103
+ return 'local';
104
+ }
105
+
106
+ /**
107
+ * Resolve a single credential by walking the candidate keys against
108
+ * process.env then the active secrets store. Returns the first match.
109
+ *
110
+ * Throws when no candidate resolves — adapters should let this bubble
111
+ * up so the CLI surfaces a clean "missing credentials" message rather
112
+ * than a vague "401 Unauthorized" downstream.
113
+ */
114
+ export async function resolveAdapterCredential(
115
+ options: ResolveAdapterCredentialOptions
116
+ ): Promise<ResolvedAdapterCredential> {
117
+ const env = options.environment ?? 'local';
118
+ const tier = inferTier(env, options.tier);
119
+ const includeEnv = options.includeProcessEnv ?? true;
120
+
121
+ // 1. process.env (CI / shell override)
122
+ if (includeEnv) {
123
+ for (const key of options.keyCandidates) {
124
+ const v = process.env[key];
125
+ if (v && v.length > 0) {
126
+ return { key, value: v, source: 'process.env' };
127
+ }
128
+ }
129
+ }
130
+
131
+ // 2. secrets/store runtime resolution (tier-aware, priority-ordered)
132
+ const runtime = getVibesRuntime();
133
+ if (!runtime.hasKind('secrets/store')) {
134
+ throw new Error(
135
+ `No secrets/store kind registered; cannot resolve ${options.humanName ?? options.keyCandidates[0]}. ` +
136
+ 'Load the secrets plugin (and a backend like encrypted-local) before calling this adapter.'
137
+ );
138
+ }
139
+
140
+ const descriptors = runtime
141
+ .assets('secrets/store')
142
+ .descriptors() as SecretsStoreDescriptorShape[];
143
+ // `VIBES_DEBUG_SECRETS=1` dumps the visible descriptor pool when
144
+ // you're tracing why a credential is missing or getting misrouted.
145
+ if (process.env.VIBES_DEBUG_SECRETS === '1') {
146
+ console.error(`[secrets-debug] descriptors visible at tier=${tier}:`);
147
+ for (const d of descriptors) {
148
+ console.error(` - id=${d.id} priority=${d.priority ?? '-'} tiers=${JSON.stringify(d.tiers)}`);
149
+ }
150
+ }
151
+ const target = pickBackend(descriptors, tier);
152
+ if (!target) {
153
+ throw new Error(
154
+ `No secrets/store backend available for tier=${tier}. Register a backend ` +
155
+ '(encrypted-local for local, cloudflare-secrets-store for production) before calling this adapter.'
156
+ );
157
+ }
158
+
159
+ const store = (await runtime
160
+ .query('secrets/store')
161
+ .withId(target.id)
162
+ .resolve()) as SecretsStoreImplShape;
163
+
164
+ if (!store || typeof store.get !== 'function') {
165
+ throw new Error(
166
+ `Resolved secrets/store '${target.id}' has no get() method for ${options.humanName ?? options.keyCandidates[0]}.`
167
+ );
168
+ }
169
+
170
+ for (const key of options.keyCandidates) {
171
+ try {
172
+ const v = await store.get(env, key);
173
+ if (v && v.length > 0) {
174
+ return { key, value: v, source: target.id };
175
+ }
176
+ } catch {
177
+ // next candidate
178
+ }
179
+ }
180
+
181
+ throw new Error(
182
+ `Missing credential: none of [${options.keyCandidates.join(', ')}] resolved from process.env or ` +
183
+ `${target.id} (environment=${env}, tier=${tier}). ` +
184
+ `Set the value with \`vibes secrets set --environment ${env} ${options.keyCandidates[0]} <value>\`.`
185
+ );
186
+ }
187
+
188
+ /**
189
+ * Convenience wrapper for resolving multiple credentials at once.
190
+ * Each key gets a candidate list of one (the key itself). Use the
191
+ * single-credential form when you need fallback chains.
192
+ */
193
+ export async function resolveAdapterCredentials(
194
+ keys: readonly string[],
195
+ options?: Omit<ResolveAdapterCredentialOptions, 'keyCandidates' | 'humanName'>
196
+ ): Promise<Record<string, ResolvedAdapterCredential>> {
197
+ const out: Record<string, ResolvedAdapterCredential> = {};
198
+ for (const key of keys) {
199
+ out[key] = await resolveAdapterCredential({
200
+ keyCandidates: [key],
201
+ ...(options ?? {})
202
+ });
203
+ }
204
+ return out;
205
+ }
@@ -0,0 +1,57 @@
1
+ import {
2
+ appDeploymentSchema,
3
+ deploymentDependencySchema,
4
+ deploymentEnvRequirementSchema,
5
+ deploymentOriginSchema,
6
+ type AppDeployment,
7
+ type AppDeploymentInput,
8
+ type DeploymentDependency,
9
+ type DeploymentEnvRequirement,
10
+ type DeploymentOrigin,
11
+ type DeploymentUpstream
12
+ } from './schemas.ts';
13
+
14
+ export function createAppDeployment(input: AppDeploymentInput): AppDeployment {
15
+ return appDeploymentSchema.parse(input);
16
+ }
17
+
18
+ export function createDeploymentOrigin(input: DeploymentOrigin): DeploymentOrigin {
19
+ return deploymentOriginSchema.parse(input);
20
+ }
21
+
22
+ export function createDeploymentEnvRequirement(
23
+ input: DeploymentEnvRequirement
24
+ ): DeploymentEnvRequirement {
25
+ return deploymentEnvRequirementSchema.parse(input);
26
+ }
27
+
28
+ export function createDeploymentDependency(input: DeploymentDependency): DeploymentDependency {
29
+ return deploymentDependencySchema.parse(input);
30
+ }
31
+
32
+ export function getPrimaryOrigin(deployment: AppDeployment): DeploymentOrigin {
33
+ return (
34
+ deployment.origins.find((origin) => origin.kind === 'primary') ?? deployment.origins[0]
35
+ );
36
+ }
37
+
38
+ export function getUpstreamOriginEnv(
39
+ upstream: DeploymentUpstream
40
+ ): Record<string, string> {
41
+ if (!upstream.originEnvVar) {
42
+ return {};
43
+ }
44
+
45
+ return {
46
+ [upstream.originEnvVar]: upstream.origin
47
+ };
48
+ }
49
+
50
+ export type {
51
+ AppDeployment,
52
+ AppDeploymentInput,
53
+ DeploymentDependency,
54
+ DeploymentEnvRequirement,
55
+ DeploymentOrigin,
56
+ DeploymentUpstream
57
+ };
package/src/index.ts ADDED
@@ -0,0 +1,149 @@
1
+ export {
2
+ createAppDeployment,
3
+ createDeploymentOrigin,
4
+ createDeploymentEnvRequirement,
5
+ createDeploymentDependency,
6
+ getPrimaryOrigin,
7
+ getUpstreamOriginEnv
8
+ } from './deployment.ts';
9
+ export type {
10
+ AppDeployment,
11
+ AppDeploymentInput,
12
+ DeploymentDependency,
13
+ DeploymentEnvRequirement,
14
+ DeploymentOrigin,
15
+ DeploymentUpstream
16
+ } from './deployment.ts';
17
+ export {
18
+ deploymentProviderSchema,
19
+ appRuntimeSchema,
20
+ deploymentOriginSchema,
21
+ deploymentBuildSchema,
22
+ deploymentEnvRequirementSchema,
23
+ deploymentDependencySchema,
24
+ deploymentUpstreamSchema,
25
+ appDeploymentSchema
26
+ } from './schemas.ts';
27
+ export type {
28
+ DeploymentProvider,
29
+ AppRuntime,
30
+ DeploymentOrigin as DeploymentOriginType,
31
+ DeploymentBuild,
32
+ DeploymentEnvRequirement as DeploymentEnvRequirementType,
33
+ DeploymentDependency as DeploymentDependencyType,
34
+ DeploymentUpstream as DeploymentUpstreamType,
35
+ AppDeploymentInput as AppDeploymentInputType,
36
+ AppDeployment as AppDeploymentType
37
+ } from './schemas.ts';
38
+ export {
39
+ webAppKind, WebAppDescriptorSchema, WebAppConfigSchema, CloudflareWorkersWebAppConfigSchema, DigitalOceanAppWebAppConfigSchema
40
+ } from './kinds/index.ts';
41
+ export type { WebAppDescriptor, WebAppDependency } from './kinds/index.ts';
42
+ export {
43
+ workerKind, WorkerDescriptorSchema, WorkerConfigSchema, DoksDeploymentWorkerConfigSchema, WorkerScalingSchema
44
+ } from './kinds/index.ts';
45
+ export type { WorkerDescriptor, WorkerScaling } from './kinds/index.ts';
46
+ export {
47
+ databaseKind, DatabaseDescriptorSchema, DatabaseConfigSchema, DoksDatabaseConfigSchema, DOManagedPostgresConfigSchema, TursoDatabaseConfigSchema, CloudflareD1ConfigSchema, DatabaseBackupSchema
48
+ } from './kinds/index.ts';
49
+ export type { DatabaseDescriptor, DatabaseBackup } from './kinds/index.ts';
50
+ export {
51
+ queueKind, QueueDescriptorSchema, QueueConfigSchema, DoksQueueConfigSchema, QueueTopicSchema
52
+ } from './kinds/index.ts';
53
+ export type { QueueDescriptor, QueueTopic } from './kinds/index.ts';
54
+ export { cacheKind, CacheDescriptorSchema, CacheConfigSchema, DOManagedRedisConfigSchema } from './kinds/index.ts';
55
+ export type { CacheDescriptor } from './kinds/index.ts';
56
+ export {
57
+ objectStorageKind, ObjectStorageDescriptorSchema, ObjectStorageConfigSchema, DOSpacesConfigSchema, CorsRuleSchema, LifecycleRuleSchema
58
+ } from './kinds/index.ts';
59
+ export type { ObjectStorageDescriptor, CorsRule, LifecycleRule } from './kinds/index.ts';
60
+ export {
61
+ sandboxKind, SandboxDescriptorSchema, SandboxConfigSchema, CloudflareSandboxConfigSchema, DockerSandboxConfigSchema, DigitalOceanDropletSandboxConfigSchema, SandboxOutboundProxySchema, SandboxResourceLimitsSchema
62
+ } from './kinds/index.ts';
63
+ export type { SandboxDescriptor, SandboxOutboundProxy, SandboxResourceLimits } from './kinds/index.ts';
64
+ export {
65
+ gitHostingKind, GitHostingDescriptorSchema, GitHostingConfigSchema, GiteaDoksGitHostingConfigSchema, GitHostingTypeSchema, GitHostingAuthMethodSchema
66
+ } from './kinds/index.ts';
67
+ export type { GitHostingDescriptor, GitHostingType, GitHostingAuthMethod } from './kinds/index.ts';
68
+ export {
69
+ packageRegistryKind, PackageRegistryDescriptorSchema, PackageRegistryConfigSchema, VerdaccioDoksPackageRegistryConfigSchema, PackageRegistryTypeSchema
70
+ } from './kinds/index.ts';
71
+ export type { PackageRegistryDescriptor, PackageRegistryType } from './kinds/index.ts';
72
+ export {
73
+ logsKind, InfraLogsDescriptorSchema, type InfraLogsDescriptor, type InfraLogsDescriptorInput, type InfraLogsKind, type InfraLogsKindInput, type LogsImplementation
74
+ } from './kinds/index.ts';
75
+ export {
76
+ artifactKind,
77
+ ArtifactDescriptorSchema,
78
+ type ArtifactDescriptor,
79
+ type ArtifactGenerator,
80
+ type ArtifactOutput
81
+ } from './kinds/index.ts';
82
+ export { INFRA_DISCOVERY_PATTERN, extractStem } from './kinds/index.ts';
83
+ export {
84
+ alertsKind,
85
+ InfraAlertsDescriptorSchema,
86
+ AlertPolicySchema,
87
+ AlertMechanismSchema,
88
+ AlertTypeSchema,
89
+ AlertWindowSchema,
90
+ type InfraAlertsDescriptor,
91
+ type InfraAlertsDescriptorInput,
92
+ type InfraAlertsKind,
93
+ type InfraAlertsKindInput,
94
+ type AlertsImplementation,
95
+ type AlertPolicy,
96
+ type AlertMechanism,
97
+ type AlertType
98
+ } from './kinds/index.ts';
99
+ export {
100
+ observabilityKind,
101
+ InfraObservabilityDescriptorSchema,
102
+ ObservabilityScopeSchema,
103
+ ObservabilitySettingsSchema,
104
+ type InfraObservabilityDescriptor,
105
+ type InfraObservabilityDescriptorInput,
106
+ type InfraObservabilityKind,
107
+ type InfraObservabilityKindInput,
108
+ type ObservabilityImplementation,
109
+ type ObservabilityScope,
110
+ type ObservabilitySettings,
111
+ type ObservabilitySnapshot,
112
+ type SetObservabilityInput,
113
+ type SetObservabilityResult
114
+ } from './kinds/index.ts';
115
+ export {
116
+ rumKind,
117
+ InfraRumDescriptorSchema,
118
+ RumPrivacySchema,
119
+ RumInjectionSchema,
120
+ type InfraRumDescriptor,
121
+ type InfraRumDescriptorInput,
122
+ type InfraRumKind,
123
+ type InfraRumKindInput,
124
+ type RumImplementation,
125
+ type RumSiteSnapshot,
126
+ type RumPrivacy,
127
+ type RumInjection
128
+ } from './kinds/index.ts';
129
+ export {
130
+ resolveAdapterCredential,
131
+ resolveAdapterCredentials,
132
+ type EnvironmentTier,
133
+ type ResolveAdapterCredentialOptions,
134
+ type ResolvedAdapterCredential
135
+ } from './credentials/resolve.ts';
136
+ export {
137
+ bootstrapSeedKind,
138
+ bootstrapSeedConditionSchema,
139
+ bootstrapSeedDescriptorSchema,
140
+ bootstrapSeedModeSchema,
141
+ runBootstrapSeeds,
142
+ type BootstrapSeedCondition,
143
+ type BootstrapSeedDescriptor,
144
+ type BootstrapSeedImpl,
145
+ type BootstrapSeedMode,
146
+ type BootstrapSeedRunOptions,
147
+ type BootstrapSeedRunResult
148
+ } from './bootstrap/index.ts';
149
+ export { default as infraPlugin } from './infra.plugin';
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Infrastructure Runtime Plugin
3
+ *
4
+ * Registers all infra resource kinds with the runtime.
5
+ */
6
+
7
+ import { createRuntimePlugin } from '@vibesdotdev/runtime/factory/plugin';
8
+ import { webAppKind } from './kinds/web-app.kind';
9
+ import { workerKind } from './kinds/worker.kind';
10
+ import { databaseKind } from './kinds/database.kind';
11
+ import { queueKind } from './kinds/queue.kind';
12
+ import { cacheKind } from './kinds/cache.kind';
13
+ import { objectStorageKind } from './kinds/object-storage.kind';
14
+ import { sandboxKind } from './kinds/sandbox.kind';
15
+ import { gitHostingKind } from './kinds/git-hosting.kind';
16
+ import { packageRegistryKind } from './kinds/package-registry.kind';
17
+ import { logsKind } from './kinds/logs.kind';
18
+ import { alertsKind } from './kinds/alerts.kind.ts';
19
+ import { observabilityKind } from './kinds/observability.kind.ts';
20
+ import { rumKind } from './kinds/rum.kind.ts';
21
+ import { bootstrapSeedKind } from './bootstrap/index.ts';
22
+
23
+ /**
24
+ * Note on layering: inference is an apps/ai domain concern, not an infra kind.
25
+ * Existing runtime kinds `ai/provider`, `ai/adapter`, `ai/model`, and
26
+ * `ai/generation` already express "who/how/where inference runs", and the
27
+ * runtime's hardware scope + connectionMode qualifier + priority already
28
+ * select between consumer-direct, consumer-worker, cloud-worker, and
29
+ * cloud-via-API strategies. New strategies (CF Unified Inference, Agents
30
+ * Week bindings) should be added as new `ai/provider` descriptors + adapters,
31
+ * not as a parallel `infra/inference` kind.
32
+ */
33
+
34
+ export default createRuntimePlugin({
35
+ id: 'infra',
36
+ name: 'Infrastructure',
37
+ description: 'Resource kinds for deployable infrastructure',
38
+ kinds: [
39
+ webAppKind,
40
+ workerKind,
41
+ databaseKind,
42
+ queueKind,
43
+ cacheKind,
44
+ objectStorageKind,
45
+ sandboxKind,
46
+ gitHostingKind,
47
+ packageRegistryKind,
48
+ logsKind,
49
+ alertsKind,
50
+ observabilityKind,
51
+ rumKind,
52
+ bootstrapSeedKind
53
+ ]
54
+ });
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Infra Alerts Kind
3
+ *
4
+ * Provider-agnostic schema for alerting / notification policies that
5
+ * span billing (budget + per-product usage), reliability (error rate,
6
+ * latency), and operational lifecycle (deploys, subscription changes).
7
+ *
8
+ * Per-provider implementations (cloudflare-alerts, do-monitoring,
9
+ * cloudwatch-alarms) map this shape onto the native API. Where a
10
+ * provider doesn't support a `type`, the impl rejects with a clear
11
+ * "unsupported" error rather than silently falling back.
12
+ *
13
+ * Configuration is declarative — typically encoded in an
14
+ * `infra/account.config.ts` or `infra/alerts.infra.ts` file alongside
15
+ * `infra/dns.ts`. The CLI (`vibes infra alerts list|create|delete`)
16
+ * wraps the same primitive for one-off ops and drift detection.
17
+ */
18
+
19
+ import * as z from 'zod/v4';
20
+ import { createRuntimeKind } from '@vibesdotdev/runtime/factory/kind';
21
+ import { RuntimeDescriptorSchema } from '@vibesdotdev/runtime/schemas/descriptor';
22
+ import { INFRA_DISCOVERY_PATTERN, extractStem } from './discovery.ts';
23
+
24
+ /**
25
+ * Delivery channel for an alert. Provider impls translate these to
26
+ * native delivery slots (e.g. CF's `mechanisms.email[]`).
27
+ */
28
+ const AlertMechanismSchema = z.discriminatedUnion('kind', [
29
+ z.object({
30
+ kind: z.literal('email'),
31
+ /** Email address — must be a verified destination on the provider. */
32
+ target: z.email()
33
+ }),
34
+ z.object({
35
+ kind: z.literal('webhook'),
36
+ /** Outbound webhook URL. */
37
+ target: z.string().url(),
38
+ /** Optional secret name (in the active secrets store) used for HMAC signing. */
39
+ secretRef: z.string().optional()
40
+ }),
41
+ z.object({
42
+ kind: z.literal('pagerduty'),
43
+ /** PagerDuty integration key (Events API v2). */
44
+ target: z.string().min(1)
45
+ }),
46
+ z.object({
47
+ kind: z.literal('slack'),
48
+ /** Slack webhook URL (Incoming Webhooks). */
49
+ target: z.string().url()
50
+ })
51
+ ]);
52
+
53
+ export type AlertMechanism = z.infer<typeof AlertMechanismSchema>;
54
+
55
+ /**
56
+ * Cross-provider alert categories. Each maps to one or more native
57
+ * alert types per provider.
58
+ */
59
+ const AlertTypeSchema = z.enum([
60
+ /** Total cost crossed a dollar threshold for the current billing period. */
61
+ 'budget',
62
+ /** Per-product usage crossed a numeric threshold (events/req/GB/etc.). */
63
+ 'usage',
64
+ /** Worker/service error rate over a rolling window. */
65
+ 'error-rate',
66
+ /** P99 latency or wall-time crossed a threshold. */
67
+ 'latency',
68
+ /** Catch-all for provider-specific types not yet modeled here. */
69
+ 'custom'
70
+ ]);
71
+
72
+ export type AlertType = z.infer<typeof AlertTypeSchema>;
73
+
74
+ const AlertWindowSchema = z.enum(['5m', '15m', '1h', '6h', '24h', '7d', '30d']);
75
+
76
+ const AlertPolicySchema = z.object({
77
+ /** Stable id for diffing. Auto-generated when omitted. */
78
+ id: z.string().min(1).optional(),
79
+ /** Human-readable name (also surfaced by the provider). */
80
+ name: z.string().min(1),
81
+ type: AlertTypeSchema,
82
+ /**
83
+ * Threshold value. `budget` reads it as USD (or local currency).
84
+ * `usage`/`error-rate`/`latency` read it as units defined by the
85
+ * filter (event count, %, ms).
86
+ */
87
+ threshold: z.number().nonnegative(),
88
+ /** Optional secondary threshold for tiered alerts. */
89
+ warningThreshold: z.number().nonnegative().optional(),
90
+ /** Rolling window for `usage`/`error-rate`/`latency`. Ignored for `budget`. */
91
+ window: AlertWindowSchema.optional(),
92
+ /**
93
+ * Adapter-specific filters. Examples:
94
+ * { product: 'workers_observability' } // usage on a specific CF product
95
+ * { worker: 'vibes-account' } // error-rate scoped to one worker
96
+ * { native_alert_type: 'health_check_status_notification' } // custom escape
97
+ */
98
+ filters: z.record(z.string(), z.string()).default({}),
99
+ mechanisms: z.array(AlertMechanismSchema).min(1),
100
+ enabled: z.boolean().default(true),
101
+ description: z.string().optional()
102
+ });
103
+
104
+ export type AlertPolicy = z.infer<typeof AlertPolicySchema>;
105
+
106
+ const AlertsBaseSchema = RuntimeDescriptorSchema.extend({
107
+ kind: z.literal('infra/alerts'),
108
+ /** Adapter id (e.g. 'cloudflare-alerts', 'do-monitoring'). */
109
+ adapter: z.string().min(1),
110
+ adapterConfig: z.record(z.string(), z.unknown()).optional(),
111
+ /**
112
+ * Default environment name for credential resolution via `vibes
113
+ * secrets`. Defaults to 'local' since the CLI / dev workflow is
114
+ * the primary caller; production Workers that consume this adapter
115
+ * read credentials from CF Secrets Store bindings directly, not
116
+ * through this descriptor.
117
+ */
118
+ environment: z.string().default('local'),
119
+ policies: z.array(AlertPolicySchema).default([])
120
+ });
121
+
122
+ export type InfraAlertsDescriptor = z.infer<typeof AlertsBaseSchema>;
123
+ export type InfraAlertsDescriptorInput = z.input<typeof AlertsBaseSchema>;
124
+
125
+ /**
126
+ * Implementation contract every provider implements. Operations are
127
+ * idempotent: `applyPolicies` reconciles the live policy set to the
128
+ * desired set without trampling unrelated policies on the provider.
129
+ */
130
+ export interface AlertsImplementation {
131
+ /** List policies currently configured on the provider. */
132
+ listPolicies(): Promise<Array<AlertPolicy & { providerId: string }>>;
133
+ /** Create a single policy. Returns the provider-assigned id. */
134
+ createPolicy(policy: AlertPolicy): Promise<{ providerId: string }>;
135
+ /** Delete a policy by provider id. */
136
+ deletePolicy(providerId: string): Promise<void>;
137
+ /**
138
+ * Diff desired (descriptor) against live; return the set of policies
139
+ * that need create/update/delete. Empty arrays = in sync.
140
+ */
141
+ diff(desired: AlertPolicy[]): Promise<{
142
+ create: AlertPolicy[];
143
+ update: Array<{ providerId: string; policy: AlertPolicy }>;
144
+ delete: Array<{ providerId: string; name: string }>;
145
+ }>;
146
+ }
147
+
148
+ export const alertsKind = createRuntimeKind<InfraAlertsDescriptor, AlertsImplementation>({
149
+ id: 'infra/alerts',
150
+ descriptorSchema: AlertsBaseSchema,
151
+ discoveryPattern: INFRA_DISCOVERY_PATTERN,
152
+ extractStem
153
+ });
154
+
155
+ export type InfraAlertsKind = InfraAlertsDescriptor;
156
+ export type InfraAlertsKindInput = InfraAlertsDescriptorInput;
157
+
158
+ export {
159
+ AlertsBaseSchema as InfraAlertsDescriptorSchema,
160
+ AlertPolicySchema,
161
+ AlertMechanismSchema,
162
+ AlertTypeSchema,
163
+ AlertWindowSchema
164
+ };