@tinybirdco/sdk 0.0.20 → 0.0.21

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 (39) hide show
  1. package/dist/api/dashboard.d.ts +74 -0
  2. package/dist/api/dashboard.d.ts.map +1 -0
  3. package/dist/api/dashboard.js +102 -0
  4. package/dist/api/dashboard.js.map +1 -0
  5. package/dist/api/dashboard.test.d.ts +2 -0
  6. package/dist/api/dashboard.test.d.ts.map +1 -0
  7. package/dist/api/dashboard.test.js +91 -0
  8. package/dist/api/dashboard.test.js.map +1 -0
  9. package/dist/cli/commands/branch.d.ts +2 -0
  10. package/dist/cli/commands/branch.d.ts.map +1 -1
  11. package/dist/cli/commands/branch.js +12 -1
  12. package/dist/cli/commands/branch.js.map +1 -1
  13. package/dist/cli/commands/build.d.ts +17 -0
  14. package/dist/cli/commands/build.d.ts.map +1 -1
  15. package/dist/cli/commands/build.js +25 -0
  16. package/dist/cli/commands/build.js.map +1 -1
  17. package/dist/cli/commands/dev.d.ts +2 -0
  18. package/dist/cli/commands/dev.d.ts.map +1 -1
  19. package/dist/cli/commands/dev.js +19 -3
  20. package/dist/cli/commands/dev.js.map +1 -1
  21. package/dist/cli/index.js +23 -24
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/cli/output.d.ts +20 -0
  24. package/dist/cli/output.d.ts.map +1 -1
  25. package/dist/cli/output.js +37 -0
  26. package/dist/cli/output.js.map +1 -1
  27. package/dist/index.d.ts +2 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +2 -0
  30. package/dist/index.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/api/dashboard.test.ts +115 -0
  33. package/src/api/dashboard.ts +121 -0
  34. package/src/cli/commands/branch.ts +15 -1
  35. package/src/cli/commands/build.ts +46 -0
  36. package/src/cli/commands/dev.ts +47 -7
  37. package/src/cli/index.ts +25 -27
  38. package/src/cli/output.ts +54 -0
  39. package/src/index.ts +9 -0
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Tinybird Dashboard URL utilities
3
+ *
4
+ * Generates dashboard links for workspaces and branches
5
+ */
6
+
7
+ /**
8
+ * Region information extracted from API URL
9
+ */
10
+ export interface RegionInfo {
11
+ /** Cloud provider: "gcp" or "aws" */
12
+ provider: string;
13
+ /** Region identifier (e.g., "europe-west3", "us-east4", "us-west-2") */
14
+ region: string;
15
+ }
16
+
17
+ /**
18
+ * Mapping of API hostnames to region information
19
+ *
20
+ * Based on https://www.tinybird.co/docs/api-reference#current-tinybird-regions
21
+ */
22
+ const API_REGION_MAP: Record<string, RegionInfo> = {
23
+ // GCP Regions
24
+ "api.tinybird.co": { provider: "gcp", region: "europe-west3" },
25
+ "api.us-east.tinybird.co": { provider: "gcp", region: "us-east4" },
26
+ // AWS Regions
27
+ "api.eu-central-1.aws.tinybird.co": { provider: "aws", region: "eu-central-1" },
28
+ "api.us-east-1.aws.tinybird.co": { provider: "aws", region: "us-east-1" },
29
+ "api.us-west-2.aws.tinybird.co": { provider: "aws", region: "us-west-2" },
30
+ };
31
+
32
+ /**
33
+ * Parse an API URL to extract region information
34
+ *
35
+ * @param apiUrl - The Tinybird API base URL (e.g., "https://api.tinybird.co")
36
+ * @returns Region info or null if the URL doesn't match a known region
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * parseApiUrl("https://api.tinybird.co")
41
+ * // => { provider: "gcp", region: "europe-west3" }
42
+ *
43
+ * parseApiUrl("https://api.us-west-2.aws.tinybird.co")
44
+ * // => { provider: "aws", region: "us-west-2" }
45
+ * ```
46
+ */
47
+ export function parseApiUrl(apiUrl: string): RegionInfo | null {
48
+ try {
49
+ const url = new URL(apiUrl);
50
+ const hostname = url.hostname;
51
+ return API_REGION_MAP[hostname] ?? null;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Generate a Tinybird dashboard URL for a workspace
59
+ *
60
+ * @param apiUrl - The Tinybird API base URL
61
+ * @param workspaceName - The workspace name
62
+ * @returns Dashboard URL or null if the API URL is not recognized
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * getDashboardUrl("https://api.tinybird.co", "my_workspace")
67
+ * // => "https://cloud.tinybird.co/gcp/europe-west3/my_workspace"
68
+ * ```
69
+ */
70
+ export function getDashboardUrl(apiUrl: string, workspaceName: string): string | null {
71
+ const regionInfo = parseApiUrl(apiUrl);
72
+ if (!regionInfo) {
73
+ return null;
74
+ }
75
+
76
+ return `https://cloud.tinybird.co/${regionInfo.provider}/${regionInfo.region}/${workspaceName}`;
77
+ }
78
+
79
+ /**
80
+ * Generate a Tinybird dashboard URL for a branch
81
+ *
82
+ * @param apiUrl - The Tinybird API base URL
83
+ * @param workspaceName - The workspace name
84
+ * @param branchName - The branch name
85
+ * @returns Dashboard URL or null if the API URL is not recognized
86
+ *
87
+ * @example
88
+ * ```ts
89
+ * getBranchDashboardUrl("https://api.tinybird.co", "my_workspace", "feature_branch")
90
+ * // => "https://cloud.tinybird.co/gcp/europe-west3/my_workspace~feature_branch"
91
+ * ```
92
+ */
93
+ export function getBranchDashboardUrl(
94
+ apiUrl: string,
95
+ workspaceName: string,
96
+ branchName: string
97
+ ): string | null {
98
+ const regionInfo = parseApiUrl(apiUrl);
99
+ if (!regionInfo) {
100
+ return null;
101
+ }
102
+
103
+ return `https://cloud.tinybird.co/${regionInfo.provider}/${regionInfo.region}/${workspaceName}~${branchName}`;
104
+ }
105
+
106
+ /**
107
+ * Generate a local Tinybird dashboard URL
108
+ *
109
+ * @param workspaceName - The local workspace name
110
+ * @param port - The local Tinybird port (default: 7181)
111
+ * @returns Local dashboard URL
112
+ *
113
+ * @example
114
+ * ```ts
115
+ * getLocalDashboardUrl("my_local_workspace")
116
+ * // => "https://cloud.tinybird.co/local/7181/my_local_workspace"
117
+ * ```
118
+ */
119
+ export function getLocalDashboardUrl(workspaceName: string, port = 7181): string {
120
+ return `https://cloud.tinybird.co/local/${port}/${workspaceName}`;
121
+ }
@@ -16,6 +16,7 @@ import {
16
16
  removeBranch as removeCachedBranch,
17
17
  listCachedBranches,
18
18
  } from "../branch-store.js";
19
+ import { getBranchDashboardUrl } from "../../api/dashboard.js";
19
20
 
20
21
  /**
21
22
  * Branch command options
@@ -53,6 +54,8 @@ export interface BranchStatusResult {
53
54
  tinybirdBranch?: TinybirdBranch;
54
55
  /** Whether a cached token exists */
55
56
  hasCachedToken: boolean;
57
+ /** Dashboard URL for the branch */
58
+ dashboardUrl?: string;
56
59
  /** Error message if failed */
57
60
  error?: string;
58
61
  }
