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

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 +250 -101
  2. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -196,6 +196,10 @@ var API_URLS = {
196
196
  production: "https://api.turbo-ops.de",
197
197
  dev: "https://api.dev.turbo-ops.de"
198
198
  };
199
+ var APP_URLS = {
200
+ production: "https://turbo-ops.de",
201
+ dev: "https://dev.turbo-ops.de"
202
+ };
199
203
  function getDefaultApiUrl() {
200
204
  const version = getCurrentVersion();
201
205
  const isDevVersion = version.includes("-dev");
@@ -232,6 +236,15 @@ var configService = {
232
236
  setApiUrl(url) {
233
237
  config.set("apiUrl", url);
234
238
  },
239
+ /**
240
+ * Get App URL (frontend)
241
+ * Returns the appropriate frontend URL based on CLI version
242
+ */
243
+ getAppUrl() {
244
+ const version = getCurrentVersion();
245
+ const isDevVersion = version.includes("-dev");
246
+ return isDevVersion ? APP_URLS.dev : APP_URLS.production;
247
+ },
235
248
  /**
236
249
  * Get authentication token
237
250
  */
@@ -336,7 +349,6 @@ var configService = {
336
349
 
337
350
  // src/commands/login.ts
338
351
  import { Command } from "commander";
339
- import prompts from "prompts";
340
352
 
341
353
  // src/services/api.ts
342
354
  function isProjectToken(token) {
@@ -634,6 +646,137 @@ var apiClient = {
634
646
  }
635
647
  };
636
648
 
649
+ // src/services/auth.ts
650
+ import os from "os";
651
+ var authService = {
652
+ /**
653
+ * Start browser-based authentication flow
654
+ * Returns the auth code and URL for the browser
655
+ */
656
+ async initAuth() {
657
+ const apiUrl = configService.getApiUrl();
658
+ const version = getCurrentVersion();
659
+ const platform = os.platform();
660
+ const hostname = os.hostname();
661
+ try {
662
+ const response = await fetch(`${apiUrl}/cli/auth/init`, {
663
+ body: JSON.stringify({
664
+ cliVersion: version,
665
+ hostname,
666
+ platform
667
+ }),
668
+ headers: {
669
+ "Content-Type": "application/json"
670
+ },
671
+ method: "POST"
672
+ });
673
+ if (!response.ok) {
674
+ return null;
675
+ }
676
+ return await response.json();
677
+ } catch {
678
+ return null;
679
+ }
680
+ },
681
+ /**
682
+ * Poll for authentication status
683
+ */
684
+ async pollAuth(code, pollToken) {
685
+ const apiUrl = configService.getApiUrl();
686
+ try {
687
+ const response = await fetch(`${apiUrl}/cli/auth/poll/${code}`, {
688
+ headers: {
689
+ "X-Poll-Token": pollToken
690
+ }
691
+ });
692
+ if (!response.ok) {
693
+ return { status: "expired" };
694
+ }
695
+ return await response.json();
696
+ } catch {
697
+ return { status: "expired" };
698
+ }
699
+ },
700
+ /**
701
+ * Validate a CLI token
702
+ */
703
+ async validateToken(token) {
704
+ const apiUrl = configService.getApiUrl();
705
+ try {
706
+ const response = await fetch(`${apiUrl}/cli/auth/validate`, {
707
+ headers: {
708
+ Authorization: `Bearer ${token}`
709
+ }
710
+ });
711
+ if (!response.ok) {
712
+ return { valid: false };
713
+ }
714
+ return await response.json();
715
+ } catch {
716
+ return { valid: false };
717
+ }
718
+ },
719
+ /**
720
+ * Perform browser-based login
721
+ * Opens browser, waits for user to authorize, returns user info
722
+ */
723
+ async browserLogin() {
724
+ const open = (await import("open")).default;
725
+ logger.info("Initialisiere Anmeldung...");
726
+ const authInit = await this.initAuth();
727
+ if (!authInit) {
728
+ return { error: "Konnte Authentifizierung nicht initialisieren", success: false };
729
+ }
730
+ logger.newline();
731
+ logger.info("Bitte autorisieren Sie die CLI in Ihrem Browser.");
732
+ logger.newline();
733
+ logger.info(`Autorisierungscode: ${authInit.code}`);
734
+ logger.newline();
735
+ try {
736
+ await open(authInit.authUrl);
737
+ logger.info("Browser wurde ge\xF6ffnet...");
738
+ } catch {
739
+ logger.warning("Browser konnte nicht ge\xF6ffnet werden.");
740
+ logger.info(`Bitte \xF6ffnen Sie diese URL manuell: ${authInit.authUrl}`);
741
+ }
742
+ logger.newline();
743
+ logger.info("Warte auf Autorisierung...");
744
+ const maxAttempts = 150;
745
+ const pollInterval = 2e3;
746
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
747
+ const result = await this.pollAuth(authInit.code, authInit.pollToken);
748
+ if (result.status === "authorized" && result.token) {
749
+ configService.setToken(result.token);
750
+ if (result.user) {
751
+ configService.setUserId(result.user.id);
752
+ }
753
+ return {
754
+ success: true,
755
+ user: result.user
756
+ };
757
+ }
758
+ if (result.status === "expired" || result.status === "consumed") {
759
+ return { error: "Autorisierungscode abgelaufen oder bereits verwendet", success: false };
760
+ }
761
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
762
+ }
763
+ return { error: "Zeit\xFCberschreitung bei der Anmeldung", success: false };
764
+ },
765
+ /**
766
+ * Check if current token is a CLI token and validate it
767
+ */
768
+ async checkCliToken() {
769
+ const token = configService.getToken();
770
+ if (!token) {
771
+ return { valid: false };
772
+ }
773
+ if (!token.startsWith("turbo_cli_")) {
774
+ return { valid: true };
775
+ }
776
+ return await this.validateToken(token);
777
+ }
778
+ };
779
+
637
780
  // src/utils/spinner.ts
638
781
  import ora from "ora";
639
782
  function createSpinner(text) {
@@ -660,60 +803,59 @@ var loginCommand = new Command("login").description("Authenticate with TurboOps
660
803
  logger.header("TurboOps Login");
661
804
  if (options.token) {
662
805
  configService.setToken(options.token);
663
- const { data: data2, error: error2 } = await withSpinner(
806
+ if (options.token.startsWith("turbo_cli_")) {
807
+ const { data: data2, error: error2 } = await withSpinner("Verifying token...", async () => {
808
+ const result2 = await authService.validateToken(options.token);
809
+ if (result2.valid && result2.user) {
810
+ return { data: result2.user, error: void 0 };
811
+ }
812
+ return { data: void 0, error: "Invalid token" };
813
+ });
814
+ if (error2 || !data2) {
815
+ configService.clearToken();
816
+ logger.error(`Authentication failed: ${error2 || "Invalid token"}`);
817
+ process.exit(10 /* AUTH_ERROR */);
818
+ }
819
+ configService.setUserId(data2.id);
820
+ addJsonData({
821
+ authenticated: true,
822
+ user: { email: data2.email, id: data2.id }
823
+ });
824
+ logger.success(`Authenticated as ${data2.email}`);
825
+ return;
826
+ }
827
+ const { data, error } = await withSpinner(
664
828
  "Verifying token...",
665
829
  () => apiClient.whoami()
666
830
  );
667
- if (error2 || !data2) {
831
+ if (error || !data) {
668
832
  configService.clearToken();
669
- logger.error(`Authentication failed: ${error2 || "Invalid token"}`);
833
+ logger.error(`Authentication failed: ${error || "Invalid token"}`);
670
834
  process.exit(10 /* AUTH_ERROR */);
671
835
  }
672
- configService.setUserId(data2.id);
836
+ configService.setUserId(data.id);
673
837
  addJsonData({
674
838
  authenticated: true,
675
- user: { id: data2.id, email: data2.email }
839
+ user: { email: data.email, id: data.id }
676
840
  });
677
- logger.success(`Authenticated as ${data2.email}`);
841
+ logger.success(`Authenticated as ${data.email}`);
678
842
  return;
679
843
  }
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 });
696
- return;
697
- }
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" });
844
+ const result = await authService.browserLogin();
845
+ if (!result.success) {
846
+ logger.error(`Login failed: ${result.error || "Unknown error"}`);
847
+ addJsonData({ authenticated: false, error: result.error || "Unknown error" });
705
848
  process.exit(10 /* AUTH_ERROR */);
706
849
  }
707
- configService.setToken(data.token);
708
- configService.setUserId(data.user.id);
709
850
  addJsonData({
710
851
  authenticated: true,
711
- user: { id: data.user.id, email: data.user.email }
852
+ user: result.user ? { email: result.user.email, fullName: result.user.fullName, id: result.user.id } : void 0
712
853
  });
713
- logger.success(`Logged in as ${data.user.email}`);
714
854
  logger.newline();
715
- logger.info("Your credentials have been saved.");
716
- logger.info("Run `turbo whoami` to verify your authentication.");
855
+ logger.success(`Angemeldet als ${result.user?.email || "Unknown"}`);
856
+ logger.newline();
857
+ logger.info("Ihre Anmeldedaten wurden gespeichert.");
858
+ logger.info("F\xFChren Sie `turbo whoami` aus, um Ihre Authentifizierung zu \xFCberpr\xFCfen.");
717
859
  });
718
860
  var logoutCommand = new Command("logout").description("Remove stored authentication").action(() => {
719
861
  configService.clearToken();
@@ -726,6 +868,36 @@ var whoamiCommand = new Command("whoami").description("Show current authenticate
726
868
  addJsonData({ authenticated: false });
727
869
  process.exit(10 /* AUTH_ERROR */);
728
870
  }
871
+ const token = configService.getToken();
872
+ if (token?.startsWith("turbo_cli_")) {
873
+ const { data: data2, error: error2 } = await withSpinner("Fetching user info...", async () => {
874
+ const result = await authService.validateToken(token);
875
+ if (result.valid && result.user) {
876
+ return { data: result.user, error: void 0 };
877
+ }
878
+ return { data: void 0, error: "Invalid token" };
879
+ });
880
+ if (error2 || !data2) {
881
+ logger.error(`Failed to fetch user info: ${error2 || "Unknown error"}`);
882
+ addJsonData({ authenticated: false, error: error2 || "Unknown error" });
883
+ process.exit(13 /* API_ERROR */);
884
+ }
885
+ addJsonData({
886
+ authenticated: true,
887
+ user: {
888
+ email: data2.email,
889
+ id: data2.id,
890
+ name: data2.fullName || null
891
+ }
892
+ });
893
+ logger.header("Current User");
894
+ logger.table({
895
+ Email: data2.email,
896
+ ID: data2.id,
897
+ Name: data2.fullName || "-"
898
+ });
899
+ return;
900
+ }
729
901
  const { data, error } = await withSpinner(
730
902
  "Fetching user info...",
731
903
  () => apiClient.whoami()
@@ -738,15 +910,15 @@ var whoamiCommand = new Command("whoami").description("Show current authenticate
738
910
  addJsonData({
739
911
  authenticated: true,
740
912
  user: {
741
- id: data.id,
742
913
  email: data.email,
914
+ id: data.id,
743
915
  name: data.name || null
744
916
  }
745
917
  });
746
918
  logger.header("Current User");
747
919
  logger.table({
748
- ID: data.id,
749
920
  Email: data.email,
921
+ ID: data.id,
750
922
  Name: data.name || "-"
751
923
  });
752
924
  });
@@ -1151,128 +1323,105 @@ envCommand.addCommand(envUnsetCommand);
1151
1323
 
1152
1324
  // src/commands/init.ts
1153
1325
  import { Command as Command5 } from "commander";
1154
- import prompts2 from "prompts";
1326
+ import prompts from "prompts";
1155
1327
  import chalk6 from "chalk";
1156
1328
  var initCommand = new Command5("init").description("Initialize TurboOps project in current directory").action(async () => {
1157
1329
  logger.header("TurboOps Project Initialization");
1158
1330
  if (!configService.isAuthenticated()) {
1159
- logger.warning("Not authenticated. Please log in first.");
1331
+ logger.warning("Nicht authentifiziert. Bitte melden Sie sich zuerst an.");
1160
1332
  logger.newline();
1161
- const { shouldLogin } = await prompts2({
1162
- type: "confirm",
1333
+ const { shouldLogin } = await prompts({
1334
+ initial: true,
1335
+ message: "M\xF6chten Sie sich jetzt anmelden?",
1163
1336
  name: "shouldLogin",
1164
- message: "Would you like to log in now?",
1165
- initial: true
1337
+ type: "confirm"
1166
1338
  });
1167
1339
  if (!shouldLogin) {
1168
- logger.info("Run `turbo login` when ready.");
1340
+ logger.info("F\xFChren Sie `turbo login` aus, wenn Sie bereit sind.");
1169
1341
  addJsonData({ initialized: false, reason: "not_authenticated" });
1170
1342
  return;
1171
1343
  }
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"}`);
1344
+ const result = await authService.browserLogin();
1345
+ if (!result.success) {
1346
+ logger.error(`Anmeldung fehlgeschlagen: ${result.error || "Unbekannter Fehler"}`);
1196
1347
  addJsonData({
1348
+ error: result.error || "Unknown error",
1197
1349
  initialized: false,
1198
- reason: "login_failed",
1199
- error: error || "Unknown error"
1350
+ reason: "login_failed"
1200
1351
  });
1201
1352
  process.exit(10 /* AUTH_ERROR */);
1202
1353
  }
1203
- configService.setToken(data.token);
1204
- configService.setUserId(data.user.id);
1205
- logger.success(`Logged in as ${data.user.email}`);
1354
+ logger.success(`Angemeldet als ${result.user?.email || "Unknown"}`);
1206
1355
  }
1207
1356
  logger.newline();
1208
- const { projectSlug } = await prompts2({
1209
- type: "text",
1357
+ const { projectSlug } = await prompts({
1358
+ hint: "Der Slug Ihres TurboOps-Projekts",
1359
+ message: "Projekt-Slug:",
1210
1360
  name: "projectSlug",
1211
- message: "Project slug:",
1212
- hint: "The slug of your TurboOps project",
1213
- validate: (value) => value.length > 0 || "Project slug is required"
1361
+ type: "text",
1362
+ validate: (value) => value.length > 0 || "Projekt-Slug ist erforderlich"
1214
1363
  });
1215
1364
  if (!projectSlug) {
1216
- logger.warning("Initialization cancelled");
1365
+ logger.warning("Initialisierung abgebrochen");
1217
1366
  addJsonData({ initialized: false, reason: "cancelled" });
1218
1367
  return;
1219
1368
  }
1220
1369
  const { data: project, error: projectError } = await withSpinner(
1221
- "Verifying project...",
1370
+ "Verifiziere Projekt...",
1222
1371
  () => apiClient.getProject(projectSlug)
1223
1372
  );
1224
1373
  if (projectError || !project) {
1225
1374
  logger.error(
1226
- `Project "${projectSlug}" not found or you don't have access.`
1375
+ `Projekt "${projectSlug}" nicht gefunden oder Sie haben keinen Zugriff.`
1227
1376
  );
1228
1377
  logger.info(
1229
- "Make sure the project exists in TurboOps and you have permission to access it."
1378
+ "Stellen Sie sicher, dass das Projekt in TurboOps existiert und Sie die Berechtigung haben, darauf zuzugreifen."
1230
1379
  );
1231
1380
  addJsonData({
1232
1381
  initialized: false,
1233
- reason: "project_not_found",
1234
- projectSlug
1382
+ projectSlug,
1383
+ reason: "project_not_found"
1235
1384
  });
1236
1385
  process.exit(12 /* NOT_FOUND */);
1237
1386
  }
1238
1387
  configService.setProject(projectSlug);
1239
1388
  logger.newline();
1240
- logger.success("Project initialized!");
1389
+ logger.success("Projekt initialisiert!");
1241
1390
  logger.newline();
1242
- logger.header("Project Details");
1391
+ logger.header("Projektdetails");
1243
1392
  logger.table({
1244
1393
  Name: project.name,
1245
- Slug: project.slug,
1246
- Repository: project.repositoryUrl || "-"
1394
+ Repository: project.repositoryUrl || "-",
1395
+ Slug: project.slug
1247
1396
  });
1248
1397
  const { data: environments } = await apiClient.getEnvironments(project.id);
1249
1398
  const envList = environments && environments.length > 0 ? environments.map((env) => ({
1250
- slug: env.slug,
1251
1399
  name: env.name,
1400
+ slug: env.slug,
1252
1401
  type: env.type
1253
1402
  })) : [];
1254
1403
  addJsonData({
1404
+ environments: envList,
1255
1405
  initialized: true,
1256
1406
  project: {
1257
1407
  name: project.name,
1258
- slug: project.slug,
1259
- repositoryUrl: project.repositoryUrl || null
1260
- },
1261
- environments: envList
1408
+ repositoryUrl: project.repositoryUrl || null,
1409
+ slug: project.slug
1410
+ }
1262
1411
  });
1263
1412
  if (environments && environments.length > 0) {
1264
1413
  logger.newline();
1265
- logger.header("Available Environments");
1414
+ logger.header("Verf\xFCgbare Umgebungen");
1266
1415
  for (const env of environments) {
1267
1416
  console.log(` ${chalk6.bold(env.slug)} - ${env.name} (${env.type})`);
1268
1417
  }
1269
1418
  }
1270
1419
  logger.newline();
1271
- logger.header("Next Steps");
1420
+ logger.header("N\xE4chste Schritte");
1272
1421
  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"
1422
+ "F\xFChren Sie `turbo status` aus, um alle Umgebungen zu sehen",
1423
+ "F\xFChren Sie `turbo deploy <umgebung>` aus, um zu deployen",
1424
+ "F\xFChren Sie `turbo logs <umgebung>` aus, um Logs anzuzeigen"
1276
1425
  ]);
1277
1426
  });
1278
1427
 
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.580",
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"