@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.
- package/dist/chunk-VO4OYJW3.js +53 -0
- package/dist/index.js +993 -58
- package/dist/prompts-B53LIJLG.js +12 -0
- package/package.json +1 -1
|
@@ -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/
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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
|
-
|
|
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 (!
|
|
3508
|
+
if (!existsSync3(options.input)) {
|
|
2824
3509
|
throw new InvalidArgumentError(`File not found: ${options.input}`);
|
|
2825
3510
|
}
|
|
2826
|
-
const content =
|
|
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 =
|
|
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
|
|
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();
|