@@ -135,14 +138,16 @@ export async function runBranchStatus(
135
138
  const tinybirdBranchName = config.tinybirdBranch; // Sanitized name
136
139
  const isMainBranch = config.isMainBranch;
137
140
 
138
- // Fetch the workspace ID from the API
141
+ // Fetch the workspace from the API
139
142
  let workspaceId: string;
143
+ let workspaceName: string;
140
144
  try {
141
145
  const workspace = await getWorkspace({
142
146
  baseUrl: config.baseUrl,
143
147
  token: config.token,
144
148
  });
145
149
  workspaceId = workspace.id;
150
+ workspaceName = workspace.name;
146
151
  } catch (error) {
147
152
  return {
148
153
  success: false,
@@ -154,6 +159,11 @@ export async function runBranchStatus(
154
159
  };
155
160
  }
156
161
 
162
+ // Generate dashboard URL for the branch
163
+ const dashboardUrl = tinybirdBranchName
164
+ ? getBranchDashboardUrl(config.baseUrl, workspaceName, tinybirdBranchName) ?? undefined
165
+ : undefined;
166
+
157
167
  // Check for cached token (use sanitized name)
158
168
  const cachedBranch = tinybirdBranchName ? getBranchToken(workspaceId, tinybirdBranchName) : null;
159
169
  const hasCachedToken = cachedBranch !== null;
@@ -166,6 +176,7 @@ export async function runBranchStatus(
166
176
  tinybirdBranchName,
167
177
  isMainBranch,
168
178
  hasCachedToken,
179
+ dashboardUrl,
169
180
  };
170
181
  }
171
182
 
@@ -186,6 +197,7 @@ export async function runBranchStatus(
186
197
  isMainBranch,
187
198
  tinybirdBranch,
188
199
  hasCachedToken,
200
+ dashboardUrl,
189
201
  };
190
202
  } catch (error) {
191
203
  // If 404, branch doesn't exist yet
@@ -196,6 +208,7 @@ export async function runBranchStatus(
196
208
  tinybirdBranchName,
197
209
  isMainBranch,
198
210
  hasCachedToken,
211
+ dashboardUrl,
199
212
  };
200
213
  }
