@tarout/cli 0.1.4 → 0.2.0

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.
@@ -0,0 +1,53 @@
1
+ // src/utils/prompts.ts
2
+ import inquirer from "inquirer";
3
+ async function confirm(message, defaultValue = false) {
4
+ const { confirmed } = await inquirer.prompt([
5
+ {
6
+ type: "confirm",
7
+ name: "confirmed",
8
+ message,
9
+ default: defaultValue
10
+ }
11
+ ]);
12
+ return confirmed;
13
+ }
14
+ async function input(message, defaultValue) {
15
+ const { value } = await inquirer.prompt([
16
+ {
17
+ type: "input",
18
+ name: "value",
19
+ message,
20
+ default: defaultValue
21
+ }
22
+ ]);
23
+ return value;
24
+ }
25
+ async function select(message, choices) {
26
+ const { value } = await inquirer.prompt([
27
+ {
28
+ type: "list",
29
+ name: "value",
30
+ message,
31
+ choices
32
+ }
33
+ ]);
34
+ return value;
35
+ }
36
+ async function password(message) {
37
+ const { value } = await inquirer.prompt([
38
+ {
39
+ type: "password",
40
+ name: "value",
41
+ message,
42
+ mask: "*"
43
+ }
44
+ ]);
45
+ return value;
46
+ }
47
+
48
+ export {
49
+ confirm,
50
+ input,
51
+ select,
52
+ password
53
+ };
package/dist/index.js CHANGED
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ confirm,
4
+ input,
5
+ select
6
+ } from "./chunk-VO4OYJW3.js";
2
7
  import {
3
8
  failSpinner,
4
9
  startSpinner,
@@ -19,6 +24,14 @@ import superjson from "superjson";
19
24
 
20
25
  // src/lib/config.ts
21
26
  import Conf from "conf";
27
+ import {
28
+ existsSync,
29
+ mkdirSync,
30
+ readFileSync,
31
+ rmSync,
32
+ writeFileSync
33
+ } from "fs";
34
+ import { join } from "path";
22
35
  var config = new Conf({
23
36
  projectName: "tarout",
24
37
  defaults: {
@@ -65,6 +78,58 @@ function updateProfile(updates) {
65
78
  });
66
79
  }
67
80
  }
81
+ var PROJECT_CONFIG_DIR = ".tarout";
82
+ var PROJECT_CONFIG_FILE = "project.json";
83
+ function getProjectConfigDir(basePath) {
84
+ const base = basePath || process.cwd();
85
+ return join(base, PROJECT_CONFIG_DIR);
86
+ }
87
+ function getProjectConfigPath(basePath) {
88
+ return join(getProjectConfigDir(basePath), PROJECT_CONFIG_FILE);
89
+ }
90
+ function isProjectLinked(basePath) {
91
+ return existsSync(getProjectConfigPath(basePath));
92
+ }
93
+ function getProjectConfig(basePath) {
94
+ const configPath = getProjectConfigPath(basePath);
95
+ if (!existsSync(configPath)) {
96
+ return null;
97
+ }
98
+ try {
99
+ const content = readFileSync(configPath, "utf-8");
100
+ return JSON.parse(content);
101
+ } catch {
102
+ return null;
103
+ }
104
+ }
105
+ function setProjectConfig(config2, basePath) {
106
+ const configDir = getProjectConfigDir(basePath);
107
+ const configPath = getProjectConfigPath(basePath);
108
+ if (!existsSync(configDir)) {
109
+ mkdirSync(configDir, { recursive: true });
110
+ }
111
+ writeFileSync(configPath, JSON.stringify(config2, null, 2), "utf-8");
112
+ const gitignorePath = join(configDir, ".gitignore");
113
+ if (!existsSync(gitignorePath)) {
114
+ writeFileSync(
115
+ gitignorePath,
116
+ "# Ignore local tarout config\n*\n!.gitignore\n",
117
+ "utf-8"
118
+ );
119
+ }
120
+ }
121
+ function removeProjectConfig(basePath) {
122
+ const configDir = getProjectConfigDir(basePath);
123
+ if (!existsSync(configDir)) {
124
+ return false;
125
+ }
126
+ try {
127
+ rmSync(configDir, { recursive: true, force: true });
128
+ return true;
129
+ } catch {
130
+ return false;
131
+ }
132
+ }
68
133
 
69
134
  // src/utils/exit-codes.ts
70
135
  var ExitCode = {
@@ -651,42 +716,6 @@ function getApiClient() {
651
716
  return client;
652
717
  }
653
718
 
654
- // src/utils/prompts.ts
655
- import inquirer from "inquirer";
656
- async function confirm(message, defaultValue = false) {
657
- const { confirmed } = await inquirer.prompt([
658
- {
659
- type: "confirm",
660
- name: "confirmed",
661
- message,
662
- default: defaultValue
663
- }
664
- ]);
665
- return confirmed;
666
- }
667
- async function input(message, defaultValue) {
668
- const { value } = await inquirer.prompt([
669
- {
670
- type: "input",
671
- name: "value",
672
- message,
673
- default: defaultValue
674
- }
675
- ]);
676
- return value;
677
- }
678
- async function select(message, choices) {
679
- const { value } = await inquirer.prompt([
680
- {
681
- type: "list",
682
- name: "value",
683
- message,
684
- choices
685
- }
686
- ]);
687
- return value;
688
- }
689
-
690
719
  // src/commands/apps.ts
691
720
  function registerAppsCommands(program2) {
692
721
  const apps = program2.command("apps").description("Manage applications");
@@ -1219,8 +1248,424 @@ function registerAuthCommands(program2) {
1219
1248
  });
1220
1249
  }
1221
1250
 
1222
- // src/commands/db.ts
1251
+ // src/lib/process.ts
1223
1252
  import { spawn } from "child_process";
1253
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
1254
+ import { join as join2 } from "path";
1255
+ function readPackageJson(basePath) {
1256
+ const base = basePath || process.cwd();
1257
+ const packagePath = join2(base, "package.json");
1258
+ if (!existsSync2(packagePath)) {
1259
+ return null;
1260
+ }
1261
+ try {
1262
+ const content = readFileSync2(packagePath, "utf-8");
1263
+ return JSON.parse(content);
1264
+ } catch {
1265
+ return null;
1266
+ }
1267
+ }
1268
+ function detectPackageManager(basePath) {
1269
+ const base = basePath || process.cwd();
1270
+ if (existsSync2(join2(base, "bun.lockb")) || existsSync2(join2(base, "bun.lock"))) {
1271
+ return "bun";
1272
+ }
1273
+ if (existsSync2(join2(base, "pnpm-lock.yaml"))) {
1274
+ return "pnpm";
1275
+ }
1276
+ if (existsSync2(join2(base, "yarn.lock"))) {
1277
+ return "yarn";
1278
+ }
1279
+ if (existsSync2(join2(base, "package-lock.json"))) {
1280
+ return "npm";
1281
+ }
1282
+ const pkg = readPackageJson(basePath);
1283
+ if (pkg?.packageManager) {
1284
+ if (pkg.packageManager.startsWith("bun")) return "bun";
1285
+ if (pkg.packageManager.startsWith("pnpm")) return "pnpm";
1286
+ if (pkg.packageManager.startsWith("yarn")) return "yarn";
1287
+ if (pkg.packageManager.startsWith("npm")) return "npm";
1288
+ }
1289
+ return "npm";
1290
+ }
1291
+ var FRAMEWORKS = [
1292
+ {
1293
+ dependencies: ["next"],
1294
+ info: {
1295
+ name: "Next.js",
1296
+ devCommand: "next dev",
1297
+ buildCommand: "next build",
1298
+ defaultPort: 3e3
1299
+ }
1300
+ },
1301
+ {
1302
+ dependencies: ["vite"],
1303
+ info: {
1304
+ name: "Vite",
1305
+ devCommand: "vite",
1306
+ buildCommand: "vite build",
1307
+ defaultPort: 5173
1308
+ }
1309
+ },
1310
+ {
1311
+ dependencies: ["@remix-run/dev"],
1312
+ info: {
1313
+ name: "Remix",
1314
+ devCommand: "remix dev",
1315
+ buildCommand: "remix build",
1316
+ defaultPort: 3e3
1317
+ }
1318
+ },
1319
+ {
1320
+ dependencies: ["nuxt"],
1321
+ info: {
1322
+ name: "Nuxt",
1323
+ devCommand: "nuxt dev",
1324
+ buildCommand: "nuxt build",
1325
+ defaultPort: 3e3
1326
+ }
1327
+ },
1328
+ {
1329
+ dependencies: ["astro"],
1330
+ info: {
1331
+ name: "Astro",
1332
+ devCommand: "astro dev",
1333
+ buildCommand: "astro build",
1334
+ defaultPort: 4321
1335
+ }
1336
+ },
1337
+ {
1338
+ dependencies: ["@angular/core"],
1339
+ info: {
1340
+ name: "Angular",
1341
+ devCommand: "ng serve",
1342
+ buildCommand: "ng build",
1343
+ defaultPort: 4200
1344
+ }
1345
+ },
1346
+ {
1347
+ dependencies: ["svelte-kit", "@sveltejs/kit"],
1348
+ info: {
1349
+ name: "SvelteKit",
1350
+ devCommand: "svelte-kit dev",
1351
+ buildCommand: "svelte-kit build",
1352
+ defaultPort: 5173
1353
+ }
1354
+ },
1355
+ {
1356
+ dependencies: ["svelte"],
1357
+ info: {
1358
+ name: "Svelte",
1359
+ devCommand: "vite",
1360
+ buildCommand: "vite build",
1361
+ defaultPort: 5173
1362
+ }
1363
+ },
1364
+ {
1365
+ dependencies: ["react-scripts"],
1366
+ info: {
1367
+ name: "Create React App",
1368
+ devCommand: "react-scripts start",
1369
+ buildCommand: "react-scripts build",
1370
+ defaultPort: 3e3
1371
+ }
1372
+ },
1373
+ {
1374
+ dependencies: ["gatsby"],
1375
+ info: {
1376
+ name: "Gatsby",
1377
+ devCommand: "gatsby develop",
1378
+ buildCommand: "gatsby build",
1379
+ defaultPort: 8e3
1380
+ }
1381
+ },
1382
+ {
1383
+ dependencies: ["express"],
1384
+ info: {
1385
+ name: "Express",
1386
+ devCommand: "node",
1387
+ buildCommand: "echo 'No build required'",
1388
+ defaultPort: 3e3
1389
+ }
1390
+ }
1391
+ ];
1392
+ function detectFramework(pkg) {
1393
+ const allDeps = {
1394
+ ...pkg.dependencies,
1395
+ ...pkg.devDependencies
1396
+ };
1397
+ for (const framework of FRAMEWORKS) {
1398
+ for (const dep of framework.dependencies) {
1399
+ if (dep in allDeps) {
1400
+ return framework.info;
1401
+ }
1402
+ }
1403
+ }
1404
+ return null;
1405
+ }
1406
+ function getDevCommand(pkg, pm) {
1407
+ const devScripts = ["dev", "start:dev", "serve", "start"];
1408
+ for (const script of devScripts) {
1409
+ if (pkg.scripts?.[script]) {
1410
+ return `${pm} run ${script}`;
1411
+ }
1412
+ }
1413
+ const framework = detectFramework(pkg);
1414
+ if (framework) {
1415
+ return `${pm === "npm" ? "npx" : pm} ${framework.devCommand}`;
1416
+ }
1417
+ return `${pm} run dev`;
1418
+ }
1419
+ function getBuildCommand(pkg, pm) {
1420
+ if (pkg.scripts?.build) {
1421
+ return `${pm} run build`;
1422
+ }
1423
+ const framework = detectFramework(pkg);
1424
+ if (framework) {
1425
+ return `${pm === "npm" ? "npx" : pm} ${framework.buildCommand}`;
1426
+ }
1427
+ return `${pm} run build`;
1428
+ }
1429
+ function getDefaultPort(pkg) {
1430
+ const framework = detectFramework(pkg);
1431
+ return framework?.defaultPort || 3e3;
1432
+ }
1433
+ function runCommand(command, env, options = {}) {
1434
+ return new Promise((resolve) => {
1435
+ const [cmd, ...args] = parseCommand(command);
1436
+ const spawnOptions = {
1437
+ cwd: options.cwd || process.cwd(),
1438
+ env: {
1439
+ ...process.env,
1440
+ ...env
1441
+ },
1442
+ stdio: ["inherit", "pipe", "pipe"],
1443
+ shell: true
1444
+ };
1445
+ const child = spawn(cmd, args, spawnOptions);
1446
+ child.stdout?.on("data", (data) => {
1447
+ const str = data.toString();
1448
+ if (options.onStdout) {
1449
+ options.onStdout(str);
1450
+ } else {
1451
+ process.stdout.write(str);
1452
+ }
1453
+ });
1454
+ child.stderr?.on("data", (data) => {
1455
+ const str = data.toString();
1456
+ if (options.onStderr) {
1457
+ options.onStderr(str);
1458
+ } else {
1459
+ process.stderr.write(str);
1460
+ }
1461
+ });
1462
+ const handleSignal = () => {
1463
+ child.kill("SIGINT");
1464
+ };
1465
+ process.on("SIGINT", handleSignal);
1466
+ process.on("SIGTERM", handleSignal);
1467
+ child.on("close", (code, signal) => {
1468
+ process.off("SIGINT", handleSignal);
1469
+ process.off("SIGTERM", handleSignal);
1470
+ resolve({
1471
+ exitCode: code ?? 1,
1472
+ signal
1473
+ });
1474
+ });
1475
+ child.on("error", (err) => {
1476
+ process.off("SIGINT", handleSignal);
1477
+ process.off("SIGTERM", handleSignal);
1478
+ console.error(`Failed to start process: ${err.message}`);
1479
+ resolve({
1480
+ exitCode: 1,
1481
+ signal: null
1482
+ });
1483
+ });
1484
+ });
1485
+ }
1486
+ function parseCommand(command) {
1487
+ const parts = [];
1488
+ let current = "";
1489
+ let inQuote = false;
1490
+ let quoteChar = "";
1491
+ for (const char of command) {
1492
+ if ((char === '"' || char === "'") && !inQuote) {
1493
+ inQuote = true;
1494
+ quoteChar = char;
1495
+ } else if (char === quoteChar && inQuote) {
1496
+ inQuote = false;
1497
+ quoteChar = "";
1498
+ } else if (char === " " && !inQuote) {
1499
+ if (current) {
1500
+ parts.push(current);
1501
+ current = "";
1502
+ }
1503
+ } else {
1504
+ current += char;
1505
+ }
1506
+ }
1507
+ if (current) {
1508
+ parts.push(current);
1509
+ }
1510
+ return parts;
1511
+ }
1512
+ function envVarsToObject(variables) {
1513
+ const result = {};
1514
+ for (const v of variables) {
1515
+ if (v.value !== null) {
1516
+ result[v.key] = v.value;
1517
+ }
1518
+ }
1519
+ return result;
1520
+ }
1521
+
1522
+ // src/commands/build.ts
1523
+ function registerBuildCommand(program2) {
1524
+ program2.command("build").description("Build locally with cloud environment variables").option("-a, --app <app>", "Application ID or name (overrides linked app)").option("-c, --command <command>", "Custom build command to run").action(async (options) => {
1525
+ try {
1526
+ if (!isLoggedIn()) throw new AuthError();
1527
+ const profile = getCurrentProfile();
1528
+ if (!profile) throw new AuthError();
1529
+ const client2 = getApiClient();
1530
+ let applicationId;
1531
+ let appName;
1532
+ if (options.app) {
1533
+ const _spinner = startSpinner("Finding application...");
1534
+ const apps = await client2.application.allByOrganization.query();
1535
+ const app = findApp2(apps, options.app);
1536
+ if (!app) {
1537
+ failSpinner();
1538
+ const suggestions = findSimilar(
1539
+ options.app,
1540
+ apps.map((a) => a.name)
1541
+ );
1542
+ throw new NotFoundError("Application", options.app, suggestions);
1543
+ }
1544
+ applicationId = app.applicationId;
1545
+ appName = app.name;
1546
+ succeedSpinner();
1547
+ } else if (isProjectLinked()) {
1548
+ const config2 = getProjectConfig();
1549
+ if (!config2) {
1550
+ throw new CliError(
1551
+ "Project config is corrupted. Run 'tarout link' to relink."
1552
+ );
1553
+ }
1554
+ applicationId = config2.applicationId;
1555
+ appName = config2.name;
1556
+ } else {
1557
+ throw new InvalidArgumentError(
1558
+ "No linked application. Run 'tarout link' first or use --app flag."
1559
+ );
1560
+ }
1561
+ const pkg = readPackageJson();
1562
+ if (!pkg) {
1563
+ throw new CliError(
1564
+ "No package.json found in current directory. Make sure you're in a Node.js project."
1565
+ );
1566
+ }
1567
+ const pm = detectPackageManager();
1568
+ let buildCommand = options.command;
1569
+ if (!buildCommand) {
1570
+ buildCommand = getBuildCommand(pkg, pm);
1571
+ }
1572
+ const _envSpinner = startSpinner(
1573
+ `Fetching environment variables for ${appName}...`
1574
+ );
1575
+ let envVars = {};
1576
+ try {
1577
+ const variables = await client2.envVariable.list.query({
1578
+ applicationId,
1579
+ includeValues: true
1580
+ });
1581
+ envVars = envVarsToObject(variables);
1582
+ succeedSpinner(
1583
+ `Loaded ${Object.keys(envVars).length} environment variables`
1584
+ );
1585
+ } catch (err) {
1586
+ failSpinner();
1587
+ throw new CliError(
1588
+ `Failed to fetch environment variables: ${err instanceof Error ? err.message : "Unknown error"}`
1589
+ );
1590
+ }
1591
+ envVars.NODE_ENV = envVars.NODE_ENV || "production";
1592
+ const framework = detectFramework(pkg);
1593
+ if (isJsonMode()) {
1594
+ const startTime2 = Date.now();
1595
+ const result2 = await runCommand(buildCommand, envVars);
1596
+ const duration2 = Math.round((Date.now() - startTime2) / 1e3);
1597
+ outputData({
1598
+ success: result2.exitCode === 0,
1599
+ applicationId,
1600
+ appName,
1601
+ command: buildCommand,
1602
+ framework: framework?.name || "Unknown",
1603
+ envVarCount: Object.keys(envVars).length,
1604
+ packageManager: pm,
1605
+ exitCode: result2.exitCode,
1606
+ duration: duration2
1607
+ });
1608
+ if (result2.exitCode !== 0) {
1609
+ process.exit(result2.exitCode);
1610
+ }
1611
+ return;
1612
+ }
1613
+ log("");
1614
+ log(colors.bold(`Building ${colors.cyan(appName)}`));
1615
+ log("");
1616
+ log(` Framework: ${colors.dim(framework?.name || "Unknown")}`);
1617
+ log(` Package Manager: ${colors.dim(pm)}`);
1618
+ log(` Command: ${colors.dim(buildCommand)}`);
1619
+ log(
1620
+ ` Env Variables: ${colors.dim(String(Object.keys(envVars).length))}`
1621
+ );
1622
+ log("");
1623
+ log(colors.dim("\u2500".repeat(50)));
1624
+ log("");
1625
+ const startTime = Date.now();
1626
+ const result = await runCommand(buildCommand, envVars);
1627
+ const duration = Math.round((Date.now() - startTime) / 1e3);
1628
+ log("");
1629
+ log(colors.dim("\u2500".repeat(50)));
1630
+ log("");
1631
+ if (result.exitCode === 0) {
1632
+ log(colors.success(`Build completed successfully in ${duration}s`));
1633
+ log("");
1634
+ log("Next steps:");
1635
+ log(` ${colors.dim("tarout deploy")} - Deploy to cloud`);
1636
+ log("");
1637
+ } else {
1638
+ log(
1639
+ colors.error(
1640
+ `Build failed with exit code ${result.exitCode} (${duration}s)`
1641
+ )
1642
+ );
1643
+ log("");
1644
+ log("Troubleshooting:");
1645
+ log(` ${colors.dim("1.")} Check the build output above for errors`);
1646
+ log(` ${colors.dim("2.")} Verify all dependencies are installed`);
1647
+ log(
1648
+ ` ${colors.dim("3.")} Make sure environment variables are correct`
1649
+ );
1650
+ log("");
1651
+ throw new BuildFailedError(
1652
+ `Build failed with exit code ${result.exitCode}`
1653
+ );
1654
+ }
1655
+ } catch (err) {
1656
+ handleError(err);
1657
+ }
1658
+ });
1659
+ }
1660
+ function findApp2(apps, identifier) {
1661
+ const lowerIdentifier = identifier.toLowerCase();
1662
+ return apps.find(
1663
+ (app) => app.applicationId === identifier || app.applicationId.startsWith(identifier) || app.name.toLowerCase() === lowerIdentifier || app.appName?.toLowerCase() === lowerIdentifier
1664
+ );
1665
+ }
1666
+
1667
+ // src/commands/db.ts
1668
+ import { spawn as spawn2 } from "child_process";
1224
1669
  function registerDbCommands(program2) {
1225
1670
  const db = program2.command("db").description("Manage databases");
1226
1671
  db.command("list").alias("ls").description("List all databases").option("-t, --type <type>", "Filter by type (postgres, mysql, redis)").action(async (options) => {
@@ -1561,7 +2006,7 @@ function registerDbCommands(program2) {
1561
2006
  log(`Connecting to ${colors.bold(dbDetails.name)}...`);
1562
2007
  log(colors.dim("Press Ctrl+D to exit"));
1563
2008
  log("");
1564
- const child = spawn(command, args, {
2009
+ const child = spawn2(command, args, {
1565
2010
  stdio: "inherit",
1566
2011
  env: { ...process.env, ...env }
1567
2012
  });
@@ -1758,7 +2203,7 @@ function registerDeployCommands(program2) {
1758
2203
  const client2 = getApiClient();
1759
2204
  const _spinner = startSpinner("Finding application...");
1760
2205
  const apps = await client2.application.allByOrganization.query();
1761
- const app = findApp2(apps, appIdentifier);
2206
+ const app = findApp3(apps, appIdentifier);
1762
2207
  if (!app) {
1763
2208
  failSpinner();
1764
2209
  const suggestions = findSimilar(
@@ -1811,7 +2256,7 @@ function registerDeployCommands(program2) {
1811
2256
  const client2 = getApiClient();
1812
2257
  const _spinner = startSpinner("Fetching status...");
1813
2258
  const apps = await client2.application.allByOrganization.query();
1814
- const appSummary = findApp2(apps, appIdentifier);
2259
+ const appSummary = findApp3(apps, appIdentifier);
1815
2260
  if (!appSummary) {
1816
2261
  failSpinner();
1817
2262
  const suggestions = findSimilar(
@@ -1860,7 +2305,7 @@ function registerDeployCommands(program2) {
1860
2305
  const client2 = getApiClient();
1861
2306
  const _spinner = startSpinner("Cancelling deployment...");
1862
2307
  const apps = await client2.application.allByOrganization.query();
1863
- const app = findApp2(apps, appIdentifier);
2308
+ const app = findApp3(apps, appIdentifier);
1864
2309
  if (!app) {
1865
2310
  failSpinner();
1866
2311
  const suggestions = findSimilar(
@@ -1886,7 +2331,7 @@ function registerDeployCommands(program2) {
1886
2331
  const client2 = getApiClient();
1887
2332
  const _spinner = startSpinner("Fetching deployments...");
1888
2333
  const apps = await client2.application.allByOrganization.query();
1889
- const appSummary = findApp2(apps, appIdentifier);
2334
+ const appSummary = findApp3(apps, appIdentifier);
1890
2335
  if (!appSummary) {
1891
2336
  failSpinner();
1892
2337
  const suggestions = findSimilar(
@@ -2064,6 +2509,123 @@ function registerDeployCommands(program2) {
2064
2509
  handleError(err);
2065
2510
  }
2066
2511
  });
2512
+ program2.command("deploy:rollback").argument("<app>", "Application ID or name").description("Rollback to a previous deployment").option("--to <deployment-id>", "Specific deployment ID to rollback to").option("--previous", "Rollback to the immediately previous deployment").option("-w, --wait", "Wait for rollback to complete").action(async (appIdentifier, options) => {
2513
+ try {
2514
+ if (!isLoggedIn()) throw new AuthError();
2515
+ const client2 = getApiClient();
2516
+ const _spinner = startSpinner("Finding application...");
2517
+ const apps = await client2.application.allByOrganization.query();
2518
+ const appSummary = findApp3(apps, appIdentifier);
2519
+ if (!appSummary) {
2520
+ failSpinner();
2521
+ const suggestions = findSimilar(
2522
+ appIdentifier,
2523
+ apps.map((a) => a.name)
2524
+ );
2525
+ throw new NotFoundError("Application", appIdentifier, suggestions);
2526
+ }
2527
+ const deployments = await client2.deployment.all.query({
2528
+ applicationId: appSummary.applicationId
2529
+ });
2530
+ succeedSpinner();
2531
+ const successfulDeployments = deployments.filter(
2532
+ (d) => d.status === "done"
2533
+ );
2534
+ if (successfulDeployments.length === 0) {
2535
+ log("");
2536
+ log("No successful deployments found to rollback to.");
2537
+ return;
2538
+ }
2539
+ let targetDeploymentId;
2540
+ if (options.to) {
2541
+ const targetDeployment = successfulDeployments.find(
2542
+ (d) => d.deploymentId === options.to || d.deploymentId.startsWith(options.to)
2543
+ );
2544
+ if (!targetDeployment) {
2545
+ throw new NotFoundError("Deployment", options.to);
2546
+ }
2547
+ targetDeploymentId = targetDeployment.deploymentId;
2548
+ } else if (options.previous) {
2549
+ if (successfulDeployments.length < 2) {
2550
+ log("");
2551
+ log("No previous deployment to rollback to.");
2552
+ log("There must be at least 2 successful deployments.");
2553
+ return;
2554
+ }
2555
+ targetDeploymentId = successfulDeployments[1].deploymentId;
2556
+ } else {
2557
+ log("");
2558
+ log(
2559
+ `Select a deployment to rollback to for ${colors.cyan(appSummary.name)}:`
2560
+ );
2561
+ log("");
2562
+ const choices = successfulDeployments.slice(0, 10).map((d, index) => ({
2563
+ name: `${colors.cyan(d.deploymentId.slice(0, 8))} - ${d.title || "Deployment"} (${formatDate3(d.createdAt)})${index === 0 ? colors.dim(" [current]") : ""}`,
2564
+ value: d.deploymentId
2565
+ }));
2566
+ const { select: select2 } = await import("./prompts-B53LIJLG.js");
2567
+ targetDeploymentId = await select2("Select deployment:", choices);
2568
+ }
2569
+ if (!options.yes) {
2570
+ const targetDeployment = successfulDeployments.find(
2571
+ (d) => d.deploymentId === targetDeploymentId
2572
+ );
2573
+ log("");
2574
+ log(`Rolling back ${colors.cyan(appSummary.name)} to:`);
2575
+ log(` Deployment: ${colors.cyan(targetDeploymentId.slice(0, 8))}`);
2576
+ log(` Title: ${targetDeployment?.title || "Deployment"}`);
2577
+ log(` Created: ${formatDate3(targetDeployment?.createdAt)}`);
2578
+ log("");
2579
+ const { confirm: confirm2 } = await import("./prompts-B53LIJLG.js");
2580
+ const confirmed = await confirm2(
2581
+ "Are you sure you want to rollback?",
2582
+ false
2583
+ );
2584
+ if (!confirmed) {
2585
+ log("Cancelled.");
2586
+ return;
2587
+ }
2588
+ }
2589
+ const _rollbackSpinner = startSpinner("Initiating rollback...");
2590
+ const result = await client2.deployment.rollback.mutate({
2591
+ applicationId: appSummary.applicationId,
2592
+ targetDeploymentId
2593
+ });
2594
+ if (options.wait) {
2595
+ await streamDeploymentWithLogs(
2596
+ client2,
2597
+ result.deploymentId,
2598
+ appSummary.name,
2599
+ appSummary.applicationId
2600
+ );
2601
+ return;
2602
+ }
2603
+ succeedSpinner("Rollback initiated!");
2604
+ if (isJsonMode()) {
2605
+ outputData({
2606
+ deploymentId: result.deploymentId,
2607
+ rollbackFrom: targetDeploymentId,
2608
+ status: "running"
2609
+ });
2610
+ } else {
2611
+ quietOutput(result.deploymentId);
2612
+ log("");
2613
+ log(`Deployment ID: ${colors.cyan(result.deploymentId)}`);
2614
+ log(`Rolling back to: ${colors.dim(targetDeploymentId.slice(0, 8))}`);
2615
+ log("");
2616
+ log("Rollback is running in the background.");
2617
+ log(
2618
+ `Check status: ${colors.dim(`tarout deploy:status ${appSummary.applicationId.slice(0, 8)}`)}`
2619
+ );
2620
+ log(
2621
+ `View logs: ${colors.dim(`tarout deploy:logs ${result.deploymentId.slice(0, 8)}`)}`
2622
+ );
2623
+ log("");
2624
+ }
2625
+ } catch (err) {
2626
+ handleError(err);
2627
+ }
2628
+ });
2067
2629
  }
2068
2630
  function registerLogsCommand(program2) {
2069
2631
  program2.command("logs").argument("<app>", "Application ID or name").description("View application logs").option(
@@ -2076,7 +2638,7 @@ function registerLogsCommand(program2) {
2076
2638
  const client2 = getApiClient();
2077
2639
  const _spinner = startSpinner("Fetching logs...");
2078
2640
  const apps = await client2.application.allByOrganization.query();
2079
- const app = findApp2(apps, appIdentifier);
2641
+ const app = findApp3(apps, appIdentifier);
2080
2642
  if (!app) {
2081
2643
  failSpinner();
2082
2644
  const suggestions = findSimilar(
@@ -2149,7 +2711,7 @@ function registerLogsCommand(program2) {
2149
2711
  }
2150
2712
  });
2151
2713
  }
2152
- function findApp2(apps, identifier) {
2714
+ function findApp3(apps, identifier) {
2153
2715
  const lowerIdentifier = identifier.toLowerCase();
2154
2716
  return apps.find(
2155
2717
  (app) => app.applicationId === identifier || app.applicationId.startsWith(identifier) || app.name.toLowerCase() === lowerIdentifier || app.appName?.toLowerCase() === lowerIdentifier
@@ -2396,6 +2958,129 @@ function printLogLine(line) {
2396
2958
  }
2397
2959
  }
2398
2960
 
2961
+ // src/commands/dev.ts
2962
+ function registerDevCommand(program2) {
2963
+ program2.command("dev").description(
2964
+ "Run local development server with cloud environment variables"
2965
+ ).option("-a, --app <app>", "Application ID or name (overrides linked app)").option("-p, --port <port>", "Port to run the dev server on").option("-c, --command <command>", "Custom dev command to run").action(async (options) => {
2966
+ try {
2967
+ if (!isLoggedIn()) throw new AuthError();
2968
+ const profile = getCurrentProfile();
2969
+ if (!profile) throw new AuthError();
2970
+ const client2 = getApiClient();
2971
+ let applicationId;
2972
+ let appName;
2973
+ if (options.app) {
2974
+ const _spinner = startSpinner("Finding application...");
2975
+ const apps = await client2.application.allByOrganization.query();
2976
+ const app = findApp4(apps, options.app);
2977
+ if (!app) {
2978
+ failSpinner();
2979
+ const suggestions = findSimilar(
2980
+ options.app,
2981
+ apps.map((a) => a.name)
2982
+ );
2983
+ throw new NotFoundError("Application", options.app, suggestions);
2984
+ }
2985
+ applicationId = app.applicationId;
2986
+ appName = app.name;
2987
+ succeedSpinner();
2988
+ } else if (isProjectLinked()) {
2989
+ const config2 = getProjectConfig();
2990
+ if (!config2) {
2991
+ throw new CliError(
2992
+ "Project config is corrupted. Run 'tarout link' to relink."
2993
+ );
2994
+ }
2995
+ applicationId = config2.applicationId;
2996
+ appName = config2.name;
2997
+ } else {
2998
+ throw new InvalidArgumentError(
2999
+ "No linked application. Run 'tarout link' first or use --app flag."
3000
+ );
3001
+ }
3002
+ const pkg = readPackageJson();
3003
+ if (!pkg) {
3004
+ throw new CliError(
3005
+ "No package.json found in current directory. Make sure you're in a Node.js project."
3006
+ );
3007
+ }
3008
+ const pm = detectPackageManager();
3009
+ let devCommand = options.command;
3010
+ if (!devCommand) {
3011
+ devCommand = getDevCommand(pkg, pm);
3012
+ }
3013
+ const defaultPort = getDefaultPort(pkg);
3014
+ const port = options.port || defaultPort;
3015
+ const _envSpinner = startSpinner(
3016
+ `Fetching environment variables for ${appName}...`
3017
+ );
3018
+ let envVars = {};
3019
+ try {
3020
+ const variables = await client2.envVariable.list.query({
3021
+ applicationId,
3022
+ includeValues: true
3023
+ });
3024
+ envVars = envVarsToObject(variables);
3025
+ succeedSpinner(
3026
+ `Loaded ${Object.keys(envVars).length} environment variables`
3027
+ );
3028
+ } catch (err) {
3029
+ failSpinner();
3030
+ throw new CliError(
3031
+ `Failed to fetch environment variables: ${err instanceof Error ? err.message : "Unknown error"}`
3032
+ );
3033
+ }
3034
+ if (options.port) {
3035
+ envVars.PORT = String(port);
3036
+ }
3037
+ const framework = detectFramework(pkg);
3038
+ if (isJsonMode()) {
3039
+ outputData({
3040
+ applicationId,
3041
+ appName,
3042
+ command: devCommand,
3043
+ port,
3044
+ framework: framework?.name || "Unknown",
3045
+ envVarCount: Object.keys(envVars).length,
3046
+ packageManager: pm
3047
+ });
3048
+ return;
3049
+ }
3050
+ log("");
3051
+ log(colors.bold(`Running dev server for ${colors.cyan(appName)}`));
3052
+ log("");
3053
+ log(` Framework: ${colors.dim(framework?.name || "Unknown")}`);
3054
+ log(` Package Manager: ${colors.dim(pm)}`);
3055
+ log(` Command: ${colors.dim(devCommand)}`);
3056
+ log(` Port: ${colors.dim(String(port))}`);
3057
+ log(
3058
+ ` Env Variables: ${colors.dim(String(Object.keys(envVars).length))}`
3059
+ );
3060
+ log("");
3061
+ log(colors.dim("\u2500".repeat(50)));
3062
+ log("");
3063
+ const result = await runCommand(devCommand, envVars);
3064
+ if (result.signal) {
3065
+ log("");
3066
+ log(colors.dim(`Process terminated by ${result.signal}`));
3067
+ } else if (result.exitCode !== 0) {
3068
+ log("");
3069
+ log(colors.error(`Process exited with code ${result.exitCode}`));
3070
+ process.exit(result.exitCode);
3071
+ }
3072
+ } catch (err) {
3073
+ handleError(err);
3074
+ }
3075
+ });
3076
+ }
3077
+ function findApp4(apps, identifier) {
3078
+ const lowerIdentifier = identifier.toLowerCase();
3079
+ return apps.find(
3080
+ (app) => app.applicationId === identifier || app.applicationId.startsWith(identifier) || app.name.toLowerCase() === lowerIdentifier || app.appName?.toLowerCase() === lowerIdentifier
3081
+ );
3082
+ }
3083
+
2399
3084
  // src/commands/domains.ts
2400
3085
  function registerDomainsCommands(program2) {
2401
3086
  const domains = program2.command("domains").description("Manage custom domains");
@@ -2407,7 +3092,7 @@ function registerDomainsCommands(program2) {
2407
3092
  let domainsList;
2408
3093
  if (appIdentifier) {
2409
3094
  const apps = await client2.application.allByOrganization.query();
2410
- const app = findApp3(apps, appIdentifier);
3095
+ const app = findApp5(apps, appIdentifier);
2411
3096
  if (!app) {
2412
3097
  failSpinner();
2413
3098
  const suggestions = findSimilar(
@@ -2469,7 +3154,7 @@ function registerDomainsCommands(program2) {
2469
3154
  const client2 = getApiClient();
2470
3155
  const _spinner = startSpinner("Finding application...");
2471
3156
  const apps = await client2.application.allByOrganization.query();
2472
- const app = findApp3(apps, appIdentifier);
3157
+ const app = findApp5(apps, appIdentifier);
2473
3158
  if (!app) {
2474
3159
  failSpinner();
2475
3160
  const suggestions = findSimilar(
@@ -2606,7 +3291,7 @@ function registerDomainsCommands(program2) {
2606
3291
  }
2607
3292
  });
2608
3293
  }
2609
- function findApp3(apps, identifier) {
3294
+ function findApp5(apps, identifier) {
2610
3295
  const lowerIdentifier = identifier.toLowerCase();
2611
3296
  return apps.find(
2612
3297
  (app) => app.applicationId === identifier || app.applicationId.startsWith(identifier) || app.name.toLowerCase() === lowerIdentifier || app.appName?.toLowerCase() === lowerIdentifier
@@ -2629,7 +3314,7 @@ function updateSpinner2(text) {
2629
3314
  }
2630
3315
 
2631
3316
  // src/commands/env.ts
2632
- import { existsSync, readFileSync, writeFileSync } from "fs";
3317
+ import { existsSync as existsSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
2633
3318
  function registerEnvCommands(program2) {
2634
3319
  const env = program2.command("env").argument("<app>", "Application ID or name").description("Manage environment variables");
2635
3320
  env.command("list").alias("ls").description("List all environment variables").option("--reveal", "Show actual values (not masked)").action(async (options, command) => {
@@ -2639,7 +3324,7 @@ function registerEnvCommands(program2) {
2639
3324
  const client2 = getApiClient();
2640
3325
  const _spinner = startSpinner("Fetching environment variables...");
2641
3326
  const apps = await client2.application.allByOrganization.query();
2642
- const app = findApp4(apps, appIdentifier);
3327
+ const app = findApp6(apps, appIdentifier);
2643
3328
  if (!app) {
2644
3329
  failSpinner();
2645
3330
  const suggestions = findSimilar(
@@ -2704,7 +3389,7 @@ function registerEnvCommands(program2) {
2704
3389
  const client2 = getApiClient();
2705
3390
  const _spinner = startSpinner("Setting environment variable...");
2706
3391
  const apps = await client2.application.allByOrganization.query();
2707
- const app = findApp4(apps, appIdentifier);
3392
+ const app = findApp6(apps, appIdentifier);
2708
3393
  if (!app) {
2709
3394
  failSpinner();
2710
3395
  const suggestions = findSimilar(
@@ -2750,7 +3435,7 @@ function registerEnvCommands(program2) {
2750
3435
  const client2 = getApiClient();
2751
3436
  const _spinner = startSpinner("Removing environment variable...");
2752
3437
  const apps = await client2.application.allByOrganization.query();
2753
- const app = findApp4(apps, appIdentifier);
3438
+ const app = findApp6(apps, appIdentifier);
2754
3439
  if (!app) {
2755
3440
  failSpinner();
2756
3441
  const suggestions = findSimilar(
@@ -2780,7 +3465,7 @@ function registerEnvCommands(program2) {
2780
3465
  const client2 = getApiClient();
2781
3466
  const _spinner = startSpinner("Downloading environment variables...");
2782
3467
  const apps = await client2.application.allByOrganization.query();
2783
- const app = findApp4(apps, appIdentifier);
3468
+ const app = findApp6(apps, appIdentifier);
2784
3469
  if (!app) {
2785
3470
  failSpinner();
2786
3471
  const suggestions = findSimilar(
@@ -2789,7 +3474,7 @@ function registerEnvCommands(program2) {
2789
3474
  );
2790
3475
  throw new NotFoundError("Application", appIdentifier, suggestions);
2791
3476
  }
2792
- if (existsSync(options.output) && !shouldSkipConfirmation()) {
3477
+ if (existsSync3(options.output) && !shouldSkipConfirmation()) {
2793
3478
  succeedSpinner();
2794
3479
  const confirmed = await confirm(
2795
3480
  `File ${options.output} already exists. Overwrite?`,
@@ -2805,7 +3490,7 @@ function registerEnvCommands(program2) {
2805
3490
  format: "dotenv",
2806
3491
  maskSecrets: !options.reveal
2807
3492
  });
2808
- writeFileSync(options.output, result.content);
3493
+ writeFileSync2(options.output, result.content);
2809
3494
  succeedSpinner(`Saved to ${options.output}`);
2810
3495
  if (isJsonMode()) {
2811
3496
  outputData({ file: options.output, content: result.content });
@@ -2820,14 +3505,14 @@ function registerEnvCommands(program2) {
2820
3505
  try {
2821
3506
  if (!isLoggedIn()) throw new AuthError();
2822
3507
  const appIdentifier = command.parent.parent.args[0];
2823
- if (!existsSync(options.input)) {
3508
+ if (!existsSync3(options.input)) {
2824
3509
  throw new InvalidArgumentError(`File not found: ${options.input}`);
2825
3510
  }
2826
- const content = readFileSync(options.input, "utf-8");
3511
+ const content = readFileSync3(options.input, "utf-8");
2827
3512
  const client2 = getApiClient();
2828
3513
  const _spinner = startSpinner("Uploading environment variables...");
2829
3514
  const apps = await client2.application.allByOrganization.query();
2830
- const app = findApp4(apps, appIdentifier);
3515
+ const app = findApp6(apps, appIdentifier);
2831
3516
  if (!app) {
2832
3517
  failSpinner();
2833
3518
  const suggestions = findSimilar(
@@ -2856,7 +3541,7 @@ function registerEnvCommands(program2) {
2856
3541
  }
2857
3542
  });
2858
3543
  }
2859
- function findApp4(apps, identifier) {
3544
+ function findApp6(apps, identifier) {
2860
3545
  const lowerIdentifier = identifier.toLowerCase();
2861
3546
  return apps.find(
2862
3547
  (app) => app.applicationId === identifier || app.applicationId.startsWith(identifier) || app.name.toLowerCase() === lowerIdentifier || app.appName?.toLowerCase() === lowerIdentifier
@@ -2872,6 +3557,253 @@ function formatDate4(date) {
2872
3557
  return d.toLocaleDateString("en-US", { month: "short", day: "numeric" });
2873
3558
  }
2874
3559
 
3560
+ // src/commands/link.ts
3561
+ import { basename } from "path";
3562
+ function registerLinkCommands(program2) {
3563
+ program2.command("link").argument(
3564
+ "[app]",
3565
+ "Application ID or name (optional, will prompt if not provided)"
3566
+ ).description("Link local directory to a Tarout application").option("-y, --yes", "Skip confirmation prompts").action(async (appIdentifier, options) => {
3567
+ try {
3568
+ if (!isLoggedIn()) throw new AuthError();
3569
+ const profile = getCurrentProfile();
3570
+ if (!profile) throw new AuthError();
3571
+ const client2 = getApiClient();
3572
+ const cwd = process.cwd();
3573
+ const dirName = basename(cwd);
3574
+ if (isProjectLinked()) {
3575
+ const existingConfig = getProjectConfig();
3576
+ if (existingConfig && !shouldSkipConfirmation() && !options.yes) {
3577
+ log("");
3578
+ log(
3579
+ `This directory is already linked to ${colors.cyan(existingConfig.name)}`
3580
+ );
3581
+ log(`Application ID: ${colors.dim(existingConfig.applicationId)}`);
3582
+ log("");
3583
+ const confirmed = await confirm(
3584
+ "Do you want to relink to a different application?",
3585
+ false
3586
+ );
3587
+ if (!confirmed) {
3588
+ log("Cancelled.");
3589
+ return;
3590
+ }
3591
+ }
3592
+ }
3593
+ const _spinner = startSpinner("Fetching applications...");
3594
+ const apps = await client2.application.allByOrganization.query();
3595
+ succeedSpinner();
3596
+ if (apps.length === 0) {
3597
+ log("");
3598
+ log("No applications found in your organization.");
3599
+ log("");
3600
+ log(`Create one with: ${colors.dim("tarout apps create <name>")}`);
3601
+ return;
3602
+ }
3603
+ let selectedApp = null;
3604
+ if (appIdentifier) {
3605
+ selectedApp = findApp7(apps, appIdentifier) || null;
3606
+ if (!selectedApp) {
3607
+ const suggestions = findSimilar(
3608
+ appIdentifier,
3609
+ apps.map((a) => a.name)
3610
+ );
3611
+ throw new NotFoundError("Application", appIdentifier, suggestions);
3612
+ }
3613
+ } else {
3614
+ log("");
3615
+ log(`Linking ${colors.cyan(dirName)} to a Tarout application`);
3616
+ log("");
3617
+ const choices = apps.map(
3618
+ (app) => ({
3619
+ name: `${app.name} ${colors.dim(`(${app.applicationId.slice(0, 8)})`)}`,
3620
+ value: app.applicationId
3621
+ })
3622
+ );
3623
+ const selectedId = await select("Select an application:", choices);
3624
+ selectedApp = apps.find(
3625
+ (a) => a.applicationId === selectedId
3626
+ ) || null;
3627
+ }
3628
+ if (!selectedApp) {
3629
+ log("No application selected.");
3630
+ return;
3631
+ }
3632
+ setProjectConfig({
3633
+ applicationId: selectedApp.applicationId,
3634
+ name: selectedApp.name,
3635
+ organizationId: profile.organizationId,
3636
+ linkedAt: (/* @__PURE__ */ new Date()).toISOString()
3637
+ });
3638
+ if (isJsonMode()) {
3639
+ outputData({
3640
+ linked: true,
3641
+ applicationId: selectedApp.applicationId,
3642
+ name: selectedApp.name,
3643
+ directory: cwd
3644
+ });
3645
+ return;
3646
+ }
3647
+ quietOutput(selectedApp.applicationId);
3648
+ box("Project Linked", [
3649
+ `Application: ${colors.cyan(selectedApp.name)}`,
3650
+ `ID: ${colors.dim(selectedApp.applicationId)}`,
3651
+ `Directory: ${colors.dim(cwd)}`
3652
+ ]);
3653
+ log("You can now use:");
3654
+ log(
3655
+ ` ${colors.dim("tarout dev")} - Run dev server with cloud env vars`
3656
+ );
3657
+ log(
3658
+ ` ${colors.dim("tarout build")} - Build locally with cloud env vars`
3659
+ );
3660
+ log(` ${colors.dim("tarout deploy")} - Deploy to cloud`);
3661
+ log("");
3662
+ } catch (err) {
3663
+ handleError(err);
3664
+ }
3665
+ });
3666
+ program2.command("unlink").description("Unlink the current directory from Tarout").action(async () => {
3667
+ try {
3668
+ const config2 = getProjectConfig();
3669
+ if (!config2) {
3670
+ log("");
3671
+ log("This directory is not linked to any Tarout application.");
3672
+ log("");
3673
+ log(`Link with: ${colors.dim("tarout link")}`);
3674
+ return;
3675
+ }
3676
+ if (!shouldSkipConfirmation()) {
3677
+ log("");
3678
+ log(`Currently linked to: ${colors.cyan(config2.name)}`);
3679
+ log(`Application ID: ${colors.dim(config2.applicationId)}`);
3680
+ log("");
3681
+ const confirmed = await confirm(
3682
+ "Are you sure you want to unlink this directory?",
3683
+ false
3684
+ );
3685
+ if (!confirmed) {
3686
+ log("Cancelled.");
3687
+ return;
3688
+ }
3689
+ }
3690
+ const removed = removeProjectConfig();
3691
+ if (isJsonMode()) {
3692
+ outputData({
3693
+ unlinked: removed,
3694
+ applicationId: config2.applicationId,
3695
+ name: config2.name
3696
+ });
3697
+ return;
3698
+ }
3699
+ if (removed) {
3700
+ log("");
3701
+ log(colors.success(`Unlinked from ${config2.name}`));
3702
+ log("");
3703
+ } else {
3704
+ log("");
3705
+ log(colors.warn("Could not remove project configuration."));
3706
+ log("");
3707
+ }
3708
+ } catch (err) {
3709
+ handleError(err);
3710
+ }
3711
+ });
3712
+ program2.command("status").description("Show link status for current directory").action(async () => {
3713
+ try {
3714
+ const config2 = getProjectConfig();
3715
+ if (!config2) {
3716
+ if (isJsonMode()) {
3717
+ outputData({ linked: false });
3718
+ return;
3719
+ }
3720
+ log("");
3721
+ log("This directory is not linked to any Tarout application.");
3722
+ log("");
3723
+ log(`Link with: ${colors.dim("tarout link")}`);
3724
+ return;
3725
+ }
3726
+ if (isLoggedIn()) {
3727
+ const client2 = getApiClient();
3728
+ const _spinner = startSpinner("Fetching application status...");
3729
+ try {
3730
+ const app = await client2.application.one.query({
3731
+ applicationId: config2.applicationId
3732
+ });
3733
+ succeedSpinner();
3734
+ if (isJsonMode()) {
3735
+ outputData({
3736
+ linked: true,
3737
+ applicationId: config2.applicationId,
3738
+ name: config2.name,
3739
+ status: app.applicationStatus,
3740
+ url: app.cloudServiceUrl,
3741
+ linkedAt: config2.linkedAt
3742
+ });
3743
+ return;
3744
+ }
3745
+ log("");
3746
+ log(colors.bold("Project Status"));
3747
+ log("");
3748
+ log(`Application: ${colors.cyan(app.name)}`);
3749
+ log(`ID: ${colors.dim(config2.applicationId)}`);
3750
+ log(`Status: ${getStatusIndicator(app.applicationStatus)}`);
3751
+ if (app.cloudServiceUrl) {
3752
+ log(`URL: ${colors.cyan(app.cloudServiceUrl)}`);
3753
+ }
3754
+ log(`Linked: ${new Date(config2.linkedAt).toLocaleString()}`);
3755
+ log("");
3756
+ } catch {
3757
+ failSpinner();
3758
+ log("");
3759
+ log(colors.warn("Warning: Could not fetch application status."));
3760
+ log("The application may have been deleted.");
3761
+ log("");
3762
+ log(`Unlink with: ${colors.dim("tarout unlink")}`);
3763
+ log("");
3764
+ }
3765
+ } else {
3766
+ if (isJsonMode()) {
3767
+ outputData({
3768
+ linked: true,
3769
+ applicationId: config2.applicationId,
3770
+ name: config2.name,
3771
+ linkedAt: config2.linkedAt
3772
+ });
3773
+ return;
3774
+ }
3775
+ log("");
3776
+ log(colors.bold("Project Status"));
3777
+ log("");
3778
+ log(`Application: ${colors.cyan(config2.name)}`);
3779
+ log(`ID: ${colors.dim(config2.applicationId)}`);
3780
+ log(`Linked: ${new Date(config2.linkedAt).toLocaleString()}`);
3781
+ log("");
3782
+ log(colors.dim("Log in to see full application status."));
3783
+ log("");
3784
+ }
3785
+ } catch (err) {
3786
+ handleError(err);
3787
+ }
3788
+ });
3789
+ }
3790
+ function findApp7(apps, identifier) {
3791
+ const lowerIdentifier = identifier.toLowerCase();
3792
+ return apps.find(
3793
+ (app) => app.applicationId === identifier || app.applicationId.startsWith(identifier) || app.name.toLowerCase() === lowerIdentifier || app.appName?.toLowerCase() === lowerIdentifier
3794
+ );
3795
+ }
3796
+ function getStatusIndicator(status) {
3797
+ const indicators = {
3798
+ running: colors.success("running"),
3799
+ idle: colors.warn("idle"),
3800
+ error: colors.error("error"),
3801
+ deploying: colors.info("deploying"),
3802
+ stopped: colors.dim("stopped")
3803
+ };
3804
+ return indicators[status.toLowerCase()] || status;
3805
+ }
3806
+
2875
3807
  // src/commands/orgs.ts
2876
3808
  function registerOrgsCommands(program2) {
2877
3809
  const orgs = program2.command("orgs").description("Manage organizations");
@@ -3060,4 +3992,7 @@ registerDbCommands(program);
3060
3992
  registerDomainsCommands(program);
3061
3993
  registerOrgsCommands(program);
3062
3994
  registerEnvsCommands(program);
3995
+ registerLinkCommands(program);
3996
+ registerDevCommand(program);
3997
+ registerBuildCommand(program);
3063
3998
  program.parse();
@@ -0,0 +1,12 @@
1
+ import {
2
+ confirm,
3
+ input,
4
+ password,
5
+ select
6
+ } from "./chunk-VO4OYJW3.js";
7
+ export {
8
+ confirm,
9
+ input,
10
+ password,
11
+ select
12
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarout/cli",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "Tarout PaaS Command Line Interface",
5
5
  "type": "module",
6
6
  "bin": {