@quiltdata/benchling-webhook 0.7.2-20251106T010445Z → 0.7.3-20251106T131511Z
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 +0 -1
- package/dist/bin/benchling-webhook.d.ts.map +1 -1
- package/dist/bin/benchling-webhook.js +2 -7
- package/dist/bin/benchling-webhook.js.map +1 -1
- package/dist/bin/cli.js +17 -12
- package/dist/bin/cli.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 +83 -10
- 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 +96 -69
- package/dist/bin/commands/infer-quilt-config.js.map +1 -1
- package/dist/bin/commands/init.d.ts +5 -1
- package/dist/bin/commands/init.d.ts.map +1 -1
- package/dist/bin/commands/init.js +19 -111
- package/dist/bin/commands/init.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 +227 -405
- 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 +0 -6
- package/dist/lib/benchling-webhook-stack.d.ts.map +1 -1
- package/dist/lib/benchling-webhook-stack.js +21 -20
- 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 +7 -4
- package/dist/scripts/check-logs.d.ts +1 -1
- package/dist/scripts/check-logs.d.ts.map +1 -1
- package/dist/scripts/check-logs.js +10 -4
- package/dist/scripts/check-logs.js.map +1 -1
- 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/dist/scripts/send-event.d.ts +1 -1
- package/dist/scripts/send-event.d.ts.map +1 -1
- package/dist/scripts/send-event.js +4 -5
- package/dist/scripts/send-event.js.map +1 -1
- package/package.json +7 -4
- 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,13 @@ 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
57
|
const chalk_1 = __importDefault(require("chalk"));
|
|
60
|
-
const boxen_1 = __importDefault(require("boxen"));
|
|
61
|
-
const ora_1 = __importDefault(require("ora"));
|
|
62
58
|
const client_s3_1 = require("@aws-sdk/client-s3");
|
|
63
59
|
const xdg_config_1 = require("../../lib/xdg-config");
|
|
64
60
|
const infer_quilt_config_1 = require("../commands/infer-quilt-config");
|
|
61
|
+
const sqs_1 = require("../../lib/utils/sqs");
|
|
65
62
|
const manifest_1 = require("./manifest");
|
|
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
|
-
}
|
|
82
63
|
// =============================================================================
|
|
83
64
|
// VALIDATION FUNCTIONS (from scripts/config/validator.ts)
|
|
84
65
|
// =============================================================================
|
|
@@ -285,311 +266,195 @@ async function runConfigWizard(options = {}) {
|
|
|
285
266
|
console.log(`Creating profile inheriting from: ${inheritFrom}\n`);
|
|
286
267
|
}
|
|
287
268
|
const config = { ...existingConfig };
|
|
269
|
+
let awsAccountId;
|
|
288
270
|
// If non-interactive, validate that all required fields are present
|
|
289
271
|
if (nonInteractive) {
|
|
290
272
|
if (!config.benchling?.tenant || !config.benchling?.clientId || !config.benchling?.clientSecret) {
|
|
291
273
|
throw new Error("Non-interactive mode requires benchlingTenant, benchlingClientId, and benchlingClientSecret to be already configured");
|
|
292
274
|
}
|
|
293
|
-
|
|
275
|
+
// Add metadata and inheritance marker before returning
|
|
276
|
+
const now = new Date().toISOString();
|
|
277
|
+
const finalConfig = config;
|
|
278
|
+
finalConfig._metadata = {
|
|
279
|
+
version: "0.7.0",
|
|
280
|
+
createdAt: config._metadata?.createdAt || now,
|
|
281
|
+
updatedAt: now,
|
|
282
|
+
source: "wizard",
|
|
283
|
+
};
|
|
284
|
+
if (inheritFrom) {
|
|
285
|
+
finalConfig._inherits = inheritFrom;
|
|
286
|
+
}
|
|
287
|
+
return finalConfig;
|
|
294
288
|
}
|
|
295
289
|
// Prompt for Quilt configuration (if not inherited)
|
|
296
290
|
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",
|
|
291
|
+
console.log("Step 1: Quilt Configuration\n");
|
|
292
|
+
const quiltAnswers = await inquirer_1.default.prompt([
|
|
293
|
+
{
|
|
294
|
+
type: "input",
|
|
295
|
+
name: "stackArn",
|
|
296
|
+
message: "Quilt Stack ARN:",
|
|
297
|
+
default: config.quilt?.stackArn,
|
|
298
|
+
validate: (input) => input.trim().length > 0 && input.startsWith("arn:aws:cloudformation:") ||
|
|
299
|
+
"Stack ARN is required and must start with arn:aws:cloudformation:",
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
type: "input",
|
|
303
|
+
name: "catalog",
|
|
304
|
+
message: "Quilt Catalog URL (domain or full URL):",
|
|
305
|
+
default: config.quilt?.catalog,
|
|
306
|
+
validate: (input) => {
|
|
307
|
+
const trimmed = input.trim();
|
|
308
|
+
if (trimmed.length === 0) {
|
|
309
|
+
return "Catalog URL is required";
|
|
310
|
+
}
|
|
311
|
+
return true;
|
|
365
312
|
},
|
|
366
|
-
{
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
message: "Quilt Athena Database:",
|
|
370
|
-
default: config.quilt?.database || "quilt_catalog",
|
|
371
|
-
validate: (input) => input.trim().length > 0 || "Database name is required",
|
|
313
|
+
filter: (input) => {
|
|
314
|
+
// Strip protocol if present, store only domain
|
|
315
|
+
return input.trim().replace(/^https?:\/\//, "").replace(/\/$/, "");
|
|
372
316
|
},
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
type: "input",
|
|
320
|
+
name: "database",
|
|
321
|
+
message: "Quilt Athena Database:",
|
|
322
|
+
default: config.quilt?.database || "quilt_catalog",
|
|
323
|
+
validate: (input) => input.trim().length > 0 || "Database name is required",
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
type: "input",
|
|
327
|
+
name: "queueUrl",
|
|
328
|
+
message: "SQS Queue URL:",
|
|
329
|
+
default: config.quilt?.queueUrl,
|
|
330
|
+
validate: (input) => {
|
|
331
|
+
return (0, sqs_1.isQueueUrl)(input) ||
|
|
332
|
+
"Queue URL is required and must look like https://sqs.<region>.amazonaws.com/<account>/<queue>";
|
|
380
333
|
},
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
334
|
+
},
|
|
335
|
+
]);
|
|
336
|
+
// Extract region and account ID from stack ARN
|
|
337
|
+
// ARN format: arn:aws:cloudformation:REGION:ACCOUNT_ID:stack/STACK_NAME/STACK_ID
|
|
338
|
+
const arnMatch = quiltAnswers.stackArn.match(/^arn:aws:cloudformation:([^:]+):(\d{12}):/);
|
|
339
|
+
const quiltRegion = arnMatch ? arnMatch[1] : "us-east-1";
|
|
340
|
+
awsAccountId = arnMatch ? arnMatch[2] : undefined;
|
|
341
|
+
config.quilt = {
|
|
342
|
+
stackArn: quiltAnswers.stackArn,
|
|
343
|
+
catalog: quiltAnswers.catalog,
|
|
344
|
+
database: quiltAnswers.database,
|
|
345
|
+
queueUrl: quiltAnswers.queueUrl,
|
|
346
|
+
region: quiltRegion,
|
|
347
|
+
};
|
|
394
348
|
}
|
|
395
|
-
// Prompt for Benchling configuration
|
|
396
|
-
console.log("\nStep 2:
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
349
|
+
// Prompt for Benchling configuration
|
|
350
|
+
console.log("\nStep 2: Benchling Configuration\n");
|
|
351
|
+
// First, get tenant
|
|
352
|
+
const tenantAnswer = await inquirer_1.default.prompt([
|
|
353
|
+
{
|
|
354
|
+
type: "input",
|
|
355
|
+
name: "tenant",
|
|
356
|
+
message: "Benchling Tenant:",
|
|
357
|
+
default: config.benchling?.tenant,
|
|
358
|
+
validate: (input) => input.trim().length > 0 || "Tenant is required",
|
|
359
|
+
},
|
|
360
|
+
]);
|
|
361
|
+
// Ask if they have an app_definition_id BEFORE asking for credentials
|
|
362
|
+
const hasAppDefId = await inquirer_1.default.prompt([
|
|
363
|
+
{
|
|
364
|
+
type: "confirm",
|
|
365
|
+
name: "hasIt",
|
|
366
|
+
message: "Do you have a Benchling App Definition ID for this app?",
|
|
367
|
+
default: !!config.benchling?.appDefinitionId,
|
|
368
|
+
},
|
|
369
|
+
]);
|
|
370
|
+
let appDefinitionId;
|
|
371
|
+
if (hasAppDefId.hasIt) {
|
|
372
|
+
// They have it, ask for it
|
|
373
|
+
const appDefAnswer = await inquirer_1.default.prompt([
|
|
374
|
+
{
|
|
375
|
+
type: "input",
|
|
376
|
+
name: "appDefinitionId",
|
|
377
|
+
message: "Benchling App Definition ID:",
|
|
378
|
+
default: config.benchling?.appDefinitionId,
|
|
379
|
+
validate: (input) => input.trim().length > 0 || "App definition ID is required",
|
|
380
|
+
},
|
|
381
|
+
]);
|
|
382
|
+
appDefinitionId = appDefAnswer.appDefinitionId;
|
|
403
383
|
}
|
|
404
384
|
else {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
default: config.benchling?.tenant || "",
|
|
412
|
-
filter: (value) => value.trim(),
|
|
413
|
-
validate: (input) => input.trim().length > 0 || "Tenant is required",
|
|
414
|
-
},
|
|
415
|
-
]);
|
|
416
|
-
const candidateTenant = tenantAnswer.tenant.trim();
|
|
417
|
-
const tenantValidation = await validateBenchlingTenant(candidateTenant);
|
|
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,
|
|
385
|
+
// They don't have it, create the manifest and show instructions
|
|
386
|
+
console.log("\n" + chalk_1.default.blue("Creating app manifest...") + "\n");
|
|
387
|
+
// Create manifest using the existing command
|
|
388
|
+
await (0, manifest_1.manifestCommand)({
|
|
389
|
+
catalog: config.quilt?.catalog,
|
|
390
|
+
output: "app-manifest.yaml",
|
|
439
391
|
});
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
{
|
|
444
|
-
type: "confirm",
|
|
445
|
-
name: "overwrite",
|
|
446
|
-
message: `A manifest already exists at ${manifestPath}. Overwrite it?`,
|
|
447
|
-
default: false,
|
|
448
|
-
},
|
|
449
|
-
]);
|
|
450
|
-
shouldWriteManifest = overwrite;
|
|
451
|
-
if (!overwrite) {
|
|
452
|
-
console.log(`\nUsing existing manifest: ${manifestPath}`);
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
if (shouldWriteManifest) {
|
|
456
|
-
(0, fs_1.writeFileSync)(manifestPath, manifestContent, "utf-8");
|
|
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([
|
|
392
|
+
console.log("\n" + chalk_1.default.yellow("After you have installed the app in Benchling and have the App Definition ID, you can continue.") + "\n");
|
|
393
|
+
// Now ask for the app definition ID
|
|
394
|
+
const appDefAnswer = await inquirer_1.default.prompt([
|
|
477
395
|
{
|
|
478
|
-
type: "
|
|
479
|
-
name: "
|
|
480
|
-
message: "
|
|
481
|
-
|
|
396
|
+
type: "input",
|
|
397
|
+
name: "appDefinitionId",
|
|
398
|
+
message: "Benchling App Definition ID:",
|
|
399
|
+
validate: (input) => input.trim().length > 0 || "App definition ID is required",
|
|
482
400
|
},
|
|
483
401
|
]);
|
|
484
|
-
|
|
485
|
-
console.log("\n⏸ Setup paused. Re-run when ready:\n");
|
|
486
|
-
console.log(` ${chalk_1.default.cyan("npx @quiltdata/benchling-webhook setup")}\n`);
|
|
487
|
-
process.exit(0);
|
|
488
|
-
}
|
|
489
|
-
console.log(`Using manifest app name: ${manifestAppName}\n`);
|
|
402
|
+
appDefinitionId = appDefAnswer.appDefinitionId;
|
|
490
403
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
validate: (input) => input.trim().length > 0 || "Client ID is required",
|
|
511
|
-
},
|
|
512
|
-
{
|
|
513
|
-
type: "password",
|
|
514
|
-
name: "clientSecret",
|
|
515
|
-
message: benchlingClientSecret
|
|
516
|
-
? "Benchling OAuth Client Secret (press Enter to keep existing):"
|
|
517
|
-
: "Benchling OAuth Client Secret:",
|
|
518
|
-
},
|
|
519
|
-
{
|
|
520
|
-
type: "input",
|
|
521
|
-
name: "appDefinitionId",
|
|
522
|
-
message: "Benchling App Definition ID:",
|
|
523
|
-
default: benchlingAppDefinitionId || "",
|
|
524
|
-
filter: (value) => value.trim(),
|
|
525
|
-
validate: (input) => input.trim().length > 0 || "App Definition ID is required",
|
|
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");
|
|
404
|
+
// Now ask for OAuth credentials (which must come from the app)
|
|
405
|
+
const credentialAnswers = await inquirer_1.default.prompt([
|
|
406
|
+
{
|
|
407
|
+
type: "input",
|
|
408
|
+
name: "clientId",
|
|
409
|
+
message: "Benchling OAuth Client ID (from the app above):",
|
|
410
|
+
default: config.benchling?.clientId,
|
|
411
|
+
validate: (input) => input.trim().length > 0 || "Client ID is required",
|
|
412
|
+
},
|
|
413
|
+
{
|
|
414
|
+
type: "password",
|
|
415
|
+
name: "clientSecret",
|
|
416
|
+
message: config.benchling?.clientSecret
|
|
417
|
+
? "Benchling OAuth Client Secret (press Enter to keep existing):"
|
|
418
|
+
: "Benchling OAuth Client Secret (from the app above):",
|
|
419
|
+
validate: (input) => {
|
|
420
|
+
// If there's an existing secret and input is empty, we'll keep the existing one
|
|
421
|
+
if (config.benchling?.clientSecret && input.trim().length === 0) {
|
|
422
|
+
return true;
|
|
572
423
|
}
|
|
573
|
-
|
|
574
|
-
|
|
424
|
+
return input.trim().length > 0 || "Client secret is required";
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
]);
|
|
428
|
+
// Ask for optional test entry ID
|
|
429
|
+
const testEntryAnswer = await inquirer_1.default.prompt([
|
|
430
|
+
{
|
|
431
|
+
type: "input",
|
|
432
|
+
name: "testEntryId",
|
|
433
|
+
message: "Benchling Test Entry ID (optional):",
|
|
434
|
+
default: config.benchling?.testEntryId || "",
|
|
435
|
+
},
|
|
436
|
+
]);
|
|
437
|
+
// Handle empty password input - keep existing secret if user pressed Enter
|
|
438
|
+
if (credentialAnswers.clientSecret.trim().length === 0 && config.benchling?.clientSecret) {
|
|
439
|
+
credentialAnswers.clientSecret = config.benchling.clientSecret;
|
|
575
440
|
}
|
|
576
441
|
config.benchling = {
|
|
577
|
-
tenant:
|
|
578
|
-
clientId:
|
|
579
|
-
clientSecret:
|
|
580
|
-
appDefinitionId:
|
|
442
|
+
tenant: tenantAnswer.tenant,
|
|
443
|
+
clientId: credentialAnswers.clientId,
|
|
444
|
+
clientSecret: credentialAnswers.clientSecret,
|
|
445
|
+
appDefinitionId: appDefinitionId,
|
|
581
446
|
};
|
|
582
|
-
if (
|
|
583
|
-
config.benchling.testEntryId =
|
|
447
|
+
if (testEntryAnswer.testEntryId && testEntryAnswer.testEntryId.trim() !== "") {
|
|
448
|
+
config.benchling.testEntryId = testEntryAnswer.testEntryId;
|
|
584
449
|
}
|
|
585
450
|
// Prompt for package configuration
|
|
586
|
-
console.log("\
|
|
451
|
+
console.log("\nStep 3: Package Configuration\n");
|
|
587
452
|
const packageAnswers = await inquirer_1.default.prompt([
|
|
588
453
|
{
|
|
589
454
|
type: "input",
|
|
590
455
|
name: "bucket",
|
|
591
456
|
message: "Package S3 Bucket:",
|
|
592
|
-
default: config.packages?.bucket
|
|
457
|
+
default: config.packages?.bucket,
|
|
593
458
|
validate: (input) => input.trim().length > 0 || "Bucket name is required",
|
|
594
459
|
},
|
|
595
460
|
{
|
|
@@ -611,17 +476,8 @@ async function runConfigWizard(options = {}) {
|
|
|
611
476
|
metadataKey: packageAnswers.metadataKey,
|
|
612
477
|
};
|
|
613
478
|
// Prompt for deployment configuration
|
|
614
|
-
console.log("\
|
|
615
|
-
const defaultAccount = config.deployment?.account || getAccountFromStackArn(config.quilt?.stackArn);
|
|
479
|
+
console.log("\nStep 4: Deployment Configuration\n");
|
|
616
480
|
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
481
|
{
|
|
626
482
|
type: "input",
|
|
627
483
|
name: "region",
|
|
@@ -630,18 +486,26 @@ async function runConfigWizard(options = {}) {
|
|
|
630
486
|
},
|
|
631
487
|
{
|
|
632
488
|
type: "input",
|
|
633
|
-
name: "
|
|
634
|
-
message: "
|
|
635
|
-
default: config.deployment?.
|
|
489
|
+
name: "account",
|
|
490
|
+
message: "AWS Account ID:",
|
|
491
|
+
default: config.deployment?.account || awsAccountId || config.quilt?.stackArn?.match(/:(\d{12}):/)?.[1],
|
|
492
|
+
validate: (input) => {
|
|
493
|
+
if (!input || input.trim().length === 0) {
|
|
494
|
+
return "AWS Account ID is required";
|
|
495
|
+
}
|
|
496
|
+
if (!/^\d{12}$/.test(input.trim())) {
|
|
497
|
+
return "AWS Account ID must be a 12-digit number";
|
|
498
|
+
}
|
|
499
|
+
return true;
|
|
500
|
+
},
|
|
636
501
|
},
|
|
637
502
|
]);
|
|
638
503
|
config.deployment = {
|
|
639
|
-
...(deploymentAnswers.account ? { account: deploymentAnswers.account } : {}),
|
|
640
504
|
region: deploymentAnswers.region,
|
|
641
|
-
|
|
505
|
+
account: deploymentAnswers.account,
|
|
642
506
|
};
|
|
643
507
|
// Optional: Logging configuration
|
|
644
|
-
console.log("\
|
|
508
|
+
console.log("\nStep 5: Optional Configuration\n");
|
|
645
509
|
const optionalAnswers = await inquirer_1.default.prompt([
|
|
646
510
|
{
|
|
647
511
|
type: "list",
|
|
@@ -650,12 +514,6 @@ async function runConfigWizard(options = {}) {
|
|
|
650
514
|
choices: ["DEBUG", "INFO", "WARNING", "ERROR"],
|
|
651
515
|
default: config.logging?.level || "INFO",
|
|
652
516
|
},
|
|
653
|
-
{
|
|
654
|
-
type: "confirm",
|
|
655
|
-
name: "enableVerification",
|
|
656
|
-
message: "Enable webhook signature verification:",
|
|
657
|
-
default: config.security?.enableVerification !== false,
|
|
658
|
-
},
|
|
659
517
|
{
|
|
660
518
|
type: "input",
|
|
661
519
|
name: "webhookAllowList",
|
|
@@ -667,13 +525,13 @@ async function runConfigWizard(options = {}) {
|
|
|
667
525
|
level: optionalAnswers.logLevel,
|
|
668
526
|
};
|
|
669
527
|
config.security = {
|
|
670
|
-
enableVerification:
|
|
528
|
+
enableVerification: true,
|
|
671
529
|
webhookAllowList: optionalAnswers.webhookAllowList,
|
|
672
530
|
};
|
|
673
531
|
// Add metadata
|
|
674
532
|
const now = new Date().toISOString();
|
|
675
533
|
config._metadata = {
|
|
676
|
-
version:
|
|
534
|
+
version: "0.7.0",
|
|
677
535
|
createdAt: config._metadata?.createdAt || now,
|
|
678
536
|
updatedAt: now,
|
|
679
537
|
source: "wizard",
|
|
@@ -695,37 +553,41 @@ async function runConfigWizard(options = {}) {
|
|
|
695
553
|
* 5. Save to XDG config directory
|
|
696
554
|
*/
|
|
697
555
|
async function runInstallWizard(options = {}) {
|
|
698
|
-
const { profile = "default", inheritFrom, nonInteractive = false, skipValidation = false,
|
|
556
|
+
const { profile = "default", inheritFrom, nonInteractive = false, skipValidation = false, awsProfile, awsRegion = "us-east-1", } = options;
|
|
699
557
|
const xdg = new xdg_config_1.XDGConfig();
|
|
700
558
|
console.log("\n╔═══════════════════════════════════════════════════════════╗");
|
|
701
|
-
|
|
702
|
-
console.log(`${headerLine.padEnd(59, " ")}║`);
|
|
559
|
+
console.log("║ Benchling Webhook Setup (v0.7.0) ║");
|
|
703
560
|
console.log("╚═══════════════════════════════════════════════════════════╝\n");
|
|
704
561
|
// Step 1: Load existing configuration (if profile exists)
|
|
705
562
|
let existingConfig;
|
|
563
|
+
// Determine if we should inherit from 'default' when profile is not 'default'
|
|
564
|
+
const shouldInheritFromDefault = profile !== "default" && !inheritFrom;
|
|
565
|
+
const effectiveInheritFrom = inheritFrom || (shouldInheritFromDefault ? "default" : undefined);
|
|
706
566
|
if (xdg.profileExists(profile)) {
|
|
707
567
|
console.log(`Loading existing configuration for profile: ${profile}\n`);
|
|
708
568
|
try {
|
|
709
|
-
existingConfig =
|
|
710
|
-
? xdg.readProfileWithInheritance(profile,
|
|
569
|
+
existingConfig = effectiveInheritFrom
|
|
570
|
+
? xdg.readProfileWithInheritance(profile, effectiveInheritFrom)
|
|
711
571
|
: xdg.readProfile(profile);
|
|
712
572
|
}
|
|
713
573
|
catch (error) {
|
|
714
574
|
console.warn(`Warning: Could not load existing config: ${error.message}`);
|
|
715
575
|
}
|
|
716
576
|
}
|
|
717
|
-
else if (
|
|
718
|
-
|
|
577
|
+
else if (effectiveInheritFrom) {
|
|
578
|
+
// If profile doesn't exist but we should inherit, load base profile
|
|
579
|
+
console.log(`Creating new profile '${profile}' inheriting from '${effectiveInheritFrom}'\n`);
|
|
719
580
|
try {
|
|
720
|
-
existingConfig = xdg.readProfile(
|
|
581
|
+
existingConfig = xdg.readProfile(effectiveInheritFrom);
|
|
721
582
|
}
|
|
722
583
|
catch (error) {
|
|
723
|
-
throw new Error(`Base profile '${
|
|
584
|
+
throw new Error(`Base profile '${effectiveInheritFrom}' not found: ${error.message}`);
|
|
724
585
|
}
|
|
725
586
|
}
|
|
726
587
|
// Step 2: Infer Quilt configuration (unless inheriting from another profile)
|
|
727
588
|
let quiltConfig = existingConfig?.quilt || {};
|
|
728
|
-
|
|
589
|
+
let inferredAccountId;
|
|
590
|
+
if (!effectiveInheritFrom || !existingConfig?.quilt) {
|
|
729
591
|
console.log("Step 1: Inferring Quilt configuration from AWS...\n");
|
|
730
592
|
try {
|
|
731
593
|
const inferenceResult = await (0, infer_quilt_config_1.inferQuiltConfig)({
|
|
@@ -733,29 +595,8 @@ async function runInstallWizard(options = {}) {
|
|
|
733
595
|
profile: awsProfile,
|
|
734
596
|
interactive: !nonInteractive,
|
|
735
597
|
});
|
|
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
|
-
}
|
|
598
|
+
quiltConfig = inferenceResult;
|
|
599
|
+
inferredAccountId = inferenceResult.account;
|
|
759
600
|
console.log("✓ Quilt configuration inferred\n");
|
|
760
601
|
}
|
|
761
602
|
catch (error) {
|
|
@@ -783,12 +624,17 @@ async function runInstallWizard(options = {}) {
|
|
|
783
624
|
...existingConfig?.quilt,
|
|
784
625
|
...quiltConfig,
|
|
785
626
|
},
|
|
627
|
+
// Pass through inferred account ID for deployment config
|
|
628
|
+
deployment: {
|
|
629
|
+
...existingConfig?.deployment,
|
|
630
|
+
account: existingConfig?.deployment?.account || inferredAccountId,
|
|
631
|
+
},
|
|
786
632
|
};
|
|
787
633
|
// Step 3: Run interactive wizard for remaining configuration
|
|
788
|
-
|
|
634
|
+
const config = await runConfigWizard({
|
|
789
635
|
existingConfig: partialConfig,
|
|
790
636
|
nonInteractive,
|
|
791
|
-
inheritFrom,
|
|
637
|
+
inheritFrom: effectiveInheritFrom,
|
|
792
638
|
});
|
|
793
639
|
// Step 4: Validate configuration
|
|
794
640
|
if (!skipValidation) {
|
|
@@ -824,66 +670,28 @@ async function runInstallWizard(options = {}) {
|
|
|
824
670
|
console.log("");
|
|
825
671
|
}
|
|
826
672
|
}
|
|
827
|
-
// Step 5:
|
|
673
|
+
// Step 5: Save configuration
|
|
828
674
|
console.log(`Saving configuration to profile: ${profile}...\n`);
|
|
829
675
|
try {
|
|
830
676
|
xdg.writeProfile(profile, config);
|
|
831
|
-
console.log(
|
|
677
|
+
console.log(`✓ Configuration saved to: ~/.config/benchling-webhook/${profile}/config.json\n`);
|
|
832
678
|
}
|
|
833
679
|
catch (error) {
|
|
834
680
|
throw new Error(`Failed to save configuration: ${error.message}`);
|
|
835
681
|
}
|
|
836
|
-
// Step 6:
|
|
837
|
-
console.log(
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
682
|
+
// Step 6: Display next steps
|
|
683
|
+
console.log("╔═══════════════════════════════════════════════════════════╗");
|
|
684
|
+
console.log("║ Setup Complete! ║");
|
|
685
|
+
console.log("╚═══════════════════════════════════════════════════════════╝\n");
|
|
686
|
+
console.log("Next steps:");
|
|
687
|
+
if (profile === "default") {
|
|
688
|
+
console.log(" 1. Deploy to AWS: npm run deploy");
|
|
689
|
+
console.log(" 2. Test integration: npm run test\n");
|
|
841
690
|
}
|
|
842
691
|
else {
|
|
843
|
-
console.log(
|
|
844
|
-
|
|
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
|
-
}
|
|
692
|
+
console.log(` 1. Deploy to AWS: npx benchling-webhook deploy --profile ${profile} --stage ${profile}`);
|
|
693
|
+
console.log(` 2. Test integration: npm run test:${profile}\n`);
|
|
879
694
|
}
|
|
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
|
-
});
|
|
887
695
|
return config;
|
|
888
696
|
}
|
|
889
697
|
// =============================================================================
|
|
@@ -896,6 +704,20 @@ async function runInstallWizard(options = {}) {
|
|
|
896
704
|
* @returns Promise that resolves when wizard completes
|
|
897
705
|
*/
|
|
898
706
|
async function setupWizardCommand(options = {}) {
|
|
899
|
-
|
|
707
|
+
try {
|
|
708
|
+
await runInstallWizard(options);
|
|
709
|
+
}
|
|
710
|
+
catch (error) {
|
|
711
|
+
// Handle user cancellation (Ctrl+C) gracefully
|
|
712
|
+
const err = error;
|
|
713
|
+
if (err &&
|
|
714
|
+
(err.message?.includes("User force closed") ||
|
|
715
|
+
err.message?.includes("ERR_USE_AFTER_CLOSE") ||
|
|
716
|
+
err.code === "ERR_USE_AFTER_CLOSE")) {
|
|
717
|
+
console.log(chalk_1.default.yellow("\n✖ Setup cancelled by user"));
|
|
718
|
+
process.exit(0);
|
|
719
|
+
}
|
|
720
|
+
throw error;
|
|
721
|
+
}
|
|
900
722
|
}
|
|
901
723
|
//# sourceMappingURL=setup-wizard.js.map
|