@vibesdotdev/infra-deploy 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 (183) hide show
  1. package/README.md +125 -0
  2. package/SPEC.md +181 -0
  3. package/dist/cli/alerts/infra-alerts-create.cli-command.descriptor.d.ts +39 -0
  4. package/dist/cli/alerts/infra-alerts-create.cli-command.descriptor.d.ts.map +1 -0
  5. package/dist/cli/alerts/infra-alerts-create.cli-command.descriptor.js +61 -0
  6. package/dist/cli/alerts/infra-alerts-create.cli-command.descriptor.js.map +1 -0
  7. package/dist/cli/alerts/infra-alerts-create.cli-command.impl.d.ts +13 -0
  8. package/dist/cli/alerts/infra-alerts-create.cli-command.impl.d.ts.map +1 -0
  9. package/dist/cli/alerts/infra-alerts-create.cli-command.impl.js +109 -0
  10. package/dist/cli/alerts/infra-alerts-create.cli-command.impl.js.map +1 -0
  11. package/dist/cli/alerts/infra-alerts-delete.cli-command.descriptor.d.ts +31 -0
  12. package/dist/cli/alerts/infra-alerts-delete.cli-command.descriptor.d.ts.map +1 -0
  13. package/dist/cli/alerts/infra-alerts-delete.cli-command.descriptor.js +36 -0
  14. package/dist/cli/alerts/infra-alerts-delete.cli-command.descriptor.js.map +1 -0
  15. package/dist/cli/alerts/infra-alerts-delete.cli-command.impl.d.ts +11 -0
  16. package/dist/cli/alerts/infra-alerts-delete.cli-command.impl.d.ts.map +1 -0
  17. package/dist/cli/alerts/infra-alerts-delete.cli-command.impl.js +67 -0
  18. package/dist/cli/alerts/infra-alerts-delete.cli-command.impl.js.map +1 -0
  19. package/dist/cli/alerts/infra-alerts-list.cli-command.descriptor.d.ts +21 -0
  20. package/dist/cli/alerts/infra-alerts-list.cli-command.descriptor.d.ts.map +1 -0
  21. package/dist/cli/alerts/infra-alerts-list.cli-command.descriptor.js +27 -0
  22. package/dist/cli/alerts/infra-alerts-list.cli-command.descriptor.js.map +1 -0
  23. package/dist/cli/alerts/infra-alerts-list.cli-command.impl.d.ts +12 -0
  24. package/dist/cli/alerts/infra-alerts-list.cli-command.impl.d.ts.map +1 -0
  25. package/dist/cli/alerts/infra-alerts-list.cli-command.impl.js +74 -0
  26. package/dist/cli/alerts/infra-alerts-list.cli-command.impl.js.map +1 -0
  27. package/dist/cli/alerts/infra-alerts.cli-group.descriptor.d.ts +12 -0
  28. package/dist/cli/alerts/infra-alerts.cli-group.descriptor.d.ts.map +1 -0
  29. package/dist/cli/alerts/infra-alerts.cli-group.descriptor.js +12 -0
  30. package/dist/cli/alerts/infra-alerts.cli-group.descriptor.js.map +1 -0
  31. package/dist/cli/audit/infra-audit.cli-command.descriptor.d.ts +21 -0
  32. package/dist/cli/audit/infra-audit.cli-command.descriptor.d.ts.map +1 -0
  33. package/dist/cli/audit/infra-audit.cli-command.descriptor.js +28 -0
  34. package/dist/cli/audit/infra-audit.cli-command.descriptor.js.map +1 -0
  35. package/dist/cli/audit/infra-audit.cli-command.impl.d.ts +18 -0
  36. package/dist/cli/audit/infra-audit.cli-command.impl.d.ts.map +1 -0
  37. package/dist/cli/audit/infra-audit.cli-command.impl.js +219 -0
  38. package/dist/cli/audit/infra-audit.cli-command.impl.js.map +1 -0
  39. package/dist/cli/infra-deploy.cli-group.descriptor.d.ts +12 -0
  40. package/dist/cli/infra-deploy.cli-group.descriptor.d.ts.map +1 -0
  41. package/dist/cli/infra-deploy.cli-group.descriptor.js +12 -0
  42. package/dist/cli/infra-deploy.cli-group.descriptor.js.map +1 -0
  43. package/dist/cli/infra.cli-group.descriptor.d.ts +11 -0
  44. package/dist/cli/infra.cli-group.descriptor.d.ts.map +1 -0
  45. package/dist/cli/infra.cli-group.descriptor.js +11 -0
  46. package/dist/cli/infra.cli-group.descriptor.js.map +1 -0
  47. package/dist/cli/list/infra-deploy.list.cli-command.descriptor.d.ts +34 -0
  48. package/dist/cli/list/infra-deploy.list.cli-command.descriptor.d.ts.map +1 -0
  49. package/dist/cli/list/infra-deploy.list.cli-command.descriptor.js +29 -0
  50. package/dist/cli/list/infra-deploy.list.cli-command.descriptor.js.map +1 -0
  51. package/dist/cli/list/infra-deploy.list.cli-command.impl.d.ts +5 -0
  52. package/dist/cli/list/infra-deploy.list.cli-command.impl.d.ts.map +1 -0
  53. package/dist/cli/list/infra-deploy.list.cli-command.impl.js +110 -0
  54. package/dist/cli/list/infra-deploy.list.cli-command.impl.js.map +1 -0
  55. package/dist/cli/logs/infra-logs.cli-command.descriptor.d.ts +39 -0
  56. package/dist/cli/logs/infra-logs.cli-command.descriptor.d.ts.map +1 -0
  57. package/dist/cli/logs/infra-logs.cli-command.descriptor.js +64 -0
  58. package/dist/cli/logs/infra-logs.cli-command.descriptor.js.map +1 -0
  59. package/dist/cli/logs/infra-logs.cli-command.impl.d.ts +5 -0
  60. package/dist/cli/logs/infra-logs.cli-command.impl.d.ts.map +1 -0
  61. package/dist/cli/logs/infra-logs.cli-command.impl.js +323 -0
  62. package/dist/cli/logs/infra-logs.cli-command.impl.js.map +1 -0
  63. package/dist/cli/logs/stream-worker-tail.d.ts +62 -0
  64. package/dist/cli/logs/stream-worker-tail.d.ts.map +1 -0
  65. package/dist/cli/logs/stream-worker-tail.js +165 -0
  66. package/dist/cli/logs/stream-worker-tail.js.map +1 -0
  67. package/dist/cli/npm/infra-npm-publish.cli-command.descriptor.d.ts +27 -0
  68. package/dist/cli/npm/infra-npm-publish.cli-command.descriptor.d.ts.map +1 -0
  69. package/dist/cli/npm/infra-npm-publish.cli-command.descriptor.js +75 -0
  70. package/dist/cli/npm/infra-npm-publish.cli-command.descriptor.js.map +1 -0
  71. package/dist/cli/npm/infra-npm-publish.cli-command.impl.d.ts +5 -0
  72. package/dist/cli/npm/infra-npm-publish.cli-command.impl.d.ts.map +1 -0
  73. package/dist/cli/npm/infra-npm-publish.cli-command.impl.js +383 -0
  74. package/dist/cli/npm/infra-npm-publish.cli-command.impl.js.map +1 -0
  75. package/dist/cli/npm/infra-npm.cli-group.descriptor.d.ts +12 -0
  76. package/dist/cli/npm/infra-npm.cli-group.descriptor.d.ts.map +1 -0
  77. package/dist/cli/npm/infra-npm.cli-group.descriptor.js +12 -0
  78. package/dist/cli/npm/infra-npm.cli-group.descriptor.js.map +1 -0
  79. package/dist/cli/observability/infra-observability-set.cli-command.descriptor.d.ts +25 -0
  80. package/dist/cli/observability/infra-observability-set.cli-command.descriptor.d.ts.map +1 -0
  81. package/dist/cli/observability/infra-observability-set.cli-command.descriptor.js +57 -0
  82. package/dist/cli/observability/infra-observability-set.cli-command.descriptor.js.map +1 -0
  83. package/dist/cli/observability/infra-observability-set.cli-command.impl.d.ts +17 -0
  84. package/dist/cli/observability/infra-observability-set.cli-command.impl.d.ts.map +1 -0
  85. package/dist/cli/observability/infra-observability-set.cli-command.impl.js +152 -0
  86. package/dist/cli/observability/infra-observability-set.cli-command.impl.js.map +1 -0
  87. package/dist/cli/observability/infra-observability-status.cli-command.descriptor.d.ts +21 -0
  88. package/dist/cli/observability/infra-observability-status.cli-command.descriptor.d.ts.map +1 -0
  89. package/dist/cli/observability/infra-observability-status.cli-command.descriptor.js +35 -0
  90. package/dist/cli/observability/infra-observability-status.cli-command.descriptor.js.map +1 -0
  91. package/dist/cli/observability/infra-observability-status.cli-command.impl.d.ts +17 -0
  92. package/dist/cli/observability/infra-observability-status.cli-command.impl.d.ts.map +1 -0
  93. package/dist/cli/observability/infra-observability-status.cli-command.impl.js +99 -0
  94. package/dist/cli/observability/infra-observability-status.cli-command.impl.js.map +1 -0
  95. package/dist/cli/observability/infra-observability.cli-group.descriptor.d.ts +12 -0
  96. package/dist/cli/observability/infra-observability.cli-group.descriptor.d.ts.map +1 -0
  97. package/dist/cli/observability/infra-observability.cli-group.descriptor.js +12 -0
  98. package/dist/cli/observability/infra-observability.cli-group.descriptor.js.map +1 -0
  99. package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.d.ts +27 -0
  100. package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.d.ts.map +1 -0
  101. package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.js +35 -0
  102. package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.js.map +1 -0
  103. package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.impl.d.ts +5 -0
  104. package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.impl.d.ts.map +1 -0
  105. package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.impl.js +99 -0
  106. package/dist/cli/regenerate/infra-deploy.regenerate.cli-command.impl.js.map +1 -0
  107. package/dist/cli/rum/infra-rum-status.cli-command.descriptor.d.ts +21 -0
  108. package/dist/cli/rum/infra-rum-status.cli-command.descriptor.d.ts.map +1 -0
  109. package/dist/cli/rum/infra-rum-status.cli-command.descriptor.js +27 -0
  110. package/dist/cli/rum/infra-rum-status.cli-command.descriptor.js.map +1 -0
  111. package/dist/cli/rum/infra-rum-status.cli-command.impl.d.ts +12 -0
  112. package/dist/cli/rum/infra-rum-status.cli-command.impl.d.ts.map +1 -0
  113. package/dist/cli/rum/infra-rum-status.cli-command.impl.js +88 -0
  114. package/dist/cli/rum/infra-rum-status.cli-command.impl.js.map +1 -0
  115. package/dist/cli/rum/infra-rum.cli-group.descriptor.d.ts +12 -0
  116. package/dist/cli/rum/infra-rum.cli-group.descriptor.d.ts.map +1 -0
  117. package/dist/cli/rum/infra-rum.cli-group.descriptor.js +12 -0
  118. package/dist/cli/rum/infra-rum.cli-group.descriptor.js.map +1 -0
  119. package/dist/cli/run/infra-deploy.run.cli-command.descriptor.d.ts +27 -0
  120. package/dist/cli/run/infra-deploy.run.cli-command.descriptor.d.ts.map +1 -0
  121. package/dist/cli/run/infra-deploy.run.cli-command.descriptor.js +49 -0
  122. package/dist/cli/run/infra-deploy.run.cli-command.descriptor.js.map +1 -0
  123. package/dist/cli/run/infra-deploy.run.cli-command.impl.d.ts +5 -0
  124. package/dist/cli/run/infra-deploy.run.cli-command.impl.d.ts.map +1 -0
  125. package/dist/cli/run/infra-deploy.run.cli-command.impl.js +272 -0
  126. package/dist/cli/run/infra-deploy.run.cli-command.impl.js.map +1 -0
  127. package/dist/cli/shared/discover-deployments.d.ts +41 -0
  128. package/dist/cli/shared/discover-deployments.d.ts.map +1 -0
  129. package/dist/cli/shared/discover-deployments.js +95 -0
  130. package/dist/cli/shared/discover-deployments.js.map +1 -0
  131. package/dist/config-loader.d.ts +24 -0
  132. package/dist/config-loader.d.ts.map +1 -0
  133. package/dist/config-loader.js +135 -0
  134. package/dist/config-loader.js.map +1 -0
  135. package/dist/index.d.ts +5 -0
  136. package/dist/index.d.ts.map +1 -0
  137. package/dist/index.js +4 -0
  138. package/dist/index.js.map +1 -0
  139. package/dist/infra-deploy.plugin.d.ts +3 -0
  140. package/dist/infra-deploy.plugin.d.ts.map +1 -0
  141. package/dist/infra-deploy.plugin.js +59 -0
  142. package/dist/infra-deploy.plugin.js.map +1 -0
  143. package/dist/regenerate.d.ts +55 -0
  144. package/dist/regenerate.d.ts.map +1 -0
  145. package/dist/regenerate.js +206 -0
  146. package/dist/regenerate.js.map +1 -0
  147. package/package.json +85 -0
  148. package/src/cli/alerts/infra-alerts-create.cli-command.descriptor.ts +61 -0
  149. package/src/cli/alerts/infra-alerts-create.cli-command.impl.ts +131 -0
  150. package/src/cli/alerts/infra-alerts-delete.cli-command.descriptor.ts +36 -0
  151. package/src/cli/alerts/infra-alerts-delete.cli-command.impl.ts +75 -0
  152. package/src/cli/alerts/infra-alerts-list.cli-command.descriptor.ts +27 -0
  153. package/src/cli/alerts/infra-alerts-list.cli-command.impl.ts +88 -0
  154. package/src/cli/alerts/infra-alerts.cli-group.descriptor.ts +12 -0
  155. package/src/cli/audit/infra-audit.cli-command.descriptor.ts +28 -0
  156. package/src/cli/audit/infra-audit.cli-command.impl.ts +293 -0
  157. package/src/cli/infra-deploy.cli-group.descriptor.ts +12 -0
  158. package/src/cli/infra.cli-group.descriptor.ts +11 -0
  159. package/src/cli/list/infra-deploy.list.cli-command.descriptor.ts +29 -0
  160. package/src/cli/list/infra-deploy.list.cli-command.impl.ts +125 -0
  161. package/src/cli/logs/infra-logs.cli-command.descriptor.ts +65 -0
  162. package/src/cli/logs/infra-logs.cli-command.impl.ts +354 -0
  163. package/src/cli/logs/stream-worker-tail.ts +202 -0
  164. package/src/cli/npm/infra-npm-publish.cli-command.descriptor.ts +75 -0
  165. package/src/cli/npm/infra-npm-publish.cli-command.impl.ts +474 -0
  166. package/src/cli/npm/infra-npm.cli-group.descriptor.ts +12 -0
  167. package/src/cli/observability/infra-observability-set.cli-command.descriptor.ts +57 -0
  168. package/src/cli/observability/infra-observability-set.cli-command.impl.ts +173 -0
  169. package/src/cli/observability/infra-observability-status.cli-command.descriptor.ts +35 -0
  170. package/src/cli/observability/infra-observability-status.cli-command.impl.ts +124 -0
  171. package/src/cli/observability/infra-observability.cli-group.descriptor.ts +12 -0
  172. package/src/cli/regenerate/infra-deploy.regenerate.cli-command.descriptor.ts +36 -0
  173. package/src/cli/regenerate/infra-deploy.regenerate.cli-command.impl.ts +103 -0
  174. package/src/cli/rum/infra-rum-status.cli-command.descriptor.ts +27 -0
  175. package/src/cli/rum/infra-rum-status.cli-command.impl.ts +112 -0
  176. package/src/cli/rum/infra-rum.cli-group.descriptor.ts +12 -0
  177. package/src/cli/run/infra-deploy.run.cli-command.descriptor.ts +49 -0
  178. package/src/cli/run/infra-deploy.run.cli-command.impl.ts +330 -0
  179. package/src/cli/shared/discover-deployments.ts +127 -0
  180. package/src/config-loader.ts +179 -0
  181. package/src/index.ts +11 -0
  182. package/src/infra-deploy.plugin.ts +83 -0
  183. package/src/regenerate.ts +230 -0
