thinkwork-cli 0.3.1 → 0.3.2

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/README.md CHANGED
@@ -64,6 +64,8 @@ No repo clone required — `thinkwork init` scaffolds all Terraform modules from
64
64
 
65
65
  | Command | Description |
66
66
  |---------|-------------|
67
+ | `thinkwork status` | Discover all deployed environments in AWS (clickable URLs) |
68
+ | `thinkwork status -s <stage>` | Detailed view for one environment |
67
69
  | `thinkwork outputs -s <stage>` | Show deployment outputs (API URL, Cognito IDs, etc.) |
68
70
  | `thinkwork config list` | List all initialized environments |
69
71
  | `thinkwork config list -s <stage>` | Show full config for an environment (secrets masked) |
@@ -110,6 +112,25 @@ All initialized environments are saved to `~/.thinkwork/environments/<stage>/con
110
112
 
111
113
  ## Examples
112
114
 
115
+ ### Check all environments
116
+
117
+ ```bash
118
+ thinkwork status
119
+ ```
120
+
121
+ ```
122
+ Environments
123
+ ──────────────────────────────────────────────────────────────────────
124
+ Stage Source Lambdas AgentCore Memory URLs
125
+ ──────────────────────────────────────────────────────────────────────
126
+ ● dev aws+cli 42 active hindsight ✓ API: https://ho7oy...
127
+ WS: dcrs2r...
128
+ Mem: http://tw-dev...
129
+ ──────────────────────────────────────────────────────────────────────
130
+ ```
131
+
132
+ URLs are clickable in supported terminals (iTerm2, Windows Terminal, VS Code, etc.).
133
+
113
134
  ### Switch memory engine
114
135
 
115
136
  ```bash
package/dist/cli.js CHANGED
@@ -1276,6 +1276,10 @@ output "hindsight_endpoint" { value = module.thinkwork.hindsight_endpoint }
1276
1276
  // src/commands/status.ts
1277
1277
  import { execSync as execSync6 } from "child_process";
1278
1278
  import chalk6 from "chalk";
