thinkwork-cli 0.12.6 → 0.12.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js
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
|
);
|
|
@@ -2267,9 +2041,9 @@ function registerInitCommand(program2) {
|
|
|
2267
2041
|
printError("Stage name is required. Pass -s <name> or re-run in an interactive terminal.");
|
|
2268
2042
|
process.exit(1);
|
|
2269
2043
|
}
|
|
2270
|
-
const { input:
|
|
2044
|
+
const { input: input21 } = await import("@inquirer/prompts");
|
|
2271
2045
|
try {
|
|
2272
|
-
stage = await
|
|
2046
|
+
stage = await input21({
|
|
2273
2047
|
message: "Stage name (e.g. dev, staging, prod):",
|
|
2274
2048
|
validate: (v) => validateStage(v).error ?? true
|
|
2275
2049
|
});
|
|
@@ -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
|
});
|
|
@@ -2883,10 +2657,10 @@ async function getTenant(client, id) {
|
|
|
2883
2657
|
async function getTenantBySlug(client, slug) {
|
|
2884
2658
|
return client.fetch(`/api/tenants/by-slug/${encodeURIComponent(slug)}`);
|
|
2885
2659
|
}
|
|
2886
|
-
async function updateTenant(client, id,
|
|
2660
|
+
async function updateTenant(client, id, input21) {
|
|
2887
2661
|
return client.fetch(`/api/tenants/${encodeURIComponent(id)}`, {
|
|
2888
2662
|
method: "PUT",
|
|
2889
|
-
body: JSON.stringify(
|
|
2663
|
+
body: JSON.stringify(input21)
|
|
2890
2664
|
});
|
|
2891
2665
|
}
|
|
2892
2666
|
|
|
@@ -2897,12 +2671,12 @@ __export(admin_keys_exports, {
|
|
|
2897
2671
|
listAdminKeys: () => listAdminKeys,
|
|
2898
2672
|
revokeAdminKey: () => revokeAdminKey
|
|
2899
2673
|
});
|
|
2900
|
-
async function createAdminKey(client, tenantIdOrSlug,
|
|
2674
|
+
async function createAdminKey(client, tenantIdOrSlug, input21 = {}) {
|
|
2901
2675
|
return client.fetch(
|
|
2902
2676
|
`/api/tenants/${encodeURIComponent(tenantIdOrSlug)}/mcp-admin-keys`,
|
|
2903
2677
|
{
|
|
2904
2678
|
method: "POST",
|
|
2905
|
-
body: JSON.stringify(
|
|
2679
|
+
body: JSON.stringify(input21)
|
|
2906
2680
|
}
|
|
2907
2681
|
);
|
|
2908
2682
|
}
|
|
@@ -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}`);
|
|
@@ -3244,7 +2937,7 @@ Examples:
|
|
|
3244
2937
|
).action(
|
|
3245
2938
|
async (nameArg, opts) => {
|
|
3246
2939
|
try {
|
|
3247
|
-
const { input:
|
|
2940
|
+
const { input: input21 } = await import("@inquirer/prompts");
|
|
3248
2941
|
const { stage, api, tenant } = await resolveMcpContext(opts);
|
|
3249
2942
|
let name = nameArg;
|
|
3250
2943
|
if (!name) {
|
|
@@ -3252,7 +2945,7 @@ Examples:
|
|
|
3252
2945
|
printError("Name is required. Pass it as a positional arg.");
|
|
3253
2946
|
process.exit(1);
|
|
3254
2947
|
}
|
|
3255
|
-
name = await
|
|
2948
|
+
name = await input21({ message: "Server name:" });
|
|
3256
2949
|
}
|
|
3257
2950
|
let url = opts.url;
|
|
3258
2951
|
if (!url) {
|
|
@@ -3260,7 +2953,7 @@ Examples:
|
|
|
3260
2953
|
printError("--url is required. Pass it as a flag.");
|
|
3261
2954
|
process.exit(1);
|
|
3262
2955
|
}
|
|
3263
|
-
url = await
|
|
2956
|
+
url = await input21({
|
|
3264
2957
|
message: "MCP server URL:",
|
|
3265
2958
|
validate: (v) => v.startsWith("http://") || v.startsWith("https://") ? true : "URL must start with http:// or https://"
|
|
3266
2959
|
});
|
|
@@ -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("");
|
|
@@ -3420,7 +3113,7 @@ Examples:
|
|
|
3420
3113
|
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--agent <id>", "Agent ID").action(
|
|
3421
3114
|
async (mcpServerArg, opts) => {
|
|
3422
3115
|
try {
|
|
3423
|
-
const { input:
|
|
3116
|
+
const { input: input21 } = await import("@inquirer/prompts");
|
|
3424
3117
|
const { api, tenant } = await resolveMcpContext(opts);
|
|
3425
3118
|
const server = await resolveServer(mcpServerArg, api, tenant.slug);
|
|
3426
3119
|
let agent = opts.agent;
|
|
@@ -3429,7 +3122,7 @@ Examples:
|
|
|
3429
3122
|
printError("--agent is required. Pass it as a flag.");
|
|
3430
3123
|
process.exit(1);
|
|
3431
3124
|
}
|
|
3432
|
-
agent = await
|
|
3125
|
+
agent = await input21({ message: "Agent ID:" });
|
|
3433
3126
|
}
|
|
3434
3127
|
const result = await apiFetch(
|
|
3435
3128
|
api.apiUrl,
|
|
@@ -3452,7 +3145,7 @@ Examples:
|
|
|
3452
3145
|
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--agent <id>", "Agent ID").action(
|
|
3453
3146
|
async (mcpServerArg, opts) => {
|
|
3454
3147
|
try {
|
|
3455
|
-
const { input:
|
|
3148
|
+
const { input: input21 } = await import("@inquirer/prompts");
|
|
3456
3149
|
const { api, tenant } = await resolveMcpContext(opts);
|
|
3457
3150
|
const server = await resolveServer(mcpServerArg, api, tenant.slug);
|
|
3458
3151
|
let agent = opts.agent;
|
|
@@ -3461,7 +3154,7 @@ Examples:
|
|
|
3461
3154
|
printError("--agent is required. Pass it as a flag.");
|
|
3462
3155
|
process.exit(1);
|
|
3463
3156
|
}
|
|
3464
|
-
agent = await
|
|
3157
|
+
agent = await input21({ message: "Agent ID:" });
|
|
3465
3158
|
}
|
|
3466
3159
|
await apiFetch(
|
|
3467
3160
|
api.apiUrl,
|
|
@@ -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);
|
|
@@ -4648,6 +4341,8 @@ var CliUpdateScheduledJobDocument = { "kind": "Document", "definitions": [{ "kin
|
|
|
4648
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" } }] } }] } }] };
|
|
4649
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" } }] } }] } }] };
|
|
4650
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" } }] } }] } }] };
|
|
4344
|
+
var CliInstallSkillDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliInstallSkill" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "input" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "InstallSkillInput" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "installSkill" }, "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": "tenantId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "skillId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "source" } }, { "kind": "Field", "name": { "kind": "Name", "value": "version" } }, { "kind": "Field", "name": { "kind": "Name", "value": "catalogVersion" } }, { "kind": "Field", "name": { "kind": "Name", "value": "enabled" } }, { "kind": "Field", "name": { "kind": "Name", "value": "installedAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updatedAt" } }] } }] } }] };
|
|
4345
|
+
var CliUninstallSkillDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliUninstallSkill" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "tenantId" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "ID" } } } }, { "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "skillId" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "String" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "uninstallSkill" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "tenantId" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "tenantId" } } }, { "kind": "Argument", "name": { "kind": "Name", "value": "skillId" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "skillId" } } }] }] } }] };
|
|
4651
4346
|
var CliTeamsDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliTeams" }, "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": "teams" }, "arguments": [{ "kind": "Argument", "name": { "kind": "Name", "value": "tenantId" }, "value": { "kind": "Variable", "name": { "kind": "Name", "value": "tenantId" } } }], "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" } }, { "kind": "Field", "name": { "kind": "Name", "value": "budgetMonthlyCents" } }, { "kind": "Field", "name": { "kind": "Name", "value": "createdAt" } }] } }] } }] };
|
|
4652
4347
|
var CliTeamDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "query", "name": { "kind": "Name", "value": "CliTeam" }, "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": "team" }, "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": "slug" } }, { "kind": "Field", "name": { "kind": "Name", "value": "description" } }, { "kind": "Field", "name": { "kind": "Name", "value": "type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }, { "kind": "Field", "name": { "kind": "Name", "value": "budgetMonthlyCents" } }, { "kind": "Field", "name": { "kind": "Name", "value": "createdAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "updatedAt" } }, { "kind": "Field", "name": { "kind": "Name", "value": "agents" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "agentId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "role" } }, { "kind": "Field", "name": { "kind": "Name", "value": "joinedAt" } }] } }, { "kind": "Field", "name": { "kind": "Name", "value": "users" }, "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "id" } }, { "kind": "Field", "name": { "kind": "Name", "value": "userId" } }, { "kind": "Field", "name": { "kind": "Name", "value": "role" } }, { "kind": "Field", "name": { "kind": "Name", "value": "joinedAt" } }] } }] } }] } }] };
|
|
4653
4348
|
var CliCreateTeamDocument = { "kind": "Document", "definitions": [{ "kind": "OperationDefinition", "operation": "mutation", "name": { "kind": "Name", "value": "CliCreateTeam" }, "variableDefinitions": [{ "kind": "VariableDefinition", "variable": { "kind": "Variable", "name": { "kind": "Name", "value": "input" } }, "type": { "kind": "NonNullType", "type": { "kind": "NamedType", "name": { "kind": "Name", "value": "CreateTeamInput" } } } }], "selectionSet": { "kind": "SelectionSet", "selections": [{ "kind": "Field", "name": { "kind": "Name", "value": "createTeam" }, "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": "type" } }, { "kind": "Field", "name": { "kind": "Name", "value": "status" } }] } }] } }] };
|
|
@@ -4704,6 +4399,8 @@ var CliUpdateWebhookDocument = { "kind": "Document", "definitions": [{ "kind": "
|
|
|
4704
4399
|
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" } } }] }] } }] };
|
|
4705
4400
|
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" } }] } }] } }] };
|
|
4706
4401
|
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" } }] } }] } }] };
|
|
4402
|
+
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" } }] } }] } }] };
|
|
4403
|
+
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" } }] } }] } }] };
|
|
4707
4404
|
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" } }] } }] } }] };
|
|
4708
4405
|
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" } }] } }] } }] };
|
|
4709
4406
|
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" } }] } }] } }] };
|
|
@@ -4827,6 +4524,8 @@ var documents = {
|
|
|
4827
4524
|
"\n query CliSchedJobTenantBySlug($slug: String!) {\n tenantBySlug(slug: $slug) {\n id\n }\n }\n": CliSchedJobTenantBySlugDocument,
|
|
4828
4525
|
"\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,
|
|
4829
4526
|
"\n query CliSkillTenantBySlug($slug: String!) {\n tenantBySlug(slug: $slug) {\n id\n }\n }\n": CliSkillTenantBySlugDocument,
|
|
4527
|
+
"\n mutation CliInstallSkill($input: InstallSkillInput!) {\n installSkill(input: $input) {\n id\n tenantId\n skillId\n source\n version\n catalogVersion\n enabled\n installedAt\n updatedAt\n }\n }\n": CliInstallSkillDocument,
|
|
4528
|
+
"\n mutation CliUninstallSkill($tenantId: ID!, $skillId: String!) {\n uninstallSkill(tenantId: $tenantId, skillId: $skillId)\n }\n": CliUninstallSkillDocument,
|
|
4830
4529
|
"\n query CliTeams($tenantId: ID!) {\n teams(tenantId: $tenantId) {\n id\n name\n slug\n type\n status\n budgetMonthlyCents\n createdAt\n }\n }\n": CliTeamsDocument,
|
|
4831
4530
|
"\n query CliTeam($id: ID!) {\n team(id: $id) {\n id\n name\n slug\n description\n type\n status\n budgetMonthlyCents\n createdAt\n updatedAt\n agents {\n id\n agentId\n role\n joinedAt\n }\n users {\n id\n userId\n role\n joinedAt\n }\n }\n }\n": CliTeamDocument,
|
|
4832
4531
|
"\n mutation CliCreateTeam($input: CreateTeamInput!) {\n createTeam(input: $input) {\n id\n name\n type\n status\n }\n }\n": CliCreateTeamDocument,
|
|
@@ -4883,6 +4582,8 @@ var documents = {
|
|
|
4883
4582
|
"\n mutation CliDeleteWebhook($id: ID!) {\n deleteWebhook(id: $id)\n }\n": CliDeleteWebhookDocument,
|
|
4884
4583
|
"\n mutation CliRegenerateWebhookToken($id: ID!) {\n regenerateWebhookToken(id: $id) {\n id\n token\n }\n }\n": CliRegenerateWebhookTokenDocument,
|
|
4885
4584
|
"\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,
|
|
4585
|
+
"\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,
|
|
4586
|
+
"\n query CliWebhookForTest($id: ID!) {\n webhook(id: $id) {\n id\n token\n }\n }\n": CliWebhookForTestDocument,
|
|
4886
4587
|
"\n query CliWebhookTenantBySlug($slug: String!) {\n tenantBySlug(slug: $slug) {\n id\n }\n }\n": CliWebhookTenantBySlugDocument,
|
|
4887
4588
|
"\n query CliWikiTenantBySlug($slug: String!) {\n tenantBySlug(slug: $slug) {\n id\n slug\n name\n }\n }\n": CliWikiTenantBySlugDocument,
|
|
4888
4589
|
"\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,
|
|
@@ -5341,15 +5042,15 @@ async function runThreadCreate(title, opts) {
|
|
|
5341
5042
|
// src/commands/thread/update.ts
|
|
5342
5043
|
async function runThreadUpdate(id, opts) {
|
|
5343
5044
|
const ctx = await resolveThreadContext(opts);
|
|
5344
|
-
const
|
|
5345
|
-
if (opts.title !== void 0)
|
|
5346
|
-
if (opts.assignee !== void 0)
|
|
5347
|
-
if (opts.due !== void 0)
|
|
5348
|
-
if (Object.keys(
|
|
5045
|
+
const input21 = {};
|
|
5046
|
+
if (opts.title !== void 0) input21.title = opts.title;
|
|
5047
|
+
if (opts.assignee !== void 0) input21.assigneeId = opts.assignee;
|
|
5048
|
+
if (opts.due !== void 0) input21.dueAt = opts.due;
|
|
5049
|
+
if (Object.keys(input21).length === 0) {
|
|
5349
5050
|
printError("Nothing to update. Pass at least one of --title, --assignee, --due.");
|
|
5350
5051
|
process.exit(1);
|
|
5351
5052
|
}
|
|
5352
|
-
const data = await gqlMutate(ctx.client, UpdateThreadDoc, { id, input:
|
|
5053
|
+
const data = await gqlMutate(ctx.client, UpdateThreadDoc, { id, input: input21 });
|
|
5353
5054
|
const updated = data.updateThread;
|
|
5354
5055
|
if (isJsonMode()) {
|
|
5355
5056
|
printJson(updated);
|
|
@@ -5971,15 +5672,15 @@ async function runLabelCreate(name, opts) {
|
|
|
5971
5672
|
}
|
|
5972
5673
|
async function runLabelUpdate(id, opts) {
|
|
5973
5674
|
const ctx = await resolveLabelContext(opts);
|
|
5974
|
-
const
|
|
5975
|
-
if (opts.name !== void 0)
|
|
5976
|
-
if (opts.color !== void 0)
|
|
5977
|
-
if (opts.description !== void 0)
|
|
5978
|
-
if (Object.keys(
|
|
5675
|
+
const input21 = {};
|
|
5676
|
+
if (opts.name !== void 0) input21.name = opts.name;
|
|
5677
|
+
if (opts.color !== void 0) input21.color = validateColor(opts.color);
|
|
5678
|
+
if (opts.description !== void 0) input21.description = opts.description;
|
|
5679
|
+
if (Object.keys(input21).length === 0) {
|
|
5979
5680
|
printError("Nothing to update. Pass at least one of --name, --color, --description.");
|
|
5980
5681
|
process.exit(1);
|
|
5981
5682
|
}
|
|
5982
|
-
const data = await gqlMutate(ctx.client, UpdateThreadLabelDoc, { id, input:
|
|
5683
|
+
const data = await gqlMutate(ctx.client, UpdateThreadLabelDoc, { id, input: input21 });
|
|
5983
5684
|
const updated = data.updateThreadLabel;
|
|
5984
5685
|
if (isJsonMode()) {
|
|
5985
5686
|
printJson(updated);
|
|
@@ -7017,19 +6718,19 @@ async function runAgentUpdate(id, opts) {
|
|
|
7017
6718
|
if (!systemPrompt && opts.systemPromptFile) {
|
|
7018
6719
|
systemPrompt = await readFile4(opts.systemPromptFile, "utf-8");
|
|
7019
6720
|
}
|
|
7020
|
-
const
|
|
7021
|
-
if (opts.name !== void 0)
|
|
7022
|
-
if (opts.role !== void 0)
|
|
7023
|
-
if (opts.type !== void 0)
|
|
7024
|
-
if (opts.parent !== void 0)
|
|
7025
|
-
if (opts.reportsTo !== void 0)
|
|
7026
|
-
if (systemPrompt !== void 0)
|
|
7027
|
-
if (opts.model !== void 0)
|
|
7028
|
-
if (Object.keys(
|
|
6721
|
+
const input21 = {};
|
|
6722
|
+
if (opts.name !== void 0) input21.name = opts.name;
|
|
6723
|
+
if (opts.role !== void 0) input21.role = opts.role;
|
|
6724
|
+
if (opts.type !== void 0) input21.type = parseEnum(opts.type, TYPE_BY_NAME, "--type");
|
|
6725
|
+
if (opts.parent !== void 0) input21.parentAgentId = opts.parent;
|
|
6726
|
+
if (opts.reportsTo !== void 0) input21.reportsTo = opts.reportsTo;
|
|
6727
|
+
if (systemPrompt !== void 0) input21.systemPrompt = systemPrompt;
|
|
6728
|
+
if (opts.model !== void 0) input21.runtimeConfig = { model: opts.model };
|
|
6729
|
+
if (Object.keys(input21).length === 0) {
|
|
7029
6730
|
printError("Nothing to update. Pass at least one field flag.");
|
|
7030
6731
|
process.exit(1);
|
|
7031
6732
|
}
|
|
7032
|
-
const data = await gqlMutate(ctx.client, UpdateAgentDoc, { id, input:
|
|
6733
|
+
const data = await gqlMutate(ctx.client, UpdateAgentDoc, { id, input: input21 });
|
|
7033
6734
|
if (isJsonMode()) {
|
|
7034
6735
|
printJson(data.updateAgent);
|
|
7035
6736
|
return;
|
|
@@ -7485,7 +7186,7 @@ Examples:
|
|
|
7485
7186
|
}
|
|
7486
7187
|
|
|
7487
7188
|
// src/commands/computer.ts
|
|
7488
|
-
import
|
|
7189
|
+
import chalk12 from "chalk";
|
|
7489
7190
|
async function resolveComputerContext(opts) {
|
|
7490
7191
|
const stage = await resolveStage({ flag: opts.stage });
|
|
7491
7192
|
const api = resolveApiConfig(stage);
|
|
@@ -7525,7 +7226,7 @@ function printMigrationReport(response) {
|
|
|
7525
7226
|
if (!response.report) return;
|
|
7526
7227
|
const summary = response.report.summary ?? {};
|
|
7527
7228
|
console.log("");
|
|
7528
|
-
console.log(
|
|
7229
|
+
console.log(chalk12.bold(" Summary"));
|
|
7529
7230
|
for (const [status, count] of Object.entries(summary)) {
|
|
7530
7231
|
if (!count) continue;
|
|
7531
7232
|
console.log(` ${status.padEnd(28)} ${count}`);
|
|
@@ -7606,7 +7307,7 @@ function registerComputerCommand(program2) {
|
|
|
7606
7307
|
if (response.status === 409 && response.body.blockers) {
|
|
7607
7308
|
if (!isJsonMode()) {
|
|
7608
7309
|
console.log("");
|
|
7609
|
-
console.log(
|
|
7310
|
+
console.log(chalk12.bold(" Blockers"));
|
|
7610
7311
|
console.log(JSON.stringify(response.body.blockers, null, 2));
|
|
7611
7312
|
}
|
|
7612
7313
|
}
|
|
@@ -7663,7 +7364,7 @@ function registerComputerCommand(program2) {
|
|
|
7663
7364
|
const tenantId = resolveTenantId(opts);
|
|
7664
7365
|
const computerId = resolveComputerId(opts);
|
|
7665
7366
|
const taskType = resolveTaskType(opts);
|
|
7666
|
-
const
|
|
7367
|
+
const input21 = taskType === "workspace_file_write" ? { path: opts.path, content: opts.content } : void 0;
|
|
7667
7368
|
if (!isJsonMode()) printHeader("computer task enqueue", stage);
|
|
7668
7369
|
const response = await apiFetchRaw(
|
|
7669
7370
|
api.apiUrl,
|
|
@@ -7675,7 +7376,7 @@ function registerComputerCommand(program2) {
|
|
|
7675
7376
|
tenantId,
|
|
7676
7377
|
computerId,
|
|
7677
7378
|
taskType,
|
|
7678
|
-
input:
|
|
7379
|
+
input: input21,
|
|
7679
7380
|
idempotencyKey: opts.idempotencyKey
|
|
7680
7381
|
})
|
|
7681
7382
|
}
|
|
@@ -7947,15 +7648,15 @@ async function runTemplateUpdate(id, opts) {
|
|
|
7947
7648
|
"--system-prompt-file is not yet wired in the CLI \u2014 edit prompt files via the admin UI."
|
|
7948
7649
|
);
|
|
7949
7650
|
}
|
|
7950
|
-
const
|
|
7951
|
-
if (opts.name !== void 0)
|
|
7952
|
-
if (opts.model !== void 0)
|
|
7953
|
-
if (opts.description !== void 0)
|
|
7954
|
-
if (Object.keys(
|
|
7651
|
+
const input21 = {};
|
|
7652
|
+
if (opts.name !== void 0) input21.name = opts.name;
|
|
7653
|
+
if (opts.model !== void 0) input21.model = opts.model;
|
|
7654
|
+
if (opts.description !== void 0) input21.description = opts.description;
|
|
7655
|
+
if (Object.keys(input21).length === 0) {
|
|
7955
7656
|
printError("Nothing to update. Pass at least one of --name, --model, --description.");
|
|
7956
7657
|
process.exit(1);
|
|
7957
7658
|
}
|
|
7958
|
-
const data = await gqlMutate(ctx.client, UpdateAgentTemplateDoc, { id, input:
|
|
7659
|
+
const data = await gqlMutate(ctx.client, UpdateAgentTemplateDoc, { id, input: input21 });
|
|
7959
7660
|
if (isJsonMode()) {
|
|
7960
7661
|
printJson(data.updateAgentTemplate);
|
|
7961
7662
|
return;
|
|
@@ -8277,15 +7978,15 @@ async function runTenantUpdate(id, opts) {
|
|
|
8277
7978
|
printMissingApiSessionError(stage, false);
|
|
8278
7979
|
process.exit(1);
|
|
8279
7980
|
}
|
|
8280
|
-
const
|
|
8281
|
-
if (opts.name !== void 0)
|
|
8282
|
-
if (opts.plan !== void 0)
|
|
8283
|
-
if (opts.issuePrefix !== void 0)
|
|
8284
|
-
if (Object.keys(
|
|
7981
|
+
const input21 = {};
|
|
7982
|
+
if (opts.name !== void 0) input21.name = opts.name;
|
|
7983
|
+
if (opts.plan !== void 0) input21.plan = opts.plan;
|
|
7984
|
+
if (opts.issuePrefix !== void 0) input21.issuePrefix = opts.issuePrefix;
|
|
7985
|
+
if (Object.keys(input21).length === 0) {
|
|
8285
7986
|
printError("Nothing to update. Pass at least one of --name, --plan, --issue-prefix.");
|
|
8286
7987
|
process.exit(1);
|
|
8287
7988
|
}
|
|
8288
|
-
const data = await gqlMutate(client, UpdateTenantDoc, { id, input:
|
|
7989
|
+
const data = await gqlMutate(client, UpdateTenantDoc, { id, input: input21 });
|
|
8289
7990
|
if (isJsonMode()) {
|
|
8290
7991
|
printJson(data.updateTenant);
|
|
8291
7992
|
return;
|
|
@@ -8323,18 +8024,18 @@ async function runTenantSettingsSet(tenantArg, opts) {
|
|
|
8323
8024
|
stage: opts.stage,
|
|
8324
8025
|
tenant: tenantArg
|
|
8325
8026
|
});
|
|
8326
|
-
const
|
|
8327
|
-
if (opts.defaultModel !== void 0)
|
|
8027
|
+
const input21 = {};
|
|
8028
|
+
if (opts.defaultModel !== void 0) input21.defaultModel = opts.defaultModel;
|
|
8328
8029
|
if (opts.monthlyBudgetUsd !== void 0) {
|
|
8329
|
-
|
|
8030
|
+
input21.budgetMonthlyCents = Math.round(Number.parseFloat(opts.monthlyBudgetUsd) * 100);
|
|
8330
8031
|
}
|
|
8331
|
-
if (opts.maxAgents !== void 0)
|
|
8032
|
+
if (opts.maxAgents !== void 0) input21.maxAgents = Number.parseInt(opts.maxAgents, 10);
|
|
8332
8033
|
if (opts.autoCloseAfterDays !== void 0) {
|
|
8333
|
-
|
|
8034
|
+
input21.autoCloseThreadMinutes = Math.round(Number.parseFloat(opts.autoCloseAfterDays) * 60 * 24);
|
|
8334
8035
|
}
|
|
8335
8036
|
const features = parseFeatureFlags(opts.feature);
|
|
8336
|
-
if (features !== void 0)
|
|
8337
|
-
if (Object.keys(
|
|
8037
|
+
if (features !== void 0) input21.features = JSON.stringify(features);
|
|
8038
|
+
if (Object.keys(input21).length === 0) {
|
|
8338
8039
|
printError(
|
|
8339
8040
|
"Nothing to set. Pass at least one of --default-model, --monthly-budget-usd, --max-agents, --auto-close-after-days, --feature."
|
|
8340
8041
|
);
|
|
@@ -8342,7 +8043,7 @@ async function runTenantSettingsSet(tenantArg, opts) {
|
|
|
8342
8043
|
}
|
|
8343
8044
|
const data = await gqlMutate(ctx.client, UpdateTenantSettingsDoc, {
|
|
8344
8045
|
tenantId: ctx.tenantId,
|
|
8345
|
-
input:
|
|
8046
|
+
input: input21
|
|
8346
8047
|
});
|
|
8347
8048
|
if (isJsonMode()) {
|
|
8348
8049
|
printJson(data.updateTenantSettings);
|
|
@@ -8587,14 +8288,14 @@ async function runMemberInvite(email, opts) {
|
|
|
8587
8288
|
}
|
|
8588
8289
|
async function runMemberUpdate(memberId, opts) {
|
|
8589
8290
|
const ctx = await resolveMemberContext(opts);
|
|
8590
|
-
const
|
|
8591
|
-
if (opts.role !== void 0)
|
|
8592
|
-
if (opts.status !== void 0)
|
|
8593
|
-
if (Object.keys(
|
|
8291
|
+
const input21 = {};
|
|
8292
|
+
if (opts.role !== void 0) input21.role = opts.role;
|
|
8293
|
+
if (opts.status !== void 0) input21.status = opts.status;
|
|
8294
|
+
if (Object.keys(input21).length === 0) {
|
|
8594
8295
|
printError("Nothing to update. Pass at least one of --role, --status.");
|
|
8595
8296
|
process.exit(1);
|
|
8596
8297
|
}
|
|
8597
|
-
const data = await gqlMutate(ctx.client, UpdateTenantMemberDoc, { id: memberId, input:
|
|
8298
|
+
const data = await gqlMutate(ctx.client, UpdateTenantMemberDoc, { id: memberId, input: input21 });
|
|
8598
8299
|
const updated = data.updateTenantMember;
|
|
8599
8300
|
if (isJsonMode()) {
|
|
8600
8301
|
printJson(updated);
|
|
@@ -8878,18 +8579,18 @@ async function runTeamCreate(name, opts) {
|
|
|
8878
8579
|
}
|
|
8879
8580
|
async function runTeamUpdate(id, opts) {
|
|
8880
8581
|
const ctx = await resolveTeamContext(opts);
|
|
8881
|
-
const
|
|
8882
|
-
if (opts.name !== void 0)
|
|
8883
|
-
if (opts.description !== void 0)
|
|
8884
|
-
if (opts.status !== void 0)
|
|
8582
|
+
const input21 = {};
|
|
8583
|
+
if (opts.name !== void 0) input21.name = opts.name;
|
|
8584
|
+
if (opts.description !== void 0) input21.description = opts.description;
|
|
8585
|
+
if (opts.status !== void 0) input21.status = opts.status;
|
|
8885
8586
|
if (opts.budgetUsd !== void 0) {
|
|
8886
|
-
|
|
8587
|
+
input21.budgetMonthlyCents = Math.round(Number.parseFloat(opts.budgetUsd) * 100);
|
|
8887
8588
|
}
|
|
8888
|
-
if (Object.keys(
|
|
8589
|
+
if (Object.keys(input21).length === 0) {
|
|
8889
8590
|
printError("Nothing to update. Pass at least one of --name, --description, --status, --budget-usd.");
|
|
8890
8591
|
process.exit(1);
|
|
8891
8592
|
}
|
|
8892
|
-
const data = await gqlMutate(ctx.client, UpdateTeamDoc, { id, input:
|
|
8593
|
+
const data = await gqlMutate(ctx.client, UpdateTeamDoc, { id, input: input21 });
|
|
8893
8594
|
if (isJsonMode()) {
|
|
8894
8595
|
printJson(data.updateTeam);
|
|
8895
8596
|
return;
|
|
@@ -9213,14 +8914,14 @@ async function runKbCreate(name, opts) {
|
|
|
9213
8914
|
}
|
|
9214
8915
|
async function runKbUpdate(id, opts) {
|
|
9215
8916
|
const ctx = await resolveKbContext(opts);
|
|
9216
|
-
const
|
|
9217
|
-
if (opts.name !== void 0)
|
|
9218
|
-
if (opts.description !== void 0)
|
|
9219
|
-
if (Object.keys(
|
|
8917
|
+
const input21 = {};
|
|
8918
|
+
if (opts.name !== void 0) input21.name = opts.name;
|
|
8919
|
+
if (opts.description !== void 0) input21.description = opts.description;
|
|
8920
|
+
if (Object.keys(input21).length === 0) {
|
|
9220
8921
|
printError("Nothing to update. Pass at least one of --name, --description.");
|
|
9221
8922
|
process.exit(1);
|
|
9222
8923
|
}
|
|
9223
|
-
const data = await gqlMutate(ctx.client, UpdateKBDoc, { id, input:
|
|
8924
|
+
const data = await gqlMutate(ctx.client, UpdateKBDoc, { id, input: input21 });
|
|
9224
8925
|
if (isJsonMode()) {
|
|
9225
8926
|
printJson(data.updateKnowledgeBase);
|
|
9226
8927
|
return;
|
|
@@ -9669,12 +9370,12 @@ async function runRoutineCreate(name, opts) {
|
|
|
9669
9370
|
}
|
|
9670
9371
|
async function runRoutineUpdate(id, opts) {
|
|
9671
9372
|
const ctx = await resolveRoutineContext(opts);
|
|
9672
|
-
const
|
|
9673
|
-
if (opts.name !== void 0)
|
|
9674
|
-
if (opts.status !== void 0)
|
|
9675
|
-
if (opts.agent !== void 0)
|
|
9676
|
-
if (opts.team !== void 0)
|
|
9677
|
-
if (Object.keys(
|
|
9373
|
+
const input21 = {};
|
|
9374
|
+
if (opts.name !== void 0) input21.name = opts.name;
|
|
9375
|
+
if (opts.status !== void 0) input21.status = opts.status;
|
|
9376
|
+
if (opts.agent !== void 0) input21.agentId = opts.agent;
|
|
9377
|
+
if (opts.team !== void 0) input21.teamId = opts.team;
|
|
9378
|
+
if (Object.keys(input21).length === 0) {
|
|
9678
9379
|
printError("Nothing to update.");
|
|
9679
9380
|
process.exit(1);
|
|
9680
9381
|
}
|
|
@@ -9684,7 +9385,7 @@ async function runRoutineUpdate(id, opts) {
|
|
|
9684
9385
|
);
|
|
9685
9386
|
process.exit(1);
|
|
9686
9387
|
}
|
|
9687
|
-
const data = await gqlMutate(ctx.client, UpdateRoutineDoc, { id, input:
|
|
9388
|
+
const data = await gqlMutate(ctx.client, UpdateRoutineDoc, { id, input: input21 });
|
|
9688
9389
|
if (isJsonMode()) {
|
|
9689
9390
|
printJson(data.updateRoutine);
|
|
9690
9391
|
return;
|
|
@@ -10164,18 +9865,18 @@ async function runSchedDelete(id, opts) {
|
|
|
10164
9865
|
}
|
|
10165
9866
|
async function runSchedUpdate(id, opts) {
|
|
10166
9867
|
const ctx = await resolveSchedContext(opts);
|
|
10167
|
-
const
|
|
10168
|
-
if (opts.name !== void 0)
|
|
10169
|
-
if (opts.description !== void 0)
|
|
10170
|
-
if (opts.prompt !== void 0)
|
|
9868
|
+
const input21 = {};
|
|
9869
|
+
if (opts.name !== void 0) input21.name = opts.name;
|
|
9870
|
+
if (opts.description !== void 0) input21.description = opts.description;
|
|
9871
|
+
if (opts.prompt !== void 0) input21.prompt = opts.prompt;
|
|
10171
9872
|
if (opts.schedule !== void 0) {
|
|
10172
|
-
|
|
10173
|
-
|
|
9873
|
+
input21.scheduleExpression = opts.schedule;
|
|
9874
|
+
input21.scheduleType = opts.schedule.trim().startsWith("cron(") ? "cron" : "rate";
|
|
10174
9875
|
}
|
|
10175
|
-
if (opts.timezone !== void 0)
|
|
9876
|
+
if (opts.timezone !== void 0) input21.timezone = opts.timezone;
|
|
10176
9877
|
if (opts.payload !== void 0) {
|
|
10177
9878
|
try {
|
|
10178
|
-
|
|
9879
|
+
input21.config = JSON.stringify(JSON.parse(opts.payload));
|
|
10179
9880
|
} catch (err) {
|
|
10180
9881
|
printError(`--payload is not valid JSON: ${err.message}`);
|
|
10181
9882
|
process.exit(1);
|
|
@@ -10185,9 +9886,9 @@ async function runSchedUpdate(id, opts) {
|
|
|
10185
9886
|
printError("--enable and --disable are mutually exclusive.");
|
|
10186
9887
|
process.exit(1);
|
|
10187
9888
|
}
|
|
10188
|
-
if (opts.enable)
|
|
10189
|
-
if (opts.disable)
|
|
10190
|
-
if (Object.keys(
|
|
9889
|
+
if (opts.enable) input21.enabled = true;
|
|
9890
|
+
if (opts.disable) input21.enabled = false;
|
|
9891
|
+
if (Object.keys(input21).length === 0) {
|
|
10191
9892
|
printError(
|
|
10192
9893
|
"Nothing to update. Pass at least one of --schedule, --timezone, --payload, --enable/--disable, --name, --description, --prompt."
|
|
10193
9894
|
);
|
|
@@ -10195,7 +9896,7 @@ async function runSchedUpdate(id, opts) {
|
|
|
10195
9896
|
}
|
|
10196
9897
|
const data = await gqlMutate(ctx.client, UpdateScheduledJobDoc, {
|
|
10197
9898
|
id,
|
|
10198
|
-
input:
|
|
9899
|
+
input: input21
|
|
10199
9900
|
});
|
|
10200
9901
|
if (isJsonMode()) {
|
|
10201
9902
|
printJson(data.updateScheduledJob);
|
|
@@ -10734,6 +10435,28 @@ var WebhookDeliveriesDoc = graphql(`
|
|
|
10734
10435
|
}
|
|
10735
10436
|
}
|
|
10736
10437
|
`);
|
|
10438
|
+
var TestWebhookDoc = graphql(`
|
|
10439
|
+
mutation CliTestWebhook($id: ID!) {
|
|
10440
|
+
testWebhook(id: $id) {
|
|
10441
|
+
id
|
|
10442
|
+
webhookId
|
|
10443
|
+
tenantId
|
|
10444
|
+
receivedAt
|
|
10445
|
+
resolutionStatus
|
|
10446
|
+
signatureStatus
|
|
10447
|
+
statusCode
|
|
10448
|
+
bodyPreview
|
|
10449
|
+
}
|
|
10450
|
+
}
|
|
10451
|
+
`);
|
|
10452
|
+
var WebhookForTestDoc = graphql(`
|
|
10453
|
+
query CliWebhookForTest($id: ID!) {
|
|
10454
|
+
webhook(id: $id) {
|
|
10455
|
+
id
|
|
10456
|
+
token
|
|
10457
|
+
}
|
|
10458
|
+
}
|
|
10459
|
+
`);
|
|
10737
10460
|
var WebhookTenantBySlugDoc = graphql(`
|
|
10738
10461
|
query CliWebhookTenantBySlug($slug: String!) {
|
|
10739
10462
|
tenantBySlug(slug: $slug) {
|
|
@@ -10884,25 +10607,25 @@ async function runWebhookCreate(name, opts) {
|
|
|
10884
10607
|
}
|
|
10885
10608
|
async function runWebhookUpdate(id, opts) {
|
|
10886
10609
|
const ctx = await resolveWebhookContext(opts);
|
|
10887
|
-
const
|
|
10888
|
-
if (opts.targetType !== void 0)
|
|
10610
|
+
const input21 = {};
|
|
10611
|
+
if (opts.targetType !== void 0) input21.targetType = opts.targetType.toUpperCase();
|
|
10889
10612
|
if (opts.targetId !== void 0) {
|
|
10890
10613
|
const tt = (opts.targetType ?? "").toUpperCase();
|
|
10891
|
-
if (tt === "AGENT")
|
|
10892
|
-
else if (tt === "ROUTINE")
|
|
10614
|
+
if (tt === "AGENT") input21.agentId = opts.targetId;
|
|
10615
|
+
else if (tt === "ROUTINE") input21.routineId = opts.targetId;
|
|
10893
10616
|
else {
|
|
10894
10617
|
printError("--target-id requires --target-type <AGENT|ROUTINE> on the same call.");
|
|
10895
10618
|
process.exit(1);
|
|
10896
10619
|
}
|
|
10897
10620
|
}
|
|
10898
|
-
if (opts.rateLimit !== void 0)
|
|
10899
|
-
if (opts.enable)
|
|
10900
|
-
if (opts.disable)
|
|
10901
|
-
if (Object.keys(
|
|
10621
|
+
if (opts.rateLimit !== void 0) input21.rateLimit = Number.parseInt(opts.rateLimit, 10);
|
|
10622
|
+
if (opts.enable) input21.enabled = true;
|
|
10623
|
+
if (opts.disable) input21.enabled = false;
|
|
10624
|
+
if (Object.keys(input21).length === 0) {
|
|
10902
10625
|
printError("Nothing to update.");
|
|
10903
10626
|
process.exit(1);
|
|
10904
10627
|
}
|
|
10905
|
-
const data = await gqlMutate(ctx.client, UpdateWebhookDoc, { id, input:
|
|
10628
|
+
const data = await gqlMutate(ctx.client, UpdateWebhookDoc, { id, input: input21 });
|
|
10906
10629
|
if (isJsonMode()) {
|
|
10907
10630
|
printJson(data.updateWebhook);
|
|
10908
10631
|
return;
|
|
@@ -11011,12 +10734,39 @@ async function runWebhookDeliveries(id, opts) {
|
|
|
11011
10734
|
]
|
|
11012
10735
|
);
|
|
11013
10736
|
}
|
|
11014
|
-
function
|
|
11015
|
-
|
|
11016
|
-
|
|
11017
|
-
|
|
10737
|
+
async function runWebhookTest(id, _opts) {
|
|
10738
|
+
const ctx = await resolveWebhookContext(_opts);
|
|
10739
|
+
const data = await gqlMutate(ctx.client, TestWebhookDoc, { id });
|
|
10740
|
+
const delivery = data.testWebhook;
|
|
10741
|
+
let curlHint = null;
|
|
10742
|
+
try {
|
|
10743
|
+
const tokenData = await gqlQuery(ctx.client, WebhookForTestDoc, { id });
|
|
10744
|
+
if (tokenData.webhook?.token) {
|
|
10745
|
+
const { resolveApiConfig: resolveApiConfig2 } = await import("./api-client-MC4NOZ4L.js");
|
|
10746
|
+
const api = resolveApiConfig2(ctx.stage);
|
|
10747
|
+
if (api?.apiUrl) {
|
|
10748
|
+
const base = api.apiUrl.replace(/\/$/, "");
|
|
10749
|
+
curlHint = `${base}/webhooks/${tokenData.webhook.token}`;
|
|
10750
|
+
}
|
|
10751
|
+
}
|
|
10752
|
+
} catch {
|
|
10753
|
+
}
|
|
10754
|
+
if (isJsonMode()) {
|
|
10755
|
+
printJson({ delivery, publicUrl: curlHint });
|
|
10756
|
+
return;
|
|
10757
|
+
}
|
|
10758
|
+
printSuccess(
|
|
10759
|
+
`Recorded synthetic delivery ${delivery.id} (resolution=${delivery.resolutionStatus}).`
|
|
11018
10760
|
);
|
|
11019
|
-
|
|
10761
|
+
if (curlHint) {
|
|
10762
|
+
console.log("");
|
|
10763
|
+
console.log(" For end-to-end reachability check, curl the public URL:");
|
|
10764
|
+
console.log(
|
|
10765
|
+
` curl -X POST -H 'content-type: application/json' \\
|
|
10766
|
+
-d '{"hello":"world"}' \\
|
|
10767
|
+
${curlHint}`
|
|
10768
|
+
);
|
|
10769
|
+
}
|
|
11020
10770
|
}
|
|
11021
10771
|
function registerWebhookCommand(program2) {
|
|
11022
10772
|
const wh = program2.command("webhook").alias("webhooks").description("Manage inbound webhooks that dispatch to agents or routines.");
|
|
@@ -11031,16 +10781,21 @@ Examples:
|
|
|
11031
10781
|
).action(runWebhookCreate);
|
|
11032
10782
|
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);
|
|
11033
10783
|
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);
|
|
11034
|
-
wh.command("test <id>").description(
|
|
10784
|
+
wh.command("test <id>").description(
|
|
10785
|
+
"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."
|
|
10786
|
+
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").action(runWebhookTest);
|
|
11035
10787
|
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);
|
|
11036
10788
|
wh.command("deliveries <id>").description(
|
|
11037
10789
|
"Show recent delivery attempts for a webhook (newest first). Default 25, max 500."
|
|
11038
10790
|
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--limit <n>", "Max rows (1-500)", "25").action(runWebhookDeliveries);
|
|
11039
10791
|
}
|
|
11040
10792
|
|
|
10793
|
+
// src/commands/skill.ts
|
|
10794
|
+
import { input as input18 } from "@inquirer/prompts";
|
|
10795
|
+
|
|
11041
10796
|
// src/lib/plugin-zip.ts
|
|
11042
10797
|
import { createReadStream, promises as fsp, statSync } from "fs";
|
|
11043
|
-
import { basename, join as
|
|
10798
|
+
import { basename, join as join5, relative, resolve as resolve3, sep } from "path";
|
|
11044
10799
|
import JSZip from "jszip";
|
|
11045
10800
|
var PluginZipError = class extends Error {
|
|
11046
10801
|
constructor(message, kind) {
|
|
@@ -11067,7 +10822,7 @@ async function buildPluginZip(pluginDir) {
|
|
|
11067
10822
|
"missing-directory"
|
|
11068
10823
|
);
|
|
11069
10824
|
}
|
|
11070
|
-
const manifestPath =
|
|
10825
|
+
const manifestPath = join5(root, "plugin.json");
|
|
11071
10826
|
let manifestRaw;
|
|
11072
10827
|
try {
|
|
11073
10828
|
manifestRaw = await fsp.readFile(manifestPath, "utf8");
|
|
@@ -11146,7 +10901,7 @@ async function walkDir(rootDir, currentDir) {
|
|
|
11146
10901
|
const out = [];
|
|
11147
10902
|
const dirents = await fsp.readdir(currentDir, { withFileTypes: true });
|
|
11148
10903
|
for (const ent of dirents) {
|
|
11149
|
-
const abs =
|
|
10904
|
+
const abs = join5(currentDir, ent.name);
|
|
11150
10905
|
const rel = relative(rootDir, abs);
|
|
11151
10906
|
if (ent.name === ".git" || ent.name === ".DS_Store" || ent.name === "node_modules") {
|
|
11152
10907
|
continue;
|
|
@@ -11178,12 +10933,12 @@ function hasParentSegment(relPath) {
|
|
|
11178
10933
|
}
|
|
11179
10934
|
|
|
11180
10935
|
// src/lib/plugin-push.ts
|
|
11181
|
-
async function pushPluginZip(
|
|
11182
|
-
const base =
|
|
10936
|
+
async function pushPluginZip(input21) {
|
|
10937
|
+
const base = input21.apiUrl.replace(/\/+$/, "");
|
|
11183
10938
|
const presignRes = await fetch(`${base}/api/plugins/presign`, {
|
|
11184
10939
|
method: "POST",
|
|
11185
|
-
headers: withJson(
|
|
11186
|
-
body: JSON.stringify({ fileName:
|
|
10940
|
+
headers: withJson(input21.headers),
|
|
10941
|
+
body: JSON.stringify({ fileName: input21.fileName })
|
|
11187
10942
|
});
|
|
11188
10943
|
if (!presignRes.ok) {
|
|
11189
10944
|
throw new Error(`presign failed: ${await describeHttpError(presignRes)}`);
|
|
@@ -11197,14 +10952,14 @@ async function pushPluginZip(input20) {
|
|
|
11197
10952
|
const putRes = await fetch(presign.uploadUrl, {
|
|
11198
10953
|
method: "PUT",
|
|
11199
10954
|
headers: { "Content-Type": "application/zip" },
|
|
11200
|
-
body: new Uint8Array(
|
|
10955
|
+
body: new Uint8Array(input21.zipBuffer)
|
|
11201
10956
|
});
|
|
11202
10957
|
if (!putRes.ok) {
|
|
11203
10958
|
throw new Error(`S3 PUT failed: HTTP ${putRes.status}`);
|
|
11204
10959
|
}
|
|
11205
10960
|
const installRes = await fetch(`${base}/api/plugins/upload`, {
|
|
11206
10961
|
method: "POST",
|
|
11207
|
-
headers: withJson(
|
|
10962
|
+
headers: withJson(input21.headers),
|
|
11208
10963
|
body: JSON.stringify({ s3Key: presign.s3Key })
|
|
11209
10964
|
});
|
|
11210
10965
|
const installBody = await installRes.json().catch(() => ({}));
|
|
@@ -11267,14 +11022,59 @@ var SkillTenantBySlugDoc = graphql(`
|
|
|
11267
11022
|
}
|
|
11268
11023
|
}
|
|
11269
11024
|
`);
|
|
11025
|
+
var InstallSkillDoc = graphql(`
|
|
11026
|
+
mutation CliInstallSkill($input: InstallSkillInput!) {
|
|
11027
|
+
installSkill(input: $input) {
|
|
11028
|
+
id
|
|
11029
|
+
tenantId
|
|
11030
|
+
skillId
|
|
11031
|
+
source
|
|
11032
|
+
version
|
|
11033
|
+
catalogVersion
|
|
11034
|
+
enabled
|
|
11035
|
+
installedAt
|
|
11036
|
+
updatedAt
|
|
11037
|
+
}
|
|
11038
|
+
}
|
|
11039
|
+
`);
|
|
11040
|
+
var UninstallSkillDoc = graphql(`
|
|
11041
|
+
mutation CliUninstallSkill($tenantId: ID!, $skillId: String!) {
|
|
11042
|
+
uninstallSkill(tenantId: $tenantId, skillId: $skillId)
|
|
11043
|
+
}
|
|
11044
|
+
`);
|
|
11270
11045
|
async function resolveSkillContext(opts) {
|
|
11271
11046
|
const region = opts.region ?? "us-east-1";
|
|
11272
11047
|
const stage = await resolveStage({ flag: opts.stage, region });
|
|
11273
11048
|
const session = loadStageSession(stage);
|
|
11274
|
-
const { client } = await getGqlClient({ stage, region });
|
|
11049
|
+
const { client, tenantSlug: ctxSlug } = await getGqlClient({ stage, region });
|
|
11275
11050
|
if (!session) {
|
|
11276
11051
|
}
|
|
11277
|
-
return { stage, region, client };
|
|
11052
|
+
return { stage, region, client, session, ctxSlug };
|
|
11053
|
+
}
|
|
11054
|
+
async function resolveTenantIdForSkill(ctx, tenantOpt) {
|
|
11055
|
+
const flagOrEnv = tenantOpt ?? process.env.THINKWORK_TENANT;
|
|
11056
|
+
if (flagOrEnv) {
|
|
11057
|
+
if (ctx.session?.tenantSlug === flagOrEnv && ctx.session.tenantId) {
|
|
11058
|
+
return ctx.session.tenantId;
|
|
11059
|
+
}
|
|
11060
|
+
const data = await gqlQuery(ctx.client, SkillTenantBySlugDoc, {
|
|
11061
|
+
slug: flagOrEnv
|
|
11062
|
+
});
|
|
11063
|
+
if (!data.tenantBySlug) {
|
|
11064
|
+
printError(`Tenant "${flagOrEnv}" not found.`);
|
|
11065
|
+
process.exit(1);
|
|
11066
|
+
}
|
|
11067
|
+
return data.tenantBySlug.id;
|
|
11068
|
+
}
|
|
11069
|
+
if (ctx.session?.tenantId) return ctx.session.tenantId;
|
|
11070
|
+
if (ctx.ctxSlug) {
|
|
11071
|
+
const data = await gqlQuery(ctx.client, SkillTenantBySlugDoc, {
|
|
11072
|
+
slug: ctx.ctxSlug
|
|
11073
|
+
});
|
|
11074
|
+
if (data.tenantBySlug) return data.tenantBySlug.id;
|
|
11075
|
+
}
|
|
11076
|
+
printMissingApiSessionError(ctx.stage, ctx.session !== null);
|
|
11077
|
+
process.exit(1);
|
|
11278
11078
|
}
|
|
11279
11079
|
async function runSkillCatalog(opts) {
|
|
11280
11080
|
const ctx = await resolveSkillContext(opts);
|
|
@@ -11339,13 +11139,59 @@ async function runSkillList(opts) {
|
|
|
11339
11139
|
]
|
|
11340
11140
|
);
|
|
11341
11141
|
}
|
|
11342
|
-
function
|
|
11142
|
+
async function runSkillInstall(slug, opts) {
|
|
11143
|
+
const ctx = await resolveSkillContext(opts);
|
|
11144
|
+
const tenantId = await resolveTenantIdForSkill(ctx, opts.tenant);
|
|
11145
|
+
const data = await gqlMutate(ctx.client, InstallSkillDoc, {
|
|
11146
|
+
input: { tenantId, skillId: slug, version: opts.version ?? null }
|
|
11147
|
+
});
|
|
11148
|
+
if (isJsonMode()) {
|
|
11149
|
+
printJson(data.installSkill);
|
|
11150
|
+
return;
|
|
11151
|
+
}
|
|
11152
|
+
printSuccess(
|
|
11153
|
+
`Installed skill ${data.installSkill.skillId} (source=${data.installSkill.source}, version=${data.installSkill.version ?? "\u2014"}).`
|
|
11154
|
+
);
|
|
11155
|
+
}
|
|
11156
|
+
async function runSkillDelete(slug, opts) {
|
|
11157
|
+
const ctx = await resolveSkillContext(opts);
|
|
11158
|
+
const tenantId = await resolveTenantIdForSkill(ctx, opts.tenant);
|
|
11159
|
+
if (!opts.yes) {
|
|
11160
|
+
if (!isInteractive()) {
|
|
11161
|
+
printError(
|
|
11162
|
+
"Refusing to uninstall without --yes in non-interactive mode."
|
|
11163
|
+
);
|
|
11164
|
+
process.exit(1);
|
|
11165
|
+
}
|
|
11166
|
+
requireTty("confirmation");
|
|
11167
|
+
const answer = await promptOrExit(
|
|
11168
|
+
() => input18({
|
|
11169
|
+
message: `Uninstall skill "${slug}" from this tenant? Type "uninstall" to confirm:`
|
|
11170
|
+
})
|
|
11171
|
+
);
|
|
11172
|
+
if (answer.trim() !== "uninstall") {
|
|
11173
|
+
console.log(" Cancelled.");
|
|
11174
|
+
return;
|
|
11175
|
+
}
|
|
11176
|
+
}
|
|
11177
|
+
const data = await gqlMutate(ctx.client, UninstallSkillDoc, {
|
|
11178
|
+
tenantId,
|
|
11179
|
+
skillId: slug
|
|
11180
|
+
});
|
|
11181
|
+
if (isJsonMode()) {
|
|
11182
|
+
printJson({ skillId: slug, uninstalled: data.uninstallSkill });
|
|
11183
|
+
return;
|
|
11184
|
+
}
|
|
11185
|
+
if (data.uninstallSkill) {
|
|
11186
|
+
printSuccess(`Uninstalled skill ${slug} from tenant.`);
|
|
11187
|
+
} else {
|
|
11188
|
+
console.log(` Skill "${slug}" was not installed (no-op).`);
|
|
11189
|
+
}
|
|
11190
|
+
}
|
|
11191
|
+
function retiredVerb(verb, hint) {
|
|
11343
11192
|
printError(
|
|
11344
|
-
`\`skill ${verb}\`
|
|
11345
|
-
|
|
11346
|
-
and the REST \`skill push\` upload path. Tenant-scoped install/upgrade/create/update/delete
|
|
11347
|
-
is tracked as a Phase-3 follow-up. Use \`thinkwork skill push <folder>\` to upload custom
|
|
11348
|
-
plugins; toggle catalog skills per-agent via \`thinkwork agent skills set\` for now.`
|
|
11193
|
+
`\`skill ${verb}\` was retired: ${hint}
|
|
11194
|
+
See \`thinkwork skill --help\` for the supported verbs.`
|
|
11349
11195
|
);
|
|
11350
11196
|
process.exit(2);
|
|
11351
11197
|
}
|
|
@@ -11355,11 +11201,27 @@ function registerSkillCommand(program2) {
|
|
|
11355
11201
|
);
|
|
11356
11202
|
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);
|
|
11357
11203
|
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);
|
|
11358
|
-
skill.command("install <slug>").description(
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
skill.command("
|
|
11362
|
-
|
|
11204
|
+
skill.command("install <slug>").description(
|
|
11205
|
+
"Install a catalog skill into the tenant (upserts tenant_skills). Idempotent \u2014 re-running bumps the version. Per-agent assignment still goes through `agent skills set`."
|
|
11206
|
+
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--version <v>", "Pin to a specific version (defaults to catalog's current)").action(runSkillInstall);
|
|
11207
|
+
skill.command("upgrade <slug>").description(
|
|
11208
|
+
"Upgrade an installed skill to the catalog's current version (or to --version). Same mutation as `install`."
|
|
11209
|
+
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("--version <v>", "Pin to a specific version").action(runSkillInstall);
|
|
11210
|
+
skill.command("create [slug]").description("Retired \u2014 use `thinkwork skill push <folder>` to publish a custom skill.").action(
|
|
11211
|
+
() => retiredVerb(
|
|
11212
|
+
"create",
|
|
11213
|
+
"custom-skill authoring is done by uploading a folder via `thinkwork skill push <folder>`."
|
|
11214
|
+
)
|
|
11215
|
+
);
|
|
11216
|
+
skill.command("update <slug>").description("Retired \u2014 re-push the skill folder with `thinkwork skill push <folder>` to update.").action(
|
|
11217
|
+
() => retiredVerb(
|
|
11218
|
+
"update",
|
|
11219
|
+
"updates are done by re-pushing the skill folder via `thinkwork skill push <folder>`."
|
|
11220
|
+
)
|
|
11221
|
+
);
|
|
11222
|
+
skill.command("delete <slug>").description(
|
|
11223
|
+
"Uninstall a skill from the tenant (deletes the tenant_skills row). Confirms unless --yes."
|
|
11224
|
+
).option("-s, --stage <name>", "Deployment stage").option("-t, --tenant <slug>", "Tenant slug").option("-y, --yes", "Skip confirmation").action(runSkillDelete);
|
|
11363
11225
|
skill.command("push <folder>").description(
|
|
11364
11226
|
"Zip a local plugin folder and upload it to the tenant as a pending plugin."
|
|
11365
11227
|
).option("-s, --stage <name>", "Deployment stage").option("--region <name>", "AWS region", "us-east-1").addHelpText(
|
|
@@ -11723,7 +11585,7 @@ function registerMemoryCommand(program2) {
|
|
|
11723
11585
|
}
|
|
11724
11586
|
|
|
11725
11587
|
// src/commands/recipe.ts
|
|
11726
|
-
import { confirm as confirm15, input as
|
|
11588
|
+
import { confirm as confirm15, input as input19 } from "@inquirer/prompts";
|
|
11727
11589
|
var RecipesDoc = graphql(`
|
|
11728
11590
|
query CliRecipes($tenantId: ID!, $threadId: ID, $agentId: ID, $limit: Int, $cursor: String) {
|
|
11729
11591
|
recipes(tenantId: $tenantId, threadId: $threadId, agentId: $agentId, limit: $limit, cursor: $cursor) {
|
|
@@ -11879,7 +11741,7 @@ async function runRecipeCreate(name, opts) {
|
|
|
11879
11741
|
process.exit(1);
|
|
11880
11742
|
}
|
|
11881
11743
|
requireTty("Recipe name");
|
|
11882
|
-
title = await promptOrExit(() =>
|
|
11744
|
+
title = await promptOrExit(() => input19({ message: "Recipe title:" }));
|
|
11883
11745
|
}
|
|
11884
11746
|
if (!opts.tool) {
|
|
11885
11747
|
printError("--tool <server/tool> is required.");
|
|
@@ -11920,22 +11782,22 @@ async function runRecipeCreate(name, opts) {
|
|
|
11920
11782
|
}
|
|
11921
11783
|
async function runRecipeUpdate(id, opts) {
|
|
11922
11784
|
const ctx = await resolveRecipeContext(opts);
|
|
11923
|
-
const
|
|
11924
|
-
if (opts.title !== void 0)
|
|
11925
|
-
if (opts.summary !== void 0)
|
|
11785
|
+
const input21 = {};
|
|
11786
|
+
if (opts.title !== void 0) input21.title = opts.title;
|
|
11787
|
+
if (opts.summary !== void 0) input21.summary = opts.summary;
|
|
11926
11788
|
if (opts.params !== void 0) {
|
|
11927
11789
|
try {
|
|
11928
|
-
|
|
11790
|
+
input21.params = JSON.parse(opts.params);
|
|
11929
11791
|
} catch (err) {
|
|
11930
11792
|
printError(`--params is not valid JSON: ${err.message}`);
|
|
11931
11793
|
process.exit(1);
|
|
11932
11794
|
}
|
|
11933
11795
|
}
|
|
11934
|
-
if (Object.keys(
|
|
11796
|
+
if (Object.keys(input21).length === 0) {
|
|
11935
11797
|
printError("Nothing to update.");
|
|
11936
11798
|
process.exit(1);
|
|
11937
11799
|
}
|
|
11938
|
-
const data = await gqlMutate(ctx.client, UpdateRecipeDoc, { id, input:
|
|
11800
|
+
const data = await gqlMutate(ctx.client, UpdateRecipeDoc, { id, input: input21 });
|
|
11939
11801
|
if (isJsonMode()) {
|
|
11940
11802
|
printJson(data.updateRecipe);
|
|
11941
11803
|
return;
|
|
@@ -12789,7 +12651,7 @@ Examples:
|
|
|
12789
12651
|
|
|
12790
12652
|
// src/commands/eval/run.ts
|
|
12791
12653
|
import { checkbox, confirm as confirm17 } from "@inquirer/prompts";
|
|
12792
|
-
import
|
|
12654
|
+
import ora from "ora";
|
|
12793
12655
|
|
|
12794
12656
|
// src/commands/eval/gql.ts
|
|
12795
12657
|
var EvalRunsDoc = graphql(`
|
|
@@ -13174,7 +13036,7 @@ async function runEvalRun(opts) {
|
|
|
13174
13036
|
}
|
|
13175
13037
|
async function pollUntilTerminal(client, runId, intervalSec, timeoutSec) {
|
|
13176
13038
|
const deadline = Date.now() + timeoutSec * 1e3;
|
|
13177
|
-
const spinner = isJsonMode() ? null :
|
|
13039
|
+
const spinner = isJsonMode() ? null : ora({ text: "Waiting for run to complete\u2026" }).start();
|
|
13178
13040
|
try {
|
|
13179
13041
|
while (Date.now() < deadline) {
|
|
13180
13042
|
const data = await gqlQuery(client, EvalRunDoc, { id: runId });
|
|
@@ -13304,13 +13166,13 @@ async function runEvalGet(runId, opts) {
|
|
|
13304
13166
|
}
|
|
13305
13167
|
|
|
13306
13168
|
// src/commands/eval/watch.ts
|
|
13307
|
-
import
|
|
13169
|
+
import ora2 from "ora";
|
|
13308
13170
|
async function runEvalWatch(runId, opts) {
|
|
13309
13171
|
const ctx = await resolveEvalContext(opts);
|
|
13310
13172
|
const intervalSec = Number.parseInt(opts.interval ?? "3", 10);
|
|
13311
13173
|
const timeoutSec = Number.parseInt(opts.timeout ?? "900", 10);
|
|
13312
13174
|
const deadline = Date.now() + timeoutSec * 1e3;
|
|
13313
|
-
const spinner = isJsonMode() ? null :
|
|
13175
|
+
const spinner = isJsonMode() ? null : ora2({ text: `Watching run ${runId}\u2026` }).start();
|
|
13314
13176
|
try {
|
|
13315
13177
|
while (Date.now() < deadline) {
|
|
13316
13178
|
const data = await gqlQuery(ctx.client, EvalRunDoc, { id: runId });
|
|
@@ -13515,8 +13377,8 @@ async function runEvalTestCaseGet(id, opts) {
|
|
|
13515
13377
|
}
|
|
13516
13378
|
|
|
13517
13379
|
// src/commands/eval/test-case/create.ts
|
|
13518
|
-
import { readFileSync as
|
|
13519
|
-
import { input as
|
|
13380
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
13381
|
+
import { input as input20, select as select8, checkbox as checkbox2 } from "@inquirer/prompts";
|
|
13520
13382
|
var DEFAULT_EVALUATORS = [
|
|
13521
13383
|
"Builtin.Helpfulness",
|
|
13522
13384
|
"Builtin.Correctness",
|
|
@@ -13546,17 +13408,17 @@ async function runEvalTestCaseCreate(opts) {
|
|
|
13546
13408
|
if (!name) {
|
|
13547
13409
|
requireTty("Name");
|
|
13548
13410
|
name = await promptOrExit(
|
|
13549
|
-
() =>
|
|
13411
|
+
() => input20({ message: "Test case name?", validate: (v) => v.trim().length > 0 || "Required" })
|
|
13550
13412
|
);
|
|
13551
13413
|
}
|
|
13552
13414
|
if (!category) {
|
|
13553
13415
|
category = await promptOrExit(
|
|
13554
|
-
() =>
|
|
13416
|
+
() => input20({ message: "Category (free-form label)?", validate: (v) => v.trim().length > 0 || "Required" })
|
|
13555
13417
|
);
|
|
13556
13418
|
}
|
|
13557
13419
|
if (!query) {
|
|
13558
13420
|
query = await promptOrExit(
|
|
13559
|
-
() =>
|
|
13421
|
+
() => input20({ message: "Query the agent under test will receive?", validate: (v) => v.trim().length > 0 || "Required" })
|
|
13560
13422
|
);
|
|
13561
13423
|
}
|
|
13562
13424
|
if (interactive && agentTemplateId === null) {
|
|
@@ -13588,7 +13450,7 @@ async function runEvalTestCaseCreate(opts) {
|
|
|
13588
13450
|
}
|
|
13589
13451
|
let assertions = null;
|
|
13590
13452
|
if (opts.assertionsFile) {
|
|
13591
|
-
const parsed = JSON.parse(
|
|
13453
|
+
const parsed = JSON.parse(readFileSync4(opts.assertionsFile, "utf8"));
|
|
13592
13454
|
if (!Array.isArray(parsed)) {
|
|
13593
13455
|
printError(`--assertions-file must contain a JSON array.`);
|
|
13594
13456
|
process.exit(1);
|
|
@@ -13619,31 +13481,31 @@ async function runEvalTestCaseCreate(opts) {
|
|
|
13619
13481
|
}
|
|
13620
13482
|
|
|
13621
13483
|
// src/commands/eval/test-case/update.ts
|
|
13622
|
-
import { readFileSync as
|
|
13484
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
13623
13485
|
async function runEvalTestCaseUpdate(id, opts) {
|
|
13624
13486
|
const ctx = await resolveEvalContext(opts);
|
|
13625
|
-
const
|
|
13626
|
-
if (opts.name !== void 0)
|
|
13627
|
-
if (opts.category !== void 0)
|
|
13628
|
-
if (opts.query !== void 0)
|
|
13629
|
-
if (opts.systemPrompt !== void 0)
|
|
13630
|
-
if (opts.agentTemplate !== void 0)
|
|
13631
|
-
if (opts.evaluator !== void 0)
|
|
13632
|
-
if (opts.tag !== void 0)
|
|
13633
|
-
if (opts.enabled !== void 0)
|
|
13487
|
+
const input21 = {};
|
|
13488
|
+
if (opts.name !== void 0) input21.name = opts.name;
|
|
13489
|
+
if (opts.category !== void 0) input21.category = opts.category;
|
|
13490
|
+
if (opts.query !== void 0) input21.query = opts.query;
|
|
13491
|
+
if (opts.systemPrompt !== void 0) input21.systemPrompt = opts.systemPrompt;
|
|
13492
|
+
if (opts.agentTemplate !== void 0) input21.agentTemplateId = opts.agentTemplate;
|
|
13493
|
+
if (opts.evaluator !== void 0) input21.agentcoreEvaluatorIds = opts.evaluator;
|
|
13494
|
+
if (opts.tag !== void 0) input21.tags = opts.tag;
|
|
13495
|
+
if (opts.enabled !== void 0) input21.enabled = opts.enabled;
|
|
13634
13496
|
if (opts.assertionsFile) {
|
|
13635
|
-
const parsed = JSON.parse(
|
|
13497
|
+
const parsed = JSON.parse(readFileSync5(opts.assertionsFile, "utf8"));
|
|
13636
13498
|
if (!Array.isArray(parsed)) {
|
|
13637
13499
|
printError(`--assertions-file must contain a JSON array.`);
|
|
13638
13500
|
process.exit(1);
|
|
13639
13501
|
}
|
|
13640
|
-
|
|
13502
|
+
input21.assertions = parsed;
|
|
13641
13503
|
}
|
|
13642
|
-
if (Object.keys(
|
|
13504
|
+
if (Object.keys(input21).length === 0) {
|
|
13643
13505
|
printError("No fields to update. Pass at least one --<field>.");
|
|
13644
13506
|
process.exit(1);
|
|
13645
13507
|
}
|
|
13646
|
-
const res = await gqlMutate(ctx.client, UpdateEvalTestCaseDoc, { id, input:
|
|
13508
|
+
const res = await gqlMutate(ctx.client, UpdateEvalTestCaseDoc, { id, input: input21 });
|
|
13647
13509
|
if (isJsonMode()) {
|
|
13648
13510
|
printJson(res.updateEvalTestCase);
|
|
13649
13511
|
return;
|
|
@@ -13763,7 +13625,7 @@ Examples:
|
|
|
13763
13625
|
}
|
|
13764
13626
|
|
|
13765
13627
|
// src/commands/wiki/compile.ts
|
|
13766
|
-
import
|
|
13628
|
+
import ora3 from "ora";
|
|
13767
13629
|
|
|
13768
13630
|
// src/commands/wiki/gql.ts
|
|
13769
13631
|
var TenantBySlugDoc2 = graphql(`
|
|
@@ -14033,7 +13895,7 @@ async function runWikiCompile(opts) {
|
|
|
14033
13895
|
const errors = [];
|
|
14034
13896
|
let forbiddenHit = false;
|
|
14035
13897
|
for (const target of targets) {
|
|
14036
|
-
const spinner = isJsonMode() || scope.mode === "all" ? null :
|
|
13898
|
+
const spinner = isJsonMode() || scope.mode === "all" ? null : ora3({
|
|
14037
13899
|
text: `Enqueuing compile for ${target.label}\u2026`,
|
|
14038
13900
|
prefixText: " "
|
|
14039
13901
|
}).start();
|
|
@@ -14131,7 +13993,7 @@ async function runWikiCompile(opts) {
|
|
|
14131
13993
|
}
|
|
14132
13994
|
}
|
|
14133
13995
|
async function watchSingleJob(ctx, target) {
|
|
14134
|
-
const spinner = isJsonMode() ? null :
|
|
13996
|
+
const spinner = isJsonMode() ? null : ora3({
|
|
14135
13997
|
text: `Watching job ${shortJobId(target.jobId)} for ${target.agentLabel}\u2026`,
|
|
14136
13998
|
prefixText: " "
|
|
14137
13999
|
}).start();
|
|
@@ -14180,7 +14042,7 @@ async function watchSingleJob(ctx, target) {
|
|
|
14180
14042
|
|
|
14181
14043
|
// src/commands/wiki/rebuild.ts
|
|
14182
14044
|
import { confirm as confirm20 } from "@inquirer/prompts";
|
|
14183
|
-
import
|
|
14045
|
+
import ora4 from "ora";
|
|
14184
14046
|
async function runWikiRebuild(opts) {
|
|
14185
14047
|
if (opts.all) {
|
|
14186
14048
|
printError(
|
|
@@ -14211,7 +14073,7 @@ async function runWikiRebuild(opts) {
|
|
|
14211
14073
|
process.exit(0);
|
|
14212
14074
|
}
|
|
14213
14075
|
}
|
|
14214
|
-
const resetSpinner = isJsonMode() ? null :
|
|
14076
|
+
const resetSpinner = isJsonMode() ? null : ora4({
|
|
14215
14077
|
text: `Archiving active pages for ${agentLabel}\u2026`,
|
|
14216
14078
|
prefixText: " "
|
|
14217
14079
|
}).start();
|
|
@@ -14248,7 +14110,7 @@ async function runWikiRebuild(opts) {
|
|
|
14248
14110
|
}
|
|
14249
14111
|
process.exit(1);
|
|
14250
14112
|
}
|
|
14251
|
-
const compileSpinner = isJsonMode() ? null :
|
|
14113
|
+
const compileSpinner = isJsonMode() ? null : ora4({
|
|
14252
14114
|
text: `Enqueuing fresh compile for ${agentLabel}\u2026`,
|
|
14253
14115
|
prefixText: " "
|
|
14254
14116
|
}).start();
|
|
@@ -14321,7 +14183,7 @@ async function runWikiRebuild(opts) {
|
|
|
14321
14183
|
}
|
|
14322
14184
|
}
|
|
14323
14185
|
async function watchRebuildJob(ctx, target) {
|
|
14324
|
-
const spinner = isJsonMode() ? null :
|
|
14186
|
+
const spinner = isJsonMode() ? null : ora4({
|
|
14325
14187
|
text: `Watching rebuild job ${shortJobId(target.jobId)}\u2026`,
|
|
14326
14188
|
prefixText: " "
|
|
14327
14189
|
}).start();
|
|
@@ -14357,7 +14219,7 @@ async function watchRebuildJob(ctx, target) {
|
|
|
14357
14219
|
}
|
|
14358
14220
|
|
|
14359
14221
|
// src/commands/wiki/status.ts
|
|
14360
|
-
import
|
|
14222
|
+
import ora5 from "ora";
|
|
14361
14223
|
var DEFAULT_WATCH_INTERVAL_MS = 3e3;
|
|
14362
14224
|
async function runWikiStatus(opts) {
|
|
14363
14225
|
const ctx = await resolveWikiContext(opts);
|
|
@@ -14409,7 +14271,7 @@ async function runWikiStatus(opts) {
|
|
|
14409
14271
|
process.exit(0);
|
|
14410
14272
|
}
|
|
14411
14273
|
const latestId = jobs[0].id;
|
|
14412
|
-
const spinner = isJsonMode() ? null :
|
|
14274
|
+
const spinner = isJsonMode() ? null : ora5({
|
|
14413
14275
|
text: `Watching job ${shortJobId(latestId)}\u2026`,
|
|
14414
14276
|
prefixText: " "
|
|
14415
14277
|
}).start();
|
|
@@ -14626,14 +14488,14 @@ import { resolve as resolve5 } from "path";
|
|
|
14626
14488
|
|
|
14627
14489
|
// src/commands/enterprise/template.ts
|
|
14628
14490
|
import {
|
|
14629
|
-
existsSync as
|
|
14630
|
-
mkdirSync as
|
|
14631
|
-
readFileSync as
|
|
14632
|
-
readdirSync
|
|
14491
|
+
existsSync as existsSync7,
|
|
14492
|
+
mkdirSync as mkdirSync4,
|
|
14493
|
+
readFileSync as readFileSync6,
|
|
14494
|
+
readdirSync,
|
|
14633
14495
|
statSync as statSync2,
|
|
14634
|
-
writeFileSync as
|
|
14496
|
+
writeFileSync as writeFileSync4
|
|
14635
14497
|
} from "fs";
|
|
14636
|
-
import { dirname as dirname4, join as
|
|
14498
|
+
import { dirname as dirname4, join as join6, relative as relative2, resolve as resolve4 } from "path";
|
|
14637
14499
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14638
14500
|
var __dirname2 = dirname4(fileURLToPath3(import.meta.url));
|
|
14639
14501
|
var MANAGED_MARKER = "thinkwork-managed: enterprise-deploy-template";
|
|
@@ -14649,17 +14511,17 @@ function renderEnterpriseDeployRepoTemplate(options) {
|
|
|
14649
14511
|
stages
|
|
14650
14512
|
});
|
|
14651
14513
|
const templateFiles = listTemplateFiles(templateRoot).filter(
|
|
14652
|
-
(
|
|
14653
|
-
relative2(templateRoot,
|
|
14514
|
+
(path) => !relative2(templateRoot, path).startsWith("terraform/stages/") && !/^terraform\/backend-[^.]+\.hcl$/.test(
|
|
14515
|
+
relative2(templateRoot, path).split("\\").join("/")
|
|
14654
14516
|
)
|
|
14655
14517
|
);
|
|
14656
14518
|
const written = [];
|
|
14657
14519
|
const preserved = [];
|
|
14658
14520
|
for (const templatePath of templateFiles) {
|
|
14659
14521
|
const relativePath = relative2(templateRoot, templatePath);
|
|
14660
|
-
const outputPath =
|
|
14522
|
+
const outputPath = join6(targetDir, relativePath);
|
|
14661
14523
|
let content = applyTemplate(
|
|
14662
|
-
|
|
14524
|
+
readFileSync6(templatePath, "utf8"),
|
|
14663
14525
|
replacements
|
|
14664
14526
|
);
|
|
14665
14527
|
if (relativePath.split("\\").join("/") === "customer/deployment.json") {
|
|
@@ -14667,30 +14529,30 @@ function renderEnterpriseDeployRepoTemplate(options) {
|
|
|
14667
14529
|
}
|
|
14668
14530
|
writeManagedFile(outputPath, content, written, preserved);
|
|
14669
14531
|
}
|
|
14670
|
-
const stageTemplateRoot =
|
|
14671
|
-
const backendTemplatePath =
|
|
14532
|
+
const stageTemplateRoot = join6(templateRoot, "terraform", "stages");
|
|
14533
|
+
const backendTemplatePath = join6(
|
|
14672
14534
|
templateRoot,
|
|
14673
14535
|
"terraform",
|
|
14674
14536
|
"backend-dev.hcl"
|
|
14675
14537
|
);
|
|
14676
14538
|
for (const stage of stages) {
|
|
14677
|
-
const explicitTemplate =
|
|
14678
|
-
const fallbackTemplate =
|
|
14679
|
-
const templatePath =
|
|
14680
|
-
const outputPath =
|
|
14539
|
+
const explicitTemplate = join6(stageTemplateRoot, `${stage}.tfvars`);
|
|
14540
|
+
const fallbackTemplate = join6(stageTemplateRoot, "dev.tfvars");
|
|
14541
|
+
const templatePath = existsSync7(explicitTemplate) ? explicitTemplate : fallbackTemplate;
|
|
14542
|
+
const outputPath = join6(
|
|
14681
14543
|
targetDir,
|
|
14682
14544
|
"terraform",
|
|
14683
14545
|
"stages",
|
|
14684
14546
|
`${stage}.tfvars`
|
|
14685
14547
|
);
|
|
14686
|
-
const content = applyTemplate(
|
|
14548
|
+
const content = applyTemplate(readFileSync6(templatePath, "utf8"), {
|
|
14687
14549
|
...replacements,
|
|
14688
14550
|
STAGE: stage
|
|
14689
14551
|
});
|
|
14690
14552
|
writeManagedFile(outputPath, content, written, preserved);
|
|
14691
|
-
const backendPath =
|
|
14553
|
+
const backendPath = join6(targetDir, "terraform", `backend-${stage}.hcl`);
|
|
14692
14554
|
const backendContent = applyTemplate(
|
|
14693
|
-
|
|
14555
|
+
readFileSync6(backendTemplatePath, "utf8"),
|
|
14694
14556
|
{
|
|
14695
14557
|
...replacements,
|
|
14696
14558
|
STAGE: stage
|
|
@@ -14730,7 +14592,7 @@ function findEnterpriseTemplateRoot() {
|
|
|
14730
14592
|
resolve4(__dirname2, "commands/enterprise/templates/deploy-repo")
|
|
14731
14593
|
];
|
|
14732
14594
|
for (const candidate of candidates) {
|
|
14733
|
-
if (
|
|
14595
|
+
if (existsSync7(join6(candidate, "thinkwork.lock"))) return candidate;
|
|
14734
14596
|
}
|
|
14735
14597
|
throw new Error(
|
|
14736
14598
|
"Enterprise deployment repo template not found. The CLI package may be incomplete."
|
|
@@ -14771,13 +14633,13 @@ function renderCustomerDeploymentJson(content, customerSlug, stages) {
|
|
|
14771
14633
|
}
|
|
14772
14634
|
function listTemplateFiles(root) {
|
|
14773
14635
|
const out = [];
|
|
14774
|
-
for (const entry of
|
|
14775
|
-
const
|
|
14776
|
-
const stat = statSync2(
|
|
14636
|
+
for (const entry of readdirSync(root)) {
|
|
14637
|
+
const path = join6(root, entry);
|
|
14638
|
+
const stat = statSync2(path);
|
|
14777
14639
|
if (stat.isDirectory()) {
|
|
14778
|
-
out.push(...listTemplateFiles(
|
|
14640
|
+
out.push(...listTemplateFiles(path));
|
|
14779
14641
|
} else if (stat.isFile()) {
|
|
14780
|
-
out.push(
|
|
14642
|
+
out.push(path);
|
|
14781
14643
|
}
|
|
14782
14644
|
}
|
|
14783
14645
|
return out.sort();
|
|
@@ -14790,24 +14652,24 @@ function applyTemplate(source, replacements) {
|
|
|
14790
14652
|
return replacements[key];
|
|
14791
14653
|
});
|
|
14792
14654
|
}
|
|
14793
|
-
function writeManagedFile(
|
|
14794
|
-
|
|
14795
|
-
if (
|
|
14796
|
-
const current =
|
|
14655
|
+
function writeManagedFile(path, content, written, preserved) {
|
|
14656
|
+
mkdirSync4(dirname4(path), { recursive: true });
|
|
14657
|
+
if (existsSync7(path)) {
|
|
14658
|
+
const current = readFileSync6(path, "utf8");
|
|
14797
14659
|
if (!current.includes(MANAGED_MARKER)) {
|
|
14798
|
-
preserved.push(
|
|
14660
|
+
preserved.push(path);
|
|
14799
14661
|
return;
|
|
14800
14662
|
}
|
|
14801
14663
|
}
|
|
14802
|
-
|
|
14803
|
-
written.push(
|
|
14664
|
+
writeFileSync4(path, content);
|
|
14665
|
+
written.push(path);
|
|
14804
14666
|
}
|
|
14805
14667
|
|
|
14806
14668
|
// src/commands/enterprise/aws-bootstrap.ts
|
|
14807
14669
|
import { execFileSync } from "child_process";
|
|
14808
|
-
import { mkdtempSync, writeFileSync as
|
|
14670
|
+
import { mkdtempSync, writeFileSync as writeFileSync5 } from "fs";
|
|
14809
14671
|
import { tmpdir } from "os";
|
|
14810
|
-
import { join as
|
|
14672
|
+
import { join as join7 } from "path";
|
|
14811
14673
|
function buildEnterpriseAwsBootstrapPlan(config) {
|
|
14812
14674
|
const oidcProviderArn = `arn:aws:iam::${config.accountId}:oidc-provider/token.actions.githubusercontent.com`;
|
|
14813
14675
|
return {
|
|
@@ -15026,9 +14888,9 @@ var AwsCliEnterpriseBootstrapClient = class {
|
|
|
15026
14888
|
message: `Deploy role ${role.roleName} already exists; updated inline deploy policy ${role.deployPolicyName}.`
|
|
15027
14889
|
};
|
|
15028
14890
|
}
|
|
15029
|
-
const dir = mkdtempSync(
|
|
15030
|
-
const trustPath =
|
|
15031
|
-
|
|
14891
|
+
const dir = mkdtempSync(join7(tmpdir(), "thinkwork-enterprise-role-"));
|
|
14892
|
+
const trustPath = join7(dir, "trust.json");
|
|
14893
|
+
writeFileSync5(trustPath, JSON.stringify(role.trustPolicy));
|
|
15032
14894
|
execFileSync("aws", [
|
|
15033
14895
|
"iam",
|
|
15034
14896
|
"create-role",
|
|
@@ -15046,9 +14908,9 @@ var AwsCliEnterpriseBootstrapClient = class {
|
|
|
15046
14908
|
}
|
|
15047
14909
|
};
|
|
15048
14910
|
function putRolePolicy(role) {
|
|
15049
|
-
const dir = mkdtempSync(
|
|
15050
|
-
const policyPath =
|
|
15051
|
-
|
|
14911
|
+
const dir = mkdtempSync(join7(tmpdir(), "thinkwork-enterprise-policy-"));
|
|
14912
|
+
const policyPath = join7(dir, "policy.json");
|
|
14913
|
+
writeFileSync5(policyPath, JSON.stringify(role.deployPolicy));
|
|
15052
14914
|
execFileSync("aws", [
|
|
15053
14915
|
"iam",
|
|
15054
14916
|
"put-role-policy",
|
|
@@ -15104,12 +14966,12 @@ function awsOk(args) {
|
|
|
15104
14966
|
|
|
15105
14967
|
// src/commands/enterprise/github.ts
|
|
15106
14968
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
15107
|
-
function parseGitHubRepository(
|
|
15108
|
-
const trimmed =
|
|
14969
|
+
function parseGitHubRepository(input21) {
|
|
14970
|
+
const trimmed = input21.trim();
|
|
15109
14971
|
const match = /^([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)$/.exec(trimmed);
|
|
15110
14972
|
if (!match) {
|
|
15111
14973
|
throw new Error(
|
|
15112
|
-
`Invalid GitHub repository "${
|
|
14974
|
+
`Invalid GitHub repository "${input21}". Use owner/name, for example acme/thinkwork-deploy.`
|
|
15113
14975
|
);
|
|
15114
14976
|
}
|
|
15115
14977
|
return {
|
|
@@ -15474,16 +15336,16 @@ async function resolveCustomerSlug(flag) {
|
|
|
15474
15336
|
if (!process.stdin.isTTY) {
|
|
15475
15337
|
throw new Error("Customer slug is required. Pass --customer <slug>.");
|
|
15476
15338
|
}
|
|
15477
|
-
const { input:
|
|
15478
|
-
return
|
|
15339
|
+
const { input: input21 } = await import("@inquirer/prompts");
|
|
15340
|
+
return input21({ message: "Customer slug:" });
|
|
15479
15341
|
}
|
|
15480
15342
|
async function resolveRepository(flag) {
|
|
15481
15343
|
if (flag) return flag;
|
|
15482
15344
|
if (!process.stdin.isTTY) {
|
|
15483
15345
|
throw new Error("GitHub repository is required. Pass --repo <owner/name>.");
|
|
15484
15346
|
}
|
|
15485
|
-
const { input:
|
|
15486
|
-
return
|
|
15347
|
+
const { input: input21 } = await import("@inquirer/prompts");
|
|
15348
|
+
return input21({ message: "GitHub deployment repo (owner/name):" });
|
|
15487
15349
|
}
|
|
15488
15350
|
function printBootstrapSummary(result) {
|
|
15489
15351
|
console.log("");
|
|
@@ -15506,14 +15368,14 @@ function printBootstrapSummary(result) {
|
|
|
15506
15368
|
import { resolve as resolve6 } from "path";
|
|
15507
15369
|
|
|
15508
15370
|
// src/commands/enterprise/overlay-schema.ts
|
|
15509
|
-
import { existsSync as
|
|
15510
|
-
import { join as
|
|
15371
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync7, statSync as statSync3 } from "fs";
|
|
15372
|
+
import { join as join8, relative as relative3, sep as sep2 } from "path";
|
|
15511
15373
|
function loadEnterpriseOverlayDefinition(repoRoot) {
|
|
15512
|
-
const
|
|
15513
|
-
if (!
|
|
15514
|
-
throw new Error(`Missing customer overlay definition: ${
|
|
15374
|
+
const path = join8(repoRoot, "customer", "deployment.json");
|
|
15375
|
+
if (!existsSync8(path)) {
|
|
15376
|
+
throw new Error(`Missing customer overlay definition: ${path}`);
|
|
15515
15377
|
}
|
|
15516
|
-
const parsed = JSON.parse(
|
|
15378
|
+
const parsed = JSON.parse(readFileSync7(path, "utf8"));
|
|
15517
15379
|
if (parsed.schemaVersion !== 1) {
|
|
15518
15380
|
throw new Error(
|
|
15519
15381
|
`Unsupported customer/deployment.json schemaVersion ${parsed.schemaVersion}`
|
|
@@ -15545,11 +15407,11 @@ function stageOverlay(definition, stage) {
|
|
|
15545
15407
|
}
|
|
15546
15408
|
function readCustomerEvalPack(repoRoot, packName) {
|
|
15547
15409
|
assertPackName(packName, "eval");
|
|
15548
|
-
const
|
|
15549
|
-
if (!
|
|
15550
|
-
throw new Error(`Eval pack "${packName}" is missing: ${
|
|
15410
|
+
const path = join8(repoRoot, "customer", "evals", `${packName}.json`);
|
|
15411
|
+
if (!existsSync8(path)) {
|
|
15412
|
+
throw new Error(`Eval pack "${packName}" is missing: ${path}`);
|
|
15551
15413
|
}
|
|
15552
|
-
const parsed = JSON.parse(
|
|
15414
|
+
const parsed = JSON.parse(readFileSync7(path, "utf8"));
|
|
15553
15415
|
if (!Array.isArray(parsed)) {
|
|
15554
15416
|
throw new Error(`Eval pack "${packName}" must be a JSON array`);
|
|
15555
15417
|
}
|
|
@@ -15559,27 +15421,27 @@ function readCustomerEvalPack(repoRoot, packName) {
|
|
|
15559
15421
|
}
|
|
15560
15422
|
function readJsonSeedPack(repoRoot, packName) {
|
|
15561
15423
|
assertPackName(packName, "seed");
|
|
15562
|
-
const
|
|
15563
|
-
if (!
|
|
15564
|
-
throw new Error(`Seed pack "${packName}" is missing: ${
|
|
15424
|
+
const path = join8(repoRoot, "customer", "seeds", `${packName}.json`);
|
|
15425
|
+
if (!existsSync8(path)) {
|
|
15426
|
+
throw new Error(`Seed pack "${packName}" is missing: ${path}`);
|
|
15565
15427
|
}
|
|
15566
|
-
return JSON.parse(
|
|
15428
|
+
return JSON.parse(readFileSync7(path, "utf8"));
|
|
15567
15429
|
}
|
|
15568
15430
|
function collectOverlayFiles(repoRoot, family, packName) {
|
|
15569
15431
|
assertPackName(packName, family);
|
|
15570
|
-
const root =
|
|
15571
|
-
if (!
|
|
15432
|
+
const root = join8(repoRoot, "customer", family, packName);
|
|
15433
|
+
if (!existsSync8(root) || !statSync3(root).isDirectory()) {
|
|
15572
15434
|
throw new Error(`Overlay pack "${family}/${packName}" is missing: ${root}`);
|
|
15573
15435
|
}
|
|
15574
15436
|
const files = [];
|
|
15575
|
-
walkFiles(root, (
|
|
15576
|
-
const relativePath = relative3(root,
|
|
15437
|
+
walkFiles(root, (path) => {
|
|
15438
|
+
const relativePath = relative3(root, path).split(sep2).join("/");
|
|
15577
15439
|
if (relativePath === ".DS_Store" || relativePath.endsWith("/.DS_Store")) {
|
|
15578
15440
|
return;
|
|
15579
15441
|
}
|
|
15580
15442
|
files.push({
|
|
15581
15443
|
relativePath,
|
|
15582
|
-
content:
|
|
15444
|
+
content: readFileSync7(path, "utf8")
|
|
15583
15445
|
});
|
|
15584
15446
|
});
|
|
15585
15447
|
if (family === "skills" && !files.some((file) => file.relativePath === "SKILL.md")) {
|
|
@@ -15680,12 +15542,12 @@ function assertPackName(value, label) {
|
|
|
15680
15542
|
}
|
|
15681
15543
|
}
|
|
15682
15544
|
function walkFiles(root, visit) {
|
|
15683
|
-
for (const entry of
|
|
15684
|
-
const
|
|
15545
|
+
for (const entry of readdirSync2(root, { withFileTypes: true })) {
|
|
15546
|
+
const path = join8(root, entry.name);
|
|
15685
15547
|
if (entry.isDirectory()) {
|
|
15686
|
-
walkFiles(
|
|
15548
|
+
walkFiles(path, visit);
|
|
15687
15549
|
} else if (entry.isFile()) {
|
|
15688
|
-
visit(
|
|
15550
|
+
visit(path);
|
|
15689
15551
|
}
|
|
15690
15552
|
}
|
|
15691
15553
|
}
|
|
@@ -15737,7 +15599,7 @@ async function applyEnterpriseOverlay(plan, client) {
|
|
|
15737
15599
|
skipped: 0
|
|
15738
15600
|
};
|
|
15739
15601
|
for (const testCase of operation.testCases) {
|
|
15740
|
-
const
|
|
15602
|
+
const input21 = withOverlayTags(
|
|
15741
15603
|
operation.pack,
|
|
15742
15604
|
testCase,
|
|
15743
15605
|
client.targetAgentTemplateId
|
|
@@ -15746,16 +15608,16 @@ async function applyEnterpriseOverlay(plan, client) {
|
|
|
15746
15608
|
overlayKeyTag(operation.pack, testCase)
|
|
15747
15609
|
);
|
|
15748
15610
|
if (existingCase) {
|
|
15749
|
-
if (sameEval(existingCase,
|
|
15611
|
+
if (sameEval(existingCase, input21)) {
|
|
15750
15612
|
result.evals.skipped++;
|
|
15751
15613
|
packResult.skipped++;
|
|
15752
15614
|
} else {
|
|
15753
|
-
await client.updateEvalTestCase(existingCase.id,
|
|
15615
|
+
await client.updateEvalTestCase(existingCase.id, input21);
|
|
15754
15616
|
result.evals.updated++;
|
|
15755
15617
|
packResult.updated++;
|
|
15756
15618
|
}
|
|
15757
15619
|
} else {
|
|
15758
|
-
await client.createEvalTestCase(
|
|
15620
|
+
await client.createEvalTestCase(input21);
|
|
15759
15621
|
result.evals.inserted++;
|
|
15760
15622
|
packResult.inserted++;
|
|
15761
15623
|
}
|
|
@@ -15925,19 +15787,19 @@ async function createOverlayApiClient(plan, opts) {
|
|
|
15925
15787
|
});
|
|
15926
15788
|
return data.evalTestCases;
|
|
15927
15789
|
},
|
|
15928
|
-
async createEvalTestCase(
|
|
15790
|
+
async createEvalTestCase(input21) {
|
|
15929
15791
|
await gqlMutate(ctx.client, CreateEvalTestCaseDoc, {
|
|
15930
15792
|
tenantId: ctx.tenantId,
|
|
15931
|
-
input: evalInput(
|
|
15793
|
+
input: evalInput(input21)
|
|
15932
15794
|
});
|
|
15933
15795
|
},
|
|
15934
|
-
async updateEvalTestCase(id,
|
|
15796
|
+
async updateEvalTestCase(id, input21) {
|
|
15935
15797
|
await gqlMutate(ctx.client, UpdateEvalTestCaseDoc, {
|
|
15936
15798
|
id,
|
|
15937
|
-
input: evalInput(
|
|
15799
|
+
input: evalInput(input21)
|
|
15938
15800
|
});
|
|
15939
15801
|
},
|
|
15940
|
-
async putWorkspaceFile(
|
|
15802
|
+
async putWorkspaceFile(input21) {
|
|
15941
15803
|
const response = await fetch(
|
|
15942
15804
|
`${apiUrl.replace(/\/+$/, "")}/api/workspaces/files`,
|
|
15943
15805
|
{
|
|
@@ -15950,15 +15812,15 @@ async function createOverlayApiClient(plan, opts) {
|
|
|
15950
15812
|
body: JSON.stringify({
|
|
15951
15813
|
action: "put",
|
|
15952
15814
|
templateId,
|
|
15953
|
-
path:
|
|
15954
|
-
content:
|
|
15815
|
+
path: input21.path,
|
|
15816
|
+
content: input21.content
|
|
15955
15817
|
})
|
|
15956
15818
|
}
|
|
15957
15819
|
);
|
|
15958
15820
|
if (!response.ok) {
|
|
15959
15821
|
const text = await response.text();
|
|
15960
15822
|
throw new Error(
|
|
15961
|
-
`PUT ${
|
|
15823
|
+
`PUT ${input21.path} failed: ${response.status} ${text || response.statusText}`
|
|
15962
15824
|
);
|
|
15963
15825
|
}
|
|
15964
15826
|
}
|
|
@@ -15976,17 +15838,17 @@ async function resolveTemplateId(client, tenantId, templateSlug) {
|
|
|
15976
15838
|
}
|
|
15977
15839
|
return template.id;
|
|
15978
15840
|
}
|
|
15979
|
-
function evalInput(
|
|
15841
|
+
function evalInput(input21) {
|
|
15980
15842
|
return {
|
|
15981
|
-
name:
|
|
15982
|
-
category:
|
|
15983
|
-
query:
|
|
15984
|
-
systemPrompt:
|
|
15985
|
-
agentTemplateId:
|
|
15986
|
-
assertions:
|
|
15987
|
-
agentcoreEvaluatorIds:
|
|
15988
|
-
tags:
|
|
15989
|
-
enabled:
|
|
15843
|
+
name: input21.name,
|
|
15844
|
+
category: input21.category,
|
|
15845
|
+
query: input21.query,
|
|
15846
|
+
systemPrompt: input21.systemPrompt ?? null,
|
|
15847
|
+
agentTemplateId: input21.agentTemplateId ?? null,
|
|
15848
|
+
assertions: input21.assertions,
|
|
15849
|
+
agentcoreEvaluatorIds: input21.agentcoreEvaluatorIds ?? [],
|
|
15850
|
+
tags: input21.tags ?? [],
|
|
15851
|
+
enabled: input21.enabled ?? true
|
|
15990
15852
|
};
|
|
15991
15853
|
}
|
|
15992
15854
|
function publicPlan(plan) {
|