201
214
 
@@ -205,6 +218,7 @@ export async function runBranchStatus(
205
218
  tinybirdBranchName,
206
219
  isMainBranch,
207
220
  hasCachedToken,
221
+ dashboardUrl,
208
222
  error: (error as Error).message,
209
223
  };
210
224
  }
@@ -12,6 +12,8 @@ import {
12
12
  getLocalWorkspaceName,
13
13
  LocalNotRunningError,
14
14
  } from "../../api/local.js";
15
+ import { getWorkspace } from "../../api/workspaces.js";
16
+ import { getBranchDashboardUrl, getLocalDashboardUrl } from "../../api/dashboard.js";
15
17
 
16
18
  /**
17
19
  * Build command options
@@ -27,6 +29,22 @@ export interface BuildCommandOptions {
27
29
  devModeOverride?: DevMode;
28
30
  }
29
31
 
32
+ /**
33
+ * Branch info included in build result
34
+ */
35
+ export interface BuildBranchInfo {
36
+ /** Git branch name */
37
+ gitBranch: string | null;
38
+ /** Tinybird branch name */
39
+ tinybirdBranch: string | null;
40
+ /** Whether the branch was newly created */
41
+ wasCreated: boolean;
42
+ /** Dashboard URL for the branch */
43
+ dashboardUrl?: string;
44
+ /** Whether using local mode */
45
+ isLocal: boolean;
46
+ }
47
+
30
48
  /**
31
49
  * Build command result
32
50
  */
@@ -37,6 +55,8 @@ export interface BuildCommandResult {
37
55
  build?: BuildFromIncludeResult;
38
56
  /** Build API result (if not dry run) */
39
57
  deploy?: BuildApiResult;
58
+ /** Branch info (when building to a branch) */
59
+ branchInfo?: BuildBranchInfo;
40
60
  /** Error message if failed */
41
61
  error?: string;
42
62
  /** Duration in milliseconds */
@@ -101,6 +121,7 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
101
121
  }
102
122
 
103
123
  let deployResult: BuildApiResult;
124
+ let branchInfo: BuildBranchInfo | undefined;
104
125
 
105
126
  // Handle local mode
106
127
  if (devMode === "local") {
@@ -123,6 +144,14 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
123
144
  console.log(`[debug] Workspace ${wasCreated ? "created" : "found"}: ${workspace.name}`);
124
145
  }
125
146
 
147
+ branchInfo = {
148
+ gitBranch: config.gitBranch,
149
+ tinybirdBranch: workspaceName,
150
+ wasCreated,
151
+ dashboardUrl: getLocalDashboardUrl(workspaceName),
152
+ isLocal: true,
153
+ };
154
+
126
155
  // Always use /v1/build for local (no deploy endpoint)
