@turboops/cli 1.0.0-dev.579 → 1.0.0-dev.582

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 (2) hide show
  1. package/dist/index.js +769 -171
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -6,6 +6,8 @@ import chalk8 from "chalk";
6
6
 
7
7
  // src/services/config.ts
8
8
  import Conf from "conf";
9
+ import * as fs from "fs";
10
+ import * as path from "path";
9
11
 
10
12
  // src/utils/logger.ts
11
13
  import chalk from "chalk";
@@ -133,9 +135,9 @@ function loadPackageJson() {
133
135
  join(__dirname, "../package.json")
134
136
  // From dist/
135
137
  ];
136
- for (const path of paths) {
137
- if (existsSync(path)) {
138
- return require2(path);
138
+ for (const path2 of paths) {
139
+ if (existsSync(path2)) {
140
+ return require2(path2);
139
141
  }
140
142
  }
141
143
  return { version: "0.0.0", name: "@turboops/cli" };
@@ -196,90 +198,150 @@ var API_URLS = {
196
198
  production: "https://api.turbo-ops.de",
197
199
  dev: "https://api.dev.turbo-ops.de"
198
200
  };
199
- function getDefaultApiUrl() {
200
- const version = getCurrentVersion();
201
- const isDevVersion = version.includes("-dev");
202
- return isDevVersion ? API_URLS.dev : API_URLS.production;
203
- }
204
- var defaults = {
205
- apiUrl: API_URLS.production,
201
+ var APP_URLS = {
202
+ production: "https://turbo-ops.de",
203
+ dev: "https://dev.turbo-ops.de"
204
+ };
205
+ var LOCAL_CONFIG_FILE = ".turboops.json";
206
+ var globalDefaults = {
206
207
  token: null,
207
- project: null,
208
208
  userId: null
209
209
  };
210
- var config = new Conf({
210
+ var globalConfig = new Conf({
211
211
  projectName: "turboops-cli",
212
- defaults
212
+ defaults: globalDefaults
213
213
  });
214
+ function findProjectRoot() {
215
+ let currentDir = process.cwd();
216
+ const root = path.parse(currentDir).root;
217
+ while (currentDir !== root) {
218
+ if (fs.existsSync(path.join(currentDir, LOCAL_CONFIG_FILE))) {
219
+ return currentDir;
220
+ }
221
+ if (fs.existsSync(path.join(currentDir, ".git"))) {
222
+ return currentDir;
223
+ }
224
+ if (fs.existsSync(path.join(currentDir, "package.json"))) {
225
+ return currentDir;
226
+ }
227
+ currentDir = path.dirname(currentDir);
228
+ }
229
+ return null;
230
+ }
231
+ function getLocalConfigPath() {
232
+ const projectRoot = findProjectRoot();
233
+ if (!projectRoot) {
234
+ return null;
235
+ }
236
+ return path.join(projectRoot, LOCAL_CONFIG_FILE);
237
+ }
238
+ function readLocalConfig() {
239
+ const configPath = getLocalConfigPath();
240
+ if (!configPath || !fs.existsSync(configPath)) {
241
+ return null;
242
+ }
243
+ try {
244
+ const content = fs.readFileSync(configPath, "utf-8");
245
+ return JSON.parse(content);
246
+ } catch {
247
+ return null;
248
+ }
249
+ }
250
+ function writeLocalConfig(config) {
251
+ const projectRoot = findProjectRoot() || process.cwd();
252
+ const configPath = path.join(projectRoot, LOCAL_CONFIG_FILE);
253
+ try {
254
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
255
+ return true;
256
+ } catch (error) {
257
+ logger.error(`Failed to write ${LOCAL_CONFIG_FILE}: ${error}`);
258
+ return false;
259
+ }
260
+ }
261
+ function getApiUrl() {
262
+ const version = getCurrentVersion();
263
+ const isDevVersion = version.includes("-dev");
264
+ return isDevVersion ? API_URLS.dev : API_URLS.production;
265
+ }
214
266
  var configService = {
215
267
  /**
216
- * Get API URL
217
- * Priority: ENV variable > stored config > version-based default
268
+ * Get API URL (fixed based on CLI version)
218
269
  */
219
270
  getApiUrl() {
220
271
  if (process.env.TURBOOPS_API_URL) {
221
272
  return process.env.TURBOOPS_API_URL;
222
273
  }
223
- const storedUrl = config.get("apiUrl");
224
- if (storedUrl === "https://api.turboops.io" || storedUrl === API_URLS.production) {
225
- return getDefaultApiUrl();
226
- }
227
- return storedUrl;
274
+ return getApiUrl();
228
275
  },
229
276
  /**
230
- * Set API URL
277
+ * Get App URL (frontend)
278
+ * Returns the appropriate frontend URL based on CLI version
231
279
  */
232
- setApiUrl(url) {
233
- config.set("apiUrl", url);
280
+ getAppUrl() {
281
+ const version = getCurrentVersion();
282
+ const isDevVersion = version.includes("-dev");
283
+ return isDevVersion ? APP_URLS.dev : APP_URLS.production;
234
284
  },
235
285
  /**
236
- * Get authentication token
286
+ * Get authentication token (global - user session)
237
287
  */
238
288
  getToken() {
239
- return process.env.TURBOOPS_TOKEN || config.get("token");
289
+ return process.env.TURBOOPS_TOKEN || globalConfig.get("token");
240
290
  },
241
291
  /**
242
- * Set authentication token
292
+ * Set authentication token (global - user session)
243
293
  */
244
294
  setToken(token) {
245
- config.set("token", token);
295
+ globalConfig.set("token", token);
246
296
  },
247
297
  /**
248
298
  * Clear authentication token
249
299
  */
250
300
  clearToken() {
251
- config.set("token", null);
252
- config.set("userId", null);
301
+ globalConfig.set("token", null);
302
+ globalConfig.set("userId", null);
253
303
  },
254
304
  /**
255
- * Get current project
305
+ * Get current project (local - project specific)
256
306
  */
257
307
  getProject() {
258
- return process.env.TURBOOPS_PROJECT || config.get("project");
308
+ if (process.env.TURBOOPS_PROJECT) {
309
+ return process.env.TURBOOPS_PROJECT;
310
+ }
311
+ const localConfig = readLocalConfig();
312
+ return localConfig?.project || null;
259
313
  },
260
314
  /**
261
- * Set current project
315
+ * Set current project (local - writes to .turboops.json)
262
316
  */
263
317
  setProject(project) {
264
- config.set("project", project);
318
+ const localConfig = readLocalConfig() || { project: "" };
319
+ localConfig.project = project;
320
+ writeLocalConfig(localConfig);
265
321
  },
266
322
  /**
267
323
  * Clear current project
268
324
  */
269
325
  clearProject() {
270
- config.set("project", null);
326
+ const configPath = getLocalConfigPath();
327
+ if (configPath && fs.existsSync(configPath)) {
328
+ try {
329
+ fs.unlinkSync(configPath);
330
+ } catch {
331
+ }
332
+ }
271
333
  },
272
334
  /**
273
- * Get user ID
335
+ * Get user ID (global - user session)
274
336
  */
275
337
  getUserId() {
276
- return config.get("userId");
338
+ return globalConfig.get("userId");
277
339
  },
278
340
  /**
279
- * Set user ID
341
+ * Set user ID (global - user session)
280
342
  */
281
343
  setUserId(userId) {
282
- config.set("userId", userId);
344
+ globalConfig.set("userId", userId);
283
345
  },
284
346
  /**
285
347
  * Check if user is authenticated
@@ -293,6 +355,19 @@ var configService = {
293
355
  hasProject() {
294
356
  return !!this.getProject();
295
357
  },
358
+ /**
359
+ * Check if local config exists
360
+ */
361
+ hasLocalConfig() {
362
+ const configPath = getLocalConfigPath();
363
+ return !!configPath && fs.existsSync(configPath);
364
+ },
365
+ /**
366
+ * Get local config file path (for display purposes)
367
+ */
368
+ getLocalConfigPath() {
369
+ return getLocalConfigPath();
370
+ },
296
371
  /**
297
372
  * Get all configuration
298
373
  */
@@ -305,17 +380,25 @@ var configService = {
305
380
  };
306
381
  },
307
382
  /**
308
- * Clear all configuration
383
+ * Clear all configuration (both global and local)
309
384
  */
310
385
  clearAll() {
311
- config.clear();
386
+ globalConfig.clear();
387
+ this.clearProject();
312
388
  },
313
389
  /**
314
- * Check if using a project token (turbo_xxx format)
390
+ * Check if using a project token (turbo_xxx format, but not turbo_cli_xxx)
315
391
  */
316
392
  isProjectToken() {
317
393
  const token = this.getToken();
318
- return !!token && token.startsWith("turbo_");
394
+ return !!token && token.startsWith("turbo_") && !token.startsWith("turbo_cli_");
395
+ },
396
+ /**
397
+ * Check if using a CLI session token (turbo_cli_xxx format)
398
+ */
399
+ isCliSessionToken() {
400
+ const token = this.getToken();
401
+ return !!token && token.startsWith("turbo_cli_");
319
402
  },
320
403
  /**
321
404
  * Show current configuration
@@ -323,12 +406,15 @@ var configService = {
323
406
  show() {
324
407
  const data = this.getAll();
325
408
  const isProjectToken2 = this.isProjectToken();
409
+ const isCliToken = this.isCliSessionToken();
410
+ const localConfigPath = this.getLocalConfigPath();
326
411
  logger.header("Configuration");
327
412
  logger.table({
328
413
  "API URL": data.apiUrl,
329
414
  Token: data.token || "Not set",
330
- "Token Type": isProjectToken2 ? "Project Token" : data.token ? "User Token" : "N/A",
415
+ "Token Type": isProjectToken2 ? "Project Token (CI/CD)" : isCliToken ? "CLI Session Token" : data.token ? "User Token" : "N/A",
331
416
  Project: data.project || (isProjectToken2 ? "(from token)" : "Not set"),
417
+ "Project Config": localConfigPath || "(not found)",
332
418
  "User ID": data.userId || (isProjectToken2 ? "(N/A for project token)" : "Not set")
333
419
  });
334
420
  }
@@ -336,7 +422,6 @@ var configService = {
336
422
 
337
423
  // src/commands/login.ts
338
424
  import { Command } from "commander";
339
- import prompts from "prompts";
340
425
 
341
426
  // src/services/api.ts
342
427
  function isProjectToken(token) {
@@ -347,7 +432,7 @@ var apiClient = {
347
432
  /**
348
433
  * Make an API request
349
434
  */
350
- async request(method, path, body) {
435
+ async request(method, path2, body) {
351
436
  const apiUrl = configService.getApiUrl();
352
437
  const token = configService.getToken();
353
438
  if (!token) {
@@ -356,7 +441,7 @@ var apiClient = {
356
441
  status: 401
357
442
  };
358
443
  }
359
- const url = `${apiUrl}${path}`;
444
+ const url = `${apiUrl}${path2}`;
360
445
  const headers = {
361
446
  "Content-Type": "application/json",
362
447
  Authorization: `Bearer ${token}`
@@ -501,23 +586,66 @@ var apiClient = {
501
586
  return this.request("GET", `/deployment/projects/by-slug/${slug}`);
502
587
  },
503
588
  /**
504
- * Get environments for project
589
+ * Create a new project
590
+ */
591
+ async createProject(data) {
592
+ const payload = {
593
+ name: data.name,
594
+ slug: data.slug
595
+ };
596
+ if (data.customer) payload.customer = data.customer;
597
+ if (data.description) payload.description = data.description;
598
+ if (data.repositoryUrl) payload.repositoryUrl = data.repositoryUrl;
599
+ return this.request("POST", "/deployment/projects/simple", payload);
600
+ },
601
+ /**
602
+ * Get all customers
603
+ */
604
+ async getCustomers() {
605
+ return this.request("GET", "/customer");
606
+ },
607
+ /**
608
+ * Get environments (stages) for project
505
609
  */
506
610
  async getEnvironments(projectId) {
507
611
  return this.request(
508
612
  "GET",
509
- `/deployment/environments?projectId=${projectId}`
613
+ `/deployment/stages?projectId=${projectId}`
510
614
  );
511
615
  },
512
616
  /**
513
- * Get environment by slug
617
+ * Get environment (stage) by slug
514
618
  */
515
619
  async getEnvironmentBySlug(projectId, slug) {
516
620
  return this.request(
517
621
  "GET",
518
- `/deployment/environments/by-slug/${projectId}/${slug}`
622
+ `/deployment/stages/by-slug/${projectId}/${slug}`
519
623
  );
520
624
  },
625
+ /**
626
+ * Create a new stage
627
+ */
628
+ async createStage(data) {
629
+ return this.request("POST", "/deployment/stages/simple", data);
630
+ },
631
+ /**
632
+ * Get deployment-ready servers
633
+ */
634
+ async getDeploymentServers() {
635
+ return this.request("GET", "/server?deploymentReady=true");
636
+ },
637
+ /**
638
+ * Generate CI/CD pipeline configuration
639
+ */
640
+ async generatePipeline(projectId, type) {
641
+ return this.request("GET", `/deployment/projects/${projectId}/pipeline/${type}`);
642
+ },
643
+ /**
644
+ * Get required secrets for pipeline
645
+ */
646
+ async getPipelineSecrets(projectId, type) {
647
+ return this.request("GET", `/deployment/projects/${projectId}/pipeline/${type}/secrets`);
648
+ },
521
649
  /**
522
650
  * Trigger deployment
523
651
  */
@@ -548,7 +676,7 @@ var apiClient = {
548
676
  async restart(environmentId) {
549
677
  return this.request(
550
678
  "POST",
551
- `/deployment/environments/${environmentId}/restart`
679
+ `/deployment/stages/${environmentId}/restart`
552
680
  );
553
681
  },
554
682
  /**
@@ -557,7 +685,7 @@ var apiClient = {
557
685
  async stop(environmentId) {
558
686
  return this.request(
559
687
  "POST",
560
- `/deployment/environments/${environmentId}/stop`
688
+ `/deployment/stages/${environmentId}/stop`
561
689
  );
562
690
  },
563
691
  /**
@@ -566,7 +694,7 @@ var apiClient = {
566
694
  async wake(environmentId) {
567
695
  return this.request(
568
696
  "POST",
569
- `/deployment/environments/${environmentId}/wake`
697
+ `/deployment/stages/${environmentId}/wake`
570
698
  );
571
699
  },
572
700
  /**
@@ -581,7 +709,7 @@ var apiClient = {
581
709
  async getEnvVars(environmentId) {
582
710
  return this.request(
583
711
  "GET",
584
- `/deployment/environments/${environmentId}/env-vars`
712
+ `/deployment/stages/${environmentId}/env-vars`
585
713
  );
586
714
  },
587
715
  /**
@@ -590,7 +718,7 @@ var apiClient = {
590
718
  async setEnvVar(environmentId, key, value, secret) {
591
719
  return this.request(
592
720
  "PUT",
593
- `/deployment/environments/${environmentId}/env-vars`,
721
+ `/deployment/stages/${environmentId}/env-vars`,
594
722
  {
595
723
  key,
596
724
  value,
@@ -604,7 +732,7 @@ var apiClient = {
604
732
  async deleteEnvVar(environmentId, key) {
605
733
  return this.request(
606
734
  "DELETE",
607
- `/deployment/environments/${environmentId}/env-vars/${key}`
735
+ `/deployment/stages/${environmentId}/env-vars/${key}`
608
736
  );
609
737
  },
610
738
  /**
@@ -634,6 +762,137 @@ var apiClient = {
634
762
  }
635
763
  };
636
764
 
765
+ // src/services/auth.ts
766
+ import os from "os";
767
+ var authService = {
768
+ /**
769
+ * Start browser-based authentication flow
770
+ * Returns the auth code and URL for the browser
771
+ */
772
+ async initAuth() {
773
+ const apiUrl = configService.getApiUrl();
774
+ const version = getCurrentVersion();
775
+ const platform = os.platform();
776
+ const hostname = os.hostname();
777
+ try {
778
+ const response = await fetch(`${apiUrl}/cli/auth/init`, {
779
+ body: JSON.stringify({
780
+ cliVersion: version,
781
+ hostname,
782
+ platform
783
+ }),
784
+ headers: {
785
+ "Content-Type": "application/json"
786
+ },
787
+ method: "POST"
788
+ });
789
+ if (!response.ok) {
790
+ return null;
791
+ }
792
+ return await response.json();
793
+ } catch {
794
+ return null;
795
+ }
796
+ },
797
+ /**
798
+ * Poll for authentication status
799
+ */
800
+ async pollAuth(code, pollToken) {
801
+ const apiUrl = configService.getApiUrl();
802
+ try {
803
+ const response = await fetch(`${apiUrl}/cli/auth/poll/${code}`, {
804
+ headers: {
805
+ "X-Poll-Token": pollToken
806
+ }
807
+ });
808
+ if (!response.ok) {
809
+ return { status: "expired" };
810
+ }
811
+ return await response.json();
812
+ } catch {
813
+ return { status: "expired" };
814
+ }
815
+ },
816
+ /**
817
+ * Validate a CLI token
818
+ */
819
+ async validateToken(token) {
820
+ const apiUrl = configService.getApiUrl();
821
+ try {
822
+ const response = await fetch(`${apiUrl}/cli/auth/validate`, {
823
+ headers: {
824
+ Authorization: `Bearer ${token}`
825
+ }
826
+ });
827
+ if (!response.ok) {
828
+ return { valid: false };
829
+ }
830
+ return await response.json();
831
+ } catch {
832
+ return { valid: false };
833
+ }
834
+ },
835
+ /**
836
+ * Perform browser-based login
837
+ * Opens browser, waits for user to authorize, returns user info
838
+ */
839
+ async browserLogin() {
840
+ const open = (await import("open")).default;
841
+ logger.info("Initialisiere Anmeldung...");
842
+ const authInit = await this.initAuth();
843
+ if (!authInit) {
844
+ return { error: "Konnte Authentifizierung nicht initialisieren", success: false };
845
+ }
846
+ logger.newline();
847
+ logger.info("Bitte autorisieren Sie die CLI in Ihrem Browser.");
848
+ logger.newline();
849
+ logger.info(`Autorisierungscode: ${authInit.code}`);
850
+ logger.newline();
851
+ try {
852
+ await open(authInit.authUrl);
853
+ logger.info("Browser wurde ge\xF6ffnet...");
854
+ } catch {
855
+ logger.warning("Browser konnte nicht ge\xF6ffnet werden.");
856
+ logger.info(`Bitte \xF6ffnen Sie diese URL manuell: ${authInit.authUrl}`);
857
+ }
858
+ logger.newline();
859
+ logger.info("Warte auf Autorisierung...");
860
+ const maxAttempts = 150;
861
+ const pollInterval = 2e3;
862
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
863
+ const result = await this.pollAuth(authInit.code, authInit.pollToken);
864
+ if (result.status === "authorized" && result.token) {
865
+ configService.setToken(result.token);
866
+ if (result.user) {
867
+ configService.setUserId(result.user.id);
868
+ }
869
+ return {
870
+ success: true,
871
+ user: result.user
872
+ };
873
+ }
874
+ if (result.status === "expired" || result.status === "consumed") {
875
+ return { error: "Autorisierungscode abgelaufen oder bereits verwendet", success: false };
876
+ }
877
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
878
+ }
879
+ return { error: "Zeit\xFCberschreitung bei der Anmeldung", success: false };
880
+ },
881
+ /**
882
+ * Check if current token is a CLI token and validate it
883
+ */
884
+ async checkCliToken() {
885
+ const token = configService.getToken();
886
+ if (!token) {
887
+ return { valid: false };
888
+ }
889
+ if (!token.startsWith("turbo_cli_")) {
890
+ return { valid: true };
891
+ }
892
+ return await this.validateToken(token);
893
+ }
894
+ };
895
+
637
896
  // src/utils/spinner.ts
638
897
  import ora from "ora";
639
898
  function createSpinner(text) {
@@ -660,60 +919,59 @@ var loginCommand = new Command("login").description("Authenticate with TurboOps
660
919
  logger.header("TurboOps Login");
661
920
  if (options.token) {
662
921
  configService.setToken(options.token);
663
- const { data: data2, error: error2 } = await withSpinner(
922
+ if (options.token.startsWith("turbo_cli_")) {
923
+ const { data: data2, error: error2 } = await withSpinner("Verifying token...", async () => {
924
+ const result2 = await authService.validateToken(options.token);
925
+ if (result2.valid && result2.user) {
926
+ return { data: result2.user, error: void 0 };
927
+ }
928
+ return { data: void 0, error: "Invalid token" };
929
+ });
930
+ if (error2 || !data2) {
931
+ configService.clearToken();
932
+ logger.error(`Authentication failed: ${error2 || "Invalid token"}`);
933
+ process.exit(10 /* AUTH_ERROR */);
934
+ }
935
+ configService.setUserId(data2.id);
936
+ addJsonData({
937
+ authenticated: true,
938
+ user: { email: data2.email, id: data2.id }
939
+ });
940
+ logger.success(`Authenticated as ${data2.email}`);
941
+ return;
942
+ }
943
+ const { data, error } = await withSpinner(
664
944
  "Verifying token...",
665
945
  () => apiClient.whoami()
666
946
  );
667
- if (error2 || !data2) {
947
+ if (error || !data) {
668
948
  configService.clearToken();
669
- logger.error(`Authentication failed: ${error2 || "Invalid token"}`);
949
+ logger.error(`Authentication failed: ${error || "Invalid token"}`);
670
950
  process.exit(10 /* AUTH_ERROR */);
671
951
  }
672
- configService.setUserId(data2.id);
952
+ configService.setUserId(data.id);
673
953
  addJsonData({
674
954
  authenticated: true,
675
- user: { id: data2.id, email: data2.email }
955
+ user: { email: data.email, id: data.id }
676
956
  });
677
- logger.success(`Authenticated as ${data2.email}`);
678
- return;
679
- }
680
- const response = await prompts([
681
- {
682
- type: "text",
683
- name: "email",
684
- message: "Email:",
685
- validate: (value) => value.includes("@") || "Please enter a valid email"
686
- },
687
- {
688
- type: "password",
689
- name: "password",
690
- message: "Password:"
691
- }
692
- ]);
693
- if (!response.email || !response.password) {
694
- logger.warning("Login cancelled");
695
- addJsonData({ authenticated: false, cancelled: true });
957
+ logger.success(`Authenticated as ${data.email}`);
696
958
  return;
697
959
  }
698
- const { data, error } = await withSpinner(
699
- "Logging in...",
700
- () => apiClient.login(response.email, response.password)
701
- );
702
- if (error || !data) {
703
- logger.error(`Login failed: ${error || "Unknown error"}`);
704
- addJsonData({ authenticated: false, error: error || "Unknown error" });
960
+ const result = await authService.browserLogin();
961
+ if (!result.success) {
962
+ logger.error(`Login failed: ${result.error || "Unknown error"}`);
963
+ addJsonData({ authenticated: false, error: result.error || "Unknown error" });
705
964
  process.exit(10 /* AUTH_ERROR */);
706
965
  }
707
- configService.setToken(data.token);
708
- configService.setUserId(data.user.id);
709
966
  addJsonData({
710
967
  authenticated: true,
711
- user: { id: data.user.id, email: data.user.email }
968
+ user: result.user ? { email: result.user.email, fullName: result.user.fullName, id: result.user.id } : void 0
712
969
  });
713
- logger.success(`Logged in as ${data.user.email}`);
714
970
  logger.newline();
715
- logger.info("Your credentials have been saved.");
716
- logger.info("Run `turbo whoami` to verify your authentication.");
971
+ logger.success(`Angemeldet als ${result.user?.email || "Unknown"}`);
972
+ logger.newline();
973
+ logger.info("Ihre Anmeldedaten wurden gespeichert.");
974
+ logger.info("F\xFChren Sie `turbo whoami` aus, um Ihre Authentifizierung zu \xFCberpr\xFCfen.");
717
975
  });
718
976
  var logoutCommand = new Command("logout").description("Remove stored authentication").action(() => {
719
977
  configService.clearToken();
@@ -726,6 +984,36 @@ var whoamiCommand = new Command("whoami").description("Show current authenticate
726
984
  addJsonData({ authenticated: false });
727
985
  process.exit(10 /* AUTH_ERROR */);
728
986
  }
987
+ const token = configService.getToken();
988
+ if (token?.startsWith("turbo_cli_")) {
989
+ const { data: data2, error: error2 } = await withSpinner("Fetching user info...", async () => {
990
+ const result = await authService.validateToken(token);
991
+ if (result.valid && result.user) {
992
+ return { data: result.user, error: void 0 };
993
+ }
994
+ return { data: void 0, error: "Invalid token" };
995
+ });
996
+ if (error2 || !data2) {
997
+ logger.error(`Failed to fetch user info: ${error2 || "Unknown error"}`);
998
+ addJsonData({ authenticated: false, error: error2 || "Unknown error" });
999
+ process.exit(13 /* API_ERROR */);
1000
+ }
1001
+ addJsonData({
1002
+ authenticated: true,
1003
+ user: {
1004
+ email: data2.email,
1005
+ id: data2.id,
1006
+ name: data2.fullName || null
1007
+ }
1008
+ });
1009
+ logger.header("Current User");
1010
+ logger.table({
1011
+ Email: data2.email,
1012
+ ID: data2.id,
1013
+ Name: data2.fullName || "-"
1014
+ });
1015
+ return;
1016
+ }
729
1017
  const { data, error } = await withSpinner(
730
1018
  "Fetching user info...",
731
1019
  () => apiClient.whoami()
@@ -738,15 +1026,15 @@ var whoamiCommand = new Command("whoami").description("Show current authenticate
738
1026
  addJsonData({
739
1027
  authenticated: true,
740
1028
  user: {
741
- id: data.id,
742
1029
  email: data.email,
1030
+ id: data.id,
743
1031
  name: data.name || null
744
1032
  }
745
1033
  });
746
1034
  logger.header("Current User");
747
1035
  logger.table({
748
- ID: data.id,
749
1036
  Email: data.email,
1037
+ ID: data.id,
750
1038
  Name: data.name || "-"
751
1039
  });
752
1040
  });
@@ -1034,11 +1322,77 @@ var statusCommand = new Command3("status").description("Show deployment status")
1034
1322
  });
1035
1323
  }
1036
1324
  });
1037
- var configCommand = new Command3("config").description("Show current configuration").action(() => {
1038
- const config2 = configService.getAll();
1039
- addJsonData({ config: config2 });
1325
+ var configCommand = new Command3("config").description("Manage configuration");
1326
+ configCommand.command("show", { isDefault: true }).description("Show current configuration").action(() => {
1327
+ const config = configService.getAll();
1328
+ addJsonData({ config });
1040
1329
  configService.show();
1041
1330
  });
1331
+ configCommand.command("set").description("Set a configuration value").argument("<key>", "Configuration key (token, project)").argument("<value>", "Value to set").action((key, value) => {
1332
+ switch (key.toLowerCase()) {
1333
+ case "token":
1334
+ configService.setToken(value);
1335
+ logger.success("Token saved (global)");
1336
+ addJsonData({ key: "token", saved: true, scope: "global" });
1337
+ break;
1338
+ case "project":
1339
+ configService.setProject(value);
1340
+ logger.success(`Project saved to .turboops.json`);
1341
+ addJsonData({ key: "project", value, saved: true, scope: "local" });
1342
+ break;
1343
+ default:
1344
+ logger.error(`Unknown config key: ${key}`);
1345
+ logger.info("Available keys: token, project");
1346
+ addJsonData({ error: `Unknown config key: ${key}` });
1347
+ process.exit(14 /* VALIDATION_ERROR */);
1348
+ }
1349
+ });
1350
+ configCommand.command("get").description("Get a configuration value").argument("<key>", "Configuration key (token, project, api-url)").action((key) => {
1351
+ let value = null;
1352
+ switch (key.toLowerCase()) {
1353
+ case "token":
1354
+ value = configService.getToken();
1355
+ if (value) {
1356
+ console.log("***");
1357
+ addJsonData({ key: "token", set: true });
1358
+ } else {
1359
+ console.log("(not set)");
1360
+ addJsonData({ key: "token", set: false });
1361
+ }
1362
+ return;
1363
+ case "project":
1364
+ value = configService.getProject();
1365
+ break;
1366
+ case "api-url":
1367
+ value = configService.getApiUrl();
1368
+ break;
1369
+ default:
1370
+ logger.error(`Unknown config key: ${key}`);
1371
+ logger.info("Available keys: token, project, api-url");
1372
+ addJsonData({ error: `Unknown config key: ${key}` });
1373
+ process.exit(14 /* VALIDATION_ERROR */);
1374
+ }
1375
+ console.log(value || "(not set)");
1376
+ addJsonData({ key, value });
1377
+ });
1378
+ configCommand.command("clear").description("Clear configuration").option("--all", "Clear all configuration").option("--token", "Clear token only").option("--project", "Clear project only").action((opts) => {
1379
+ if (opts.all) {
1380
+ configService.clearAll();
1381
+ logger.success("All configuration cleared");
1382
+ addJsonData({ cleared: "all" });
1383
+ } else if (opts.token) {
1384
+ configService.clearToken();
1385
+ logger.success("Token cleared");
1386
+ addJsonData({ cleared: "token" });
1387
+ } else if (opts.project) {
1388
+ configService.clearProject();
1389
+ logger.success("Project configuration cleared");
1390
+ addJsonData({ cleared: "project" });
1391
+ } else {
1392
+ logger.error("Specify what to clear: --all, --token, or --project");
1393
+ process.exit(14 /* VALIDATION_ERROR */);
1394
+ }
1395
+ });
1042
1396
  function getStatusColor(status) {
1043
1397
  switch (status) {
1044
1398
  case "healthy":
@@ -1151,130 +1505,377 @@ envCommand.addCommand(envUnsetCommand);
1151
1505
 
1152
1506
  // src/commands/init.ts
1153
1507
  import { Command as Command5 } from "commander";
1154
- import prompts2 from "prompts";
1508
+ import prompts from "prompts";
1155
1509
  import chalk6 from "chalk";
1156
1510
  var initCommand = new Command5("init").description("Initialize TurboOps project in current directory").action(async () => {
1157
1511
  logger.header("TurboOps Project Initialization");
1158
1512
  if (!configService.isAuthenticated()) {
1159
- logger.warning("Not authenticated. Please log in first.");
1513
+ logger.warning("Nicht authentifiziert. Bitte melden Sie sich zuerst an.");
1160
1514
  logger.newline();
1161
- const { shouldLogin } = await prompts2({
1162
- type: "confirm",
1515
+ const { shouldLogin } = await prompts({
1516
+ initial: true,
1517
+ message: "M\xF6chten Sie sich jetzt anmelden?",
1163
1518
  name: "shouldLogin",
1164
- message: "Would you like to log in now?",
1165
- initial: true
1519
+ type: "confirm"
1166
1520
  });
1167
1521
  if (!shouldLogin) {
1168
- logger.info("Run `turbo login` when ready.");
1522
+ logger.info("F\xFChren Sie `turbo login` aus, wenn Sie bereit sind.");
1169
1523
  addJsonData({ initialized: false, reason: "not_authenticated" });
1170
1524
  return;
1171
1525
  }
1172
- const credentials = await prompts2([
1173
- {
1174
- type: "text",
1175
- name: "email",
1176
- message: "Email:",
1177
- validate: (value) => value.includes("@") || "Please enter a valid email"
1178
- },
1179
- {
1180
- type: "password",
1181
- name: "password",
1182
- message: "Password:"
1183
- }
1184
- ]);
1185
- if (!credentials.email || !credentials.password) {
1186
- logger.warning("Login cancelled");
1187
- addJsonData({ initialized: false, reason: "login_cancelled" });
1188
- return;
1189
- }
1190
- const { data, error } = await withSpinner(
1191
- "Logging in...",
1192
- () => apiClient.login(credentials.email, credentials.password)
1193
- );
1194
- if (error || !data) {
1195
- logger.error(`Login failed: ${error || "Unknown error"}`);
1526
+ const result = await authService.browserLogin();
1527
+ if (!result.success) {
1528
+ logger.error(`Anmeldung fehlgeschlagen: ${result.error || "Unbekannter Fehler"}`);
1196
1529
  addJsonData({
1530
+ error: result.error || "Unknown error",
1197
1531
  initialized: false,
1198
- reason: "login_failed",
1199
- error: error || "Unknown error"
1532
+ reason: "login_failed"
1200
1533
  });
1201
1534
  process.exit(10 /* AUTH_ERROR */);
1202
1535
  }
1203
- configService.setToken(data.token);
1204
- configService.setUserId(data.user.id);
1205
- logger.success(`Logged in as ${data.user.email}`);
1536
+ logger.success(`Angemeldet als ${result.user?.email || "Unknown"}`);
1206
1537
  }
1207
1538
  logger.newline();
1208
- const { projectSlug } = await prompts2({
1209
- type: "text",
1539
+ const { projectSlug } = await prompts({
1540
+ hint: "Der Slug Ihres TurboOps-Projekts",
1541
+ message: "Projekt-Slug:",
1210
1542
  name: "projectSlug",
1211
- message: "Project slug:",
1212
- hint: "The slug of your TurboOps project",
1213
- validate: (value) => value.length > 0 || "Project slug is required"
1543
+ type: "text",
1544
+ validate: (value) => value.length > 0 || "Projekt-Slug ist erforderlich"
1214
1545
  });
1215
1546
  if (!projectSlug) {
1216
- logger.warning("Initialization cancelled");
1547
+ logger.warning("Initialisierung abgebrochen");
1217
1548
  addJsonData({ initialized: false, reason: "cancelled" });
1218
1549
  return;
1219
1550
  }
1220
1551
  const { data: project, error: projectError } = await withSpinner(
1221
- "Verifying project...",
1552
+ "Suche Projekt...",
1222
1553
  () => apiClient.getProject(projectSlug)
1223
1554
  );
1224
- if (projectError || !project) {
1225
- logger.error(
1226
- `Project "${projectSlug}" not found or you don't have access.`
1227
- );
1228
- logger.info(
1229
- "Make sure the project exists in TurboOps and you have permission to access it."
1230
- );
1555
+ if (project) {
1556
+ await setupProject(project);
1557
+ return;
1558
+ }
1559
+ logger.newline();
1560
+ logger.warning(`Projekt "${projectSlug}" nicht gefunden.`);
1561
+ const { shouldCreate } = await prompts({
1562
+ initial: true,
1563
+ message: "M\xF6chten Sie ein neues Projekt anlegen?",
1564
+ name: "shouldCreate",
1565
+ type: "confirm"
1566
+ });
1567
+ if (!shouldCreate) {
1568
+ logger.info("Initialisierung abgebrochen.");
1569
+ addJsonData({ initialized: false, reason: "project_not_found" });
1570
+ return;
1571
+ }
1572
+ await createNewProject(projectSlug);
1573
+ });
1574
+ async function setupProject(project) {
1575
+ configService.setProject(project.slug);
1576
+ const { data: environments } = await apiClient.getEnvironments(project.id);
1577
+ if (!environments || environments.length === 0) {
1578
+ logger.newline();
1579
+ const { shouldCreateStage } = await prompts({
1580
+ initial: true,
1581
+ message: "Das Projekt hat noch keine Stages. M\xF6chten Sie jetzt eine erstellen?",
1582
+ name: "shouldCreateStage",
1583
+ type: "confirm"
1584
+ });
1585
+ if (shouldCreateStage) {
1586
+ await createFirstStage(project.id, project.slug);
1587
+ }
1588
+ }
1589
+ const fs2 = await import("fs/promises");
1590
+ const path2 = await import("path");
1591
+ const gitlabCiPath = path2.join(process.cwd(), ".gitlab-ci.yml");
1592
+ let hasGitLabPipeline = false;
1593
+ try {
1594
+ await fs2.access(gitlabCiPath);
1595
+ hasGitLabPipeline = true;
1596
+ } catch {
1597
+ }
1598
+ if (!hasGitLabPipeline) {
1599
+ logger.newline();
1600
+ const { shouldCreatePipeline } = await prompts({
1601
+ initial: false,
1602
+ message: "M\xF6chten Sie eine CI/CD Pipeline anlegen?",
1603
+ name: "shouldCreatePipeline",
1604
+ type: "confirm"
1605
+ });
1606
+ if (shouldCreatePipeline) {
1607
+ await createPipeline(project.id);
1608
+ }
1609
+ }
1610
+ await showFinalSummary(project);
1611
+ }
1612
+ async function createNewProject(slug) {
1613
+ logger.newline();
1614
+ logger.header("Neues Projekt erstellen");
1615
+ const { data: customers } = await withSpinner(
1616
+ "Lade verf\xFCgbare Kunden...",
1617
+ () => apiClient.getCustomers()
1618
+ );
1619
+ const promptQuestions = [
1620
+ {
1621
+ initial: slug.charAt(0).toUpperCase() + slug.slice(1).replace(/-/g, " "),
1622
+ message: "Projektname:",
1623
+ name: "name",
1624
+ type: "text",
1625
+ validate: (value) => value.length > 0 || "Projektname ist erforderlich"
1626
+ }
1627
+ ];
1628
+ if (customers && customers.length > 0) {
1629
+ promptQuestions.push({
1630
+ choices: [
1631
+ { title: "(Kein Kunde)", value: "" },
1632
+ ...customers.map((c) => ({ title: c.name, value: c.id }))
1633
+ ],
1634
+ message: "Kunde (optional):",
1635
+ name: "customer",
1636
+ type: "select"
1637
+ });
1638
+ }
1639
+ promptQuestions.push(
1640
+ {
1641
+ message: "Beschreibung (optional):",
1642
+ name: "description",
1643
+ type: "text"
1644
+ },
1645
+ {
1646
+ message: "Repository URL (optional):",
1647
+ name: "repositoryUrl",
1648
+ type: "text"
1649
+ }
1650
+ );
1651
+ const projectDetails = await prompts(promptQuestions);
1652
+ if (!projectDetails.name) {
1653
+ logger.warning("Projekterstellung abgebrochen");
1654
+ addJsonData({ initialized: false, reason: "cancelled" });
1655
+ return;
1656
+ }
1657
+ const { data: newProject, error: createError } = await withSpinner(
1658
+ "Erstelle Projekt...",
1659
+ () => apiClient.createProject({
1660
+ customer: projectDetails.customer || void 0,
1661
+ description: projectDetails.description || void 0,
1662
+ name: projectDetails.name,
1663
+ repositoryUrl: projectDetails.repositoryUrl || void 0,
1664
+ slug
1665
+ })
1666
+ );
1667
+ if (createError || !newProject) {
1668
+ logger.error(`Projekt konnte nicht erstellt werden: ${createError || "Unbekannter Fehler"}`);
1231
1669
  addJsonData({
1670
+ error: createError || "Unknown error",
1232
1671
  initialized: false,
1233
- reason: "project_not_found",
1234
- projectSlug
1672
+ reason: "create_failed"
1235
1673
  });
1236
- process.exit(12 /* NOT_FOUND */);
1674
+ process.exit(13 /* API_ERROR */);
1675
+ }
1676
+ logger.success(`Projekt "${newProject.name}" wurde erstellt!`);
1677
+ configService.setProject(newProject.slug);
1678
+ logger.newline();
1679
+ const { shouldCreateStage } = await prompts({
1680
+ initial: true,
1681
+ message: "M\xF6chten Sie jetzt die erste Stage anlegen?",
1682
+ name: "shouldCreateStage",
1683
+ type: "confirm"
1684
+ });
1685
+ if (shouldCreateStage) {
1686
+ await createFirstStage(newProject.id, newProject.slug);
1687
+ }
1688
+ logger.newline();
1689
+ const { shouldCreatePipeline } = await prompts({
1690
+ initial: true,
1691
+ message: "M\xF6chten Sie eine CI/CD Pipeline anlegen?",
1692
+ name: "shouldCreatePipeline",
1693
+ type: "confirm"
1694
+ });
1695
+ if (shouldCreatePipeline) {
1696
+ await createPipeline(newProject.id);
1697
+ }
1698
+ await showFinalSummary(newProject);
1699
+ }
1700
+ async function createFirstStage(projectId, projectSlug) {
1701
+ logger.newline();
1702
+ logger.header("Erste Stage erstellen");
1703
+ const { data: servers } = await withSpinner(
1704
+ "Lade verf\xFCgbare Server...",
1705
+ () => apiClient.getDeploymentServers()
1706
+ );
1707
+ const stageTypes = [
1708
+ { title: "Development", value: "development" },
1709
+ { title: "Staging", value: "staging" },
1710
+ { title: "Production", value: "production" }
1711
+ ];
1712
+ const stageQuestions = [
1713
+ {
1714
+ choices: stageTypes,
1715
+ initial: 0,
1716
+ message: "Stage-Typ:",
1717
+ name: "type",
1718
+ type: "select"
1719
+ },
1720
+ {
1721
+ initial: (prev) => prev === "production" ? "Production" : prev === "staging" ? "Staging" : "Development",
1722
+ message: "Stage-Name:",
1723
+ name: "name",
1724
+ type: "text",
1725
+ validate: (value) => value.length > 0 || "Name ist erforderlich"
1726
+ },
1727
+ {
1728
+ initial: (_prev, values) => values.type || "",
1729
+ message: "Stage-Slug:",
1730
+ name: "slug",
1731
+ type: "text",
1732
+ validate: (value) => value.length > 0 || "Slug ist erforderlich"
1733
+ },
1734
+ {
1735
+ initial: (_prev, values) => `${values.slug || ""}.${projectSlug}.example.com`,
1736
+ message: "Domain:",
1737
+ name: "domain",
1738
+ type: "text"
1739
+ },
1740
+ {
1741
+ initial: (_prev, values) => values.type === "production" ? "main" : values.type === "staging" ? "staging" : "develop",
1742
+ message: "Branch:",
1743
+ name: "branch",
1744
+ type: "text"
1745
+ }
1746
+ ];
1747
+ if (servers && servers.length > 0) {
1748
+ stageQuestions.push({
1749
+ choices: [
1750
+ { title: "(Sp\xE4ter ausw\xE4hlen)", value: "" },
1751
+ ...servers.map((s) => ({ title: `${s.name} (${s.host})`, value: s.id }))
1752
+ ],
1753
+ message: "Server (optional):",
1754
+ name: "server",
1755
+ type: "select"
1756
+ });
1757
+ }
1758
+ const stageDetails = await prompts(stageQuestions);
1759
+ if (!stageDetails.name || !stageDetails.slug) {
1760
+ logger.warning("Stage-Erstellung abgebrochen");
1761
+ return;
1762
+ }
1763
+ const { data: newStage, error: stageError } = await withSpinner(
1764
+ "Erstelle Stage...",
1765
+ () => apiClient.createStage({
1766
+ project: projectId,
1767
+ name: stageDetails.name,
1768
+ slug: stageDetails.slug,
1769
+ type: stageDetails.type,
1770
+ domain: stageDetails.domain || void 0,
1771
+ server: stageDetails.server || void 0,
1772
+ branch: stageDetails.branch || void 0
1773
+ })
1774
+ );
1775
+ if (stageError || !newStage) {
1776
+ logger.error(`Stage konnte nicht erstellt werden: ${stageError || "Unbekannter Fehler"}`);
1777
+ return;
1778
+ }
1779
+ logger.success(`Stage "${newStage.name}" wurde erstellt!`);
1780
+ }
1781
+ async function createPipeline(projectId) {
1782
+ logger.newline();
1783
+ logger.header("CI/CD Pipeline erstellen");
1784
+ const pipelineQuestions = [
1785
+ {
1786
+ choices: [
1787
+ { title: "GitLab CI/CD", value: "gitlab" },
1788
+ { title: "GitHub Actions", value: "github" }
1789
+ ],
1790
+ initial: 0,
1791
+ message: "Pipeline-Typ:",
1792
+ name: "pipelineType",
1793
+ type: "select"
1794
+ }
1795
+ ];
1796
+ const pipelineDetails = await prompts(pipelineQuestions);
1797
+ if (!pipelineDetails.pipelineType) {
1798
+ logger.warning("Pipeline-Erstellung abgebrochen");
1799
+ return;
1800
+ }
1801
+ const { data: pipeline, error } = await withSpinner(
1802
+ "Generiere Pipeline...",
1803
+ () => apiClient.generatePipeline(projectId, pipelineDetails.pipelineType)
1804
+ );
1805
+ if (error || !pipeline) {
1806
+ logger.error(`Pipeline konnte nicht generiert werden: ${error || "Unbekannter Fehler"}`);
1807
+ return;
1808
+ }
1809
+ const fs2 = await import("fs/promises");
1810
+ const path2 = await import("path");
1811
+ let filePath;
1812
+ if (pipelineDetails.pipelineType === "github") {
1813
+ const workflowsDir = path2.join(process.cwd(), ".github", "workflows");
1814
+ await fs2.mkdir(workflowsDir, { recursive: true });
1815
+ filePath = path2.join(workflowsDir, "deploy.yml");
1816
+ } else {
1817
+ filePath = path2.join(process.cwd(), ".gitlab-ci.yml");
1818
+ }
1819
+ try {
1820
+ await fs2.writeFile(filePath, pipeline.content, "utf-8");
1821
+ logger.success(`${pipeline.filename} wurde erstellt!`);
1822
+ logger.newline();
1823
+ const { data: secrets } = await apiClient.getPipelineSecrets(projectId, pipelineDetails.pipelineType);
1824
+ if (secrets && secrets.length > 0) {
1825
+ logger.header("Erforderliche CI/CD Secrets");
1826
+ for (const secret of secrets) {
1827
+ const value = secret.isSecret ? chalk6.dim("(geheim)") : chalk6.cyan(secret.value || "-");
1828
+ console.log(` ${chalk6.bold(secret.name)}: ${value}`);
1829
+ console.log(` ${chalk6.dim(secret.description)}`);
1830
+ }
1831
+ logger.newline();
1832
+ logger.info("F\xFCgen Sie diese Werte als CI/CD Secrets/Variables hinzu.");
1833
+ logger.info("Projekt-Token k\xF6nnen Sie in der TurboOps Web-UI erstellen.");
1834
+ }
1835
+ } catch (error2) {
1836
+ logger.error(`Fehler beim Schreiben der Pipeline-Datei: ${error2}`);
1237
1837
  }
1238
- configService.setProject(projectSlug);
1838
+ }
1839
+ async function showFinalSummary(project) {
1239
1840
  logger.newline();
1240
- logger.success("Project initialized!");
1841
+ logger.success("Projekt initialisiert!");
1241
1842
  logger.newline();
1242
- logger.header("Project Details");
1843
+ logger.header("Projektdetails");
1243
1844
  logger.table({
1244
1845
  Name: project.name,
1245
- Slug: project.slug,
1246
- Repository: project.repositoryUrl || "-"
1846
+ Repository: project.repositoryUrl || "-",
1847
+ Slug: project.slug
1247
1848
  });
1248
1849
  const { data: environments } = await apiClient.getEnvironments(project.id);
1249
1850
  const envList = environments && environments.length > 0 ? environments.map((env) => ({
1250
- slug: env.slug,
1251
1851
  name: env.name,
1852
+ slug: env.slug,
1252
1853
  type: env.type
1253
1854
  })) : [];
1254
1855
  addJsonData({
1856
+ environments: envList,
1255
1857
  initialized: true,
1256
1858
  project: {
1257
1859
  name: project.name,
1258
- slug: project.slug,
1259
- repositoryUrl: project.repositoryUrl || null
1260
- },
1261
- environments: envList
1860
+ repositoryUrl: project.repositoryUrl || null,
1861
+ slug: project.slug
1862
+ }
1262
1863
  });
1263
1864
  if (environments && environments.length > 0) {
1264
1865
  logger.newline();
1265
- logger.header("Available Environments");
1866
+ logger.header("Verf\xFCgbare Stages");
1266
1867
  for (const env of environments) {
1267
1868
  console.log(` ${chalk6.bold(env.slug)} - ${env.name} (${env.type})`);
1268
1869
  }
1269
1870
  }
1270
1871
  logger.newline();
1271
- logger.header("Next Steps");
1872
+ logger.header("N\xE4chste Schritte");
1272
1873
  logger.list([
1273
- "Run `turbo status` to see all environments",
1274
- "Run `turbo deploy <environment>` to deploy",
1275
- "Run `turbo logs <environment>` to view logs"
1874
+ "F\xFChren Sie `turbo status` aus, um alle Stages zu sehen",
1875
+ "F\xFChren Sie `turbo deploy <stage>` aus, um zu deployen",
1876
+ "F\xFChren Sie `turbo logs <stage>` aus, um Logs anzuzeigen"
1276
1877
  ]);
1277
- });
1878
+ }
1278
1879
 
1279
1880
  // src/commands/logs.ts
1280
1881
  import { Command as Command6 } from "commander";
@@ -1439,7 +2040,7 @@ function getLevelColor(level) {
1439
2040
  var VERSION = getCurrentVersion();
1440
2041
  var shouldCheckUpdate = true;
1441
2042
  var program = new Command7();
1442
- program.name("turbo").description("TurboCLI - Command line interface for TurboOps deployments").version(VERSION, "-v, --version", "Show version number").option("--project <slug>", "Override project slug").option("--token <token>", "Override API token").option("--api <url>", "Override API URL").option("--json", "Output as JSON").option("--quiet", "Only show errors").option("--verbose", "Show debug output").option("--no-update-check", "Skip version check");
2043
+ program.name("turbo").description("TurboCLI - Command line interface for TurboOps deployments").version(VERSION, "-v, --version", "Show version number").option("--project <slug>", "Override project slug").option("--token <token>", "Override API token").option("--json", "Output as JSON").option("--quiet", "Only show errors").option("--verbose", "Show debug output").option("--no-update-check", "Skip version check");
1443
2044
  program.hook("preAction", (thisCommand) => {
1444
2045
  const opts = thisCommand.opts();
1445
2046
  clearJsonData();
@@ -1456,9 +2057,6 @@ program.hook("preAction", (thisCommand) => {
1456
2057
  if (opts.token) {
1457
2058
  configService.setToken(opts.token);
1458
2059
  }
1459
- if (opts.api) {
1460
- configService.setApiUrl(opts.api);
1461
- }
1462
2060
  if (opts.verbose) {
1463
2061
  process.env.DEBUG = "true";
1464
2062
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turboops/cli",
3
- "version": "1.0.0-dev.579",
3
+ "version": "1.0.0-dev.582",
4
4
  "description": "TurboCLI - Command line interface for TurboOps deployments",
5
5
  "author": "lenne.tech GmbH",
6
6
  "license": "MIT",
@@ -26,6 +26,7 @@
26
26
  "chalk": "^5.3.0",
27
27
  "commander": "^12.1.0",
28
28
  "conf": "^13.0.1",
29
+ "open": "^10.1.0",
29
30
  "ora": "^8.0.1",
30
31
  "prompts": "^2.4.2",
31
32
  "socket.io-client": "^4.7.0"