thinkwork-cli 0.5.1 → 0.5.3
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 +16 -11
- package/dist/cli.js +437 -83
- package/dist/terraform/examples/greenfield/main.tf +14 -9
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +7 -4
- package/dist/terraform/modules/app/agentcore-memory/README.md +77 -0
- package/dist/terraform/modules/app/agentcore-memory/main.tf +159 -0
- package/dist/terraform/modules/app/agentcore-memory/scripts/create_or_find_memory.sh +212 -0
- package/dist/terraform/modules/app/agentcore-runtime/main.tf +29 -18
- package/dist/terraform/modules/app/hindsight-memory/README.md +50 -29
- package/dist/terraform/modules/app/hindsight-memory/main.tf +9 -7
- package/dist/terraform/modules/app/lambda-api/handlers.tf +43 -38
- package/dist/terraform/modules/app/lambda-api/main.tf +58 -0
- package/dist/terraform/modules/app/lambda-api/variables.tf +35 -5
- package/dist/terraform/modules/thinkwork/main.tf +44 -18
- package/dist/terraform/modules/thinkwork/outputs.tf +10 -5
- package/dist/terraform/modules/thinkwork/variables.tf +12 -6
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -534,24 +534,28 @@ function resolveTerraformDir(stage) {
|
|
|
534
534
|
}
|
|
535
535
|
|
|
536
536
|
// src/commands/config.ts
|
|
537
|
-
var VALID_MEMORY_ENGINES = ["managed", "hindsight"];
|
|
538
537
|
function readTfVar(tfvarsPath, key) {
|
|
539
538
|
if (!existsSync3(tfvarsPath)) return null;
|
|
540
539
|
const content = readFileSync2(tfvarsPath, "utf-8");
|
|
541
|
-
const
|
|
542
|
-
|
|
540
|
+
const quoted = content.match(new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, "m"));
|
|
541
|
+
if (quoted) return quoted[1];
|
|
542
|
+
const bare = content.match(new RegExp(`^${key}\\s*=\\s*([^\\s#]+)`, "m"));
|
|
543
|
+
return bare ? bare[1] : null;
|
|
543
544
|
}
|
|
545
|
+
var BARE_KEYS = /* @__PURE__ */ new Set(["enable_hindsight"]);
|
|
544
546
|
function setTfVar(tfvarsPath, key, value) {
|
|
545
547
|
if (!existsSync3(tfvarsPath)) {
|
|
546
548
|
throw new Error(`terraform.tfvars not found at ${tfvarsPath}`);
|
|
547
549
|
}
|
|
548
550
|
let content = readFileSync2(tfvarsPath, "utf-8");
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
551
|
+
const bare = BARE_KEYS.has(key);
|
|
552
|
+
const newLine = bare ? `${key} = ${value}` : `${key} = "${value}"`;
|
|
553
|
+
const existingRegex = new RegExp(`^${key}\\s*=\\s*(?:"[^"]*"|[^\\s#]+)`, "m");
|
|
554
|
+
if (existingRegex.test(content)) {
|
|
555
|
+
content = content.replace(existingRegex, newLine);
|
|
552
556
|
} else {
|
|
553
557
|
content += `
|
|
554
|
-
${
|
|
558
|
+
${newLine}
|
|
555
559
|
`;
|
|
556
560
|
}
|
|
557
561
|
writeFileSync2(tfvarsPath, content);
|
|
@@ -581,7 +585,7 @@ function registerConfigCommand(program2) {
|
|
|
581
585
|
console.log(` ${chalk3.bold("Region:")} ${env.region}`);
|
|
582
586
|
console.log(` ${chalk3.bold("Account:")} ${env.accountId}`);
|
|
583
587
|
console.log(` ${chalk3.bold("Database:")} ${env.databaseEngine}`);
|
|
584
|
-
console.log(` ${chalk3.bold("Memory:")} ${env.
|
|
588
|
+
console.log(` ${chalk3.bold("Memory:")} managed (always on)${env.enableHindsight ? " + hindsight" : ""}`);
|
|
585
589
|
console.log(` ${chalk3.bold("Terraform dir:")} ${env.terraformDir}`);
|
|
586
590
|
console.log(` ${chalk3.bold("Created:")} ${env.createdAt}`);
|
|
587
591
|
console.log(` ${chalk3.bold("Updated:")} ${env.updatedAt}`);
|
|
@@ -622,7 +626,7 @@ function registerConfigCommand(program2) {
|
|
|
622
626
|
console.log(chalk3.bold(" Environments"));
|
|
623
627
|
console.log(chalk3.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"));
|
|
624
628
|
for (const env of envs) {
|
|
625
|
-
const memBadge = env.
|
|
629
|
+
const memBadge = env.enableHindsight ? chalk3.magenta("managed+hindsight") : chalk3.dim("managed");
|
|
626
630
|
const dbBadge = env.databaseEngine === "rds-postgres" ? chalk3.yellow("rds") : chalk3.dim("aurora");
|
|
627
631
|
console.log(
|
|
628
632
|
` ${chalk3.bold.cyan(env.stage.padEnd(16))}${env.region.padEnd(14)}${env.accountId.padEnd(16)}${dbBadge.padEnd(20)}${memBadge}`
|
|
@@ -634,7 +638,7 @@ function registerConfigCommand(program2) {
|
|
|
634
638
|
console.log(` Show details: ${chalk3.cyan("thinkwork config list -s <stage>")}`);
|
|
635
639
|
console.log("");
|
|
636
640
|
});
|
|
637
|
-
config.command("get <key>").description("Get a configuration value (e.g.
|
|
641
|
+
config.command("get <key>").description("Get a configuration value (e.g. enable-hindsight)").requiredOption("-s, --stage <name>", "Deployment stage").action((key, opts) => {
|
|
638
642
|
const stageCheck = validateStage(opts.stage);
|
|
639
643
|
if (!stageCheck.valid) {
|
|
640
644
|
printError(stageCheck.error);
|
|
@@ -655,17 +659,27 @@ function registerConfigCommand(program2) {
|
|
|
655
659
|
printError(stageCheck.error);
|
|
656
660
|
process.exit(1);
|
|
657
661
|
}
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
662
|
+
let tfKey = key.replace(/-/g, "_");
|
|
663
|
+
let tfValue = value;
|
|
664
|
+
if (tfKey === "memory_engine") {
|
|
665
|
+
if (value !== "managed" && value !== "hindsight") {
|
|
666
|
+
printError(`Invalid memory engine "${value}". Must be 'managed' or 'hindsight'. Note: memory_engine is deprecated \u2014 use enable_hindsight instead.`);
|
|
667
|
+
process.exit(1);
|
|
668
|
+
}
|
|
669
|
+
printWarning("memory_engine is deprecated \u2014 translating to enable_hindsight. Managed memory is always on.");
|
|
670
|
+
tfKey = "enable_hindsight";
|
|
671
|
+
tfValue = value === "hindsight" ? "true" : "false";
|
|
672
|
+
}
|
|
673
|
+
if (tfKey === "enable_hindsight" && tfValue !== "true" && tfValue !== "false") {
|
|
674
|
+
printError(`Invalid enable_hindsight value "${tfValue}". Must be 'true' or 'false'.`);
|
|
661
675
|
process.exit(1);
|
|
662
676
|
}
|
|
663
677
|
const identity = getAwsIdentity();
|
|
664
678
|
printHeader("config set", opts.stage, identity);
|
|
665
679
|
const tfvarsPath = resolveTfvarsPath(opts.stage);
|
|
666
680
|
const oldValue = readTfVar(tfvarsPath, tfKey);
|
|
667
|
-
setTfVar(tfvarsPath, tfKey,
|
|
668
|
-
console.log(` ${
|
|
681
|
+
setTfVar(tfvarsPath, tfKey, tfValue);
|
|
682
|
+
console.log(` ${tfKey}: ${oldValue ?? "(unset)"} \u2192 ${tfValue}`);
|
|
669
683
|
if (opts.apply) {
|
|
670
684
|
const tfDir = resolveTerraformDir(opts.stage);
|
|
671
685
|
if (!tfDir) {
|
|
@@ -685,9 +699,9 @@ function registerConfigCommand(program2) {
|
|
|
685
699
|
printError(`Deploy failed (exit ${code})`);
|
|
686
700
|
process.exit(code);
|
|
687
701
|
}
|
|
688
|
-
printSuccess(`Configuration applied: ${
|
|
702
|
+
printSuccess(`Configuration applied: ${tfKey} = ${tfValue}`);
|
|
689
703
|
} else {
|
|
690
|
-
printSuccess(`Configuration updated: ${
|
|
704
|
+
printSuccess(`Configuration updated: ${tfKey} = ${tfValue}`);
|
|
691
705
|
printWarning("Run with --apply to deploy the change, or run 'thinkwork deploy' separately.");
|
|
692
706
|
}
|
|
693
707
|
});
|
|
@@ -738,8 +752,8 @@ function registerBootstrapCommand(program2) {
|
|
|
738
752
|
bucket = await getTerraformOutput(cwd, "bucket_name");
|
|
739
753
|
dbEndpoint = await getTerraformOutput(cwd, "db_cluster_endpoint");
|
|
740
754
|
const secretArn = await getTerraformOutput(cwd, "db_secret_arn");
|
|
741
|
-
const { execSync:
|
|
742
|
-
const secretJson =
|
|
755
|
+
const { execSync: execSync10 } = await import("child_process");
|
|
756
|
+
const secretJson = execSync10(
|
|
743
757
|
`aws secretsmanager get-secret-value --secret-id "${secretArn}" --query SecretString --output text`,
|
|
744
758
|
{ encoding: "utf-8" }
|
|
745
759
|
).trim();
|
|
@@ -887,10 +901,10 @@ async function ensurePrerequisites() {
|
|
|
887
901
|
}
|
|
888
902
|
|
|
889
903
|
// src/commands/login.ts
|
|
890
|
-
function ask(
|
|
904
|
+
function ask(prompt2) {
|
|
891
905
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
892
906
|
return new Promise((resolve3) => {
|
|
893
|
-
rl.question(
|
|
907
|
+
rl.question(prompt2, (answer) => {
|
|
894
908
|
rl.close();
|
|
895
909
|
resolve3(answer.trim());
|
|
896
910
|
});
|
|
@@ -980,19 +994,19 @@ import { fileURLToPath } from "url";
|
|
|
980
994
|
import { createInterface as createInterface3 } from "readline";
|
|
981
995
|
import chalk5 from "chalk";
|
|
982
996
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
983
|
-
function ask2(
|
|
997
|
+
function ask2(prompt2, defaultVal = "") {
|
|
984
998
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
985
999
|
const suffix = defaultVal ? chalk5.dim(` [${defaultVal}]`) : "";
|
|
986
1000
|
return new Promise((resolve3) => {
|
|
987
|
-
rl.question(` ${
|
|
1001
|
+
rl.question(` ${prompt2}${suffix}: `, (answer) => {
|
|
988
1002
|
rl.close();
|
|
989
1003
|
resolve3(answer.trim() || defaultVal);
|
|
990
1004
|
});
|
|
991
1005
|
});
|
|
992
1006
|
}
|
|
993
|
-
function choose(
|
|
1007
|
+
function choose(prompt2, options, defaultVal) {
|
|
994
1008
|
const optStr = options.map((o) => o === defaultVal ? chalk5.bold(o) : chalk5.dim(o)).join(" / ");
|
|
995
|
-
return ask2(`${
|
|
1009
|
+
return ask2(`${prompt2} (${optStr})`, defaultVal);
|
|
996
1010
|
}
|
|
997
1011
|
function generateSecret(length = 32) {
|
|
998
1012
|
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
@@ -1003,9 +1017,9 @@ function generateSecret(length = 32) {
|
|
|
1003
1017
|
return result;
|
|
1004
1018
|
}
|
|
1005
1019
|
function findBundledTerraform() {
|
|
1006
|
-
const bundled = resolve2(__dirname, "
|
|
1020
|
+
const bundled = resolve2(__dirname, "terraform");
|
|
1007
1021
|
if (existsSync5(join3(bundled, "modules"))) return bundled;
|
|
1008
|
-
const repoTf = resolve2(__dirname, "..", "..", "..", "
|
|
1022
|
+
const repoTf = resolve2(__dirname, "..", "..", "..", "terraform");
|
|
1009
1023
|
if (existsSync5(join3(repoTf, "modules"))) return repoTf;
|
|
1010
1024
|
throw new Error(
|
|
1011
1025
|
"Terraform modules not found. The CLI package may be incomplete.\nTry reinstalling: npm install -g thinkwork-cli@latest"
|
|
@@ -1027,7 +1041,9 @@ function buildTfvars(config) {
|
|
|
1027
1041
|
`db_password = "${config.db_password}"`,
|
|
1028
1042
|
``,
|
|
1029
1043
|
`# \u2500\u2500 Memory \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
|
|
1030
|
-
|
|
1044
|
+
`# AgentCore managed memory is always on (automatic retention).`,
|
|
1045
|
+
`# Hindsight is an optional add-on for semantic + graph retrieval.`,
|
|
1046
|
+
`enable_hindsight = ${config.enable_hindsight === "true"}`,
|
|
1031
1047
|
``,
|
|
1032
1048
|
`# \u2500\u2500 Auth \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
|
|
1033
1049
|
`api_auth_secret = "${config.api_auth_secret}"`
|
|
@@ -1094,7 +1110,7 @@ function registerInitCommand(program2) {
|
|
|
1094
1110
|
if (opts.defaults) {
|
|
1095
1111
|
config.region = identity.region !== "unknown" ? identity.region : "us-east-1";
|
|
1096
1112
|
config.database_engine = "aurora-serverless";
|
|
1097
|
-
config.
|
|
1113
|
+
config.enable_hindsight = "false";
|
|
1098
1114
|
config.google_oauth_client_id = "";
|
|
1099
1115
|
config.google_oauth_client_secret = "";
|
|
1100
1116
|
config.admin_url = "http://localhost:5174";
|
|
@@ -1108,9 +1124,11 @@ function registerInitCommand(program2) {
|
|
|
1108
1124
|
config.database_engine = await choose("Database engine", ["aurora-serverless", "rds-postgres"], "aurora-serverless");
|
|
1109
1125
|
console.log("");
|
|
1110
1126
|
console.log(chalk5.dim(" \u2500\u2500 Memory \u2500\u2500"));
|
|
1111
|
-
console.log(chalk5.dim(" managed
|
|
1112
|
-
console.log(chalk5.dim("
|
|
1113
|
-
|
|
1127
|
+
console.log(chalk5.dim(" AgentCore managed memory is always on (automatic retention)."));
|
|
1128
|
+
console.log(chalk5.dim(" Hindsight is an optional add-on: ECS Fargate service for"));
|
|
1129
|
+
console.log(chalk5.dim(" semantic + entity-graph retrieval (~$75/mo)."));
|
|
1130
|
+
const hindsightAnswer = await ask2("Enable Hindsight long-term memory add-on? (y/N)", "N");
|
|
1131
|
+
config.enable_hindsight = hindsightAnswer.toLowerCase() === "y" ? "true" : "false";
|
|
1114
1132
|
console.log("");
|
|
1115
1133
|
console.log(chalk5.dim(" \u2500\u2500 Auth \u2500\u2500"));
|
|
1116
1134
|
const useGoogle = await ask2("Enable Google OAuth login? (y/N)", "N");
|
|
@@ -1184,21 +1202,86 @@ provider "aws" {
|
|
|
1184
1202
|
region = var.region
|
|
1185
1203
|
}
|
|
1186
1204
|
|
|
1187
|
-
variable "stage" {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
variable "
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
variable "
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
variable "
|
|
1201
|
-
|
|
1205
|
+
variable "stage" {
|
|
1206
|
+
type = string
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
variable "region" {
|
|
1210
|
+
type = string
|
|
1211
|
+
default = "us-east-1"
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
variable "account_id" {
|
|
1215
|
+
type = string
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
variable "db_password" {
|
|
1219
|
+
type = string
|
|
1220
|
+
sensitive = true
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
variable "database_engine" {
|
|
1224
|
+
type = string
|
|
1225
|
+
default = "aurora-serverless"
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
variable "enable_hindsight" {
|
|
1229
|
+
type = bool
|
|
1230
|
+
default = false
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1233
|
+
variable "agentcore_memory_id" {
|
|
1234
|
+
type = string
|
|
1235
|
+
default = ""
|
|
1236
|
+
description = "Optional pre-existing Bedrock AgentCore Memory resource ID. Leave empty to auto-provision."
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
variable "google_oauth_client_id" {
|
|
1240
|
+
type = string
|
|
1241
|
+
default = ""
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
variable "google_oauth_client_secret" {
|
|
1245
|
+
type = string
|
|
1246
|
+
sensitive = true
|
|
1247
|
+
default = ""
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
variable "pre_signup_lambda_zip" {
|
|
1251
|
+
type = string
|
|
1252
|
+
default = ""
|
|
1253
|
+
}
|
|
1254
|
+
|
|
1255
|
+
variable "lambda_zips_dir" {
|
|
1256
|
+
type = string
|
|
1257
|
+
default = ""
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
variable "api_auth_secret" {
|
|
1261
|
+
type = string
|
|
1262
|
+
sensitive = true
|
|
1263
|
+
default = ""
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
variable "admin_callback_urls" {
|
|
1267
|
+
type = list(string)
|
|
1268
|
+
default = ["http://localhost:5174", "http://localhost:5174/auth/callback"]
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
variable "admin_logout_urls" {
|
|
1272
|
+
type = list(string)
|
|
1273
|
+
default = ["http://localhost:5174"]
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
variable "mobile_callback_urls" {
|
|
1277
|
+
type = list(string)
|
|
1278
|
+
default = ["exp://localhost:8081", "thinkwork://", "thinkwork://auth/callback"]
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
variable "mobile_logout_urls" {
|
|
1282
|
+
type = list(string)
|
|
1283
|
+
default = ["exp://localhost:8081", "thinkwork://"]
|
|
1284
|
+
}
|
|
1202
1285
|
|
|
1203
1286
|
module "thinkwork" {
|
|
1204
1287
|
source = "./modules/thinkwork"
|
|
@@ -1209,7 +1292,8 @@ module "thinkwork" {
|
|
|
1209
1292
|
|
|
1210
1293
|
db_password = var.db_password
|
|
1211
1294
|
database_engine = var.database_engine
|
|
1212
|
-
|
|
1295
|
+
enable_hindsight = var.enable_hindsight
|
|
1296
|
+
agentcore_memory_id = var.agentcore_memory_id
|
|
1213
1297
|
google_oauth_client_id = var.google_oauth_client_id
|
|
1214
1298
|
google_oauth_client_secret = var.google_oauth_client_secret
|
|
1215
1299
|
pre_signup_lambda_zip = var.pre_signup_lambda_zip
|
|
@@ -1221,16 +1305,50 @@ module "thinkwork" {
|
|
|
1221
1305
|
mobile_logout_urls = var.mobile_logout_urls
|
|
1222
1306
|
}
|
|
1223
1307
|
|
|
1224
|
-
output "api_endpoint"
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
output "
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
output "
|
|
1233
|
-
|
|
1308
|
+
output "api_endpoint" {
|
|
1309
|
+
value = module.thinkwork.api_endpoint
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
output "user_pool_id" {
|
|
1313
|
+
value = module.thinkwork.user_pool_id
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
output "admin_client_id" {
|
|
1317
|
+
value = module.thinkwork.admin_client_id
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
output "mobile_client_id" {
|
|
1321
|
+
value = module.thinkwork.mobile_client_id
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
output "bucket_name" {
|
|
1325
|
+
value = module.thinkwork.bucket_name
|
|
1326
|
+
}
|
|
1327
|
+
|
|
1328
|
+
output "db_cluster_endpoint" {
|
|
1329
|
+
value = module.thinkwork.db_cluster_endpoint
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
output "db_secret_arn" {
|
|
1333
|
+
value = module.thinkwork.db_secret_arn
|
|
1334
|
+
sensitive = true
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
output "ecr_repository_url" {
|
|
1338
|
+
value = module.thinkwork.ecr_repository_url
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
output "hindsight_enabled" {
|
|
1342
|
+
value = module.thinkwork.hindsight_enabled
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
output "hindsight_endpoint" {
|
|
1346
|
+
value = module.thinkwork.hindsight_endpoint
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
output "agentcore_memory_id" {
|
|
1350
|
+
value = module.thinkwork.agentcore_memory_id
|
|
1351
|
+
}
|
|
1234
1352
|
`);
|
|
1235
1353
|
}
|
|
1236
1354
|
console.log(` Wrote ${chalk5.cyan(tfDir + "/")}`);
|
|
@@ -1240,7 +1358,7 @@ output "hindsight_endpoint" { value = module.thinkwork.hindsight_endpoint }
|
|
|
1240
1358
|
console.log(` ${chalk5.bold("Region:")} ${config.region}`);
|
|
1241
1359
|
console.log(` ${chalk5.bold("Account:")} ${config.account_id}`);
|
|
1242
1360
|
console.log(` ${chalk5.bold("Database:")} ${config.database_engine}`);
|
|
1243
|
-
console.log(` ${chalk5.bold("Memory:")} ${config.
|
|
1361
|
+
console.log(` ${chalk5.bold("Memory:")} managed (always on)${config.enable_hindsight === "true" ? " + hindsight" : ""}`);
|
|
1244
1362
|
console.log(` ${chalk5.bold("Google OAuth:")} ${config.google_oauth_client_id ? "enabled" : "disabled"}`);
|
|
1245
1363
|
console.log(` ${chalk5.bold("Directory:")} ${tfDir}`);
|
|
1246
1364
|
console.log(chalk5.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"));
|
|
@@ -1258,7 +1376,7 @@ output "hindsight_endpoint" { value = module.thinkwork.hindsight_endpoint }
|
|
|
1258
1376
|
accountId: config.account_id,
|
|
1259
1377
|
terraformDir: tfDir,
|
|
1260
1378
|
databaseEngine: config.database_engine,
|
|
1261
|
-
|
|
1379
|
+
enableHindsight: config.enable_hindsight === "true",
|
|
1262
1380
|
createdAt: now,
|
|
1263
1381
|
updatedAt: now
|
|
1264
1382
|
});
|
|
@@ -1332,7 +1450,7 @@ function discoverAwsStages(region) {
|
|
|
1332
1450
|
`ecs describe-services --cluster thinkwork-${stage}-cluster --services thinkwork-${stage}-hindsight --region ${region} --query "services[0].runningCount" --output text 2>/dev/null`
|
|
1333
1451
|
);
|
|
1334
1452
|
if (ecsRaw && ecsRaw !== "None" && ecsRaw !== "0") {
|
|
1335
|
-
info.
|
|
1453
|
+
info.hindsightEnabled = true;
|
|
1336
1454
|
const albRaw = runAws(
|
|
1337
1455
|
`elbv2 describe-load-balancers --region ${region} --query "LoadBalancers[?contains(LoadBalancerName, 'tw-${stage}-hindsight')].DNSName|[0]" --output text`
|
|
1338
1456
|
);
|
|
@@ -1346,7 +1464,7 @@ function discoverAwsStages(region) {
|
|
|
1346
1464
|
}
|
|
1347
1465
|
}
|
|
1348
1466
|
} else {
|
|
1349
|
-
info.
|
|
1467
|
+
info.hindsightEnabled = false;
|
|
1350
1468
|
}
|
|
1351
1469
|
const dbRaw = runAws(
|
|
1352
1470
|
`rds describe-db-clusters --region ${region} --query "DBClusters[?starts_with(DBClusterIdentifier, 'thinkwork-${stage}')].Endpoint|[0]" --output text`
|
|
@@ -1380,8 +1498,9 @@ function printStageDetail(info) {
|
|
|
1380
1498
|
console.log(` ${chalk6.bold("Account:")} ${info.accountId}`);
|
|
1381
1499
|
console.log(` ${chalk6.bold("Lambda fns:")} ${info.lambdaCount || "\u2014"}`);
|
|
1382
1500
|
console.log(` ${chalk6.bold("AgentCore:")} ${info.agentcoreStatus || "unknown"}`);
|
|
1383
|
-
console.log(` ${chalk6.bold("Memory:")}
|
|
1384
|
-
|
|
1501
|
+
console.log(` ${chalk6.bold("Memory:")} managed (always on)`);
|
|
1502
|
+
const hindsightLabel = info.hindsightEnabled === void 0 ? "unknown" : info.hindsightEnabled ? info.hindsightHealth || "running" : "disabled";
|
|
1503
|
+
console.log(` ${chalk6.bold("Hindsight:")} ${hindsightLabel}`);
|
|
1385
1504
|
if (info.bucketName) console.log(` ${chalk6.bold("S3 bucket:")} ${info.bucketName}`);
|
|
1386
1505
|
if (info.dbEndpoint) console.log(` ${chalk6.bold("Database:")} ${info.dbEndpoint}`);
|
|
1387
1506
|
if (info.ecrUrl) console.log(` ${chalk6.bold("ECR:")} ${info.ecrUrl}`);
|
|
@@ -1671,12 +1790,246 @@ function registerMcpCommand(program2) {
|
|
|
1671
1790
|
});
|
|
1672
1791
|
}
|
|
1673
1792
|
|
|
1674
|
-
// src/commands/
|
|
1793
|
+
// src/commands/tools.ts
|
|
1794
|
+
import { readFileSync as readFileSync4, existsSync as existsSync7 } from "fs";
|
|
1675
1795
|
import { execSync as execSync8 } from "child_process";
|
|
1796
|
+
import { createInterface as createInterface4 } from "readline";
|
|
1676
1797
|
import chalk8 from "chalk";
|
|
1798
|
+
function readTfVar3(tfvarsPath, key) {
|
|
1799
|
+
if (!existsSync7(tfvarsPath)) return null;
|
|
1800
|
+
const content = readFileSync4(tfvarsPath, "utf-8");
|
|
1801
|
+
const match = content.match(new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, "m"));
|
|
1802
|
+
return match ? match[1] : null;
|
|
1803
|
+
}
|
|
1804
|
+
function resolveTfvarsPath3(stage) {
|
|
1805
|
+
const tfDir = resolveTerraformDir(stage);
|
|
1806
|
+
if (tfDir) {
|
|
1807
|
+
const direct = `${tfDir}/terraform.tfvars`;
|
|
1808
|
+
if (existsSync7(direct)) return direct;
|
|
1809
|
+
}
|
|
1810
|
+
const terraformDir = process.env.THINKWORK_TERRAFORM_DIR || process.cwd();
|
|
1811
|
+
const cwd = resolveTierDir(terraformDir, stage, "app");
|
|
1812
|
+
return `${cwd}/terraform.tfvars`;
|
|
1813
|
+
}
|
|
1814
|
+
function getApiEndpoint2(stage, region) {
|
|
1815
|
+
try {
|
|
1816
|
+
const raw = execSync8(
|
|
1817
|
+
`aws apigatewayv2 get-apis --region ${region} --query "Items[?Name=='thinkwork-${stage}-api'].ApiEndpoint|[0]" --output text`,
|
|
1818
|
+
{ encoding: "utf-8", timeout: 15e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
1819
|
+
).trim();
|
|
1820
|
+
return raw && raw !== "None" ? raw : null;
|
|
1821
|
+
} catch {
|
|
1822
|
+
return null;
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
async function apiFetch2(apiUrl, authSecret, path2, options = {}, extraHeaders = {}) {
|
|
1826
|
+
const res = await fetch(`${apiUrl}${path2}`, {
|
|
1827
|
+
...options,
|
|
1828
|
+
headers: {
|
|
1829
|
+
"Content-Type": "application/json",
|
|
1830
|
+
Authorization: `Bearer ${authSecret}`,
|
|
1831
|
+
...extraHeaders,
|
|
1832
|
+
...options.headers
|
|
1833
|
+
}
|
|
1834
|
+
});
|
|
1835
|
+
if (!res.ok) {
|
|
1836
|
+
const body = await res.json().catch(() => ({}));
|
|
1837
|
+
throw new Error(body.error || `HTTP ${res.status}`);
|
|
1838
|
+
}
|
|
1839
|
+
return res.json();
|
|
1840
|
+
}
|
|
1841
|
+
function resolveApiConfig2(stage) {
|
|
1842
|
+
const tfvarsPath = resolveTfvarsPath3(stage);
|
|
1843
|
+
const authSecret = readTfVar3(tfvarsPath, "api_auth_secret");
|
|
1844
|
+
if (!authSecret) {
|
|
1845
|
+
printError(`Cannot read api_auth_secret from ${tfvarsPath}`);
|
|
1846
|
+
return null;
|
|
1847
|
+
}
|
|
1848
|
+
const region = readTfVar3(tfvarsPath, "region") || "us-east-1";
|
|
1849
|
+
const apiUrl = getApiEndpoint2(stage, region);
|
|
1850
|
+
if (!apiUrl) {
|
|
1851
|
+
printError(`Cannot discover API endpoint for stage "${stage}". Is the stack deployed?`);
|
|
1852
|
+
return null;
|
|
1853
|
+
}
|
|
1854
|
+
return { apiUrl, authSecret };
|
|
1855
|
+
}
|
|
1856
|
+
function prompt(question) {
|
|
1857
|
+
const rl = createInterface4({ input: process.stdin, output: process.stdout });
|
|
1858
|
+
return new Promise((resolve3) => {
|
|
1859
|
+
rl.question(question, (answer) => {
|
|
1860
|
+
rl.close();
|
|
1861
|
+
resolve3(answer.trim());
|
|
1862
|
+
});
|
|
1863
|
+
});
|
|
1864
|
+
}
|
|
1865
|
+
var TOOL_PROVIDERS = {
|
|
1866
|
+
"web-search": ["exa", "serpapi"]
|
|
1867
|
+
};
|
|
1868
|
+
function registerToolsCommand(program2) {
|
|
1869
|
+
const tools = program2.command("tools").description("Configure built-in agent tools (web_search, \u2026) for your tenant");
|
|
1870
|
+
tools.command("list").description("List configured built-in tools").requiredOption("-s, --stage <name>", "Deployment stage").requiredOption("--tenant <slug>", "Tenant slug").action(async (opts) => {
|
|
1871
|
+
const check = validateStage(opts.stage);
|
|
1872
|
+
if (!check.valid) {
|
|
1873
|
+
printError(check.error);
|
|
1874
|
+
process.exit(1);
|
|
1875
|
+
}
|
|
1876
|
+
const api = resolveApiConfig2(opts.stage);
|
|
1877
|
+
if (!api) process.exit(1);
|
|
1878
|
+
printHeader("tools list", opts.stage);
|
|
1879
|
+
try {
|
|
1880
|
+
const { tools: rows } = await apiFetch2(
|
|
1881
|
+
api.apiUrl,
|
|
1882
|
+
api.authSecret,
|
|
1883
|
+
"/api/skills/builtin-tools",
|
|
1884
|
+
{},
|
|
1885
|
+
{ "x-tenant-slug": opts.tenant }
|
|
1886
|
+
);
|
|
1887
|
+
if (!rows || rows.length === 0) {
|
|
1888
|
+
console.log(chalk8.dim(" No built-in tools configured."));
|
|
1889
|
+
console.log(chalk8.dim(" Try: thinkwork tools web-search set --tenant <slug> -s <stage>"));
|
|
1890
|
+
return;
|
|
1891
|
+
}
|
|
1892
|
+
console.log("");
|
|
1893
|
+
for (const r of rows) {
|
|
1894
|
+
const status = r.enabled ? chalk8.green("enabled") : chalk8.dim("disabled");
|
|
1895
|
+
const key = r.hasSecret ? chalk8.green("yes") : chalk8.red("no");
|
|
1896
|
+
const provider = r.provider ?? chalk8.dim("\u2014");
|
|
1897
|
+
console.log(` ${chalk8.bold(r.toolSlug)} ${status}`);
|
|
1898
|
+
console.log(` Provider: ${provider}`);
|
|
1899
|
+
console.log(` Has key: ${key}`);
|
|
1900
|
+
if (r.lastTestedAt) {
|
|
1901
|
+
console.log(` Tested: ${new Date(r.lastTestedAt).toLocaleString()}`);
|
|
1902
|
+
}
|
|
1903
|
+
console.log("");
|
|
1904
|
+
}
|
|
1905
|
+
} catch (err) {
|
|
1906
|
+
printError(err.message);
|
|
1907
|
+
process.exit(1);
|
|
1908
|
+
}
|
|
1909
|
+
});
|
|
1910
|
+
const webSearch = tools.command("web-search").description("Configure the web_search built-in tool");
|
|
1911
|
+
webSearch.command("set").description("Set or update web_search provider + API key (enables the tool)").requiredOption("-s, --stage <name>", "Deployment stage").requiredOption("--tenant <slug>", "Tenant slug").option("--provider <name>", `Provider (${TOOL_PROVIDERS["web-search"].join("|")})`).option("--key <key>", "API key").action(async (opts) => {
|
|
1912
|
+
const check = validateStage(opts.stage);
|
|
1913
|
+
if (!check.valid) {
|
|
1914
|
+
printError(check.error);
|
|
1915
|
+
process.exit(1);
|
|
1916
|
+
}
|
|
1917
|
+
const api = resolveApiConfig2(opts.stage);
|
|
1918
|
+
if (!api) process.exit(1);
|
|
1919
|
+
let provider = opts.provider;
|
|
1920
|
+
if (!provider) {
|
|
1921
|
+
provider = (await prompt(`Provider [${TOOL_PROVIDERS["web-search"].join("/")}]: `)).toLowerCase();
|
|
1922
|
+
}
|
|
1923
|
+
if (!TOOL_PROVIDERS["web-search"].includes(provider)) {
|
|
1924
|
+
printError(`provider must be one of: ${TOOL_PROVIDERS["web-search"].join(", ")}`);
|
|
1925
|
+
process.exit(1);
|
|
1926
|
+
}
|
|
1927
|
+
let apiKey = opts.key;
|
|
1928
|
+
if (!apiKey) {
|
|
1929
|
+
apiKey = await prompt(`${provider} API key: `);
|
|
1930
|
+
}
|
|
1931
|
+
if (!apiKey) {
|
|
1932
|
+
printError("API key is required");
|
|
1933
|
+
process.exit(1);
|
|
1934
|
+
}
|
|
1935
|
+
try {
|
|
1936
|
+
await apiFetch2(
|
|
1937
|
+
api.apiUrl,
|
|
1938
|
+
api.authSecret,
|
|
1939
|
+
"/api/skills/builtin-tools/web-search",
|
|
1940
|
+
{
|
|
1941
|
+
method: "PUT",
|
|
1942
|
+
body: JSON.stringify({ provider, apiKey, enabled: true })
|
|
1943
|
+
},
|
|
1944
|
+
{ "x-tenant-slug": opts.tenant }
|
|
1945
|
+
);
|
|
1946
|
+
printSuccess(`web_search configured with provider=${provider}, enabled=true`);
|
|
1947
|
+
printWarning("Run `thinkwork tools web-search test` to verify connectivity.");
|
|
1948
|
+
} catch (err) {
|
|
1949
|
+
printError(err.message);
|
|
1950
|
+
process.exit(1);
|
|
1951
|
+
}
|
|
1952
|
+
});
|
|
1953
|
+
webSearch.command("test").description("Test the stored web_search provider + key against the provider API").requiredOption("-s, --stage <name>", "Deployment stage").requiredOption("--tenant <slug>", "Tenant slug").action(async (opts) => {
|
|
1954
|
+
const check = validateStage(opts.stage);
|
|
1955
|
+
if (!check.valid) {
|
|
1956
|
+
printError(check.error);
|
|
1957
|
+
process.exit(1);
|
|
1958
|
+
}
|
|
1959
|
+
const api = resolveApiConfig2(opts.stage);
|
|
1960
|
+
if (!api) process.exit(1);
|
|
1961
|
+
printHeader("tools web-search test", opts.stage);
|
|
1962
|
+
try {
|
|
1963
|
+
const result = await apiFetch2(
|
|
1964
|
+
api.apiUrl,
|
|
1965
|
+
api.authSecret,
|
|
1966
|
+
"/api/skills/builtin-tools/web-search/test",
|
|
1967
|
+
{ method: "POST", body: "{}" },
|
|
1968
|
+
{ "x-tenant-slug": opts.tenant }
|
|
1969
|
+
);
|
|
1970
|
+
if (result.ok) {
|
|
1971
|
+
printSuccess(`${result.provider}: ${result.resultCount} result(s) returned.`);
|
|
1972
|
+
} else {
|
|
1973
|
+
printError(`Test failed: ${result.error}`);
|
|
1974
|
+
process.exit(1);
|
|
1975
|
+
}
|
|
1976
|
+
} catch (err) {
|
|
1977
|
+
printError(err.message);
|
|
1978
|
+
process.exit(1);
|
|
1979
|
+
}
|
|
1980
|
+
});
|
|
1981
|
+
webSearch.command("disable").description("Disable web_search without deleting the stored key").requiredOption("-s, --stage <name>", "Deployment stage").requiredOption("--tenant <slug>", "Tenant slug").action(async (opts) => {
|
|
1982
|
+
const check = validateStage(opts.stage);
|
|
1983
|
+
if (!check.valid) {
|
|
1984
|
+
printError(check.error);
|
|
1985
|
+
process.exit(1);
|
|
1986
|
+
}
|
|
1987
|
+
const api = resolveApiConfig2(opts.stage);
|
|
1988
|
+
if (!api) process.exit(1);
|
|
1989
|
+
try {
|
|
1990
|
+
await apiFetch2(
|
|
1991
|
+
api.apiUrl,
|
|
1992
|
+
api.authSecret,
|
|
1993
|
+
"/api/skills/builtin-tools/web-search",
|
|
1994
|
+
{ method: "PUT", body: JSON.stringify({ enabled: false }) },
|
|
1995
|
+
{ "x-tenant-slug": opts.tenant }
|
|
1996
|
+
);
|
|
1997
|
+
printSuccess("web_search disabled.");
|
|
1998
|
+
} catch (err) {
|
|
1999
|
+
printError(err.message);
|
|
2000
|
+
process.exit(1);
|
|
2001
|
+
}
|
|
2002
|
+
});
|
|
2003
|
+
webSearch.command("clear").description("Remove web_search config entirely (deletes stored key)").requiredOption("-s, --stage <name>", "Deployment stage").requiredOption("--tenant <slug>", "Tenant slug").action(async (opts) => {
|
|
2004
|
+
const check = validateStage(opts.stage);
|
|
2005
|
+
if (!check.valid) {
|
|
2006
|
+
printError(check.error);
|
|
2007
|
+
process.exit(1);
|
|
2008
|
+
}
|
|
2009
|
+
const api = resolveApiConfig2(opts.stage);
|
|
2010
|
+
if (!api) process.exit(1);
|
|
2011
|
+
try {
|
|
2012
|
+
await apiFetch2(
|
|
2013
|
+
api.apiUrl,
|
|
2014
|
+
api.authSecret,
|
|
2015
|
+
"/api/skills/builtin-tools/web-search",
|
|
2016
|
+
{ method: "DELETE" },
|
|
2017
|
+
{ "x-tenant-slug": opts.tenant }
|
|
2018
|
+
);
|
|
2019
|
+
printSuccess("web_search configuration cleared.");
|
|
2020
|
+
} catch (err) {
|
|
2021
|
+
printError(err.message);
|
|
2022
|
+
process.exit(1);
|
|
2023
|
+
}
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// src/commands/update.ts
|
|
2028
|
+
import { execSync as execSync9 } from "child_process";
|
|
2029
|
+
import chalk9 from "chalk";
|
|
1677
2030
|
function getLatestVersion() {
|
|
1678
2031
|
try {
|
|
1679
|
-
return
|
|
2032
|
+
return execSync9("npm view thinkwork-cli version", {
|
|
1680
2033
|
encoding: "utf-8",
|
|
1681
2034
|
timeout: 1e4,
|
|
1682
2035
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -1687,7 +2040,7 @@ function getLatestVersion() {
|
|
|
1687
2040
|
}
|
|
1688
2041
|
function detectInstallMethod() {
|
|
1689
2042
|
try {
|
|
1690
|
-
const which =
|
|
2043
|
+
const which = execSync9("which thinkwork", {
|
|
1691
2044
|
encoding: "utf-8",
|
|
1692
2045
|
timeout: 5e3,
|
|
1693
2046
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -1708,31 +2061,31 @@ function compareVersions(a, b) {
|
|
|
1708
2061
|
}
|
|
1709
2062
|
return 0;
|
|
1710
2063
|
}
|
|
1711
|
-
function
|
|
1712
|
-
program2.command("
|
|
1713
|
-
printHeader("
|
|
1714
|
-
console.log(` Current version: ${
|
|
2064
|
+
function registerUpdateCommand(program2) {
|
|
2065
|
+
program2.command("update").description("Check for and install CLI updates").option("--check", "Only check for updates, don't install").action(async (opts) => {
|
|
2066
|
+
printHeader("update", "", null);
|
|
2067
|
+
console.log(` Current version: ${chalk9.bold(VERSION)}`);
|
|
1715
2068
|
const latest = getLatestVersion();
|
|
1716
2069
|
if (!latest) {
|
|
1717
|
-
console.log(
|
|
2070
|
+
console.log(chalk9.yellow(" Could not check npm registry for updates."));
|
|
1718
2071
|
return;
|
|
1719
2072
|
}
|
|
1720
|
-
console.log(` Latest version: ${
|
|
2073
|
+
console.log(` Latest version: ${chalk9.bold(latest)}`);
|
|
1721
2074
|
console.log("");
|
|
1722
2075
|
const cmp = compareVersions(VERSION, latest);
|
|
1723
2076
|
if (cmp >= 0) {
|
|
1724
|
-
console.log(
|
|
2077
|
+
console.log(chalk9.green(" \u2713 You're on the latest version."));
|
|
1725
2078
|
console.log("");
|
|
1726
2079
|
return;
|
|
1727
2080
|
}
|
|
1728
|
-
console.log(
|
|
2081
|
+
console.log(chalk9.cyan(` Update available: ${VERSION} \u2192 ${latest}`));
|
|
1729
2082
|
console.log("");
|
|
1730
2083
|
if (opts.check) {
|
|
1731
2084
|
const method2 = detectInstallMethod();
|
|
1732
2085
|
if (method2 === "homebrew") {
|
|
1733
|
-
console.log(` Run: ${
|
|
2086
|
+
console.log(` Run: ${chalk9.cyan("brew upgrade thinkwork-ai/tap/thinkwork")}`);
|
|
1734
2087
|
} else {
|
|
1735
|
-
console.log(` Run: ${
|
|
2088
|
+
console.log(` Run: ${chalk9.cyan(`npm install -g thinkwork-cli@${latest}`)}`);
|
|
1736
2089
|
}
|
|
1737
2090
|
console.log("");
|
|
1738
2091
|
return;
|
|
@@ -1740,16 +2093,16 @@ function registerUpgradeCommand(program2) {
|
|
|
1740
2093
|
const method = detectInstallMethod();
|
|
1741
2094
|
const cmd = method === "homebrew" ? "brew upgrade thinkwork-ai/tap/thinkwork" : `npm install -g thinkwork-cli@${latest}`;
|
|
1742
2095
|
console.log(` Installing via ${method}...`);
|
|
1743
|
-
console.log(
|
|
2096
|
+
console.log(chalk9.dim(` $ ${cmd}`));
|
|
1744
2097
|
console.log("");
|
|
1745
2098
|
try {
|
|
1746
|
-
|
|
2099
|
+
execSync9(cmd, { stdio: "inherit", timeout: 12e4 });
|
|
1747
2100
|
console.log("");
|
|
1748
|
-
console.log(
|
|
2101
|
+
console.log(chalk9.green(` \u2713 Upgraded to thinkwork-cli@${latest}`));
|
|
1749
2102
|
} catch {
|
|
1750
2103
|
console.log("");
|
|
1751
|
-
console.log(
|
|
1752
|
-
console.log(` ${
|
|
2104
|
+
console.log(chalk9.red(` Failed to upgrade. Try manually:`));
|
|
2105
|
+
console.log(` ${chalk9.cyan(cmd)}`);
|
|
1753
2106
|
}
|
|
1754
2107
|
console.log("");
|
|
1755
2108
|
});
|
|
@@ -1780,5 +2133,6 @@ registerStatusCommand(program);
|
|
|
1780
2133
|
registerOutputsCommand(program);
|
|
1781
2134
|
registerConfigCommand(program);
|
|
1782
2135
|
registerMcpCommand(program);
|
|
1783
|
-
|
|
2136
|
+
registerToolsCommand(program);
|
|
2137
|
+
registerUpdateCommand(program);
|
|
1784
2138
|
program.parse();
|