127
156
  deployResult = await buildToTinybird(
128
157
  {
@@ -197,6 +226,21 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
197
226
  if (debug) {
198
227
  console.log(`[debug] Using branch token for branch: ${config.tinybirdBranch}`);
199
228
  }
229
+
230
+ // Get workspace name for dashboard URL
231
+ const workspace = await getWorkspace({
232
+ baseUrl: config.baseUrl,
233
+ token: config.token,
234
+ });
235
+ const dashboardUrl = getBranchDashboardUrl(config.baseUrl, workspace.name, config.tinybirdBranch!) ?? undefined;
236
+
237
+ branchInfo = {
238
+ gitBranch: config.gitBranch,
239
+ tinybirdBranch: config.tinybirdBranch,
240
+ wasCreated: tinybirdBranch.wasCreated ?? false,
241
+ dashboardUrl,
242
+ isLocal: false,
243
+ };
200
244
  } catch (error) {
201
245
  return {
202
246
  success: false,
@@ -231,6 +275,7 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
231
275
  success: false,
232
276
  build: buildResult,
233
277
  deploy: deployResult,
278
+ branchInfo,
234
279
  error: deployResult.error,
235
280
  durationMs: Date.now() - startTime,
236
281
  };
@@ -240,6 +285,7 @@ export async function runBuild(options: BuildCommandOptions = {}): Promise<Build
240
285
  success: true,
241
286
  build: buildResult,
242
287
  deploy: deployResult,
288
+ branchInfo,
243
289
  durationMs: Date.now() - startTime,
244
290
  };
245
291
  }
@@ -4,7 +4,16 @@
4
4
 
5
5
  import * as path from "path";
6
6
  import { watch } from "chokidar";
7
- import { loadConfig, configExists, findConfigFile, hasValidToken, updateConfig, LOCAL_BASE_URL, type ResolvedConfig, type DevMode } from "../config.js";
7
+ import {
8
+ loadConfig,
9
+ configExists,
10
+ findConfigFile,
11
+ hasValidToken,
12
+ updateConfig,
13
+ LOCAL_BASE_URL,
14
+ type ResolvedConfig,
15
+ type DevMode,
16
+ } from "../config.js";
8
17
  import { runBuild, type BuildCommandResult } from "./build.js";
9
18
  import { getOrCreateBranch, type TinybirdBranch } from "../../api/branches.js";
10
19
  import { browserLogin } from "../auth.js";
@@ -19,6 +28,8 @@ import {
19
28
  getLocalWorkspaceName,
20
29
  type LocalWorkspace,
21
30
  } from "../../api/local.js";
31
+ import { getWorkspace } from "../../api/workspaces.js";
32
+ import { getBranchDashboardUrl, getLocalDashboardUrl } from "../../api/dashboard.js";
22
33
 
23
34
  /**
24
35
  * Login result info
@@ -70,6 +81,8 @@ export interface BranchReadyInfo {
70
81
  isLocal?: boolean;
71
82
  /** Local workspace info (only in local mode) */
72
83
  localWorkspace?: LocalWorkspace;
84
+ /** Dashboard URL for the branch (only in branch mode) */
85
+ dashboardUrl?: string;
73
86
  }
74
87
 
75
88
  /**
@@ -99,7 +112,9 @@ export interface DevController {
99
112
  * @param options - Dev options
100
113
  * @returns Dev controller
101
114
  */
102
- export async function runDev(options: DevCommandOptions = {}): Promise<DevController> {
115
+ export async function runDev(
116
+ options: DevCommandOptions = {}
117
+ ): Promise<DevController> {
103
118
  const cwd = options.cwd ?? process.cwd();
104
119
  const debounceMs = options.debounce ?? 100;
105
120
 
@@ -129,7 +144,8 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
129
144
 
130
145
  if (!authResult.success || !authResult.token) {
131
146
  throw new Error(
132
- authResult.error ?? "Login failed. Run 'npx tinybird login' to authenticate."
147
+ authResult.error ??
148
+ "Login failed. Run 'npx tinybird login' to authenticate."
133
149
  );
134
150
  }
135
151
 
@@ -173,8 +189,14 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
173
189
  if (devMode === "local") {
174
190
  // Local mode: get tokens from local container and set up workspace
175
191
  const localTokens = await getLocalTokens();
176
- const workspaceName = getLocalWorkspaceName(config.tinybirdBranch, config.cwd);
177
- const { workspace, wasCreated } = await getOrCreateLocalWorkspace(localTokens, workspaceName);
192
+ const workspaceName = getLocalWorkspaceName(
193
+ config.tinybirdBranch,
194
+ config.cwd
195
+ );
196
+ const { workspace, wasCreated } = await getOrCreateLocalWorkspace(
197
+ localTokens,
198
+ workspaceName
199
+ );
178
200
 
179
201
  effectiveToken = workspace.token;
180
202
  effectiveBaseUrl = LOCAL_BASE_URL;
@@ -184,6 +206,7 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
184
206
  isLocal: true,
185
207
  localWorkspace: workspace,
186
208
  wasCreated,
209
+ dashboardUrl: getLocalDashboardUrl(workspace.name),
187
210
  };
188
211
  } else {
189
212
  // Branch mode: use Tinybird cloud with branches
@@ -216,11 +239,22 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
216
239
  }
217
240
 
218
241
  effectiveToken = tinybirdBranch.token;
242
+
243
+ // Get workspace name for dashboard URL
244
+ const workspace = await getWorkspace({
245
+ baseUrl: config.baseUrl,
246
+ token: config.token,
247
+ });
248
+ const dashboardUrl =
249
+ getBranchDashboardUrl(config.baseUrl, workspace.name, branchName) ??
250
+ undefined;
251
+
219
252
  branchInfo = {
220
253
  gitBranch: config.gitBranch, // Original git branch name for display
221
254
  isMainBranch: false,
222
255
  tinybirdBranch,
223
256
  wasCreated: tinybirdBranch.wasCreated ?? false,
257
+ dashboardUrl,
224
258
  };
225
259
  }
