thinkwork-cli 0.3.0 → 0.3.1
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/cli.js
CHANGED
|
@@ -738,8 +738,8 @@ function registerBootstrapCommand(program2) {
|
|
|
738
738
|
bucket = await getTerraformOutput(cwd, "bucket_name");
|
|
739
739
|
dbEndpoint = await getTerraformOutput(cwd, "db_cluster_endpoint");
|
|
740
740
|
const secretArn = await getTerraformOutput(cwd, "db_secret_arn");
|
|
741
|
-
const { execSync:
|
|
742
|
-
const secretJson =
|
|
741
|
+
const { execSync: execSync7 } = await import("child_process");
|
|
742
|
+
const secretJson = execSync7(
|
|
743
743
|
`aws secretsmanager get-secret-value --secret-id "${secretArn}" --query SecretString --output text`,
|
|
744
744
|
{ encoding: "utf-8" }
|
|
745
745
|
).trim();
|
|
@@ -1273,6 +1273,160 @@ output "hindsight_endpoint" { value = module.thinkwork.hindsight_endpoint }
|
|
|
1273
1273
|
});
|
|
1274
1274
|
}
|
|
1275
1275
|
|
|
1276
|
+
// src/commands/status.ts
|
|
1277
|
+
import { execSync as execSync6 } from "child_process";
|
|
1278
|
+
import chalk6 from "chalk";
|
|
1279
|
+
function runAws(cmd) {
|
|
1280
|
+
try {
|
|
1281
|
+
return execSync6(`aws ${cmd}`, {
|
|
1282
|
+
encoding: "utf-8",
|
|
1283
|
+
timeout: 15e3,
|
|
1284
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
1285
|
+
}).trim();
|
|
1286
|
+
} catch {
|
|
1287
|
+
return null;
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
function discoverAwsStages(region) {
|
|
1291
|
+
const stages = /* @__PURE__ */ new Map();
|
|
1292
|
+
const raw = runAws(
|
|
1293
|
+
`lambda list-functions --region ${region} --query "Functions[?starts_with(FunctionName, 'thinkwork-')].FunctionName" --output json`
|
|
1294
|
+
);
|
|
1295
|
+
if (!raw) return stages;
|
|
1296
|
+
const functions = JSON.parse(raw);
|
|
1297
|
+
for (const fn of functions) {
|
|
1298
|
+
const match = fn.match(/^thinkwork-(.+?)-api-graphql-http$/);
|
|
1299
|
+
if (match) {
|
|
1300
|
+
const stage = match[1];
|
|
1301
|
+
stages.set(stage, { stage, source: "aws", region });
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
for (const [stage, info] of stages) {
|
|
1305
|
+
const count = functions.filter((f) => f.startsWith(`thinkwork-${stage}-`)).length;
|
|
1306
|
+
info.lambdaCount = count;
|
|
1307
|
+
const apiRaw = runAws(
|
|
1308
|
+
`apigatewayv2 get-apis --region ${region} --query "Items[?Name=='thinkwork-${stage}-api'].ApiEndpoint|[0]" --output text`
|
|
1309
|
+
);
|
|
1310
|
+
if (apiRaw && apiRaw !== "None") info.apiEndpoint = apiRaw;
|
|
1311
|
+
const acRaw = runAws(
|
|
1312
|
+
`lambda get-function --function-name thinkwork-${stage}-agentcore --region ${region} --query "Configuration.State" --output text 2>/dev/null`
|
|
1313
|
+
);
|
|
1314
|
+
info.agentcoreStatus = acRaw || "not deployed";
|
|
1315
|
+
const bucketRaw = runAws(
|
|
1316
|
+
`s3api head-bucket --bucket thinkwork-${stage}-storage --region ${region} 2>/dev/null && echo "exists"`
|
|
1317
|
+
);
|
|
1318
|
+
info.bucketName = bucketRaw ? `thinkwork-${stage}-storage` : void 0;
|
|
1319
|
+
const ecsRaw = runAws(
|
|
1320
|
+
`ecs describe-services --cluster thinkwork-${stage}-cluster --services thinkwork-${stage}-hindsight --region ${region} --query "services[0].runningCount" --output text 2>/dev/null`
|
|
1321
|
+
);
|
|
1322
|
+
if (ecsRaw && ecsRaw !== "None" && ecsRaw !== "0") {
|
|
1323
|
+
info.memoryEngine = "hindsight";
|
|
1324
|
+
const albRaw = runAws(
|
|
1325
|
+
`elbv2 describe-load-balancers --region ${region} --query "LoadBalancers[?contains(LoadBalancerName, 'tw-${stage}-hindsight')].DNSName|[0]" --output text`
|
|
1326
|
+
);
|
|
1327
|
+
if (albRaw && albRaw !== "None") {
|
|
1328
|
+
try {
|
|
1329
|
+
const health = execSync6(`curl -s --max-time 3 http://${albRaw}/health`, { encoding: "utf-8" }).trim();
|
|
1330
|
+
info.hindsightHealth = health.includes("healthy") ? "healthy" : "unhealthy";
|
|
1331
|
+
} catch {
|
|
1332
|
+
info.hindsightHealth = "unreachable";
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
} else {
|
|
1336
|
+
info.memoryEngine = "managed";
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
return stages;
|
|
1340
|
+
}
|
|
1341
|
+
function registerStatusCommand(program2) {
|
|
1342
|
+
program2.command("status").description("Show all Thinkwork environments (AWS + local)").option("-s, --stage <name>", "Show details for a specific stage").option("--region <region>", "AWS region to scan", "us-east-1").action(async (opts) => {
|
|
1343
|
+
const identity = getAwsIdentity();
|
|
1344
|
+
printHeader("status", opts.stage || "all", identity);
|
|
1345
|
+
if (!identity) {
|
|
1346
|
+
printError("AWS credentials not configured. Run `thinkwork login` first.");
|
|
1347
|
+
process.exit(1);
|
|
1348
|
+
}
|
|
1349
|
+
console.log(chalk6.dim(" Scanning AWS account for Thinkwork deployments...\n"));
|
|
1350
|
+
const awsStages = discoverAwsStages(opts.region);
|
|
1351
|
+
const localEnvs = listEnvironments();
|
|
1352
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1353
|
+
for (const [stage, info] of awsStages) {
|
|
1354
|
+
const local = localEnvs.find((e) => e.stage === stage);
|
|
1355
|
+
merged.set(stage, {
|
|
1356
|
+
stage,
|
|
1357
|
+
source: local ? "both" : "aws",
|
|
1358
|
+
region: opts.region,
|
|
1359
|
+
accountId: identity.account,
|
|
1360
|
+
...info
|
|
1361
|
+
});
|
|
1362
|
+
}
|
|
1363
|
+
for (const env of localEnvs) {
|
|
1364
|
+
if (!merged.has(env.stage)) {
|
|
1365
|
+
merged.set(env.stage, {
|
|
1366
|
+
stage: env.stage,
|
|
1367
|
+
source: "local",
|
|
1368
|
+
region: env.region,
|
|
1369
|
+
accountId: env.accountId
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
if (opts.stage) {
|
|
1374
|
+
const info = merged.get(opts.stage);
|
|
1375
|
+
if (!info) {
|
|
1376
|
+
printError(`No environment "${opts.stage}" found in AWS or local config.`);
|
|
1377
|
+
process.exit(1);
|
|
1378
|
+
}
|
|
1379
|
+
console.log(chalk6.bold.cyan(` \u2B21 ${info.stage}`));
|
|
1380
|
+
console.log(chalk6.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1381
|
+
console.log(` ${chalk6.bold("Source:")} ${info.source === "both" ? "AWS + local config" : info.source === "aws" ? "AWS (no local config)" : "local only (not in AWS)"}`);
|
|
1382
|
+
console.log(` ${chalk6.bold("Region:")} ${info.region}`);
|
|
1383
|
+
console.log(` ${chalk6.bold("Account:")} ${info.accountId}`);
|
|
1384
|
+
if (info.apiEndpoint) console.log(` ${chalk6.bold("API:")} ${info.apiEndpoint}`);
|
|
1385
|
+
if (info.lambdaCount) console.log(` ${chalk6.bold("Lambda fns:")} ${info.lambdaCount}`);
|
|
1386
|
+
console.log(` ${chalk6.bold("AgentCore:")} ${info.agentcoreStatus || "unknown"}`);
|
|
1387
|
+
console.log(` ${chalk6.bold("Memory:")} ${info.memoryEngine || "unknown"}`);
|
|
1388
|
+
if (info.hindsightHealth) console.log(` ${chalk6.bold("Hindsight:")} ${info.hindsightHealth}`);
|
|
1389
|
+
if (info.bucketName) console.log(` ${chalk6.bold("S3 bucket:")} ${info.bucketName}`);
|
|
1390
|
+
console.log(chalk6.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1391
|
+
const local = loadEnvironment(opts.stage);
|
|
1392
|
+
if (local) {
|
|
1393
|
+
console.log("");
|
|
1394
|
+
console.log(chalk6.dim(` Terraform dir: ${local.terraformDir}`));
|
|
1395
|
+
} else {
|
|
1396
|
+
console.log("");
|
|
1397
|
+
console.log(chalk6.dim(` No local config. Run: thinkwork init -s ${opts.stage}`));
|
|
1398
|
+
}
|
|
1399
|
+
console.log("");
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
if (merged.size === 0) {
|
|
1403
|
+
console.log(" No Thinkwork environments found.");
|
|
1404
|
+
console.log(` Run ${chalk6.cyan("thinkwork init -s <stage>")} to create one.`);
|
|
1405
|
+
console.log("");
|
|
1406
|
+
return;
|
|
1407
|
+
}
|
|
1408
|
+
console.log(chalk6.bold(" Environments"));
|
|
1409
|
+
console.log(chalk6.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1410
|
+
console.log(
|
|
1411
|
+
chalk6.dim(" ") + "Stage".padEnd(16) + "Source".padEnd(10) + "Lambdas".padEnd(10) + "AgentCore".padEnd(14) + "Memory".padEnd(14) + "API"
|
|
1412
|
+
);
|
|
1413
|
+
console.log(chalk6.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1414
|
+
for (const [, info] of [...merged].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
1415
|
+
const sourceBadge = info.source === "both" ? chalk6.green("\u25CF") : info.source === "aws" ? chalk6.yellow("\u25CF") : chalk6.dim("\u25CB");
|
|
1416
|
+
const acStatus = info.agentcoreStatus === "Active" ? chalk6.green("active") : info.agentcoreStatus === "not deployed" ? chalk6.dim("\u2014") : chalk6.yellow(info.agentcoreStatus || "\u2014");
|
|
1417
|
+
const memBadge = info.memoryEngine === "hindsight" ? info.hindsightHealth === "healthy" ? chalk6.magenta("hindsight \u2713") : chalk6.yellow("hindsight ?") : chalk6.dim(info.memoryEngine || "\u2014");
|
|
1418
|
+
console.log(
|
|
1419
|
+
` ${sourceBadge} ` + chalk6.bold(info.stage.padEnd(14)) + (info.source === "both" ? "aws+cli" : info.source).padEnd(10) + String(info.lambdaCount || "\u2014").padEnd(10) + acStatus.padEnd(22) + memBadge.padEnd(22) + chalk6.dim(info.apiEndpoint || "\u2014")
|
|
1420
|
+
);
|
|
1421
|
+
}
|
|
1422
|
+
console.log(chalk6.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
1423
|
+
console.log(chalk6.dim(` ${merged.size} environment(s) `) + chalk6.green("\u25CF") + chalk6.dim(" aws+cli ") + chalk6.yellow("\u25CF") + chalk6.dim(" aws only ") + chalk6.dim("\u25CB local only"));
|
|
1424
|
+
console.log("");
|
|
1425
|
+
console.log(` Details: ${chalk6.cyan("thinkwork status -s <stage>")}`);
|
|
1426
|
+
console.log("");
|
|
1427
|
+
});
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1276
1430
|
// src/cli.ts
|
|
1277
1431
|
var program = new Command();
|
|
1278
1432
|
program.name("thinkwork").description(
|
|
@@ -1294,6 +1448,7 @@ registerPlanCommand(program);
|
|
|
1294
1448
|
registerDeployCommand(program);
|
|
1295
1449
|
registerBootstrapCommand(program);
|
|
1296
1450
|
registerDestroyCommand(program);
|
|
1451
|
+
registerStatusCommand(program);
|
|
1297
1452
|
registerOutputsCommand(program);
|
|
1298
1453
|
registerConfigCommand(program);
|
|
1299
1454
|
program.parse();
|