thinkwork-cli 0.12.5 → 0.12.7
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/api-client-MC4NOZ4L.js +16 -0
- package/dist/chunk-34NMB5AK.js +359 -0
- package/dist/cli.js +523 -682
- package/dist/commands/enterprise/templates/deploy-repo/docs/runbook.md +197 -27
- package/dist/terraform/modules/app/lambda-api/handlers.tf +2 -1
- package/dist/terraform/modules/app/lambda-api/main.tf +19 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
import {
|
|
3
|
+
__export,
|
|
4
|
+
apiFetch,
|
|
5
|
+
apiFetchRaw,
|
|
6
|
+
ensureInit,
|
|
7
|
+
ensureWorkspace,
|
|
8
|
+
getApiEndpoint,
|
|
9
|
+
listDeployedStages,
|
|
10
|
+
listEnvironments,
|
|
11
|
+
loadEnvironment,
|
|
12
|
+
printError,
|
|
13
|
+
printHeader,
|
|
14
|
+
printMissingApiSessionError,
|
|
15
|
+
printSuccess,
|
|
16
|
+
printSummary,
|
|
17
|
+
printTierHeader,
|
|
18
|
+
printWarning,
|
|
19
|
+
resolveApiConfig,
|
|
20
|
+
resolveTerraformDir,
|
|
21
|
+
resolveTierDir,
|
|
22
|
+
runTerraform,
|
|
23
|
+
saveEnterpriseDeployment,
|
|
24
|
+
saveEnvironment
|
|
25
|
+
} from "./chunk-34NMB5AK.js";
|
|
7
26
|
|
|
8
27
|
// src/cli.ts
|
|
9
28
|
import { Command } from "commander";
|
|
@@ -22,20 +41,20 @@ function getCliConfigPath(override) {
|
|
|
22
41
|
return override ?? join(homedir(), ".thinkwork", "config.json");
|
|
23
42
|
}
|
|
24
43
|
function loadCliConfig(pathOverride) {
|
|
25
|
-
const
|
|
26
|
-
if (!existsSync(
|
|
44
|
+
const path = getCliConfigPath(pathOverride);
|
|
45
|
+
if (!existsSync(path)) return {};
|
|
27
46
|
try {
|
|
28
|
-
return JSON.parse(readFileSync(
|
|
47
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
29
48
|
} catch {
|
|
30
49
|
return {};
|
|
31
50
|
}
|
|
32
51
|
}
|
|
33
52
|
function saveCliConfig(next, pathOverride) {
|
|
34
|
-
const
|
|
35
|
-
const dir = dirname(
|
|
53
|
+
const path = getCliConfigPath(pathOverride);
|
|
54
|
+
const dir = dirname(path);
|
|
36
55
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
37
56
|
const merged = { ...loadCliConfig(pathOverride), ...next };
|
|
38
|
-
writeFileSync(
|
|
57
|
+
writeFileSync(path, JSON.stringify(merged, null, 2) + "\n");
|
|
39
58
|
}
|
|
40
59
|
function saveStageSession(stage, session, pathOverride) {
|
|
41
60
|
const current = loadCliConfig(pathOverride);
|
|
@@ -168,190 +187,9 @@ function getAwsIdentity() {
|
|
|
168
187
|
}
|
|
169
188
|
}
|
|
170
189
|
|
|
171
|
-
// src/terraform.ts
|
|
172
|
-
import { spawn } from "child_process";
|
|
173
|
-
import { existsSync as existsSync2 } from "fs";
|
|
174
|
-
import path from "path";
|
|
175
|
-
function resolveTierDir(terraformDir, stage, tier) {
|
|
176
|
-
const envDir = path.join(terraformDir, "environments", stage, tier);
|
|
177
|
-
if (existsSync2(envDir)) {
|
|
178
|
-
return envDir;
|
|
179
|
-
}
|
|
180
|
-
const greenfield = path.join(terraformDir, "examples", "greenfield");
|
|
181
|
-
if (existsSync2(greenfield)) {
|
|
182
|
-
return greenfield;
|
|
183
|
-
}
|
|
184
|
-
const flat = path.join(terraformDir);
|
|
185
|
-
if (existsSync2(path.join(flat, "main.tf"))) {
|
|
186
|
-
return flat;
|
|
187
|
-
}
|
|
188
|
-
const cwdTf = path.join(process.cwd(), "terraform");
|
|
189
|
-
if (existsSync2(path.join(cwdTf, "main.tf"))) {
|
|
190
|
-
return cwdTf;
|
|
191
|
-
}
|
|
192
|
-
return terraformDir;
|
|
193
|
-
}
|
|
194
|
-
async function ensureWorkspace(cwd, stage) {
|
|
195
|
-
const list = await runTerraformRaw(cwd, ["workspace", "list"]);
|
|
196
|
-
const workspaces = list.split("\n").map((l) => l.replace("*", "").trim()).filter(Boolean);
|
|
197
|
-
if (!workspaces.includes(stage)) {
|
|
198
|
-
await runTerraformRaw(cwd, ["workspace", "new", stage]);
|
|
199
|
-
} else {
|
|
200
|
-
await runTerraformRaw(cwd, ["workspace", "select", stage]);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
function runTerraformRaw(cwd, args) {
|
|
204
|
-
return new Promise((resolve7, reject) => {
|
|
205
|
-
const proc = spawn("terraform", args, {
|
|
206
|
-
cwd,
|
|
207
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
208
|
-
});
|
|
209
|
-
let stdout = "";
|
|
210
|
-
let stderr = "";
|
|
211
|
-
proc.stdout.on("data", (d) => stdout += d);
|
|
212
|
-
proc.stderr.on("data", (d) => stderr += d);
|
|
213
|
-
proc.on("close", (code) => {
|
|
214
|
-
if (code === 0) resolve7(stdout);
|
|
215
|
-
else reject(new Error(`terraform ${args.join(" ")} failed (exit ${code}): ${stderr}`));
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
}
|
|
219
|
-
function runTerraform(cwd, args) {
|
|
220
|
-
return new Promise((resolve7) => {
|
|
221
|
-
console.log(`
|
|
222
|
-
\u2192 terraform ${args.join(" ")}
|
|
223
|
-
`);
|
|
224
|
-
const proc = spawn("terraform", args, {
|
|
225
|
-
cwd,
|
|
226
|
-
stdio: "inherit"
|
|
227
|
-
});
|
|
228
|
-
proc.on("close", (code) => resolve7(code ?? 1));
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
async function ensureInit(cwd) {
|
|
232
|
-
const dotTerraform = path.join(cwd, ".terraform");
|
|
233
|
-
if (!existsSync2(dotTerraform)) {
|
|
234
|
-
const code = await runTerraform(cwd, ["init"]);
|
|
235
|
-
if (code !== 0) {
|
|
236
|
-
throw new Error("terraform init failed");
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// src/ui.ts
|
|
242
|
-
import chalk2 from "chalk";
|
|
243
|
-
import ora from "ora";
|
|
244
|
-
var TIER_LABELS = {
|
|
245
|
-
foundation: "Foundation",
|
|
246
|
-
data: "Data",
|
|
247
|
-
app: "App"
|
|
248
|
-
};
|
|
249
|
-
function printHeader(command, stage, identity) {
|
|
250
|
-
console.log("");
|
|
251
|
-
console.log(chalk2.bold.cyan(" \u2B21 Thinkwork") + chalk2.dim(` \u2014 ${command}`));
|
|
252
|
-
console.log(chalk2.dim(` Stage: ${chalk2.white(stage)}`));
|
|
253
|
-
if (identity) {
|
|
254
|
-
console.log(chalk2.dim(` AWS: ${chalk2.white(identity.account)} / ${chalk2.white(identity.region)}`));
|
|
255
|
-
}
|
|
256
|
-
console.log("");
|
|
257
|
-
}
|
|
258
|
-
function printTierHeader(tier, index, total) {
|
|
259
|
-
const label = TIER_LABELS[tier] ?? tier;
|
|
260
|
-
const progress = chalk2.dim(`[${index + 1}/${total}]`);
|
|
261
|
-
console.log(` ${progress} ${chalk2.bold(label)}`);
|
|
262
|
-
}
|
|
263
|
-
function printSuccess(message) {
|
|
264
|
-
console.log(`
|
|
265
|
-
${chalk2.green("\u2713")} ${chalk2.bold(message)}`);
|
|
266
|
-
}
|
|
267
|
-
function printError(message) {
|
|
268
|
-
console.log(`
|
|
269
|
-
${chalk2.red("\u2717")} ${chalk2.bold.red(message)}`);
|
|
270
|
-
}
|
|
271
|
-
function printMissingApiSessionError(stage, hasSession) {
|
|
272
|
-
if (!hasSession) {
|
|
273
|
-
printError(`No API session for stage "${stage}".`);
|
|
274
|
-
console.log("");
|
|
275
|
-
console.log(` ${chalk2.bold("To fix:")} thinkwork login --stage ${stage}`);
|
|
276
|
-
console.log(
|
|
277
|
-
chalk2.dim(
|
|
278
|
-
` (the deploy-side \`thinkwork login\` only configures an AWS profile \u2014
|
|
279
|
-
it does NOT open an API session.)`
|
|
280
|
-
)
|
|
281
|
-
);
|
|
282
|
-
console.log("");
|
|
283
|
-
} else {
|
|
284
|
-
printError(`Session for stage "${stage}" has no tenant cached.`);
|
|
285
|
-
console.log("");
|
|
286
|
-
console.log(` ${chalk2.bold("To fix:")} thinkwork login --stage ${stage}`);
|
|
287
|
-
console.log(
|
|
288
|
-
chalk2.dim(
|
|
289
|
-
` Or pass --tenant <slug>, or set THINKWORK_TENANT.`
|
|
290
|
-
)
|
|
291
|
-
);
|
|
292
|
-
console.log("");
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
function printWarning(message) {
|
|
296
|
-
console.log(` ${chalk2.yellow("\u26A0")} ${message}`);
|
|
297
|
-
}
|
|
298
|
-
function printSummary(command, stage, tiers, startTime) {
|
|
299
|
-
const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
|
|
300
|
-
console.log("");
|
|
301
|
-
console.log(chalk2.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"));
|
|
302
|
-
console.log(` ${chalk2.bold("Command:")} ${command}`);
|
|
303
|
-
console.log(` ${chalk2.bold("Stage:")} ${stage}`);
|
|
304
|
-
console.log(` ${chalk2.bold("Tiers:")} ${tiers.map((t) => TIER_LABELS[t] ?? t).join(" \u2192 ")}`);
|
|
305
|
-
console.log(` ${chalk2.bold("Time:")} ${elapsed}s`);
|
|
306
|
-
console.log(chalk2.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"));
|
|
307
|
-
}
|
|
308
|
-
|
|
309
190
|
// src/lib/resolve-stage.ts
|
|
310
191
|
import { select } from "@inquirer/prompts";
|
|
311
192
|
|
|
312
|
-
// src/aws-discovery.ts
|
|
313
|
-
import { execSync as execSync2 } from "child_process";
|
|
314
|
-
function runAws(cmd) {
|
|
315
|
-
try {
|
|
316
|
-
return execSync2(`aws ${cmd}`, {
|
|
317
|
-
encoding: "utf-8",
|
|
318
|
-
timeout: 15e3,
|
|
319
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
320
|
-
}).trim();
|
|
321
|
-
} catch {
|
|
322
|
-
return null;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
function listDeployedStages(region) {
|
|
326
|
-
const raw = runAws(
|
|
327
|
-
`lambda list-functions --region ${region} --query "Functions[?starts_with(FunctionName, 'thinkwork-')].FunctionName" --output json`
|
|
328
|
-
);
|
|
329
|
-
if (!raw) return [];
|
|
330
|
-
try {
|
|
331
|
-
const functions = JSON.parse(raw);
|
|
332
|
-
const stages = /* @__PURE__ */ new Set();
|
|
333
|
-
for (const fn of functions) {
|
|
334
|
-
const m = fn.match(/^thinkwork-(.+?)-api-graphql-http$/);
|
|
335
|
-
if (m) stages.add(m[1]);
|
|
336
|
-
}
|
|
337
|
-
return [...stages].sort();
|
|
338
|
-
} catch {
|
|
339
|
-
return [];
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
function getApiEndpoint(stage, region) {
|
|
343
|
-
const raw = runAws(
|
|
344
|
-
`apigatewayv2 get-apis --region ${region} --query "Items[?Name=='thinkwork-${stage}-api'].ApiEndpoint|[0]" --output text`
|
|
345
|
-
);
|
|
346
|
-
return raw && raw !== "None" ? raw : null;
|
|
347
|
-
}
|
|
348
|
-
function getApiAuthSecretFromLambda(stage, region) {
|
|
349
|
-
const raw = runAws(
|
|
350
|
-
`lambda get-function-configuration --function-name thinkwork-${stage}-api-tenants --region ${region} --query "Environment.Variables.API_AUTH_SECRET" --output text`
|
|
351
|
-
);
|
|
352
|
-
return raw && raw !== "None" ? raw : null;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
193
|
// src/lib/interactive.ts
|
|
356
194
|
function isCancellation(err) {
|
|
357
195
|
return err instanceof Error && err.name === "ExitPromptError";
|
|
@@ -457,8 +295,8 @@ function registerPlanCommand(program2) {
|
|
|
457
295
|
}
|
|
458
296
|
|
|
459
297
|
// src/commands/deploy.ts
|
|
460
|
-
import { spawn
|
|
461
|
-
import { existsSync as
|
|
298
|
+
import { spawn } from "child_process";
|
|
299
|
+
import { existsSync as existsSync2 } from "fs";
|
|
462
300
|
import { dirname as dirname2, resolve as pathResolve } from "path";
|
|
463
301
|
import { fileURLToPath } from "url";
|
|
464
302
|
|
|
@@ -554,7 +392,7 @@ async function runPostDeployProbe(stage) {
|
|
|
554
392
|
return;
|
|
555
393
|
}
|
|
556
394
|
await new Promise((resolve7) => {
|
|
557
|
-
const proc =
|
|
395
|
+
const proc = spawn("bash", [scriptPath, "--stage", stage], {
|
|
558
396
|
stdio: "inherit",
|
|
559
397
|
env: process.env
|
|
560
398
|
});
|
|
@@ -584,7 +422,7 @@ function locatePostDeployScript() {
|
|
|
584
422
|
)
|
|
585
423
|
];
|
|
586
424
|
for (const candidate of candidates) {
|
|
587
|
-
if (
|
|
425
|
+
if (existsSync2(candidate)) return candidate;
|
|
588
426
|
}
|
|
589
427
|
return null;
|
|
590
428
|
}
|
|
@@ -650,14 +488,14 @@ function registerDestroyCommand(program2) {
|
|
|
650
488
|
}
|
|
651
489
|
|
|
652
490
|
// src/commands/doctor.ts
|
|
653
|
-
import
|
|
654
|
-
import { execSync as
|
|
491
|
+
import chalk2 from "chalk";
|
|
492
|
+
import { execSync as execSync2 } from "child_process";
|
|
655
493
|
function checkAwsCli() {
|
|
656
494
|
return {
|
|
657
495
|
name: "AWS CLI installed",
|
|
658
496
|
run: () => {
|
|
659
497
|
try {
|
|
660
|
-
const v =
|
|
498
|
+
const v = execSync2("aws --version", { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
661
499
|
return { pass: true, detail: v.split(" ")[0] ?? v };
|
|
662
500
|
} catch {
|
|
663
501
|
return { pass: false, detail: "aws CLI not found. Install: https://aws.amazon.com/cli/" };
|
|
@@ -670,7 +508,7 @@ function checkTerraformCli() {
|
|
|
670
508
|
name: "Terraform CLI installed",
|
|
671
509
|
run: () => {
|
|
672
510
|
try {
|
|
673
|
-
const v =
|
|
511
|
+
const v = execSync2("terraform version -json", { encoding: "utf-8", timeout: 5e3, stdio: ["pipe", "pipe", "pipe"] });
|
|
674
512
|
const parsed = JSON.parse(v);
|
|
675
513
|
return { pass: true, detail: `v${parsed.terraform_version}` };
|
|
676
514
|
} catch {
|
|
@@ -696,7 +534,7 @@ function checkBedrockAccess() {
|
|
|
696
534
|
name: "Bedrock model access",
|
|
697
535
|
run: () => {
|
|
698
536
|
try {
|
|
699
|
-
|
|
537
|
+
execSync2(
|
|
700
538
|
"aws bedrock get-foundation-model --model-identifier anthropic.claude-3-haiku-20240307-v1:0 --output json --region us-east-1",
|
|
701
539
|
{ encoding: "utf-8", timeout: 1e4, stdio: ["pipe", "pipe", "pipe"] }
|
|
702
540
|
);
|
|
@@ -729,17 +567,17 @@ function registerDoctorCommand(program2) {
|
|
|
729
567
|
let allPass = true;
|
|
730
568
|
for (const check of checks) {
|
|
731
569
|
const result = check.run();
|
|
732
|
-
const icon = result.pass ?
|
|
733
|
-
const detail = result.pass ?
|
|
570
|
+
const icon = result.pass ? chalk2.green("\u2713") : chalk2.red("\u2717");
|
|
571
|
+
const detail = result.pass ? chalk2.dim(result.detail) : chalk2.yellow(result.detail);
|
|
734
572
|
console.log(` ${icon} ${check.name} ${detail}`);
|
|
735
573
|
if (!result.pass) allPass = false;
|
|
736
574
|
}
|
|
737
575
|
if (allPass) {
|
|
738
576
|
console.log(`
|
|
739
|
-
${
|
|
577
|
+
${chalk2.green.bold("All checks passed.")}`);
|
|
740
578
|
} else {
|
|
741
579
|
console.log(`
|
|
742
|
-
${
|
|
580
|
+
${chalk2.yellow.bold("Some checks failed.")} Fix the issues above before deploying.`);
|
|
743
581
|
}
|
|
744
582
|
process.exit(allPass ? 0 : 1);
|
|
745
583
|
});
|
|
@@ -778,75 +616,11 @@ function registerOutputsCommand(program2) {
|
|
|
778
616
|
}
|
|
779
617
|
|
|
780
618
|
// src/commands/config.ts
|
|
781
|
-
import { readFileSync as
|
|
782
|
-
import
|
|
783
|
-
|
|
784
|
-
// src/environments.ts
|
|
785
|
-
import {
|
|
786
|
-
existsSync as existsSync4,
|
|
787
|
-
mkdirSync as mkdirSync2,
|
|
788
|
-
writeFileSync as writeFileSync2,
|
|
789
|
-
readFileSync as readFileSync2,
|
|
790
|
-
readdirSync
|
|
791
|
-
} from "fs";
|
|
792
|
-
import { join as join2 } from "path";
|
|
793
|
-
import { homedir as homedir2 } from "os";
|
|
794
|
-
var THINKWORK_HOME = join2(homedir2(), ".thinkwork");
|
|
795
|
-
var ENVIRONMENTS_DIR = join2(THINKWORK_HOME, "environments");
|
|
796
|
-
var ENTERPRISE_DEPLOYMENTS_DIR = join2(
|
|
797
|
-
THINKWORK_HOME,
|
|
798
|
-
"enterprise-deployments"
|
|
799
|
-
);
|
|
800
|
-
function ensureDir(dir) {
|
|
801
|
-
if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
|
|
802
|
-
}
|
|
803
|
-
function saveEnvironment(config) {
|
|
804
|
-
ensureDir(ENVIRONMENTS_DIR);
|
|
805
|
-
const envDir = join2(ENVIRONMENTS_DIR, config.stage);
|
|
806
|
-
ensureDir(envDir);
|
|
807
|
-
writeFileSync2(
|
|
808
|
-
join2(envDir, "config.json"),
|
|
809
|
-
JSON.stringify(config, null, 2) + "\n"
|
|
810
|
-
);
|
|
811
|
-
}
|
|
812
|
-
function saveEnterpriseDeployment(config) {
|
|
813
|
-
ensureDir(ENTERPRISE_DEPLOYMENTS_DIR);
|
|
814
|
-
writeFileSync2(
|
|
815
|
-
join2(ENTERPRISE_DEPLOYMENTS_DIR, `${config.customerSlug}.json`),
|
|
816
|
-
JSON.stringify(config, null, 2) + "\n"
|
|
817
|
-
);
|
|
818
|
-
}
|
|
819
|
-
function loadEnvironment(stage) {
|
|
820
|
-
const configPath = join2(ENVIRONMENTS_DIR, stage, "config.json");
|
|
821
|
-
if (!existsSync4(configPath)) return null;
|
|
822
|
-
return JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
823
|
-
}
|
|
824
|
-
function listEnvironments() {
|
|
825
|
-
if (!existsSync4(ENVIRONMENTS_DIR)) return [];
|
|
826
|
-
return readdirSync(ENVIRONMENTS_DIR).filter((name) => {
|
|
827
|
-
return existsSync4(join2(ENVIRONMENTS_DIR, name, "config.json"));
|
|
828
|
-
}).map((name) => {
|
|
829
|
-
return JSON.parse(
|
|
830
|
-
readFileSync2(join2(ENVIRONMENTS_DIR, name, "config.json"), "utf-8")
|
|
831
|
-
);
|
|
832
|
-
}).sort((a, b) => a.stage.localeCompare(b.stage));
|
|
833
|
-
}
|
|
834
|
-
function resolveTerraformDir(stage) {
|
|
835
|
-
const env = loadEnvironment(stage);
|
|
836
|
-
if (env?.terraformDir && existsSync4(env.terraformDir)) {
|
|
837
|
-
return env.terraformDir;
|
|
838
|
-
}
|
|
839
|
-
const envVar = process.env.THINKWORK_TERRAFORM_DIR;
|
|
840
|
-
if (envVar && existsSync4(envVar)) return envVar;
|
|
841
|
-
const cwdTf = join2(process.cwd(), "terraform");
|
|
842
|
-
if (existsSync4(join2(cwdTf, "main.tf"))) return cwdTf;
|
|
843
|
-
return null;
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
// src/commands/config.ts
|
|
619
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync3 } from "fs";
|
|
620
|
+
import chalk3 from "chalk";
|
|
847
621
|
function readTfVar(tfvarsPath, key) {
|
|
848
|
-
if (!
|
|
849
|
-
const content =
|
|
622
|
+
if (!existsSync3(tfvarsPath)) return null;
|
|
623
|
+
const content = readFileSync2(tfvarsPath, "utf-8");
|
|
850
624
|
const quoted = content.match(new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, "m"));
|
|
851
625
|
if (quoted) return quoted[1];
|
|
852
626
|
const bare = content.match(new RegExp(`^${key}\\s*=\\s*([^\\s#]+)`, "m"));
|
|
@@ -854,10 +628,10 @@ function readTfVar(tfvarsPath, key) {
|
|
|
854
628
|
}
|
|
855
629
|
var BARE_KEYS = /* @__PURE__ */ new Set(["enable_hindsight"]);
|
|
856
630
|
function setTfVar(tfvarsPath, key, value) {
|
|
857
|
-
if (!
|
|
631
|
+
if (!existsSync3(tfvarsPath)) {
|
|
858
632
|
throw new Error(`terraform.tfvars not found at ${tfvarsPath}`);
|
|
859
633
|
}
|
|
860
|
-
let content =
|
|
634
|
+
let content = readFileSync2(tfvarsPath, "utf-8");
|
|
861
635
|
const bare = BARE_KEYS.has(key);
|
|
862
636
|
const newLine = bare ? `${key} = ${value}` : `${key} = "${value}"`;
|
|
863
637
|
const existingRegex = new RegExp(`^${key}\\s*=\\s*(?:"[^"]*"|[^\\s#]+)`, "m");
|
|
@@ -868,13 +642,13 @@ function setTfVar(tfvarsPath, key, value) {
|
|
|
868
642
|
${newLine}
|
|
869
643
|
`;
|
|
870
644
|
}
|
|
871
|
-
|
|
645
|
+
writeFileSync2(tfvarsPath, content);
|
|
872
646
|
}
|
|
873
647
|
function resolveTfvarsPath(stage) {
|
|
874
648
|
const tfDir = resolveTerraformDir(stage);
|
|
875
649
|
if (tfDir) {
|
|
876
650
|
const direct = `${tfDir}/terraform.tfvars`;
|
|
877
|
-
if (
|
|
651
|
+
if (existsSync3(direct)) return direct;
|
|
878
652
|
}
|
|
879
653
|
const terraformDir = process.env.THINKWORK_TERRAFORM_DIR || process.cwd();
|
|
880
654
|
const cwd = resolveTierDir(terraformDir, stage, "app");
|
|
@@ -890,21 +664,21 @@ function registerConfigCommand(program2) {
|
|
|
890
664
|
process.exit(1);
|
|
891
665
|
}
|
|
892
666
|
console.log("");
|
|
893
|
-
console.log(
|
|
894
|
-
console.log(
|
|
895
|
-
console.log(` ${
|
|
896
|
-
console.log(` ${
|
|
897
|
-
console.log(` ${
|
|
898
|
-
console.log(` ${
|
|
899
|
-
console.log(` ${
|
|
900
|
-
console.log(` ${
|
|
901
|
-
console.log(` ${
|
|
902
|
-
console.log(
|
|
667
|
+
console.log(chalk3.bold.cyan(` \u2B21 ${env.stage}`));
|
|
668
|
+
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"));
|
|
669
|
+
console.log(` ${chalk3.bold("Region:")} ${env.region}`);
|
|
670
|
+
console.log(` ${chalk3.bold("Account:")} ${env.accountId}`);
|
|
671
|
+
console.log(` ${chalk3.bold("Database:")} ${env.databaseEngine}`);
|
|
672
|
+
console.log(` ${chalk3.bold("Memory:")} managed (always on)${env.enableHindsight ? " + hindsight" : ""}`);
|
|
673
|
+
console.log(` ${chalk3.bold("Terraform dir:")} ${env.terraformDir}`);
|
|
674
|
+
console.log(` ${chalk3.bold("Created:")} ${env.createdAt}`);
|
|
675
|
+
console.log(` ${chalk3.bold("Updated:")} ${env.updatedAt}`);
|
|
676
|
+
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"));
|
|
903
677
|
const tfvarsPath = `${env.terraformDir}/terraform.tfvars`;
|
|
904
|
-
if (
|
|
678
|
+
if (existsSync3(tfvarsPath)) {
|
|
905
679
|
console.log("");
|
|
906
|
-
console.log(
|
|
907
|
-
const content =
|
|
680
|
+
console.log(chalk3.dim(" terraform.tfvars:"));
|
|
681
|
+
const content = readFileSync2(tfvarsPath, "utf-8");
|
|
908
682
|
for (const line of content.split("\n")) {
|
|
909
683
|
if (line.trim() && !line.trim().startsWith("#")) {
|
|
910
684
|
const masked = line.replace(
|
|
@@ -917,7 +691,7 @@ function registerConfigCommand(program2) {
|
|
|
917
691
|
/^(google_oauth_client_secret\s*=\s*)".*"/,
|
|
918
692
|
'$1"********"'
|
|
919
693
|
);
|
|
920
|
-
console.log(` ${
|
|
694
|
+
console.log(` ${chalk3.dim(masked)}`);
|
|
921
695
|
}
|
|
922
696
|
}
|
|
923
697
|
}
|
|
@@ -928,24 +702,24 @@ function registerConfigCommand(program2) {
|
|
|
928
702
|
if (envs.length === 0) {
|
|
929
703
|
console.log("");
|
|
930
704
|
console.log(" No environments found.");
|
|
931
|
-
console.log(` Run ${
|
|
705
|
+
console.log(` Run ${chalk3.cyan("thinkwork init -s <stage>")} to create one.`);
|
|
932
706
|
console.log("");
|
|
933
707
|
return;
|
|
934
708
|
}
|
|
935
709
|
console.log("");
|
|
936
|
-
console.log(
|
|
937
|
-
console.log(
|
|
710
|
+
console.log(chalk3.bold(" Environments"));
|
|
711
|
+
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"));
|
|
938
712
|
for (const env of envs) {
|
|
939
|
-
const memBadge = env.enableHindsight ?
|
|
940
|
-
const dbBadge = env.databaseEngine === "rds-postgres" ?
|
|
713
|
+
const memBadge = env.enableHindsight ? chalk3.magenta("managed+hindsight") : chalk3.dim("managed");
|
|
714
|
+
const dbBadge = env.databaseEngine === "rds-postgres" ? chalk3.yellow("rds") : chalk3.dim("aurora");
|
|
941
715
|
console.log(
|
|
942
|
-
` ${
|
|
716
|
+
` ${chalk3.bold.cyan(env.stage.padEnd(16))}${env.region.padEnd(14)}${env.accountId.padEnd(16)}${dbBadge.padEnd(20)}${memBadge}`
|
|
943
717
|
);
|
|
944
718
|
}
|
|
945
|
-
console.log(
|
|
946
|
-
console.log(
|
|
719
|
+
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"));
|
|
720
|
+
console.log(chalk3.dim(` ${envs.length} environment(s)`));
|
|
947
721
|
console.log("");
|
|
948
|
-
console.log(` Show details: ${
|
|
722
|
+
console.log(` Show details: ${chalk3.cyan("thinkwork config list -s <stage>")}`);
|
|
949
723
|
console.log("");
|
|
950
724
|
});
|
|
951
725
|
config.command("get <key>").description("Get a configuration value (e.g. enable-hindsight). Prompts for stage in a TTY when omitted.").option("-s, --stage <name>", "Deployment stage").action(async (key, opts) => {
|
|
@@ -1022,11 +796,11 @@ function registerConfigCommand(program2) {
|
|
|
1022
796
|
}
|
|
1023
797
|
|
|
1024
798
|
// src/commands/bootstrap.ts
|
|
1025
|
-
import { spawn as
|
|
799
|
+
import { spawn as spawn2 } from "child_process";
|
|
1026
800
|
import { resolve } from "path";
|
|
1027
801
|
function getTerraformOutput(cwd, key) {
|
|
1028
802
|
return new Promise((resolve7, reject) => {
|
|
1029
|
-
const proc =
|
|
803
|
+
const proc = spawn2("terraform", ["output", "-raw", key], {
|
|
1030
804
|
cwd,
|
|
1031
805
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1032
806
|
});
|
|
@@ -1040,7 +814,7 @@ function getTerraformOutput(cwd, key) {
|
|
|
1040
814
|
}
|
|
1041
815
|
function runScript(scriptPath, args) {
|
|
1042
816
|
return new Promise((resolve7) => {
|
|
1043
|
-
const proc =
|
|
817
|
+
const proc = spawn2("bash", [scriptPath, ...args], {
|
|
1044
818
|
stdio: "inherit"
|
|
1045
819
|
});
|
|
1046
820
|
proc.on("close", (code) => resolve7(code ?? 1));
|
|
@@ -1068,8 +842,8 @@ function registerBootstrapCommand(program2) {
|
|
|
1068
842
|
bucket = await getTerraformOutput(cwd, "bucket_name");
|
|
1069
843
|
dbEndpoint = await getTerraformOutput(cwd, "db_cluster_endpoint");
|
|
1070
844
|
const secretArn = await getTerraformOutput(cwd, "db_secret_arn");
|
|
1071
|
-
const { execSync:
|
|
1072
|
-
const secretJson =
|
|
845
|
+
const { execSync: execSync9 } = await import("child_process");
|
|
846
|
+
const secretJson = execSync9(
|
|
1073
847
|
`aws secretsmanager get-secret-value --secret-id "${secretArn}" --query SecretString --output text`,
|
|
1074
848
|
{ encoding: "utf-8" }
|
|
1075
849
|
).trim();
|
|
@@ -1092,17 +866,17 @@ function registerBootstrapCommand(program2) {
|
|
|
1092
866
|
}
|
|
1093
867
|
|
|
1094
868
|
// src/commands/login.ts
|
|
1095
|
-
import { execSync as
|
|
869
|
+
import { execSync as execSync5 } from "child_process";
|
|
1096
870
|
import { createInterface as createInterface2 } from "readline";
|
|
1097
871
|
import { select as select2, Separator } from "@inquirer/prompts";
|
|
1098
|
-
import
|
|
872
|
+
import chalk6 from "chalk";
|
|
1099
873
|
|
|
1100
874
|
// src/aws-profiles.ts
|
|
1101
|
-
import { existsSync as
|
|
1102
|
-
import { homedir as
|
|
1103
|
-
import { join as
|
|
1104
|
-
var CREDENTIALS_PATH =
|
|
1105
|
-
var CONFIG_PATH =
|
|
875
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
876
|
+
import { homedir as homedir2 } from "os";
|
|
877
|
+
import { join as join2 } from "path";
|
|
878
|
+
var CREDENTIALS_PATH = join2(homedir2(), ".aws", "credentials");
|
|
879
|
+
var CONFIG_PATH = join2(homedir2(), ".aws", "config");
|
|
1106
880
|
function parseIni(content) {
|
|
1107
881
|
const sections = {};
|
|
1108
882
|
let current = null;
|
|
@@ -1141,8 +915,8 @@ function classify(fields) {
|
|
|
1141
915
|
}
|
|
1142
916
|
function listAwsProfiles() {
|
|
1143
917
|
const byName = /* @__PURE__ */ new Map();
|
|
1144
|
-
if (
|
|
1145
|
-
const sections = parseIni(
|
|
918
|
+
if (existsSync4(CREDENTIALS_PATH)) {
|
|
919
|
+
const sections = parseIni(readFileSync3(CREDENTIALS_PATH, "utf-8"));
|
|
1146
920
|
for (const [section, fields] of Object.entries(sections)) {
|
|
1147
921
|
byName.set(section, {
|
|
1148
922
|
name: section,
|
|
@@ -1151,8 +925,8 @@ function listAwsProfiles() {
|
|
|
1151
925
|
});
|
|
1152
926
|
}
|
|
1153
927
|
}
|
|
1154
|
-
if (
|
|
1155
|
-
const sections = parseIni(
|
|
928
|
+
if (existsSync4(CONFIG_PATH)) {
|
|
929
|
+
const sections = parseIni(readFileSync3(CONFIG_PATH, "utf-8"));
|
|
1156
930
|
for (const [section, fields] of Object.entries(sections)) {
|
|
1157
931
|
const name = normalizeConfigSection(section);
|
|
1158
932
|
if (!name) continue;
|
|
@@ -1174,14 +948,14 @@ function listAwsProfiles() {
|
|
|
1174
948
|
}
|
|
1175
949
|
|
|
1176
950
|
// src/prerequisites.ts
|
|
1177
|
-
import { execSync as
|
|
1178
|
-
import { mkdirSync as
|
|
1179
|
-
import { join as
|
|
1180
|
-
import { homedir as
|
|
1181
|
-
import
|
|
951
|
+
import { execSync as execSync3 } from "child_process";
|
|
952
|
+
import { mkdirSync as mkdirSync2, createWriteStream, chmodSync } from "fs";
|
|
953
|
+
import { join as join3 } from "path";
|
|
954
|
+
import { homedir as homedir3, platform, arch } from "os";
|
|
955
|
+
import chalk4 from "chalk";
|
|
1182
956
|
function run(cmd, opts) {
|
|
1183
957
|
try {
|
|
1184
|
-
return
|
|
958
|
+
return execSync3(cmd, {
|
|
1185
959
|
encoding: "utf-8",
|
|
1186
960
|
timeout: 3e4,
|
|
1187
961
|
stdio: opts?.silent ? ["pipe", "pipe", "pipe"] : void 0
|
|
@@ -1198,27 +972,27 @@ function hasBrew() {
|
|
|
1198
972
|
}
|
|
1199
973
|
async function ensureAwsCli() {
|
|
1200
974
|
if (isInstalled("aws")) return true;
|
|
1201
|
-
console.log(` ${
|
|
975
|
+
console.log(` ${chalk4.yellow("\u2192")} AWS CLI not found. Installing...`);
|
|
1202
976
|
const os = platform();
|
|
1203
977
|
if (os === "darwin" && hasBrew()) {
|
|
1204
978
|
const result = run("brew install awscli");
|
|
1205
979
|
if (result !== null && isInstalled("aws")) {
|
|
1206
|
-
console.log(` ${
|
|
980
|
+
console.log(` ${chalk4.green("\u2713")} AWS CLI installed via Homebrew`);
|
|
1207
981
|
return true;
|
|
1208
982
|
}
|
|
1209
983
|
}
|
|
1210
984
|
if (os === "linux") {
|
|
1211
985
|
try {
|
|
1212
|
-
const tmpDir =
|
|
1213
|
-
|
|
1214
|
-
const zipPath =
|
|
986
|
+
const tmpDir = join3(homedir3(), ".thinkwork", "tmp");
|
|
987
|
+
mkdirSync2(tmpDir, { recursive: true });
|
|
988
|
+
const zipPath = join3(tmpDir, "awscliv2.zip");
|
|
1215
989
|
console.log(" Downloading AWS CLI...");
|
|
1216
990
|
run(`curl -sL "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "${zipPath}"`);
|
|
1217
991
|
run(`cd "${tmpDir}" && unzip -qo "${zipPath}"`);
|
|
1218
|
-
run(`"${tmpDir}/aws/install" --install-dir "${
|
|
1219
|
-
process.env.PATH = `${
|
|
992
|
+
run(`"${tmpDir}/aws/install" --install-dir "${homedir3()}/.thinkwork/aws-cli" --bin-dir "${homedir3()}/.local/bin" --update`);
|
|
993
|
+
process.env.PATH = `${homedir3()}/.local/bin:${process.env.PATH}`;
|
|
1220
994
|
if (isInstalled("aws")) {
|
|
1221
|
-
console.log(` ${
|
|
995
|
+
console.log(` ${chalk4.green("\u2713")} AWS CLI installed to ~/.local/bin/aws`);
|
|
1222
996
|
return true;
|
|
1223
997
|
}
|
|
1224
998
|
} catch {
|
|
@@ -1226,31 +1000,31 @@ async function ensureAwsCli() {
|
|
|
1226
1000
|
}
|
|
1227
1001
|
if (os === "darwin") {
|
|
1228
1002
|
try {
|
|
1229
|
-
const tmpDir =
|
|
1230
|
-
|
|
1231
|
-
const pkgPath =
|
|
1003
|
+
const tmpDir = join3(homedir3(), ".thinkwork", "tmp");
|
|
1004
|
+
mkdirSync2(tmpDir, { recursive: true });
|
|
1005
|
+
const pkgPath = join3(tmpDir, "AWSCLIV2.pkg");
|
|
1232
1006
|
console.log(" Downloading AWS CLI...");
|
|
1233
1007
|
run(`curl -sL "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "${pkgPath}"`);
|
|
1234
1008
|
run(`installer -pkg "${pkgPath}" -target CurrentUserHomeDirectory 2>/dev/null || sudo installer -pkg "${pkgPath}" -target /`);
|
|
1235
1009
|
if (isInstalled("aws")) {
|
|
1236
|
-
console.log(` ${
|
|
1010
|
+
console.log(` ${chalk4.green("\u2713")} AWS CLI installed`);
|
|
1237
1011
|
return true;
|
|
1238
1012
|
}
|
|
1239
1013
|
} catch {
|
|
1240
1014
|
}
|
|
1241
1015
|
}
|
|
1242
|
-
console.log(` ${
|
|
1243
|
-
console.log(` Install manually: ${
|
|
1016
|
+
console.log(` ${chalk4.red("\u2717")} Could not auto-install AWS CLI.`);
|
|
1017
|
+
console.log(` Install manually: ${chalk4.cyan("https://aws.amazon.com/cli/")}`);
|
|
1244
1018
|
return false;
|
|
1245
1019
|
}
|
|
1246
1020
|
async function ensureTerraform() {
|
|
1247
1021
|
if (isInstalled("terraform")) return true;
|
|
1248
|
-
console.log(` ${
|
|
1022
|
+
console.log(` ${chalk4.yellow("\u2192")} Terraform not found. Installing...`);
|
|
1249
1023
|
const os = platform();
|
|
1250
1024
|
if ((os === "darwin" || os === "linux") && hasBrew()) {
|
|
1251
1025
|
const result = run("brew install hashicorp/tap/terraform");
|
|
1252
1026
|
if (result !== null && isInstalled("terraform")) {
|
|
1253
|
-
console.log(` ${
|
|
1027
|
+
console.log(` ${chalk4.green("\u2713")} Terraform installed via Homebrew`);
|
|
1254
1028
|
return true;
|
|
1255
1029
|
}
|
|
1256
1030
|
}
|
|
@@ -1259,30 +1033,30 @@ async function ensureTerraform() {
|
|
|
1259
1033
|
const archName = arch() === "arm64" ? "arm64" : "amd64";
|
|
1260
1034
|
const url = `https://releases.hashicorp.com/terraform/${tfVersion}/terraform_${tfVersion}_${osName}_${archName}.zip`;
|
|
1261
1035
|
try {
|
|
1262
|
-
const tmpDir =
|
|
1263
|
-
const binDir =
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
const zipPath =
|
|
1036
|
+
const tmpDir = join3(homedir3(), ".thinkwork", "tmp");
|
|
1037
|
+
const binDir = join3(homedir3(), ".local", "bin");
|
|
1038
|
+
mkdirSync2(tmpDir, { recursive: true });
|
|
1039
|
+
mkdirSync2(binDir, { recursive: true });
|
|
1040
|
+
const zipPath = join3(tmpDir, "terraform.zip");
|
|
1267
1041
|
console.log(` Downloading Terraform ${tfVersion}...`);
|
|
1268
1042
|
run(`curl -sL "${url}" -o "${zipPath}"`);
|
|
1269
1043
|
run(`unzip -qo "${zipPath}" -d "${binDir}"`);
|
|
1270
|
-
chmodSync(
|
|
1044
|
+
chmodSync(join3(binDir, "terraform"), 493);
|
|
1271
1045
|
if (!process.env.PATH?.includes(binDir)) {
|
|
1272
1046
|
process.env.PATH = `${binDir}:${process.env.PATH}`;
|
|
1273
1047
|
}
|
|
1274
1048
|
if (isInstalled("terraform")) {
|
|
1275
|
-
console.log(` ${
|
|
1049
|
+
console.log(` ${chalk4.green("\u2713")} Terraform ${tfVersion} installed to ~/.local/bin/terraform`);
|
|
1276
1050
|
return true;
|
|
1277
1051
|
}
|
|
1278
1052
|
} catch {
|
|
1279
1053
|
}
|
|
1280
|
-
console.log(` ${
|
|
1281
|
-
console.log(` Install manually: ${
|
|
1054
|
+
console.log(` ${chalk4.red("\u2717")} Could not auto-install Terraform.`);
|
|
1055
|
+
console.log(` Install manually: ${chalk4.cyan("https://developer.hashicorp.com/terraform/install")}`);
|
|
1282
1056
|
return false;
|
|
1283
1057
|
}
|
|
1284
1058
|
async function ensurePrerequisites() {
|
|
1285
|
-
console.log(
|
|
1059
|
+
console.log(chalk4.dim(" Checking prerequisites...\n"));
|
|
1286
1060
|
const awsOk2 = await ensureAwsCli();
|
|
1287
1061
|
const tfOk = await ensureTerraform();
|
|
1288
1062
|
if (awsOk2 && tfOk) {
|
|
@@ -1290,15 +1064,15 @@ async function ensurePrerequisites() {
|
|
|
1290
1064
|
return true;
|
|
1291
1065
|
}
|
|
1292
1066
|
console.log("");
|
|
1293
|
-
console.log(` ${
|
|
1067
|
+
console.log(` ${chalk4.red("Missing prerequisites.")} Install them and try again.`);
|
|
1294
1068
|
return false;
|
|
1295
1069
|
}
|
|
1296
1070
|
|
|
1297
1071
|
// src/cognito-discovery.ts
|
|
1298
|
-
import { execSync as
|
|
1299
|
-
function
|
|
1072
|
+
import { execSync as execSync4 } from "child_process";
|
|
1073
|
+
function runAws(cmd) {
|
|
1300
1074
|
try {
|
|
1301
|
-
return
|
|
1075
|
+
return execSync4(`aws ${cmd}`, {
|
|
1302
1076
|
encoding: "utf-8",
|
|
1303
1077
|
timeout: 15e3,
|
|
1304
1078
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -1318,7 +1092,7 @@ function tryTerraformOutput(stage) {
|
|
|
1318
1092
|
}
|
|
1319
1093
|
const read = (key) => {
|
|
1320
1094
|
try {
|
|
1321
|
-
return
|
|
1095
|
+
return execSync4(`terraform output -raw ${key}`, {
|
|
1322
1096
|
cwd,
|
|
1323
1097
|
encoding: "utf-8",
|
|
1324
1098
|
timeout: 15e3,
|
|
@@ -1334,7 +1108,7 @@ function tryTerraformOutput(stage) {
|
|
|
1334
1108
|
return { userPoolId, clientId, domain };
|
|
1335
1109
|
}
|
|
1336
1110
|
function tryAwsDiscovery(stage, region) {
|
|
1337
|
-
const listRaw =
|
|
1111
|
+
const listRaw = runAws(
|
|
1338
1112
|
`cognito-idp list-user-pools --max-results 60 --region ${region} --output json`
|
|
1339
1113
|
);
|
|
1340
1114
|
if (!listRaw) return {};
|
|
@@ -1346,7 +1120,7 @@ function tryAwsDiscovery(stage, region) {
|
|
|
1346
1120
|
)
|
|
1347
1121
|
);
|
|
1348
1122
|
if (!pool) return {};
|
|
1349
|
-
const clientsRaw =
|
|
1123
|
+
const clientsRaw = runAws(
|
|
1350
1124
|
`cognito-idp list-user-pool-clients --user-pool-id ${pool.Id} --region ${region} --output json`
|
|
1351
1125
|
);
|
|
1352
1126
|
let clientId;
|
|
@@ -1379,8 +1153,8 @@ function discoverCognitoConfig(stage, region) {
|
|
|
1379
1153
|
// src/cognito-oauth.ts
|
|
1380
1154
|
import { createServer } from "http";
|
|
1381
1155
|
import { randomBytes } from "crypto";
|
|
1382
|
-
import { spawn as
|
|
1383
|
-
import
|
|
1156
|
+
import { spawn as spawn3 } from "child_process";
|
|
1157
|
+
import chalk5 from "chalk";
|
|
1384
1158
|
var CLI_LOOPBACK_PORT = 42010;
|
|
1385
1159
|
var CALLBACK_PATH = "/callback";
|
|
1386
1160
|
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
@@ -1396,9 +1170,9 @@ async function loginWithCognito(opts) {
|
|
|
1396
1170
|
timeoutMs,
|
|
1397
1171
|
onListening: () => {
|
|
1398
1172
|
logStderr("");
|
|
1399
|
-
logStderr(` ${
|
|
1400
|
-
logStderr(` ${
|
|
1401
|
-
logStderr(` ${
|
|
1173
|
+
logStderr(` ${chalk5.cyan("Opening browser to sign in\u2026")}`);
|
|
1174
|
+
logStderr(` ${chalk5.dim("If it doesn't open automatically, visit:")}`);
|
|
1175
|
+
logStderr(` ${chalk5.dim(authorizeUrl)}`);
|
|
1402
1176
|
logStderr("");
|
|
1403
1177
|
if (opts.openBrowser !== false) {
|
|
1404
1178
|
(opts.launchBrowser ?? openInBrowser)(authorizeUrl);
|
|
@@ -1558,7 +1332,7 @@ function openInBrowser(url) {
|
|
|
1558
1332
|
const cmd = platform2 === "darwin" ? "open" : platform2 === "win32" ? "cmd" : "xdg-open";
|
|
1559
1333
|
const args = platform2 === "win32" ? ["/c", "start", "", url] : [url];
|
|
1560
1334
|
try {
|
|
1561
|
-
|
|
1335
|
+
spawn3(cmd, args, { stdio: "ignore", detached: true }).unref();
|
|
1562
1336
|
} catch {
|
|
1563
1337
|
}
|
|
1564
1338
|
}
|
|
@@ -1629,7 +1403,7 @@ function ask(prompt) {
|
|
|
1629
1403
|
}
|
|
1630
1404
|
function verifyProfile(profile) {
|
|
1631
1405
|
try {
|
|
1632
|
-
const raw =
|
|
1406
|
+
const raw = execSync5(
|
|
1633
1407
|
`aws sts get-caller-identity --profile ${profile} --output json`,
|
|
1634
1408
|
{ encoding: "utf-8", timeout: 15e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
1635
1409
|
);
|
|
@@ -1659,7 +1433,7 @@ async function pickProfile(profiles) {
|
|
|
1659
1433
|
return { kind: "cancel" };
|
|
1660
1434
|
}
|
|
1661
1435
|
const choices = profiles.map((p) => ({
|
|
1662
|
-
name: `${p.name} ${
|
|
1436
|
+
name: `${p.name} ${chalk6.dim(`(${describeType(p.type)})`)}`,
|
|
1663
1437
|
value: { kind: "existing", name: p.name }
|
|
1664
1438
|
}));
|
|
1665
1439
|
choices.push(new Separator());
|
|
@@ -1706,15 +1480,15 @@ async function runKeyEntry(targetProfile) {
|
|
|
1706
1480
|
const region = await ask(" Default region [us-east-1]: ");
|
|
1707
1481
|
const finalRegion = region || "us-east-1";
|
|
1708
1482
|
try {
|
|
1709
|
-
|
|
1483
|
+
execSync5(
|
|
1710
1484
|
`aws configure set aws_access_key_id "${accessKeyId}" --profile ${targetProfile}`,
|
|
1711
1485
|
{ stdio: "pipe" }
|
|
1712
1486
|
);
|
|
1713
|
-
|
|
1487
|
+
execSync5(
|
|
1714
1488
|
`aws configure set aws_secret_access_key "${secretAccessKey}" --profile ${targetProfile}`,
|
|
1715
1489
|
{ stdio: "pipe" }
|
|
1716
1490
|
);
|
|
1717
|
-
|
|
1491
|
+
execSync5(
|
|
1718
1492
|
`aws configure set region "${finalRegion}" --profile ${targetProfile}`,
|
|
1719
1493
|
{ stdio: "pipe" }
|
|
1720
1494
|
);
|
|
@@ -1728,7 +1502,7 @@ function runSsoLogin(targetProfile) {
|
|
|
1728
1502
|
console.log(" Launching AWS SSO login...");
|
|
1729
1503
|
console.log("");
|
|
1730
1504
|
try {
|
|
1731
|
-
|
|
1505
|
+
execSync5(`aws sso login --profile ${targetProfile}`, { stdio: "inherit" });
|
|
1732
1506
|
return true;
|
|
1733
1507
|
} catch {
|
|
1734
1508
|
printError(
|
|
@@ -1757,7 +1531,7 @@ function finalizeAws(profile, mode) {
|
|
|
1757
1531
|
` (\`thinkwork list\`, \`thinkwork deploy\`, \u2026) will use it automatically.`
|
|
1758
1532
|
);
|
|
1759
1533
|
console.log(
|
|
1760
|
-
|
|
1534
|
+
chalk6.dim(
|
|
1761
1535
|
` Override per-command with --profile <other>, or unset with \`rm ~/.thinkwork/config.json\`.`
|
|
1762
1536
|
)
|
|
1763
1537
|
);
|
|
@@ -1771,10 +1545,10 @@ async function offerApiLoginChain(opts) {
|
|
|
1771
1545
|
if (candidates.length === 0 || !isInteractive()) {
|
|
1772
1546
|
console.log("");
|
|
1773
1547
|
console.log(
|
|
1774
|
-
` ${
|
|
1548
|
+
` ${chalk6.bold("Next:")} run ${chalk6.cyan("thinkwork login --stage <stage>")} if you also need`
|
|
1775
1549
|
);
|
|
1776
1550
|
console.log(
|
|
1777
|
-
` an API session (required for ${
|
|
1551
|
+
` an API session (required for ${chalk6.cyan("eval")}, ${chalk6.cyan("agent")}, ${chalk6.cyan("thread")}, etc.).`
|
|
1778
1552
|
);
|
|
1779
1553
|
return;
|
|
1780
1554
|
}
|
|
@@ -1808,8 +1582,8 @@ async function offerApiLoginChain(opts) {
|
|
|
1808
1582
|
if (!chosen) {
|
|
1809
1583
|
console.log("");
|
|
1810
1584
|
console.log(
|
|
1811
|
-
|
|
1812
|
-
` Skipped. Run ${
|
|
1585
|
+
chalk6.dim(
|
|
1586
|
+
` Skipped. Run ${chalk6.cyan("thinkwork login --stage <stage>")} later for an API session.`
|
|
1813
1587
|
)
|
|
1814
1588
|
);
|
|
1815
1589
|
return;
|
|
@@ -1906,7 +1680,7 @@ async function doCognitoLogin(opts) {
|
|
|
1906
1680
|
}
|
|
1907
1681
|
console.log("");
|
|
1908
1682
|
console.log(
|
|
1909
|
-
|
|
1683
|
+
chalk6.dim(
|
|
1910
1684
|
` Token expires: ${new Date(tokens.expiresAt * 1e3).toISOString()}. Refreshed automatically.`
|
|
1911
1685
|
)
|
|
1912
1686
|
);
|
|
@@ -2060,9 +1834,9 @@ Registered callback URL:
|
|
|
2060
1834
|
const profiles = listAwsProfiles();
|
|
2061
1835
|
if (profiles.length === 0) {
|
|
2062
1836
|
console.log("");
|
|
2063
|
-
console.log(
|
|
1837
|
+
console.log(chalk6.dim(" No AWS profiles found in ~/.aws/."));
|
|
2064
1838
|
console.log(
|
|
2065
|
-
|
|
1839
|
+
chalk6.dim(" Falling through to access-key entry for a new profile.")
|
|
2066
1840
|
);
|
|
2067
1841
|
if (!await runKeyEntry(targetProfile)) process.exit(1);
|
|
2068
1842
|
process.env.AWS_PROFILE = targetProfile;
|
|
@@ -2073,7 +1847,7 @@ Registered callback URL:
|
|
|
2073
1847
|
const choice = await pickProfile(profiles);
|
|
2074
1848
|
if (choice.kind === "cancel") {
|
|
2075
1849
|
console.log("");
|
|
2076
|
-
console.log(
|
|
1850
|
+
console.log(chalk6.dim(" Cancelled. No changes made."));
|
|
2077
1851
|
return;
|
|
2078
1852
|
}
|
|
2079
1853
|
if (choice.kind === "keys") {
|
|
@@ -2174,16 +1948,16 @@ Examples:
|
|
|
2174
1948
|
}
|
|
2175
1949
|
|
|
2176
1950
|
// src/commands/init.ts
|
|
2177
|
-
import { existsSync as
|
|
2178
|
-
import { resolve as resolve2, join as
|
|
2179
|
-
import { execSync as
|
|
1951
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, cpSync } from "fs";
|
|
1952
|
+
import { resolve as resolve2, join as join4, dirname as dirname3 } from "path";
|
|
1953
|
+
import { execSync as execSync6 } from "child_process";
|
|
2180
1954
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2181
1955
|
import { createInterface as createInterface3 } from "readline";
|
|
2182
|
-
import
|
|
1956
|
+
import chalk7 from "chalk";
|
|
2183
1957
|
var __dirname = dirname3(fileURLToPath2(import.meta.url));
|
|
2184
1958
|
function ask2(prompt, defaultVal = "") {
|
|
2185
1959
|
const rl = createInterface3({ input: process.stdin, output: process.stdout });
|
|
2186
|
-
const suffix = defaultVal ?
|
|
1960
|
+
const suffix = defaultVal ? chalk7.dim(` [${defaultVal}]`) : "";
|
|
2187
1961
|
return new Promise((resolve7) => {
|
|
2188
1962
|
rl.question(` ${prompt}${suffix}: `, (answer) => {
|
|
2189
1963
|
rl.close();
|
|
@@ -2192,7 +1966,7 @@ function ask2(prompt, defaultVal = "") {
|
|
|
2192
1966
|
});
|
|
2193
1967
|
}
|
|
2194
1968
|
function choose(prompt, options, defaultVal) {
|
|
2195
|
-
const optStr = options.map((o) => o === defaultVal ?
|
|
1969
|
+
const optStr = options.map((o) => o === defaultVal ? chalk7.bold(o) : chalk7.dim(o)).join(" / ");
|
|
2196
1970
|
return ask2(`${prompt} (${optStr})`, defaultVal);
|
|
2197
1971
|
}
|
|
2198
1972
|
function generateSecret(length = 32) {
|
|
@@ -2205,9 +1979,9 @@ function generateSecret(length = 32) {
|
|
|
2205
1979
|
}
|
|
2206
1980
|
function findBundledTerraform() {
|
|
2207
1981
|
const bundled = resolve2(__dirname, "terraform");
|
|
2208
|
-
if (
|
|
1982
|
+
if (existsSync6(join4(bundled, "modules"))) return bundled;
|
|
2209
1983
|
const repoTf = resolve2(__dirname, "..", "..", "..", "terraform");
|
|
2210
|
-
if (
|
|
1984
|
+
if (existsSync6(join4(repoTf, "modules"))) return repoTf;
|
|
2211
1985
|
throw new Error(
|
|
2212
1986
|
"Terraform modules not found. The CLI package may be incomplete.\nTry reinstalling: npm install -g thinkwork-cli@latest"
|
|
2213
1987
|
);
|
|
@@ -2297,9 +2071,9 @@ function registerInitCommand(program2) {
|
|
|
2297
2071
|
process.exit(1);
|
|
2298
2072
|
}
|
|
2299
2073
|
const targetDir = resolve2(opts.dir);
|
|
2300
|
-
const tfDir =
|
|
2301
|
-
const tfvarsPath =
|
|
2302
|
-
if (
|
|
2074
|
+
const tfDir = join4(targetDir, "terraform");
|
|
2075
|
+
const tfvarsPath = join4(tfDir, "terraform.tfvars");
|
|
2076
|
+
if (existsSync6(tfvarsPath)) {
|
|
2303
2077
|
printWarning(`terraform.tfvars already exists at ${tfvarsPath}`);
|
|
2304
2078
|
const overwrite = await ask2("Overwrite?", "N");
|
|
2305
2079
|
if (overwrite.toLowerCase() !== "y") {
|
|
@@ -2323,21 +2097,21 @@ function registerInitCommand(program2) {
|
|
|
2323
2097
|
config.admin_url = "http://localhost:5174";
|
|
2324
2098
|
config.mobile_scheme = "thinkwork";
|
|
2325
2099
|
} else {
|
|
2326
|
-
console.log(
|
|
2100
|
+
console.log(chalk7.bold(" Configure your Thinkwork environment\n"));
|
|
2327
2101
|
const defaultRegion = identity.region !== "unknown" ? identity.region : "us-east-1";
|
|
2328
2102
|
config.region = await ask2("AWS Region", defaultRegion);
|
|
2329
2103
|
console.log("");
|
|
2330
|
-
console.log(
|
|
2104
|
+
console.log(chalk7.dim(" \u2500\u2500 Database \u2500\u2500"));
|
|
2331
2105
|
config.database_engine = await choose("Database engine", ["aurora-serverless", "rds-postgres"], "aurora-serverless");
|
|
2332
2106
|
console.log("");
|
|
2333
|
-
console.log(
|
|
2334
|
-
console.log(
|
|
2335
|
-
console.log(
|
|
2336
|
-
console.log(
|
|
2107
|
+
console.log(chalk7.dim(" \u2500\u2500 Memory \u2500\u2500"));
|
|
2108
|
+
console.log(chalk7.dim(" AgentCore managed memory is always on (automatic retention)."));
|
|
2109
|
+
console.log(chalk7.dim(" Hindsight is an optional add-on: ECS Fargate service for"));
|
|
2110
|
+
console.log(chalk7.dim(" semantic + entity-graph retrieval (~$75/mo)."));
|
|
2337
2111
|
const hindsightAnswer = await ask2("Enable Hindsight long-term memory add-on? (y/N)", "N");
|
|
2338
2112
|
config.enable_hindsight = hindsightAnswer.toLowerCase() === "y" ? "true" : "false";
|
|
2339
2113
|
console.log("");
|
|
2340
|
-
console.log(
|
|
2114
|
+
console.log(chalk7.dim(" \u2500\u2500 Auth \u2500\u2500"));
|
|
2341
2115
|
const useGoogle = await ask2("Enable Google OAuth login? (y/N)", "N");
|
|
2342
2116
|
if (useGoogle.toLowerCase() === "y") {
|
|
2343
2117
|
config.google_oauth_client_id = await ask2("Google OAuth Client ID");
|
|
@@ -2347,13 +2121,13 @@ function registerInitCommand(program2) {
|
|
|
2347
2121
|
config.google_oauth_client_secret = "";
|
|
2348
2122
|
}
|
|
2349
2123
|
console.log("");
|
|
2350
|
-
console.log(
|
|
2124
|
+
console.log(chalk7.dim(" \u2500\u2500 Frontend URLs \u2500\u2500"));
|
|
2351
2125
|
config.admin_url = await ask2("Admin UI URL", "http://localhost:5174");
|
|
2352
2126
|
config.mobile_scheme = await ask2("Mobile app URL scheme", "thinkwork");
|
|
2353
2127
|
console.log("");
|
|
2354
|
-
console.log(
|
|
2355
|
-
console.log(
|
|
2356
|
-
console.log(
|
|
2128
|
+
console.log(chalk7.dim(" \u2500\u2500 Secrets (auto-generated) \u2500\u2500"));
|
|
2129
|
+
console.log(chalk7.dim(` DB password: ${config.db_password.slice(0, 8)}...`));
|
|
2130
|
+
console.log(chalk7.dim(` API auth secret: ${config.api_auth_secret.slice(0, 16)}...`));
|
|
2357
2131
|
}
|
|
2358
2132
|
console.log("");
|
|
2359
2133
|
console.log(" Scaffolding Terraform modules...");
|
|
@@ -2364,24 +2138,24 @@ function registerInitCommand(program2) {
|
|
|
2364
2138
|
printError(String(err));
|
|
2365
2139
|
process.exit(1);
|
|
2366
2140
|
}
|
|
2367
|
-
|
|
2141
|
+
mkdirSync3(tfDir, { recursive: true });
|
|
2368
2142
|
const copyDirs = ["modules", "examples"];
|
|
2369
2143
|
for (const dir of copyDirs) {
|
|
2370
|
-
const src =
|
|
2371
|
-
const dst =
|
|
2372
|
-
if (
|
|
2144
|
+
const src = join4(bundledTf, dir);
|
|
2145
|
+
const dst = join4(tfDir, dir);
|
|
2146
|
+
if (existsSync6(src) && !existsSync6(dst)) {
|
|
2373
2147
|
cpSync(src, dst, { recursive: true });
|
|
2374
2148
|
}
|
|
2375
2149
|
}
|
|
2376
|
-
const schemaPath =
|
|
2377
|
-
if (
|
|
2378
|
-
cpSync(schemaPath,
|
|
2150
|
+
const schemaPath = join4(bundledTf, "schema.graphql");
|
|
2151
|
+
if (existsSync6(schemaPath) && !existsSync6(join4(tfDir, "schema.graphql"))) {
|
|
2152
|
+
cpSync(schemaPath, join4(tfDir, "schema.graphql"));
|
|
2379
2153
|
}
|
|
2380
2154
|
const tfvars = buildTfvars(config);
|
|
2381
|
-
|
|
2382
|
-
const mainTfPath =
|
|
2383
|
-
if (!
|
|
2384
|
-
|
|
2155
|
+
writeFileSync3(tfvarsPath, tfvars);
|
|
2156
|
+
const mainTfPath = join4(tfDir, "main.tf");
|
|
2157
|
+
if (!existsSync6(mainTfPath)) {
|
|
2158
|
+
writeFileSync3(mainTfPath, `################################################################################
|
|
2385
2159
|
# Thinkwork \u2014 ${config.stage}
|
|
2386
2160
|
# Generated by: thinkwork init -s ${config.stage}
|
|
2387
2161
|
################################################################################
|
|
@@ -2558,20 +2332,20 @@ output "agentcore_memory_id" {
|
|
|
2558
2332
|
}
|
|
2559
2333
|
`);
|
|
2560
2334
|
}
|
|
2561
|
-
console.log(` Wrote ${
|
|
2335
|
+
console.log(` Wrote ${chalk7.cyan(tfDir + "/")}`);
|
|
2562
2336
|
console.log("");
|
|
2563
|
-
console.log(
|
|
2564
|
-
console.log(` ${
|
|
2565
|
-
console.log(` ${
|
|
2566
|
-
console.log(` ${
|
|
2567
|
-
console.log(` ${
|
|
2568
|
-
console.log(` ${
|
|
2569
|
-
console.log(` ${
|
|
2570
|
-
console.log(` ${
|
|
2571
|
-
console.log(
|
|
2337
|
+
console.log(chalk7.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"));
|
|
2338
|
+
console.log(` ${chalk7.bold("Stage:")} ${config.stage}`);
|
|
2339
|
+
console.log(` ${chalk7.bold("Region:")} ${config.region}`);
|
|
2340
|
+
console.log(` ${chalk7.bold("Account:")} ${config.account_id}`);
|
|
2341
|
+
console.log(` ${chalk7.bold("Database:")} ${config.database_engine}`);
|
|
2342
|
+
console.log(` ${chalk7.bold("Memory:")} managed (always on)${config.enable_hindsight === "true" ? " + hindsight" : ""}`);
|
|
2343
|
+
console.log(` ${chalk7.bold("Google OAuth:")} ${config.google_oauth_client_id ? "enabled" : "disabled"}`);
|
|
2344
|
+
console.log(` ${chalk7.bold("Directory:")} ${tfDir}`);
|
|
2345
|
+
console.log(chalk7.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"));
|
|
2572
2346
|
console.log("\n Initializing Terraform...\n");
|
|
2573
2347
|
try {
|
|
2574
|
-
|
|
2348
|
+
execSync6("terraform init", { cwd: tfDir, stdio: "inherit" });
|
|
2575
2349
|
} catch {
|
|
2576
2350
|
printWarning("Terraform init failed. Run `thinkwork doctor -s " + stage + "` to check prerequisites.");
|
|
2577
2351
|
return;
|
|
@@ -2590,24 +2364,24 @@ output "agentcore_memory_id" {
|
|
|
2590
2364
|
printSuccess(`Environment "${stage}" initialized`);
|
|
2591
2365
|
console.log("");
|
|
2592
2366
|
console.log(" Next steps:");
|
|
2593
|
-
console.log(` ${
|
|
2594
|
-
console.log(` ${
|
|
2595
|
-
console.log(` ${
|
|
2596
|
-
console.log(` ${
|
|
2367
|
+
console.log(` ${chalk7.cyan("1.")} thinkwork plan -s ${stage} ${chalk7.dim("# Review infrastructure plan")}`);
|
|
2368
|
+
console.log(` ${chalk7.cyan("2.")} thinkwork deploy -s ${stage} ${chalk7.dim("# Deploy to AWS (~5 min)")}`);
|
|
2369
|
+
console.log(` ${chalk7.cyan("3.")} thinkwork bootstrap -s ${stage} ${chalk7.dim("# Seed workspace files + skills")}`);
|
|
2370
|
+
console.log(` ${chalk7.cyan("4.")} thinkwork outputs -s ${stage} ${chalk7.dim("# Show API URL, Cognito IDs, etc.")}`);
|
|
2597
2371
|
console.log("");
|
|
2598
2372
|
});
|
|
2599
2373
|
}
|
|
2600
2374
|
|
|
2601
2375
|
// src/commands/status.ts
|
|
2602
|
-
import { execSync as
|
|
2603
|
-
import
|
|
2376
|
+
import { execSync as execSync7 } from "child_process";
|
|
2377
|
+
import chalk8 from "chalk";
|
|
2604
2378
|
function link(url, label) {
|
|
2605
2379
|
const text = label || url;
|
|
2606
2380
|
return `\x1B]8;;${url}\x1B\\${text}\x1B]8;;\x1B\\`;
|
|
2607
2381
|
}
|
|
2608
|
-
function
|
|
2382
|
+
function runAws2(cmd) {
|
|
2609
2383
|
try {
|
|
2610
|
-
return
|
|
2384
|
+
return execSync7(`aws ${cmd}`, {
|
|
2611
2385
|
encoding: "utf-8",
|
|
2612
2386
|
timeout: 15e3,
|
|
2613
2387
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2618,7 +2392,7 @@ function runAws3(cmd) {
|
|
|
2618
2392
|
}
|
|
2619
2393
|
function discoverAwsStages(region) {
|
|
2620
2394
|
const stages = /* @__PURE__ */ new Map();
|
|
2621
|
-
const raw =
|
|
2395
|
+
const raw = runAws2(
|
|
2622
2396
|
`lambda list-functions --region ${region} --query "Functions[?starts_with(FunctionName, 'thinkwork-')].FunctionName" --output json`
|
|
2623
2397
|
);
|
|
2624
2398
|
if (!raw) return stages;
|
|
@@ -2633,38 +2407,38 @@ function discoverAwsStages(region) {
|
|
|
2633
2407
|
for (const [stage, info] of stages) {
|
|
2634
2408
|
const count = functions.filter((f) => f.startsWith(`thinkwork-${stage}-`)).length;
|
|
2635
2409
|
info.lambdaCount = count;
|
|
2636
|
-
const apiRaw =
|
|
2410
|
+
const apiRaw = runAws2(
|
|
2637
2411
|
`apigatewayv2 get-apis --region ${region} --query "Items[?Name=='thinkwork-${stage}-api'].ApiEndpoint|[0]" --output text`
|
|
2638
2412
|
);
|
|
2639
2413
|
if (apiRaw && apiRaw !== "None") info.apiEndpoint = apiRaw;
|
|
2640
|
-
const appsyncRaw =
|
|
2414
|
+
const appsyncRaw = runAws2(
|
|
2641
2415
|
`appsync list-graphql-apis --region ${region} --query "graphqlApis[?name=='thinkwork-${stage}-subscriptions'].uris.REALTIME|[0]" --output text`
|
|
2642
2416
|
);
|
|
2643
2417
|
if (appsyncRaw && appsyncRaw !== "None") info.appsyncUrl = appsyncRaw;
|
|
2644
|
-
const appsyncApiRaw =
|
|
2418
|
+
const appsyncApiRaw = runAws2(
|
|
2645
2419
|
`appsync list-graphql-apis --region ${region} --query "graphqlApis[?name=='thinkwork-${stage}-subscriptions'].uris.GRAPHQL|[0]" --output text`
|
|
2646
2420
|
);
|
|
2647
2421
|
if (appsyncApiRaw && appsyncApiRaw !== "None") info.appsyncApiUrl = appsyncApiRaw;
|
|
2648
|
-
const acRaw =
|
|
2422
|
+
const acRaw = runAws2(
|
|
2649
2423
|
`lambda get-function --function-name thinkwork-${stage}-agentcore --region ${region} --query "Configuration.State" --output text 2>/dev/null`
|
|
2650
2424
|
);
|
|
2651
2425
|
info.agentcoreStatus = acRaw || "not deployed";
|
|
2652
|
-
const bucketRaw =
|
|
2426
|
+
const bucketRaw = runAws2(
|
|
2653
2427
|
`s3api head-bucket --bucket thinkwork-${stage}-storage --region ${region} 2>/dev/null && echo "exists"`
|
|
2654
2428
|
);
|
|
2655
2429
|
info.bucketName = bucketRaw ? `thinkwork-${stage}-storage` : void 0;
|
|
2656
|
-
const ecsRaw =
|
|
2430
|
+
const ecsRaw = runAws2(
|
|
2657
2431
|
`ecs describe-services --cluster thinkwork-${stage}-cluster --services thinkwork-${stage}-hindsight --region ${region} --query "services[0].runningCount" --output text 2>/dev/null`
|
|
2658
2432
|
);
|
|
2659
2433
|
if (ecsRaw && ecsRaw !== "None" && ecsRaw !== "0") {
|
|
2660
2434
|
info.hindsightEnabled = true;
|
|
2661
|
-
const albRaw =
|
|
2435
|
+
const albRaw = runAws2(
|
|
2662
2436
|
`elbv2 describe-load-balancers --region ${region} --query "LoadBalancers[?contains(LoadBalancerName, 'tw-${stage}-hindsight')].DNSName|[0]" --output text`
|
|
2663
2437
|
);
|
|
2664
2438
|
if (albRaw && albRaw !== "None") {
|
|
2665
2439
|
info.hindsightEndpoint = `http://${albRaw}`;
|
|
2666
2440
|
try {
|
|
2667
|
-
const health =
|
|
2441
|
+
const health = execSync7(`curl -s --max-time 3 http://${albRaw}/health`, { encoding: "utf-8" }).trim();
|
|
2668
2442
|
info.hindsightHealth = health.includes("healthy") ? "healthy" : "unhealthy";
|
|
2669
2443
|
} catch {
|
|
2670
2444
|
info.hindsightHealth = "unreachable";
|
|
@@ -2673,15 +2447,15 @@ function discoverAwsStages(region) {
|
|
|
2673
2447
|
} else {
|
|
2674
2448
|
info.hindsightEnabled = false;
|
|
2675
2449
|
}
|
|
2676
|
-
const dbRaw =
|
|
2450
|
+
const dbRaw = runAws2(
|
|
2677
2451
|
`rds describe-db-clusters --region ${region} --query "DBClusters[?starts_with(DBClusterIdentifier, 'thinkwork-${stage}')].Endpoint|[0]" --output text`
|
|
2678
2452
|
);
|
|
2679
2453
|
if (dbRaw && dbRaw !== "None") info.dbEndpoint = dbRaw;
|
|
2680
|
-
const ecrRaw =
|
|
2454
|
+
const ecrRaw = runAws2(
|
|
2681
2455
|
`ecr describe-repositories --region ${region} --query "repositories[?repositoryName=='thinkwork-${stage}-agentcore'].repositoryUri|[0]" --output text`
|
|
2682
2456
|
);
|
|
2683
2457
|
if (ecrRaw && ecrRaw !== "None") info.ecrUrl = ecrRaw;
|
|
2684
|
-
const cfJson =
|
|
2458
|
+
const cfJson = runAws2(
|
|
2685
2459
|
`cloudfront list-distributions --query "DistributionList.Items[?contains(Origins.Items[0].DomainName, 'thinkwork-${stage}-')].{Origin:Origins.Items[0].DomainName,Domain:DomainName}" --output json`
|
|
2686
2460
|
);
|
|
2687
2461
|
if (cfJson) {
|
|
@@ -2698,33 +2472,33 @@ function discoverAwsStages(region) {
|
|
|
2698
2472
|
return stages;
|
|
2699
2473
|
}
|
|
2700
2474
|
function printStageDetail(info) {
|
|
2701
|
-
console.log(
|
|
2702
|
-
console.log(
|
|
2703
|
-
console.log(` ${
|
|
2704
|
-
console.log(` ${
|
|
2705
|
-
console.log(` ${
|
|
2706
|
-
console.log(` ${
|
|
2707
|
-
console.log(` ${
|
|
2708
|
-
console.log(` ${
|
|
2475
|
+
console.log(chalk8.bold.cyan(` \u2B21 ${info.stage}`));
|
|
2476
|
+
console.log(chalk8.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"));
|
|
2477
|
+
console.log(` ${chalk8.bold("Source:")} ${info.source === "both" ? "AWS + local config" : info.source === "aws" ? "AWS (no local config)" : "local only (not in AWS)"}`);
|
|
2478
|
+
console.log(` ${chalk8.bold("Region:")} ${info.region}`);
|
|
2479
|
+
console.log(` ${chalk8.bold("Account:")} ${info.accountId}`);
|
|
2480
|
+
console.log(` ${chalk8.bold("Lambda fns:")} ${info.lambdaCount || "\u2014"}`);
|
|
2481
|
+
console.log(` ${chalk8.bold("AgentCore:")} ${info.agentcoreStatus || "unknown"}`);
|
|
2482
|
+
console.log(` ${chalk8.bold("Memory:")} managed (always on)`);
|
|
2709
2483
|
const hindsightLabel = info.hindsightEnabled === void 0 ? "unknown" : info.hindsightEnabled ? info.hindsightHealth || "running" : "disabled";
|
|
2710
|
-
console.log(` ${
|
|
2711
|
-
if (info.bucketName) console.log(` ${
|
|
2712
|
-
if (info.dbEndpoint) console.log(` ${
|
|
2713
|
-
if (info.ecrUrl) console.log(` ${
|
|
2484
|
+
console.log(` ${chalk8.bold("Hindsight:")} ${hindsightLabel}`);
|
|
2485
|
+
if (info.bucketName) console.log(` ${chalk8.bold("S3 bucket:")} ${info.bucketName}`);
|
|
2486
|
+
if (info.dbEndpoint) console.log(` ${chalk8.bold("Database:")} ${info.dbEndpoint}`);
|
|
2487
|
+
if (info.ecrUrl) console.log(` ${chalk8.bold("ECR:")} ${info.ecrUrl}`);
|
|
2714
2488
|
console.log("");
|
|
2715
|
-
console.log(
|
|
2489
|
+
console.log(chalk8.bold(" URLs:"));
|
|
2716
2490
|
if (info.adminUrl) console.log(` Admin: ${link(info.adminUrl)}`);
|
|
2717
2491
|
if (info.docsUrl) console.log(` Docs: ${link(info.docsUrl)}`);
|
|
2718
2492
|
if (info.apiEndpoint) console.log(` API: ${link(info.apiEndpoint)}`);
|
|
2719
2493
|
if (info.appsyncApiUrl) console.log(` AppSync: ${link(info.appsyncApiUrl)}`);
|
|
2720
2494
|
if (info.appsyncUrl) console.log(` WebSocket: ${link(info.appsyncUrl)}`);
|
|
2721
2495
|
if (info.hindsightEndpoint) console.log(` Hindsight: ${link(info.hindsightEndpoint)}`);
|
|
2722
|
-
console.log(
|
|
2496
|
+
console.log(chalk8.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"));
|
|
2723
2497
|
const local = loadEnvironment(info.stage);
|
|
2724
2498
|
if (local) {
|
|
2725
|
-
console.log(
|
|
2499
|
+
console.log(chalk8.dim(` Terraform dir: ${local.terraformDir}`));
|
|
2726
2500
|
} else {
|
|
2727
|
-
console.log(
|
|
2501
|
+
console.log(chalk8.dim(` No local config. Run: thinkwork init -s ${info.stage}`));
|
|
2728
2502
|
}
|
|
2729
2503
|
console.log("");
|
|
2730
2504
|
}
|
|
@@ -2761,7 +2535,7 @@ and AgentCore for per-stage detail.
|
|
|
2761
2535
|
printError("AWS credentials not configured. Run `thinkwork login` first.");
|
|
2762
2536
|
process.exit(1);
|
|
2763
2537
|
}
|
|
2764
|
-
console.log(
|
|
2538
|
+
console.log(chalk8.dim(" Scanning AWS account for Thinkwork deployments...\n"));
|
|
2765
2539
|
const awsStages = discoverAwsStages(opts.region);
|
|
2766
2540
|
const localEnvs = listEnvironments();
|
|
2767
2541
|
const merged = /* @__PURE__ */ new Map();
|
|
@@ -2796,7 +2570,7 @@ and AgentCore for per-stage detail.
|
|
|
2796
2570
|
}
|
|
2797
2571
|
if (merged.size === 0) {
|
|
2798
2572
|
console.log(" No Thinkwork environments found.");
|
|
2799
|
-
console.log(` Run ${
|
|
2573
|
+
console.log(` Run ${chalk8.cyan("thinkwork init -s <stage>")} to create one.`);
|
|
2800
2574
|
console.log("");
|
|
2801
2575
|
return;
|
|
2802
2576
|
}
|
|
@@ -2807,7 +2581,7 @@ and AgentCore for per-stage detail.
|
|
|
2807
2581
|
}
|
|
2808
2582
|
|
|
2809
2583
|
// src/commands/mcp.ts
|
|
2810
|
-
import
|
|
2584
|
+
import chalk9 from "chalk";
|
|
2811
2585
|
|
|
2812
2586
|
// ../../packages/admin-ops/src/client.ts
|
|
2813
2587
|
var AdminOpsError = class extends Error {
|
|
@@ -2823,7 +2597,7 @@ var AdminOpsError = class extends Error {
|
|
|
2823
2597
|
function createClient(config) {
|
|
2824
2598
|
const fetchImpl = config.fetchImpl ?? fetch;
|
|
2825
2599
|
const base = config.apiUrl.replace(/\/+$/, "");
|
|
2826
|
-
const doFetch = async (
|
|
2600
|
+
const doFetch = async (path, init = {}) => {
|
|
2827
2601
|
const headers = {
|
|
2828
2602
|
"Content-Type": "application/json",
|
|
2829
2603
|
Authorization: `Bearer ${config.authSecret}`,
|
|
@@ -2833,7 +2607,7 @@ function createClient(config) {
|
|
|
2833
2607
|
if (config.principalEmail) headers["x-principal-email"] = config.principalEmail;
|
|
2834
2608
|
if (config.tenantId) headers["x-tenant-id"] = config.tenantId;
|
|
2835
2609
|
if (config.agentId) headers["x-agent-id"] = config.agentId;
|
|
2836
|
-
const res = await fetchImpl(`${base}${
|
|
2610
|
+
const res = await fetchImpl(`${base}${path}`, {
|
|
2837
2611
|
...init,
|
|
2838
2612
|
headers: { ...headers, ...init.headers }
|
|
2839
2613
|
});
|
|
@@ -2919,87 +2693,6 @@ async function revokeAdminKey(client, tenantIdOrSlug, keyId) {
|
|
|
2919
2693
|
);
|
|
2920
2694
|
}
|
|
2921
2695
|
|
|
2922
|
-
// src/api-client.ts
|
|
2923
|
-
import { readFileSync as readFileSync5, existsSync as existsSync9 } from "fs";
|
|
2924
|
-
import { execSync as execSync9 } from "child_process";
|
|
2925
|
-
function readTfVar2(tfvarsPath, key) {
|
|
2926
|
-
if (!existsSync9(tfvarsPath)) return null;
|
|
2927
|
-
const content = readFileSync5(tfvarsPath, "utf-8");
|
|
2928
|
-
const match = content.match(new RegExp(`^${key}\\s*=\\s*"([^"]*)"`, "m"));
|
|
2929
|
-
return match ? match[1] : null;
|
|
2930
|
-
}
|
|
2931
|
-
function resolveTfvarsPath2(stage) {
|
|
2932
|
-
const tfDir = resolveTerraformDir(stage);
|
|
2933
|
-
if (tfDir) {
|
|
2934
|
-
const direct = `${tfDir}/terraform.tfvars`;
|
|
2935
|
-
if (existsSync9(direct)) return direct;
|
|
2936
|
-
}
|
|
2937
|
-
const terraformDir = process.env.THINKWORK_TERRAFORM_DIR || process.cwd();
|
|
2938
|
-
const cwd = resolveTierDir(terraformDir, stage, "app");
|
|
2939
|
-
return `${cwd}/terraform.tfvars`;
|
|
2940
|
-
}
|
|
2941
|
-
function getApiEndpoint2(stage, region) {
|
|
2942
|
-
try {
|
|
2943
|
-
const raw = execSync9(
|
|
2944
|
-
`aws apigatewayv2 get-apis --region ${region} --query "Items[?Name=='thinkwork-${stage}-api'].ApiEndpoint|[0]" --output text`,
|
|
2945
|
-
{ encoding: "utf-8", timeout: 15e3, stdio: ["pipe", "pipe", "pipe"] }
|
|
2946
|
-
).trim();
|
|
2947
|
-
return raw && raw !== "None" ? raw : null;
|
|
2948
|
-
} catch {
|
|
2949
|
-
return null;
|
|
2950
|
-
}
|
|
2951
|
-
}
|
|
2952
|
-
async function apiFetch(apiUrl, authSecret, path2, options = {}, extraHeaders = {}) {
|
|
2953
|
-
const res = await fetch(`${apiUrl}${path2}`, {
|
|
2954
|
-
...options,
|
|
2955
|
-
headers: {
|
|
2956
|
-
"Content-Type": "application/json",
|
|
2957
|
-
Authorization: `Bearer ${authSecret}`,
|
|
2958
|
-
...extraHeaders,
|
|
2959
|
-
...options.headers
|
|
2960
|
-
}
|
|
2961
|
-
});
|
|
2962
|
-
if (!res.ok) {
|
|
2963
|
-
const body = await res.json().catch(() => ({}));
|
|
2964
|
-
throw new Error(body.error || `HTTP ${res.status}`);
|
|
2965
|
-
}
|
|
2966
|
-
return res.json();
|
|
2967
|
-
}
|
|
2968
|
-
async function apiFetchRaw(apiUrl, authSecret, path2, options = {}, extraHeaders = {}) {
|
|
2969
|
-
const res = await fetch(`${apiUrl}${path2}`, {
|
|
2970
|
-
...options,
|
|
2971
|
-
headers: {
|
|
2972
|
-
"Content-Type": "application/json",
|
|
2973
|
-
Authorization: `Bearer ${authSecret}`,
|
|
2974
|
-
...extraHeaders,
|
|
2975
|
-
...options.headers
|
|
2976
|
-
}
|
|
2977
|
-
});
|
|
2978
|
-
const body = await res.json().catch(() => ({}));
|
|
2979
|
-
return { ok: res.ok, status: res.status, body };
|
|
2980
|
-
}
|
|
2981
|
-
function resolveApiConfig(stage, regionOverride) {
|
|
2982
|
-
const tfvarsPath = resolveTfvarsPath2(stage);
|
|
2983
|
-
const tfAuthSecret = readTfVar2(tfvarsPath, "api_auth_secret");
|
|
2984
|
-
const tfRegion = readTfVar2(tfvarsPath, "region");
|
|
2985
|
-
const region = regionOverride || tfRegion || "us-east-1";
|
|
2986
|
-
const apiUrl = getApiEndpoint2(stage, region);
|
|
2987
|
-
if (!apiUrl) {
|
|
2988
|
-
printError(
|
|
2989
|
-
`Cannot discover API endpoint for stage "${stage}" in ${region}. Is the stack deployed?`
|
|
2990
|
-
);
|
|
2991
|
-
return null;
|
|
2992
|
-
}
|
|
2993
|
-
const authSecret = tfAuthSecret ?? getApiAuthSecretFromLambda(stage, region);
|
|
2994
|
-
if (!authSecret) {
|
|
2995
|
-
printError(
|
|
2996
|
-
`Cannot read api_auth_secret. Tried terraform.tfvars at ${tfvarsPath} and the \`thinkwork-${stage}-api-tenants\` Lambda env. Deploy the stack or set --profile to a role with lambda:GetFunctionConfiguration.`
|
|
2997
|
-
);
|
|
2998
|
-
return null;
|
|
2999
|
-
}
|
|
3000
|
-
return { apiUrl, authSecret };
|
|
3001
|
-
}
|
|
3002
|
-
|
|
3003
2696
|
// src/lib/resolve-tenant.ts
|
|
3004
2697
|
import { select as select4 } from "@inquirer/prompts";
|
|
3005
2698
|
async function resolveTenant(opts) {
|
|
@@ -3173,7 +2866,7 @@ async function resolveServer(identifier, api, tenantSlug) {
|
|
|
3173
2866
|
getId: (s) => s.id,
|
|
3174
2867
|
getAliases: (s) => [s.slug, s.name],
|
|
3175
2868
|
resourceLabel: "MCP server",
|
|
3176
|
-
pickerLabel: (s) => `${s.name} ${
|
|
2869
|
+
pickerLabel: (s) => `${s.name} ${chalk9.dim(`(${s.slug}, ${s.id})`)}`
|
|
3177
2870
|
});
|
|
3178
2871
|
}
|
|
3179
2872
|
function formatAuth(s) {
|
|
@@ -3206,13 +2899,13 @@ Examples:
|
|
|
3206
2899
|
{ "x-tenant-slug": tenant.slug }
|
|
3207
2900
|
);
|
|
3208
2901
|
if (!servers || servers.length === 0) {
|
|
3209
|
-
console.log(
|
|
2902
|
+
console.log(chalk9.dim(" No MCP servers registered."));
|
|
3210
2903
|
return;
|
|
3211
2904
|
}
|
|
3212
2905
|
console.log("");
|
|
3213
2906
|
for (const s of servers) {
|
|
3214
|
-
const status = s.enabled ?
|
|
3215
|
-
console.log(` ${
|
|
2907
|
+
const status = s.enabled ? chalk9.green("enabled") : chalk9.dim("disabled");
|
|
2908
|
+
console.log(` ${chalk9.bold(s.name)} ${chalk9.dim(s.slug)} ${status}`);
|
|
3216
2909
|
console.log(` ID: ${s.id}`);
|
|
3217
2910
|
console.log(` URL: ${s.url}`);
|
|
3218
2911
|
console.log(` Transport: ${s.transport}`);
|
|
@@ -3393,12 +3086,12 @@ Examples:
|
|
|
3393
3086
|
if (result.ok) {
|
|
3394
3087
|
printSuccess(`Connection successful: ${server.name}.`);
|
|
3395
3088
|
if (result.tools?.length) {
|
|
3396
|
-
console.log(
|
|
3089
|
+
console.log(chalk9.bold(`
|
|
3397
3090
|
Discovered tools (${result.tools.length}):
|
|
3398
3091
|
`));
|
|
3399
3092
|
for (const t of result.tools) {
|
|
3400
3093
|
console.log(
|
|
3401
|
-
` ${
|
|
3094
|
+
` ${chalk9.cyan(t.name)}${t.description ? chalk9.dim(` - ${t.description}`) : ""}`
|
|
3402
3095
|
);
|
|
3403
3096
|
}
|
|
3404
3097
|
console.log("");
|
|
@@ -3503,8 +3196,8 @@ old one.
|
|
|
3503
3196
|
});
|
|
3504
3197
|
printJson(created);
|
|
3505
3198
|
printWarning("This token will NOT be shown again. Copy it now.");
|
|
3506
|
-
printSuccess(`MCP admin key created: ${
|
|
3507
|
-
console.log(` ${
|
|
3199
|
+
printSuccess(`MCP admin key created: ${chalk9.bold(created.name)} (${created.id})`);
|
|
3200
|
+
console.log(` ${chalk9.dim("Token:")} ${chalk9.cyan(created.token)}`);
|
|
3508
3201
|
} catch (err) {
|
|
3509
3202
|
if (isCancellation(err)) return;
|
|
3510
3203
|
printError(err instanceof Error ? err.message : String(err));
|
|
@@ -3650,7 +3343,7 @@ SPA (or a future \`thinkwork mcp assign\` CLI pass).
|
|
|
3650
3343
|
|
|
3651
3344
|
// src/commands/tools.ts
|
|
3652
3345
|
import { select as select6, password } from "@inquirer/prompts";
|
|
3653
|
-
import
|
|
3346
|
+
import chalk10 from "chalk";
|
|
3654
3347
|
var TOOL_PROVIDERS = {
|
|
3655
3348
|
"web-search": ["exa", "serpapi"]
|
|
3656
3349
|
};
|
|
@@ -3691,16 +3384,16 @@ Examples:
|
|
|
3691
3384
|
{ "x-tenant-slug": tenant.slug }
|
|
3692
3385
|
);
|
|
3693
3386
|
if (!rows || rows.length === 0) {
|
|
3694
|
-
console.log(
|
|
3695
|
-
console.log(
|
|
3387
|
+
console.log(chalk10.dim(" No built-in tools configured."));
|
|
3388
|
+
console.log(chalk10.dim(" Try: thinkwork tools web-search set"));
|
|
3696
3389
|
return;
|
|
3697
3390
|
}
|
|
3698
3391
|
console.log("");
|
|
3699
3392
|
for (const r of rows) {
|
|
3700
|
-
const status = r.enabled ?
|
|
3701
|
-
const key = r.hasSecret ?
|
|
3702
|
-
const provider = r.provider ??
|
|
3703
|
-
console.log(` ${
|
|
3393
|
+
const status = r.enabled ? chalk10.green("enabled") : chalk10.dim("disabled");
|
|
3394
|
+
const key = r.hasSecret ? chalk10.green("yes") : chalk10.red("no");
|
|
3395
|
+
const provider = r.provider ?? chalk10.dim("\u2014");
|
|
3396
|
+
console.log(` ${chalk10.bold(r.toolSlug)} ${status}`);
|
|
3704
3397
|
console.log(` Provider: ${provider}`);
|
|
3705
3398
|
console.log(` Has key: ${key}`);
|
|
3706
3399
|
if (r.lastTestedAt) console.log(` Tested: ${new Date(r.lastTestedAt).toLocaleString()}`);
|
|
@@ -3831,12 +3524,12 @@ Examples:
|
|
|
3831
3524
|
}
|
|
3832
3525
|
|
|
3833
3526
|
// src/commands/update.ts
|
|
3834
|
-
import { execSync as
|
|
3527
|
+
import { execSync as execSync8 } from "child_process";
|
|
3835
3528
|
import { realpathSync } from "fs";
|
|
3836
|
-
import
|
|
3529
|
+
import chalk11 from "chalk";
|
|
3837
3530
|
function getLatestVersion() {
|
|
3838
3531
|
try {
|
|
3839
|
-
return
|
|
3532
|
+
return execSync8("npm view thinkwork-cli version", {
|
|
3840
3533
|
encoding: "utf-8",
|
|
3841
3534
|
timeout: 1e4,
|
|
3842
3535
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -3847,7 +3540,7 @@ function getLatestVersion() {
|
|
|
3847
3540
|
}
|
|
3848
3541
|
function detectInstallMethod() {
|
|
3849
3542
|
try {
|
|
3850
|
-
const which =
|
|
3543
|
+
const which = execSync8("which thinkwork", {
|
|
3851
3544
|
encoding: "utf-8",
|
|
3852
3545
|
timeout: 5e3,
|
|
3853
3546
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -3875,28 +3568,28 @@ function compareVersions(a, b) {
|
|
|
3875
3568
|
function registerUpdateCommand(program2) {
|
|
3876
3569
|
program2.command("update").description("Check for and install CLI updates").option("--check", "Only check for updates, don't install").action(async (opts) => {
|
|
3877
3570
|
printHeader("update", "", null);
|
|
3878
|
-
console.log(` Current version: ${
|
|
3571
|
+
console.log(` Current version: ${chalk11.bold(VERSION)}`);
|
|
3879
3572
|
const latest = getLatestVersion();
|
|
3880
3573
|
if (!latest) {
|
|
3881
|
-
console.log(
|
|
3574
|
+
console.log(chalk11.yellow(" Could not check npm registry for updates."));
|
|
3882
3575
|
return;
|
|
3883
3576
|
}
|
|
3884
|
-
console.log(` Latest version: ${
|
|
3577
|
+
console.log(` Latest version: ${chalk11.bold(latest)}`);
|
|
3885
3578
|
console.log("");
|
|
3886
3579
|
const cmp = compareVersions(VERSION, latest);
|
|
3887
3580
|
if (cmp >= 0) {
|
|
3888
|
-
console.log(
|
|
3581
|
+
console.log(chalk11.green(" \u2713 You're on the latest version."));
|
|
3889
3582
|
console.log("");
|
|
3890
3583
|
return;
|
|
3891
3584
|
}
|
|
3892
|
-
console.log(
|
|
3585
|
+
console.log(chalk11.cyan(` Update available: ${VERSION} \u2192 ${latest}`));
|
|
3893
3586
|
console.log("");
|
|
3894
3587
|
if (opts.check) {
|
|
3895
3588
|
const method2 = detectInstallMethod();
|
|
3896
3589
|
if (method2 === "homebrew") {
|
|
3897
|
-
console.log(` Run: ${
|
|
3590
|
+
console.log(` Run: ${chalk11.cyan("brew upgrade thinkwork-ai/tap/thinkwork")}`);
|
|
3898
3591
|
} else {
|
|
3899
|
-
console.log(` Run: ${
|
|
3592
|
+
console.log(` Run: ${chalk11.cyan(`npm install -g thinkwork-cli@${latest}`)}`);
|
|
3900
3593
|
}
|
|
3901
3594
|
console.log("");
|
|
3902
3595
|
return;
|
|
@@ -3904,27 +3597,27 @@ function registerUpdateCommand(program2) {
|
|
|
3904
3597
|
const method = detectInstallMethod();
|
|
3905
3598
|
const cmd = method === "homebrew" ? "brew upgrade thinkwork-ai/tap/thinkwork" : `npm install -g thinkwork-cli@${latest}`;
|
|
3906
3599
|
console.log(` Installing via ${method}...`);
|
|
3907
|
-
console.log(
|
|
3600
|
+
console.log(chalk11.dim(` $ ${cmd}`));
|
|
3908
3601
|
console.log("");
|
|
3909
3602
|
try {
|
|
3910
|
-
|
|
3603
|
+
execSync8(cmd, { stdio: "inherit", timeout: 12e4 });
|
|
3911
3604
|
console.log("");
|
|
3912
|
-
console.log(
|
|
3605
|
+
console.log(chalk11.green(` \u2713 Upgraded to thinkwork-cli@${latest}`));
|
|
3913
3606
|
} catch {
|
|
3914
3607
|
console.log("");
|
|
3915
|
-
console.log(
|
|
3916
|
-
console.log(` ${
|
|
3608
|
+
console.log(chalk11.red(` Failed to upgrade. Try manually:`));
|
|
3609
|
+
console.log(` ${chalk11.cyan(cmd)}`);
|
|
3917
3610
|
}
|
|
3918
3611
|
console.log("");
|
|
3919
3612
|
});
|
|
3920
3613
|
}
|
|
3921
3614
|
|
|
3922
3615
|
// src/commands/user.ts
|
|
3923
|
-
import { spawn as
|
|
3616
|
+
import { spawn as spawn4 } from "child_process";
|
|
3924
3617
|
import { input as input2, select as select7 } from "@inquirer/prompts";
|
|
3925
3618
|
function getTerraformOutput2(cwd, key) {
|
|
3926
3619
|
return new Promise((resolve7, reject) => {
|
|
3927
|
-
const proc =
|
|
3620
|
+
const proc = spawn4("terraform", ["output", "-raw", key], {
|
|
3928
3621
|
cwd,
|
|
3929
3622
|
stdio: ["pipe", "pipe", "pipe"]
|
|
3930
3623
|
});
|
|
@@ -3956,7 +3649,7 @@ function runAwsCognitoReset(userPoolId, username, region) {
|
|
|
3956
3649
|
"json"
|
|
3957
3650
|
];
|
|
3958
3651
|
if (region) args.push("--region", region);
|
|
3959
|
-
const proc =
|
|
3652
|
+
const proc = spawn4("aws", args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
3960
3653
|
let stdout = "";
|
|
3961
3654
|
let stderr = "";
|
|
3962
3655
|
proc.stdout.on("data", (d) => stdout += d);
|
|
@@ -4643,6 +4336,8 @@ var CliScheduledJobsDocument = { "kind": "Document", "definitions": [{ "kind": "
|
|
|
4643
4336
|
var CliScheduledJobDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliScheduledJob" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "scheduledJob" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "id" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "triggerType" } }, { "kind": "Field", "name": { "kind": "Name", "value": "agentId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "routineId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "prompt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "scheduleType" } }, { "kind": "Field", "name": { "kind": "Name", "value": "scheduleExpression" } }, { "kind": "Field", "name": { "kind": "Name", "value": "timezone" } }, { "kind": "Field", "name": { "kind": "Name", "value": "enabled" } }, { "kind": "Field", "name": { "kind": "Name", "value": "ebScheduleName" } }, { "kind": "Field", "name": { "kind": "Name", "value": "lastRunAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "nextRunAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "createdAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updatedAt" } }] } }] } }] };
|
|
4644
4337
|
var CliCreateScheduledJobDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliCreateScheduledJob" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "input" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "CreateScheduledJobInput" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "createScheduledJob" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "input" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "input" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "enabled" } }, { "kind": "Field", "name": { "kind": "Name", "value": "scheduleExpression" } }, { "kind": "Field", "name": { "kind": "Name", "value": "timezone" } }] } }] } }] };
|
|
4645
4338
|
var CliDeleteScheduledJobDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliDeleteScheduledJob" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "deleteScheduledJob" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "id" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "ok" } }] } }] } }] };
|
|
4339
|
+
var CliRunScheduledJobDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliRunScheduledJob" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "runScheduledJob" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "id" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "dispatched" } }, { "kind": "Field", "name": { "kind": "Name", "value": "statusCode" } }, { "kind": "Field", "name": { "kind": "Name", "value": "errorMessage" } }] } }] } }] };
|
|
4340
|
+
var CliUpdateScheduledJobDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliUpdateScheduledJob" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "input" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "UpdateScheduledJobInput" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "updateScheduledJob" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "id" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "input" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "input" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "enabled" } }, { "kind": "Field", "name": { "kind": "Name", "value": "scheduleType" } }, { "kind": "Field", "name": { "kind": "Name", "value": "scheduleExpression" } }, { "kind": "Field", "name": { "kind": "Name", "value": "timezone" } }, { "kind": "Field", "name": { "kind": "Name", "value": "nextRunAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updatedAt" } }] } }] } }] };
|
|
4646
4341
|
var CliSchedJobTenantBySlugDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliSchedJobTenantBySlug" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "tenantBySlug" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "slug" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }] } }] } }] };
|
|
4647
4342
|
var CliSkillCatalogDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliSkillCatalog" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "skillCatalog" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "skillId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "displayName" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "category" } }, { "kind": "Field", "name": { "kind": "Name", "value": "icon" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source" } }, { "kind": "Field", "name": { "kind": "Name", "value": "enabled" } }] } }] } }] };
|
|
4648
4343
|
var CliSkillTenantBySlugDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliSkillTenantBySlug" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "tenantBySlug" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "slug" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }] } }] } }] };
|
|
@@ -4702,6 +4397,8 @@ var CliUpdateWebhookDocument = { "kind": "Document", "definitions": [{ "kind": "
|
|
|
4702
4397
|
var CliDeleteWebhookDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliDeleteWebhook" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "deleteWebhook" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "id" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } } }] }] } }] };
|
|
4703
4398
|
var CliRegenerateWebhookTokenDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliRegenerateWebhookToken" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "regenerateWebhookToken" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "id" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "token" } }] } }] } }] };
|
|
4704
4399
|
var CliWebhookDeliveriesDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliWebhookDeliveries" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "webhookId" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "limit" } }, "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "Int" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "webhookDeliveries" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "webhookId" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "webhookId" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "limit" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "limit" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "providerName" } }, { "kind": "Field", "name": { "kind": "Name", "value": "providerEventId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "normalizedKind" } }, { "kind": "Field", "name": { "kind": "Name", "value": "receivedAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "signatureStatus" } }, { "kind": "Field", "name": { "kind": "Name", "value": "resolutionStatus" } }, { "kind": "Field", "name": { "kind": "Name", "value": "statusCode" } }, { "kind": "Field", "name": { "kind": "Name", "value": "durationMs" } }, { "kind": "Field", "name": { "kind": "Name", "value": "threadId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "threadCreated" } }, { "kind": "Field", "name": { "kind": "Name", "value": "retryCount" } }, { "kind": "Field", "name": { "kind": "Name", "value": "isReplay" } }, { "kind": "Field", "name": { "kind": "Name", "value": "errorMessage" } }] } }] } }] };
|
|
4400
|
+
var CliTestWebhookDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliTestWebhook" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "testWebhook" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "id" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "webhookId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "tenantId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "receivedAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "resolutionStatus" } }, { "kind": "Field", "name": { "kind": "Name", "value": "signatureStatus" } }, { "kind": "Field", "name": { "kind": "Name", "value": "statusCode" } }, { "kind": "Field", "name": { "kind": "Name", "value": "bodyPreview" } }] } }] } }] };
|
|
4401
|
+
var CliWebhookForTestDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliWebhookForTest" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "webhook" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "id" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "id" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "token" } }] } }] } }] };
|
|
4705
4402
|
var CliWebhookTenantBySlugDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliWebhookTenantBySlug" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "tenantBySlug" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "slug" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }] } }] } }] };
|
|
4706
4403
|
var CliWikiTenantBySlugDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliWikiTenantBySlug" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "tenantBySlug" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "slug" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "slug" } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }] } }] } }] };
|
|
4707
4404
|
var CliAllTenantAgentsForWikiDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliAllTenantAgentsForWiki" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "tenantId" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "allTenantAgents" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "tenantId" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "tenantId" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "includeSystem" }, "value": { "kind": "BooleanValue", "value": false } }, { "kind": "Argument", "name": { "kind": "Name", "value": "includeSubAgents" }, "value": { "kind": "BooleanValue", "value": false } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "name" } }, { "kind": "Field", "name": { "kind": "Name", "value": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }] } }] } }] };
|
|
@@ -4820,6 +4517,8 @@ var documents = {
|
|
|
4820
4517
|
"\n query CliScheduledJob($id: ID!) {\n scheduledJob(id: $id) {\n id\n name\n description\n triggerType\n agentId\n routineId\n prompt\n scheduleType\n scheduleExpression\n timezone\n enabled\n ebScheduleName\n lastRunAt\n nextRunAt\n createdAt\n updatedAt\n }\n }\n": CliScheduledJobDocument,
|
|
4821
4518
|
"\n mutation CliCreateScheduledJob($input: CreateScheduledJobInput!) {\n createScheduledJob(input: $input) {\n id\n name\n enabled\n scheduleExpression\n timezone\n }\n }\n": CliCreateScheduledJobDocument,
|
|
4822
4519
|
"\n mutation CliDeleteScheduledJob($id: ID!) {\n deleteScheduledJob(id: $id) {\n id\n ok\n }\n }\n": CliDeleteScheduledJobDocument,
|
|
4520
|
+
"\n mutation CliRunScheduledJob($id: ID!) {\n runScheduledJob(id: $id) {\n id\n dispatched\n statusCode\n errorMessage\n }\n }\n": CliRunScheduledJobDocument,
|
|
4521
|
+
"\n mutation CliUpdateScheduledJob($id: ID!, $input: UpdateScheduledJobInput!) {\n updateScheduledJob(id: $id, input: $input) {\n id\n name\n enabled\n scheduleType\n scheduleExpression\n timezone\n nextRunAt\n updatedAt\n }\n }\n": CliUpdateScheduledJobDocument,
|
|
4823
4522
|
"\n query CliSchedJobTenantBySlug($slug: String!) {\n tenantBySlug(slug: $slug) {\n id\n }\n }\n": CliSchedJobTenantBySlugDocument,
|
|
4824
4523
|
"\n query CliSkillCatalog {\n skillCatalog {\n id\n skillId\n displayName\n description\n category\n icon\n source\n enabled\n }\n }\n": CliSkillCatalogDocument,
|
|
4825
4524
|
"\n query CliSkillTenantBySlug($slug: String!) {\n tenantBySlug(slug: $slug) {\n id\n }\n }\n": CliSkillTenantBySlugDocument,
|
|
@@ -4879,6 +4578,8 @@ var documents = {
|
|
|
4879
4578
|
"\n mutation CliDeleteWebhook($id: ID!) {\n deleteWebhook(id: $id)\n }\n": CliDeleteWebhookDocument,
|
|
4880
4579
|
"\n mutation CliRegenerateWebhookToken($id: ID!) {\n regenerateWebhookToken(id: $id) {\n id\n token\n }\n }\n": CliRegenerateWebhookTokenDocument,
|
|
4881
4580
|
"\n query CliWebhookDeliveries($webhookId: ID!, $limit: Int) {\n webhookDeliveries(webhookId: $webhookId, limit: $limit) {\n id\n providerName\n providerEventId\n normalizedKind\n receivedAt\n signatureStatus\n resolutionStatus\n statusCode\n durationMs\n threadId\n threadCreated\n retryCount\n isReplay\n errorMessage\n }\n }\n": CliWebhookDeliveriesDocument,
|
|
4581
|
+
"\n mutation CliTestWebhook($id: ID!) {\n testWebhook(id: $id) {\n id\n webhookId\n tenantId\n receivedAt\n resolutionStatus\n signatureStatus\n statusCode\n bodyPreview\n }\n }\n": CliTestWebhookDocument,
|
|
4582
|
+
"\n query CliWebhookForTest($id: ID!) {\n webhook(id: $id) {\n id\n token\n }\n }\n": CliWebhookForTestDocument,
|
|
4882
4583
|
"\n query CliWebhookTenantBySlug($slug: String!) {\n tenantBySlug(slug: $slug) {\n id\n }\n }\n": CliWebhookTenantBySlugDocument,
|
|
4883
4584
|
"\n query CliWikiTenantBySlug($slug: String!) {\n tenantBySlug(slug: $slug) {\n id\n slug\n name\n }\n }\n": CliWikiTenantBySlugDocument,
|
|
4884
4585
|
"\n query CliAllTenantAgentsForWiki($tenantId: ID!) {\n allTenantAgents(tenantId: $tenantId, includeSystem: false, includeSubAgents: false) {\n id\n name\n slug\n type\n status\n }\n }\n": CliAllTenantAgentsForWikiDocument,
|
|
@@ -7481,7 +7182,7 @@ Examples:
|
|
|
7481
7182
|
}
|
|
7482
7183
|
|
|
7483
7184
|
// src/commands/computer.ts
|
|
7484
|
-
import
|
|
7185
|
+
import chalk12 from "chalk";
|
|
7485
7186
|
async function resolveComputerContext(opts) {
|
|
7486
7187
|
const stage = await resolveStage({ flag: opts.stage });
|
|
7487
7188
|
const api = resolveApiConfig(stage);
|
|
@@ -7521,7 +7222,7 @@ function printMigrationReport(response) {
|
|
|
7521
7222
|
if (!response.report) return;
|
|
7522
7223
|
const summary = response.report.summary ?? {};
|
|
7523
7224
|
console.log("");
|
|
7524
|
-
console.log(
|
|
7225
|
+
console.log(chalk12.bold(" Summary"));
|
|
7525
7226
|
for (const [status, count] of Object.entries(summary)) {
|
|
7526
7227
|
if (!count) continue;
|
|
7527
7228
|
console.log(` ${status.padEnd(28)} ${count}`);
|
|
@@ -7602,7 +7303,7 @@ function registerComputerCommand(program2) {
|
|
|
7602
7303
|
if (response.status === 409 && response.body.blockers) {
|
|
7603
7304
|
if (!isJsonMode()) {
|
|
7604
7305
|
console.log("");
|
|
7605
|
-
console.log(
|
|
7306
|
+
console.log(chalk12.bold(" Blockers"));
|
|
7606
7307
|
console.log(JSON.stringify(response.body.blockers, null, 2));
|
|
7607
7308
|
}
|
|
7608
7309
|
}
|
|
@@ -9941,6 +9642,30 @@ var DeleteScheduledJobDoc = graphql(`
|
|
|
9941
9642
|
}
|
|
9942
9643
|
}
|
|
9943
9644
|
`);
|
|
9645
|
+
var RunScheduledJobDoc = graphql(`
|
|
9646
|
+
mutation CliRunScheduledJob($id: ID!) {
|
|
9647
|
+
runScheduledJob(id: $id) {
|
|
9648
|
+
id
|
|
9649
|
+
dispatched
|
|
9650
|
+
statusCode
|
|
9651
|
+
errorMessage
|
|
9652
|
+
}
|
|
9653
|
+
}
|
|
9654
|
+
`);
|
|
9655
|
+
var UpdateScheduledJobDoc = graphql(`
|
|
9656
|
+
mutation CliUpdateScheduledJob($id: ID!, $input: UpdateScheduledJobInput!) {
|
|
9657
|
+
updateScheduledJob(id: $id, input: $input) {
|
|
9658
|
+
id
|
|
9659
|
+
name
|
|
9660
|
+
enabled
|
|
9661
|
+
scheduleType
|
|
9662
|
+
scheduleExpression
|
|
9663
|
+
timezone
|
|
9664
|
+
nextRunAt
|
|
9665
|
+
updatedAt
|
|
9666
|
+
}
|
|
9667
|
+
}
|
|
9668
|
+
`);
|
|
9944
9669
|
var SchedJobTenantBySlugDoc = graphql(`
|
|
9945
9670
|
query CliSchedJobTenantBySlug($slug: String!) {
|
|
9946
9671
|
tenantBySlug(slug: $slug) {
|
|
@@ -10134,13 +9859,74 @@ async function runSchedDelete(id, opts) {
|
|
|
10134
9859
|
);
|
|
10135
9860
|
}
|
|
10136
9861
|
}
|
|
10137
|
-
function
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
|
|
10141
|
-
|
|
9862
|
+
async function runSchedUpdate(id, opts) {
|
|
9863
|
+
const ctx = await resolveSchedContext(opts);
|
|
9864
|
+
const input20 = {};
|
|
9865
|
+
if (opts.name !== void 0) input20.name = opts.name;
|
|
9866
|
+
if (opts.description !== void 0) input20.description = opts.description;
|
|
9867
|
+
if (opts.prompt !== void 0) input20.prompt = opts.prompt;
|
|
9868
|
+
if (opts.schedule !== void 0) {
|
|
9869
|
+
input20.scheduleExpression = opts.schedule;
|
|
9870
|
+
input20.scheduleType = opts.schedule.trim().startsWith("cron(") ? "cron" : "rate";
|
|
9871
|
+
}
|
|
9872
|
+
if (opts.timezone !== void 0) input20.timezone = opts.timezone;
|
|
9873
|
+
if (opts.payload !== void 0) {
|
|
9874
|
+
try {
|
|
9875
|
+
input20.config = JSON.stringify(JSON.parse(opts.payload));
|
|
9876
|
+
} catch (err) {
|
|
9877
|
+
printError(`--payload is not valid JSON: ${err.message}`);
|
|
9878
|
+
process.exit(1);
|
|
9879
|
+
}
|
|
9880
|
+
}
|
|
9881
|
+
if (opts.enable && opts.disable) {
|
|
9882
|
+
printError("--enable and --disable are mutually exclusive.");
|
|
9883
|
+
process.exit(1);
|
|
9884
|
+
}
|
|
9885
|
+
if (opts.enable) input20.enabled = true;
|
|
9886
|
+
if (opts.disable) input20.enabled = false;
|
|
9887
|
+
if (Object.keys(input20).length === 0) {
|
|
9888
|
+
printError(
|
|
9889
|
+
"Nothing to update. Pass at least one of --schedule, --timezone, --payload, --enable/--disable, --name, --description, --prompt."
|
|
9890
|
+
);
|
|
9891
|
+
process.exit(1);
|
|
9892
|
+
}
|
|
9893
|
+
const data = await gqlMutate(ctx.client, UpdateScheduledJobDoc, {
|
|
9894
|
+
id,
|
|
9895
|
+
input: input20
|
|
9896
|
+
});
|
|
9897
|
+
if (isJsonMode()) {
|
|
9898
|
+
printJson(data.updateScheduledJob);
|
|
9899
|
+
return;
|
|
9900
|
+
}
|
|
9901
|
+
const job = data.updateScheduledJob;
|
|
9902
|
+
printSuccess(
|
|
9903
|
+
`Updated scheduled job ${job.id} \u2014 ${job.name} (${job.scheduleExpression}, ${job.timezone}, ${job.enabled ? "enabled" : "disabled"}).`
|
|
10142
9904
|
);
|
|
10143
|
-
|
|
9905
|
+
}
|
|
9906
|
+
async function runSchedRun(id, opts) {
|
|
9907
|
+
const ctx = await resolveSchedContext(opts);
|
|
9908
|
+
const data = await gqlMutate(ctx.client, RunScheduledJobDoc, { id });
|
|
9909
|
+
const result = data.runScheduledJob;
|
|
9910
|
+
if (isJsonMode()) {
|
|
9911
|
+
printJson(result);
|
|
9912
|
+
if (!result.dispatched) process.exit(1);
|
|
9913
|
+
return;
|
|
9914
|
+
}
|
|
9915
|
+
if (result.dispatched) {
|
|
9916
|
+
printSuccess(
|
|
9917
|
+
`Dispatched scheduled job ${result.id} (status ${result.statusCode ?? "\u2014"}). Downstream side effects run asynchronously.`
|
|
9918
|
+
);
|
|
9919
|
+
if (opts.wait) {
|
|
9920
|
+
console.log(
|
|
9921
|
+
` --wait is not yet implemented on the API side; check ${result.id}'s last_run_at via \`scheduled-job get\`.`
|
|
9922
|
+
);
|
|
9923
|
+
}
|
|
9924
|
+
} else {
|
|
9925
|
+
printError(
|
|
9926
|
+
`Dispatch failed: ${result.errorMessage ?? "unknown reason"} (status ${result.statusCode ?? "\u2014"}).`
|
|
9927
|
+
);
|
|
9928
|
+
process.exit(1);
|
|
9929
|
+
}
|
|
10144
9930
|
}
|
|
10145
9931
|
function registerScheduledJobCommand(program2) {
|
|
10146
9932
|
const job = program2.command("scheduled-job").alias("cron").description("Manage AWS-Scheduler-backed recurring agent jobs (wakeups on a cadence).");
|
|
@@ -10157,11 +9943,15 @@ Examples:
|
|
|
10157
9943
|
$ thinkwork scheduled-job create "Hourly check" --agent agt-check --schedule "rate(1 hour)"
|
|
10158
9944
|
`
|
|
10159
9945
|
).action(runSchedCreate);
|
|
10160
|
-
job.command("update <id>").description(
|
|
9946
|
+
job.command("update <id>").description(
|
|
9947
|
+
"Update a scheduled job. Changes the EventBridge schedule when --schedule / --timezone / --enable / --disable are passed; partial-update otherwise."
|
|
9948
|
+
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--schedule <expr>", "EventBridge schedule (cron(\u2026) or rate(\u2026))").option("--timezone <tz>").option("--payload <json>", "Payload to pass to the agent/routine").option("--enable", "Re-enable a paused job").option("--disable", "Pause without deleting").option("--name <text>").option("--description <text>").option("--prompt <text>").action(runSchedUpdate);
|
|
10161
9949
|
job.command("delete <id>").description(
|
|
10162
9950
|
"Delete a scheduled job. Deprovisions the EventBridge schedule first, then removes the row."
|
|
10163
9951
|
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("-y, --yes", "Skip confirmation").action(runSchedDelete);
|
|
10164
|
-
job.command("run <id>").description(
|
|
9952
|
+
job.command("run <id>").description(
|
|
9953
|
+
"Fire a scheduled job once, immediately. Bypasses the EventBridge schedule; downstream side effects (thread turn, routine run) remain async."
|
|
9954
|
+
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--wait", "(not yet supported server-side; reserved for future)").action(runSchedRun);
|
|
10165
9955
|
}
|
|
10166
9956
|
|
|
10167
9957
|
// src/commands/turn.ts
|
|
@@ -10641,6 +10431,28 @@ var WebhookDeliveriesDoc = graphql(`
|
|
|
10641
10431
|
}
|
|
10642
10432
|
}
|
|
10643
10433
|
`);
|
|
10434
|
+
var TestWebhookDoc = graphql(`
|
|
10435
|
+
mutation CliTestWebhook($id: ID!) {
|
|
10436
|
+
testWebhook(id: $id) {
|
|
10437
|
+
id
|
|
10438
|
+
webhookId
|
|
10439
|
+
tenantId
|
|
10440
|
+
receivedAt
|
|
10441
|
+
resolutionStatus
|
|
10442
|
+
signatureStatus
|
|
10443
|
+
statusCode
|
|
10444
|
+
bodyPreview
|
|
10445
|
+
}
|
|
10446
|
+
}
|
|
10447
|
+
`);
|
|
10448
|
+
var WebhookForTestDoc = graphql(`
|
|
10449
|
+
query CliWebhookForTest($id: ID!) {
|
|
10450
|
+
webhook(id: $id) {
|
|
10451
|
+
id
|
|
10452
|
+
token
|
|
10453
|
+
}
|
|
10454
|
+
}
|
|
10455
|
+
`);
|
|
10644
10456
|
var WebhookTenantBySlugDoc = graphql(`
|
|
10645
10457
|
query CliWebhookTenantBySlug($slug: String!) {
|
|
10646
10458
|
tenantBySlug(slug: $slug) {
|
|
@@ -10918,12 +10730,39 @@ async function runWebhookDeliveries(id, opts) {
|
|
|
10918
10730
|
]
|
|
10919
10731
|
);
|
|
10920
10732
|
}
|
|
10921
|
-
function
|
|
10922
|
-
|
|
10923
|
-
|
|
10924
|
-
|
|
10733
|
+
async function runWebhookTest(id, _opts) {
|
|
10734
|
+
const ctx = await resolveWebhookContext(_opts);
|
|
10735
|
+
const data = await gqlMutate(ctx.client, TestWebhookDoc, { id });
|
|
10736
|
+
const delivery = data.testWebhook;
|
|
10737
|
+
let curlHint = null;
|
|
10738
|
+
try {
|
|
10739
|
+
const tokenData = await gqlQuery(ctx.client, WebhookForTestDoc, { id });
|
|
10740
|
+
if (tokenData.webhook?.token) {
|
|
10741
|
+
const { resolveApiConfig: resolveApiConfig2 } = await import("./api-client-MC4NOZ4L.js");
|
|
10742
|
+
const api = resolveApiConfig2(ctx.stage);
|
|
10743
|
+
if (api?.apiUrl) {
|
|
10744
|
+
const base = api.apiUrl.replace(/\/$/, "");
|
|
10745
|
+
curlHint = `${base}/webhooks/${tokenData.webhook.token}`;
|
|
10746
|
+
}
|
|
10747
|
+
}
|
|
10748
|
+
} catch {
|
|
10749
|
+
}
|
|
10750
|
+
if (isJsonMode()) {
|
|
10751
|
+
printJson({ delivery, publicUrl: curlHint });
|
|
10752
|
+
return;
|
|
10753
|
+
}
|
|
10754
|
+
printSuccess(
|
|
10755
|
+
`Recorded synthetic delivery ${delivery.id} (resolution=${delivery.resolutionStatus}).`
|
|
10925
10756
|
);
|
|
10926
|
-
|
|
10757
|
+
if (curlHint) {
|
|
10758
|
+
console.log("");
|
|
10759
|
+
console.log(" For end-to-end reachability check, curl the public URL:");
|
|
10760
|
+
console.log(
|
|
10761
|
+
` curl -X POST -H 'content-type: application/json' \\
|
|
10762
|
+
-d '{"hello":"world"}' \\
|
|
10763
|
+
${curlHint}`
|
|
10764
|
+
);
|
|
10765
|
+
}
|
|
10927
10766
|
}
|
|
10928
10767
|
function registerWebhookCommand(program2) {
|
|
10929
10768
|
const wh = program2.command("webhook").alias("webhooks").description("Manage inbound webhooks that dispatch to agents or routines.");
|
|
@@ -10938,7 +10777,9 @@ Examples:
|
|
|
10938
10777
|
).action(runWebhookCreate);
|
|
10939
10778
|
wh.command("update <id>").description("Update a webhook's target, rate limit, or enabled state.").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--target-type <t>").option("--target-id <id>").option("--rate-limit <rpm>").option("--allowed-ips <csv>").option("--enable").option("--disable").action(runWebhookUpdate);
|
|
10940
10779
|
wh.command("delete <id>").description("Delete a webhook (its URL stops working immediately).").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("-y, --yes", "Skip confirmation").action(runWebhookDelete);
|
|
10941
|
-
wh.command("test <id>").description(
|
|
10780
|
+
wh.command("test <id>").description(
|
|
10781
|
+
"Record a synthetic test delivery row for the webhook (visible via `webhook deliveries`). Does NOT trigger downstream dispatch; prints a curl one-liner for end-to-end reachability against the public URL."
|
|
10782
|
+
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").action(runWebhookTest);
|
|
10942
10783
|
wh.command("rotate <id>").description("Generate a new token for an existing webhook.").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("-y, --yes", "Skip confirmation").action(runWebhookRotate);
|
|
10943
10784
|
wh.command("deliveries <id>").description(
|
|
10944
10785
|
"Show recent delivery attempts for a webhook (newest first). Default 25, max 500."
|
|
@@ -10947,7 +10788,7 @@ Examples:
|
|
|
10947
10788
|
|
|
10948
10789
|
// src/lib/plugin-zip.ts
|
|
10949
10790
|
import { createReadStream, promises as fsp, statSync } from "fs";
|
|
10950
|
-
import { basename, join as
|
|
10791
|
+
import { basename, join as join5, relative, resolve as resolve3, sep } from "path";
|
|
10951
10792
|
import JSZip from "jszip";
|
|
10952
10793
|
var PluginZipError = class extends Error {
|
|
10953
10794
|
constructor(message, kind) {
|
|
@@ -10974,7 +10815,7 @@ async function buildPluginZip(pluginDir) {
|
|
|
10974
10815
|
"missing-directory"
|
|
10975
10816
|
);
|
|
10976
10817
|
}
|
|
10977
|
-
const manifestPath =
|
|
10818
|
+
const manifestPath = join5(root, "plugin.json");
|
|
10978
10819
|
let manifestRaw;
|
|
10979
10820
|
try {
|
|
10980
10821
|
manifestRaw = await fsp.readFile(manifestPath, "utf8");
|
|
@@ -11053,7 +10894,7 @@ async function walkDir(rootDir, currentDir) {
|
|
|
11053
10894
|
const out = [];
|
|
11054
10895
|
const dirents = await fsp.readdir(currentDir, { withFileTypes: true });
|
|
11055
10896
|
for (const ent of dirents) {
|
|
11056
|
-
const abs =
|
|
10897
|
+
const abs = join5(currentDir, ent.name);
|
|
11057
10898
|
const rel = relative(rootDir, abs);
|
|
11058
10899
|
if (ent.name === ".git" || ent.name === ".DS_Store" || ent.name === "node_modules") {
|
|
11059
10900
|
continue;
|
|
@@ -11246,7 +11087,7 @@ async function runSkillList(opts) {
|
|
|
11246
11087
|
]
|
|
11247
11088
|
);
|
|
11248
11089
|
}
|
|
11249
|
-
function
|
|
11090
|
+
function notYetImplementedAtApi(verb) {
|
|
11250
11091
|
printError(
|
|
11251
11092
|
`\`skill ${verb}\` is not yet implemented at the GraphQL API.
|
|
11252
11093
|
The current schema exposes skillCatalog (read), per-computer enableSkill/disableSkill,
|
|
@@ -11262,11 +11103,11 @@ function registerSkillCommand(program2) {
|
|
|
11262
11103
|
);
|
|
11263
11104
|
skill.command("catalog").description("Browse the skill catalog. Client-side filters --search and --tag are applied locally.").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--search <q>", "Filter by keyword").option("--tag <t>", "Filter by category").action(runSkillCatalog);
|
|
11264
11105
|
skill.command("list").alias("ls").description("List skills available to the tenant.").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--custom-only", "Only show tenant-owned custom skills (source=tenant)").action(runSkillList);
|
|
11265
|
-
skill.command("install <slug>").description("Install a public skill. (API surface pending \u2014 toggle per-agent via `agent skills set`.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--version <v>", "Pin to a specific version").action(() =>
|
|
11266
|
-
skill.command("upgrade <slug>").description("Upgrade an installed skill. (API surface pending.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").action(() =>
|
|
11267
|
-
skill.command("create [slug]").description("Publish a custom tenant-scoped skill. (Use `skill push <folder>` for now.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--name <n>").option("--description <text>").option("--manifest-file <path>", "Path to the MCP server manifest JSON").option("--endpoint <url>", "MCP server HTTP/SSE endpoint").action(() =>
|
|
11268
|
-
skill.command("update <slug>").description("Update a custom skill. (API surface pending.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--name <n>").option("--description <text>").option("--manifest-file <path>").option("--endpoint <url>").action(() =>
|
|
11269
|
-
skill.command("delete <slug>").description("Delete a custom skill. (API surface pending.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("-y, --yes", "Skip confirmation").action(() =>
|
|
11106
|
+
skill.command("install <slug>").description("Install a public skill. (API surface pending \u2014 toggle per-agent via `agent skills set`.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--version <v>", "Pin to a specific version").action(() => notYetImplementedAtApi("install"));
|
|
11107
|
+
skill.command("upgrade <slug>").description("Upgrade an installed skill. (API surface pending.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").action(() => notYetImplementedAtApi("upgrade"));
|
|
11108
|
+
skill.command("create [slug]").description("Publish a custom tenant-scoped skill. (Use `skill push <folder>` for now.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--name <n>").option("--description <text>").option("--manifest-file <path>", "Path to the MCP server manifest JSON").option("--endpoint <url>", "MCP server HTTP/SSE endpoint").action(() => notYetImplementedAtApi("create"));
|
|
11109
|
+
skill.command("update <slug>").description("Update a custom skill. (API surface pending.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--name <n>").option("--description <text>").option("--manifest-file <path>").option("--endpoint <url>").action(() => notYetImplementedAtApi("update"));
|
|
11110
|
+
skill.command("delete <slug>").description("Delete a custom skill. (API surface pending.)").option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("-y, --yes", "Skip confirmation").action(() => notYetImplementedAtApi("delete"));
|
|
11270
11111
|
skill.command("push <folder>").description(
|
|
11271
11112
|
"Zip a local plugin folder and upload it to the tenant as a pending plugin."
|
|
11272
11113
|
).option("-s, --stage <name>", "Deployment stage").option("--region <name>", "AWS region", "us-east-1").addHelpText(
|
|
@@ -12696,7 +12537,7 @@ Examples:
|
|
|
12696
12537
|
|
|
12697
12538
|
// src/commands/eval/run.ts
|
|
12698
12539
|
import { checkbox, confirm as confirm17 } from "@inquirer/prompts";
|
|
12699
|
-
import
|
|
12540
|
+
import ora from "ora";
|
|
12700
12541
|
|
|
12701
12542
|
// src/commands/eval/gql.ts
|
|
12702
12543
|
var EvalRunsDoc = graphql(`
|
|
@@ -13081,7 +12922,7 @@ async function runEvalRun(opts) {
|
|
|
13081
12922
|
}
|
|
13082
12923
|
async function pollUntilTerminal(client, runId, intervalSec, timeoutSec) {
|
|
13083
12924
|
const deadline = Date.now() + timeoutSec * 1e3;
|
|
13084
|
-
const spinner = isJsonMode() ? null :
|
|
12925
|
+
const spinner = isJsonMode() ? null : ora({ text: "Waiting for run to complete\u2026" }).start();
|
|
13085
12926
|
try {
|
|
13086
12927
|
while (Date.now() < deadline) {
|
|
13087
12928
|
const data = await gqlQuery(client, EvalRunDoc, { id: runId });
|
|
@@ -13211,13 +13052,13 @@ async function runEvalGet(runId, opts) {
|
|
|
13211
13052
|
}
|
|
13212
13053
|
|
|
13213
13054
|
// src/commands/eval/watch.ts
|
|
13214
|
-
import
|
|
13055
|
+
import ora2 from "ora";
|
|
13215
13056
|
async function runEvalWatch(runId, opts) {
|
|
13216
13057
|
const ctx = await resolveEvalContext(opts);
|
|
13217
13058
|
const intervalSec = Number.parseInt(opts.interval ?? "3", 10);
|
|
13218
13059
|
const timeoutSec = Number.parseInt(opts.timeout ?? "900", 10);
|
|
13219
13060
|
const deadline = Date.now() + timeoutSec * 1e3;
|
|
13220
|
-
const spinner = isJsonMode() ? null :
|
|
13061
|
+
const spinner = isJsonMode() ? null : ora2({ text: `Watching run ${runId}\u2026` }).start();
|
|
13221
13062
|
try {
|
|
13222
13063
|
while (Date.now() < deadline) {
|
|
13223
13064
|
const data = await gqlQuery(ctx.client, EvalRunDoc, { id: runId });
|
|
@@ -13422,7 +13263,7 @@ async function runEvalTestCaseGet(id, opts) {
|
|
|
13422
13263
|
}
|
|
13423
13264
|
|
|
13424
13265
|
// src/commands/eval/test-case/create.ts
|
|
13425
|
-
import { readFileSync as
|
|
13266
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
13426
13267
|
import { input as input19, select as select8, checkbox as checkbox2 } from "@inquirer/prompts";
|
|
13427
13268
|
var DEFAULT_EVALUATORS = [
|
|
13428
13269
|
"Builtin.Helpfulness",
|
|
@@ -13495,7 +13336,7 @@ async function runEvalTestCaseCreate(opts) {
|
|
|
13495
13336
|
}
|
|
13496
13337
|
let assertions = null;
|
|
13497
13338
|
if (opts.assertionsFile) {
|
|
13498
|
-
const parsed = JSON.parse(
|
|
13339
|
+
const parsed = JSON.parse(readFileSync4(opts.assertionsFile, "utf8"));
|
|
13499
13340
|
if (!Array.isArray(parsed)) {
|
|
13500
13341
|
printError(`--assertions-file must contain a JSON array.`);
|
|
13501
13342
|
process.exit(1);
|
|
@@ -13526,7 +13367,7 @@ async function runEvalTestCaseCreate(opts) {
|
|
|
13526
13367
|
}
|
|
13527
13368
|
|
|
13528
13369
|
// src/commands/eval/test-case/update.ts
|
|
13529
|
-
import { readFileSync as
|
|
13370
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
13530
13371
|
async function runEvalTestCaseUpdate(id, opts) {
|
|
13531
13372
|
const ctx = await resolveEvalContext(opts);
|
|
13532
13373
|
const input20 = {};
|
|
@@ -13539,7 +13380,7 @@ async function runEvalTestCaseUpdate(id, opts) {
|
|
|
13539
13380
|
if (opts.tag !== void 0) input20.tags = opts.tag;
|
|
13540
13381
|
if (opts.enabled !== void 0) input20.enabled = opts.enabled;
|
|
13541
13382
|
if (opts.assertionsFile) {
|
|
13542
|
-
const parsed = JSON.parse(
|
|
13383
|
+
const parsed = JSON.parse(readFileSync5(opts.assertionsFile, "utf8"));
|
|
13543
13384
|
if (!Array.isArray(parsed)) {
|
|
13544
13385
|
printError(`--assertions-file must contain a JSON array.`);
|
|
13545
13386
|
process.exit(1);
|
|
@@ -13670,7 +13511,7 @@ Examples:
|
|
|
13670
13511
|
}
|
|
13671
13512
|
|
|
13672
13513
|
// src/commands/wiki/compile.ts
|
|
13673
|
-
import
|
|
13514
|
+
import ora3 from "ora";
|
|
13674
13515
|
|
|
13675
13516
|
// src/commands/wiki/gql.ts
|
|
13676
13517
|
var TenantBySlugDoc2 = graphql(`
|
|
@@ -13940,7 +13781,7 @@ async function runWikiCompile(opts) {
|
|
|
13940
13781
|
const errors = [];
|
|
13941
13782
|
let forbiddenHit = false;
|
|
13942
13783
|
for (const target of targets) {
|
|
13943
|
-
const spinner = isJsonMode() || scope.mode === "all" ? null :
|
|
13784
|
+
const spinner = isJsonMode() || scope.mode === "all" ? null : ora3({
|
|
13944
13785
|
text: `Enqueuing compile for ${target.label}\u2026`,
|
|
13945
13786
|
prefixText: " "
|
|
13946
13787
|
}).start();
|
|
@@ -14038,7 +13879,7 @@ async function runWikiCompile(opts) {
|
|
|
14038
13879
|
}
|
|
14039
13880
|
}
|
|
14040
13881
|
async function watchSingleJob(ctx, target) {
|
|
14041
|
-
const spinner = isJsonMode() ? null :
|
|
13882
|
+
const spinner = isJsonMode() ? null : ora3({
|
|
14042
13883
|
text: `Watching job ${shortJobId(target.jobId)} for ${target.agentLabel}\u2026`,
|
|
14043
13884
|
prefixText: " "
|
|
14044
13885
|
}).start();
|
|
@@ -14087,7 +13928,7 @@ async function watchSingleJob(ctx, target) {
|
|
|
14087
13928
|
|
|
14088
13929
|
// src/commands/wiki/rebuild.ts
|
|
14089
13930
|
import { confirm as confirm20 } from "@inquirer/prompts";
|
|
14090
|
-
import
|
|
13931
|
+
import ora4 from "ora";
|
|
14091
13932
|
async function runWikiRebuild(opts) {
|
|
14092
13933
|
if (opts.all) {
|
|
14093
13934
|
printError(
|
|
@@ -14118,7 +13959,7 @@ async function runWikiRebuild(opts) {
|
|
|
14118
13959
|
process.exit(0);
|
|
14119
13960
|
}
|
|
14120
13961
|
}
|
|
14121
|
-
const resetSpinner = isJsonMode() ? null :
|
|
13962
|
+
const resetSpinner = isJsonMode() ? null : ora4({
|
|
14122
13963
|
text: `Archiving active pages for ${agentLabel}\u2026`,
|
|
14123
13964
|
prefixText: " "
|
|
14124
13965
|
}).start();
|
|
@@ -14155,7 +13996,7 @@ async function runWikiRebuild(opts) {
|
|
|
14155
13996
|
}
|
|
14156
13997
|
process.exit(1);
|
|
14157
13998
|
}
|
|
14158
|
-
const compileSpinner = isJsonMode() ? null :
|
|
13999
|
+
const compileSpinner = isJsonMode() ? null : ora4({
|
|
14159
14000
|
text: `Enqueuing fresh compile for ${agentLabel}\u2026`,
|
|
14160
14001
|
prefixText: " "
|
|
14161
14002
|
}).start();
|
|
@@ -14228,7 +14069,7 @@ async function runWikiRebuild(opts) {
|
|
|
14228
14069
|
}
|
|
14229
14070
|
}
|
|
14230
14071
|
async function watchRebuildJob(ctx, target) {
|
|
14231
|
-
const spinner = isJsonMode() ? null :
|
|
14072
|
+
const spinner = isJsonMode() ? null : ora4({
|
|
14232
14073
|
text: `Watching rebuild job ${shortJobId(target.jobId)}\u2026`,
|
|
14233
14074
|
prefixText: " "
|
|
14234
14075
|
}).start();
|
|
@@ -14264,7 +14105,7 @@ async function watchRebuildJob(ctx, target) {
|
|
|
14264
14105
|
}
|
|
14265
14106
|
|
|
14266
14107
|
// src/commands/wiki/status.ts
|
|
14267
|
-
import
|
|
14108
|
+
import ora5 from "ora";
|
|
14268
14109
|
var DEFAULT_WATCH_INTERVAL_MS = 3e3;
|
|
14269
14110
|
async function runWikiStatus(opts) {
|
|
14270
14111
|
const ctx = await resolveWikiContext(opts);
|
|
@@ -14316,7 +14157,7 @@ async function runWikiStatus(opts) {
|
|
|
14316
14157
|
process.exit(0);
|
|
14317
14158
|
}
|
|
14318
14159
|
const latestId = jobs[0].id;
|
|
14319
|
-
const spinner = isJsonMode() ? null :
|
|
14160
|
+
const spinner = isJsonMode() ? null : ora5({
|
|
14320
14161
|
text: `Watching job ${shortJobId(latestId)}\u2026`,
|
|
14321
14162
|
prefixText: " "
|
|
14322
14163
|
}).start();
|
|
@@ -14533,14 +14374,14 @@ import { resolve as resolve5 } from "path";
|
|
|
14533
14374
|
|
|
14534
14375
|
// src/commands/enterprise/template.ts
|
|
14535
14376
|
import {
|
|
14536
|
-
existsSync as
|
|
14537
|
-
mkdirSync as
|
|
14538
|
-
readFileSync as
|
|
14539
|
-
readdirSync
|
|
14377
|
+
existsSync as existsSync7,
|
|
14378
|
+
mkdirSync as mkdirSync4,
|
|
14379
|
+
readFileSync as readFileSync6,
|
|
14380
|
+
readdirSync,
|
|
14540
14381
|
statSync as statSync2,
|
|
14541
|
-
writeFileSync as
|
|
14382
|
+
writeFileSync as writeFileSync4
|
|
14542
14383
|
} from "fs";
|
|
14543
|
-
import { dirname as dirname4, join as
|
|
14384
|
+
import { dirname as dirname4, join as join6, relative as relative2, resolve as resolve4 } from "path";
|
|
14544
14385
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14545
14386
|
var __dirname2 = dirname4(fileURLToPath3(import.meta.url));
|
|
14546
14387
|
var MANAGED_MARKER = "thinkwork-managed: enterprise-deploy-template";
|
|
@@ -14556,17 +14397,17 @@ function renderEnterpriseDeployRepoTemplate(options) {
|
|
|
14556
14397
|
stages
|
|
14557
14398
|
});
|
|
14558
14399
|
const templateFiles = listTemplateFiles(templateRoot).filter(
|
|
14559
|
-
(
|
|
14560
|
-
relative2(templateRoot,
|
|
14400
|
+
(path) => !relative2(templateRoot, path).startsWith("terraform/stages/") && !/^terraform\/backend-[^.]+\.hcl$/.test(
|
|
14401
|
+
relative2(templateRoot, path).split("\\").join("/")
|
|
14561
14402
|
)
|
|
14562
14403
|
);
|
|
14563
14404
|
const written = [];
|
|
14564
14405
|
const preserved = [];
|
|
14565
14406
|
for (const templatePath of templateFiles) {
|
|
14566
14407
|
const relativePath = relative2(templateRoot, templatePath);
|
|
14567
|
-
const outputPath =
|
|
14408
|
+
const outputPath = join6(targetDir, relativePath);
|
|
14568
14409
|
let content = applyTemplate(
|
|
14569
|
-
|
|
14410
|
+
readFileSync6(templatePath, "utf8"),
|
|
14570
14411
|
replacements
|
|
14571
14412
|
);
|
|
14572
14413
|
if (relativePath.split("\\").join("/") === "customer/deployment.json") {
|
|
@@ -14574,30 +14415,30 @@ function renderEnterpriseDeployRepoTemplate(options) {
|
|
|
14574
14415
|
}
|
|
14575
14416
|
writeManagedFile(outputPath, content, written, preserved);
|
|
14576
14417
|
}
|
|
14577
|
-
const stageTemplateRoot =
|
|
14578
|
-
const backendTemplatePath =
|
|
14418
|
+
const stageTemplateRoot = join6(templateRoot, "terraform", "stages");
|
|
14419
|
+
const backendTemplatePath = join6(
|
|
14579
14420
|
templateRoot,
|
|
14580
14421
|
"terraform",
|
|
14581
14422
|
"backend-dev.hcl"
|
|
14582
14423
|
);
|
|
14583
14424
|
for (const stage of stages) {
|
|
14584
|
-
const explicitTemplate =
|
|
14585
|
-
const fallbackTemplate =
|
|
14586
|
-
const templatePath =
|
|
14587
|
-
const outputPath =
|
|
14425
|
+
const explicitTemplate = join6(stageTemplateRoot, `${stage}.tfvars`);
|
|
14426
|
+
const fallbackTemplate = join6(stageTemplateRoot, "dev.tfvars");
|
|
14427
|
+
const templatePath = existsSync7(explicitTemplate) ? explicitTemplate : fallbackTemplate;
|
|
14428
|
+
const outputPath = join6(
|
|
14588
14429
|
targetDir,
|
|
14589
14430
|
"terraform",
|
|
14590
14431
|
"stages",
|
|
14591
14432
|
`${stage}.tfvars`
|
|
14592
14433
|
);
|
|
14593
|
-
const content = applyTemplate(
|
|
14434
|
+
const content = applyTemplate(readFileSync6(templatePath, "utf8"), {
|
|
14594
14435
|
...replacements,
|
|
14595
14436
|
STAGE: stage
|
|
14596
14437
|
});
|
|
14597
14438
|
writeManagedFile(outputPath, content, written, preserved);
|
|
14598
|
-
const backendPath =
|
|
14439
|
+
const backendPath = join6(targetDir, "terraform", `backend-${stage}.hcl`);
|
|
14599
14440
|
const backendContent = applyTemplate(
|
|
14600
|
-
|
|
14441
|
+
readFileSync6(backendTemplatePath, "utf8"),
|
|
14601
14442
|
{
|
|
14602
14443
|
...replacements,
|
|
14603
14444
|
STAGE: stage
|
|
@@ -14637,7 +14478,7 @@ function findEnterpriseTemplateRoot() {
|
|
|
14637
14478
|
resolve4(__dirname2, "commands/enterprise/templates/deploy-repo")
|
|
14638
14479
|
];
|
|
14639
14480
|
for (const candidate of candidates) {
|
|
14640
|
-
if (
|
|
14481
|
+
if (existsSync7(join6(candidate, "thinkwork.lock"))) return candidate;
|
|
14641
14482
|
}
|
|
14642
14483
|
throw new Error(
|
|
14643
14484
|
"Enterprise deployment repo template not found. The CLI package may be incomplete."
|
|
@@ -14678,13 +14519,13 @@ function renderCustomerDeploymentJson(content, customerSlug, stages) {
|
|
|
14678
14519
|
}
|
|
14679
14520
|
function listTemplateFiles(root) {
|
|
14680
14521
|
const out = [];
|
|
14681
|
-
for (const entry of
|
|
14682
|
-
const
|
|
14683
|
-
const stat = statSync2(
|
|
14522
|
+
for (const entry of readdirSync(root)) {
|
|
14523
|
+
const path = join6(root, entry);
|
|
14524
|
+
const stat = statSync2(path);
|
|
14684
14525
|
if (stat.isDirectory()) {
|
|
14685
|
-
out.push(...listTemplateFiles(
|
|
14526
|
+
out.push(...listTemplateFiles(path));
|
|
14686
14527
|
} else if (stat.isFile()) {
|
|
14687
|
-
out.push(
|
|
14528
|
+
out.push(path);
|
|
14688
14529
|
}
|
|
14689
14530
|
}
|
|
14690
14531
|
return out.sort();
|
|
@@ -14697,24 +14538,24 @@ function applyTemplate(source, replacements) {
|
|
|
14697
14538
|
return replacements[key];
|
|
14698
14539
|
});
|
|
14699
14540
|
}
|
|
14700
|
-
function writeManagedFile(
|
|
14701
|
-
|
|
14702
|
-
if (
|
|
14703
|
-
const current =
|
|
14541
|
+
function writeManagedFile(path, content, written, preserved) {
|
|
14542
|
+
mkdirSync4(dirname4(path), { recursive: true });
|
|
14543
|
+
if (existsSync7(path)) {
|
|
14544
|
+
const current = readFileSync6(path, "utf8");
|
|
14704
14545
|
if (!current.includes(MANAGED_MARKER)) {
|
|
14705
|
-
preserved.push(
|
|
14546
|
+
preserved.push(path);
|
|
14706
14547
|
return;
|
|
14707
14548
|
}
|
|
14708
14549
|
}
|
|
14709
|
-
|
|
14710
|
-
written.push(
|
|
14550
|
+
writeFileSync4(path, content);
|
|
14551
|
+
written.push(path);
|
|
14711
14552
|
}
|
|
14712
14553
|
|
|
14713
14554
|
// src/commands/enterprise/aws-bootstrap.ts
|
|
14714
14555
|
import { execFileSync } from "child_process";
|
|
14715
|
-
import { mkdtempSync, writeFileSync as
|
|
14556
|
+
import { mkdtempSync, writeFileSync as writeFileSync5 } from "fs";
|
|
14716
14557
|
import { tmpdir } from "os";
|
|
14717
|
-
import { join as
|
|
14558
|
+
import { join as join7 } from "path";
|
|
14718
14559
|
function buildEnterpriseAwsBootstrapPlan(config) {
|
|
14719
14560
|
const oidcProviderArn = `arn:aws:iam::${config.accountId}:oidc-provider/token.actions.githubusercontent.com`;
|
|
14720
14561
|
return {
|
|
@@ -14933,9 +14774,9 @@ var AwsCliEnterpriseBootstrapClient = class {
|
|
|
14933
14774
|
message: `Deploy role ${role.roleName} already exists; updated inline deploy policy ${role.deployPolicyName}.`
|
|
14934
14775
|
};
|
|
14935
14776
|
}
|
|
14936
|
-
const dir = mkdtempSync(
|
|
14937
|
-
const trustPath =
|
|
14938
|
-
|
|
14777
|
+
const dir = mkdtempSync(join7(tmpdir(), "thinkwork-enterprise-role-"));
|
|
14778
|
+
const trustPath = join7(dir, "trust.json");
|
|
14779
|
+
writeFileSync5(trustPath, JSON.stringify(role.trustPolicy));
|
|
14939
14780
|
execFileSync("aws", [
|
|
14940
14781
|
"iam",
|
|
14941
14782
|
"create-role",
|
|
@@ -14953,9 +14794,9 @@ var AwsCliEnterpriseBootstrapClient = class {
|
|
|
14953
14794
|
}
|
|
14954
14795
|
};
|
|
14955
14796
|
function putRolePolicy(role) {
|
|
14956
|
-
const dir = mkdtempSync(
|
|
14957
|
-
const policyPath =
|
|
14958
|
-
|
|
14797
|
+
const dir = mkdtempSync(join7(tmpdir(), "thinkwork-enterprise-policy-"));
|
|
14798
|
+
const policyPath = join7(dir, "policy.json");
|
|
14799
|
+
writeFileSync5(policyPath, JSON.stringify(role.deployPolicy));
|
|
14959
14800
|
execFileSync("aws", [
|
|
14960
14801
|
"iam",
|
|
14961
14802
|
"put-role-policy",
|
|
@@ -15413,14 +15254,14 @@ function printBootstrapSummary(result) {
|
|
|
15413
15254
|
import { resolve as resolve6 } from "path";
|
|
15414
15255
|
|
|
15415
15256
|
// src/commands/enterprise/overlay-schema.ts
|
|
15416
|
-
import { existsSync as
|
|
15417
|
-
import { join as
|
|
15257
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
15258
|
+
import { join as join8, relative as relative3, sep as sep2 } from "path";
|
|
15418
15259
|
function loadEnterpriseOverlayDefinition(repoRoot) {
|
|
15419
|
-
const
|
|
15420
|
-
if (!
|
|
15421
|
-
throw new Error(`Missing customer overlay definition: ${
|
|
15260
|
+
const path = join8(repoRoot, "customer", "deployment.json");
|
|
15261
|
+
if (!existsSync8(path)) {
|
|
15262
|
+
throw new Error(`Missing customer overlay definition: ${path}`);
|
|
15422
15263
|
}
|
|
15423
|
-
const parsed = JSON.parse(
|
|
15264
|
+
const parsed = JSON.parse(readFileSync7(path, "utf8"));
|
|
15424
15265
|
if (parsed.schemaVersion !== 1) {
|
|
15425
15266
|
throw new Error(
|
|
15426
15267
|
`Unsupported customer/deployment.json schemaVersion ${parsed.schemaVersion}`
|
|
@@ -15452,11 +15293,11 @@ function stageOverlay(definition, stage) {
|
|
|
15452
15293
|
}
|
|
15453
15294
|
function readCustomerEvalPack(repoRoot, packName) {
|
|
15454
15295
|
assertPackName(packName, "eval");
|
|
15455
|
-
const
|
|
15456
|
-
if (!
|
|
15457
|
-
throw new Error(`Eval pack "${packName}" is missing: ${
|
|
15296
|
+
const path = join8(repoRoot, "customer", "evals", `${packName}.json`);
|
|
15297
|
+
if (!existsSync8(path)) {
|
|
15298
|
+
throw new Error(`Eval pack "${packName}" is missing: ${path}`);
|
|
15458
15299
|
}
|
|
15459
|
-
const parsed = JSON.parse(
|
|
15300
|
+
const parsed = JSON.parse(readFileSync7(path, "utf8"));
|
|
15460
15301
|
if (!Array.isArray(parsed)) {
|
|
15461
15302
|
throw new Error(`Eval pack "${packName}" must be a JSON array`);
|
|
15462
15303
|
}
|
|
@@ -15466,27 +15307,27 @@ function readCustomerEvalPack(repoRoot, packName) {
|
|
|
15466
15307
|
}
|
|
15467
15308
|
function readJsonSeedPack(repoRoot, packName) {
|
|
15468
15309
|
assertPackName(packName, "seed");
|
|
15469
|
-
const
|
|
15470
|
-
if (!
|
|
15471
|
-
throw new Error(`Seed pack "${packName}" is missing: ${
|
|
15310
|
+
const path = join8(repoRoot, "customer", "seeds", `${packName}.json`);
|
|
15311
|
+
if (!existsSync8(path)) {
|
|
15312
|
+
throw new Error(`Seed pack "${packName}" is missing: ${path}`);
|
|
15472
15313
|
}
|
|
15473
|
-
return JSON.parse(
|
|
15314
|
+
return JSON.parse(readFileSync7(path, "utf8"));
|
|
15474
15315
|
}
|
|
15475
15316
|
function collectOverlayFiles(repoRoot, family, packName) {
|
|
15476
15317
|
assertPackName(packName, family);
|
|
15477
|
-
const root =
|
|
15478
|
-
if (!
|
|
15318
|
+
const root = join8(repoRoot, "customer", family, packName);
|
|
15319
|
+
if (!existsSync8(root) || !statSync3(root).isDirectory()) {
|
|
15479
15320
|
throw new Error(`Overlay pack "${family}/${packName}" is missing: ${root}`);
|
|
15480
15321
|
}
|
|
15481
15322
|
const files = [];
|
|
15482
|
-
walkFiles(root, (
|
|
15483
|
-
const relativePath = relative3(root,
|
|
15323
|
+
walkFiles(root, (path) => {
|
|
15324
|
+
const relativePath = relative3(root, path).split(sep2).join("/");
|
|
15484
15325
|
if (relativePath === ".DS_Store" || relativePath.endsWith("/.DS_Store")) {
|
|
15485
15326
|
return;
|
|
15486
15327
|
}
|
|
15487
15328
|
files.push({
|
|
15488
15329
|
relativePath,
|
|
15489
|
-
content:
|
|
15330
|
+
content: readFileSync7(path, "utf8")
|
|
15490
15331
|
});
|
|
15491
15332
|
});
|
|
15492
15333
|
if (family === "skills" && !files.some((file) => file.relativePath === "SKILL.md")) {
|
|
@@ -15587,12 +15428,12 @@ function assertPackName(value, label) {
|
|
|
15587
15428
|
}
|
|
15588
15429
|
}
|
|
15589
15430
|
function walkFiles(root, visit) {
|
|
15590
|
-
for (const entry of
|
|
15591
|
-
const
|
|
15431
|
+
for (const entry of readdirSync2(root, { withFileTypes: true })) {
|
|
15432
|
+
const path = join8(root, entry.name);
|
|
15592
15433
|
if (entry.isDirectory()) {
|
|
15593
|
-
walkFiles(
|
|
15434
|
+
walkFiles(path, visit);
|
|
15594
15435
|
} else if (entry.isFile()) {
|
|
15595
|
-
visit(
|
|
15436
|
+
visit(path);
|
|
15596
15437
|
}
|
|
15597
15438
|
}
|
|
15598
15439
|
}
|