@turboops/cli 1.0.0-dev.580 → 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 +531 -82
  2. package/package.json +1 -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" };
@@ -200,41 +202,76 @@ var APP_URLS = {
200
202
  production: "https://turbo-ops.de",
201
203
  dev: "https://dev.turbo-ops.de"
202
204
  };
203
- function getDefaultApiUrl() {
204
- const version = getCurrentVersion();
205
- const isDevVersion = version.includes("-dev");
206
- return isDevVersion ? API_URLS.dev : API_URLS.production;
207
- }
208
- var defaults = {
209
- apiUrl: API_URLS.production,
205
+ var LOCAL_CONFIG_FILE = ".turboops.json";
206
+ var globalDefaults = {
210
207
  token: null,
211
- project: null,
212
208
  userId: null
213
209
  };
214
- var config = new Conf({
210
+ var globalConfig = new Conf({
215
211
  projectName: "turboops-cli",
216
- defaults
212
+ defaults: globalDefaults
217
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
+ }
218
266
  var configService = {
219
267
  /**
220
- * Get API URL
221
- * Priority: ENV variable > stored config > version-based default
268
+ * Get API URL (fixed based on CLI version)
222
269
  */
223
270
  getApiUrl() {
224
271
  if (process.env.TURBOOPS_API_URL) {
225
272
  return process.env.TURBOOPS_API_URL;
226
273
  }
227
- const storedUrl = config.get("apiUrl");
228
- if (storedUrl === "https://api.turboops.io" || storedUrl === API_URLS.production) {
229
- return getDefaultApiUrl();
230
- }
231
- return storedUrl;
232
- },
233
- /**
234
- * Set API URL
235
- */
236
- setApiUrl(url) {
237
- config.set("apiUrl", url);
274
+ return getApiUrl();
238
275
  },
239
276
  /**
240
277
  * Get App URL (frontend)
@@ -246,53 +283,65 @@ var configService = {
246
283
  return isDevVersion ? APP_URLS.dev : APP_URLS.production;
247
284
  },
248
285
  /**
249
- * Get authentication token
286
+ * Get authentication token (global - user session)
250
287
  */
251
288
  getToken() {
252
- return process.env.TURBOOPS_TOKEN || config.get("token");
289
+ return process.env.TURBOOPS_TOKEN || globalConfig.get("token");
253
290
  },
254
291
  /**
255
- * Set authentication token
292
+ * Set authentication token (global - user session)
256
293
  */
257
294
  setToken(token) {
258
- config.set("token", token);
295
+ globalConfig.set("token", token);
259
296
  },
260
297
  /**
261
298
  * Clear authentication token
262
299
  */
263
300
  clearToken() {
264
- config.set("token", null);
265
- config.set("userId", null);
301
+ globalConfig.set("token", null);
302
+ globalConfig.set("userId", null);
266
303
  },
267
304
  /**
268
- * Get current project
305
+ * Get current project (local - project specific)
269
306
  */
270
307
  getProject() {
271
- 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;
272
313
  },
273
314
  /**
274
- * Set current project
315
+ * Set current project (local - writes to .turboops.json)
275
316
  */
276
317
  setProject(project) {
277
- config.set("project", project);
318
+ const localConfig = readLocalConfig() || { project: "" };
319
+ localConfig.project = project;
320
+ writeLocalConfig(localConfig);
278
321
  },
279
322
  /**
280
323
  * Clear current project
281
324
  */
282
325
  clearProject() {
283
- config.set("project", null);
326
+ const configPath = getLocalConfigPath();
327
+ if (configPath && fs.existsSync(configPath)) {
328
+ try {
329
+ fs.unlinkSync(configPath);
330
+ } catch {
331
+ }
332
+ }
284
333
  },
285
334
  /**
286
- * Get user ID
335
+ * Get user ID (global - user session)
287
336
  */
288
337
  getUserId() {
289
- return config.get("userId");
338
+ return globalConfig.get("userId");
290
339
  },
291
340
  /**
292
- * Set user ID
341
+ * Set user ID (global - user session)
293
342
  */
294
343
  setUserId(userId) {
295
- config.set("userId", userId);
344
+ globalConfig.set("userId", userId);
296
345
  },
297
346
  /**
298
347
  * Check if user is authenticated
@@ -306,6 +355,19 @@ var configService = {
306
355
  hasProject() {
307
356
  return !!this.getProject();
308
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
+ },
309
371
  /**
310
372
  * Get all configuration
311
373
  */
@@ -318,17 +380,25 @@ var configService = {
318
380
  };
319
381
  },
320
382
  /**
321
- * Clear all configuration
383
+ * Clear all configuration (both global and local)
322
384
  */
323
385
  clearAll() {
324
- config.clear();
386
+ globalConfig.clear();
387
+ this.clearProject();
325
388
  },
326
389
  /**
327
- * Check if using a project token (turbo_xxx format)
390
+ * Check if using a project token (turbo_xxx format, but not turbo_cli_xxx)
328
391
  */
329
392
  isProjectToken() {
330
393
  const token = this.getToken();
331
- 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_");
332
402
  },
333
403
  /**
334
404
  * Show current configuration
@@ -336,12 +406,15 @@ var configService = {
336
406
  show() {
337
407
  const data = this.getAll();
338
408
  const isProjectToken2 = this.isProjectToken();
409
+ const isCliToken = this.isCliSessionToken();
410
+ const localConfigPath = this.getLocalConfigPath();
339
411
  logger.header("Configuration");
340
412
  logger.table({
341
413
  "API URL": data.apiUrl,
342
414
  Token: data.token || "Not set",
343
- "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",
344
416
  Project: data.project || (isProjectToken2 ? "(from token)" : "Not set"),
417
+ "Project Config": localConfigPath || "(not found)",
345
418
  "User ID": data.userId || (isProjectToken2 ? "(N/A for project token)" : "Not set")
346
419
  });
347
420
  }
@@ -359,7 +432,7 @@ var apiClient = {
359
432
  /**
360
433
  * Make an API request
361
434
  */
362
- async request(method, path, body) {
435
+ async request(method, path2, body) {
363
436
  const apiUrl = configService.getApiUrl();
364
437
  const token = configService.getToken();
365
438
  if (!token) {
@@ -368,7 +441,7 @@ var apiClient = {
368
441
  status: 401
369
442
  };
370
443
  }
371
- const url = `${apiUrl}${path}`;
444
+ const url = `${apiUrl}${path2}`;
372
445
  const headers = {
373
446
  "Content-Type": "application/json",
374
447
  Authorization: `Bearer ${token}`
@@ -513,23 +586,66 @@ var apiClient = {
513
586
  return this.request("GET", `/deployment/projects/by-slug/${slug}`);
514
587
  },
515
588
  /**
516
- * 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
517
609
  */
518
610
  async getEnvironments(projectId) {
519
611
  return this.request(
520
612
  "GET",
521
- `/deployment/environments?projectId=${projectId}`
613
+ `/deployment/stages?projectId=${projectId}`
522
614
  );
523
615
  },
524
616
  /**
525
- * Get environment by slug
617
+ * Get environment (stage) by slug
526
618
  */
527
619
  async getEnvironmentBySlug(projectId, slug) {
528
620
  return this.request(
529
621
  "GET",
530
- `/deployment/environments/by-slug/${projectId}/${slug}`
622
+ `/deployment/stages/by-slug/${projectId}/${slug}`
531
623
  );
532
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
+ },
533
649
  /**
534
650
  * Trigger deployment
535
651
  */
@@ -560,7 +676,7 @@ var apiClient = {
560
676
  async restart(environmentId) {
561
677
  return this.request(
562
678
  "POST",
563
- `/deployment/environments/${environmentId}/restart`
679
+ `/deployment/stages/${environmentId}/restart`
564
680
  );
565
681
  },
566
682
  /**
@@ -569,7 +685,7 @@ var apiClient = {
569
685
  async stop(environmentId) {
570
686
  return this.request(
571
687
  "POST",
572
- `/deployment/environments/${environmentId}/stop`
688
+ `/deployment/stages/${environmentId}/stop`
573
689
  );
574
690
  },
575
691
  /**
@@ -578,7 +694,7 @@ var apiClient = {
578
694
  async wake(environmentId) {
579
695
  return this.request(
580
696
  "POST",
581
- `/deployment/environments/${environmentId}/wake`
697
+ `/deployment/stages/${environmentId}/wake`
582
698
  );
583
699
  },
584
700
  /**
@@ -593,7 +709,7 @@ var apiClient = {
593
709
  async getEnvVars(environmentId) {
594
710
  return this.request(
595
711
  "GET",
596
- `/deployment/environments/${environmentId}/env-vars`
712
+ `/deployment/stages/${environmentId}/env-vars`
597
713
  );
598
714
  },
599
715
  /**
@@ -602,7 +718,7 @@ var apiClient = {
602
718
  async setEnvVar(environmentId, key, value, secret) {
603
719
  return this.request(
604
720
  "PUT",
605
- `/deployment/environments/${environmentId}/env-vars`,
721
+ `/deployment/stages/${environmentId}/env-vars`,
606
722
  {
607
723
  key,
608
724
  value,
@@ -616,7 +732,7 @@ var apiClient = {
616
732
  async deleteEnvVar(environmentId, key) {
617
733
  return this.request(
618
734
  "DELETE",
619
- `/deployment/environments/${environmentId}/env-vars/${key}`
735
+ `/deployment/stages/${environmentId}/env-vars/${key}`
620
736
  );
621
737
  },
622
738
  /**
@@ -1206,11 +1322,77 @@ var statusCommand = new Command3("status").description("Show deployment status")
1206
1322
  });
1207
1323
  }
1208
1324
  });
1209
- var configCommand = new Command3("config").description("Show current configuration").action(() => {
1210
- const config2 = configService.getAll();
1211
- 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 });
1212
1329
  configService.show();
1213
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
+ });
1214
1396
  function getStatusColor(status) {
1215
1397
  switch (status) {
1216
1398
  case "healthy":
@@ -1367,24 +1549,294 @@ var initCommand = new Command5("init").description("Initialize TurboOps project
1367
1549
  return;
1368
1550
  }
1369
1551
  const { data: project, error: projectError } = await withSpinner(
1370
- "Verifiziere Projekt...",
1552
+ "Suche Projekt...",
1371
1553
  () => apiClient.getProject(projectSlug)
1372
1554
  );
1373
- if (projectError || !project) {
1374
- logger.error(
1375
- `Projekt "${projectSlug}" nicht gefunden oder Sie haben keinen Zugriff.`
1376
- );
1377
- logger.info(
1378
- "Stellen Sie sicher, dass das Projekt in TurboOps existiert und Sie die Berechtigung haben, darauf zuzugreifen."
1379
- );
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"}`);
1380
1669
  addJsonData({
1670
+ error: createError || "Unknown error",
1381
1671
  initialized: false,
1382
- projectSlug,
1383
- reason: "project_not_found"
1672
+ reason: "create_failed"
1384
1673
  });
1385
- process.exit(12 /* NOT_FOUND */);
1674
+ process.exit(13 /* API_ERROR */);
1386
1675
  }
1387
- configService.setProject(projectSlug);
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}`);
1837
+ }
1838
+ }
1839
+ async function showFinalSummary(project) {
1388
1840
  logger.newline();
1389
1841
  logger.success("Projekt initialisiert!");
1390
1842
  logger.newline();
@@ -1411,7 +1863,7 @@ var initCommand = new Command5("init").description("Initialize TurboOps project
1411
1863
  });
1412
1864
  if (environments && environments.length > 0) {
1413
1865
  logger.newline();
1414
- logger.header("Verf\xFCgbare Umgebungen");
1866
+ logger.header("Verf\xFCgbare Stages");
1415
1867
  for (const env of environments) {
1416
1868
  console.log(` ${chalk6.bold(env.slug)} - ${env.name} (${env.type})`);
1417
1869
  }
@@ -1419,11 +1871,11 @@ var initCommand = new Command5("init").description("Initialize TurboOps project
1419
1871
  logger.newline();
1420
1872
  logger.header("N\xE4chste Schritte");
1421
1873
  logger.list([
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"
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"
1425
1877
  ]);
1426
- });
1878
+ }
1427
1879
 
1428
1880
  // src/commands/logs.ts
1429
1881
  import { Command as Command6 } from "commander";
@@ -1588,7 +2040,7 @@ function getLevelColor(level) {
1588
2040
  var VERSION = getCurrentVersion();
1589
2041
  var shouldCheckUpdate = true;
1590
2042
  var program = new Command7();
1591
- 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");
1592
2044
  program.hook("preAction", (thisCommand) => {
1593
2045
  const opts = thisCommand.opts();
1594
2046
  clearJsonData();
@@ -1605,9 +2057,6 @@ program.hook("preAction", (thisCommand) => {
1605
2057
  if (opts.token) {
1606
2058
  configService.setToken(opts.token);
1607
2059
  }
1608
- if (opts.api) {
1609
- configService.setApiUrl(opts.api);
1610
- }
1611
2060
  if (opts.verbose) {
1612
2061
  process.env.DEBUG = "true";
1613
2062
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@turboops/cli",
3
- "version": "1.0.0-dev.580",
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",