1279
+ function link(url, label) {
1280
+ const text = label || url;
1281
+ return `\x1B]8;;${url}\x1B\\${text}\x1B]8;;\x1B\\`;
1282
+ }
1279
1283
  function runAws(cmd) {
1280
1284
  try {
1281
1285
  return execSync6(`aws ${cmd}`, {
@@ -1308,6 +1312,10 @@ function discoverAwsStages(region) {
1308
1312
  `apigatewayv2 get-apis --region ${region} --query "Items[?Name=='thinkwork-${stage}-api'].ApiEndpoint|[0]" --output text`
1309
1313
  );
1310
1314
  if (apiRaw && apiRaw !== "None") info.apiEndpoint = apiRaw;
1315
+ const appsyncRaw = runAws(
1316
+ `appsync list-graphql-apis --region ${region} --query "graphqlApis[?name=='thinkwork-${stage}-subscriptions'].uris.REALTIME|[0]" --output text`
1317
+ );
1318
+ if (appsyncRaw && appsyncRaw !== "None") info.appsyncUrl = appsyncRaw;
1311
1319
  const acRaw = runAws(
1312
1320
  `lambda get-function --function-name thinkwork-${stage}-agentcore --region ${region} --query "Configuration.State" --output text 2>/dev/null`
1313
1321
  );
@@ -1325,6 +1333,7 @@ function discoverAwsStages(region) {
1325
1333
  `elbv2 describe-load-balancers --region ${region} --query "LoadBalancers[?contains(LoadBalancerName, 'tw-${stage}-hindsight')].DNSName|[0]" --output text`
1326
1334
  );
1327
1335
  if (albRaw && albRaw !== "None") {
1336
+ info.hindsightEndpoint = `http://${albRaw}`;
1328
1337
  try {
1329
1338
  const health = execSync6(`curl -s --max-time 3 http://${albRaw}/health`, { encoding: "utf-8" }).trim();
1330
1339
  info.hindsightHealth = health.includes("healthy") ? "healthy" : "unhealthy";
@@ -1381,19 +1390,21 @@ function registerStatusCommand(program2) {
1381
1390
  console.log(` ${chalk6.bold("Source:")} ${info.source === "both" ? "AWS + local config" : info.source === "aws" ? "AWS (no local config)" : "local only (not in AWS)"}`);
1382
1391
  console.log(` ${chalk6.bold("Region:")} ${info.region}`);
1383
1392
  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}`);
1393
+ console.log(` ${chalk6.bold("Lambda fns:")} ${info.lambdaCount || "\u2014"}`);
1386
1394
  console.log(` ${chalk6.bold("AgentCore:")} ${info.agentcoreStatus || "unknown"}`);
1387
1395
  console.log(` ${chalk6.bold("Memory:")} ${info.memoryEngine || "unknown"}`);
1388
1396
  if (info.hindsightHealth) console.log(` ${chalk6.bold("Hindsight:")} ${info.hindsightHealth}`);
1389
1397
  if (info.bucketName) console.log(` ${chalk6.bold("S3 bucket:")} ${info.bucketName}`);
1398
+ console.log("");
1399
+ console.log(chalk6.bold(" URLs:"));
1400
+ if (info.apiEndpoint) console.log(` API: ${link(info.apiEndpoint)}`);
1401
+ if (info.appsyncUrl) console.log(` WebSocket: ${link(info.appsyncUrl)}`);
1402
+ if (info.hindsightEndpoint) console.log(` Hindsight: ${link(info.hindsightEndpoint)}`);
1390
1403
  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
1404
  const local = loadEnvironment(opts.stage);
1392
1405
  if (local) {
1393
- console.log("");
1394
1406
  console.log(chalk6.dim(` Terraform dir: ${local.terraformDir}`));
1395
1407
  } else {
1396
- console.log("");
1397
1408
  console.log(chalk6.dim(` No local config. Run: thinkwork init -s ${opts.stage}`));
1398
1409
  }
1399
1410
  console.log("");
@@ -1405,22 +1416,40 @@ function registerStatusCommand(program2) {
1405
1416
  console.log("");
1406
1417
  return;
1407
1418
  }
1419
+ const COL1 = 16;
1420
+ const COL2 = 10;
1421
+ const COL3 = 10;
1422
+ const COL4 = 14;
1423
+ const COL5 = 14;
1424
+ const pad = " ".repeat(2 + 2 + COL1 + COL2 + COL3 + COL4 + COL5);
1408
1425
  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"));
1426
+ 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\u2500\u2500\u2500\u2500"));
1410
1427
  console.log(
1411
- chalk6.dim(" ") + "Stage".padEnd(16) + "Source".padEnd(10) + "Lambdas".padEnd(10) + "AgentCore".padEnd(14) + "Memory".padEnd(14) + "API"
1428
+ chalk6.dim(" ") + "Stage".padEnd(COL1) + "Source".padEnd(COL2) + "Lambdas".padEnd(COL3) + "AgentCore".padEnd(COL4) + "Memory".padEnd(COL5) + "URLs"
1412
1429
  );
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"));
1430
+ 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\u2500\u2500\u2500\u2500"));
1414
1431
  for (const [, info] of [...merged].sort((a, b) => a[0].localeCompare(b[0]))) {
1415
1432
  const sourceBadge = info.source === "both" ? chalk6.green("\u25CF") : info.source === "aws" ? chalk6.yellow("\u25CF") : chalk6.dim("\u25CB");
1416
1433
  const acStatus = info.agentcoreStatus === "Active" ? chalk6.green("active") : info.agentcoreStatus === "not deployed" ? chalk6.dim("\u2014") : chalk6.yellow(info.agentcoreStatus || "\u2014");
1417
1434
  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
- );
1435
+ const prefix = ` ${sourceBadge} ` + chalk6.bold(info.stage.padEnd(COL1 - 1)) + " " + (info.source === "both" ? "aws+cli" : info.source).padEnd(COL2) + String(info.lambdaCount || "\u2014").padEnd(COL3);
1436
+ const urls = [];
1437
+ if (info.apiEndpoint) urls.push(`API: ${link(info.apiEndpoint, info.apiEndpoint)}`);
1438
+ if (info.appsyncUrl) urls.push(`WS: ${link(info.appsyncUrl, info.appsyncUrl.replace("wss://", "").split(".")[0] + "...")}`);
1439
+ if (info.hindsightEndpoint) urls.push(`Mem: ${link(info.hindsightEndpoint, info.hindsightEndpoint)}`);
1440
+ if (urls.length === 0) {
1441
+ console.log(prefix + acStatus.padEnd(22) + memBadge.padEnd(22) + chalk6.dim("\u2014"));
1442
+ } else {
1443
+ console.log(prefix + acStatus.padEnd(22) + memBadge.padEnd(22) + chalk6.dim(urls[0]));
1444
+ for (let i = 1; i < urls.length; i++) {
1445
+ console.log(pad + chalk6.dim(urls[i]));
1446
+ }
1447
+ }
1421
1448
  }
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"));
1449
+ 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\u2500\u2500\u2500\u2500"));
1450
+ console.log(
1451
+ 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")
1452
+ );
1424
1453
  console.log("");
1425
1454
  console.log(` Details: ${chalk6.cyan("thinkwork status -s <stage>")}`);
1426
1455
  console.log("");
@@ -152,7 +152,7 @@ variable "memory_engine" {
152
152
  variable "hindsight_image_tag" {
153
153
  description = "Hindsight Docker image tag (only used when memory_engine = 'hindsight')"
154
154
  type = string
155
- default = "0.4.22"
155
+ default = "0.5.0"
156
156
  }
157
157
 
158
158
  variable "agentcore_memory_id" {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thinkwork-cli",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Thinkwork CLI — deploy, manage, and interact with your Thinkwork stack",
5
5
  "license": "MIT",
6
6
  "type": "module",