@quiltdata/benchling-webhook 0.7.2-20251106T010445Z → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -159
- package/dist/bin/benchling-webhook.d.ts.map +1 -1
- package/dist/bin/benchling-webhook.js +2 -4
- package/dist/bin/benchling-webhook.js.map +1 -1
- package/dist/bin/commands/config-profiles.d.ts.map +1 -1
- package/dist/bin/commands/config-profiles.js +1 -2
- package/dist/bin/commands/config-profiles.js.map +1 -1
- package/dist/bin/commands/deploy.d.ts +1 -1
- package/dist/bin/commands/deploy.d.ts.map +1 -1
- package/dist/bin/commands/deploy.js +67 -9
- package/dist/bin/commands/deploy.js.map +1 -1
- package/dist/bin/commands/infer-quilt-config.d.ts +6 -15
- package/dist/bin/commands/infer-quilt-config.d.ts.map +1 -1
- package/dist/bin/commands/infer-quilt-config.js +35 -64
- package/dist/bin/commands/infer-quilt-config.js.map +1 -1
- package/dist/bin/commands/manifest.d.ts +0 -11
- package/dist/bin/commands/manifest.d.ts.map +1 -1
- package/dist/bin/commands/manifest.js +8 -22
- package/dist/bin/commands/manifest.js.map +1 -1
- package/dist/bin/commands/setup-profile.d.ts.map +1 -1
- package/dist/bin/commands/setup-profile.js +15 -17
- package/dist/bin/commands/setup-profile.js.map +1 -1
- package/dist/bin/commands/setup-wizard.d.ts +0 -2
- package/dist/bin/commands/setup-wizard.d.ts.map +1 -1
- package/dist/bin/commands/setup-wizard.js +172 -414
- package/dist/bin/commands/setup-wizard.js.map +1 -1
- package/dist/bin/commands/sync-secrets.js +2 -2
- package/dist/lib/benchling-webhook-stack.d.ts.map +1 -1
- package/dist/lib/benchling-webhook-stack.js +15 -1
- package/dist/lib/benchling-webhook-stack.js.map +1 -1
- package/dist/lib/configuration-saver.d.ts +0 -8
- package/dist/lib/configuration-saver.d.ts.map +1 -1
- package/dist/lib/configuration-saver.js +1 -18
- package/dist/lib/configuration-saver.js.map +1 -1
- package/dist/lib/fargate-service.d.ts +4 -2
- package/dist/lib/fargate-service.d.ts.map +1 -1
- package/dist/lib/fargate-service.js +25 -12
- package/dist/lib/fargate-service.js.map +1 -1
- package/dist/lib/types/config.d.ts +9 -70
- package/dist/lib/types/config.d.ts.map +1 -1
- package/dist/lib/types/config.js +2 -3
- package/dist/lib/types/config.js.map +1 -1
- package/dist/lib/utils/config-loader.d.ts.map +1 -1
- package/dist/lib/utils/config-loader.js +3 -2
- package/dist/lib/utils/config-loader.js.map +1 -1
- package/dist/lib/utils/config-resolver.d.ts +2 -2
- package/dist/lib/utils/config-resolver.d.ts.map +1 -1
- package/dist/lib/utils/config-resolver.js +14 -7
- package/dist/lib/utils/config-resolver.js.map +1 -1
- package/dist/lib/utils/config.d.ts +1 -1
- package/dist/lib/utils/config.d.ts.map +1 -1
- package/dist/lib/utils/config.js +7 -3
- package/dist/lib/utils/config.js.map +1 -1
- package/dist/lib/utils/sqs.d.ts +13 -0
- package/dist/lib/utils/sqs.d.ts.map +1 -0
- package/dist/lib/utils/sqs.js +22 -0
- package/dist/lib/utils/sqs.js.map +1 -0
- package/dist/lib/utils/stack-inference.d.ts.map +1 -1
- package/dist/lib/utils/stack-inference.js +8 -6
- package/dist/lib/utils/stack-inference.js.map +1 -1
- package/dist/lib/xdg-config.d.ts +3 -1
- package/dist/lib/xdg-config.d.ts.map +1 -1
- package/dist/lib/xdg-config.js +7 -1
- package/dist/lib/xdg-config.js.map +1 -1
- package/dist/package.json +4 -2
- package/dist/scripts/get-dev-version.d.ts +16 -0
- package/dist/scripts/get-dev-version.d.ts.map +1 -0
- package/dist/scripts/get-dev-version.js +57 -0
- package/dist/scripts/get-dev-version.js.map +1 -0
- package/package.json +4 -2
- package/dist/lib/xdg-cli-wrapper.d.ts +0 -113
- package/dist/lib/xdg-cli-wrapper.d.ts.map +0 -1
- package/dist/lib/xdg-cli-wrapper.js +0 -289
- package/dist/lib/xdg-cli-wrapper.js.map +0 -1
- package/dist/scripts/infer-quilt-config.d.ts +0 -47
- package/dist/scripts/infer-quilt-config.d.ts.map +0 -1
- package/dist/scripts/infer-quilt-config.js +0 -315
- package/dist/scripts/infer-quilt-config.js.map +0 -1
|
@@ -53,32 +53,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
53
53
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
54
|
exports.setupWizardCommand = setupWizardCommand;
|
|
55
55
|
const https = __importStar(require("https"));
|
|
56
|
-
const fs_1 = require("fs");
|
|
57
|
-
const path_1 = require("path");
|
|
58
56
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
59
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
60
|
-
const boxen_1 = __importDefault(require("boxen"));
|
|
61
|
-
const ora_1 = __importDefault(require("ora"));
|
|
62
57
|
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
63
58
|
const xdg_config_1 = require("../../lib/xdg-config");
|
|
64
59
|
const infer_quilt_config_1 = require("../commands/infer-quilt-config");
|
|
65
|
-
const
|
|
66
|
-
const sync_secrets_1 = require("./sync-secrets");
|
|
67
|
-
const deploy_1 = require("./deploy");
|
|
68
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
69
|
-
const pkg = require("../../package.json");
|
|
70
|
-
const MANIFEST_FILENAME = "benchling-app-manifest.yaml";
|
|
71
|
-
function getAccountFromStackArn(stackArn) {
|
|
72
|
-
if (!stackArn) {
|
|
73
|
-
return undefined;
|
|
74
|
-
}
|
|
75
|
-
const parts = stackArn.split(":");
|
|
76
|
-
if (parts.length < 5) {
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
79
|
-
const account = parts[4];
|
|
80
|
-
return /^[0-9]{12}$/.test(account) ? account : undefined;
|
|
81
|
-
}
|
|
60
|
+
const sqs_1 = require("../../lib/utils/sqs");
|
|
82
61
|
// =============================================================================
|
|
83
62
|
// VALIDATION FUNCTIONS (from scripts/config/validator.ts)
|
|
84
63
|
// =============================================================================
|
|
@@ -285,311 +264,152 @@ async function runConfigWizard(options = {}) {
|
|
|
285
264
|
console.log(`Creating profile inheriting from: ${inheritFrom}\n`);
|
|
286
265
|
}
|
|
287
266
|
const config = { ...existingConfig };
|
|
267
|
+
let awsAccountId;
|
|
288
268
|
// If non-interactive, validate that all required fields are present
|
|
289
269
|
if (nonInteractive) {
|
|
290
270
|
if (!config.benchling?.tenant || !config.benchling?.clientId || !config.benchling?.clientSecret) {
|
|
291
271
|
throw new Error("Non-interactive mode requires benchlingTenant, benchlingClientId, and benchlingClientSecret to be already configured");
|
|
292
272
|
}
|
|
293
|
-
|
|
273
|
+
// Add metadata and inheritance marker before returning
|
|
274
|
+
const now = new Date().toISOString();
|
|
275
|
+
const finalConfig = config;
|
|
276
|
+
finalConfig._metadata = {
|
|
277
|
+
version: "0.7.0",
|
|
278
|
+
createdAt: config._metadata?.createdAt || now,
|
|
279
|
+
updatedAt: now,
|
|
280
|
+
source: "wizard",
|
|
281
|
+
};
|
|
282
|
+
if (inheritFrom) {
|
|
283
|
+
finalConfig._inherits = inheritFrom;
|
|
284
|
+
}
|
|
285
|
+
return finalConfig;
|
|
294
286
|
}
|
|
295
287
|
// Prompt for Quilt configuration (if not inherited)
|
|
296
288
|
if (!inheritFrom) {
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
message: "Use these inferred Quilt settings?",
|
|
319
|
-
default: true,
|
|
320
|
-
},
|
|
321
|
-
]);
|
|
322
|
-
if (!confirmQuilt) {
|
|
323
|
-
console.log("\nPlease enter Quilt configuration manually:\n");
|
|
324
|
-
shouldPromptForQuilt = true;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
// Only prompt for Quilt fields if not all are present OR user chose to enter manually
|
|
328
|
-
if (shouldPromptForQuilt) {
|
|
329
|
-
if (!hasAllQuiltFields) {
|
|
330
|
-
console.log("Step 1: Quilt Configuration\n");
|
|
331
|
-
console.log("Note: Run 'npm run setup:infer' first to auto-detect Quilt stack\n");
|
|
332
|
-
}
|
|
333
|
-
const quiltAnswers = await inquirer_1.default.prompt([
|
|
334
|
-
{
|
|
335
|
-
type: "input",
|
|
336
|
-
name: "stackArn",
|
|
337
|
-
message: "Quilt Stack ARN:",
|
|
338
|
-
default: config.quilt?.stackArn,
|
|
339
|
-
validate: (input) => input.trim().length > 0 && input.startsWith("arn:aws:cloudformation:") ||
|
|
340
|
-
"Stack ARN is required and must start with arn:aws:cloudformation:",
|
|
341
|
-
},
|
|
342
|
-
{
|
|
343
|
-
type: "input",
|
|
344
|
-
name: "catalog",
|
|
345
|
-
message: "Quilt Catalog URL (domain or full URL):",
|
|
346
|
-
default: config.quilt?.catalog,
|
|
347
|
-
validate: (input) => {
|
|
348
|
-
const trimmed = input.trim();
|
|
349
|
-
if (trimmed.length === 0) {
|
|
350
|
-
return "Catalog URL is required";
|
|
351
|
-
}
|
|
352
|
-
return true;
|
|
353
|
-
},
|
|
354
|
-
filter: (input) => {
|
|
355
|
-
// Strip protocol if present, store only domain
|
|
356
|
-
return input.trim().replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
{
|
|
360
|
-
type: "input",
|
|
361
|
-
name: "bucket",
|
|
362
|
-
message: "Quilt S3 Bucket:",
|
|
363
|
-
default: config.quilt?.bucket,
|
|
364
|
-
validate: (input) => input.trim().length > 0 || "Bucket name is required",
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
type: "input",
|
|
368
|
-
name: "database",
|
|
369
|
-
message: "Quilt Athena Database:",
|
|
370
|
-
default: config.quilt?.database || "quilt_catalog",
|
|
371
|
-
validate: (input) => input.trim().length > 0 || "Database name is required",
|
|
372
|
-
},
|
|
373
|
-
{
|
|
374
|
-
type: "input",
|
|
375
|
-
name: "queueArn",
|
|
376
|
-
message: "SQS Queue ARN:",
|
|
377
|
-
default: config.quilt?.queueArn,
|
|
378
|
-
validate: (input) => input.trim().length > 0 && input.startsWith("arn:aws:sqs:") ||
|
|
379
|
-
"Queue ARN is required and must start with arn:aws:sqs:",
|
|
380
|
-
},
|
|
381
|
-
]);
|
|
382
|
-
// Extract region from stack ARN
|
|
383
|
-
const arnMatch = quiltAnswers.stackArn.match(/^arn:aws:cloudformation:([^:]+):/);
|
|
384
|
-
const quiltRegion = arnMatch ? arnMatch[1] : "us-east-1";
|
|
385
|
-
config.quilt = {
|
|
386
|
-
stackArn: quiltAnswers.stackArn,
|
|
387
|
-
catalog: quiltAnswers.catalog,
|
|
388
|
-
bucket: quiltAnswers.bucket,
|
|
389
|
-
database: quiltAnswers.database,
|
|
390
|
-
queueArn: quiltAnswers.queueArn,
|
|
391
|
-
region: quiltRegion,
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
// Prompt for Benchling configuration & guide app setup
|
|
396
|
-
console.log("\nStep 2: Create Benchling App\n");
|
|
397
|
-
let benchlingTenant = config.benchling?.tenant?.trim();
|
|
398
|
-
benchlingTenant = benchlingTenant && benchlingTenant.length > 0 ? benchlingTenant : undefined;
|
|
399
|
-
if (nonInteractive) {
|
|
400
|
-
if (!benchlingTenant) {
|
|
401
|
-
throw new Error("Benchling tenant must be provided in non-interactive mode");
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
else {
|
|
405
|
-
while (!benchlingTenant) {
|
|
406
|
-
const tenantAnswer = await inquirer_1.default.prompt([
|
|
407
|
-
{
|
|
408
|
-
type: "input",
|
|
409
|
-
name: "tenant",
|
|
410
|
-
message: "Benchling tenant (e.g., 'acme' for acme.benchling.com):",
|
|
411
|
-
default: config.benchling?.tenant || "",
|
|
412
|
-
filter: (value) => value.trim(),
|
|
413
|
-
validate: (input) => input.trim().length > 0 || "Tenant is required",
|
|
289
|
+
console.log("Step 1: Quilt Configuration\n");
|
|
290
|
+
const quiltAnswers = await inquirer_1.default.prompt([
|
|
291
|
+
{
|
|
292
|
+
type: "input",
|
|
293
|
+
name: "stackArn",
|
|
294
|
+
message: "Quilt Stack ARN:",
|
|
295
|
+
default: config.quilt?.stackArn,
|
|
296
|
+
validate: (input) => input.trim().length > 0 && input.startsWith("arn:aws:cloudformation:") ||
|
|
297
|
+
"Stack ARN is required and must start with arn:aws:cloudformation:",
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
type: "input",
|
|
301
|
+
name: "catalog",
|
|
302
|
+
message: "Quilt Catalog URL (domain or full URL):",
|
|
303
|
+
default: config.quilt?.catalog,
|
|
304
|
+
validate: (input) => {
|
|
305
|
+
const trimmed = input.trim();
|
|
306
|
+
if (trimmed.length === 0) {
|
|
307
|
+
return "Catalog URL is required";
|
|
308
|
+
}
|
|
309
|
+
return true;
|
|
414
310
|
},
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
if (tenantValidation.isValid) {
|
|
419
|
-
benchlingTenant = candidateTenant;
|
|
420
|
-
if (tenantValidation.warnings && tenantValidation.warnings.length > 0) {
|
|
421
|
-
console.warn("");
|
|
422
|
-
console.warn("⚠ Tenant validation warnings:");
|
|
423
|
-
tenantValidation.warnings.forEach((warning) => console.warn(` - ${warning}`));
|
|
424
|
-
console.warn("");
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
console.error("\n❌ Benchling tenant validation failed:");
|
|
429
|
-
tenantValidation.errors.forEach((err) => console.error(` - ${err}`));
|
|
430
|
-
console.log("");
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
let manifestPath = (0, path_1.join)(process.cwd(), MANIFEST_FILENAME);
|
|
435
|
-
if (!nonInteractive) {
|
|
436
|
-
const manifestContent = (0, manifest_1.generateBenchlingManifest)({
|
|
437
|
-
catalogDomain: config.quilt?.catalog,
|
|
438
|
-
version: pkg.version,
|
|
439
|
-
});
|
|
440
|
-
let shouldWriteManifest = true;
|
|
441
|
-
if ((0, fs_1.existsSync)(manifestPath)) {
|
|
442
|
-
const { overwrite } = await inquirer_1.default.prompt([
|
|
443
|
-
{
|
|
444
|
-
type: "confirm",
|
|
445
|
-
name: "overwrite",
|
|
446
|
-
message: `A manifest already exists at ${manifestPath}. Overwrite it?`,
|
|
447
|
-
default: false,
|
|
311
|
+
filter: (input) => {
|
|
312
|
+
// Strip protocol if present, store only domain
|
|
313
|
+
return input.trim().replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
448
314
|
},
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
console.log(`\n✓ Generated app manifest: ${manifestPath}`);
|
|
458
|
-
}
|
|
459
|
-
const manifestAppName = config.quilt?.catalog && config.quilt.catalog.length > 0
|
|
460
|
-
? config.quilt.catalog.replace(/[.:]/g, "-")
|
|
461
|
-
: "Quilt Integration";
|
|
462
|
-
const instructions = chalk_1.default.bold("Create your Benchling app:\n\n") +
|
|
463
|
-
`1. Open ${chalk_1.default.cyan(`https://${benchlingTenant}.benchling.com/admin/apps`)}\n` +
|
|
464
|
-
"2. Click 'Create New App'\n" +
|
|
465
|
-
`3. Upload the manifest: ${chalk_1.default.cyan(manifestPath)}\n` +
|
|
466
|
-
"4. Create OAuth credentials and copy the Client ID / Secret\n" +
|
|
467
|
-
"5. Install the app (leave webhook URL blank for now)\n" +
|
|
468
|
-
"6. Copy the App Definition ID from the overview page\n";
|
|
469
|
-
console.log();
|
|
470
|
-
console.log((0, boxen_1.default)(instructions, {
|
|
471
|
-
padding: 1,
|
|
472
|
-
borderColor: "blue",
|
|
473
|
-
borderStyle: "round",
|
|
474
|
-
}));
|
|
475
|
-
console.log();
|
|
476
|
-
const { ready } = await inquirer_1.default.prompt([
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
type: "input",
|
|
318
|
+
name: "database",
|
|
319
|
+
message: "Quilt Athena Database:",
|
|
320
|
+
default: config.quilt?.database || "quilt_catalog",
|
|
321
|
+
validate: (input) => input.trim().length > 0 || "Database name is required",
|
|
322
|
+
},
|
|
477
323
|
{
|
|
478
|
-
type: "
|
|
479
|
-
name: "
|
|
480
|
-
message: "
|
|
481
|
-
default:
|
|
324
|
+
type: "input",
|
|
325
|
+
name: "queueUrl",
|
|
326
|
+
message: "SQS Queue URL:",
|
|
327
|
+
default: config.quilt?.queueUrl,
|
|
328
|
+
validate: (input) => {
|
|
329
|
+
return (0, sqs_1.isQueueUrl)(input) ||
|
|
330
|
+
"Queue URL is required and must look like https://sqs.<region>.amazonaws.com/<account>/<queue>";
|
|
331
|
+
},
|
|
482
332
|
},
|
|
483
333
|
]);
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
if (!benchlingClientId || !benchlingClientSecret || !benchlingAppDefinitionId) {
|
|
497
|
-
throw new Error("Non-interactive mode requires Benchling clientId, clientSecret, and appDefinitionId to be configured");
|
|
498
|
-
}
|
|
334
|
+
// Extract region and account ID from stack ARN
|
|
335
|
+
// ARN format: arn:aws:cloudformation:REGION:ACCOUNT_ID:stack/STACK_NAME/STACK_ID
|
|
336
|
+
const arnMatch = quiltAnswers.stackArn.match(/^arn:aws:cloudformation:([^:]+):(\d{12}):/);
|
|
337
|
+
const quiltRegion = arnMatch ? arnMatch[1] : "us-east-1";
|
|
338
|
+
awsAccountId = arnMatch ? arnMatch[2] : undefined;
|
|
339
|
+
config.quilt = {
|
|
340
|
+
stackArn: quiltAnswers.stackArn,
|
|
341
|
+
catalog: quiltAnswers.catalog,
|
|
342
|
+
database: quiltAnswers.database,
|
|
343
|
+
queueUrl: quiltAnswers.queueUrl,
|
|
344
|
+
region: quiltRegion,
|
|
345
|
+
};
|
|
499
346
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
{
|
|
528
|
-
type: "input",
|
|
529
|
-
name: "testEntryId",
|
|
530
|
-
message: "Benchling Test Entry ID (optional):",
|
|
531
|
-
default: benchlingTestEntryId || "",
|
|
532
|
-
filter: (value) => value.trim(),
|
|
533
|
-
},
|
|
534
|
-
]);
|
|
535
|
-
const candidateSecret = credentialAnswers.clientSecret.trim().length === 0 && benchlingClientSecret
|
|
536
|
-
? benchlingClientSecret
|
|
537
|
-
: credentialAnswers.clientSecret.trim();
|
|
538
|
-
if (!candidateSecret) {
|
|
539
|
-
console.error("\n❌ Benchling OAuth client secret is required\n");
|
|
540
|
-
continue;
|
|
541
|
-
}
|
|
542
|
-
const spinner = (0, ora_1.default)("Validating Benchling credentials...").start();
|
|
543
|
-
const validation = await validateBenchlingCredentials(benchlingTenant, credentialAnswers.clientId.trim(), candidateSecret);
|
|
544
|
-
if (validation.isValid) {
|
|
545
|
-
spinner.succeed("Benchling credentials validated");
|
|
546
|
-
if (validation.warnings && validation.warnings.length > 0) {
|
|
547
|
-
console.warn("\n⚠ Credential validation warnings:");
|
|
548
|
-
validation.warnings.forEach((warn) => console.warn(` - ${warn}`));
|
|
549
|
-
console.warn("");
|
|
550
|
-
}
|
|
551
|
-
benchlingClientId = credentialAnswers.clientId.trim();
|
|
552
|
-
benchlingClientSecret = candidateSecret;
|
|
553
|
-
benchlingAppDefinitionId = credentialAnswers.appDefinitionId.trim();
|
|
554
|
-
benchlingTestEntryId = credentialAnswers.testEntryId || undefined;
|
|
555
|
-
credentialsValid = true;
|
|
556
|
-
}
|
|
557
|
-
else {
|
|
558
|
-
spinner.fail("Benchling credential validation failed");
|
|
559
|
-
console.error("");
|
|
560
|
-
validation.errors.forEach((err) => console.error(` - ${err}`));
|
|
561
|
-
console.error("");
|
|
562
|
-
const { retry } = await inquirer_1.default.prompt([
|
|
563
|
-
{
|
|
564
|
-
type: "confirm",
|
|
565
|
-
name: "retry",
|
|
566
|
-
message: "Credentials invalid. Try again?",
|
|
567
|
-
default: true,
|
|
568
|
-
},
|
|
569
|
-
]);
|
|
570
|
-
if (!retry) {
|
|
571
|
-
throw new Error("Setup aborted due to invalid Benchling credentials");
|
|
347
|
+
// Prompt for Benchling configuration
|
|
348
|
+
console.log("\nStep 2: Benchling Configuration\n");
|
|
349
|
+
const benchlingAnswers = await inquirer_1.default.prompt([
|
|
350
|
+
{
|
|
351
|
+
type: "input",
|
|
352
|
+
name: "tenant",
|
|
353
|
+
message: "Benchling Tenant:",
|
|
354
|
+
default: config.benchling?.tenant,
|
|
355
|
+
validate: (input) => input.trim().length > 0 || "Tenant is required",
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
type: "input",
|
|
359
|
+
name: "clientId",
|
|
360
|
+
message: "Benchling OAuth Client ID:",
|
|
361
|
+
default: config.benchling?.clientId,
|
|
362
|
+
validate: (input) => input.trim().length > 0 || "Client ID is required",
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
type: "password",
|
|
366
|
+
name: "clientSecret",
|
|
367
|
+
message: config.benchling?.clientSecret
|
|
368
|
+
? "Benchling OAuth Client Secret (press Enter to keep existing):"
|
|
369
|
+
: "Benchling OAuth Client Secret:",
|
|
370
|
+
validate: (input) => {
|
|
371
|
+
// If there's an existing secret and input is empty, we'll keep the existing one
|
|
372
|
+
if (config.benchling?.clientSecret && input.trim().length === 0) {
|
|
373
|
+
return true;
|
|
572
374
|
}
|
|
573
|
-
|
|
574
|
-
|
|
375
|
+
return input.trim().length > 0 || "Client secret is required";
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
type: "input",
|
|
380
|
+
name: "appDefinitionId",
|
|
381
|
+
message: "Benchling App Definition ID:",
|
|
382
|
+
default: config.benchling?.appDefinitionId,
|
|
383
|
+
validate: (input) => input.trim().length > 0 || "App definition ID is required",
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
type: "input",
|
|
387
|
+
name: "testEntryId",
|
|
388
|
+
message: "Benchling Test Entry ID (optional):",
|
|
389
|
+
default: config.benchling?.testEntryId || "",
|
|
390
|
+
},
|
|
391
|
+
]);
|
|
392
|
+
// Handle empty password input - keep existing secret if user pressed Enter
|
|
393
|
+
if (benchlingAnswers.clientSecret.trim().length === 0 && config.benchling?.clientSecret) {
|
|
394
|
+
benchlingAnswers.clientSecret = config.benchling.clientSecret;
|
|
575
395
|
}
|
|
576
396
|
config.benchling = {
|
|
577
|
-
tenant:
|
|
578
|
-
clientId:
|
|
579
|
-
clientSecret:
|
|
580
|
-
appDefinitionId:
|
|
397
|
+
tenant: benchlingAnswers.tenant,
|
|
398
|
+
clientId: benchlingAnswers.clientId,
|
|
399
|
+
clientSecret: benchlingAnswers.clientSecret,
|
|
400
|
+
appDefinitionId: benchlingAnswers.appDefinitionId,
|
|
581
401
|
};
|
|
582
|
-
if (
|
|
583
|
-
config.benchling.testEntryId =
|
|
402
|
+
if (benchlingAnswers.testEntryId && benchlingAnswers.testEntryId.trim() !== "") {
|
|
403
|
+
config.benchling.testEntryId = benchlingAnswers.testEntryId;
|
|
584
404
|
}
|
|
585
405
|
// Prompt for package configuration
|
|
586
|
-
console.log("\
|
|
406
|
+
console.log("\nStep 3: Package Configuration\n");
|
|
587
407
|
const packageAnswers = await inquirer_1.default.prompt([
|
|
588
408
|
{
|
|
589
409
|
type: "input",
|
|
590
410
|
name: "bucket",
|
|
591
411
|
message: "Package S3 Bucket:",
|
|
592
|
-
default: config.packages?.bucket
|
|
412
|
+
default: config.packages?.bucket,
|
|
593
413
|
validate: (input) => input.trim().length > 0 || "Bucket name is required",
|
|
594
414
|
},
|
|
595
415
|
{
|
|
@@ -611,17 +431,8 @@ async function runConfigWizard(options = {}) {
|
|
|
611
431
|
metadataKey: packageAnswers.metadataKey,
|
|
612
432
|
};
|
|
613
433
|
// Prompt for deployment configuration
|
|
614
|
-
console.log("\
|
|
615
|
-
const defaultAccount = config.deployment?.account || getAccountFromStackArn(config.quilt?.stackArn);
|
|
434
|
+
console.log("\nStep 4: Deployment Configuration\n");
|
|
616
435
|
const deploymentAnswers = await inquirer_1.default.prompt([
|
|
617
|
-
{
|
|
618
|
-
type: "input",
|
|
619
|
-
name: "account",
|
|
620
|
-
message: "AWS Account ID:",
|
|
621
|
-
default: defaultAccount || "",
|
|
622
|
-
filter: (value) => value.trim(),
|
|
623
|
-
validate: (input) => input.trim().length === 0 || /^[0-9]{12}$/.test(input.trim()) || "Account ID must be a 12 digit number",
|
|
624
|
-
},
|
|
625
436
|
{
|
|
626
437
|
type: "input",
|
|
627
438
|
name: "region",
|
|
@@ -630,18 +441,26 @@ async function runConfigWizard(options = {}) {
|
|
|
630
441
|
},
|
|
631
442
|
{
|
|
632
443
|
type: "input",
|
|
633
|
-
name: "
|
|
634
|
-
message: "
|
|
635
|
-
default: config.deployment?.
|
|
444
|
+
name: "account",
|
|
445
|
+
message: "AWS Account ID:",
|
|
446
|
+
default: config.deployment?.account || awsAccountId || config.quilt?.stackArn?.match(/:(\d{12}):/)?.[1],
|
|
447
|
+
validate: (input) => {
|
|
448
|
+
if (!input || input.trim().length === 0) {
|
|
449
|
+
return "AWS Account ID is required";
|
|
450
|
+
}
|
|
451
|
+
if (!/^\d{12}$/.test(input.trim())) {
|
|
452
|
+
return "AWS Account ID must be a 12-digit number";
|
|
453
|
+
}
|
|
454
|
+
return true;
|
|
455
|
+
},
|
|
636
456
|
},
|
|
637
457
|
]);
|
|
638
458
|
config.deployment = {
|
|
639
|
-
...(deploymentAnswers.account ? { account: deploymentAnswers.account } : {}),
|
|
640
459
|
region: deploymentAnswers.region,
|
|
641
|
-
|
|
460
|
+
account: deploymentAnswers.account,
|
|
642
461
|
};
|
|
643
462
|
// Optional: Logging configuration
|
|
644
|
-
console.log("\
|
|
463
|
+
console.log("\nStep 5: Optional Configuration\n");
|
|
645
464
|
const optionalAnswers = await inquirer_1.default.prompt([
|
|
646
465
|
{
|
|
647
466
|
type: "list",
|
|
@@ -650,12 +469,6 @@ async function runConfigWizard(options = {}) {
|
|
|
650
469
|
choices: ["DEBUG", "INFO", "WARNING", "ERROR"],
|
|
651
470
|
default: config.logging?.level || "INFO",
|
|
652
471
|
},
|
|
653
|
-
{
|
|
654
|
-
type: "confirm",
|
|
655
|
-
name: "enableVerification",
|
|
656
|
-
message: "Enable webhook signature verification:",
|
|
657
|
-
default: config.security?.enableVerification !== false,
|
|
658
|
-
},
|
|
659
472
|
{
|
|
660
473
|
type: "input",
|
|
661
474
|
name: "webhookAllowList",
|
|
@@ -667,13 +480,13 @@ async function runConfigWizard(options = {}) {
|
|
|
667
480
|
level: optionalAnswers.logLevel,
|
|
668
481
|
};
|
|
669
482
|
config.security = {
|
|
670
|
-
enableVerification:
|
|
483
|
+
enableVerification: true,
|
|
671
484
|
webhookAllowList: optionalAnswers.webhookAllowList,
|
|
672
485
|
};
|
|
673
486
|
// Add metadata
|
|
674
487
|
const now = new Date().toISOString();
|
|
675
488
|
config._metadata = {
|
|
676
|
-
version:
|
|
489
|
+
version: "0.7.0",
|
|
677
490
|
createdAt: config._metadata?.createdAt || now,
|
|
678
491
|
updatedAt: now,
|
|
679
492
|
source: "wizard",
|
|
@@ -695,37 +508,41 @@ async function runConfigWizard(options = {}) {
|
|
|
695
508
|
* 5. Save to XDG config directory
|
|
696
509
|
*/
|
|
697
510
|
async function runInstallWizard(options = {}) {
|
|
698
|
-
const { profile = "default", inheritFrom, nonInteractive = false, skipValidation = false,
|
|
511
|
+
const { profile = "default", inheritFrom, nonInteractive = false, skipValidation = false, awsProfile, awsRegion = "us-east-1", } = options;
|
|
699
512
|
const xdg = new xdg_config_1.XDGConfig();
|
|
700
513
|
console.log("\n╔═══════════════════════════════════════════════════════════╗");
|
|
701
|
-
|
|
702
|
-
console.log(`${headerLine.padEnd(59, " ")}║`);
|
|
514
|
+
console.log("║ Benchling Webhook Setup (v0.7.0) ║");
|
|
703
515
|
console.log("╚═══════════════════════════════════════════════════════════╝\n");
|
|
704
516
|
// Step 1: Load existing configuration (if profile exists)
|
|
705
517
|
let existingConfig;
|
|
518
|
+
// Determine if we should inherit from 'default' when profile is not 'default'
|
|
519
|
+
const shouldInheritFromDefault = profile !== "default" && !inheritFrom;
|
|
520
|
+
const effectiveInheritFrom = inheritFrom || (shouldInheritFromDefault ? "default" : undefined);
|
|
706
521
|
if (xdg.profileExists(profile)) {
|
|
707
522
|
console.log(`Loading existing configuration for profile: ${profile}\n`);
|
|
708
523
|
try {
|
|
709
|
-
existingConfig =
|
|
710
|
-
? xdg.readProfileWithInheritance(profile,
|
|
524
|
+
existingConfig = effectiveInheritFrom
|
|
525
|
+
? xdg.readProfileWithInheritance(profile, effectiveInheritFrom)
|
|
711
526
|
: xdg.readProfile(profile);
|
|
712
527
|
}
|
|
713
528
|
catch (error) {
|
|
714
529
|
console.warn(`Warning: Could not load existing config: ${error.message}`);
|
|
715
530
|
}
|
|
716
531
|
}
|
|
717
|
-
else if (
|
|
718
|
-
|
|
532
|
+
else if (effectiveInheritFrom) {
|
|
533
|
+
// If profile doesn't exist but we should inherit, load base profile
|
|
534
|
+
console.log(`Creating new profile '${profile}' inheriting from '${effectiveInheritFrom}'\n`);
|
|
719
535
|
try {
|
|
720
|
-
existingConfig = xdg.readProfile(
|
|
536
|
+
existingConfig = xdg.readProfile(effectiveInheritFrom);
|
|
721
537
|
}
|
|
722
538
|
catch (error) {
|
|
723
|
-
throw new Error(`Base profile '${
|
|
539
|
+
throw new Error(`Base profile '${effectiveInheritFrom}' not found: ${error.message}`);
|
|
724
540
|
}
|
|
725
541
|
}
|
|
726
542
|
// Step 2: Infer Quilt configuration (unless inheriting from another profile)
|
|
727
543
|
let quiltConfig = existingConfig?.quilt || {};
|
|
728
|
-
|
|
544
|
+
let inferredAccountId;
|
|
545
|
+
if (!effectiveInheritFrom || !existingConfig?.quilt) {
|
|
729
546
|
console.log("Step 1: Inferring Quilt configuration from AWS...\n");
|
|
730
547
|
try {
|
|
731
548
|
const inferenceResult = await (0, infer_quilt_config_1.inferQuiltConfig)({
|
|
@@ -733,29 +550,8 @@ async function runInstallWizard(options = {}) {
|
|
|
733
550
|
profile: awsProfile,
|
|
734
551
|
interactive: !nonInteractive,
|
|
735
552
|
});
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
quiltConfig.stackArn = inferenceResult.quiltStackArn;
|
|
739
|
-
}
|
|
740
|
-
if (inferenceResult.catalogUrl) {
|
|
741
|
-
// Strip protocol and trailing slash to store only domain
|
|
742
|
-
quiltConfig.catalog = inferenceResult.catalogUrl.replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
743
|
-
}
|
|
744
|
-
if (inferenceResult.quiltUserBucket) {
|
|
745
|
-
quiltConfig.bucket = inferenceResult.quiltUserBucket;
|
|
746
|
-
}
|
|
747
|
-
if (inferenceResult.quiltDatabase) {
|
|
748
|
-
quiltConfig.database = inferenceResult.quiltDatabase;
|
|
749
|
-
}
|
|
750
|
-
if (inferenceResult.queueArn) {
|
|
751
|
-
const queueArn = inferenceResult.queueArn;
|
|
752
|
-
if (queueArn.startsWith("arn:aws:sqs:") || !quiltConfig.queueArn) {
|
|
753
|
-
quiltConfig.queueArn = queueArn;
|
|
754
|
-
}
|
|
755
|
-
}
|
|
756
|
-
if (inferenceResult.quiltRegion) {
|
|
757
|
-
quiltConfig.region = inferenceResult.quiltRegion;
|
|
758
|
-
}
|
|
553
|
+
quiltConfig = inferenceResult;
|
|
554
|
+
inferredAccountId = inferenceResult.account;
|
|
759
555
|
console.log("✓ Quilt configuration inferred\n");
|
|
760
556
|
}
|
|
761
557
|
catch (error) {
|
|
@@ -783,12 +579,17 @@ async function runInstallWizard(options = {}) {
|
|
|
783
579
|
...existingConfig?.quilt,
|
|
784
580
|
...quiltConfig,
|
|
785
581
|
},
|
|
582
|
+
// Pass through inferred account ID for deployment config
|
|
583
|
+
deployment: {
|
|
584
|
+
...existingConfig?.deployment,
|
|
585
|
+
account: existingConfig?.deployment?.account || inferredAccountId,
|
|
586
|
+
},
|
|
786
587
|
};
|
|
787
588
|
// Step 3: Run interactive wizard for remaining configuration
|
|
788
|
-
|
|
589
|
+
const config = await runConfigWizard({
|
|
789
590
|
existingConfig: partialConfig,
|
|
790
591
|
nonInteractive,
|
|
791
|
-
inheritFrom,
|
|
592
|
+
inheritFrom: effectiveInheritFrom,
|
|
792
593
|
});
|
|
793
594
|
// Step 4: Validate configuration
|
|
794
595
|
if (!skipValidation) {
|
|
@@ -824,66 +625,23 @@ async function runInstallWizard(options = {}) {
|
|
|
824
625
|
console.log("");
|
|
825
626
|
}
|
|
826
627
|
}
|
|
827
|
-
// Step 5:
|
|
628
|
+
// Step 5: Save configuration
|
|
828
629
|
console.log(`Saving configuration to profile: ${profile}...\n`);
|
|
829
630
|
try {
|
|
830
631
|
xdg.writeProfile(profile, config);
|
|
831
|
-
console.log(
|
|
632
|
+
console.log(`✓ Configuration saved to: ~/.config/benchling-webhook/${profile}/config.json\n`);
|
|
832
633
|
}
|
|
833
634
|
catch (error) {
|
|
834
635
|
throw new Error(`Failed to save configuration: ${error.message}`);
|
|
835
636
|
}
|
|
836
|
-
// Step 6:
|
|
837
|
-
console.log(
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
try {
|
|
845
|
-
await (0, sync_secrets_1.syncSecretsToAWS)({
|
|
846
|
-
profile,
|
|
847
|
-
awsProfile,
|
|
848
|
-
region: config.deployment.region,
|
|
849
|
-
force: true,
|
|
850
|
-
});
|
|
851
|
-
console.log(chalk_1.default.green("\n✓ Secrets synced to AWS Secrets Manager\n"));
|
|
852
|
-
// Reload config to capture secret ARN written by sync
|
|
853
|
-
config = xdg.readProfile(profile);
|
|
854
|
-
}
|
|
855
|
-
catch (error) {
|
|
856
|
-
throw new Error(`Secrets sync failed: ${error.message}`);
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
if (skipDeployment) {
|
|
860
|
-
console.log(chalk_1.default.yellow("Skipping deployment (--skip-deployment)."));
|
|
861
|
-
console.log(chalk_1.default.yellow(`Run \`npm run deploy -- --profile ${profile} --stage ${deployStage}\` to deploy later.\n`));
|
|
862
|
-
return config;
|
|
863
|
-
}
|
|
864
|
-
if (!nonInteractive) {
|
|
865
|
-
console.log(`Deploy target: profile=${chalk_1.default.cyan(profile)}, stage=${chalk_1.default.cyan(deployStage)}, region=${chalk_1.default.cyan(config.deployment.region)}\n`);
|
|
866
|
-
const { confirmDeploy } = await inquirer_1.default.prompt([
|
|
867
|
-
{
|
|
868
|
-
type: "confirm",
|
|
869
|
-
name: "confirmDeploy",
|
|
870
|
-
message: "Deploy AWS infrastructure now? (takes ~5-10 minutes)",
|
|
871
|
-
default: true,
|
|
872
|
-
},
|
|
873
|
-
]);
|
|
874
|
-
if (!confirmDeploy) {
|
|
875
|
-
console.log(chalk_1.default.yellow("\nDeployment skipped by user."));
|
|
876
|
-
console.log(chalk_1.default.yellow(`Re-run with \`npm run deploy -- --profile ${profile} --stage ${deployStage}\` when ready.\n`));
|
|
877
|
-
return config;
|
|
878
|
-
}
|
|
879
|
-
}
|
|
880
|
-
console.log("Deploying AWS infrastructure. This can take several minutes...\n");
|
|
881
|
-
await (0, deploy_1.deployCommand)({
|
|
882
|
-
profile,
|
|
883
|
-
stage: deployStage,
|
|
884
|
-
requireApproval: "never",
|
|
885
|
-
yes: true,
|
|
886
|
-
});
|
|
637
|
+
// Step 6: Display next steps
|
|
638
|
+
console.log("╔═══════════════════════════════════════════════════════════╗");
|
|
639
|
+
console.log("║ Setup Complete! ║");
|
|
640
|
+
console.log("╚═══════════════════════════════════════════════════════════╝\n");
|
|
641
|
+
console.log("Next steps:");
|
|
642
|
+
console.log(" 1. Sync secrets to AWS: npm run setup:sync-secrets");
|
|
643
|
+
console.log(" 2. Deploy to AWS: npm run deploy:dev");
|
|
644
|
+
console.log(" 3. Test integration: npm run test:dev\n");
|
|
887
645
|
return config;
|
|
888
646
|
}
|
|
889
647
|
// =============================================================================
|