226
260
  }
@@ -246,7 +280,11 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
246
280
  async function doBuild(): Promise<BuildCommandResult> {
247
281
  if (isBuilding) {
248
282
  pendingBuild = true;
249
- return { success: false, error: "Build already in progress", durationMs: 0 };
283
+ return {
284
+ success: false,
285
+ error: "Build already in progress",
286
+ durationMs: 0,
287
+ };
250
288
  }
251
289
 
252
290
  isBuilding = true;
@@ -357,7 +395,9 @@ export async function runDev(options: DevCommandOptions = {}): Promise<DevContro
357
395
  });
358
396
 
359
397
  watcher.on("error", (error: unknown) => {
360
- options.onError?.(error instanceof Error ? error : new Error(String(error)));
398
+ options.onError?.(
399
+ error instanceof Error ? error : new Error(String(error))
400
+ );
361
401
  });
362
402
 
363
403
  // Do initial build
package/src/cli/index.ts CHANGED
@@ -188,15 +188,19 @@ function createCli(): Command {
188
188
  devModeOverride = "local";
189
189
  }
190
190
 
191
- const modeLabel = devModeOverride === "local" ? " (local)" : "";
192
- output.highlight(`Building${modeLabel}...`);
193
-
194
191
  const result = await runBuild({
195
192
  dryRun: options.dryRun,
196
193
  devModeOverride,
197
194
  });
198
195
 
199
- const { build, deploy } = result;
196
+ const { build, deploy, branchInfo } = result;
197
+
198
+ // Show branch info
199
+ if (branchInfo) {
200
+ output.showBranchInfo(branchInfo);
201
+ }
202
+
203
+ output.highlight("Building...");
200
204
 
