thinkwork-cli 0.1.0 → 0.1.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/README.md +3 -3
- package/dist/cli.js +123 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# thinkwork-cli
|
|
2
2
|
|
|
3
3
|
Deploy and manage Thinkwork AI agent stacks on AWS.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install -g
|
|
8
|
+
npm install -g thinkwork-cli
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Or run without installing:
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
npx
|
|
14
|
+
npx thinkwork-cli --help
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
## Quick Start
|
package/dist/cli.js
CHANGED
|
@@ -713,43 +713,76 @@ import { existsSync as existsSync3, mkdirSync, writeFileSync as writeFileSync2 }
|
|
|
713
713
|
import { resolve as resolve2, join } from "path";
|
|
714
714
|
import { execSync as execSync4 } from "child_process";
|
|
715
715
|
import { createInterface as createInterface3 } from "readline";
|
|
716
|
+
import chalk3 from "chalk";
|
|
716
717
|
function ask2(prompt, defaultVal = "") {
|
|
717
718
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
718
|
-
const suffix = defaultVal ? ` [${defaultVal}]` : "";
|
|
719
|
+
const suffix = defaultVal ? chalk3.dim(` [${defaultVal}]`) : "";
|
|
719
720
|
return new Promise((resolve3) => {
|
|
720
|
-
rl.question(
|
|
721
|
+
rl.question(` ${prompt}${suffix}: `, (answer) => {
|
|
721
722
|
rl.close();
|
|
722
723
|
resolve3(answer.trim() || defaultVal);
|
|
723
724
|
});
|
|
724
725
|
});
|
|
725
726
|
}
|
|
726
|
-
function
|
|
727
|
-
const
|
|
728
|
-
|
|
729
|
-
|
|
727
|
+
function choose(prompt, options, defaultVal) {
|
|
728
|
+
const optStr = options.map((o) => o === defaultVal ? chalk3.bold(o) : chalk3.dim(o)).join(" / ");
|
|
729
|
+
return ask2(`${prompt} (${optStr})`, defaultVal);
|
|
730
|
+
}
|
|
731
|
+
function generateSecret(length = 32) {
|
|
732
|
+
const chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
|
733
|
+
let result = "";
|
|
734
|
+
const bytes = new Uint8Array(length);
|
|
730
735
|
globalThis.crypto.getRandomValues(bytes);
|
|
731
|
-
for (const b of bytes)
|
|
732
|
-
return
|
|
736
|
+
for (const b of bytes) result += chars[b % chars.length];
|
|
737
|
+
return result;
|
|
738
|
+
}
|
|
739
|
+
function buildTfvars(config) {
|
|
740
|
+
const lines = [
|
|
741
|
+
`# Thinkwork \u2014 ${config.stage} stage`,
|
|
742
|
+
`# Generated by: thinkwork init -s ${config.stage}`,
|
|
743
|
+
`# ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`,
|
|
744
|
+
``,
|
|
745
|
+
`# \u2500\u2500 Core \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
|
|
746
|
+
`stage = "${config.stage}"`,
|
|
747
|
+
`region = "${config.region}"`,
|
|
748
|
+
`account_id = "${config.account_id}"`,
|
|
749
|
+
``,
|
|
750
|
+
`# \u2500\u2500 Database \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`,
|
|
751
|
+
`database_engine = "${config.database_engine}"`,
|
|
752
|
+
`db_password = "${config.db_password}"`,
|
|
753
|
+
``,
|
|
754
|
+
`# \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`,
|
|
755
|
+
`memory_engine = "${config.memory_engine}"`,
|
|
756
|
+
``,
|
|
757
|
+
`# \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`,
|
|
758
|
+
`api_auth_secret = "${config.api_auth_secret}"`
|
|
759
|
+
];
|
|
760
|
+
if (config.google_oauth_client_id) {
|
|
761
|
+
lines.push(``);
|
|
762
|
+
lines.push(`# \u2500\u2500 Google OAuth \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
763
|
+
lines.push(`google_oauth_client_id = "${config.google_oauth_client_id}"`);
|
|
764
|
+
lines.push(`google_oauth_client_secret = "${config.google_oauth_client_secret}"`);
|
|
765
|
+
} else {
|
|
766
|
+
lines.push(``);
|
|
767
|
+
lines.push(`# \u2500\u2500 Google OAuth (uncomment to enable Google social login) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
768
|
+
lines.push(`# google_oauth_client_id = ""`);
|
|
769
|
+
lines.push(`# google_oauth_client_secret = ""`);
|
|
770
|
+
}
|
|
771
|
+
if (config.admin_url && config.admin_url !== "http://localhost:5174") {
|
|
772
|
+
lines.push(``);
|
|
773
|
+
lines.push(`# \u2500\u2500 Callback URLs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
774
|
+
lines.push(`admin_callback_urls = ["${config.admin_url}", "${config.admin_url}/auth/callback"]`);
|
|
775
|
+
lines.push(`admin_logout_urls = ["${config.admin_url}"]`);
|
|
776
|
+
}
|
|
777
|
+
if (config.mobile_scheme && config.mobile_scheme !== "thinkwork") {
|
|
778
|
+
lines.push(`mobile_callback_urls = ["${config.mobile_scheme}://", "${config.mobile_scheme}://auth/callback"]`);
|
|
779
|
+
lines.push(`mobile_logout_urls = ["${config.mobile_scheme}://"]`);
|
|
780
|
+
}
|
|
781
|
+
lines.push(``);
|
|
782
|
+
return lines.join("\n");
|
|
733
783
|
}
|
|
734
|
-
var TFVARS_TEMPLATE = `# Thinkwork \u2014 {STAGE} stage
|
|
735
|
-
# Generated by: thinkwork init -s {STAGE}
|
|
736
|
-
|
|
737
|
-
stage = "{STAGE}"
|
|
738
|
-
region = "{REGION}"
|
|
739
|
-
account_id = "{ACCOUNT_ID}"
|
|
740
|
-
|
|
741
|
-
# Database
|
|
742
|
-
database_engine = "aurora-serverless"
|
|
743
|
-
db_password = "{DB_PASSWORD}"
|
|
744
|
-
|
|
745
|
-
# Memory engine: "managed" (built-in) or "hindsight" (ECS Fargate service)
|
|
746
|
-
memory_engine = "{MEMORY_ENGINE}"
|
|
747
|
-
|
|
748
|
-
# API authentication secret (shared between services)
|
|
749
|
-
api_auth_secret = "{API_SECRET}"
|
|
750
|
-
`;
|
|
751
784
|
function registerInitCommand(program2) {
|
|
752
|
-
program2.command("init").description("Initialize a new Thinkwork environment").requiredOption("-s, --stage <name>", "Stage name (e.g. dev, staging, prod)").option("-d, --dir <path>", "Directory to initialize in", ".").action(async (opts) => {
|
|
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>", "Directory to initialize in", ".").option("--defaults", "Skip interactive prompts, use all defaults").action(async (opts) => {
|
|
753
786
|
const stageCheck = validateStage(opts.stage);
|
|
754
787
|
if (!stageCheck.valid) {
|
|
755
788
|
printError(stageCheck.error);
|
|
@@ -766,38 +799,88 @@ function registerInitCommand(program2) {
|
|
|
766
799
|
const tfvarsPath = join(tfDir, "terraform.tfvars");
|
|
767
800
|
if (existsSync3(tfvarsPath)) {
|
|
768
801
|
printWarning(`terraform.tfvars already exists at ${tfvarsPath}`);
|
|
769
|
-
const overwrite = await ask2("
|
|
802
|
+
const overwrite = await ask2("Overwrite?", "N");
|
|
770
803
|
if (overwrite.toLowerCase() !== "y") {
|
|
771
804
|
console.log(" Aborted.");
|
|
772
805
|
return;
|
|
773
806
|
}
|
|
807
|
+
console.log("");
|
|
808
|
+
}
|
|
809
|
+
const config = {
|
|
810
|
+
stage: opts.stage,
|
|
811
|
+
account_id: identity.account,
|
|
812
|
+
db_password: generateSecret(24),
|
|
813
|
+
api_auth_secret: `tw-${opts.stage}-${generateSecret(16)}`
|
|
814
|
+
};
|
|
815
|
+
if (opts.defaults) {
|
|
816
|
+
config.region = identity.region !== "unknown" ? identity.region : "us-east-1";
|
|
817
|
+
config.database_engine = "aurora-serverless";
|
|
818
|
+
config.memory_engine = "managed";
|
|
819
|
+
config.google_oauth_client_id = "";
|
|
820
|
+
config.google_oauth_client_secret = "";
|
|
821
|
+
config.admin_url = "http://localhost:5174";
|
|
822
|
+
config.mobile_scheme = "thinkwork";
|
|
823
|
+
} else {
|
|
824
|
+
console.log(chalk3.bold(" Configure your Thinkwork environment\n"));
|
|
825
|
+
const defaultRegion = identity.region !== "unknown" ? identity.region : "us-east-1";
|
|
826
|
+
config.region = await ask2("AWS Region", defaultRegion);
|
|
827
|
+
console.log("");
|
|
828
|
+
console.log(chalk3.dim(" \u2500\u2500 Database \u2500\u2500"));
|
|
829
|
+
config.database_engine = await choose("Database engine", ["aurora-serverless", "rds-postgres"], "aurora-serverless");
|
|
830
|
+
console.log("");
|
|
831
|
+
console.log(chalk3.dim(" \u2500\u2500 Memory \u2500\u2500"));
|
|
832
|
+
console.log(chalk3.dim(" managed = Built-in AgentCore memory (remember/recall/forget)"));
|
|
833
|
+
console.log(chalk3.dim(" hindsight = ECS Fargate service with semantic + graph retrieval"));
|
|
834
|
+
config.memory_engine = await choose("Memory engine", ["managed", "hindsight"], "managed");
|
|
835
|
+
console.log("");
|
|
836
|
+
console.log(chalk3.dim(" \u2500\u2500 Auth \u2500\u2500"));
|
|
837
|
+
const useGoogle = await ask2("Enable Google OAuth login? (y/N)", "N");
|
|
838
|
+
if (useGoogle.toLowerCase() === "y") {
|
|
839
|
+
config.google_oauth_client_id = await ask2("Google OAuth Client ID");
|
|
840
|
+
config.google_oauth_client_secret = await ask2("Google OAuth Client Secret");
|
|
841
|
+
} else {
|
|
842
|
+
config.google_oauth_client_id = "";
|
|
843
|
+
config.google_oauth_client_secret = "";
|
|
844
|
+
}
|
|
845
|
+
console.log("");
|
|
846
|
+
console.log(chalk3.dim(" \u2500\u2500 Frontend URLs \u2500\u2500"));
|
|
847
|
+
config.admin_url = await ask2("Admin UI URL", "http://localhost:5174");
|
|
848
|
+
config.mobile_scheme = await ask2("Mobile app URL scheme", "thinkwork");
|
|
849
|
+
console.log("");
|
|
850
|
+
console.log(chalk3.dim(" \u2500\u2500 Secrets (auto-generated) \u2500\u2500"));
|
|
851
|
+
console.log(chalk3.dim(` DB password: ${config.db_password.slice(0, 8)}...`));
|
|
852
|
+
console.log(chalk3.dim(` API auth secret: ${config.api_auth_secret.slice(0, 16)}...`));
|
|
774
853
|
}
|
|
775
|
-
console.log(" Configure your Thinkwork environment:\n");
|
|
776
|
-
const region = await ask2(" AWS Region", identity.region !== "unknown" ? identity.region : "us-east-1");
|
|
777
|
-
const memoryEngine = await ask2(" Memory engine (managed/hindsight)", "managed");
|
|
778
|
-
const dbPassword = generatePassword();
|
|
779
|
-
const apiSecret = `tw-${opts.stage}-${generatePassword().slice(0, 12)}`;
|
|
780
854
|
if (!existsSync3(tfDir)) {
|
|
781
855
|
mkdirSync(tfDir, { recursive: true });
|
|
782
856
|
}
|
|
783
|
-
const tfvars =
|
|
857
|
+
const tfvars = buildTfvars(config);
|
|
784
858
|
writeFileSync2(tfvarsPath, tfvars);
|
|
785
|
-
console.log(
|
|
786
|
-
Wrote ${tfvarsPath}`);
|
|
859
|
+
console.log("");
|
|
860
|
+
console.log(` Wrote ${chalk3.cyan(tfvarsPath)}`);
|
|
861
|
+
console.log("");
|
|
862
|
+
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"));
|
|
863
|
+
console.log(` ${chalk3.bold("Stage:")} ${config.stage}`);
|
|
864
|
+
console.log(` ${chalk3.bold("Region:")} ${config.region}`);
|
|
865
|
+
console.log(` ${chalk3.bold("Account:")} ${config.account_id}`);
|
|
866
|
+
console.log(` ${chalk3.bold("Database:")} ${config.database_engine}`);
|
|
867
|
+
console.log(` ${chalk3.bold("Memory:")} ${config.memory_engine}`);
|
|
868
|
+
console.log(` ${chalk3.bold("Google OAuth:")} ${config.google_oauth_client_id ? "enabled" : "disabled"}`);
|
|
869
|
+
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"));
|
|
787
870
|
console.log("\n Initializing Terraform...\n");
|
|
788
871
|
try {
|
|
789
872
|
execSync4("terraform init", { cwd: tfDir, stdio: "inherit" });
|
|
790
873
|
} catch {
|
|
791
|
-
printWarning("Terraform init failed
|
|
792
|
-
printWarning("Run: thinkwork doctor -s " + opts.stage);
|
|
874
|
+
printWarning("Terraform init failed. Run `thinkwork doctor -s " + opts.stage + "` to check prerequisites.");
|
|
793
875
|
return;
|
|
794
876
|
}
|
|
795
877
|
printSuccess(`Environment "${opts.stage}" initialized`);
|
|
796
878
|
console.log("");
|
|
797
879
|
console.log(" Next steps:");
|
|
798
|
-
console.log(` 1. thinkwork plan -s ${opts.stage} # Review
|
|
799
|
-
console.log(` 2. thinkwork deploy -s ${opts.stage} # Deploy
|
|
800
|
-
console.log(` 3. thinkwork bootstrap -s ${opts.stage} # Seed workspace + skills`);
|
|
880
|
+
console.log(` ${chalk3.cyan("1.")} thinkwork plan -s ${opts.stage} ${chalk3.dim("# Review infrastructure plan")}`);
|
|
881
|
+
console.log(` ${chalk3.cyan("2.")} thinkwork deploy -s ${opts.stage} ${chalk3.dim("# Deploy to AWS")}`);
|
|
882
|
+
console.log(` ${chalk3.cyan("3.")} thinkwork bootstrap -s ${opts.stage} ${chalk3.dim("# Seed workspace files + skills")}`);
|
|
883
|
+
console.log(` ${chalk3.cyan("4.")} thinkwork outputs -s ${opts.stage} ${chalk3.dim("# Show API URL, Cognito IDs, etc.")}`);
|
|
801
884
|
console.log("");
|
|
802
885
|
});
|
|
803
886
|
}
|