@@ -0,0 +1,354 @@
1
+ import { getVibesRuntime } from '@vibesdotdev/runtime';
2
+ import { requestCLIExit, type UIContext } from '@vibesdotdev/cli/providers';
3
+ import type { LogsImplementation, InfraLogsDescriptor } from '@vibesdotdev/infra-core';
4
+ import { discoverDeployments, type DiscoveredDeployment } from '../shared/discover-deployments.ts';
5
+ import { streamWorkerTail, type TailRecord } from './stream-worker-tail.ts';
6
+
7
+ /** Parse a duration suffix like "30s", "2m", "1h" → milliseconds. Falls back to 30s. */
8
+ function parseDurationMs(input: string | undefined): number {
9
+ if (!input) return 30_000;
10
+ const m = /^(\d+)\s*(ms|s|m|h)?$/i.exec(input.trim());
11
+ if (!m) return 30_000;
12
+ const n = Number.parseInt(m[1]!, 10);
13
+ const unit = (m[2] ?? 's').toLowerCase();
14
+ if (unit === 'ms') return n;
15
+ if (unit === 's') return n * 1000;
16
+ if (unit === 'm') return n * 60_000;
17
+ if (unit === 'h') return n * 3_600_000;
18
+ return 30_000;
19
+ }
20
+
21
+ function readString(v: unknown): string | undefined {
22
+ if (typeof v === 'string') return v.length > 0 ? v : undefined;
23
+ if (typeof v === 'number' && Number.isFinite(v)) return String(v);
24
+ return undefined;
25
+ }
26
+
27
+ function readBoolean(v: unknown): boolean {
28
+ return v === true || v === 'true';
29
+ }
30
+
31
+ function descriptorHasWorkerSource(descriptor: InfraLogsDescriptor, workerName: string): boolean {
32
+ return descriptor.sources?.some(
33
+ (s) => s.type === 'worker' && (s.name === workerName || descriptor.id === workerName)
34
+ ) ?? false;
35
+ }
36
+
37
+ /**
38
+ * Map an `infra/logs` descriptor adapter name to the deployment provider that
39
+ * emits workers for it. Extend as new providers register infra/logs adapters.
40
+ */
41
+ function adapterToProvider(adapter: string): string | undefined {
42
+ if (adapter === 'cloudflare-logs') return 'cloudflare-workers';
43
+ return undefined;
44
+ }
45
+
46
+ /**
47
+ * Build the `sources` list a logs descriptor should expose at display time
48
+ * by merging anything declared in the descriptor with workers discovered
49
+ * from the workspace's `deployment.config.ts` files. Sources are always
50
+ * de-duped by name.
51
+ */
52
+ function mergeDescriptorSources(
53
+ descriptor: InfraLogsDescriptor,
54
+ discovered: DiscoveredDeployment[]
55
+ ): InfraLogsDescriptor['sources'] {
56
+ const provider = adapterToProvider(descriptor.adapter);
57
+ const fromWorkspace = discovered
58
+ .filter((d) => !provider || d.provider === provider)
59
+ .filter((d) => Boolean(d.workerName))
60
+ .map((d) => ({ type: 'worker' as const, name: d.workerName! }));
61
+
62
+ const seen = new Set<string>();
63
+ const merged: NonNullable<InfraLogsDescriptor['sources']> = [];
64
+ for (const src of [...(descriptor.sources ?? []), ...fromWorkspace]) {
65
+ const key = `${src.type}:${src.name}`;
66
+ if (seen.has(key)) continue;
67
+ seen.add(key);
68
+ merged.push(src);
69
+ }
70
+ return merged;
71
+ }
72
+
73
+ export default {
74
+ async execute(args: Record<string, unknown>, opts: Record<string, unknown>): Promise<void> {
75
+ const runtime = await getVibesRuntime();
76
+ const ui = (await runtime.context('cli/ui')) as UIContext;
77
+
78
+ const sourceName = readString(args.source);
79
+ const workerName = readString(args.worker);
80
+ const output = readString(opts.output) ?? 'table';
81
+ const showAnalytics = readBoolean(opts.analytics);
82
+ const timeStart = readString(opts.timeStart) ?? '24h';
83
+ const doTail = readBoolean(opts.tail);
84
+ const status = readString(opts.status) as 'ok' | 'error' | 'canceled' | undefined;
85
+ const limitRaw = readString(opts.limit) ?? '100';
86
+ const limit = Number.parseInt(limitRaw, 10);
87
+ const windowRaw = readString(opts.window);
88
+ const windowMs = parseDurationMs(windowRaw);
89
+
90
+ // Get all infra/logs descriptors
91
+ const logsAssets = runtime.assets('infra/logs');
92
+ const rawDescriptors = logsAssets.descriptors() as InfraLogsDescriptor[];
93
+
94
+ if (rawDescriptors.length === 0) {
95
+ if (output === 'json') ui.json([]);
96
+ else ui.info('No log sources configured. Add an infra/logs descriptor to enable log querying.');
97
+ return;
98
+ }
99
+
100
+ // Augment each descriptor's sources by discovering workers from the
101
+ // workspace's deployment.config.ts files. Framework-registered
102
+ // descriptors carry no app-name list — that inventory lives in
103
+ // per-app deployment configs and is resolved here at call time.
104
+ const discovered = await discoverDeployments();
105
+ const descriptors: InfraLogsDescriptor[] = rawDescriptors.map((d) => ({
106
+ ...d,
107
+ sources: mergeDescriptorSources(d, discovered)
108
+ }));
109
+
110
+ // Resolve implementations for each descriptor
111
+ const implementations: Array<{
112
+ id: string;
113
+ descriptor: InfraLogsDescriptor;
114
+ implementation: LogsImplementation | null;
115
+ error?: string;
116
+ }> = [];
117
+
118
+ for (const descriptor of descriptors) {
119
+ try {
120
+ const resolved = await runtime.query('infra/logs').withId(descriptor.id).resolve();
121
+ const resolvedObj = resolved as unknown as {
122
+ id: string;
123
+ descriptor: InfraLogsDescriptor;
124
+ connector: unknown;
125
+ implementation?: LogsImplementation;
126
+ };
127
+
128
+ // The resolved object may have implementation directly or nested
129
+ const impl = resolvedObj.implementation ??
130
+ (resolvedObj as unknown as LogsImplementation);
131
+
132
+ implementations.push({
133
+ id: descriptor.id,
134
+ descriptor,
135
+ implementation: impl && typeof impl === 'object' && 'getAnalytics' in impl ? impl : null,
136
+ error: impl && typeof impl === 'object' && 'getAnalytics' in impl ? undefined : 'Implementation not available'
137
+ });
138
+ } catch (err) {
139
+ implementations.push({
140
+ id: descriptor.id,
141
+ descriptor,
142
+ implementation: null,
143
+ error: (err as Error).message
144
+ });
145
+ }
146
+ }
147
+
148
+ // If a specific source is requested, find it
149
+ if (sourceName) {
150
+ const implEntry = implementations.find((impl) =>
151
+ impl.id === sourceName ||
152
+ descriptorHasWorkerSource(impl.descriptor, sourceName)
153
+ );
154
+ if (!implEntry) {
155
+ ui.error(`Log source '${sourceName}' not found. Available sources: ${implementations.map((impl) => impl.id).join(', ')}`);
156
+ requestCLIExit(1);
157
+ }
158
+
159
+ const { implementation: logsImpl, descriptor, error } = implEntry;
160
+
161
+ if (!logsImpl) {
162
+ ui.error(`Cannot access log source '${sourceName}': ${error ?? 'Implementation not available'}`);
163
+ ui.info('Make sure credentials are configured in your secrets store or environment.');
164
+ return;
165
+ }
166
+
167
+ let targetWorker: string;
168
+ if (workerName) {
169
+ const specific = descriptor.sources?.find(s => s.type === 'worker' && s.name === workerName);
170
+ if (!specific) {
171
+ // Worker not in the discovered inventory — pass it through to
172
+ // the provider anyway so the user can target ad-hoc names
173
+ // (workers deployed outside this workspace, sandbox names,
174
+ // preview environments, etc.). The provider will return a
175
+ // clean error if the worker truly doesn't exist.
176
+ const known = descriptor.sources?.map((s) => s.name).join(', ') || '(none discovered)';
177
+ ui.info(`Worker '${workerName}' is not in the discovered inventory (known: ${known}). Passing through to the provider.`);
178
+ }
179
+ targetWorker = workerName;
180
+ } else {
181
+ const workerSource = descriptor.sources?.find(s => s.type === 'worker');
182
+ targetWorker = workerSource?.name ?? sourceName;
183
+ }
184
+
185
+ if (doTail) {
186
+ // Real-time log streaming
187
+ const tailResult = await logsImpl.tailLogs(targetWorker, { status, limit });
188
+ ui.info(`Log tail started: ${tailResult.tailId} (worker=${targetWorker})`);
189
+ await streamWorkerTail({
190
+ url: tailResult.websocketUrl,
191
+ ui,
192
+ filterStatus: status,
193
+ limit
194
+ });
195
+ try {
196
+ await logsImpl.stopTail(targetWorker, tailResult.tailId);
197
+ } catch {
198
+ // best-effort cleanup
199
+ }
200
+ } else if (showAnalytics) {
201
+ // Analytics summary
202
+ const analytics = await logsImpl.getAnalytics(targetWorker, { timeStart });
203
+ if (output === 'json') {
204
+ ui.json(analytics);
205
+ } else {
206
+ ui.info(`Analytics for ${targetWorker} (${timeStart}):`);
207
+ ui.table([
208
+ { metric: 'Total Invocations', value: analytics.totalInvocations.toString() },
209
+ { metric: 'Error Count', value: analytics.errorCount.toString() },
210
+ { metric: 'Error Rate', value: `${(analytics.errorRate * 100).toFixed(2)}%` },
211
+ { metric: 'Avg Duration', value: `${analytics.avgDurationMs.toFixed(2)}ms` },
212
+ { metric: 'P99 Duration', value: `${analytics.p99DurationMs.toFixed(2)}ms` },
213
+ { metric: 'Total CPU Time', value: `${analytics.totalCpuTimeMs}ms` }
214
+ ], { columns: ['metric', 'value'] });
215
+ }
216
+ } else if (workerName) {
217
+ // Windowed query: a worker was named without --tail/--analytics.
218
+ // Cloudflare exposes only live Tail for per-event data, so we
219
+ // open a tail session, capture for `--window` (default 30s),
220
+ // then close. This is the closest thing to a batch query.
221
+ const tailResult = await logsImpl.tailLogs(targetWorker, { status, limit });
222
+ if (output !== 'json') {
223
+ ui.info(
224
+ `Log query: ${targetWorker} (window=${windowRaw ?? '30s'}, status=${status ?? 'any'}, limit=${limit}). ` +
225
+ `Note: Cloudflare exposes only live Tail for per-event data — this captures errors during the window, not historical lookback.`
226
+ );
227
+ }
228
+ const records = await streamWorkerTail({
229
+ url: tailResult.websocketUrl,
230
+ ui,
231
+ filterStatus: status,
232
+ limit,
233
+ windowMs,
234
+ collect: true
235
+ });
236
+ try {
237
+ await logsImpl.stopTail(targetWorker, tailResult.tailId);
238
+ } catch {
239
+ // best-effort cleanup
240
+ }
241
+
242
+ const entries = records.map((rec: TailRecord) => ({
243
+ timestamp: rec.eventTimestamp ? new Date(rec.eventTimestamp).toISOString() : null,
244
+ outcome: rec.outcome ?? null,
245
+ scriptName: rec.scriptName ?? targetWorker,
246
+ method: rec.event?.request?.method ?? null,
247
+ url: rec.event?.request?.url ?? null,
248
+ responseStatus: rec.event?.response?.status ?? null,
249
+ trigger:
250
+ rec.event?.cron ? `cron ${rec.event.cron}` :
251
+ rec.event?.rpcMethod ? `rpc ${rec.event.rpcMethod}` :
252
+ rec.event?.request ? 'request' : 'event',
253
+ logs: (rec.logs ?? []).map((l) => ({
254
+ level: l.level ?? 'log',
255
+ message: (l.message ?? []).map((m) =>
256
+ typeof m === 'string' ? m : (() => { try { return JSON.stringify(m); } catch { return String(m); } })()
257
+ ).join(' '),
258
+ timestamp: l.timestamp ? new Date(l.timestamp).toISOString() : null
259
+ })),
260
+ exceptions: (rec.exceptions ?? []).map((e) => ({
261
+ name: e.name ?? 'Error',
262
+ message: e.message ?? '',
263
+ timestamp: e.timestamp ? new Date(e.timestamp).toISOString() : null
264
+ }))
265
+ }));
266
+
267
+ if (output === 'json') {
268
+ ui.json({
269
+ source: descriptor.id,
270
+ worker: targetWorker,
271
+ window: windowRaw ?? '30s',
272
+ status: status ?? null,
273
+ limit,
274
+ capturedAt: new Date().toISOString(),
275
+ count: entries.length,
276
+ entries
277
+ });
278
+ } else {
279
+ if (entries.length === 0) {
280
+ ui.info(`No log entries captured in the ${windowRaw ?? '30s'} window.`);
281
+ } else {
282
+ ui.info(`Captured ${entries.length} entries.`);
283
+ }
284
+ }
285
+ } else {
286
+ // No worker specified — show descriptor info.
287
+ if (output === 'json') {
288
+ ui.json({ descriptor });
289
+ } else {
290
+ ui.info(`Log source: ${descriptor.name ?? descriptor.id}`);
291
+ ui.info(`Adapter: ${descriptor.adapter}`);
292
+ ui.info(`Sources: ${descriptor.sources.length}`);
293
+ if (descriptor.sources.length > 0) {
294
+ ui.table(descriptor.sources, { columns: ['type', 'name', 'environment'] });
295
+ }
296
+ ui.info('');
297
+ ui.info(`To query a specific worker:`);
298
+ ui.info(` vibes infra logs ${descriptor.id} <worker> --status=error --window=30s --output=json`);
299
+ }
300
+ }
301
+ return;
302
+ }
303
+
304
+ // List all log sources
305
+ const rows: Array<{
306
+ id: string;
307
+ name: string;
308
+ adapter: string;
309
+ sources: string;
310
+ status: string;
311
+ }> = [];
312
+
313
+ for (const impl of implementations) {
314
+ const descriptor = impl.descriptor;
315
+ const sourceCount = descriptor.sources?.length ?? 0;
316
+
317
+ rows.push({
318
+ id: impl.id,
319
+ name: descriptor.name ?? impl.id,
320
+ adapter: descriptor.adapter,
321
+ sources: sourceCount.toString(),
322
+ status: impl.error ? 'error' : 'active'
323
+ });
324
+ }
325
+
326
+ if (output === 'json') {
327
+ ui.json(rows);
328
+ } else {
329
+ ui.info('Available log sources:');
330
+ ui.table(rows, { columns: ['id', 'name', 'adapter', 'sources', 'status'] });
331
+ ui.info('');
332
+ ui.info('Usage:');
333
+ ui.info(' vibes infra logs <source> # Show source details');
334
+ ui.info(' vibes infra logs <source> <worker> # Windowed query (30s capture)');
335
+ ui.info(' vibes infra logs <source> <worker> --analytics # Aggregate analytics');
336
+ ui.info(' vibes infra logs <source> <worker> --tail # Stream logs in real-time');
337
+ ui.info('');
338
+ ui.info('Examples:');
339
+ ui.info(' vibes infra logs cloudflare-logs vibes-auth --status=error --window=60s --output=json');
340
+ ui.info(' vibes infra logs cloudflare-logs vibes-auth --analytics --timeStart=2h');
341
+ ui.info(' vibes infra logs cloudflare-logs vibes-auth --tail --status=error');
342
+
343
+ // Show errors if any
344
+ const errors = implementations.filter(i => i.error);
345
+ if (errors.length > 0) {
346
+ ui.info('');
347
+ ui.info('Errors:');
348
+ for (const err of errors) {
349
+ ui.error(` ${err.id}: ${err.error}`);
350
+ }
351
+ }
352
+ }
353
+ }
354
+ };
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Minimal Cloudflare Worker tail WebSocket consumer.
3
+ *
4
+ * Connects to the WS URL returned by `POST /accounts/.../workers/scripts/.../tails`,
5
+ * sends the trace-v1 handshake, and prints each incoming log record as a
6
+ * formatted line to stdout. Exits cleanly on SIGINT.
7
+ *
8
+ * The server applies the filters supplied at tail-creation time, so we only
9
+ * format here — no client-side filtering beyond an optional `limit` for
10
+ * one-shot use.
11
+ */
12
+
13
+ import type { UIContext } from '@vibesdotdev/cli/providers';
14
+
15
+ const SUBPROTOCOL = 'trace-v1';
16
+
17
+ export interface TailRecord {
18
+ outcome?: string;
19
+ scriptName?: string;
20
+ eventTimestamp?: number;
21
+ event?: {
22
+ request?: { method?: string; url?: string };
23
+ response?: { status?: number };
24
+ rpcMethod?: string;
25
+ cron?: string;
26
+ };
27
+ logs?: Array<{ level?: string; message?: unknown[]; timestamp?: number }>;
28
+ exceptions?: Array<{ name?: string; message?: string; timestamp?: number }>;
29
+ diagnosticsChannelEvents?: unknown[];
30
+ }
31
+
32
+ function fmtTs(ts: number | undefined): string {
33
+ if (!ts) return new Date().toISOString();
34
+ return new Date(ts).toISOString();
35
+ }
36
+
37
+ function fmtArg(a: unknown): string {
38
+ if (a === null) return 'null';
39
+ if (typeof a === 'string') return a;
40
+ if (typeof a === 'number' || typeof a === 'boolean') return String(a);
41
+ try {
42
+ return JSON.stringify(a);
43
+ } catch {
44
+ return String(a);
45
+ }
46
+ }
47
+
48
+ function formatRecord(rec: TailRecord): string[] {
49
+ const lines: string[] = [];
50
+ const ts = fmtTs(rec.eventTimestamp);
51
+ const outcome = rec.outcome ?? 'unknown';
52
+ const req = rec.event?.request;
53
+ const res = rec.event?.response;
54
+ const trigger = req
55
+ ? `${req.method ?? 'GET'} ${req.url ?? ''}${res?.status ? ` → ${res.status}` : ''}`
56
+ : rec.event?.cron
57
+ ? `cron ${rec.event.cron}`
58
+ : rec.event?.rpcMethod
59
+ ? `rpc ${rec.event.rpcMethod}`
60
+ : 'event';
61
+
62
+ lines.push(`[${ts}] ${outcome.toUpperCase()} ${rec.scriptName ?? ''} ${trigger}`);
63
+
64
+ for (const log of rec.logs ?? []) {
65
+ const level = (log.level ?? 'log').toLowerCase();
66
+ const msg = (log.message ?? []).map(fmtArg).join(' ');
67
+ lines.push(` · ${level}: ${msg}`);
68
+ }
69
+ for (const ex of rec.exceptions ?? []) {
70
+ lines.push(` ✖ ${ex.name ?? 'Error'}: ${ex.message ?? ''}`);
71
+ }
72
+ return lines;
73
+ }
74
+
75
+ export interface StreamTailOptions {
76
+ url: string;
77
+ ui: UIContext;
78
+ filterStatus?: string;
79
+ /** Stop after this many records (0 / undefined = stream until SIGINT). */
80
+ limit?: number;
81
+ /**
82
+ * Stop after this many milliseconds, regardless of `limit`. Used by the
83
+ * windowed-query mode where the CLI wants a brief batch capture instead
84
+ * of an open-ended stream. When `windowMs` is set, the helper resolves
85
+ * cleanly at the deadline instead of waiting for SIGINT.
86
+ */
87
+ windowMs?: number;
88
+ /**
89
+ * If true, captured records are not printed and are returned via the
90
+ * resolved value instead. Used by windowed-query mode so the caller can
91
+ * format them (e.g. as a single JSON document).
92
+ */
93
+ collect?: boolean;
94
+ }
95
+
96
+ export type CapturedRecord = TailRecord;
97
+
98
+ export async function streamWorkerTail(
99
+ opts: StreamTailOptions
100
+ ): Promise<CapturedRecord[]> {
101
+ const { url, ui, limit, windowMs, collect } = opts;
102
+
103
+ return new Promise<CapturedRecord[]>((resolve, reject) => {
104
+ let ws: WebSocket;
105
+ try {
106
+ ws = new WebSocket(url, [SUBPROTOCOL]);
107
+ } catch (err) {
108
+ reject(err);
109
+ return;
110
+ }
111
+
112
+ let received = 0;
113
+ let closed = false;
114
+ const captured: CapturedRecord[] = [];
115
+ let windowTimer: ReturnType<typeof setTimeout> | undefined;
116
+ const finish = (err?: Error) => {
117
+ if (closed) return;
118
+ closed = true;
119
+ if (windowTimer) clearTimeout(windowTimer);
120
+ try {
121
+ ws.close();
122
+ } catch {
123
+ /* ignore */
124
+ }
125
+ process.off('SIGINT', onSigint);
126
+ if (err) reject(err);
127
+ else resolve(captured);
128
+ };
129
+
130
+ const onSigint = () => {
131
+ ui.info('Tail stopped.');
132
+ finish();
133
+ };
134
+ process.on('SIGINT', onSigint);
135
+
136
+ ws.addEventListener('open', () => {
137
+ if (!collect) {
138
+ ui.info(
139
+ windowMs
140
+ ? `Capturing logs for ${Math.round(windowMs / 1000)}s — press Ctrl+C to stop early.`
141
+ : 'Streaming logs — press Ctrl+C to stop.'
142
+ );
143
+ }
144
+ try {
145
+ ws.send(JSON.stringify({ debug: false }));
146
+ } catch (err) {
147
+ finish(err instanceof Error ? err : new Error(String(err)));
148
+ return;
149
+ }
150
+ if (windowMs && windowMs > 0) {
151
+ windowTimer = setTimeout(() => {
152
+ if (!collect) ui.info(`Capture window (${Math.round(windowMs / 1000)}s) elapsed.`);
153
+ finish();
154
+ }, windowMs);
155
+ }
156
+ });
157
+
158
+ ws.addEventListener('message', (ev: MessageEvent) => {
159
+ let data: string;
160
+ if (typeof ev.data === 'string') {
161
+ data = ev.data;
162
+ } else if (ev.data instanceof ArrayBuffer) {
163
+ data = new TextDecoder().decode(ev.data);
164
+ } else if (ev.data instanceof Uint8Array) {
165
+ data = new TextDecoder().decode(ev.data);
166
+ } else {
167
+ data = String(ev.data);
168
+ }
169
+ let rec: TailRecord;
170
+ try {
171
+ rec = JSON.parse(data) as TailRecord;
172
+ } catch {
173
+ if (!collect) process.stdout.write(`${data}\n`);
174
+ return;
175
+ }
176
+ if (collect) {
177
+ captured.push(rec);
178
+ } else {
179
+ for (const line of formatRecord(rec)) {
180
+ process.stdout.write(`${line}\n`);
181
+ }
182
+ }
183
+ received += 1;
184
+ if (limit && received >= limit) {
185
+ if (!collect) ui.info(`Reached limit of ${limit} records.`);
186
+ finish();
187
+ }
188
+ });
189
+
190
+ ws.addEventListener('error', (ev: Event) => {
191
+ const msg = (ev as ErrorEvent).message ?? 'tail websocket error';
192
+ finish(new Error(msg));
193
+ });
194
+
195
+ ws.addEventListener('close', (ev: CloseEvent) => {
196
+ if (!closed && !collect) {
197
+ ui.info(`Tail closed (code=${ev.code}${ev.reason ? `, reason=${ev.reason}` : ''}).`);
198
+ }
199
+ finish();
200
+ });
201
+ });
202
+ }
@@ -0,0 +1,75 @@
1
+ import { createRuntimeAsset } from '@vibesdotdev/runtime';
2
+
3
+ export default createRuntimeAsset({
4
+ id: 'infra-npm.publish',
5
+ kind: 'cli/command',
6
+ name: 'publish',
7
+ description: 'Publish npm packages using a token resolved from vibes secrets',
8
+ group: 'infra-npm',
9
+ arguments: [
10
+ {
11
+ name: 'paths',
12
+ description: 'Package directories to publish (defaults to cwd)',
13
+ required: false,
14
+ variadic: true
15
+ }
16
+ ],
17
+ options: [
18
+ {
19
+ flags: '--environment <name>',
20
+ description: 'Vibes secrets environment to read NPM_TOKEN from'
21
+ },
22
+ {
23
+ flags: '--backend <id>',
24
+ description: 'Specific secrets backend id to read from'
25
+ },
26
+ {
27
+ flags: '--token-key <key>',
28
+ description: 'Secret key containing the npm token',
29
+ default: 'NPM_TOKEN'
30
+ },
31
+ {
32
+ flags: '--registry <url>',
33
+ description: 'npm registry URL',
34
+ default: 'https://registry.npmjs.org'
35
+ },
36
+ {
37
+ flags: '--scope <scope>',
38
+ description: 'Scoped package registry override to write into the temporary npmrc',
39
+ default: '@vibesdotdev'
40
+ },
41
+ {
42
+ flags: '--access <access>',
43
+ description: 'npm access level override (public|restricted)'
44
+ },
45
+ {
46
+ flags: '--tag <tag>',
47
+ description: 'npm dist-tag',
48
+ default: 'latest'
49
+ },
50
+ {
51
+ flags: '--otp <code>',
52
+ description: 'One-time password for npm accounts that require publish-time 2FA'
53
+ },
54
+ {
55
+ flags: '--provenance',
56
+ description: 'Pass --provenance to npm publish'
57
+ },
58
+ {
59
+ flags: '--dry-run',
60
+ description: 'Pack and run npm publish --dry-run without requiring an npm token'
61
+ },
62
+ {
63
+ flags: '--yes',
64
+ description: 'Actually publish. Required unless --dry-run is set'
65
+ },
66
+ {
67
+ flags: '-o, --output <format>',
68
+ description: 'Output format (text|json)',
69
+ default: 'text'
70
+ }
71
+ ],
72
+ surfaces: ['cli'],
73
+ hardware: ['consumer', 'cloud'],
74
+ enabled: true
75
+ });