201
205
  if (!result.success) {
202
206
  // Show detailed errors if available
@@ -486,32 +490,23 @@ function createCli(): Command {
486
490
  },
487
491
  onBranchReady: (info) => {
488
492
  if (info.isLocal) {
489
- // Local mode
490
- const workspaceName = info.localWorkspace?.name ?? "unknown";
491
- if (info.wasCreated) {
492
- console.log(`Using local Tinybird container`);
493
- console.log(`Creating local workspace '${workspaceName}'...`);
494
- console.log("Workspace created.\n");
495
- } else {
496
- console.log(`Using local Tinybird container`);
497
- console.log(
498
- `Using existing local workspace '${workspaceName}'\n`
499
- );
500
- }
493
+ output.showBranchInfo({
494
+ gitBranch: info.gitBranch,
495
+ tinybirdBranch: info.localWorkspace?.name ?? null,
496
+ wasCreated: info.wasCreated ?? false,
497
+ dashboardUrl: info.dashboardUrl,
498
+ isLocal: true,
499
+ });
501
500
  } else if (info.isMainBranch) {
502
501
  console.log("On main branch - deploying to workspace\n");
503
502
  } else if (info.gitBranch) {
504
- const tinybirdName = info.tinybirdBranch?.name ?? info.gitBranch;
505
- if (info.wasCreated) {
506
- console.log(`Detected git branch: ${info.gitBranch}`);
507
- console.log(`Creating Tinybird branch '${tinybirdName}'...`);
508
- console.log("Branch created and token cached.\n");
509
- } else {
510
- console.log(`Detected git branch: ${info.gitBranch}`);
511
- console.log(
512
- `Using existing Tinybird branch '${tinybirdName}'\n`
513
- );
514
- }
503
+ output.showBranchInfo({
504
+ gitBranch: info.gitBranch,
505
+ tinybirdBranch: info.tinybirdBranch?.name ?? null,
506
+ wasCreated: info.wasCreated ?? false,
507
+ dashboardUrl: info.dashboardUrl,
508
+ isLocal: false,
509
+ });
515
510
  } else {
516
511
  console.log("Not in a git repository - deploying to workspace\n");
517
512
  }
@@ -659,6 +654,9 @@ function createCli(): Command {
659
654
  console.log(` Tinybird branch: ${result.tinybirdBranch.name}`);
660
655
  console.log(` Branch ID: ${result.tinybirdBranch.id}`);
661
656
  console.log(` Created: ${result.tinybirdBranch.created_at}`);
657
+ if (result.dashboardUrl) {
658
+ console.log(` Dashboard: ${result.dashboardUrl}`);
659
+ }
662
660
  } else if (!result.isMainBranch && result.tinybirdBranchName) {
663
661
  console.log(" Tinybird branch: not created yet");
664
662
  console.log(" (Run 'npx tinybird dev' to create it)");
package/src/cli/output.ts CHANGED
@@ -239,6 +239,59 @@ export function showDeployFailure(): void {
239
239
  error(`\n✗ Deploy failed`);
240
240
  }
241
241
 
242
+ /**
243
+ * Branch info for display
244
+ */
245
+ export interface BranchDisplayInfo {
246
+ /** Git branch name */
247
+ gitBranch: string | null;
248
+ /** Tinybird branch name */
249
+ tinybirdBranch: string | null;
250
+ /** Whether the branch was newly created */
251
+ wasCreated: boolean;
252
+ /** Dashboard URL for the branch */
253
+ dashboardUrl?: string;
254
+ /** Whether using local mode */
255
+ isLocal?: boolean;
256
+ }
257
+
258
+ /**
259
+ * Show branch information in a compact, styled format
260
+ */
261
+ export function showBranchInfo(info: BranchDisplayInfo): void {
262
+ const status = info.wasCreated
263
+ ? colorize("✓ created", "green")
264
+ : colorize("existing", "gray");
265
+
266
+ if (info.isLocal) {
267
+ // Show git branch
268
+ if (info.gitBranch) {
269
+ console.log(`» Git branch: ${info.gitBranch}`);
270
+ }
271
+ // Show local workspace
272
+ const name = info.tinybirdBranch ?? "unknown";
273
+ console.log(`» Local workspace: ${name} ${status}`);
274
+ // Show dashboard URL
275
+ if (info.dashboardUrl) {
276
+ console.log(colorize(` ↳ ${info.dashboardUrl}`, "gray"));
277
+ }
278
+ } else {
279
+ // Show git branch
280
+ if (info.gitBranch) {
281
+ console.log(`» Git branch: ${info.gitBranch}`);
282
+ }
283
+ // Show Tinybird branch
284
+ if (info.tinybirdBranch) {
285
+ console.log(`» Tinybird branch: ${info.tinybirdBranch} ${status}`);
286
+ }
287
+ // Show dashboard URL
288
+ if (info.dashboardUrl) {
289
+ console.log(colorize(` ↳ ${info.dashboardUrl}`, "gray"));
290
+ }
291
+ }
292
+ console.log("");
293
+ }
294
+
242
295
  /**
243
296
  * Output object containing all output functions
244
297
  */
@@ -265,4 +318,5 @@ export const output = {
265
318
  showValidatingDeployment,
266
319
  showDeploySuccess,
267
320
  showDeployFailure,
321
+ showBranchInfo,
268
322
  };
package/src/index.ts CHANGED
@@ -235,3 +235,12 @@ export {
235
235
  resolveToken,
236
236
  clearTokenCache,
237
237
  } from "./client/preview.js";
238
+
239
+ // ============ Dashboard URL Utilities ============
240
+ export {
241
+ parseApiUrl,
242
+ getDashboardUrl,
243
+ getBranchDashboardUrl,
244
+ getLocalDashboardUrl,
245
+ } from "./api/dashboard.js";
246
+ export type { RegionInfo } from "./api/dashboard.js";