thinkwork-cli 0.1.1 → 0.2.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 +311 -54
- package/dist/terraform/examples/greenfield/main.tf +190 -0
- package/dist/terraform/examples/greenfield/terraform.tfvars.example +28 -0
- package/dist/terraform/modules/_internal/workspace-guard/main.tf +29 -0
- package/dist/terraform/modules/app/agentcore-runtime/main.tf +217 -0
- package/dist/terraform/modules/app/appsync-subscriptions/main.tf +122 -0
- package/dist/terraform/modules/app/appsync-subscriptions/outputs.tf +20 -0
- package/dist/terraform/modules/app/appsync-subscriptions/variables.tf +31 -0
- package/dist/terraform/modules/app/crons/main.tf +55 -0
- package/dist/terraform/modules/app/hindsight-memory/README.md +66 -0
- package/dist/terraform/modules/app/hindsight-memory/main.tf +331 -0
- package/dist/terraform/modules/app/job-triggers/main.tf +70 -0
- package/dist/terraform/modules/app/lambda-api/.build/placeholder.zip +0 -0
- package/dist/terraform/modules/app/lambda-api/handlers.tf +311 -0
- package/dist/terraform/modules/app/lambda-api/main.tf +245 -0
- package/dist/terraform/modules/app/lambda-api/outputs.tf +24 -0
- package/dist/terraform/modules/app/lambda-api/variables.tf +153 -0
- package/dist/terraform/modules/app/ses-email/main.tf +51 -0
- package/dist/terraform/modules/app/static-site/main.tf +176 -0
- package/dist/terraform/modules/data/aurora-postgres/README.md +92 -0
- package/dist/terraform/modules/data/aurora-postgres/main.tf +185 -0
- package/dist/terraform/modules/data/aurora-postgres/outputs.tf +30 -0
- package/dist/terraform/modules/data/aurora-postgres/variables.tf +114 -0
- package/dist/terraform/modules/data/bedrock-knowledge-base/main.tf +102 -0
- package/dist/terraform/modules/data/s3-buckets/main.tf +91 -0
- package/dist/terraform/modules/foundation/cognito/main.tf +377 -0
- package/dist/terraform/modules/foundation/cognito/outputs.tf +29 -0
- package/dist/terraform/modules/foundation/cognito/variables.tf +124 -0
- package/dist/terraform/modules/foundation/dns/main.tf +49 -0
- package/dist/terraform/modules/foundation/kms/main.tf +49 -0
- package/dist/terraform/modules/foundation/vpc/main.tf +137 -0
- package/dist/terraform/modules/foundation/vpc/outputs.tf +14 -0
- package/dist/terraform/modules/foundation/vpc/variables.tf +40 -0
- package/dist/terraform/modules/thinkwork/main.tf +212 -0
- package/dist/terraform/modules/thinkwork/outputs.tf +87 -0
- package/dist/terraform/modules/thinkwork/variables.tf +241 -0
- package/dist/terraform/schema.graphql +199 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -83,7 +83,19 @@ function resolveTierDir(terraformDir, stage, tier) {
|
|
|
83
83
|
if (existsSync(envDir)) {
|
|
84
84
|
return envDir;
|
|
85
85
|
}
|
|
86
|
-
|
|
86
|
+
const greenfield = path.join(terraformDir, "examples", "greenfield");
|
|
87
|
+
if (existsSync(greenfield)) {
|
|
88
|
+
return greenfield;
|
|
89
|
+
}
|
|
90
|
+
const flat = path.join(terraformDir);
|
|
91
|
+
if (existsSync(path.join(flat, "main.tf"))) {
|
|
92
|
+
return flat;
|
|
93
|
+
}
|
|
94
|
+
const cwdTf = path.join(process.cwd(), "terraform");
|
|
95
|
+
if (existsSync(path.join(cwdTf, "main.tf"))) {
|
|
96
|
+
return cwdTf;
|
|
97
|
+
}
|
|
98
|
+
return terraformDir;
|
|
87
99
|
}
|
|
88
100
|
async function ensureWorkspace(cwd, stage) {
|
|
89
101
|
const list = await runTerraformRaw(cwd, ["workspace", "list"]);
|
|
@@ -473,19 +485,67 @@ function registerOutputsCommand(program2) {
|
|
|
473
485
|
}
|
|
474
486
|
|
|
475
487
|
// src/commands/config.ts
|
|
476
|
-
import { readFileSync, writeFileSync, existsSync as
|
|
488
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "fs";
|
|
489
|
+
import chalk3 from "chalk";
|
|
490
|
+
|
|
491
|
+
// src/environments.ts
|
|
492
|
+
import { existsSync as existsSync2, mkdirSync, writeFileSync, readFileSync, readdirSync } from "fs";
|
|
493
|
+
import { join } from "path";
|
|
494
|
+
import { homedir } from "os";
|
|
495
|
+
var THINKWORK_HOME = join(homedir(), ".thinkwork");
|
|
496
|
+
var ENVIRONMENTS_DIR = join(THINKWORK_HOME, "environments");
|
|
497
|
+
function ensureDir(dir) {
|
|
498
|
+
if (!existsSync2(dir)) mkdirSync(dir, { recursive: true });
|
|
499
|
+
}
|
|
500
|
+
function saveEnvironment(config) {
|
|
501
|
+
ensureDir(ENVIRONMENTS_DIR);
|
|
502
|
+
const envDir = join(ENVIRONMENTS_DIR, config.stage);
|
|
503
|
+
ensureDir(envDir);
|
|
504
|
+
writeFileSync(
|
|
505
|
+
join(envDir, "config.json"),
|
|
506
|
+
JSON.stringify(config, null, 2) + "\n"
|
|
507
|
+
);
|
|
508
|
+
}
|
|
509
|
+
function loadEnvironment(stage) {
|
|
510
|
+
const configPath = join(ENVIRONMENTS_DIR, stage, "config.json");
|
|
511
|
+
if (!existsSync2(configPath)) return null;
|
|
512
|
+
return JSON.parse(readFileSync(configPath, "utf-8"));
|
|
513
|
+
}
|
|
514
|
+
function listEnvironments() {
|
|
515
|
+
if (!existsSync2(ENVIRONMENTS_DIR)) return [];
|
|
516
|
+
return readdirSync(ENVIRONMENTS_DIR).filter((name) => {
|
|
517
|
+
return existsSync2(join(ENVIRONMENTS_DIR, name, "config.json"));
|
|
518
|
+
}).map((name) => {
|
|
519
|
+
return JSON.parse(
|
|
520
|
+
readFileSync(join(ENVIRONMENTS_DIR, name, "config.json"), "utf-8")
|
|
521
|
+
);
|
|
522
|
+
}).sort((a, b) => a.stage.localeCompare(b.stage));
|
|
523
|
+
}
|
|
524
|
+
function resolveTerraformDir(stage) {
|
|
525
|
+
const env = loadEnvironment(stage);
|
|
526
|
+
if (env?.terraformDir && existsSync2(env.terraformDir)) {
|
|
527
|
+
return env.terraformDir;
|
|
528
|
+
}
|
|
529
|
+
const envVar = process.env.THINKWORK_TERRAFORM_DIR;
|
|
530
|
+
if (envVar && existsSync2(envVar)) return envVar;
|
|
531
|
+
const cwdTf = join(process.cwd(), "terraform");
|
|
532
|
+
if (existsSync2(join(cwdTf, "main.tf"))) return cwdTf;
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// src/commands/config.ts
|
|
477
537
|
var VALID_MEMORY_ENGINES = ["managed", "hindsight"];
|
|
478
538
|
function readTfVar(tfvarsPath, key) {
|
|
479
|
-
if (!
|
|
480
|
-
const content =
|
|
539
|
+
if (!existsSync3(tfvarsPath)) return null;
|
|
540
|
+
const content = readFileSync2(tfvarsPath, "utf-8");
|
|
481
541
|
const match = content.match(new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, "m"));
|
|
482
542
|
return match ? match[1] : null;
|
|
483
543
|
}
|
|
484
544
|
function setTfVar(tfvarsPath, key, value) {
|
|
485
|
-
if (!
|
|
545
|
+
if (!existsSync3(tfvarsPath)) {
|
|
486
546
|
throw new Error(`terraform.tfvars not found at ${tfvarsPath}`);
|
|
487
547
|
}
|
|
488
|
-
let content =
|
|
548
|
+
let content = readFileSync2(tfvarsPath, "utf-8");
|
|
489
549
|
const regex = new RegExp(`^(${key}\\s*=\\s*)"[^"]*"`, "m");
|
|
490
550
|
if (regex.test(content)) {
|
|
491
551
|
content = content.replace(regex, `$1"${value}"`);
|
|
@@ -494,19 +554,93 @@ function setTfVar(tfvarsPath, key, value) {
|
|
|
494
554
|
${key} = "${value}"
|
|
495
555
|
`;
|
|
496
556
|
}
|
|
497
|
-
|
|
557
|
+
writeFileSync2(tfvarsPath, content);
|
|
558
|
+
}
|
|
559
|
+
function resolveTfvarsPath(stage) {
|
|
560
|
+
const tfDir = resolveTerraformDir(stage);
|
|
561
|
+
if (tfDir) {
|
|
562
|
+
const direct = `${tfDir}/terraform.tfvars`;
|
|
563
|
+
if (existsSync3(direct)) return direct;
|
|
564
|
+
}
|
|
565
|
+
const terraformDir = process.env.THINKWORK_TERRAFORM_DIR || process.cwd();
|
|
566
|
+
const cwd = resolveTierDir(terraformDir, stage, "app");
|
|
567
|
+
return `${cwd}/terraform.tfvars`;
|
|
498
568
|
}
|
|
499
569
|
function registerConfigCommand(program2) {
|
|
500
570
|
const config = program2.command("config").description("View or change stack configuration");
|
|
571
|
+
config.command("list").description("List all environments, or show config for a specific stage").option("-s, --stage <name>", "Show config for a specific stage").action((opts) => {
|
|
572
|
+
if (opts.stage) {
|
|
573
|
+
const env = loadEnvironment(opts.stage);
|
|
574
|
+
if (!env) {
|
|
575
|
+
printError(`Environment "${opts.stage}" not found. Run \`thinkwork init -s ${opts.stage}\` first.`);
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
console.log("");
|
|
579
|
+
console.log(chalk3.bold.cyan(` \u2B21 ${env.stage}`));
|
|
580
|
+
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"));
|
|
581
|
+
console.log(` ${chalk3.bold("Region:")} ${env.region}`);
|
|
582
|
+
console.log(` ${chalk3.bold("Account:")} ${env.accountId}`);
|
|
583
|
+
console.log(` ${chalk3.bold("Database:")} ${env.databaseEngine}`);
|
|
584
|
+
console.log(` ${chalk3.bold("Memory:")} ${env.memoryEngine}`);
|
|
585
|
+
console.log(` ${chalk3.bold("Terraform dir:")} ${env.terraformDir}`);
|
|
586
|
+
console.log(` ${chalk3.bold("Created:")} ${env.createdAt}`);
|
|
587
|
+
console.log(` ${chalk3.bold("Updated:")} ${env.updatedAt}`);
|
|
588
|
+
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"));
|
|
589
|
+
const tfvarsPath = `${env.terraformDir}/terraform.tfvars`;
|
|
590
|
+
if (existsSync3(tfvarsPath)) {
|
|
591
|
+
console.log("");
|
|
592
|
+
console.log(chalk3.dim(" terraform.tfvars:"));
|
|
593
|
+
const content = readFileSync2(tfvarsPath, "utf-8");
|
|
594
|
+
for (const line of content.split("\n")) {
|
|
595
|
+
if (line.trim() && !line.trim().startsWith("#")) {
|
|
596
|
+
const masked = line.replace(
|
|
597
|
+
/^(db_password\s*=\s*)".*"/,
|
|
598
|
+
'$1"********"'
|
|
599
|
+
).replace(
|
|
600
|
+
/^(api_auth_secret\s*=\s*)".*"/,
|
|
601
|
+
'$1"********"'
|
|
602
|
+
).replace(
|
|
603
|
+
/^(google_oauth_client_secret\s*=\s*)".*"/,
|
|
604
|
+
'$1"********"'
|
|
605
|
+
);
|
|
606
|
+
console.log(` ${chalk3.dim(masked)}`);
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
console.log("");
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
613
|
+
const envs = listEnvironments();
|
|
614
|
+
if (envs.length === 0) {
|
|
615
|
+
console.log("");
|
|
616
|
+
console.log(" No environments found.");
|
|
617
|
+
console.log(` Run ${chalk3.cyan("thinkwork init -s <stage>")} to create one.`);
|
|
618
|
+
console.log("");
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
console.log("");
|
|
622
|
+
console.log(chalk3.bold(" Environments"));
|
|
623
|
+
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
|
+
for (const env of envs) {
|
|
625
|
+
const memBadge = env.memoryEngine === "hindsight" ? chalk3.magenta("hindsight") : chalk3.dim("managed");
|
|
626
|
+
const dbBadge = env.databaseEngine === "rds-postgres" ? chalk3.yellow("rds") : chalk3.dim("aurora");
|
|
627
|
+
console.log(
|
|
628
|
+
` ${chalk3.bold.cyan(env.stage.padEnd(16))}${env.region.padEnd(14)}${env.accountId.padEnd(16)}${dbBadge.padEnd(20)}${memBadge}`
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
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"));
|
|
632
|
+
console.log(chalk3.dim(` ${envs.length} environment(s)`));
|
|
633
|
+
console.log("");
|
|
634
|
+
console.log(` Show details: ${chalk3.cyan("thinkwork config list -s <stage>")}`);
|
|
635
|
+
console.log("");
|
|
636
|
+
});
|
|
501
637
|
config.command("get <key>").description("Get a configuration value (e.g. memory-engine)").requiredOption("-s, --stage <name>", "Deployment stage").action((key, opts) => {
|
|
502
638
|
const stageCheck = validateStage(opts.stage);
|
|
503
639
|
if (!stageCheck.valid) {
|
|
504
640
|
printError(stageCheck.error);
|
|
505
641
|
process.exit(1);
|
|
506
642
|
}
|
|
507
|
-
const
|
|
508
|
-
const cwd = resolveTierDir(terraformDir, opts.stage, "app");
|
|
509
|
-
const tfvarsPath = `${cwd}/terraform.tfvars`;
|
|
643
|
+
const tfvarsPath = resolveTfvarsPath(opts.stage);
|
|
510
644
|
const tfKey = key.replace(/-/g, "_");
|
|
511
645
|
const value = readTfVar(tfvarsPath, tfKey);
|
|
512
646
|
if (value === null) {
|
|
@@ -515,7 +649,7 @@ function registerConfigCommand(program2) {
|
|
|
515
649
|
console.log(` ${key} = ${value}`);
|
|
516
650
|
}
|
|
517
651
|
});
|
|
518
|
-
config.command("set <key> <value>").description("Set a configuration value and optionally deploy
|
|
652
|
+
config.command("set <key> <value>").description("Set a configuration value and optionally deploy").requiredOption("-s, --stage <name>", "Deployment stage").option("--apply", "Run terraform apply after changing the value").action(async (key, value, opts) => {
|
|
519
653
|
const stageCheck = validateStage(opts.stage);
|
|
520
654
|
if (!stageCheck.valid) {
|
|
521
655
|
printError(stageCheck.error);
|
|
@@ -528,18 +662,21 @@ function registerConfigCommand(program2) {
|
|
|
528
662
|
}
|
|
529
663
|
const identity = getAwsIdentity();
|
|
530
664
|
printHeader("config set", opts.stage, identity);
|
|
531
|
-
const
|
|
532
|
-
const cwd = resolveTierDir(terraformDir, opts.stage, "app");
|
|
533
|
-
const tfvarsPath = `${cwd}/terraform.tfvars`;
|
|
665
|
+
const tfvarsPath = resolveTfvarsPath(opts.stage);
|
|
534
666
|
const oldValue = readTfVar(tfvarsPath, tfKey);
|
|
535
667
|
setTfVar(tfvarsPath, tfKey, value);
|
|
536
668
|
console.log(` ${key}: ${oldValue ?? "(unset)"} \u2192 ${value}`);
|
|
537
669
|
if (opts.apply) {
|
|
670
|
+
const tfDir = resolveTerraformDir(opts.stage);
|
|
671
|
+
if (!tfDir) {
|
|
672
|
+
printError("Cannot find terraform directory. Run `thinkwork init` first.");
|
|
673
|
+
process.exit(1);
|
|
674
|
+
}
|
|
538
675
|
console.log("");
|
|
539
676
|
console.log(" Applying configuration change...");
|
|
540
|
-
await ensureInit(
|
|
541
|
-
await ensureWorkspace(
|
|
542
|
-
const code = await runTerraform(
|
|
677
|
+
await ensureInit(tfDir);
|
|
678
|
+
await ensureWorkspace(tfDir, opts.stage);
|
|
679
|
+
const code = await runTerraform(tfDir, [
|
|
543
680
|
"apply",
|
|
544
681
|
"-auto-approve",
|
|
545
682
|
`-var=stage=${opts.stage}`
|
|
@@ -709,14 +846,16 @@ function registerLoginCommand(program2) {
|
|
|
709
846
|
}
|
|
710
847
|
|
|
711
848
|
// src/commands/init.ts
|
|
712
|
-
import { existsSync as
|
|
713
|
-
import { resolve as resolve2, join } from "path";
|
|
849
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync3, cpSync } from "fs";
|
|
850
|
+
import { resolve as resolve2, join as join2, dirname } from "path";
|
|
714
851
|
import { execSync as execSync4 } from "child_process";
|
|
852
|
+
import { fileURLToPath } from "url";
|
|
715
853
|
import { createInterface as createInterface3 } from "readline";
|
|
716
|
-
import
|
|
854
|
+
import chalk4 from "chalk";
|
|
855
|
+
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
717
856
|
function ask2(prompt, defaultVal = "") {
|
|
718
857
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
719
|
-
const suffix = defaultVal ?
|
|
858
|
+
const suffix = defaultVal ? chalk4.dim(` [${defaultVal}]`) : "";
|
|
720
859
|
return new Promise((resolve3) => {
|
|
721
860
|
rl.question(` ${prompt}${suffix}: `, (answer) => {
|
|
722
861
|
rl.close();
|
|
@@ -725,7 +864,7 @@ function ask2(prompt, defaultVal = "") {
|
|
|
725
864
|
});
|
|
726
865
|
}
|
|
727
866
|
function choose(prompt, options, defaultVal) {
|
|
728
|
-
const optStr = options.map((o) => o === defaultVal ?
|
|
867
|
+
const optStr = options.map((o) => o === defaultVal ? chalk4.bold(o) : chalk4.dim(o)).join(" / ");
|
|
729
868
|
return ask2(`${prompt} (${optStr})`, defaultVal);
|
|
730
869
|
}
|
|
731
870
|
function generateSecret(length = 32) {
|
|
@@ -736,6 +875,15 @@ function generateSecret(length = 32) {
|
|
|
736
875
|
for (const b of bytes) result += chars[b % chars.length];
|
|
737
876
|
return result;
|
|
738
877
|
}
|
|
878
|
+
function findBundledTerraform() {
|
|
879
|
+
const bundled = resolve2(__dirname, "..", "terraform");
|
|
880
|
+
if (existsSync4(join2(bundled, "modules"))) return bundled;
|
|
881
|
+
const repoTf = resolve2(__dirname, "..", "..", "..", "..", "terraform");
|
|
882
|
+
if (existsSync4(join2(repoTf, "modules"))) return repoTf;
|
|
883
|
+
throw new Error(
|
|
884
|
+
"Terraform modules not found. The CLI package may be incomplete.\nTry reinstalling: npm install -g thinkwork-cli@latest"
|
|
885
|
+
);
|
|
886
|
+
}
|
|
739
887
|
function buildTfvars(config) {
|
|
740
888
|
const lines = [
|
|
741
889
|
`# Thinkwork \u2014 ${config.stage} stage`,
|
|
@@ -782,7 +930,7 @@ function buildTfvars(config) {
|
|
|
782
930
|
return lines.join("\n");
|
|
783
931
|
}
|
|
784
932
|
function registerInitCommand(program2) {
|
|
785
|
-
program2.command("init").description("Initialize a new Thinkwork environment").requiredOption("-s, --stage <name>", "Stage name (e.g. dev, staging, prod)").option("-d, --dir <path>", "
|
|
933
|
+
program2.command("init").description("Initialize a new Thinkwork environment").requiredOption("-s, --stage <name>", "Stage name (e.g. dev, staging, prod)").option("-d, --dir <path>", "Target directory", ".").option("--defaults", "Skip interactive prompts, use all defaults").action(async (opts) => {
|
|
786
934
|
const stageCheck = validateStage(opts.stage);
|
|
787
935
|
if (!stageCheck.valid) {
|
|
788
936
|
printError(stageCheck.error);
|
|
@@ -794,10 +942,10 @@ function registerInitCommand(program2) {
|
|
|
794
942
|
printError("AWS credentials not configured. Run `thinkwork login` first.");
|
|
795
943
|
process.exit(1);
|
|
796
944
|
}
|
|
797
|
-
const
|
|
798
|
-
const tfDir =
|
|
799
|
-
const tfvarsPath =
|
|
800
|
-
if (
|
|
945
|
+
const targetDir = resolve2(opts.dir);
|
|
946
|
+
const tfDir = join2(targetDir, "terraform");
|
|
947
|
+
const tfvarsPath = join2(tfDir, "terraform.tfvars");
|
|
948
|
+
if (existsSync4(tfvarsPath)) {
|
|
801
949
|
printWarning(`terraform.tfvars already exists at ${tfvarsPath}`);
|
|
802
950
|
const overwrite = await ask2("Overwrite?", "N");
|
|
803
951
|
if (overwrite.toLowerCase() !== "y") {
|
|
@@ -821,19 +969,19 @@ function registerInitCommand(program2) {
|
|
|
821
969
|
config.admin_url = "http://localhost:5174";
|
|
822
970
|
config.mobile_scheme = "thinkwork";
|
|
823
971
|
} else {
|
|
824
|
-
console.log(
|
|
972
|
+
console.log(chalk4.bold(" Configure your Thinkwork environment\n"));
|
|
825
973
|
const defaultRegion = identity.region !== "unknown" ? identity.region : "us-east-1";
|
|
826
974
|
config.region = await ask2("AWS Region", defaultRegion);
|
|
827
975
|
console.log("");
|
|
828
|
-
console.log(
|
|
976
|
+
console.log(chalk4.dim(" \u2500\u2500 Database \u2500\u2500"));
|
|
829
977
|
config.database_engine = await choose("Database engine", ["aurora-serverless", "rds-postgres"], "aurora-serverless");
|
|
830
978
|
console.log("");
|
|
831
|
-
console.log(
|
|
832
|
-
console.log(
|
|
833
|
-
console.log(
|
|
979
|
+
console.log(chalk4.dim(" \u2500\u2500 Memory \u2500\u2500"));
|
|
980
|
+
console.log(chalk4.dim(" managed = Built-in AgentCore memory (remember/recall/forget)"));
|
|
981
|
+
console.log(chalk4.dim(" hindsight = ECS Fargate service with semantic + graph retrieval"));
|
|
834
982
|
config.memory_engine = await choose("Memory engine", ["managed", "hindsight"], "managed");
|
|
835
983
|
console.log("");
|
|
836
|
-
console.log(
|
|
984
|
+
console.log(chalk4.dim(" \u2500\u2500 Auth \u2500\u2500"));
|
|
837
985
|
const useGoogle = await ask2("Enable Google OAuth login? (y/N)", "N");
|
|
838
986
|
if (useGoogle.toLowerCase() === "y") {
|
|
839
987
|
config.google_oauth_client_id = await ask2("Google OAuth Client ID");
|
|
@@ -843,30 +991,128 @@ function registerInitCommand(program2) {
|
|
|
843
991
|
config.google_oauth_client_secret = "";
|
|
844
992
|
}
|
|
845
993
|
console.log("");
|
|
846
|
-
console.log(
|
|
994
|
+
console.log(chalk4.dim(" \u2500\u2500 Frontend URLs \u2500\u2500"));
|
|
847
995
|
config.admin_url = await ask2("Admin UI URL", "http://localhost:5174");
|
|
848
996
|
config.mobile_scheme = await ask2("Mobile app URL scheme", "thinkwork");
|
|
849
997
|
console.log("");
|
|
850
|
-
console.log(
|
|
851
|
-
console.log(
|
|
852
|
-
console.log(
|
|
998
|
+
console.log(chalk4.dim(" \u2500\u2500 Secrets (auto-generated) \u2500\u2500"));
|
|
999
|
+
console.log(chalk4.dim(` DB password: ${config.db_password.slice(0, 8)}...`));
|
|
1000
|
+
console.log(chalk4.dim(` API auth secret: ${config.api_auth_secret.slice(0, 16)}...`));
|
|
853
1001
|
}
|
|
854
|
-
|
|
855
|
-
|
|
1002
|
+
console.log("");
|
|
1003
|
+
console.log(" Scaffolding Terraform modules...");
|
|
1004
|
+
let bundledTf;
|
|
1005
|
+
try {
|
|
1006
|
+
bundledTf = findBundledTerraform();
|
|
1007
|
+
} catch (err) {
|
|
1008
|
+
printError(String(err));
|
|
1009
|
+
process.exit(1);
|
|
1010
|
+
}
|
|
1011
|
+
mkdirSync2(tfDir, { recursive: true });
|
|
1012
|
+
const copyDirs = ["modules", "examples"];
|
|
1013
|
+
for (const dir of copyDirs) {
|
|
1014
|
+
const src = join2(bundledTf, dir);
|
|
1015
|
+
const dst = join2(tfDir, dir);
|
|
1016
|
+
if (existsSync4(src) && !existsSync4(dst)) {
|
|
1017
|
+
cpSync(src, dst, { recursive: true });
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
const schemaPath = join2(bundledTf, "schema.graphql");
|
|
1021
|
+
if (existsSync4(schemaPath) && !existsSync4(join2(tfDir, "schema.graphql"))) {
|
|
1022
|
+
cpSync(schemaPath, join2(tfDir, "schema.graphql"));
|
|
856
1023
|
}
|
|
857
1024
|
const tfvars = buildTfvars(config);
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
1025
|
+
writeFileSync3(tfvarsPath, tfvars);
|
|
1026
|
+
const mainTfPath = join2(tfDir, "main.tf");
|
|
1027
|
+
if (!existsSync4(mainTfPath)) {
|
|
1028
|
+
writeFileSync3(mainTfPath, `################################################################################
|
|
1029
|
+
# Thinkwork \u2014 ${config.stage}
|
|
1030
|
+
# Generated by: thinkwork init -s ${config.stage}
|
|
1031
|
+
################################################################################
|
|
1032
|
+
|
|
1033
|
+
terraform {
|
|
1034
|
+
required_version = ">= 1.5"
|
|
1035
|
+
|
|
1036
|
+
required_providers {
|
|
1037
|
+
aws = {
|
|
1038
|
+
source = "hashicorp/aws"
|
|
1039
|
+
version = "~> 5.0"
|
|
1040
|
+
}
|
|
1041
|
+
archive = {
|
|
1042
|
+
source = "hashicorp/archive"
|
|
1043
|
+
version = "~> 2.0"
|
|
1044
|
+
}
|
|
1045
|
+
null = {
|
|
1046
|
+
source = "hashicorp/null"
|
|
1047
|
+
version = "~> 3.0"
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
provider "aws" {
|
|
1053
|
+
region = var.region
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
variable "stage" { type = string }
|
|
1057
|
+
variable "region" { type = string; default = "us-east-1" }
|
|
1058
|
+
variable "account_id" { type = string }
|
|
1059
|
+
variable "db_password" { type = string; sensitive = true }
|
|
1060
|
+
variable "database_engine" { type = string; default = "aurora-serverless" }
|
|
1061
|
+
variable "memory_engine" { type = string; default = "managed" }
|
|
1062
|
+
variable "google_oauth_client_id" { type = string; default = "" }
|
|
1063
|
+
variable "google_oauth_client_secret" { type = string; sensitive = true; default = "" }
|
|
1064
|
+
variable "pre_signup_lambda_zip" { type = string; default = "" }
|
|
1065
|
+
variable "lambda_zips_dir" { type = string; default = "" }
|
|
1066
|
+
variable "api_auth_secret" { type = string; sensitive = true; default = "" }
|
|
1067
|
+
variable "admin_callback_urls" { type = list(string); default = ["http://localhost:5174", "http://localhost:5174/auth/callback"] }
|
|
1068
|
+
variable "admin_logout_urls" { type = list(string); default = ["http://localhost:5174"] }
|
|
1069
|
+
variable "mobile_callback_urls" { type = list(string); default = ["exp://localhost:8081", "thinkwork://", "thinkwork://auth/callback"] }
|
|
1070
|
+
variable "mobile_logout_urls" { type = list(string); default = ["exp://localhost:8081", "thinkwork://"] }
|
|
1071
|
+
|
|
1072
|
+
module "thinkwork" {
|
|
1073
|
+
source = "./modules/thinkwork"
|
|
1074
|
+
|
|
1075
|
+
stage = var.stage
|
|
1076
|
+
region = var.region
|
|
1077
|
+
account_id = var.account_id
|
|
1078
|
+
|
|
1079
|
+
db_password = var.db_password
|
|
1080
|
+
database_engine = var.database_engine
|
|
1081
|
+
memory_engine = var.memory_engine
|
|
1082
|
+
google_oauth_client_id = var.google_oauth_client_id
|
|
1083
|
+
google_oauth_client_secret = var.google_oauth_client_secret
|
|
1084
|
+
pre_signup_lambda_zip = var.pre_signup_lambda_zip
|
|
1085
|
+
lambda_zips_dir = var.lambda_zips_dir
|
|
1086
|
+
api_auth_secret = var.api_auth_secret
|
|
1087
|
+
admin_callback_urls = var.admin_callback_urls
|
|
1088
|
+
admin_logout_urls = var.admin_logout_urls
|
|
1089
|
+
mobile_callback_urls = var.mobile_callback_urls
|
|
1090
|
+
mobile_logout_urls = var.mobile_logout_urls
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
output "api_endpoint" { value = module.thinkwork.api_endpoint }
|
|
1094
|
+
output "user_pool_id" { value = module.thinkwork.user_pool_id }
|
|
1095
|
+
output "admin_client_id" { value = module.thinkwork.admin_client_id }
|
|
1096
|
+
output "mobile_client_id" { value = module.thinkwork.mobile_client_id }
|
|
1097
|
+
output "bucket_name" { value = module.thinkwork.bucket_name }
|
|
1098
|
+
output "db_cluster_endpoint" { value = module.thinkwork.db_cluster_endpoint }
|
|
1099
|
+
output "db_secret_arn" { value = module.thinkwork.db_secret_arn; sensitive = true }
|
|
1100
|
+
output "ecr_repository_url" { value = module.thinkwork.ecr_repository_url }
|
|
1101
|
+
output "memory_engine" { value = module.thinkwork.memory_engine }
|
|
1102
|
+
output "hindsight_endpoint" { value = module.thinkwork.hindsight_endpoint }
|
|
1103
|
+
`);
|
|
1104
|
+
}
|
|
1105
|
+
console.log(` Wrote ${chalk4.cyan(tfDir + "/")}`);
|
|
861
1106
|
console.log("");
|
|
862
|
-
console.log(
|
|
863
|
-
console.log(` ${
|
|
864
|
-
console.log(` ${
|
|
865
|
-
console.log(` ${
|
|
866
|
-
console.log(` ${
|
|
867
|
-
console.log(` ${
|
|
868
|
-
console.log(` ${
|
|
869
|
-
console.log(
|
|
1107
|
+
console.log(chalk4.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"));
|
|
1108
|
+
console.log(` ${chalk4.bold("Stage:")} ${config.stage}`);
|
|
1109
|
+
console.log(` ${chalk4.bold("Region:")} ${config.region}`);
|
|
1110
|
+
console.log(` ${chalk4.bold("Account:")} ${config.account_id}`);
|
|
1111
|
+
console.log(` ${chalk4.bold("Database:")} ${config.database_engine}`);
|
|
1112
|
+
console.log(` ${chalk4.bold("Memory:")} ${config.memory_engine}`);
|
|
1113
|
+
console.log(` ${chalk4.bold("Google OAuth:")} ${config.google_oauth_client_id ? "enabled" : "disabled"}`);
|
|
1114
|
+
console.log(` ${chalk4.bold("Directory:")} ${tfDir}`);
|
|
1115
|
+
console.log(chalk4.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"));
|
|
870
1116
|
console.log("\n Initializing Terraform...\n");
|
|
871
1117
|
try {
|
|
872
1118
|
execSync4("terraform init", { cwd: tfDir, stdio: "inherit" });
|
|
@@ -874,13 +1120,24 @@ function registerInitCommand(program2) {
|
|
|
874
1120
|
printWarning("Terraform init failed. Run `thinkwork doctor -s " + opts.stage + "` to check prerequisites.");
|
|
875
1121
|
return;
|
|
876
1122
|
}
|
|
1123
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1124
|
+
saveEnvironment({
|
|
1125
|
+
stage: config.stage,
|
|
1126
|
+
region: config.region,
|
|
1127
|
+
accountId: config.account_id,
|
|
1128
|
+
terraformDir: tfDir,
|
|
1129
|
+
databaseEngine: config.database_engine,
|
|
1130
|
+
memoryEngine: config.memory_engine,
|
|
1131
|
+
createdAt: now,
|
|
1132
|
+
updatedAt: now
|
|
1133
|
+
});
|
|
877
1134
|
printSuccess(`Environment "${opts.stage}" initialized`);
|
|
878
1135
|
console.log("");
|
|
879
1136
|
console.log(" Next steps:");
|
|
880
|
-
console.log(` ${
|
|
881
|
-
console.log(` ${
|
|
882
|
-
console.log(` ${
|
|
883
|
-
console.log(` ${
|
|
1137
|
+
console.log(` ${chalk4.cyan("1.")} thinkwork plan -s ${opts.stage} ${chalk4.dim("# Review infrastructure plan")}`);
|
|
1138
|
+
console.log(` ${chalk4.cyan("2.")} thinkwork deploy -s ${opts.stage} ${chalk4.dim("# Deploy to AWS (~5 min)")}`);
|
|
1139
|
+
console.log(` ${chalk4.cyan("3.")} thinkwork bootstrap -s ${opts.stage} ${chalk4.dim("# Seed workspace files + skills")}`);
|
|
1140
|
+
console.log(` ${chalk4.cyan("4.")} thinkwork outputs -s ${opts.stage} ${chalk4.dim("# Show API URL, Cognito IDs, etc.")}`);
|
|
884
1141
|
console.log("");
|
|
885
1142
|
});
|
|
886
1143
|
}
|