@tinybirdco/sdk 0.0.28 → 0.0.30
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/README.md +15 -0
- package/dist/cli/commands/info.d.ts +104 -0
- package/dist/cli/commands/info.d.ts.map +1 -0
- package/dist/cli/commands/info.js +140 -0
- package/dist/cli/commands/info.js.map +1 -0
- package/dist/cli/commands/info.test.d.ts +2 -0
- package/dist/cli/commands/info.test.d.ts.map +1 -0
- package/dist/cli/commands/info.test.js +338 -0
- package/dist/cli/commands/info.test.js.map +1 -0
- package/dist/cli/commands/open-dashboard.d.ts +39 -0
- package/dist/cli/commands/open-dashboard.d.ts.map +1 -0
- package/dist/cli/commands/open-dashboard.js +127 -0
- package/dist/cli/commands/open-dashboard.js.map +1 -0
- package/dist/cli/commands/open-dashboard.test.d.ts +2 -0
- package/dist/cli/commands/open-dashboard.test.d.ts.map +1 -0
- package/dist/cli/commands/open-dashboard.test.js +373 -0
- package/dist/cli/commands/open-dashboard.test.js.map +1 -0
- package/dist/cli/index.js +60 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/output.d.ts +40 -0
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +74 -0
- package/dist/cli/output.js.map +1 -1
- package/package.json +21 -1
- package/src/cli/commands/info.test.ts +400 -0
- package/src/cli/commands/info.ts +253 -0
- package/src/cli/commands/open-dashboard.test.ts +472 -0
- package/src/cli/commands/open-dashboard.ts +177 -0
- package/src/cli/index.ts +70 -0
- package/src/cli/output.ts +111 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from "vitest";
|
|
2
|
+
import { runInfo } from "./info.js";
|
|
3
|
+
|
|
4
|
+
// Mock the config module
|
|
5
|
+
vi.mock("../config.js", () => ({
|
|
6
|
+
loadConfig: vi.fn(),
|
|
7
|
+
LOCAL_BASE_URL: "http://localhost:7181",
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
// Mock the API modules
|
|
11
|
+
vi.mock("../../api/workspaces.js", () => ({
|
|
12
|
+
getWorkspace: vi.fn(),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
vi.mock("../../api/branches.js", () => ({
|
|
16
|
+
listBranches: vi.fn(),
|
|
17
|
+
getBranch: vi.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
vi.mock("../../api/dashboard.js", () => ({
|
|
21
|
+
getDashboardUrl: vi.fn(),
|
|
22
|
+
getBranchDashboardUrl: vi.fn(),
|
|
23
|
+
getLocalDashboardUrl: vi.fn(),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
vi.mock("../../api/local.js", () => ({
|
|
27
|
+
isLocalRunning: vi.fn(),
|
|
28
|
+
getLocalTokens: vi.fn(),
|
|
29
|
+
getOrCreateLocalWorkspace: vi.fn(),
|
|
30
|
+
getLocalWorkspaceName: vi.fn(),
|
|
31
|
+
}));
|
|
32
|
+
|
|
33
|
+
// Import mocked functions
|
|
34
|
+
import { loadConfig } from "../config.js";
|
|
35
|
+
import { getWorkspace } from "../../api/workspaces.js";
|
|
36
|
+
import { listBranches, getBranch } from "../../api/branches.js";
|
|
37
|
+
import { getDashboardUrl, getBranchDashboardUrl, getLocalDashboardUrl } from "../../api/dashboard.js";
|
|
38
|
+
import {
|
|
39
|
+
isLocalRunning,
|
|
40
|
+
getLocalTokens,
|
|
41
|
+
getOrCreateLocalWorkspace,
|
|
42
|
+
getLocalWorkspaceName,
|
|
43
|
+
} from "../../api/local.js";
|
|
44
|
+
|
|
45
|
+
const mockedLoadConfig = vi.mocked(loadConfig);
|
|
46
|
+
const mockedGetWorkspace = vi.mocked(getWorkspace);
|
|
47
|
+
const mockedListBranches = vi.mocked(listBranches);
|
|
48
|
+
const mockedGetBranch = vi.mocked(getBranch);
|
|
49
|
+
const mockedGetDashboardUrl = vi.mocked(getDashboardUrl);
|
|
50
|
+
const mockedGetBranchDashboardUrl = vi.mocked(getBranchDashboardUrl);
|
|
51
|
+
const mockedGetLocalDashboardUrl = vi.mocked(getLocalDashboardUrl);
|
|
52
|
+
const mockedIsLocalRunning = vi.mocked(isLocalRunning);
|
|
53
|
+
const mockedGetLocalTokens = vi.mocked(getLocalTokens);
|
|
54
|
+
const mockedGetOrCreateLocalWorkspace = vi.mocked(getOrCreateLocalWorkspace);
|
|
55
|
+
const mockedGetLocalWorkspaceName = vi.mocked(getLocalWorkspaceName);
|
|
56
|
+
|
|
57
|
+
describe("Info Command", () => {
|
|
58
|
+
beforeEach(() => {
|
|
59
|
+
vi.clearAllMocks();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe("config loading", () => {
|
|
63
|
+
it("returns error when config loading fails", async () => {
|
|
64
|
+
mockedLoadConfig.mockImplementation(() => {
|
|
65
|
+
throw new Error("No tinybird.json found");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const result = await runInfo();
|
|
69
|
+
|
|
70
|
+
expect(result.success).toBe(false);
|
|
71
|
+
expect(result.error).toContain("No tinybird.json found");
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("branch mode", () => {
|
|
76
|
+
const mockConfig = {
|
|
77
|
+
cwd: "/test/project",
|
|
78
|
+
configPath: "/test/project/tinybird.json",
|
|
79
|
+
devMode: "branch" as const,
|
|
80
|
+
gitBranch: "feature/test",
|
|
81
|
+
tinybirdBranch: "feature_test",
|
|
82
|
+
isMainBranch: false,
|
|
83
|
+
baseUrl: "https://api.tinybird.co",
|
|
84
|
+
token: "test-token",
|
|
85
|
+
include: [],
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const mockWorkspace = {
|
|
89
|
+
id: "ws-123",
|
|
90
|
+
name: "test-workspace",
|
|
91
|
+
user_email: "user@example.com",
|
|
92
|
+
user_id: "user-123",
|
|
93
|
+
scope: "WORKSPACE",
|
|
94
|
+
main: null,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
beforeEach(() => {
|
|
98
|
+
mockedLoadConfig.mockReturnValue(mockConfig);
|
|
99
|
+
mockedGetWorkspace.mockResolvedValue(mockWorkspace);
|
|
100
|
+
mockedListBranches.mockResolvedValue([]);
|
|
101
|
+
mockedGetDashboardUrl.mockReturnValue("https://cloud.tinybird.co/gcp/europe-west3/test-workspace");
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("returns cloud info in branch mode", async () => {
|
|
105
|
+
const result = await runInfo();
|
|
106
|
+
|
|
107
|
+
expect(result.success).toBe(true);
|
|
108
|
+
expect(result.cloud).toEqual({
|
|
109
|
+
workspaceName: "test-workspace",
|
|
110
|
+
workspaceId: "ws-123",
|
|
111
|
+
userEmail: "user@example.com",
|
|
112
|
+
apiHost: "https://api.tinybird.co",
|
|
113
|
+
dashboardUrl: "https://cloud.tinybird.co/gcp/europe-west3/test-workspace",
|
|
114
|
+
token: "test-token",
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it("does not return local info in branch mode", async () => {
|
|
119
|
+
const result = await runInfo();
|
|
120
|
+
|
|
121
|
+
expect(result.success).toBe(true);
|
|
122
|
+
expect(result.local).toBeUndefined();
|
|
123
|
+
expect(mockedIsLocalRunning).not.toHaveBeenCalled();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("returns project info", async () => {
|
|
127
|
+
const result = await runInfo();
|
|
128
|
+
|
|
129
|
+
expect(result.success).toBe(true);
|
|
130
|
+
expect(result.project).toEqual({
|
|
131
|
+
cwd: "/test/project",
|
|
132
|
+
configPath: "/test/project/tinybird.json",
|
|
133
|
+
devMode: "branch",
|
|
134
|
+
gitBranch: "feature/test",
|
|
135
|
+
tinybirdBranch: "feature_test",
|
|
136
|
+
isMainBranch: false,
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("returns branch info when on a feature branch", async () => {
|
|
141
|
+
const mockBranch = {
|
|
142
|
+
id: "branch-123",
|
|
143
|
+
name: "feature_test",
|
|
144
|
+
token: "branch-token",
|
|
145
|
+
created_at: "2024-01-01",
|
|
146
|
+
};
|
|
147
|
+
mockedGetBranch.mockResolvedValue(mockBranch);
|
|
148
|
+
mockedGetBranchDashboardUrl.mockReturnValue(
|
|
149
|
+
"https://cloud.tinybird.co/gcp/europe-west3/test-workspace~feature_test"
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
const result = await runInfo();
|
|
153
|
+
|
|
154
|
+
expect(result.success).toBe(true);
|
|
155
|
+
expect(result.branch).toEqual({
|
|
156
|
+
name: "feature_test",
|
|
157
|
+
id: "branch-123",
|
|
158
|
+
token: "branch-token",
|
|
159
|
+
dashboardUrl: "https://cloud.tinybird.co/gcp/europe-west3/test-workspace~feature_test",
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("does not return branch info when on main branch", async () => {
|
|
164
|
+
mockedLoadConfig.mockReturnValue({
|
|
165
|
+
...mockConfig,
|
|
166
|
+
gitBranch: "main",
|
|
167
|
+
tinybirdBranch: null,
|
|
168
|
+
isMainBranch: true,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const result = await runInfo();
|
|
172
|
+
|
|
173
|
+
expect(result.success).toBe(true);
|
|
174
|
+
expect(result.branch).toBeUndefined();
|
|
175
|
+
expect(mockedGetBranch).not.toHaveBeenCalled();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("returns branches list in branch mode", async () => {
|
|
179
|
+
const mockBranches = [
|
|
180
|
+
{ id: "b1", name: "branch1", created_at: "2024-01-01" },
|
|
181
|
+
{ id: "b2", name: "branch2", created_at: "2024-01-02" },
|
|
182
|
+
];
|
|
183
|
+
mockedListBranches.mockResolvedValue(mockBranches);
|
|
184
|
+
|
|
185
|
+
const result = await runInfo();
|
|
186
|
+
|
|
187
|
+
expect(result.success).toBe(true);
|
|
188
|
+
expect(result.branches).toEqual(mockBranches);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe("local mode", () => {
|
|
193
|
+
const mockConfig = {
|
|
194
|
+
cwd: "/test/project",
|
|
195
|
+
configPath: "/test/project/tinybird.json",
|
|
196
|
+
devMode: "local" as const,
|
|
197
|
+
gitBranch: "feature/test",
|
|
198
|
+
tinybirdBranch: "feature_test",
|
|
199
|
+
isMainBranch: false,
|
|
200
|
+
baseUrl: "https://api.tinybird.co",
|
|
201
|
+
token: "test-token",
|
|
202
|
+
include: [],
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const mockWorkspace = {
|
|
206
|
+
id: "ws-123",
|
|
207
|
+
name: "test-workspace",
|
|
208
|
+
user_email: "user@example.com",
|
|
209
|
+
user_id: "user-123",
|
|
210
|
+
scope: "WORKSPACE",
|
|
211
|
+
main: null,
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
beforeEach(() => {
|
|
215
|
+
mockedLoadConfig.mockReturnValue(mockConfig);
|
|
216
|
+
mockedGetWorkspace.mockResolvedValue(mockWorkspace);
|
|
217
|
+
mockedGetDashboardUrl.mockReturnValue("https://cloud.tinybird.co/gcp/europe-west3/test-workspace");
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it("returns cloud info in local mode", async () => {
|
|
221
|
+
mockedIsLocalRunning.mockResolvedValue(false);
|
|
222
|
+
|
|
223
|
+
const result = await runInfo();
|
|
224
|
+
|
|
225
|
+
expect(result.success).toBe(true);
|
|
226
|
+
expect(result.cloud).toBeDefined();
|
|
227
|
+
expect(result.cloud?.workspaceName).toBe("test-workspace");
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it("returns local info when local is running", async () => {
|
|
231
|
+
mockedIsLocalRunning.mockResolvedValue(true);
|
|
232
|
+
mockedGetLocalTokens.mockResolvedValue({
|
|
233
|
+
user_token: "local-user-token",
|
|
234
|
+
admin_token: "local-admin-token",
|
|
235
|
+
workspace_admin_token: "local-ws-admin-token",
|
|
236
|
+
});
|
|
237
|
+
mockedGetLocalWorkspaceName.mockReturnValue("local_workspace");
|
|
238
|
+
mockedGetOrCreateLocalWorkspace.mockResolvedValue({
|
|
239
|
+
workspace: {
|
|
240
|
+
id: "local-ws-123",
|
|
241
|
+
name: "local_workspace",
|
|
242
|
+
token: "local-token",
|
|
243
|
+
},
|
|
244
|
+
wasCreated: false,
|
|
245
|
+
});
|
|
246
|
+
mockedGetLocalDashboardUrl.mockReturnValue("https://cloud.tinybird.co/local/7181/local_workspace");
|
|
247
|
+
|
|
248
|
+
const result = await runInfo();
|
|
249
|
+
|
|
250
|
+
expect(result.success).toBe(true);
|
|
251
|
+
expect(result.local).toEqual({
|
|
252
|
+
running: true,
|
|
253
|
+
workspaceName: "local_workspace",
|
|
254
|
+
workspaceId: "local-ws-123",
|
|
255
|
+
apiHost: "http://localhost:7181",
|
|
256
|
+
dashboardUrl: "https://cloud.tinybird.co/local/7181/local_workspace",
|
|
257
|
+
token: "local-token",
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it("returns local info with running=false when local is not running", async () => {
|
|
262
|
+
mockedIsLocalRunning.mockResolvedValue(false);
|
|
263
|
+
|
|
264
|
+
const result = await runInfo();
|
|
265
|
+
|
|
266
|
+
expect(result.success).toBe(true);
|
|
267
|
+
expect(result.local).toEqual({
|
|
268
|
+
running: false,
|
|
269
|
+
apiHost: "http://localhost:7181",
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("does not return branch info in local mode", async () => {
|
|
274
|
+
mockedIsLocalRunning.mockResolvedValue(false);
|
|
275
|
+
|
|
276
|
+
const result = await runInfo();
|
|
277
|
+
|
|
278
|
+
expect(result.success).toBe(true);
|
|
279
|
+
expect(result.branch).toBeUndefined();
|
|
280
|
+
expect(result.branches).toEqual([]);
|
|
281
|
+
expect(mockedGetBranch).not.toHaveBeenCalled();
|
|
282
|
+
expect(mockedListBranches).not.toHaveBeenCalled();
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
describe("error handling", () => {
|
|
287
|
+
it("returns error when workspace fetch fails", async () => {
|
|
288
|
+
mockedLoadConfig.mockReturnValue({
|
|
289
|
+
cwd: "/test",
|
|
290
|
+
configPath: "/test/tinybird.json",
|
|
291
|
+
devMode: "branch" as const,
|
|
292
|
+
gitBranch: null,
|
|
293
|
+
tinybirdBranch: null,
|
|
294
|
+
isMainBranch: true,
|
|
295
|
+
baseUrl: "https://api.tinybird.co",
|
|
296
|
+
token: "test-token",
|
|
297
|
+
include: [],
|
|
298
|
+
});
|
|
299
|
+
mockedGetWorkspace.mockRejectedValue(new Error("Unauthorized"));
|
|
300
|
+
|
|
301
|
+
const result = await runInfo();
|
|
302
|
+
|
|
303
|
+
expect(result.success).toBe(false);
|
|
304
|
+
expect(result.error).toContain("Failed to get workspace info");
|
|
305
|
+
expect(result.error).toContain("Unauthorized");
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
it("handles branch fetch error gracefully", async () => {
|
|
309
|
+
mockedLoadConfig.mockReturnValue({
|
|
310
|
+
cwd: "/test",
|
|
311
|
+
configPath: "/test/tinybird.json",
|
|
312
|
+
devMode: "branch" as const,
|
|
313
|
+
gitBranch: "feature/test",
|
|
314
|
+
tinybirdBranch: "feature_test",
|
|
315
|
+
isMainBranch: false,
|
|
316
|
+
baseUrl: "https://api.tinybird.co",
|
|
317
|
+
token: "test-token",
|
|
318
|
+
include: [],
|
|
319
|
+
});
|
|
320
|
+
mockedGetWorkspace.mockResolvedValue({
|
|
321
|
+
id: "ws-123",
|
|
322
|
+
name: "test-workspace",
|
|
323
|
+
user_email: "user@example.com",
|
|
324
|
+
user_id: "user-123",
|
|
325
|
+
scope: "WORKSPACE",
|
|
326
|
+
main: null,
|
|
327
|
+
});
|
|
328
|
+
mockedGetBranch.mockRejectedValue(new Error("Branch not found"));
|
|
329
|
+
mockedListBranches.mockResolvedValue([]);
|
|
330
|
+
mockedGetDashboardUrl.mockReturnValue(null);
|
|
331
|
+
|
|
332
|
+
const result = await runInfo();
|
|
333
|
+
|
|
334
|
+
expect(result.success).toBe(true);
|
|
335
|
+
expect(result.branch).toBeUndefined();
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it("handles branches list fetch error gracefully", async () => {
|
|
339
|
+
mockedLoadConfig.mockReturnValue({
|
|
340
|
+
cwd: "/test",
|
|
341
|
+
configPath: "/test/tinybird.json",
|
|
342
|
+
devMode: "branch" as const,
|
|
343
|
+
gitBranch: "main",
|
|
344
|
+
tinybirdBranch: null,
|
|
345
|
+
isMainBranch: true,
|
|
346
|
+
baseUrl: "https://api.tinybird.co",
|
|
347
|
+
token: "test-token",
|
|
348
|
+
include: [],
|
|
349
|
+
});
|
|
350
|
+
mockedGetWorkspace.mockResolvedValue({
|
|
351
|
+
id: "ws-123",
|
|
352
|
+
name: "test-workspace",
|
|
353
|
+
user_email: "user@example.com",
|
|
354
|
+
user_id: "user-123",
|
|
355
|
+
scope: "WORKSPACE",
|
|
356
|
+
main: null,
|
|
357
|
+
});
|
|
358
|
+
mockedListBranches.mockRejectedValue(new Error("Network error"));
|
|
359
|
+
mockedGetDashboardUrl.mockReturnValue(null);
|
|
360
|
+
|
|
361
|
+
const result = await runInfo();
|
|
362
|
+
|
|
363
|
+
expect(result.success).toBe(true);
|
|
364
|
+
expect(result.branches).toEqual([]);
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
it("handles local workspace fetch error gracefully", async () => {
|
|
368
|
+
mockedLoadConfig.mockReturnValue({
|
|
369
|
+
cwd: "/test",
|
|
370
|
+
configPath: "/test/tinybird.json",
|
|
371
|
+
devMode: "local" as const,
|
|
372
|
+
gitBranch: "feature/test",
|
|
373
|
+
tinybirdBranch: "feature_test",
|
|
374
|
+
isMainBranch: false,
|
|
375
|
+
baseUrl: "https://api.tinybird.co",
|
|
376
|
+
token: "test-token",
|
|
377
|
+
include: [],
|
|
378
|
+
});
|
|
379
|
+
mockedGetWorkspace.mockResolvedValue({
|
|
380
|
+
id: "ws-123",
|
|
381
|
+
name: "test-workspace",
|
|
382
|
+
user_email: "user@example.com",
|
|
383
|
+
user_id: "user-123",
|
|
384
|
+
scope: "WORKSPACE",
|
|
385
|
+
main: null,
|
|
386
|
+
});
|
|
387
|
+
mockedIsLocalRunning.mockResolvedValue(true);
|
|
388
|
+
mockedGetLocalTokens.mockRejectedValue(new Error("Connection refused"));
|
|
389
|
+
mockedGetDashboardUrl.mockReturnValue(null);
|
|
390
|
+
|
|
391
|
+
const result = await runInfo();
|
|
392
|
+
|
|
393
|
+
expect(result.success).toBe(true);
|
|
394
|
+
expect(result.local).toEqual({
|
|
395
|
+
running: true,
|
|
396
|
+
apiHost: "http://localhost:7181",
|
|
397
|
+
});
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
});
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Info command - shows information about the current project and workspace
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { loadConfig, LOCAL_BASE_URL, type ResolvedConfig } from "../config.js";
|
|
6
|
+
import { getWorkspace, type TinybirdWorkspace } from "../../api/workspaces.js";
|
|
7
|
+
import { listBranches, getBranch, type TinybirdBranch } from "../../api/branches.js";
|
|
8
|
+
import { getDashboardUrl, getBranchDashboardUrl, getLocalDashboardUrl } from "../../api/dashboard.js";
|
|
9
|
+
import {
|
|
10
|
+
isLocalRunning,
|
|
11
|
+
getLocalTokens,
|
|
12
|
+
getOrCreateLocalWorkspace,
|
|
13
|
+
getLocalWorkspaceName,
|
|
14
|
+
} from "../../api/local.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Info command options
|
|
18
|
+
*/
|
|
19
|
+
export interface InfoCommandOptions {
|
|
20
|
+
/** Working directory (defaults to cwd) */
|
|
21
|
+
cwd?: string;
|
|
22
|
+
/** Output as JSON */
|
|
23
|
+
json?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Cloud/workspace information
|
|
28
|
+
*/
|
|
29
|
+
export interface CloudInfo {
|
|
30
|
+
/** Workspace name */
|
|
31
|
+
workspaceName: string;
|
|
32
|
+
/** Workspace ID */
|
|
33
|
+
workspaceId: string;
|
|
34
|
+
/** User email */
|
|
35
|
+
userEmail: string;
|
|
36
|
+
/** API host URL */
|
|
37
|
+
apiHost: string;
|
|
38
|
+
/** Dashboard URL */
|
|
39
|
+
dashboardUrl?: string;
|
|
40
|
+
/** Token */
|
|
41
|
+
token: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Project configuration information
|
|
46
|
+
*/
|
|
47
|
+
export interface ProjectInfo {
|
|
48
|
+
/** Current working directory */
|
|
49
|
+
cwd: string;
|
|
50
|
+
/** Path to tinybird.json */
|
|
51
|
+
configPath: string;
|
|
52
|
+
/** Development mode */
|
|
53
|
+
devMode: string;
|
|
54
|
+
/** Git branch */
|
|
55
|
+
gitBranch: string | null;
|
|
56
|
+
/** Tinybird branch (sanitized) */
|
|
57
|
+
tinybirdBranch: string | null;
|
|
58
|
+
/** Whether on main branch */
|
|
59
|
+
isMainBranch: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Current branch information (when working on a branch)
|
|
64
|
+
*/
|
|
65
|
+
export interface BranchInfo {
|
|
66
|
+
/** Branch name */
|
|
67
|
+
name: string;
|
|
68
|
+
/** Branch ID */
|
|
69
|
+
id: string;
|
|
70
|
+
/** Branch token */
|
|
71
|
+
token: string;
|
|
72
|
+
/** Dashboard URL */
|
|
73
|
+
dashboardUrl?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Local Tinybird workspace information
|
|
78
|
+
*/
|
|
79
|
+
export interface LocalInfo {
|
|
80
|
+
/** Whether local Tinybird is running */
|
|
81
|
+
running: boolean;
|
|
82
|
+
/** Workspace name */
|
|
83
|
+
workspaceName?: string;
|
|
84
|
+
/** Workspace ID */
|
|
85
|
+
workspaceId?: string;
|
|
86
|
+
/** API host URL */
|
|
87
|
+
apiHost: string;
|
|
88
|
+
/** Dashboard URL */
|
|
89
|
+
dashboardUrl?: string;
|
|
90
|
+
/** Token */
|
|
91
|
+
token?: string;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Result of the info command
|
|
96
|
+
*/
|
|
97
|
+
export interface InfoCommandResult {
|
|
98
|
+
/** Whether the operation was successful */
|
|
99
|
+
success: boolean;
|
|
100
|
+
/** Cloud/workspace info */
|
|
101
|
+
cloud?: CloudInfo;
|
|
102
|
+
/** Local workspace info (when devMode is local) */
|
|
103
|
+
local?: LocalInfo;
|
|
104
|
+
/** Project info */
|
|
105
|
+
project?: ProjectInfo;
|
|
106
|
+
/** Current branch info (if on a branch) */
|
|
107
|
+
branch?: BranchInfo;
|
|
108
|
+
/** List of all branches */
|
|
109
|
+
branches?: TinybirdBranch[];
|
|
110
|
+
/** Error message if failed */
|
|
111
|
+
error?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Run the info command
|
|
116
|
+
*
|
|
117
|
+
* @param options - Command options
|
|
118
|
+
* @returns Info result
|
|
119
|
+
*/
|
|
120
|
+
export async function runInfo(
|
|
121
|
+
options: InfoCommandOptions = {}
|
|
122
|
+
): Promise<InfoCommandResult> {
|
|
123
|
+
const cwd = options.cwd ?? process.cwd();
|
|
124
|
+
|
|
125
|
+
// Load config
|
|
126
|
+
let config: ResolvedConfig;
|
|
127
|
+
try {
|
|
128
|
+
config = loadConfig(cwd);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
error: (error as Error).message,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Build project info first (always available)
|
|
137
|
+
const projectInfo: ProjectInfo = {
|
|
138
|
+
cwd: config.cwd,
|
|
139
|
+
configPath: config.configPath,
|
|
140
|
+
devMode: config.devMode,
|
|
141
|
+
gitBranch: config.gitBranch,
|
|
142
|
+
tinybirdBranch: config.tinybirdBranch,
|
|
143
|
+
isMainBranch: config.isMainBranch,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// Always get cloud/workspace info first (needed for local workspace name on main branch)
|
|
147
|
+
let workspace: TinybirdWorkspace;
|
|
148
|
+
try {
|
|
149
|
+
workspace = await getWorkspace({
|
|
150
|
+
baseUrl: config.baseUrl,
|
|
151
|
+
token: config.token,
|
|
152
|
+
});
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
error: `Failed to get workspace info: ${(error as Error).message}`,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Build cloud info
|
|
161
|
+
const cloudInfo: CloudInfo = {
|
|
162
|
+
workspaceName: workspace.name,
|
|
163
|
+
workspaceId: workspace.id,
|
|
164
|
+
userEmail: workspace.user_email,
|
|
165
|
+
apiHost: config.baseUrl,
|
|
166
|
+
dashboardUrl: getDashboardUrl(config.baseUrl, workspace.name) ?? undefined,
|
|
167
|
+
token: config.token,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
// Get local info if in local mode
|
|
171
|
+
let localInfo: LocalInfo | undefined;
|
|
172
|
+
if (config.devMode === "local") {
|
|
173
|
+
const localRunning = await isLocalRunning();
|
|
174
|
+
localInfo = {
|
|
175
|
+
running: localRunning,
|
|
176
|
+
apiHost: LOCAL_BASE_URL,
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
if (localRunning) {
|
|
180
|
+
try {
|
|
181
|
+
const tokens = await getLocalTokens();
|
|
182
|
+
// Determine workspace name: use authenticated workspace name on main branch,
|
|
183
|
+
// otherwise use branch name (for trunk-based development support)
|
|
184
|
+
let workspaceName: string;
|
|
185
|
+
if (config.isMainBranch || !config.tinybirdBranch) {
|
|
186
|
+
// On main branch: use the authenticated workspace name
|
|
187
|
+
workspaceName = workspace.name;
|
|
188
|
+
} else {
|
|
189
|
+
// On feature branch: use branch name
|
|
190
|
+
workspaceName = getLocalWorkspaceName(config.tinybirdBranch, config.cwd);
|
|
191
|
+
}
|
|
192
|
+
const { workspace: localWorkspace } = await getOrCreateLocalWorkspace(tokens, workspaceName);
|
|
193
|
+
localInfo = {
|
|
194
|
+
running: true,
|
|
195
|
+
workspaceName: localWorkspace.name,
|
|
196
|
+
workspaceId: localWorkspace.id,
|
|
197
|
+
apiHost: LOCAL_BASE_URL,
|
|
198
|
+
dashboardUrl: getLocalDashboardUrl(localWorkspace.name),
|
|
199
|
+
token: localWorkspace.token,
|
|
200
|
+
};
|
|
201
|
+
} catch {
|
|
202
|
+
// Local is running but couldn't get workspace info
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Get current branch info only if we're in branch mode (not local mode)
|
|
208
|
+
let branchInfo: BranchInfo | undefined;
|
|
209
|
+
let branches: TinybirdBranch[] = [];
|
|
210
|
+
|
|
211
|
+
if (config.devMode === "branch") {
|
|
212
|
+
// Get current branch info if we're on a branch (not main)
|
|
213
|
+
if (!config.isMainBranch && config.tinybirdBranch) {
|
|
214
|
+
try {
|
|
215
|
+
const branch = await getBranch(
|
|
216
|
+
{
|
|
217
|
+
baseUrl: config.baseUrl,
|
|
218
|
+
token: config.token,
|
|
219
|
+
},
|
|
220
|
+
config.tinybirdBranch
|
|
221
|
+
);
|
|
222
|
+
const dashboardUrl = getBranchDashboardUrl(config.baseUrl, workspace.name, branch.name) ?? undefined;
|
|
223
|
+
branchInfo = {
|
|
224
|
+
name: branch.name,
|
|
225
|
+
id: branch.id,
|
|
226
|
+
token: branch.token ?? "",
|
|
227
|
+
dashboardUrl,
|
|
228
|
+
};
|
|
229
|
+
} catch {
|
|
230
|
+
// Branch might not exist yet, that's ok
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Get all branches
|
|
235
|
+
try {
|
|
236
|
+
branches = await listBranches({
|
|
237
|
+
baseUrl: config.baseUrl,
|
|
238
|
+
token: config.token,
|
|
239
|
+
});
|
|
240
|
+
} catch {
|
|
241
|
+
// Branches are optional, don't fail if we can't fetch them
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
success: true,
|
|
247
|
+
cloud: cloudInfo,
|
|
248
|
+
local: localInfo,
|
|
249
|
+
project: projectInfo,
|
|
250
|
+
branch: branchInfo,
|
|
251
|
+
branches,
|
|
252
|
+
};
|
|
253
|
+
}
|