@geekmidas/cli 1.10.40 → 1.11.0

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.
Files changed (94) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +14 -0
  3. package/dist/{CachedStateProvider-37pG1HBx.cjs → CachedStateProvider-BdX7klRc.cjs} +1 -1
  4. package/dist/{CachedStateProvider-C8A-cCm5.mjs → CachedStateProvider-CzyFct42.mjs} +1 -1
  5. package/dist/{CachedStateProvider-C8A-cCm5.mjs.map → CachedStateProvider-CzyFct42.mjs.map} +1 -1
  6. package/dist/CachedStateProvider-GcGziXh1.mjs +3 -0
  7. package/dist/{CachedStateProvider-CxsXOKkg.cjs → CachedStateProvider-klLWZXWF.cjs} +1 -1
  8. package/dist/{CachedStateProvider-CxsXOKkg.cjs.map → CachedStateProvider-klLWZXWF.cjs.map} +1 -1
  9. package/dist/{HostingerProvider-ANWchdiK.mjs → HostingerProvider-B2ZYz7Yc.mjs} +1 -1
  10. package/dist/{HostingerProvider-ANWchdiK.mjs.map → HostingerProvider-B2ZYz7Yc.mjs.map} +1 -1
  11. package/dist/{HostingerProvider-5KYmwoK2.cjs → HostingerProvider-CrHfsgz9.cjs} +1 -1
  12. package/dist/{HostingerProvider-5KYmwoK2.cjs.map → HostingerProvider-CrHfsgz9.cjs.map} +1 -1
  13. package/dist/{LocalStateProvider-Dp0KkRcw.mjs → LocalStateProvider-CP49rjzt.mjs} +1 -1
  14. package/dist/{LocalStateProvider-Dp0KkRcw.mjs.map → LocalStateProvider-CP49rjzt.mjs.map} +1 -1
  15. package/dist/{LocalStateProvider-CLifRC0Y.cjs → LocalStateProvider-CalzNfNA.cjs} +1 -1
  16. package/dist/{LocalStateProvider-CLifRC0Y.cjs.map → LocalStateProvider-CalzNfNA.cjs.map} +1 -1
  17. package/dist/{Route53Provider-owQQ4pn6.cjs → Route53Provider-DwgXiLxt.cjs} +1 -1
  18. package/dist/{Route53Provider-owQQ4pn6.cjs.map → Route53Provider-DwgXiLxt.cjs.map} +1 -1
  19. package/dist/{Route53Provider-QoPgcXxn.mjs → Route53Provider-tlgBzrRQ.mjs} +1 -1
  20. package/dist/{Route53Provider-QoPgcXxn.mjs.map → Route53Provider-tlgBzrRQ.mjs.map} +1 -1
  21. package/dist/{SSMStateProvider-CksOTB8M.mjs → SSMStateProvider-BlrTiV5m.mjs} +1 -1
  22. package/dist/{SSMStateProvider-CksOTB8M.mjs.map → SSMStateProvider-BlrTiV5m.mjs.map} +1 -1
  23. package/dist/{SSMStateProvider-CT8tjl9o.cjs → SSMStateProvider-Bt6r8CTL.cjs} +1 -1
  24. package/dist/{SSMStateProvider-CT8tjl9o.cjs.map → SSMStateProvider-Bt6r8CTL.cjs.map} +1 -1
  25. package/dist/{backup-provisioner-C4noe75O.cjs → backup-provisioner-B3HBN-2k.cjs} +1 -1
  26. package/dist/{backup-provisioner-C4noe75O.cjs.map → backup-provisioner-B3HBN-2k.cjs.map} +1 -1
  27. package/dist/{backup-provisioner-BEXoHTuC.mjs → backup-provisioner-Xunq-p_8.mjs} +1 -1
  28. package/dist/{backup-provisioner-BEXoHTuC.mjs.map → backup-provisioner-Xunq-p_8.mjs.map} +1 -1
  29. package/dist/{bundler-c6QvGUBM.mjs → bundler-DkA4qzUD.mjs} +2 -2
  30. package/dist/{bundler-c6QvGUBM.mjs.map → bundler-DkA4qzUD.mjs.map} +1 -1
  31. package/dist/{bundler-BG6P249p.cjs → bundler-Dufq3uvI.cjs} +2 -2
  32. package/dist/{bundler-BG6P249p.cjs.map → bundler-Dufq3uvI.cjs.map} +1 -1
  33. package/dist/config.d.mts +2 -2
  34. package/dist/dokploy-api-BPYkG-ZZ.cjs +3 -0
  35. package/dist/dokploy-api-BiSzWM4v.mjs +3 -0
  36. package/dist/{dokploy-api-Dvyq-LsM.cjs → dokploy-api-CqOIKHme.cjs} +1 -1
  37. package/dist/{dokploy-api-Dvyq-LsM.cjs.map → dokploy-api-CqOIKHme.cjs.map} +1 -1
  38. package/dist/{dokploy-api-BIT0683o.mjs → dokploy-api-sfZFlWuE.mjs} +1 -1
  39. package/dist/{dokploy-api-BIT0683o.mjs.map → dokploy-api-sfZFlWuE.mjs.map} +1 -1
  40. package/dist/{encryption-CipsS0KW.cjs → encryption-BVS7Vmzs.cjs} +1 -1
  41. package/dist/encryption-Bd_m4q3g.mjs +3 -0
  42. package/dist/{encryption-DSkIlEZ6.mjs → encryption-CO5Q4Bhf.mjs} +1 -1
  43. package/dist/{encryption-DSkIlEZ6.mjs.map → encryption-CO5Q4Bhf.mjs.map} +1 -1
  44. package/dist/{encryption-DgKS-Dv9.cjs → encryption-MfXAiFTm.cjs} +1 -1
  45. package/dist/{encryption-DgKS-Dv9.cjs.map → encryption-MfXAiFTm.cjs.map} +1 -1
  46. package/dist/{index-BBvGMG_A.d.mts → index-Dx9Kk5bR.d.mts} +2 -2
  47. package/dist/{index-BBvGMG_A.d.mts.map → index-Dx9Kk5bR.d.mts.map} +1 -1
  48. package/dist/index.cjs +596 -1314
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.mjs +725 -1443
  51. package/dist/index.mjs.map +1 -1
  52. package/dist/{openapi-DHp49UJV.cjs → openapi-CG1LBk7s.cjs} +1 -1
  53. package/dist/{openapi-DHp49UJV.cjs.map → openapi-CG1LBk7s.cjs.map} +1 -1
  54. package/dist/{openapi-DF9GyhyZ.mjs → openapi-CtRVU6RD.mjs} +1 -1
  55. package/dist/{openapi-DF9GyhyZ.mjs.map → openapi-CtRVU6RD.mjs.map} +1 -1
  56. package/dist/{openapi-react-query-C4UdILaI.mjs → openapi-react-query-C4iIvpPf.mjs} +1 -1
  57. package/dist/{openapi-react-query-C4UdILaI.mjs.map → openapi-react-query-C4iIvpPf.mjs.map} +1 -1
  58. package/dist/{openapi-react-query-DYbBq-WJ.cjs → openapi-react-query-CYWik72n.cjs} +1 -1
  59. package/dist/{openapi-react-query-DYbBq-WJ.cjs.map → openapi-react-query-CYWik72n.cjs.map} +1 -1
  60. package/dist/openapi-react-query.cjs +1 -1
  61. package/dist/openapi-react-query.mjs +1 -1
  62. package/dist/openapi.cjs +1 -1
  63. package/dist/openapi.d.mts +1 -1
  64. package/dist/openapi.mjs +1 -1
  65. package/dist/{reconcile-CYGmqXvq.mjs → reconcile-B2FoJE0a.mjs} +1 -1
  66. package/dist/{reconcile-CYGmqXvq.mjs.map → reconcile-B2FoJE0a.mjs.map} +1 -1
  67. package/dist/{reconcile-D93YG7G5.cjs → reconcile-BzqKJ2Rt.cjs} +1 -1
  68. package/dist/{reconcile-D93YG7G5.cjs.map → reconcile-BzqKJ2Rt.cjs.map} +1 -1
  69. package/dist/setup-DDXUXzzl.mjs +765 -0
  70. package/dist/setup-DDXUXzzl.mjs.map +1 -0
  71. package/dist/setup-DLwhfyt-.cjs +8 -0
  72. package/dist/setup-Dh-B7YTX.mjs +8 -0
  73. package/dist/setup-WZ-xqAQO.cjs +849 -0
  74. package/dist/setup-WZ-xqAQO.cjs.map +1 -0
  75. package/dist/{sync-COnAugP-.mjs → sync-B5UHXwRM.mjs} +1 -1
  76. package/dist/sync-BLA94rgC.cjs +4 -0
  77. package/dist/{sync-D_NowTkZ.mjs → sync-CSooqRQ8.mjs} +1 -1
  78. package/dist/{sync-D_NowTkZ.mjs.map → sync-CSooqRQ8.mjs.map} +1 -1
  79. package/dist/{sync-DGXXSk2v.cjs → sync-o_CtEbvR.cjs} +1 -1
  80. package/dist/{sync-DGXXSk2v.cjs.map → sync-o_CtEbvR.cjs.map} +1 -1
  81. package/dist/{types-OszPdw9m.d.mts → types-CqfhdmRi.d.mts} +1 -1
  82. package/dist/{types-OszPdw9m.d.mts.map → types-CqfhdmRi.d.mts.map} +1 -1
  83. package/dist/workspace/index.d.mts +2 -2
  84. package/package.json +5 -5
  85. package/src/index.ts +4 -0
  86. package/src/init/versions.ts +5 -5
  87. package/src/setup/__tests__/auto-setup.spec.ts +147 -0
  88. package/src/setup/index.ts +46 -6
  89. package/src/test/index.ts +17 -0
  90. package/dist/CachedStateProvider-CC0DyO09.mjs +0 -3
  91. package/dist/dokploy-api-BOx71j6F.cjs +0 -3
  92. package/dist/dokploy-api-BZGk-lkD.mjs +0 -3
  93. package/dist/encryption-Db2zvGqM.mjs +0 -3
  94. package/dist/sync-D1Pa30oV.cjs +0 -4
@@ -1 +1 @@
1
- {"version":3,"file":"backup-provisioner-C4noe75O.cjs","names":["name: string","region: string","profile?: string","endpoint?: string","config: S3ClientConfig & IAMClientConfig","S3Client","IAMClient","s3: S3Client","bucketName: string","HeadBucketCommand","iam: IAMClient","userName: string","GetUserCommand","options: ProvisionBackupOptions","createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t}","CreateBucketCommand","PutBucketVersioningCommand","CreateUserCommand","PutUserPolicyCommand","accessKeyId: string","secretAccessKey: string","CreateAccessKeyCommand"],"sources":["../src/deploy/backup-provisioner.ts"],"sourcesContent":["/**\n * Backup Destination Provisioner\n *\n * Creates AWS resources (S3 bucket, IAM user, access keys) and configures\n * Dokploy backup destinations for database backups.\n */\n\nimport {\n\tCreateAccessKeyCommand,\n\tCreateUserCommand,\n\tGetUserCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tPutUserPolicyCommand,\n} from '@aws-sdk/client-iam';\nimport {\n\ttype BucketLocationConstraint,\n\tCreateBucketCommand,\n\tHeadBucketCommand,\n\tPutBucketVersioningCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { BackupsConfig } from '../workspace/types.js';\nimport type { DokployApi } from './dokploy-api.js';\nimport type { BackupState } from './state.js';\n\nexport interface ProvisionBackupOptions {\n\t/** Dokploy API client */\n\tapi: DokployApi;\n\t/** Dokploy project ID */\n\tprojectId: string;\n\t/** Workspace name (used for resource naming) */\n\tprojectName: string;\n\t/** Deploy stage (e.g., 'production', 'staging') */\n\tstage: string;\n\t/** Backup configuration */\n\tconfig: BackupsConfig;\n\t/** Existing backup state (if any) */\n\texistingState?: BackupState;\n\t/** Logger for progress output */\n\tlogger: { log: (msg: string) => void };\n\t/** AWS endpoint override (for testing with LocalStack) */\n\tawsEndpoint?: string;\n}\n\n/**\n * Generate a random suffix for unique resource names\n */\nfunction randomSuffix(): string {\n\treturn Math.random().toString(36).substring(2, 8);\n}\n\n/**\n * Sanitize a name for AWS resources (lowercase alphanumeric and hyphens)\n */\nfunction sanitizeName(name: string): string {\n\treturn name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n}\n\n/**\n * Create AWS clients with optional profile credentials\n */\nasync function createAwsClients(\n\tregion: string,\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<{ s3: S3Client; iam: IAMClient }> {\n\tconst config: S3ClientConfig & IAMClientConfig = { region };\n\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\n\t// Support custom endpoint for testing (e.g., LocalStack)\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\t(config as S3ClientConfig).forcePathStyle = true;\n\t\t// Use test credentials when endpoint is specified\n\t\tconfig.credentials = {\n\t\t\taccessKeyId: 'test',\n\t\t\tsecretAccessKey: 'test',\n\t\t};\n\t}\n\n\treturn {\n\t\ts3: new S3Client(config),\n\t\tiam: new IAMClient(config),\n\t};\n}\n\n/**\n * Check if an S3 bucket exists\n */\nasync function bucketExists(\n\ts3: S3Client,\n\tbucketName: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait s3.send(new HeadBucketCommand({ Bucket: bucketName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name === 'NotFound') {\n\t\t\treturn false;\n\t\t}\n\t\t// 403 means bucket exists but we don't have access\n\t\tif (\n\t\t\t(error as { $metadata?: { httpStatusCode?: number } }).$metadata\n\t\t\t\t?.httpStatusCode === 403\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if an IAM user exists\n */\nasync function userExists(iam: IAMClient, userName: string): Promise<boolean> {\n\ttry {\n\t\tawait iam.send(new GetUserCommand({ UserName: userName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tconst errorName = (error as { name?: string }).name;\n\t\t// AWS returns 'NoSuchEntity', LocalStack returns 'NoSuchEntityException'\n\t\tif (errorName === 'NoSuchEntity' || errorName === 'NoSuchEntityException') {\n\t\t\treturn false;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Provision backup destination for a deployment.\n *\n * Creates AWS resources (S3 bucket, IAM user) and Dokploy destination if needed.\n * Reuses existing resources from state when possible.\n */\nexport async function provisionBackupDestination(\n\toptions: ProvisionBackupOptions,\n): Promise<BackupState> {\n\tconst {\n\t\tapi,\n\t\tprojectName,\n\t\tstage,\n\t\tconfig,\n\t\texistingState,\n\t\tlogger,\n\t\tawsEndpoint,\n\t} = options;\n\n\t// If we have existing state, verify the Dokploy destination still exists\n\tif (existingState?.destinationId) {\n\t\ttry {\n\t\t\tawait api.getDestination(existingState.destinationId);\n\t\t\tlogger.log(' Using existing backup destination');\n\t\t\treturn existingState;\n\t\t} catch {\n\t\t\tlogger.log(' Existing destination not found, recreating...');\n\t\t}\n\t}\n\n\t// Create AWS clients\n\tconst aws = await createAwsClients(\n\t\tconfig.region,\n\t\tconfig.profile,\n\t\tawsEndpoint,\n\t);\n\tconst sanitizedProject = sanitizeName(projectName);\n\n\t// 1. Create or verify S3 bucket\n\tconst bucketName =\n\t\texistingState?.bucketName ??\n\t\t`${sanitizedProject}-${stage}-backups-${randomSuffix()}`;\n\n\tconst bucketAlreadyExists = await bucketExists(aws.s3, bucketName);\n\tif (!bucketAlreadyExists) {\n\t\tlogger.log(` Creating S3 bucket: ${bucketName}`);\n\n\t\t// CreateBucket needs LocationConstraint for non-us-east-1 regions\n\t\tconst createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t} = {\n\t\t\tBucket: bucketName,\n\t\t};\n\t\tif (config.region !== 'us-east-1') {\n\t\t\tcreateBucketParams.CreateBucketConfiguration = {\n\t\t\t\tLocationConstraint: config.region as BucketLocationConstraint,\n\t\t\t};\n\t\t}\n\n\t\tawait aws.s3.send(new CreateBucketCommand(createBucketParams));\n\n\t\t// Enable versioning for backup integrity\n\t\tawait aws.s3.send(\n\t\t\tnew PutBucketVersioningCommand({\n\t\t\t\tBucket: bucketName,\n\t\t\t\tVersioningConfiguration: { Status: 'Enabled' },\n\t\t\t}),\n\t\t);\n\t} else {\n\t\tlogger.log(` Using existing S3 bucket: ${bucketName}`);\n\t}\n\n\t// 2. Create or verify IAM user\n\tconst iamUserName =\n\t\texistingState?.iamUserName ?? `dokploy-backup-${sanitizedProject}-${stage}`;\n\n\tconst iamUserAlreadyExists = await userExists(aws.iam, iamUserName);\n\tif (!iamUserAlreadyExists) {\n\t\tlogger.log(` Creating IAM user: ${iamUserName}`);\n\t\tawait aws.iam.send(new CreateUserCommand({ UserName: iamUserName }));\n\t} else {\n\t\tlogger.log(` Using existing IAM user: ${iamUserName}`);\n\t}\n\n\t// 3. Attach bucket policy to IAM user\n\tconst policyDocument = {\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:GetObject',\n\t\t\t\t\t's3:PutObject',\n\t\t\t\t\t's3:DeleteObject',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketLocation',\n\t\t\t\t],\n\t\t\t\tResource: [\n\t\t\t\t\t`arn:aws:s3:::${bucketName}`,\n\t\t\t\t\t`arn:aws:s3:::${bucketName}/*`,\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tlogger.log(' Updating IAM policy');\n\tawait aws.iam.send(\n\t\tnew PutUserPolicyCommand({\n\t\t\tUserName: iamUserName,\n\t\t\tPolicyName: 'DokployBackupAccess',\n\t\t\tPolicyDocument: JSON.stringify(policyDocument),\n\t\t}),\n\t);\n\n\t// 4. Create access key (or reuse existing if state has it and destination needs recreation)\n\tlet accessKeyId: string;\n\tlet secretAccessKey: string;\n\n\tif (existingState?.iamAccessKeyId && existingState?.iamSecretAccessKey) {\n\t\t// Reuse existing credentials\n\t\tlogger.log(' Using existing IAM access key');\n\t\taccessKeyId = existingState.iamAccessKeyId;\n\t\tsecretAccessKey = existingState.iamSecretAccessKey;\n\t} else {\n\t\t// Create new access key\n\t\tlogger.log(' Creating IAM access key');\n\t\tconst accessKeyResult = await aws.iam.send(\n\t\t\tnew CreateAccessKeyCommand({ UserName: iamUserName }),\n\t\t);\n\n\t\tif (!accessKeyResult.AccessKey) {\n\t\t\tthrow new Error('Failed to create IAM access key');\n\t\t}\n\n\t\taccessKeyId = accessKeyResult.AccessKey.AccessKeyId!;\n\t\tsecretAccessKey = accessKeyResult.AccessKey.SecretAccessKey!;\n\t}\n\n\t// 5. Create Dokploy destination\n\tconst destinationName = `${sanitizedProject}-${stage}-s3`;\n\tlogger.log(` Creating Dokploy destination: ${destinationName}`);\n\n\tconst { destination, created } = await api.findOrCreateDestination(\n\t\tdestinationName,\n\t\t{\n\t\t\taccessKey: accessKeyId,\n\t\t\tsecretAccessKey: secretAccessKey,\n\t\t\tbucket: bucketName,\n\t\t\tregion: config.region,\n\t\t},\n\t);\n\n\tif (created) {\n\t\tlogger.log(' ✓ Dokploy destination created');\n\t} else {\n\t\tlogger.log(' ✓ Using existing Dokploy destination');\n\t}\n\n\t// 6. Test connection\n\ttry {\n\t\tawait api.testDestinationConnection(destination.destinationId);\n\t\tlogger.log(' ✓ Destination connection verified');\n\t} catch (error) {\n\t\tlogger.log(\n\t\t\t` ⚠ Warning: Could not verify destination connection: ${error}`,\n\t\t);\n\t}\n\n\treturn {\n\t\tbucketName,\n\t\tbucketArn: `arn:aws:s3:::${bucketName}`,\n\t\tiamUserName,\n\t\tiamAccessKeyId: accessKeyId,\n\t\tiamSecretAccessKey: secretAccessKey,\n\t\tdestinationId: destination.destinationId,\n\t\tregion: config.region,\n\t\tcreatedAt: existingState?.createdAt ?? new Date().toISOString(),\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"backup-provisioner-B3HBN-2k.cjs","names":["name: string","region: string","profile?: string","endpoint?: string","config: S3ClientConfig & IAMClientConfig","S3Client","IAMClient","s3: S3Client","bucketName: string","HeadBucketCommand","iam: IAMClient","userName: string","GetUserCommand","options: ProvisionBackupOptions","createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t}","CreateBucketCommand","PutBucketVersioningCommand","CreateUserCommand","PutUserPolicyCommand","accessKeyId: string","secretAccessKey: string","CreateAccessKeyCommand"],"sources":["../src/deploy/backup-provisioner.ts"],"sourcesContent":["/**\n * Backup Destination Provisioner\n *\n * Creates AWS resources (S3 bucket, IAM user, access keys) and configures\n * Dokploy backup destinations for database backups.\n */\n\nimport {\n\tCreateAccessKeyCommand,\n\tCreateUserCommand,\n\tGetUserCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tPutUserPolicyCommand,\n} from '@aws-sdk/client-iam';\nimport {\n\ttype BucketLocationConstraint,\n\tCreateBucketCommand,\n\tHeadBucketCommand,\n\tPutBucketVersioningCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { BackupsConfig } from '../workspace/types.js';\nimport type { DokployApi } from './dokploy-api.js';\nimport type { BackupState } from './state.js';\n\nexport interface ProvisionBackupOptions {\n\t/** Dokploy API client */\n\tapi: DokployApi;\n\t/** Dokploy project ID */\n\tprojectId: string;\n\t/** Workspace name (used for resource naming) */\n\tprojectName: string;\n\t/** Deploy stage (e.g., 'production', 'staging') */\n\tstage: string;\n\t/** Backup configuration */\n\tconfig: BackupsConfig;\n\t/** Existing backup state (if any) */\n\texistingState?: BackupState;\n\t/** Logger for progress output */\n\tlogger: { log: (msg: string) => void };\n\t/** AWS endpoint override (for testing with LocalStack) */\n\tawsEndpoint?: string;\n}\n\n/**\n * Generate a random suffix for unique resource names\n */\nfunction randomSuffix(): string {\n\treturn Math.random().toString(36).substring(2, 8);\n}\n\n/**\n * Sanitize a name for AWS resources (lowercase alphanumeric and hyphens)\n */\nfunction sanitizeName(name: string): string {\n\treturn name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n}\n\n/**\n * Create AWS clients with optional profile credentials\n */\nasync function createAwsClients(\n\tregion: string,\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<{ s3: S3Client; iam: IAMClient }> {\n\tconst config: S3ClientConfig & IAMClientConfig = { region };\n\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\n\t// Support custom endpoint for testing (e.g., LocalStack)\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\t(config as S3ClientConfig).forcePathStyle = true;\n\t\t// Use test credentials when endpoint is specified\n\t\tconfig.credentials = {\n\t\t\taccessKeyId: 'test',\n\t\t\tsecretAccessKey: 'test',\n\t\t};\n\t}\n\n\treturn {\n\t\ts3: new S3Client(config),\n\t\tiam: new IAMClient(config),\n\t};\n}\n\n/**\n * Check if an S3 bucket exists\n */\nasync function bucketExists(\n\ts3: S3Client,\n\tbucketName: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait s3.send(new HeadBucketCommand({ Bucket: bucketName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name === 'NotFound') {\n\t\t\treturn false;\n\t\t}\n\t\t// 403 means bucket exists but we don't have access\n\t\tif (\n\t\t\t(error as { $metadata?: { httpStatusCode?: number } }).$metadata\n\t\t\t\t?.httpStatusCode === 403\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if an IAM user exists\n */\nasync function userExists(iam: IAMClient, userName: string): Promise<boolean> {\n\ttry {\n\t\tawait iam.send(new GetUserCommand({ UserName: userName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tconst errorName = (error as { name?: string }).name;\n\t\t// AWS returns 'NoSuchEntity', LocalStack returns 'NoSuchEntityException'\n\t\tif (errorName === 'NoSuchEntity' || errorName === 'NoSuchEntityException') {\n\t\t\treturn false;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Provision backup destination for a deployment.\n *\n * Creates AWS resources (S3 bucket, IAM user) and Dokploy destination if needed.\n * Reuses existing resources from state when possible.\n */\nexport async function provisionBackupDestination(\n\toptions: ProvisionBackupOptions,\n): Promise<BackupState> {\n\tconst {\n\t\tapi,\n\t\tprojectName,\n\t\tstage,\n\t\tconfig,\n\t\texistingState,\n\t\tlogger,\n\t\tawsEndpoint,\n\t} = options;\n\n\t// If we have existing state, verify the Dokploy destination still exists\n\tif (existingState?.destinationId) {\n\t\ttry {\n\t\t\tawait api.getDestination(existingState.destinationId);\n\t\t\tlogger.log(' Using existing backup destination');\n\t\t\treturn existingState;\n\t\t} catch {\n\t\t\tlogger.log(' Existing destination not found, recreating...');\n\t\t}\n\t}\n\n\t// Create AWS clients\n\tconst aws = await createAwsClients(\n\t\tconfig.region,\n\t\tconfig.profile,\n\t\tawsEndpoint,\n\t);\n\tconst sanitizedProject = sanitizeName(projectName);\n\n\t// 1. Create or verify S3 bucket\n\tconst bucketName =\n\t\texistingState?.bucketName ??\n\t\t`${sanitizedProject}-${stage}-backups-${randomSuffix()}`;\n\n\tconst bucketAlreadyExists = await bucketExists(aws.s3, bucketName);\n\tif (!bucketAlreadyExists) {\n\t\tlogger.log(` Creating S3 bucket: ${bucketName}`);\n\n\t\t// CreateBucket needs LocationConstraint for non-us-east-1 regions\n\t\tconst createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t} = {\n\t\t\tBucket: bucketName,\n\t\t};\n\t\tif (config.region !== 'us-east-1') {\n\t\t\tcreateBucketParams.CreateBucketConfiguration = {\n\t\t\t\tLocationConstraint: config.region as BucketLocationConstraint,\n\t\t\t};\n\t\t}\n\n\t\tawait aws.s3.send(new CreateBucketCommand(createBucketParams));\n\n\t\t// Enable versioning for backup integrity\n\t\tawait aws.s3.send(\n\t\t\tnew PutBucketVersioningCommand({\n\t\t\t\tBucket: bucketName,\n\t\t\t\tVersioningConfiguration: { Status: 'Enabled' },\n\t\t\t}),\n\t\t);\n\t} else {\n\t\tlogger.log(` Using existing S3 bucket: ${bucketName}`);\n\t}\n\n\t// 2. Create or verify IAM user\n\tconst iamUserName =\n\t\texistingState?.iamUserName ?? `dokploy-backup-${sanitizedProject}-${stage}`;\n\n\tconst iamUserAlreadyExists = await userExists(aws.iam, iamUserName);\n\tif (!iamUserAlreadyExists) {\n\t\tlogger.log(` Creating IAM user: ${iamUserName}`);\n\t\tawait aws.iam.send(new CreateUserCommand({ UserName: iamUserName }));\n\t} else {\n\t\tlogger.log(` Using existing IAM user: ${iamUserName}`);\n\t}\n\n\t// 3. Attach bucket policy to IAM user\n\tconst policyDocument = {\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:GetObject',\n\t\t\t\t\t's3:PutObject',\n\t\t\t\t\t's3:DeleteObject',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketLocation',\n\t\t\t\t],\n\t\t\t\tResource: [\n\t\t\t\t\t`arn:aws:s3:::${bucketName}`,\n\t\t\t\t\t`arn:aws:s3:::${bucketName}/*`,\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tlogger.log(' Updating IAM policy');\n\tawait aws.iam.send(\n\t\tnew PutUserPolicyCommand({\n\t\t\tUserName: iamUserName,\n\t\t\tPolicyName: 'DokployBackupAccess',\n\t\t\tPolicyDocument: JSON.stringify(policyDocument),\n\t\t}),\n\t);\n\n\t// 4. Create access key (or reuse existing if state has it and destination needs recreation)\n\tlet accessKeyId: string;\n\tlet secretAccessKey: string;\n\n\tif (existingState?.iamAccessKeyId && existingState?.iamSecretAccessKey) {\n\t\t// Reuse existing credentials\n\t\tlogger.log(' Using existing IAM access key');\n\t\taccessKeyId = existingState.iamAccessKeyId;\n\t\tsecretAccessKey = existingState.iamSecretAccessKey;\n\t} else {\n\t\t// Create new access key\n\t\tlogger.log(' Creating IAM access key');\n\t\tconst accessKeyResult = await aws.iam.send(\n\t\t\tnew CreateAccessKeyCommand({ UserName: iamUserName }),\n\t\t);\n\n\t\tif (!accessKeyResult.AccessKey) {\n\t\t\tthrow new Error('Failed to create IAM access key');\n\t\t}\n\n\t\taccessKeyId = accessKeyResult.AccessKey.AccessKeyId!;\n\t\tsecretAccessKey = accessKeyResult.AccessKey.SecretAccessKey!;\n\t}\n\n\t// 5. Create Dokploy destination\n\tconst destinationName = `${sanitizedProject}-${stage}-s3`;\n\tlogger.log(` Creating Dokploy destination: ${destinationName}`);\n\n\tconst { destination, created } = await api.findOrCreateDestination(\n\t\tdestinationName,\n\t\t{\n\t\t\taccessKey: accessKeyId,\n\t\t\tsecretAccessKey: secretAccessKey,\n\t\t\tbucket: bucketName,\n\t\t\tregion: config.region,\n\t\t},\n\t);\n\n\tif (created) {\n\t\tlogger.log(' ✓ Dokploy destination created');\n\t} else {\n\t\tlogger.log(' ✓ Using existing Dokploy destination');\n\t}\n\n\t// 6. Test connection\n\ttry {\n\t\tawait api.testDestinationConnection(destination.destinationId);\n\t\tlogger.log(' ✓ Destination connection verified');\n\t} catch (error) {\n\t\tlogger.log(\n\t\t\t` ⚠ Warning: Could not verify destination connection: ${error}`,\n\t\t);\n\t}\n\n\treturn {\n\t\tbucketName,\n\t\tbucketArn: `arn:aws:s3:::${bucketName}`,\n\t\tiamUserName,\n\t\tiamAccessKeyId: accessKeyId,\n\t\tiamSecretAccessKey: secretAccessKey,\n\t\tdestinationId: destination.destinationId,\n\t\tregion: config.region,\n\t\tcreatedAt: existingState?.createdAt ?? new Date().toISOString(),\n\t};\n}\n"],"mappings":"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"}
@@ -160,4 +160,4 @@ async function provisionBackupDestination(options) {
160
160
 
161
161
  //#endregion
162
162
  export { provisionBackupDestination };
163
- //# sourceMappingURL=backup-provisioner-BEXoHTuC.mjs.map
163
+ //# sourceMappingURL=backup-provisioner-Xunq-p_8.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"backup-provisioner-BEXoHTuC.mjs","names":["name: string","region: string","profile?: string","endpoint?: string","config: S3ClientConfig & IAMClientConfig","s3: S3Client","bucketName: string","iam: IAMClient","userName: string","options: ProvisionBackupOptions","createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t}","accessKeyId: string","secretAccessKey: string"],"sources":["../src/deploy/backup-provisioner.ts"],"sourcesContent":["/**\n * Backup Destination Provisioner\n *\n * Creates AWS resources (S3 bucket, IAM user, access keys) and configures\n * Dokploy backup destinations for database backups.\n */\n\nimport {\n\tCreateAccessKeyCommand,\n\tCreateUserCommand,\n\tGetUserCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tPutUserPolicyCommand,\n} from '@aws-sdk/client-iam';\nimport {\n\ttype BucketLocationConstraint,\n\tCreateBucketCommand,\n\tHeadBucketCommand,\n\tPutBucketVersioningCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { BackupsConfig } from '../workspace/types.js';\nimport type { DokployApi } from './dokploy-api.js';\nimport type { BackupState } from './state.js';\n\nexport interface ProvisionBackupOptions {\n\t/** Dokploy API client */\n\tapi: DokployApi;\n\t/** Dokploy project ID */\n\tprojectId: string;\n\t/** Workspace name (used for resource naming) */\n\tprojectName: string;\n\t/** Deploy stage (e.g., 'production', 'staging') */\n\tstage: string;\n\t/** Backup configuration */\n\tconfig: BackupsConfig;\n\t/** Existing backup state (if any) */\n\texistingState?: BackupState;\n\t/** Logger for progress output */\n\tlogger: { log: (msg: string) => void };\n\t/** AWS endpoint override (for testing with LocalStack) */\n\tawsEndpoint?: string;\n}\n\n/**\n * Generate a random suffix for unique resource names\n */\nfunction randomSuffix(): string {\n\treturn Math.random().toString(36).substring(2, 8);\n}\n\n/**\n * Sanitize a name for AWS resources (lowercase alphanumeric and hyphens)\n */\nfunction sanitizeName(name: string): string {\n\treturn name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n}\n\n/**\n * Create AWS clients with optional profile credentials\n */\nasync function createAwsClients(\n\tregion: string,\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<{ s3: S3Client; iam: IAMClient }> {\n\tconst config: S3ClientConfig & IAMClientConfig = { region };\n\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\n\t// Support custom endpoint for testing (e.g., LocalStack)\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\t(config as S3ClientConfig).forcePathStyle = true;\n\t\t// Use test credentials when endpoint is specified\n\t\tconfig.credentials = {\n\t\t\taccessKeyId: 'test',\n\t\t\tsecretAccessKey: 'test',\n\t\t};\n\t}\n\n\treturn {\n\t\ts3: new S3Client(config),\n\t\tiam: new IAMClient(config),\n\t};\n}\n\n/**\n * Check if an S3 bucket exists\n */\nasync function bucketExists(\n\ts3: S3Client,\n\tbucketName: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait s3.send(new HeadBucketCommand({ Bucket: bucketName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name === 'NotFound') {\n\t\t\treturn false;\n\t\t}\n\t\t// 403 means bucket exists but we don't have access\n\t\tif (\n\t\t\t(error as { $metadata?: { httpStatusCode?: number } }).$metadata\n\t\t\t\t?.httpStatusCode === 403\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if an IAM user exists\n */\nasync function userExists(iam: IAMClient, userName: string): Promise<boolean> {\n\ttry {\n\t\tawait iam.send(new GetUserCommand({ UserName: userName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tconst errorName = (error as { name?: string }).name;\n\t\t// AWS returns 'NoSuchEntity', LocalStack returns 'NoSuchEntityException'\n\t\tif (errorName === 'NoSuchEntity' || errorName === 'NoSuchEntityException') {\n\t\t\treturn false;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Provision backup destination for a deployment.\n *\n * Creates AWS resources (S3 bucket, IAM user) and Dokploy destination if needed.\n * Reuses existing resources from state when possible.\n */\nexport async function provisionBackupDestination(\n\toptions: ProvisionBackupOptions,\n): Promise<BackupState> {\n\tconst {\n\t\tapi,\n\t\tprojectName,\n\t\tstage,\n\t\tconfig,\n\t\texistingState,\n\t\tlogger,\n\t\tawsEndpoint,\n\t} = options;\n\n\t// If we have existing state, verify the Dokploy destination still exists\n\tif (existingState?.destinationId) {\n\t\ttry {\n\t\t\tawait api.getDestination(existingState.destinationId);\n\t\t\tlogger.log(' Using existing backup destination');\n\t\t\treturn existingState;\n\t\t} catch {\n\t\t\tlogger.log(' Existing destination not found, recreating...');\n\t\t}\n\t}\n\n\t// Create AWS clients\n\tconst aws = await createAwsClients(\n\t\tconfig.region,\n\t\tconfig.profile,\n\t\tawsEndpoint,\n\t);\n\tconst sanitizedProject = sanitizeName(projectName);\n\n\t// 1. Create or verify S3 bucket\n\tconst bucketName =\n\t\texistingState?.bucketName ??\n\t\t`${sanitizedProject}-${stage}-backups-${randomSuffix()}`;\n\n\tconst bucketAlreadyExists = await bucketExists(aws.s3, bucketName);\n\tif (!bucketAlreadyExists) {\n\t\tlogger.log(` Creating S3 bucket: ${bucketName}`);\n\n\t\t// CreateBucket needs LocationConstraint for non-us-east-1 regions\n\t\tconst createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t} = {\n\t\t\tBucket: bucketName,\n\t\t};\n\t\tif (config.region !== 'us-east-1') {\n\t\t\tcreateBucketParams.CreateBucketConfiguration = {\n\t\t\t\tLocationConstraint: config.region as BucketLocationConstraint,\n\t\t\t};\n\t\t}\n\n\t\tawait aws.s3.send(new CreateBucketCommand(createBucketParams));\n\n\t\t// Enable versioning for backup integrity\n\t\tawait aws.s3.send(\n\t\t\tnew PutBucketVersioningCommand({\n\t\t\t\tBucket: bucketName,\n\t\t\t\tVersioningConfiguration: { Status: 'Enabled' },\n\t\t\t}),\n\t\t);\n\t} else {\n\t\tlogger.log(` Using existing S3 bucket: ${bucketName}`);\n\t}\n\n\t// 2. Create or verify IAM user\n\tconst iamUserName =\n\t\texistingState?.iamUserName ?? `dokploy-backup-${sanitizedProject}-${stage}`;\n\n\tconst iamUserAlreadyExists = await userExists(aws.iam, iamUserName);\n\tif (!iamUserAlreadyExists) {\n\t\tlogger.log(` Creating IAM user: ${iamUserName}`);\n\t\tawait aws.iam.send(new CreateUserCommand({ UserName: iamUserName }));\n\t} else {\n\t\tlogger.log(` Using existing IAM user: ${iamUserName}`);\n\t}\n\n\t// 3. Attach bucket policy to IAM user\n\tconst policyDocument = {\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:GetObject',\n\t\t\t\t\t's3:PutObject',\n\t\t\t\t\t's3:DeleteObject',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketLocation',\n\t\t\t\t],\n\t\t\t\tResource: [\n\t\t\t\t\t`arn:aws:s3:::${bucketName}`,\n\t\t\t\t\t`arn:aws:s3:::${bucketName}/*`,\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tlogger.log(' Updating IAM policy');\n\tawait aws.iam.send(\n\t\tnew PutUserPolicyCommand({\n\t\t\tUserName: iamUserName,\n\t\t\tPolicyName: 'DokployBackupAccess',\n\t\t\tPolicyDocument: JSON.stringify(policyDocument),\n\t\t}),\n\t);\n\n\t// 4. Create access key (or reuse existing if state has it and destination needs recreation)\n\tlet accessKeyId: string;\n\tlet secretAccessKey: string;\n\n\tif (existingState?.iamAccessKeyId && existingState?.iamSecretAccessKey) {\n\t\t// Reuse existing credentials\n\t\tlogger.log(' Using existing IAM access key');\n\t\taccessKeyId = existingState.iamAccessKeyId;\n\t\tsecretAccessKey = existingState.iamSecretAccessKey;\n\t} else {\n\t\t// Create new access key\n\t\tlogger.log(' Creating IAM access key');\n\t\tconst accessKeyResult = await aws.iam.send(\n\t\t\tnew CreateAccessKeyCommand({ UserName: iamUserName }),\n\t\t);\n\n\t\tif (!accessKeyResult.AccessKey) {\n\t\t\tthrow new Error('Failed to create IAM access key');\n\t\t}\n\n\t\taccessKeyId = accessKeyResult.AccessKey.AccessKeyId!;\n\t\tsecretAccessKey = accessKeyResult.AccessKey.SecretAccessKey!;\n\t}\n\n\t// 5. Create Dokploy destination\n\tconst destinationName = `${sanitizedProject}-${stage}-s3`;\n\tlogger.log(` Creating Dokploy destination: ${destinationName}`);\n\n\tconst { destination, created } = await api.findOrCreateDestination(\n\t\tdestinationName,\n\t\t{\n\t\t\taccessKey: accessKeyId,\n\t\t\tsecretAccessKey: secretAccessKey,\n\t\t\tbucket: bucketName,\n\t\t\tregion: config.region,\n\t\t},\n\t);\n\n\tif (created) {\n\t\tlogger.log(' ✓ Dokploy destination created');\n\t} else {\n\t\tlogger.log(' ✓ Using existing Dokploy destination');\n\t}\n\n\t// 6. Test connection\n\ttry {\n\t\tawait api.testDestinationConnection(destination.destinationId);\n\t\tlogger.log(' ✓ Destination connection verified');\n\t} catch (error) {\n\t\tlogger.log(\n\t\t\t` ⚠ Warning: Could not verify destination connection: ${error}`,\n\t\t);\n\t}\n\n\treturn {\n\t\tbucketName,\n\t\tbucketArn: `arn:aws:s3:::${bucketName}`,\n\t\tiamUserName,\n\t\tiamAccessKeyId: accessKeyId,\n\t\tiamSecretAccessKey: secretAccessKey,\n\t\tdestinationId: destination.destinationId,\n\t\tregion: config.region,\n\t\tcreatedAt: existingState?.createdAt ?? new Date().toISOString(),\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"backup-provisioner-Xunq-p_8.mjs","names":["name: string","region: string","profile?: string","endpoint?: string","config: S3ClientConfig & IAMClientConfig","s3: S3Client","bucketName: string","iam: IAMClient","userName: string","options: ProvisionBackupOptions","createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t}","accessKeyId: string","secretAccessKey: string"],"sources":["../src/deploy/backup-provisioner.ts"],"sourcesContent":["/**\n * Backup Destination Provisioner\n *\n * Creates AWS resources (S3 bucket, IAM user, access keys) and configures\n * Dokploy backup destinations for database backups.\n */\n\nimport {\n\tCreateAccessKeyCommand,\n\tCreateUserCommand,\n\tGetUserCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tPutUserPolicyCommand,\n} from '@aws-sdk/client-iam';\nimport {\n\ttype BucketLocationConstraint,\n\tCreateBucketCommand,\n\tHeadBucketCommand,\n\tPutBucketVersioningCommand,\n\tS3Client,\n\ttype S3ClientConfig,\n} from '@aws-sdk/client-s3';\nimport type { BackupsConfig } from '../workspace/types.js';\nimport type { DokployApi } from './dokploy-api.js';\nimport type { BackupState } from './state.js';\n\nexport interface ProvisionBackupOptions {\n\t/** Dokploy API client */\n\tapi: DokployApi;\n\t/** Dokploy project ID */\n\tprojectId: string;\n\t/** Workspace name (used for resource naming) */\n\tprojectName: string;\n\t/** Deploy stage (e.g., 'production', 'staging') */\n\tstage: string;\n\t/** Backup configuration */\n\tconfig: BackupsConfig;\n\t/** Existing backup state (if any) */\n\texistingState?: BackupState;\n\t/** Logger for progress output */\n\tlogger: { log: (msg: string) => void };\n\t/** AWS endpoint override (for testing with LocalStack) */\n\tawsEndpoint?: string;\n}\n\n/**\n * Generate a random suffix for unique resource names\n */\nfunction randomSuffix(): string {\n\treturn Math.random().toString(36).substring(2, 8);\n}\n\n/**\n * Sanitize a name for AWS resources (lowercase alphanumeric and hyphens)\n */\nfunction sanitizeName(name: string): string {\n\treturn name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n}\n\n/**\n * Create AWS clients with optional profile credentials\n */\nasync function createAwsClients(\n\tregion: string,\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<{ s3: S3Client; iam: IAMClient }> {\n\tconst config: S3ClientConfig & IAMClientConfig = { region };\n\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\n\t// Support custom endpoint for testing (e.g., LocalStack)\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\t(config as S3ClientConfig).forcePathStyle = true;\n\t\t// Use test credentials when endpoint is specified\n\t\tconfig.credentials = {\n\t\t\taccessKeyId: 'test',\n\t\t\tsecretAccessKey: 'test',\n\t\t};\n\t}\n\n\treturn {\n\t\ts3: new S3Client(config),\n\t\tiam: new IAMClient(config),\n\t};\n}\n\n/**\n * Check if an S3 bucket exists\n */\nasync function bucketExists(\n\ts3: S3Client,\n\tbucketName: string,\n): Promise<boolean> {\n\ttry {\n\t\tawait s3.send(new HeadBucketCommand({ Bucket: bucketName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tif ((error as { name?: string }).name === 'NotFound') {\n\t\t\treturn false;\n\t\t}\n\t\t// 403 means bucket exists but we don't have access\n\t\tif (\n\t\t\t(error as { $metadata?: { httpStatusCode?: number } }).$metadata\n\t\t\t\t?.httpStatusCode === 403\n\t\t) {\n\t\t\treturn true;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Check if an IAM user exists\n */\nasync function userExists(iam: IAMClient, userName: string): Promise<boolean> {\n\ttry {\n\t\tawait iam.send(new GetUserCommand({ UserName: userName }));\n\t\treturn true;\n\t} catch (error) {\n\t\tconst errorName = (error as { name?: string }).name;\n\t\t// AWS returns 'NoSuchEntity', LocalStack returns 'NoSuchEntityException'\n\t\tif (errorName === 'NoSuchEntity' || errorName === 'NoSuchEntityException') {\n\t\t\treturn false;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Provision backup destination for a deployment.\n *\n * Creates AWS resources (S3 bucket, IAM user) and Dokploy destination if needed.\n * Reuses existing resources from state when possible.\n */\nexport async function provisionBackupDestination(\n\toptions: ProvisionBackupOptions,\n): Promise<BackupState> {\n\tconst {\n\t\tapi,\n\t\tprojectName,\n\t\tstage,\n\t\tconfig,\n\t\texistingState,\n\t\tlogger,\n\t\tawsEndpoint,\n\t} = options;\n\n\t// If we have existing state, verify the Dokploy destination still exists\n\tif (existingState?.destinationId) {\n\t\ttry {\n\t\t\tawait api.getDestination(existingState.destinationId);\n\t\t\tlogger.log(' Using existing backup destination');\n\t\t\treturn existingState;\n\t\t} catch {\n\t\t\tlogger.log(' Existing destination not found, recreating...');\n\t\t}\n\t}\n\n\t// Create AWS clients\n\tconst aws = await createAwsClients(\n\t\tconfig.region,\n\t\tconfig.profile,\n\t\tawsEndpoint,\n\t);\n\tconst sanitizedProject = sanitizeName(projectName);\n\n\t// 1. Create or verify S3 bucket\n\tconst bucketName =\n\t\texistingState?.bucketName ??\n\t\t`${sanitizedProject}-${stage}-backups-${randomSuffix()}`;\n\n\tconst bucketAlreadyExists = await bucketExists(aws.s3, bucketName);\n\tif (!bucketAlreadyExists) {\n\t\tlogger.log(` Creating S3 bucket: ${bucketName}`);\n\n\t\t// CreateBucket needs LocationConstraint for non-us-east-1 regions\n\t\tconst createBucketParams: {\n\t\t\tBucket: string;\n\t\t\tCreateBucketConfiguration?: {\n\t\t\t\tLocationConstraint: BucketLocationConstraint;\n\t\t\t};\n\t\t} = {\n\t\t\tBucket: bucketName,\n\t\t};\n\t\tif (config.region !== 'us-east-1') {\n\t\t\tcreateBucketParams.CreateBucketConfiguration = {\n\t\t\t\tLocationConstraint: config.region as BucketLocationConstraint,\n\t\t\t};\n\t\t}\n\n\t\tawait aws.s3.send(new CreateBucketCommand(createBucketParams));\n\n\t\t// Enable versioning for backup integrity\n\t\tawait aws.s3.send(\n\t\t\tnew PutBucketVersioningCommand({\n\t\t\t\tBucket: bucketName,\n\t\t\t\tVersioningConfiguration: { Status: 'Enabled' },\n\t\t\t}),\n\t\t);\n\t} else {\n\t\tlogger.log(` Using existing S3 bucket: ${bucketName}`);\n\t}\n\n\t// 2. Create or verify IAM user\n\tconst iamUserName =\n\t\texistingState?.iamUserName ?? `dokploy-backup-${sanitizedProject}-${stage}`;\n\n\tconst iamUserAlreadyExists = await userExists(aws.iam, iamUserName);\n\tif (!iamUserAlreadyExists) {\n\t\tlogger.log(` Creating IAM user: ${iamUserName}`);\n\t\tawait aws.iam.send(new CreateUserCommand({ UserName: iamUserName }));\n\t} else {\n\t\tlogger.log(` Using existing IAM user: ${iamUserName}`);\n\t}\n\n\t// 3. Attach bucket policy to IAM user\n\tconst policyDocument = {\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:GetObject',\n\t\t\t\t\t's3:PutObject',\n\t\t\t\t\t's3:DeleteObject',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketLocation',\n\t\t\t\t],\n\t\t\t\tResource: [\n\t\t\t\t\t`arn:aws:s3:::${bucketName}`,\n\t\t\t\t\t`arn:aws:s3:::${bucketName}/*`,\n\t\t\t\t],\n\t\t\t},\n\t\t],\n\t};\n\n\tlogger.log(' Updating IAM policy');\n\tawait aws.iam.send(\n\t\tnew PutUserPolicyCommand({\n\t\t\tUserName: iamUserName,\n\t\t\tPolicyName: 'DokployBackupAccess',\n\t\t\tPolicyDocument: JSON.stringify(policyDocument),\n\t\t}),\n\t);\n\n\t// 4. Create access key (or reuse existing if state has it and destination needs recreation)\n\tlet accessKeyId: string;\n\tlet secretAccessKey: string;\n\n\tif (existingState?.iamAccessKeyId && existingState?.iamSecretAccessKey) {\n\t\t// Reuse existing credentials\n\t\tlogger.log(' Using existing IAM access key');\n\t\taccessKeyId = existingState.iamAccessKeyId;\n\t\tsecretAccessKey = existingState.iamSecretAccessKey;\n\t} else {\n\t\t// Create new access key\n\t\tlogger.log(' Creating IAM access key');\n\t\tconst accessKeyResult = await aws.iam.send(\n\t\t\tnew CreateAccessKeyCommand({ UserName: iamUserName }),\n\t\t);\n\n\t\tif (!accessKeyResult.AccessKey) {\n\t\t\tthrow new Error('Failed to create IAM access key');\n\t\t}\n\n\t\taccessKeyId = accessKeyResult.AccessKey.AccessKeyId!;\n\t\tsecretAccessKey = accessKeyResult.AccessKey.SecretAccessKey!;\n\t}\n\n\t// 5. Create Dokploy destination\n\tconst destinationName = `${sanitizedProject}-${stage}-s3`;\n\tlogger.log(` Creating Dokploy destination: ${destinationName}`);\n\n\tconst { destination, created } = await api.findOrCreateDestination(\n\t\tdestinationName,\n\t\t{\n\t\t\taccessKey: accessKeyId,\n\t\t\tsecretAccessKey: secretAccessKey,\n\t\t\tbucket: bucketName,\n\t\t\tregion: config.region,\n\t\t},\n\t);\n\n\tif (created) {\n\t\tlogger.log(' ✓ Dokploy destination created');\n\t} else {\n\t\tlogger.log(' ✓ Using existing Dokploy destination');\n\t}\n\n\t// 6. Test connection\n\ttry {\n\t\tawait api.testDestinationConnection(destination.destinationId);\n\t\tlogger.log(' ✓ Destination connection verified');\n\t} catch (error) {\n\t\tlogger.log(\n\t\t\t` ⚠ Warning: Could not verify destination connection: ${error}`,\n\t\t);\n\t}\n\n\treturn {\n\t\tbucketName,\n\t\tbucketArn: `arn:aws:s3:::${bucketName}`,\n\t\tiamUserName,\n\t\tiamAccessKeyId: accessKeyId,\n\t\tiamSecretAccessKey: secretAccessKey,\n\t\tdestinationId: destination.destinationId,\n\t\tregion: config.region,\n\t\tcreatedAt: existingState?.createdAt ?? new Date().toISOString(),\n\t};\n}\n"],"mappings":";;;;;;;AAiDA,SAAS,eAAuB;AAC/B,QAAO,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,UAAU,GAAG,EAAE;AACjD;;;;AAKD,SAAS,aAAaA,MAAsB;AAC3C,QAAO,KAAK,aAAa,CAAC,QAAQ,eAAe,IAAI;AACrD;;;;AAKD,eAAe,iBACdC,QACAC,SACAC,UAC4C;CAC5C,MAAMC,SAA2C,EAAE,OAAQ;AAE3D,KAAI,SAAS;EACZ,MAAM,EAAE,SAAS,GAAG,MAAM,OAAO;AACjC,SAAO,cAAc,QAAQ,EAAE,QAAS,EAAC;CACzC;AAGD,KAAI,UAAU;AACb,SAAO,WAAW;AAClB,EAAC,OAA0B,iBAAiB;AAE5C,SAAO,cAAc;GACpB,aAAa;GACb,iBAAiB;EACjB;CACD;AAED,QAAO;EACN,IAAI,IAAI,SAAS;EACjB,KAAK,IAAI,UAAU;CACnB;AACD;;;;AAKD,eAAe,aACdC,IACAC,YACmB;AACnB,KAAI;AACH,QAAM,GAAG,KAAK,IAAI,kBAAkB,EAAE,QAAQ,WAAY,GAAE;AAC5D,SAAO;CACP,SAAQ,OAAO;AACf,MAAK,MAA4B,SAAS,WACzC,QAAO;AAGR,MACE,MAAsD,WACpD,mBAAmB,IAEtB,QAAO;AAER,QAAM;CACN;AACD;;;;AAKD,eAAe,WAAWC,KAAgBC,UAAoC;AAC7E,KAAI;AACH,QAAM,IAAI,KAAK,IAAI,eAAe,EAAE,UAAU,SAAU,GAAE;AAC1D,SAAO;CACP,SAAQ,OAAO;EACf,MAAM,YAAa,MAA4B;AAE/C,MAAI,cAAc,kBAAkB,cAAc,wBACjD,QAAO;AAER,QAAM;CACN;AACD;;;;;;;AAQD,eAAsB,2BACrBC,SACuB;CACvB,MAAM,EACL,KACA,aACA,OACA,QACA,eACA,QACA,aACA,GAAG;AAGJ,KAAI,eAAe,cAClB,KAAI;AACH,QAAM,IAAI,eAAe,cAAc,cAAc;AACrD,SAAO,IAAI,uCAAuC;AAClD,SAAO;CACP,QAAO;AACP,SAAO,IAAI,mDAAmD;CAC9D;CAIF,MAAM,MAAM,MAAM,iBACjB,OAAO,QACP,OAAO,SACP,YACA;CACD,MAAM,mBAAmB,aAAa,YAAY;CAGlD,MAAM,aACL,eAAe,eACd,EAAE,iBAAiB,GAAG,MAAM,WAAW,cAAc,CAAC;CAExD,MAAM,sBAAsB,MAAM,aAAa,IAAI,IAAI,WAAW;AAClE,MAAK,qBAAqB;AACzB,SAAO,KAAK,yBAAyB,WAAW,EAAE;EAGlD,MAAMC,qBAKF,EACH,QAAQ,WACR;AACD,MAAI,OAAO,WAAW,YACrB,oBAAmB,4BAA4B,EAC9C,oBAAoB,OAAO,OAC3B;AAGF,QAAM,IAAI,GAAG,KAAK,IAAI,oBAAoB,oBAAoB;AAG9D,QAAM,IAAI,GAAG,KACZ,IAAI,2BAA2B;GAC9B,QAAQ;GACR,yBAAyB,EAAE,QAAQ,UAAW;EAC9C,GACD;CACD,MACA,QAAO,KAAK,+BAA+B,WAAW,EAAE;CAIzD,MAAM,cACL,eAAe,gBAAgB,iBAAiB,iBAAiB,GAAG,MAAM;CAE3E,MAAM,uBAAuB,MAAM,WAAW,IAAI,KAAK,YAAY;AACnE,MAAK,sBAAsB;AAC1B,SAAO,KAAK,wBAAwB,YAAY,EAAE;AAClD,QAAM,IAAI,IAAI,KAAK,IAAI,kBAAkB,EAAE,UAAU,YAAa,GAAE;CACpE,MACA,QAAO,KAAK,8BAA8B,YAAY,EAAE;CAIzD,MAAM,iBAAiB;EACtB,SAAS;EACT,WAAW,CACV;GACC,QAAQ;GACR,QAAQ;IACP;IACA;IACA;IACA;IACA;GACA;GACD,UAAU,EACR,eAAe,WAAW,IAC1B,eAAe,WAAW,GAC3B;EACD,CACD;CACD;AAED,QAAO,IAAI,yBAAyB;AACpC,OAAM,IAAI,IAAI,KACb,IAAI,qBAAqB;EACxB,UAAU;EACV,YAAY;EACZ,gBAAgB,KAAK,UAAU,eAAe;CAC9C,GACD;CAGD,IAAIC;CACJ,IAAIC;AAEJ,KAAI,eAAe,kBAAkB,eAAe,oBAAoB;AAEvE,SAAO,IAAI,mCAAmC;AAC9C,gBAAc,cAAc;AAC5B,oBAAkB,cAAc;CAChC,OAAM;AAEN,SAAO,IAAI,6BAA6B;EACxC,MAAM,kBAAkB,MAAM,IAAI,IAAI,KACrC,IAAI,uBAAuB,EAAE,UAAU,YAAa,GACpD;AAED,OAAK,gBAAgB,UACpB,OAAM,IAAI,MAAM;AAGjB,gBAAc,gBAAgB,UAAU;AACxC,oBAAkB,gBAAgB,UAAU;CAC5C;CAGD,MAAM,mBAAmB,EAAE,iBAAiB,GAAG,MAAM;AACrD,QAAO,KAAK,mCAAmC,gBAAgB,EAAE;CAEjE,MAAM,EAAE,aAAa,SAAS,GAAG,MAAM,IAAI,wBAC1C,iBACA;EACC,WAAW;EACM;EACjB,QAAQ;EACR,QAAQ,OAAO;CACf,EACD;AAED,KAAI,QACH,QAAO,IAAI,mCAAmC;KAE9C,QAAO,IAAI,0CAA0C;AAItD,KAAI;AACH,QAAM,IAAI,0BAA0B,YAAY,cAAc;AAC9D,SAAO,IAAI,uCAAuC;CAClD,SAAQ,OAAO;AACf,SAAO,KACL,yDAAyD,MAAM,EAChE;CACD;AAED,QAAO;EACN;EACA,YAAY,eAAe,WAAW;EACtC;EACA,gBAAgB;EAChB,oBAAoB;EACpB,eAAe,YAAY;EAC3B,QAAQ,OAAO;EACf,WAAW,eAAe,aAAa,qBAAI,QAAO,aAAa;CAC/D;AACD"}
@@ -59,7 +59,7 @@ async function bundleServer(options) {
59
59
  let masterKey;
60
60
  if (stage) {
61
61
  const { readStageSecrets, toEmbeddableSecrets, validateEnvironmentVariables, initStageSecrets, writeStageSecrets } = await import("./storage-Cs4WBsc4.mjs");
62
- const { encryptSecrets, generateDefineOptions } = await import("./encryption-Db2zvGqM.mjs");
62
+ const { encryptSecrets, generateDefineOptions } = await import("./encryption-Bd_m4q3g.mjs");
63
63
  let secrets = await readStageSecrets(stage);
64
64
  if (!secrets) {
65
65
  console.log(` Initializing secrets for stage "${stage}"...`);
@@ -132,4 +132,4 @@ async function bundleServer(options) {
132
132
 
133
133
  //#endregion
134
134
  export { bundleServer };
135
- //# sourceMappingURL=bundler-c6QvGUBM.mjs.map
135
+ //# sourceMappingURL=bundler-DkA4qzUD.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundler-c6QvGUBM.mjs","names":["constructs: Construct[]","DOCKER_SERVICE_ENV_VARS: Record<string, Record<string, string>>","options: BundleOptions","masterKey: string | undefined"],"sources":["../src/build/bundler.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\n\n/**\n * Banner to inject into ESM bundle for CJS compatibility.\n * Creates a `require` function using Node's createRequire for packages\n * that internally use CommonJS require() for Node builtins.\n */\nconst ESM_CJS_COMPAT_BANNER =\n\t'import { createRequire } from \"module\"; const require = createRequire(import.meta.url);';\n\nexport interface BundleOptions {\n\t/** Entry point file (e.g., .gkm/server/server.ts) */\n\tentryPoint: string;\n\t/** Output directory for bundled files */\n\toutputDir: string;\n\t/** Minify the output (default: true) */\n\tminify: boolean;\n\t/** Generate sourcemaps (default: false) */\n\tsourcemap: boolean;\n\t/** Packages to exclude from bundling */\n\texternal: string[];\n\t/** Stage for secrets injection (optional) */\n\tstage?: string;\n\t/** Constructs to validate environment variables for */\n\tconstructs?: Construct[];\n\t/** Docker compose services configured (for auto-populating env vars) */\n\tdockerServices?: {\n\t\tpostgres?: boolean;\n\t\tredis?: boolean;\n\t\trabbitmq?: boolean;\n\t};\n}\n\nexport interface BundleResult {\n\t/** Path to the bundled output */\n\toutputPath: string;\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n/**\n * Collect all required environment variables from constructs.\n * Uses the SnifferEnvironmentParser to detect which env vars each service needs.\n *\n * @param constructs - Array of constructs to analyze\n * @returns Deduplicated array of required environment variable names\n */\nasync function collectRequiredEnvVars(\n\tconstructs: Construct[],\n): Promise<string[]> {\n\tconst allEnvVars = new Set<string>();\n\n\tfor (const construct of constructs) {\n\t\tconst envVars = await construct.getEnvironment();\n\t\tenvVars.forEach((v) => allEnvVars.add(v));\n\t}\n\n\treturn Array.from(allEnvVars).sort();\n}\n\n/**\n * Bundle the server application using esbuild.\n * Creates a fully standalone bundle with all dependencies included.\n *\n * @param options - Bundle configuration options\n * @returns Bundle result with output path and optional master key\n */\n\n/** Default env var values for docker compose services */\nconst DOCKER_SERVICE_ENV_VARS: Record<string, Record<string, string>> = {\n\tpostgres: {\n\t\tDATABASE_URL: 'postgresql://postgres:postgres@postgres:5432/app',\n\t},\n\tredis: {\n\t\tREDIS_URL: 'redis://redis:6379',\n\t},\n\trabbitmq: {\n\t\tRABBITMQ_URL: 'amqp://rabbitmq:5672',\n\t},\n};\n\nexport async function bundleServer(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst {\n\t\tentryPoint,\n\t\toutputDir,\n\t\tminify,\n\t\tsourcemap,\n\t\texternal,\n\t\tstage,\n\t\tconstructs,\n\t\tdockerServices,\n\t} = options;\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\tconst mjsOutput = join(outputDir, 'server.mjs');\n\n\t// Build command-line arguments for esbuild\n\tconst args = [\n\t\t'npx',\n\t\t'esbuild',\n\t\tentryPoint,\n\t\t'--bundle',\n\t\t'--platform=node',\n\t\t'--target=node22',\n\t\t'--format=esm',\n\t\t`--outfile=${mjsOutput}`,\n\t\t'--packages=bundle', // Bundle all dependencies for standalone output\n\t\t`--banner:js=${ESM_CJS_COMPAT_BANNER}`, // CJS compatibility for packages like pino\n\t];\n\n\tif (minify) {\n\t\targs.push('--minify');\n\t}\n\n\tif (sourcemap) {\n\t\targs.push('--sourcemap');\n\t}\n\n\t// Add external packages (user-specified)\n\tfor (const ext of external) {\n\t\targs.push(`--external:${ext}`);\n\t}\n\n\t// Handle secrets injection if stage is provided\n\tlet masterKey: string | undefined;\n\n\tif (stage) {\n\t\tconst {\n\t\t\treadStageSecrets,\n\t\t\ttoEmbeddableSecrets,\n\t\t\tvalidateEnvironmentVariables,\n\t\t\tinitStageSecrets,\n\t\t\twriteStageSecrets,\n\t\t} = await import('../secrets/storage');\n\t\tconst { encryptSecrets, generateDefineOptions } = await import(\n\t\t\t'../secrets/encryption'\n\t\t);\n\n\t\tlet secrets = await readStageSecrets(stage);\n\n\t\tif (!secrets) {\n\t\t\t// Auto-initialize secrets for the stage\n\t\t\tconsole.log(` Initializing secrets for stage \"${stage}\"...`);\n\t\t\tsecrets = initStageSecrets(stage);\n\t\t\tawait writeStageSecrets(secrets);\n\t\t\tconsole.log(` ✓ Created .gkm/secrets/${stage}.json`);\n\t\t}\n\n\t\t// Auto-populate env vars from docker compose services\n\t\tif (dockerServices) {\n\t\t\tfor (const [service, enabled] of Object.entries(dockerServices)) {\n\t\t\t\tif (enabled && DOCKER_SERVICE_ENV_VARS[service]) {\n\t\t\t\t\tfor (const [envVar, defaultValue] of Object.entries(\n\t\t\t\t\t\tDOCKER_SERVICE_ENV_VARS[service],\n\t\t\t\t\t)) {\n\t\t\t\t\t\t// Check if not already in urls or custom\n\t\t\t\t\t\tconst urlKey = envVar as keyof typeof secrets.urls;\n\t\t\t\t\t\tif (!secrets.urls[urlKey] && !secrets.custom[envVar]) {\n\t\t\t\t\t\t\tsecrets.urls[urlKey] = defaultValue;\n\t\t\t\t\t\t\tconsole.log(` Auto-populated ${envVar} from docker compose`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate environment variables if constructs are provided\n\t\tif (constructs && constructs.length > 0) {\n\t\t\tconsole.log(' Analyzing environment variable requirements...');\n\t\t\tconst requiredVars = await collectRequiredEnvVars(constructs);\n\n\t\t\tif (requiredVars.length > 0) {\n\t\t\t\tconst validation = validateEnvironmentVariables(requiredVars, secrets);\n\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tconst errorMessage = [\n\t\t\t\t\t\t`Missing environment variables for stage \"${stage}\":`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t...validation.missing.map((v) => ` ❌ ${v}`),\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'To fix this, either:',\n\t\t\t\t\t\t` 1. Add the missing variables to .gkm/secrets/${stage}.json using:`,\n\t\t\t\t\t\t` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t` 2. Or import from a JSON file:`,\n\t\t\t\t\t\t` gkm secrets:import secrets.json --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'Required variables:',\n\t\t\t\t\t\t...validation.required.map((v) =>\n\t\t\t\t\t\t\tvalidation.missing.includes(v) ? ` ❌ ${v}` : ` ✓ ${v}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t].join('\\n');\n\n\t\t\t\t\tthrow new Error(errorMessage);\n\t\t\t\t}\n\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ✓ All ${requiredVars.length} required environment variables found`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Convert to embeddable format and encrypt\n\t\tconst embeddable = toEmbeddableSecrets(secrets);\n\t\tconst encrypted = encryptSecrets(embeddable);\n\t\tmasterKey = encrypted.masterKey;\n\n\t\t// Add define options for build-time injection using esbuild's --define:KEY=VALUE format\n\t\tconst defines = generateDefineOptions(encrypted);\n\t\tfor (const [key, value] of Object.entries(defines)) {\n\t\t\targs.push(`--define:${key}=${JSON.stringify(value)}`);\n\t\t}\n\n\t\tconsole.log(` Secrets encrypted for stage \"${stage}\"`);\n\t}\n\n\ttry {\n\t\t// Run esbuild with command-line arguments\n\t\tconst [cmd, ...cmdArgs] = args as [string, ...string[]];\n\t\tconst result = spawnSync(cmd, cmdArgs, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t\tshell: process.platform === 'win32', // Only use shell on Windows for npx resolution\n\t\t});\n\n\t\tif (result.error) {\n\t\t\tthrow result.error;\n\t\t}\n\t\tif (result.status !== 0) {\n\t\t\tthrow new Error(`esbuild exited with code ${result.status}`);\n\t\t}\n\n\t\t// Add shebang to the bundled file\n\t\tconst { readFile } = await import('node:fs/promises');\n\t\tconst content = await readFile(mjsOutput, 'utf-8');\n\t\tif (!content.startsWith('#!')) {\n\t\t\tawait writeFile(mjsOutput, `#!/usr/bin/env node\\n${content}`);\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to bundle server: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n\n\treturn {\n\t\toutputPath: mjsOutput,\n\t\tmasterKey,\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bundler-DkA4qzUD.mjs","names":["constructs: Construct[]","DOCKER_SERVICE_ENV_VARS: Record<string, Record<string, string>>","options: BundleOptions","masterKey: string | undefined"],"sources":["../src/build/bundler.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\n\n/**\n * Banner to inject into ESM bundle for CJS compatibility.\n * Creates a `require` function using Node's createRequire for packages\n * that internally use CommonJS require() for Node builtins.\n */\nconst ESM_CJS_COMPAT_BANNER =\n\t'import { createRequire } from \"module\"; const require = createRequire(import.meta.url);';\n\nexport interface BundleOptions {\n\t/** Entry point file (e.g., .gkm/server/server.ts) */\n\tentryPoint: string;\n\t/** Output directory for bundled files */\n\toutputDir: string;\n\t/** Minify the output (default: true) */\n\tminify: boolean;\n\t/** Generate sourcemaps (default: false) */\n\tsourcemap: boolean;\n\t/** Packages to exclude from bundling */\n\texternal: string[];\n\t/** Stage for secrets injection (optional) */\n\tstage?: string;\n\t/** Constructs to validate environment variables for */\n\tconstructs?: Construct[];\n\t/** Docker compose services configured (for auto-populating env vars) */\n\tdockerServices?: {\n\t\tpostgres?: boolean;\n\t\tredis?: boolean;\n\t\trabbitmq?: boolean;\n\t};\n}\n\nexport interface BundleResult {\n\t/** Path to the bundled output */\n\toutputPath: string;\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n/**\n * Collect all required environment variables from constructs.\n * Uses the SnifferEnvironmentParser to detect which env vars each service needs.\n *\n * @param constructs - Array of constructs to analyze\n * @returns Deduplicated array of required environment variable names\n */\nasync function collectRequiredEnvVars(\n\tconstructs: Construct[],\n): Promise<string[]> {\n\tconst allEnvVars = new Set<string>();\n\n\tfor (const construct of constructs) {\n\t\tconst envVars = await construct.getEnvironment();\n\t\tenvVars.forEach((v) => allEnvVars.add(v));\n\t}\n\n\treturn Array.from(allEnvVars).sort();\n}\n\n/**\n * Bundle the server application using esbuild.\n * Creates a fully standalone bundle with all dependencies included.\n *\n * @param options - Bundle configuration options\n * @returns Bundle result with output path and optional master key\n */\n\n/** Default env var values for docker compose services */\nconst DOCKER_SERVICE_ENV_VARS: Record<string, Record<string, string>> = {\n\tpostgres: {\n\t\tDATABASE_URL: 'postgresql://postgres:postgres@postgres:5432/app',\n\t},\n\tredis: {\n\t\tREDIS_URL: 'redis://redis:6379',\n\t},\n\trabbitmq: {\n\t\tRABBITMQ_URL: 'amqp://rabbitmq:5672',\n\t},\n};\n\nexport async function bundleServer(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst {\n\t\tentryPoint,\n\t\toutputDir,\n\t\tminify,\n\t\tsourcemap,\n\t\texternal,\n\t\tstage,\n\t\tconstructs,\n\t\tdockerServices,\n\t} = options;\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\tconst mjsOutput = join(outputDir, 'server.mjs');\n\n\t// Build command-line arguments for esbuild\n\tconst args = [\n\t\t'npx',\n\t\t'esbuild',\n\t\tentryPoint,\n\t\t'--bundle',\n\t\t'--platform=node',\n\t\t'--target=node22',\n\t\t'--format=esm',\n\t\t`--outfile=${mjsOutput}`,\n\t\t'--packages=bundle', // Bundle all dependencies for standalone output\n\t\t`--banner:js=${ESM_CJS_COMPAT_BANNER}`, // CJS compatibility for packages like pino\n\t];\n\n\tif (minify) {\n\t\targs.push('--minify');\n\t}\n\n\tif (sourcemap) {\n\t\targs.push('--sourcemap');\n\t}\n\n\t// Add external packages (user-specified)\n\tfor (const ext of external) {\n\t\targs.push(`--external:${ext}`);\n\t}\n\n\t// Handle secrets injection if stage is provided\n\tlet masterKey: string | undefined;\n\n\tif (stage) {\n\t\tconst {\n\t\t\treadStageSecrets,\n\t\t\ttoEmbeddableSecrets,\n\t\t\tvalidateEnvironmentVariables,\n\t\t\tinitStageSecrets,\n\t\t\twriteStageSecrets,\n\t\t} = await import('../secrets/storage');\n\t\tconst { encryptSecrets, generateDefineOptions } = await import(\n\t\t\t'../secrets/encryption'\n\t\t);\n\n\t\tlet secrets = await readStageSecrets(stage);\n\n\t\tif (!secrets) {\n\t\t\t// Auto-initialize secrets for the stage\n\t\t\tconsole.log(` Initializing secrets for stage \"${stage}\"...`);\n\t\t\tsecrets = initStageSecrets(stage);\n\t\t\tawait writeStageSecrets(secrets);\n\t\t\tconsole.log(` ✓ Created .gkm/secrets/${stage}.json`);\n\t\t}\n\n\t\t// Auto-populate env vars from docker compose services\n\t\tif (dockerServices) {\n\t\t\tfor (const [service, enabled] of Object.entries(dockerServices)) {\n\t\t\t\tif (enabled && DOCKER_SERVICE_ENV_VARS[service]) {\n\t\t\t\t\tfor (const [envVar, defaultValue] of Object.entries(\n\t\t\t\t\t\tDOCKER_SERVICE_ENV_VARS[service],\n\t\t\t\t\t)) {\n\t\t\t\t\t\t// Check if not already in urls or custom\n\t\t\t\t\t\tconst urlKey = envVar as keyof typeof secrets.urls;\n\t\t\t\t\t\tif (!secrets.urls[urlKey] && !secrets.custom[envVar]) {\n\t\t\t\t\t\t\tsecrets.urls[urlKey] = defaultValue;\n\t\t\t\t\t\t\tconsole.log(` Auto-populated ${envVar} from docker compose`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate environment variables if constructs are provided\n\t\tif (constructs && constructs.length > 0) {\n\t\t\tconsole.log(' Analyzing environment variable requirements...');\n\t\t\tconst requiredVars = await collectRequiredEnvVars(constructs);\n\n\t\t\tif (requiredVars.length > 0) {\n\t\t\t\tconst validation = validateEnvironmentVariables(requiredVars, secrets);\n\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tconst errorMessage = [\n\t\t\t\t\t\t`Missing environment variables for stage \"${stage}\":`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t...validation.missing.map((v) => ` ❌ ${v}`),\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'To fix this, either:',\n\t\t\t\t\t\t` 1. Add the missing variables to .gkm/secrets/${stage}.json using:`,\n\t\t\t\t\t\t` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t` 2. Or import from a JSON file:`,\n\t\t\t\t\t\t` gkm secrets:import secrets.json --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'Required variables:',\n\t\t\t\t\t\t...validation.required.map((v) =>\n\t\t\t\t\t\t\tvalidation.missing.includes(v) ? ` ❌ ${v}` : ` ✓ ${v}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t].join('\\n');\n\n\t\t\t\t\tthrow new Error(errorMessage);\n\t\t\t\t}\n\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ✓ All ${requiredVars.length} required environment variables found`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Convert to embeddable format and encrypt\n\t\tconst embeddable = toEmbeddableSecrets(secrets);\n\t\tconst encrypted = encryptSecrets(embeddable);\n\t\tmasterKey = encrypted.masterKey;\n\n\t\t// Add define options for build-time injection using esbuild's --define:KEY=VALUE format\n\t\tconst defines = generateDefineOptions(encrypted);\n\t\tfor (const [key, value] of Object.entries(defines)) {\n\t\t\targs.push(`--define:${key}=${JSON.stringify(value)}`);\n\t\t}\n\n\t\tconsole.log(` Secrets encrypted for stage \"${stage}\"`);\n\t}\n\n\ttry {\n\t\t// Run esbuild with command-line arguments\n\t\tconst [cmd, ...cmdArgs] = args as [string, ...string[]];\n\t\tconst result = spawnSync(cmd, cmdArgs, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t\tshell: process.platform === 'win32', // Only use shell on Windows for npx resolution\n\t\t});\n\n\t\tif (result.error) {\n\t\t\tthrow result.error;\n\t\t}\n\t\tif (result.status !== 0) {\n\t\t\tthrow new Error(`esbuild exited with code ${result.status}`);\n\t\t}\n\n\t\t// Add shebang to the bundled file\n\t\tconst { readFile } = await import('node:fs/promises');\n\t\tconst content = await readFile(mjsOutput, 'utf-8');\n\t\tif (!content.startsWith('#!')) {\n\t\t\tawait writeFile(mjsOutput, `#!/usr/bin/env node\\n${content}`);\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to bundle server: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n\n\treturn {\n\t\toutputPath: mjsOutput,\n\t\tmasterKey,\n\t};\n}\n"],"mappings":"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"}
@@ -60,7 +60,7 @@ async function bundleServer(options) {
60
60
  let masterKey;
61
61
  if (stage) {
62
62
  const { readStageSecrets, toEmbeddableSecrets, validateEnvironmentVariables, initStageSecrets, writeStageSecrets } = await Promise.resolve().then(() => require("./storage-DOEtT2Hr.cjs"));
63
- const { encryptSecrets, generateDefineOptions } = await Promise.resolve().then(() => require("./encryption-CipsS0KW.cjs"));
63
+ const { encryptSecrets, generateDefineOptions } = await Promise.resolve().then(() => require("./encryption-BVS7Vmzs.cjs"));
64
64
  let secrets = await readStageSecrets(stage);
65
65
  if (!secrets) {
66
66
  console.log(` Initializing secrets for stage "${stage}"...`);
@@ -133,4 +133,4 @@ async function bundleServer(options) {
133
133
 
134
134
  //#endregion
135
135
  exports.bundleServer = bundleServer;
136
- //# sourceMappingURL=bundler-BG6P249p.cjs.map
136
+ //# sourceMappingURL=bundler-Dufq3uvI.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"bundler-BG6P249p.cjs","names":["constructs: Construct[]","DOCKER_SERVICE_ENV_VARS: Record<string, Record<string, string>>","options: BundleOptions","masterKey: string | undefined"],"sources":["../src/build/bundler.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\n\n/**\n * Banner to inject into ESM bundle for CJS compatibility.\n * Creates a `require` function using Node's createRequire for packages\n * that internally use CommonJS require() for Node builtins.\n */\nconst ESM_CJS_COMPAT_BANNER =\n\t'import { createRequire } from \"module\"; const require = createRequire(import.meta.url);';\n\nexport interface BundleOptions {\n\t/** Entry point file (e.g., .gkm/server/server.ts) */\n\tentryPoint: string;\n\t/** Output directory for bundled files */\n\toutputDir: string;\n\t/** Minify the output (default: true) */\n\tminify: boolean;\n\t/** Generate sourcemaps (default: false) */\n\tsourcemap: boolean;\n\t/** Packages to exclude from bundling */\n\texternal: string[];\n\t/** Stage for secrets injection (optional) */\n\tstage?: string;\n\t/** Constructs to validate environment variables for */\n\tconstructs?: Construct[];\n\t/** Docker compose services configured (for auto-populating env vars) */\n\tdockerServices?: {\n\t\tpostgres?: boolean;\n\t\tredis?: boolean;\n\t\trabbitmq?: boolean;\n\t};\n}\n\nexport interface BundleResult {\n\t/** Path to the bundled output */\n\toutputPath: string;\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n/**\n * Collect all required environment variables from constructs.\n * Uses the SnifferEnvironmentParser to detect which env vars each service needs.\n *\n * @param constructs - Array of constructs to analyze\n * @returns Deduplicated array of required environment variable names\n */\nasync function collectRequiredEnvVars(\n\tconstructs: Construct[],\n): Promise<string[]> {\n\tconst allEnvVars = new Set<string>();\n\n\tfor (const construct of constructs) {\n\t\tconst envVars = await construct.getEnvironment();\n\t\tenvVars.forEach((v) => allEnvVars.add(v));\n\t}\n\n\treturn Array.from(allEnvVars).sort();\n}\n\n/**\n * Bundle the server application using esbuild.\n * Creates a fully standalone bundle with all dependencies included.\n *\n * @param options - Bundle configuration options\n * @returns Bundle result with output path and optional master key\n */\n\n/** Default env var values for docker compose services */\nconst DOCKER_SERVICE_ENV_VARS: Record<string, Record<string, string>> = {\n\tpostgres: {\n\t\tDATABASE_URL: 'postgresql://postgres:postgres@postgres:5432/app',\n\t},\n\tredis: {\n\t\tREDIS_URL: 'redis://redis:6379',\n\t},\n\trabbitmq: {\n\t\tRABBITMQ_URL: 'amqp://rabbitmq:5672',\n\t},\n};\n\nexport async function bundleServer(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst {\n\t\tentryPoint,\n\t\toutputDir,\n\t\tminify,\n\t\tsourcemap,\n\t\texternal,\n\t\tstage,\n\t\tconstructs,\n\t\tdockerServices,\n\t} = options;\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\tconst mjsOutput = join(outputDir, 'server.mjs');\n\n\t// Build command-line arguments for esbuild\n\tconst args = [\n\t\t'npx',\n\t\t'esbuild',\n\t\tentryPoint,\n\t\t'--bundle',\n\t\t'--platform=node',\n\t\t'--target=node22',\n\t\t'--format=esm',\n\t\t`--outfile=${mjsOutput}`,\n\t\t'--packages=bundle', // Bundle all dependencies for standalone output\n\t\t`--banner:js=${ESM_CJS_COMPAT_BANNER}`, // CJS compatibility for packages like pino\n\t];\n\n\tif (minify) {\n\t\targs.push('--minify');\n\t}\n\n\tif (sourcemap) {\n\t\targs.push('--sourcemap');\n\t}\n\n\t// Add external packages (user-specified)\n\tfor (const ext of external) {\n\t\targs.push(`--external:${ext}`);\n\t}\n\n\t// Handle secrets injection if stage is provided\n\tlet masterKey: string | undefined;\n\n\tif (stage) {\n\t\tconst {\n\t\t\treadStageSecrets,\n\t\t\ttoEmbeddableSecrets,\n\t\t\tvalidateEnvironmentVariables,\n\t\t\tinitStageSecrets,\n\t\t\twriteStageSecrets,\n\t\t} = await import('../secrets/storage');\n\t\tconst { encryptSecrets, generateDefineOptions } = await import(\n\t\t\t'../secrets/encryption'\n\t\t);\n\n\t\tlet secrets = await readStageSecrets(stage);\n\n\t\tif (!secrets) {\n\t\t\t// Auto-initialize secrets for the stage\n\t\t\tconsole.log(` Initializing secrets for stage \"${stage}\"...`);\n\t\t\tsecrets = initStageSecrets(stage);\n\t\t\tawait writeStageSecrets(secrets);\n\t\t\tconsole.log(` ✓ Created .gkm/secrets/${stage}.json`);\n\t\t}\n\n\t\t// Auto-populate env vars from docker compose services\n\t\tif (dockerServices) {\n\t\t\tfor (const [service, enabled] of Object.entries(dockerServices)) {\n\t\t\t\tif (enabled && DOCKER_SERVICE_ENV_VARS[service]) {\n\t\t\t\t\tfor (const [envVar, defaultValue] of Object.entries(\n\t\t\t\t\t\tDOCKER_SERVICE_ENV_VARS[service],\n\t\t\t\t\t)) {\n\t\t\t\t\t\t// Check if not already in urls or custom\n\t\t\t\t\t\tconst urlKey = envVar as keyof typeof secrets.urls;\n\t\t\t\t\t\tif (!secrets.urls[urlKey] && !secrets.custom[envVar]) {\n\t\t\t\t\t\t\tsecrets.urls[urlKey] = defaultValue;\n\t\t\t\t\t\t\tconsole.log(` Auto-populated ${envVar} from docker compose`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate environment variables if constructs are provided\n\t\tif (constructs && constructs.length > 0) {\n\t\t\tconsole.log(' Analyzing environment variable requirements...');\n\t\t\tconst requiredVars = await collectRequiredEnvVars(constructs);\n\n\t\t\tif (requiredVars.length > 0) {\n\t\t\t\tconst validation = validateEnvironmentVariables(requiredVars, secrets);\n\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tconst errorMessage = [\n\t\t\t\t\t\t`Missing environment variables for stage \"${stage}\":`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t...validation.missing.map((v) => ` ❌ ${v}`),\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'To fix this, either:',\n\t\t\t\t\t\t` 1. Add the missing variables to .gkm/secrets/${stage}.json using:`,\n\t\t\t\t\t\t` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t` 2. Or import from a JSON file:`,\n\t\t\t\t\t\t` gkm secrets:import secrets.json --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'Required variables:',\n\t\t\t\t\t\t...validation.required.map((v) =>\n\t\t\t\t\t\t\tvalidation.missing.includes(v) ? ` ❌ ${v}` : ` ✓ ${v}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t].join('\\n');\n\n\t\t\t\t\tthrow new Error(errorMessage);\n\t\t\t\t}\n\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ✓ All ${requiredVars.length} required environment variables found`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Convert to embeddable format and encrypt\n\t\tconst embeddable = toEmbeddableSecrets(secrets);\n\t\tconst encrypted = encryptSecrets(embeddable);\n\t\tmasterKey = encrypted.masterKey;\n\n\t\t// Add define options for build-time injection using esbuild's --define:KEY=VALUE format\n\t\tconst defines = generateDefineOptions(encrypted);\n\t\tfor (const [key, value] of Object.entries(defines)) {\n\t\t\targs.push(`--define:${key}=${JSON.stringify(value)}`);\n\t\t}\n\n\t\tconsole.log(` Secrets encrypted for stage \"${stage}\"`);\n\t}\n\n\ttry {\n\t\t// Run esbuild with command-line arguments\n\t\tconst [cmd, ...cmdArgs] = args as [string, ...string[]];\n\t\tconst result = spawnSync(cmd, cmdArgs, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t\tshell: process.platform === 'win32', // Only use shell on Windows for npx resolution\n\t\t});\n\n\t\tif (result.error) {\n\t\t\tthrow result.error;\n\t\t}\n\t\tif (result.status !== 0) {\n\t\t\tthrow new Error(`esbuild exited with code ${result.status}`);\n\t\t}\n\n\t\t// Add shebang to the bundled file\n\t\tconst { readFile } = await import('node:fs/promises');\n\t\tconst content = await readFile(mjsOutput, 'utf-8');\n\t\tif (!content.startsWith('#!')) {\n\t\t\tawait writeFile(mjsOutput, `#!/usr/bin/env node\\n${content}`);\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to bundle server: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n\n\treturn {\n\t\toutputPath: mjsOutput,\n\t\tmasterKey,\n\t};\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"bundler-Dufq3uvI.cjs","names":["constructs: Construct[]","DOCKER_SERVICE_ENV_VARS: Record<string, Record<string, string>>","options: BundleOptions","masterKey: string | undefined"],"sources":["../src/build/bundler.ts"],"sourcesContent":["import { spawnSync } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport type { Construct } from '@geekmidas/constructs';\n\n/**\n * Banner to inject into ESM bundle for CJS compatibility.\n * Creates a `require` function using Node's createRequire for packages\n * that internally use CommonJS require() for Node builtins.\n */\nconst ESM_CJS_COMPAT_BANNER =\n\t'import { createRequire } from \"module\"; const require = createRequire(import.meta.url);';\n\nexport interface BundleOptions {\n\t/** Entry point file (e.g., .gkm/server/server.ts) */\n\tentryPoint: string;\n\t/** Output directory for bundled files */\n\toutputDir: string;\n\t/** Minify the output (default: true) */\n\tminify: boolean;\n\t/** Generate sourcemaps (default: false) */\n\tsourcemap: boolean;\n\t/** Packages to exclude from bundling */\n\texternal: string[];\n\t/** Stage for secrets injection (optional) */\n\tstage?: string;\n\t/** Constructs to validate environment variables for */\n\tconstructs?: Construct[];\n\t/** Docker compose services configured (for auto-populating env vars) */\n\tdockerServices?: {\n\t\tpostgres?: boolean;\n\t\tredis?: boolean;\n\t\trabbitmq?: boolean;\n\t};\n}\n\nexport interface BundleResult {\n\t/** Path to the bundled output */\n\toutputPath: string;\n\t/** Ephemeral master key for deployment (only if stage was provided) */\n\tmasterKey?: string;\n}\n\n/**\n * Collect all required environment variables from constructs.\n * Uses the SnifferEnvironmentParser to detect which env vars each service needs.\n *\n * @param constructs - Array of constructs to analyze\n * @returns Deduplicated array of required environment variable names\n */\nasync function collectRequiredEnvVars(\n\tconstructs: Construct[],\n): Promise<string[]> {\n\tconst allEnvVars = new Set<string>();\n\n\tfor (const construct of constructs) {\n\t\tconst envVars = await construct.getEnvironment();\n\t\tenvVars.forEach((v) => allEnvVars.add(v));\n\t}\n\n\treturn Array.from(allEnvVars).sort();\n}\n\n/**\n * Bundle the server application using esbuild.\n * Creates a fully standalone bundle with all dependencies included.\n *\n * @param options - Bundle configuration options\n * @returns Bundle result with output path and optional master key\n */\n\n/** Default env var values for docker compose services */\nconst DOCKER_SERVICE_ENV_VARS: Record<string, Record<string, string>> = {\n\tpostgres: {\n\t\tDATABASE_URL: 'postgresql://postgres:postgres@postgres:5432/app',\n\t},\n\tredis: {\n\t\tREDIS_URL: 'redis://redis:6379',\n\t},\n\trabbitmq: {\n\t\tRABBITMQ_URL: 'amqp://rabbitmq:5672',\n\t},\n};\n\nexport async function bundleServer(\n\toptions: BundleOptions,\n): Promise<BundleResult> {\n\tconst {\n\t\tentryPoint,\n\t\toutputDir,\n\t\tminify,\n\t\tsourcemap,\n\t\texternal,\n\t\tstage,\n\t\tconstructs,\n\t\tdockerServices,\n\t} = options;\n\n\t// Ensure output directory exists\n\tawait mkdir(outputDir, { recursive: true });\n\n\tconst mjsOutput = join(outputDir, 'server.mjs');\n\n\t// Build command-line arguments for esbuild\n\tconst args = [\n\t\t'npx',\n\t\t'esbuild',\n\t\tentryPoint,\n\t\t'--bundle',\n\t\t'--platform=node',\n\t\t'--target=node22',\n\t\t'--format=esm',\n\t\t`--outfile=${mjsOutput}`,\n\t\t'--packages=bundle', // Bundle all dependencies for standalone output\n\t\t`--banner:js=${ESM_CJS_COMPAT_BANNER}`, // CJS compatibility for packages like pino\n\t];\n\n\tif (minify) {\n\t\targs.push('--minify');\n\t}\n\n\tif (sourcemap) {\n\t\targs.push('--sourcemap');\n\t}\n\n\t// Add external packages (user-specified)\n\tfor (const ext of external) {\n\t\targs.push(`--external:${ext}`);\n\t}\n\n\t// Handle secrets injection if stage is provided\n\tlet masterKey: string | undefined;\n\n\tif (stage) {\n\t\tconst {\n\t\t\treadStageSecrets,\n\t\t\ttoEmbeddableSecrets,\n\t\t\tvalidateEnvironmentVariables,\n\t\t\tinitStageSecrets,\n\t\t\twriteStageSecrets,\n\t\t} = await import('../secrets/storage');\n\t\tconst { encryptSecrets, generateDefineOptions } = await import(\n\t\t\t'../secrets/encryption'\n\t\t);\n\n\t\tlet secrets = await readStageSecrets(stage);\n\n\t\tif (!secrets) {\n\t\t\t// Auto-initialize secrets for the stage\n\t\t\tconsole.log(` Initializing secrets for stage \"${stage}\"...`);\n\t\t\tsecrets = initStageSecrets(stage);\n\t\t\tawait writeStageSecrets(secrets);\n\t\t\tconsole.log(` ✓ Created .gkm/secrets/${stage}.json`);\n\t\t}\n\n\t\t// Auto-populate env vars from docker compose services\n\t\tif (dockerServices) {\n\t\t\tfor (const [service, enabled] of Object.entries(dockerServices)) {\n\t\t\t\tif (enabled && DOCKER_SERVICE_ENV_VARS[service]) {\n\t\t\t\t\tfor (const [envVar, defaultValue] of Object.entries(\n\t\t\t\t\t\tDOCKER_SERVICE_ENV_VARS[service],\n\t\t\t\t\t)) {\n\t\t\t\t\t\t// Check if not already in urls or custom\n\t\t\t\t\t\tconst urlKey = envVar as keyof typeof secrets.urls;\n\t\t\t\t\t\tif (!secrets.urls[urlKey] && !secrets.custom[envVar]) {\n\t\t\t\t\t\t\tsecrets.urls[urlKey] = defaultValue;\n\t\t\t\t\t\t\tconsole.log(` Auto-populated ${envVar} from docker compose`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Validate environment variables if constructs are provided\n\t\tif (constructs && constructs.length > 0) {\n\t\t\tconsole.log(' Analyzing environment variable requirements...');\n\t\t\tconst requiredVars = await collectRequiredEnvVars(constructs);\n\n\t\t\tif (requiredVars.length > 0) {\n\t\t\t\tconst validation = validateEnvironmentVariables(requiredVars, secrets);\n\n\t\t\t\tif (!validation.valid) {\n\t\t\t\t\tconst errorMessage = [\n\t\t\t\t\t\t`Missing environment variables for stage \"${stage}\":`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t...validation.missing.map((v) => ` ❌ ${v}`),\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'To fix this, either:',\n\t\t\t\t\t\t` 1. Add the missing variables to .gkm/secrets/${stage}.json using:`,\n\t\t\t\t\t\t` gkm secrets:set <KEY> <VALUE> --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t` 2. Or import from a JSON file:`,\n\t\t\t\t\t\t` gkm secrets:import secrets.json --stage ${stage}`,\n\t\t\t\t\t\t'',\n\t\t\t\t\t\t'Required variables:',\n\t\t\t\t\t\t...validation.required.map((v) =>\n\t\t\t\t\t\t\tvalidation.missing.includes(v) ? ` ❌ ${v}` : ` ✓ ${v}`,\n\t\t\t\t\t\t),\n\t\t\t\t\t].join('\\n');\n\n\t\t\t\t\tthrow new Error(errorMessage);\n\t\t\t\t}\n\n\t\t\t\tconsole.log(\n\t\t\t\t\t` ✓ All ${requiredVars.length} required environment variables found`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\t// Convert to embeddable format and encrypt\n\t\tconst embeddable = toEmbeddableSecrets(secrets);\n\t\tconst encrypted = encryptSecrets(embeddable);\n\t\tmasterKey = encrypted.masterKey;\n\n\t\t// Add define options for build-time injection using esbuild's --define:KEY=VALUE format\n\t\tconst defines = generateDefineOptions(encrypted);\n\t\tfor (const [key, value] of Object.entries(defines)) {\n\t\t\targs.push(`--define:${key}=${JSON.stringify(value)}`);\n\t\t}\n\n\t\tconsole.log(` Secrets encrypted for stage \"${stage}\"`);\n\t}\n\n\ttry {\n\t\t// Run esbuild with command-line arguments\n\t\tconst [cmd, ...cmdArgs] = args as [string, ...string[]];\n\t\tconst result = spawnSync(cmd, cmdArgs, {\n\t\t\tcwd: process.cwd(),\n\t\t\tstdio: 'inherit',\n\t\t\tshell: process.platform === 'win32', // Only use shell on Windows for npx resolution\n\t\t});\n\n\t\tif (result.error) {\n\t\t\tthrow result.error;\n\t\t}\n\t\tif (result.status !== 0) {\n\t\t\tthrow new Error(`esbuild exited with code ${result.status}`);\n\t\t}\n\n\t\t// Add shebang to the bundled file\n\t\tconst { readFile } = await import('node:fs/promises');\n\t\tconst content = await readFile(mjsOutput, 'utf-8');\n\t\tif (!content.startsWith('#!')) {\n\t\t\tawait writeFile(mjsOutput, `#!/usr/bin/env node\\n${content}`);\n\t\t}\n\t} catch (error) {\n\t\tthrow new Error(\n\t\t\t`Failed to bundle server: ${error instanceof Error ? error.message : 'Unknown error'}`,\n\t\t);\n\t}\n\n\treturn {\n\t\toutputPath: mjsOutput,\n\t\tmasterKey,\n\t};\n}\n"],"mappings":";;;;;;;;;;;AAUA,MAAM,wBACL;;;;;;;;AAuCD,eAAe,uBACdA,YACoB;CACpB,MAAM,6BAAa,IAAI;AAEvB,MAAK,MAAM,aAAa,YAAY;EACnC,MAAM,UAAU,MAAM,UAAU,gBAAgB;AAChD,UAAQ,QAAQ,CAAC,MAAM,WAAW,IAAI,EAAE,CAAC;CACzC;AAED,QAAO,MAAM,KAAK,WAAW,CAAC,MAAM;AACpC;;;;;;;;;AAWD,MAAMC,0BAAkE;CACvE,UAAU,EACT,cAAc,mDACd;CACD,OAAO,EACN,WAAW,qBACX;CACD,UAAU,EACT,cAAc,uBACd;AACD;AAED,eAAsB,aACrBC,SACwB;CACxB,MAAM,EACL,YACA,WACA,QACA,WACA,UACA,OACA,YACA,gBACA,GAAG;AAGJ,OAAM,4BAAM,WAAW,EAAE,WAAW,KAAM,EAAC;CAE3C,MAAM,YAAY,oBAAK,WAAW,aAAa;CAG/C,MAAM,OAAO;EACZ;EACA;EACA;EACA;EACA;EACA;EACA;GACC,YAAY,UAAU;EACvB;GACC,cAAc,sBAAsB;CACrC;AAED,KAAI,OACH,MAAK,KAAK,WAAW;AAGtB,KAAI,UACH,MAAK,KAAK,cAAc;AAIzB,MAAK,MAAM,OAAO,SACjB,MAAK,MAAM,aAAa,IAAI,EAAE;CAI/B,IAAIC;AAEJ,KAAI,OAAO;EACV,MAAM,EACL,kBACA,qBACA,8BACA,kBACA,mBACA,GAAG,2CAAM;EACV,MAAM,EAAE,gBAAgB,uBAAuB,GAAG,2CAAM;EAIxD,IAAI,UAAU,MAAM,iBAAiB,MAAM;AAE3C,OAAK,SAAS;AAEb,WAAQ,KAAK,oCAAoC,MAAM,MAAM;AAC7D,aAAU,iBAAiB,MAAM;AACjC,SAAM,kBAAkB,QAAQ;AAChC,WAAQ,KAAK,2BAA2B,MAAM,OAAO;EACrD;AAGD,MAAI,gBACH;QAAK,MAAM,CAAC,SAAS,QAAQ,IAAI,OAAO,QAAQ,eAAe,CAC9D,KAAI,WAAW,wBAAwB,SACtC,MAAK,MAAM,CAAC,QAAQ,aAAa,IAAI,OAAO,QAC3C,wBAAwB,SACxB,EAAE;IAEF,MAAM,SAAS;AACf,SAAK,QAAQ,KAAK,YAAY,QAAQ,OAAO,SAAS;AACrD,aAAQ,KAAK,UAAU;AACvB,aAAQ,KAAK,mBAAmB,OAAO,sBAAsB;IAC7D;GACD;EAEF;AAIF,MAAI,cAAc,WAAW,SAAS,GAAG;AACxC,WAAQ,IAAI,mDAAmD;GAC/D,MAAM,eAAe,MAAM,uBAAuB,WAAW;AAE7D,OAAI,aAAa,SAAS,GAAG;IAC5B,MAAM,aAAa,6BAA6B,cAAc,QAAQ;AAEtE,SAAK,WAAW,OAAO;KACtB,MAAM,eAAe;OACnB,2CAA2C,MAAM;MAClD;MACA,GAAG,WAAW,QAAQ,IAAI,CAAC,OAAO,MAAM,EAAE,EAAE;MAC5C;MACA;OACC,iDAAiD,MAAM;OACvD,6CAA6C,MAAM;MACpD;OACC;OACA,+CAA+C,MAAM;MACtD;MACA;MACA,GAAG,WAAW,SAAS,IAAI,CAAC,MAC3B,WAAW,QAAQ,SAAS,EAAE,IAAI,MAAM,EAAE,KAAK,MAAM,EAAE,EACvD;KACD,EAAC,KAAK,KAAK;AAEZ,WAAM,IAAI,MAAM;IAChB;AAED,YAAQ,KACN,UAAU,aAAa,OAAO,uCAC/B;GACD;EACD;EAGD,MAAM,aAAa,oBAAoB,QAAQ;EAC/C,MAAM,YAAY,eAAe,WAAW;AAC5C,cAAY,UAAU;EAGtB,MAAM,UAAU,sBAAsB,UAAU;AAChD,OAAK,MAAM,CAAC,KAAK,MAAM,IAAI,OAAO,QAAQ,QAAQ,CACjD,MAAK,MAAM,WAAW,IAAI,GAAG,KAAK,UAAU,MAAM,CAAC,EAAE;AAGtD,UAAQ,KAAK,iCAAiC,MAAM,GAAG;CACvD;AAED,KAAI;EAEH,MAAM,CAAC,KAAK,GAAG,QAAQ,GAAG;EAC1B,MAAM,SAAS,kCAAU,KAAK,SAAS;GACtC,KAAK,QAAQ,KAAK;GAClB,OAAO;GACP,OAAO,QAAQ,aAAa;EAC5B,EAAC;AAEF,MAAI,OAAO,MACV,OAAM,OAAO;AAEd,MAAI,OAAO,WAAW,EACrB,OAAM,IAAI,OAAO,2BAA2B,OAAO,OAAO;EAI3D,MAAM,EAAE,UAAU,GAAG,MAAM,OAAO;EAClC,MAAM,UAAU,MAAM,SAAS,WAAW,QAAQ;AAClD,OAAK,QAAQ,WAAW,KAAK,CAC5B,OAAM,gCAAU,YAAY,uBAAuB,QAAQ,EAAE;CAE9D,SAAQ,OAAO;AACf,QAAM,IAAI,OACR,2BAA2B,iBAAiB,QAAQ,MAAM,UAAU,gBAAgB;CAEtF;AAED,QAAO;EACN,YAAY;EACZ;CACA;AACD"}
package/dist/config.d.mts CHANGED
@@ -1,5 +1,5 @@
1
- import { GkmConfig } from "./types-OszPdw9m.mjs";
2
- import { LoadedConfig, NormalizedAppConfig, NormalizedWorkspace, WorkspaceConfig, defineWorkspace } from "./index-BBvGMG_A.mjs";
1
+ import { GkmConfig } from "./types-CqfhdmRi.mjs";
2
+ import { LoadedConfig, NormalizedAppConfig, NormalizedWorkspace, WorkspaceConfig, defineWorkspace } from "./index-Dx9Kk5bR.mjs";
3
3
 
4
4
  //#region src/config.d.ts
5
5
 
@@ -0,0 +1,3 @@
1
+ const require_dokploy_api = require('./dokploy-api-CqOIKHme.cjs');
2
+
3
+ exports.DokployApi = require_dokploy_api.DokployApi;
@@ -0,0 +1,3 @@
1
+ import { DokployApi, DokployApiError } from "./dokploy-api-sfZFlWuE.mjs";
2
+
3
+ export { DokployApi };
@@ -606,4 +606,4 @@ Object.defineProperty(exports, 'DokployApiError', {
606
606
  return DokployApiError;
607
607
  }
608
608
  });
609
- //# sourceMappingURL=dokploy-api-Dvyq-LsM.cjs.map
609
+ //# sourceMappingURL=dokploy-api-CqOIKHme.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"dokploy-api-Dvyq-LsM.cjs","names":["message: string","status: number","statusText: string","issues?: Array<{ message: string }>","options: DokployApiOptions","endpoint: string","body?: Record<string, unknown>","method: 'GET' | 'POST' | 'PUT' | 'DELETE'","issues: Array<{ message: string }> | undefined","projectId: string","name: string","description?: string","environmentId: string","applicationId: string","updates: Partial<DokployApplicationUpdate>","env: string","dockerImage: string","options?: {\n\t\t\t/** Registry ID in Dokploy (for pre-configured registries) */\n\t\t\tregistryId?: string;\n\t\t\t/** Registry username (for direct auth) */\n\t\t\tusername?: string;\n\t\t\t/** Registry password (for direct auth) */\n\t\t\tpassword?: string;\n\t\t\t/** Registry URL (for direct auth, e.g., ghcr.io) */\n\t\t\tregistryUrl?: string;\n\t\t}","registryName: string","registryUrl: string","username: string","password: string","options?: {\n\t\t\timagePrefix?: string;\n\t\t}","registryId: string","updates: Partial<{\n\t\t\tregistryName: string;\n\t\t\tregistryUrl: string;\n\t\t\tusername: string;\n\t\t\tpassword: string;\n\t\t\timagePrefix: string;\n\t\t}>","options?: {\n\t\t\tappName?: string;\n\t\t\tdatabaseName?: string;\n\t\t\tdatabaseUser?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t}","options?: {\n\t\t\tdatabaseName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t}","postgresId: string","externalPort: number | null","updates: Partial<DokployPostgresUpdate>","options?: {\n\t\t\tappName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t}","options?: {\n\t\t\tdatabasePassword?: string;\n\t\t}","redisId: string","updates: Partial<DokployRedisUpdate>","options: DokployDomainCreate","domainId: string","updates: Partial<DokployDomainCreate>","domain: string","appName: string","serverId?: string","destinationId: string","options: DokployDestinationCreate","options: Omit<DokployDestinationCreate, 'name'>","updates: Partial<DokployDestinationCreate>","options: DokployBackupCreate","backupId: string","updates: Partial<DokployBackupUpdate>"],"sources":["../src/deploy/dokploy-api.ts"],"sourcesContent":["/**\n * Centralized Dokploy API client\n *\n * Handles authentication, error handling, and provides typed methods for all Dokploy API endpoints.\n */\n\nexport interface DokployApiOptions {\n\t/** Dokploy server URL (e.g., https://dokploy.example.com) */\n\tbaseUrl: string;\n\t/** API token for authentication */\n\ttoken: string;\n}\n\nexport interface DokployErrorResponse {\n\tmessage?: string;\n\tissues?: Array<{ message: string }>;\n}\n\nexport class DokployApiError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t\tpublic statusText: string,\n\t\tpublic issues?: Array<{ message: string }>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'DokployApiError';\n\t}\n}\n\n/**\n * Dokploy API client\n */\nexport class DokployApi {\n\tprivate baseUrl: string;\n\tprivate token: string;\n\n\tconstructor(options: DokployApiOptions) {\n\t\tthis.baseUrl = options.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n\t\tthis.token = options.token;\n\t}\n\n\t/**\n\t * Make a GET request to the Dokploy API\n\t */\n\tasync get<T>(endpoint: string): Promise<T> {\n\t\treturn this.request<T>('GET', endpoint);\n\t}\n\n\t/**\n\t * Make a POST request to the Dokploy API\n\t */\n\tasync post<T>(endpoint: string, body?: Record<string, unknown>): Promise<T> {\n\t\treturn this.request<T>('POST', endpoint, body);\n\t}\n\n\t/**\n\t * Make a request to the Dokploy API\n\t */\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE',\n\t\tendpoint: string,\n\t\tbody?: Record<string, unknown>,\n\t): Promise<T> {\n\t\tconst url = `${this.baseUrl}/api/${endpoint}`;\n\n\t\tconst response = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t'x-api-key': this.token,\n\t\t\t},\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tlet errorMessage = `Dokploy API error: ${response.status} ${response.statusText}`;\n\t\t\tlet issues: Array<{ message: string }> | undefined;\n\n\t\t\ttry {\n\t\t\t\tconst errorBody = (await response.json()) as DokployErrorResponse;\n\t\t\t\tif (errorBody.message) {\n\t\t\t\t\terrorMessage = `Dokploy API error: ${errorBody.message}`;\n\t\t\t\t}\n\t\t\t\tif (errorBody.issues?.length) {\n\t\t\t\t\tissues = errorBody.issues;\n\t\t\t\t\terrorMessage += `\\n Issues: ${errorBody.issues.map((i) => i.message).join(', ')}`;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore JSON parse errors\n\t\t\t}\n\n\t\t\tthrow new DokployApiError(\n\t\t\t\terrorMessage,\n\t\t\t\tresponse.status,\n\t\t\t\tresponse.statusText,\n\t\t\t\tissues,\n\t\t\t);\n\t\t}\n\n\t\t// Handle empty responses (204 No Content or empty body)\n\t\tconst text = await response.text();\n\t\tif (!text || text.trim() === '') {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn JSON.parse(text) as T;\n\t}\n\n\t/**\n\t * Validate the API token by making a test request\n\t */\n\tasync validateToken(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.get('project.all');\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ============================================\n\t// Project endpoints\n\t// ============================================\n\n\t/**\n\t * List all projects\n\t */\n\tasync listProjects(): Promise<DokployProject[]> {\n\t\treturn this.get<DokployProject[]>('project.all');\n\t}\n\n\t/**\n\t * Get a single project by ID\n\t */\n\tasync getProject(projectId: string): Promise<DokployProjectDetails> {\n\t\treturn this.get<DokployProjectDetails>(\n\t\t\t`project.one?projectId=${projectId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new project\n\t */\n\tasync createProject(\n\t\tname: string,\n\t\tdescription?: string,\n\t): Promise<{ project: DokployProject; environment: DokployEnvironment }> {\n\t\treturn this.post<{\n\t\t\tproject: DokployProject;\n\t\t\tenvironment: DokployEnvironment;\n\t\t}>('project.create', {\n\t\t\tname,\n\t\t\tdescription: description ?? `Created by gkm CLI`,\n\t\t});\n\t}\n\n\t/**\n\t * Delete a project and all its resources\n\t */\n\tasync deleteProject(projectId: string): Promise<void> {\n\t\tawait this.post('project.remove', { projectId });\n\t}\n\n\t// ============================================\n\t// Environment endpoints\n\t// ============================================\n\n\t/**\n\t * Create an environment in a project\n\t */\n\tasync createEnvironment(\n\t\tprojectId: string,\n\t\tname: string,\n\t\tdescription?: string,\n\t): Promise<DokployEnvironment> {\n\t\treturn this.post<DokployEnvironment>('environment.create', {\n\t\t\tprojectId,\n\t\t\tname,\n\t\t\tdescription: description ?? `${name} environment`,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Application endpoints\n\t// ============================================\n\n\t/**\n\t * List all applications in a project\n\t */\n\tasync listApplications(projectId: string): Promise<DokployApplication[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployApplication[]>(\n\t\t\t\t`application.all?projectId=${projectId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find an application by name in a project\n\t */\n\tasync findApplicationByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployApplication | undefined> {\n\t\tconst applications = await this.listApplications(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn applications.find(\n\t\t\t(app) => app.name === name || app.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new application\n\t */\n\tasync createApplication(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t): Promise<DokployApplication> {\n\t\treturn this.post<DokployApplication>('application.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t});\n\t}\n\n\t/**\n\t * Find or create an application by name\n\t */\n\tasync findOrCreateApplication(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t): Promise<{ application: DokployApplication; created: boolean }> {\n\t\tconst existing = await this.findApplicationByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { application: existing, created: false };\n\t\t}\n\t\tconst application = await this.createApplication(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t);\n\t\treturn { application, created: true };\n\t}\n\n\t/**\n\t * Get an application by ID\n\t */\n\tasync getApplication(\n\t\tapplicationId: string,\n\t): Promise<DokployApplication | null> {\n\t\ttry {\n\t\t\treturn await this.get<DokployApplication>(\n\t\t\t\t`application.one?applicationId=${applicationId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Application not found\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Update an application\n\t */\n\tasync updateApplication(\n\t\tapplicationId: string,\n\t\tupdates: Partial<DokployApplicationUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('application.update', {\n\t\t\tapplicationId,\n\t\t\t...updates,\n\t\t});\n\t}\n\n\t/**\n\t * Save environment variables for an application\n\t */\n\tasync saveApplicationEnv(applicationId: string, env: string): Promise<void> {\n\t\tawait this.post('application.saveEnvironment', {\n\t\t\tapplicationId,\n\t\t\tenv,\n\t\t});\n\t}\n\n\t/**\n\t * Configure application to use Docker provider (pull from registry)\n\t *\n\t * For private registries, either:\n\t * - Use `registryId` if the registry is configured in Dokploy\n\t * - Or provide `username`, `password`, and `registryUrl` directly\n\t */\n\tasync saveDockerProvider(\n\t\tapplicationId: string,\n\t\tdockerImage: string,\n\t\toptions?: {\n\t\t\t/** Registry ID in Dokploy (for pre-configured registries) */\n\t\t\tregistryId?: string;\n\t\t\t/** Registry username (for direct auth) */\n\t\t\tusername?: string;\n\t\t\t/** Registry password (for direct auth) */\n\t\t\tpassword?: string;\n\t\t\t/** Registry URL (for direct auth, e.g., ghcr.io) */\n\t\t\tregistryUrl?: string;\n\t\t},\n\t): Promise<void> {\n\t\tawait this.post('application.saveDockerProvider', {\n\t\t\tapplicationId,\n\t\t\tdockerImage,\n\t\t\t...options,\n\t\t});\n\t}\n\n\t/**\n\t * Deploy an application\n\t */\n\tasync deployApplication(applicationId: string): Promise<void> {\n\t\tawait this.post('application.deploy', { applicationId });\n\t}\n\n\t/**\n\t * Delete an application\n\t */\n\tasync deleteApplication(applicationId: string): Promise<void> {\n\t\tawait this.post('application.remove', { applicationId });\n\t}\n\n\t// ============================================\n\t// Registry endpoints\n\t// ============================================\n\n\t/**\n\t * List all registries\n\t */\n\tasync listRegistries(): Promise<DokployRegistry[]> {\n\t\treturn this.get<DokployRegistry[]>('registry.all');\n\t}\n\n\t/**\n\t * Create a new registry\n\t */\n\tasync createRegistry(\n\t\tregistryName: string,\n\t\tregistryUrl: string,\n\t\tusername: string,\n\t\tpassword: string,\n\t\toptions?: {\n\t\t\timagePrefix?: string;\n\t\t},\n\t): Promise<DokployRegistry> {\n\t\treturn this.post<DokployRegistry>('registry.create', {\n\t\t\tregistryName,\n\t\t\tregistryUrl,\n\t\t\tusername,\n\t\t\tpassword,\n\t\t\timagePrefix: options?.imagePrefix,\n\t\t});\n\t}\n\n\t/**\n\t * Get a registry by ID\n\t */\n\tasync getRegistry(registryId: string): Promise<DokployRegistry> {\n\t\treturn this.get<DokployRegistry>(`registry.one?registryId=${registryId}`);\n\t}\n\n\t/**\n\t * Update a registry\n\t */\n\tasync updateRegistry(\n\t\tregistryId: string,\n\t\tupdates: Partial<{\n\t\t\tregistryName: string;\n\t\t\tregistryUrl: string;\n\t\t\tusername: string;\n\t\t\tpassword: string;\n\t\t\timagePrefix: string;\n\t\t}>,\n\t): Promise<void> {\n\t\tawait this.post('registry.update', { registryId, ...updates });\n\t}\n\n\t/**\n\t * Delete a registry\n\t */\n\tasync deleteRegistry(registryId: string): Promise<void> {\n\t\tawait this.post('registry.remove', { registryId });\n\t}\n\n\t// ============================================\n\t// Postgres endpoints\n\t// ============================================\n\n\t/**\n\t * List all Postgres databases in a project\n\t */\n\tasync listPostgres(projectId: string): Promise<DokployPostgres[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployPostgres[]>(\n\t\t\t\t`postgres.all?projectId=${projectId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find a Postgres database by name in a project\n\t */\n\tasync findPostgresByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployPostgres | undefined> {\n\t\tconst databases = await this.listPostgres(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn databases.find(\n\t\t\t(db) => db.name === name || db.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new Postgres database\n\t */\n\tasync createPostgres(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tappName?: string;\n\t\t\tdatabaseName?: string;\n\t\t\tdatabaseUser?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<DokployPostgres> {\n\t\treturn this.post<DokployPostgres>('postgres.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName:\n\t\t\t\toptions?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t\tdatabaseName: options?.databaseName,\n\t\t\tdatabaseUser: options?.databaseUser ?? 'postgres',\n\t\t\tdatabasePassword: options?.databasePassword,\n\t\t\tdockerImage: options?.dockerImage ?? 'postgres:18',\n\t\t\tdescription: options?.description ?? `Postgres database for ${name}`,\n\t\t});\n\t}\n\n\t/**\n\t * Find or create a Postgres database by name\n\t */\n\tasync findOrCreatePostgres(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tdatabaseName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t},\n\t): Promise<{ postgres: DokployPostgres; created: boolean }> {\n\t\tconst existing = await this.findPostgresByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { postgres: existing, created: false };\n\t\t}\n\t\tconst postgres = await this.createPostgres(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\toptions,\n\t\t);\n\t\treturn { postgres, created: true };\n\t}\n\n\t/**\n\t * Get a Postgres database by ID\n\t */\n\tasync getPostgres(postgresId: string): Promise<DokployPostgres> {\n\t\treturn this.get<DokployPostgres>(`postgres.one?postgresId=${postgresId}`);\n\t}\n\n\t/**\n\t * Deploy a Postgres database\n\t */\n\tasync deployPostgres(postgresId: string): Promise<void> {\n\t\tawait this.post('postgres.deploy', { postgresId });\n\t}\n\n\t/**\n\t * Save environment variables for Postgres\n\t */\n\tasync savePostgresEnv(postgresId: string, env: string): Promise<void> {\n\t\tawait this.post('postgres.saveEnvironment', { postgresId, env });\n\t}\n\n\t/**\n\t * Set external port for Postgres (for external access)\n\t */\n\tasync savePostgresExternalPort(\n\t\tpostgresId: string,\n\t\texternalPort: number | null,\n\t): Promise<void> {\n\t\tawait this.post('postgres.saveExternalPort', { postgresId, externalPort });\n\t}\n\n\t/**\n\t * Update Postgres configuration\n\t */\n\tasync updatePostgres(\n\t\tpostgresId: string,\n\t\tupdates: Partial<DokployPostgresUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('postgres.update', { postgresId, ...updates });\n\t}\n\n\t/**\n\t * Delete a Postgres database\n\t */\n\tasync deletePostgres(postgresId: string): Promise<void> {\n\t\tawait this.post('postgres.remove', { postgresId });\n\t}\n\n\t// ============================================\n\t// Redis endpoints\n\t// ============================================\n\n\t/**\n\t * List all Redis instances in a project\n\t */\n\tasync listRedis(projectId: string): Promise<DokployRedis[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployRedis[]>(`redis.all?projectId=${projectId}`);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find a Redis instance by name in a project\n\t */\n\tasync findRedisByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployRedis | undefined> {\n\t\tconst instances = await this.listRedis(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn instances.find(\n\t\t\t(redis) => redis.name === name || redis.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new Redis instance\n\t */\n\tasync createRedis(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tappName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<DokployRedis> {\n\t\treturn this.post<DokployRedis>('redis.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName:\n\t\t\t\toptions?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t\tdatabasePassword: options?.databasePassword,\n\t\t\tdockerImage: options?.dockerImage ?? 'redis:8',\n\t\t\tdescription: options?.description ?? `Redis instance for ${name}`,\n\t\t});\n\t}\n\n\t/**\n\t * Find or create a Redis instance by name\n\t */\n\tasync findOrCreateRedis(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tdatabasePassword?: string;\n\t\t},\n\t): Promise<{ redis: DokployRedis; created: boolean }> {\n\t\tconst existing = await this.findRedisByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { redis: existing, created: false };\n\t\t}\n\t\tconst redis = await this.createRedis(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\toptions,\n\t\t);\n\t\treturn { redis, created: true };\n\t}\n\n\t/**\n\t * Get a Redis instance by ID\n\t */\n\tasync getRedis(redisId: string): Promise<DokployRedis> {\n\t\treturn this.get<DokployRedis>(`redis.one?redisId=${redisId}`);\n\t}\n\n\t/**\n\t * Deploy a Redis instance\n\t */\n\tasync deployRedis(redisId: string): Promise<void> {\n\t\tawait this.post('redis.deploy', { redisId });\n\t}\n\n\t/**\n\t * Save environment variables for Redis\n\t */\n\tasync saveRedisEnv(redisId: string, env: string): Promise<void> {\n\t\tawait this.post('redis.saveEnvironment', { redisId, env });\n\t}\n\n\t/**\n\t * Set external port for Redis (for external access)\n\t */\n\tasync saveRedisExternalPort(\n\t\tredisId: string,\n\t\texternalPort: number | null,\n\t): Promise<void> {\n\t\tawait this.post('redis.saveExternalPort', { redisId, externalPort });\n\t}\n\n\t/**\n\t * Update Redis configuration\n\t */\n\tasync updateRedis(\n\t\tredisId: string,\n\t\tupdates: Partial<DokployRedisUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('redis.update', { redisId, ...updates });\n\t}\n\n\t/**\n\t * Delete a Redis instance\n\t */\n\tasync deleteRedis(redisId: string): Promise<void> {\n\t\tawait this.post('redis.remove', { redisId });\n\t}\n\n\t// ============================================\n\t// Domain endpoints\n\t// ============================================\n\n\t/**\n\t * Create a new domain for an application\n\t */\n\tasync createDomain(options: DokployDomainCreate): Promise<DokployDomain> {\n\t\treturn this.post<DokployDomain>(\n\t\t\t'domain.create',\n\t\t\toptions as unknown as Record<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * Update an existing domain\n\t */\n\tasync updateDomain(\n\t\tdomainId: string,\n\t\tupdates: Partial<DokployDomainCreate>,\n\t): Promise<void> {\n\t\tawait this.post('domain.update', { domainId, ...updates });\n\t}\n\n\t/**\n\t * Delete a domain\n\t */\n\tasync deleteDomain(domainId: string): Promise<void> {\n\t\tawait this.post('domain.delete', { domainId });\n\t}\n\n\t/**\n\t * Get a domain by ID\n\t */\n\tasync getDomain(domainId: string): Promise<DokployDomain> {\n\t\treturn this.get<DokployDomain>(`domain.one?domainId=${domainId}`);\n\t}\n\n\t/**\n\t * Get all domains for an application\n\t */\n\tasync getDomainsByApplicationId(\n\t\tapplicationId: string,\n\t): Promise<DokployDomain[]> {\n\t\treturn this.get<DokployDomain[]>(\n\t\t\t`domain.byApplicationId?applicationId=${applicationId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Validate a domain and trigger SSL certificate generation\n\t *\n\t * This should be called after DNS records are created and propagated.\n\t * It triggers Let's Encrypt certificate generation for HTTPS domains.\n\t *\n\t * @param domain - The domain hostname to validate (e.g., 'api.example.com')\n\t */\n\tasync validateDomain(\n\t\tdomain: string,\n\t): Promise<{ isValid: boolean; resolvedIp: string }> {\n\t\treturn this.post<{ isValid: boolean; resolvedIp: string }>(\n\t\t\t'domain.validateDomain',\n\t\t\t{ domain },\n\t\t);\n\t}\n\n\t/**\n\t * Auto-generate a domain name for an application\n\t */\n\tasync generateDomain(\n\t\tappName: string,\n\t\tserverId?: string,\n\t): Promise<{ domain: string }> {\n\t\treturn this.post<{ domain: string }>('domain.generateDomain', {\n\t\t\tappName,\n\t\t\tserverId,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Destination endpoints (backup storage)\n\t// ============================================\n\n\t/**\n\t * List all backup destinations\n\t */\n\tasync listDestinations(): Promise<DokployDestination[]> {\n\t\treturn this.get<DokployDestination[]>('destination.all');\n\t}\n\n\t/**\n\t * Get a destination by ID\n\t */\n\tasync getDestination(destinationId: string): Promise<DokployDestination> {\n\t\treturn this.get<DokployDestination>(\n\t\t\t`destination.one?destinationId=${destinationId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new S3 backup destination\n\t */\n\tasync createDestination(\n\t\toptions: DokployDestinationCreate,\n\t): Promise<DokployDestination> {\n\t\treturn this.post<DokployDestination>('destination.create', { ...options });\n\t}\n\n\t/**\n\t * Find a destination by name\n\t */\n\tasync findDestinationByName(\n\t\tname: string,\n\t): Promise<DokployDestination | undefined> {\n\t\tconst destinations = await this.listDestinations();\n\t\treturn destinations.find((d) => d.name === name);\n\t}\n\n\t/**\n\t * Find or create a destination by name\n\t */\n\tasync findOrCreateDestination(\n\t\tname: string,\n\t\toptions: Omit<DokployDestinationCreate, 'name'>,\n\t): Promise<{ destination: DokployDestination; created: boolean }> {\n\t\tconst existing = await this.findDestinationByName(name);\n\t\tif (existing) {\n\t\t\treturn { destination: existing, created: false };\n\t\t}\n\t\tconst destination = await this.createDestination({ name, ...options });\n\t\treturn { destination, created: true };\n\t}\n\n\t/**\n\t * Update a destination\n\t */\n\tasync updateDestination(\n\t\tdestinationId: string,\n\t\tupdates: Partial<DokployDestinationCreate>,\n\t): Promise<void> {\n\t\tawait this.post('destination.update', { destinationId, ...updates });\n\t}\n\n\t/**\n\t * Delete a destination\n\t */\n\tasync deleteDestination(destinationId: string): Promise<void> {\n\t\tawait this.post('destination.remove', { destinationId });\n\t}\n\n\t/**\n\t * Test connection to a destination\n\t */\n\tasync testDestinationConnection(\n\t\tdestinationId: string,\n\t): Promise<{ success: boolean }> {\n\t\treturn this.post<{ success: boolean }>('destination.testConnection', {\n\t\t\tdestinationId,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Backup endpoints (scheduled backups)\n\t// ============================================\n\n\t/**\n\t * Create a backup schedule for postgres\n\t */\n\tasync createPostgresBackup(\n\t\toptions: DokployBackupCreate,\n\t): Promise<DokployBackup> {\n\t\treturn this.post<DokployBackup>('backup.create', {\n\t\t\t...options,\n\t\t\tdatabaseType: 'postgres',\n\t\t});\n\t}\n\n\t/**\n\t * List backups for a postgres database\n\t */\n\tasync listPostgresBackups(postgresId: string): Promise<DokployBackup[]> {\n\t\treturn this.get<DokployBackup[]>(\n\t\t\t`backup.all?postgresId=${postgresId}&databaseType=postgres`,\n\t\t);\n\t}\n\n\t/**\n\t * Get a backup by ID\n\t */\n\tasync getBackup(backupId: string): Promise<DokployBackup> {\n\t\treturn this.get<DokployBackup>(`backup.one?backupId=${backupId}`);\n\t}\n\n\t/**\n\t * Update a backup schedule\n\t */\n\tasync updateBackup(\n\t\tbackupId: string,\n\t\tupdates: Partial<DokployBackupUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('backup.update', { backupId, ...updates });\n\t}\n\n\t/**\n\t * Delete a backup schedule\n\t */\n\tasync deleteBackup(backupId: string): Promise<void> {\n\t\tawait this.post('backup.remove', { backupId });\n\t}\n\n\t/**\n\t * Trigger a manual backup run\n\t */\n\tasync runBackupManually(backupId: string): Promise<void> {\n\t\tawait this.post('backup.manualBackup', { backupId });\n\t}\n}\n\n// ============================================\n// Type definitions for Dokploy API responses\n// ============================================\n\nexport interface DokployProject {\n\tprojectId: string;\n\tname: string;\n\tdescription: string | null;\n\tcreatedAt?: string;\n\tadminId?: string;\n}\n\nexport interface DokployEnvironment {\n\tenvironmentId: string;\n\tname: string;\n\tdescription: string | null;\n}\n\nexport interface DokployProjectDetails extends DokployProject {\n\tenvironments: DokployEnvironment[];\n}\n\nexport interface DokployApplication {\n\tapplicationId: string;\n\tname: string;\n\tappName: string;\n\tprojectId: string;\n\tenvironmentId?: string;\n}\n\nexport interface DokployApplicationUpdate {\n\tregistryId: string;\n\tdockerImage: string;\n\tsourceType: 'docker';\n}\n\nexport interface DokployRegistry {\n\tregistryId: string;\n\tregistryName: string;\n\tregistryUrl: string;\n\tusername: string;\n\timagePrefix: string | null;\n}\n\nexport interface DokployPostgres {\n\tpostgresId: string;\n\tname: string;\n\tappName: string;\n\tdatabaseName: string;\n\tdatabaseUser: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string | null;\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplicationStatus: 'idle' | 'running' | 'done' | 'error';\n\texternalPort: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployPostgresUpdate {\n\tname: string;\n\tappName: string;\n\tdatabaseName: string;\n\tdatabaseUser: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string;\n}\n\nexport interface DokployRedis {\n\tredisId: string;\n\tname: string;\n\tappName: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string | null;\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplicationStatus: 'idle' | 'running' | 'done' | 'error';\n\texternalPort: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployRedisUpdate {\n\tname: string;\n\tappName: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string;\n}\n\nexport type DokployCertificateType = 'letsencrypt' | 'none' | 'custom';\nexport type DokployDomainType = 'application' | 'compose' | 'preview';\n\nexport interface DokployDomainCreate {\n\t/** Domain hostname (e.g., 'api.example.com') */\n\thost: string;\n\t/** URL path (optional, e.g., '/api') */\n\tpath?: string | null;\n\t/** Container port to route to (1-65535) */\n\tport?: number | null;\n\t/** Enable HTTPS */\n\thttps?: boolean;\n\t/** Associated application ID */\n\tapplicationId?: string | null;\n\t/** Certificate type for HTTPS */\n\tcertificateType?: DokployCertificateType;\n\t/** Custom certificate resolver name */\n\tcustomCertResolver?: string | null;\n\t/** Docker Compose service ID */\n\tcomposeId?: string | null;\n\t/** Service name for compose */\n\tserviceName?: string | null;\n\t/** Domain type */\n\tdomainType?: DokployDomainType | null;\n\t/** Preview deployment ID */\n\tpreviewDeploymentId?: string | null;\n\t/** Internal routing path */\n\tinternalPath?: string | null;\n\t/** Strip path from forwarded requests */\n\tstripPath?: boolean;\n}\n\nexport interface DokployDomain extends DokployDomainCreate {\n\tdomainId: string;\n\tcreatedAt?: string;\n}\n\n// ============================================\n// Destination types (backup storage)\n// ============================================\n\nexport interface DokployDestination {\n\tdestinationId: string;\n\tname: string;\n\taccessKey: string;\n\tbucket: string;\n\tregion: string;\n\tendpoint: string | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployDestinationCreate {\n\t/** User-friendly name for the destination */\n\tname: string;\n\t/** S3 access key ID */\n\taccessKey: string;\n\t/** S3 secret access key */\n\tsecretAccessKey: string;\n\t/** S3 bucket name */\n\tbucket: string;\n\t/** AWS region (e.g., 'us-east-1') */\n\tregion: string;\n\t/** Optional endpoint for S3-compatible services */\n\tendpoint?: string;\n}\n\n// ============================================\n// Backup types (scheduled backups)\n// ============================================\n\nexport type DokployDatabaseType = 'postgres' | 'mysql' | 'mariadb' | 'mongo';\n\nexport interface DokployBackup {\n\tbackupId: string;\n\tschedule: string;\n\tprefix: string;\n\tenabled: boolean;\n\tdestinationId: string;\n\tpostgresId?: string;\n\tdatabaseType: DokployDatabaseType;\n\tkeepLatestCount: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployBackupCreate {\n\t/** Cron schedule (e.g., '0 2 * * *' for 2 AM daily) */\n\tschedule: string;\n\t/** Backup file prefix (e.g., 'production/postgres') */\n\tprefix: string;\n\t/** Destination ID for backup storage */\n\tdestinationId: string;\n\t/** Database name to backup */\n\tdatabase: string;\n\t/** Postgres instance ID */\n\tpostgresId: string;\n\t/** Enable/disable backup (default: true) */\n\tenabled?: boolean;\n\t/** Number of backups to keep (default: keep all) */\n\tkeepLatestCount?: number;\n}\n\nexport interface DokployBackupUpdate {\n\tschedule?: string;\n\tprefix?: string;\n\tenabled?: boolean;\n\tkeepLatestCount?: number;\n}\n\n/**\n * Create a Dokploy API client from stored credentials or environment\n */\nexport async function createDokployApi(\n\tendpoint?: string,\n): Promise<DokployApi | null> {\n\tconst { getDokployCredentials } = await import('../auth/credentials');\n\n\t// Try environment variable first\n\tconst envToken = process.env.DOKPLOY_API_TOKEN;\n\tconst envEndpoint = endpoint || process.env.DOKPLOY_ENDPOINT;\n\n\tif (envToken && envEndpoint) {\n\t\treturn new DokployApi({ baseUrl: envEndpoint, token: envToken });\n\t}\n\n\t// Fall back to stored credentials\n\tconst creds = await getDokployCredentials();\n\tif (creds) {\n\t\treturn new DokployApi({\n\t\t\tbaseUrl: endpoint || creds.endpoint,\n\t\t\ttoken: creds.token,\n\t\t});\n\t}\n\n\treturn null;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"dokploy-api-CqOIKHme.cjs","names":["message: string","status: number","statusText: string","issues?: Array<{ message: string }>","options: DokployApiOptions","endpoint: string","body?: Record<string, unknown>","method: 'GET' | 'POST' | 'PUT' | 'DELETE'","issues: Array<{ message: string }> | undefined","projectId: string","name: string","description?: string","environmentId: string","applicationId: string","updates: Partial<DokployApplicationUpdate>","env: string","dockerImage: string","options?: {\n\t\t\t/** Registry ID in Dokploy (for pre-configured registries) */\n\t\t\tregistryId?: string;\n\t\t\t/** Registry username (for direct auth) */\n\t\t\tusername?: string;\n\t\t\t/** Registry password (for direct auth) */\n\t\t\tpassword?: string;\n\t\t\t/** Registry URL (for direct auth, e.g., ghcr.io) */\n\t\t\tregistryUrl?: string;\n\t\t}","registryName: string","registryUrl: string","username: string","password: string","options?: {\n\t\t\timagePrefix?: string;\n\t\t}","registryId: string","updates: Partial<{\n\t\t\tregistryName: string;\n\t\t\tregistryUrl: string;\n\t\t\tusername: string;\n\t\t\tpassword: string;\n\t\t\timagePrefix: string;\n\t\t}>","options?: {\n\t\t\tappName?: string;\n\t\t\tdatabaseName?: string;\n\t\t\tdatabaseUser?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t}","options?: {\n\t\t\tdatabaseName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t}","postgresId: string","externalPort: number | null","updates: Partial<DokployPostgresUpdate>","options?: {\n\t\t\tappName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t}","options?: {\n\t\t\tdatabasePassword?: string;\n\t\t}","redisId: string","updates: Partial<DokployRedisUpdate>","options: DokployDomainCreate","domainId: string","updates: Partial<DokployDomainCreate>","domain: string","appName: string","serverId?: string","destinationId: string","options: DokployDestinationCreate","options: Omit<DokployDestinationCreate, 'name'>","updates: Partial<DokployDestinationCreate>","options: DokployBackupCreate","backupId: string","updates: Partial<DokployBackupUpdate>"],"sources":["../src/deploy/dokploy-api.ts"],"sourcesContent":["/**\n * Centralized Dokploy API client\n *\n * Handles authentication, error handling, and provides typed methods for all Dokploy API endpoints.\n */\n\nexport interface DokployApiOptions {\n\t/** Dokploy server URL (e.g., https://dokploy.example.com) */\n\tbaseUrl: string;\n\t/** API token for authentication */\n\ttoken: string;\n}\n\nexport interface DokployErrorResponse {\n\tmessage?: string;\n\tissues?: Array<{ message: string }>;\n}\n\nexport class DokployApiError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t\tpublic statusText: string,\n\t\tpublic issues?: Array<{ message: string }>,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = 'DokployApiError';\n\t}\n}\n\n/**\n * Dokploy API client\n */\nexport class DokployApi {\n\tprivate baseUrl: string;\n\tprivate token: string;\n\n\tconstructor(options: DokployApiOptions) {\n\t\tthis.baseUrl = options.baseUrl.replace(/\\/$/, ''); // Remove trailing slash\n\t\tthis.token = options.token;\n\t}\n\n\t/**\n\t * Make a GET request to the Dokploy API\n\t */\n\tasync get<T>(endpoint: string): Promise<T> {\n\t\treturn this.request<T>('GET', endpoint);\n\t}\n\n\t/**\n\t * Make a POST request to the Dokploy API\n\t */\n\tasync post<T>(endpoint: string, body?: Record<string, unknown>): Promise<T> {\n\t\treturn this.request<T>('POST', endpoint, body);\n\t}\n\n\t/**\n\t * Make a request to the Dokploy API\n\t */\n\tprivate async request<T>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'DELETE',\n\t\tendpoint: string,\n\t\tbody?: Record<string, unknown>,\n\t): Promise<T> {\n\t\tconst url = `${this.baseUrl}/api/${endpoint}`;\n\n\t\tconst response = await fetch(url, {\n\t\t\tmethod,\n\t\t\theaders: {\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t'x-api-key': this.token,\n\t\t\t},\n\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t});\n\n\t\tif (!response.ok) {\n\t\t\tlet errorMessage = `Dokploy API error: ${response.status} ${response.statusText}`;\n\t\t\tlet issues: Array<{ message: string }> | undefined;\n\n\t\t\ttry {\n\t\t\t\tconst errorBody = (await response.json()) as DokployErrorResponse;\n\t\t\t\tif (errorBody.message) {\n\t\t\t\t\terrorMessage = `Dokploy API error: ${errorBody.message}`;\n\t\t\t\t}\n\t\t\t\tif (errorBody.issues?.length) {\n\t\t\t\t\tissues = errorBody.issues;\n\t\t\t\t\terrorMessage += `\\n Issues: ${errorBody.issues.map((i) => i.message).join(', ')}`;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore JSON parse errors\n\t\t\t}\n\n\t\t\tthrow new DokployApiError(\n\t\t\t\terrorMessage,\n\t\t\t\tresponse.status,\n\t\t\t\tresponse.statusText,\n\t\t\t\tissues,\n\t\t\t);\n\t\t}\n\n\t\t// Handle empty responses (204 No Content or empty body)\n\t\tconst text = await response.text();\n\t\tif (!text || text.trim() === '') {\n\t\t\treturn undefined as T;\n\t\t}\n\t\treturn JSON.parse(text) as T;\n\t}\n\n\t/**\n\t * Validate the API token by making a test request\n\t */\n\tasync validateToken(): Promise<boolean> {\n\t\ttry {\n\t\t\tawait this.get('project.all');\n\t\t\treturn true;\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t// ============================================\n\t// Project endpoints\n\t// ============================================\n\n\t/**\n\t * List all projects\n\t */\n\tasync listProjects(): Promise<DokployProject[]> {\n\t\treturn this.get<DokployProject[]>('project.all');\n\t}\n\n\t/**\n\t * Get a single project by ID\n\t */\n\tasync getProject(projectId: string): Promise<DokployProjectDetails> {\n\t\treturn this.get<DokployProjectDetails>(\n\t\t\t`project.one?projectId=${projectId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new project\n\t */\n\tasync createProject(\n\t\tname: string,\n\t\tdescription?: string,\n\t): Promise<{ project: DokployProject; environment: DokployEnvironment }> {\n\t\treturn this.post<{\n\t\t\tproject: DokployProject;\n\t\t\tenvironment: DokployEnvironment;\n\t\t}>('project.create', {\n\t\t\tname,\n\t\t\tdescription: description ?? `Created by gkm CLI`,\n\t\t});\n\t}\n\n\t/**\n\t * Delete a project and all its resources\n\t */\n\tasync deleteProject(projectId: string): Promise<void> {\n\t\tawait this.post('project.remove', { projectId });\n\t}\n\n\t// ============================================\n\t// Environment endpoints\n\t// ============================================\n\n\t/**\n\t * Create an environment in a project\n\t */\n\tasync createEnvironment(\n\t\tprojectId: string,\n\t\tname: string,\n\t\tdescription?: string,\n\t): Promise<DokployEnvironment> {\n\t\treturn this.post<DokployEnvironment>('environment.create', {\n\t\t\tprojectId,\n\t\t\tname,\n\t\t\tdescription: description ?? `${name} environment`,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Application endpoints\n\t// ============================================\n\n\t/**\n\t * List all applications in a project\n\t */\n\tasync listApplications(projectId: string): Promise<DokployApplication[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployApplication[]>(\n\t\t\t\t`application.all?projectId=${projectId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find an application by name in a project\n\t */\n\tasync findApplicationByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployApplication | undefined> {\n\t\tconst applications = await this.listApplications(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn applications.find(\n\t\t\t(app) => app.name === name || app.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new application\n\t */\n\tasync createApplication(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t): Promise<DokployApplication> {\n\t\treturn this.post<DokployApplication>('application.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName: name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t});\n\t}\n\n\t/**\n\t * Find or create an application by name\n\t */\n\tasync findOrCreateApplication(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t): Promise<{ application: DokployApplication; created: boolean }> {\n\t\tconst existing = await this.findApplicationByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { application: existing, created: false };\n\t\t}\n\t\tconst application = await this.createApplication(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t);\n\t\treturn { application, created: true };\n\t}\n\n\t/**\n\t * Get an application by ID\n\t */\n\tasync getApplication(\n\t\tapplicationId: string,\n\t): Promise<DokployApplication | null> {\n\t\ttry {\n\t\t\treturn await this.get<DokployApplication>(\n\t\t\t\t`application.one?applicationId=${applicationId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Application not found\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * Update an application\n\t */\n\tasync updateApplication(\n\t\tapplicationId: string,\n\t\tupdates: Partial<DokployApplicationUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('application.update', {\n\t\t\tapplicationId,\n\t\t\t...updates,\n\t\t});\n\t}\n\n\t/**\n\t * Save environment variables for an application\n\t */\n\tasync saveApplicationEnv(applicationId: string, env: string): Promise<void> {\n\t\tawait this.post('application.saveEnvironment', {\n\t\t\tapplicationId,\n\t\t\tenv,\n\t\t});\n\t}\n\n\t/**\n\t * Configure application to use Docker provider (pull from registry)\n\t *\n\t * For private registries, either:\n\t * - Use `registryId` if the registry is configured in Dokploy\n\t * - Or provide `username`, `password`, and `registryUrl` directly\n\t */\n\tasync saveDockerProvider(\n\t\tapplicationId: string,\n\t\tdockerImage: string,\n\t\toptions?: {\n\t\t\t/** Registry ID in Dokploy (for pre-configured registries) */\n\t\t\tregistryId?: string;\n\t\t\t/** Registry username (for direct auth) */\n\t\t\tusername?: string;\n\t\t\t/** Registry password (for direct auth) */\n\t\t\tpassword?: string;\n\t\t\t/** Registry URL (for direct auth, e.g., ghcr.io) */\n\t\t\tregistryUrl?: string;\n\t\t},\n\t): Promise<void> {\n\t\tawait this.post('application.saveDockerProvider', {\n\t\t\tapplicationId,\n\t\t\tdockerImage,\n\t\t\t...options,\n\t\t});\n\t}\n\n\t/**\n\t * Deploy an application\n\t */\n\tasync deployApplication(applicationId: string): Promise<void> {\n\t\tawait this.post('application.deploy', { applicationId });\n\t}\n\n\t/**\n\t * Delete an application\n\t */\n\tasync deleteApplication(applicationId: string): Promise<void> {\n\t\tawait this.post('application.remove', { applicationId });\n\t}\n\n\t// ============================================\n\t// Registry endpoints\n\t// ============================================\n\n\t/**\n\t * List all registries\n\t */\n\tasync listRegistries(): Promise<DokployRegistry[]> {\n\t\treturn this.get<DokployRegistry[]>('registry.all');\n\t}\n\n\t/**\n\t * Create a new registry\n\t */\n\tasync createRegistry(\n\t\tregistryName: string,\n\t\tregistryUrl: string,\n\t\tusername: string,\n\t\tpassword: string,\n\t\toptions?: {\n\t\t\timagePrefix?: string;\n\t\t},\n\t): Promise<DokployRegistry> {\n\t\treturn this.post<DokployRegistry>('registry.create', {\n\t\t\tregistryName,\n\t\t\tregistryUrl,\n\t\t\tusername,\n\t\t\tpassword,\n\t\t\timagePrefix: options?.imagePrefix,\n\t\t});\n\t}\n\n\t/**\n\t * Get a registry by ID\n\t */\n\tasync getRegistry(registryId: string): Promise<DokployRegistry> {\n\t\treturn this.get<DokployRegistry>(`registry.one?registryId=${registryId}`);\n\t}\n\n\t/**\n\t * Update a registry\n\t */\n\tasync updateRegistry(\n\t\tregistryId: string,\n\t\tupdates: Partial<{\n\t\t\tregistryName: string;\n\t\t\tregistryUrl: string;\n\t\t\tusername: string;\n\t\t\tpassword: string;\n\t\t\timagePrefix: string;\n\t\t}>,\n\t): Promise<void> {\n\t\tawait this.post('registry.update', { registryId, ...updates });\n\t}\n\n\t/**\n\t * Delete a registry\n\t */\n\tasync deleteRegistry(registryId: string): Promise<void> {\n\t\tawait this.post('registry.remove', { registryId });\n\t}\n\n\t// ============================================\n\t// Postgres endpoints\n\t// ============================================\n\n\t/**\n\t * List all Postgres databases in a project\n\t */\n\tasync listPostgres(projectId: string): Promise<DokployPostgres[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployPostgres[]>(\n\t\t\t\t`postgres.all?projectId=${projectId}`,\n\t\t\t);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find a Postgres database by name in a project\n\t */\n\tasync findPostgresByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployPostgres | undefined> {\n\t\tconst databases = await this.listPostgres(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn databases.find(\n\t\t\t(db) => db.name === name || db.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new Postgres database\n\t */\n\tasync createPostgres(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tappName?: string;\n\t\t\tdatabaseName?: string;\n\t\t\tdatabaseUser?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<DokployPostgres> {\n\t\treturn this.post<DokployPostgres>('postgres.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName:\n\t\t\t\toptions?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t\tdatabaseName: options?.databaseName,\n\t\t\tdatabaseUser: options?.databaseUser ?? 'postgres',\n\t\t\tdatabasePassword: options?.databasePassword,\n\t\t\tdockerImage: options?.dockerImage ?? 'postgres:18',\n\t\t\tdescription: options?.description ?? `Postgres database for ${name}`,\n\t\t});\n\t}\n\n\t/**\n\t * Find or create a Postgres database by name\n\t */\n\tasync findOrCreatePostgres(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tdatabaseName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t},\n\t): Promise<{ postgres: DokployPostgres; created: boolean }> {\n\t\tconst existing = await this.findPostgresByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { postgres: existing, created: false };\n\t\t}\n\t\tconst postgres = await this.createPostgres(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\toptions,\n\t\t);\n\t\treturn { postgres, created: true };\n\t}\n\n\t/**\n\t * Get a Postgres database by ID\n\t */\n\tasync getPostgres(postgresId: string): Promise<DokployPostgres> {\n\t\treturn this.get<DokployPostgres>(`postgres.one?postgresId=${postgresId}`);\n\t}\n\n\t/**\n\t * Deploy a Postgres database\n\t */\n\tasync deployPostgres(postgresId: string): Promise<void> {\n\t\tawait this.post('postgres.deploy', { postgresId });\n\t}\n\n\t/**\n\t * Save environment variables for Postgres\n\t */\n\tasync savePostgresEnv(postgresId: string, env: string): Promise<void> {\n\t\tawait this.post('postgres.saveEnvironment', { postgresId, env });\n\t}\n\n\t/**\n\t * Set external port for Postgres (for external access)\n\t */\n\tasync savePostgresExternalPort(\n\t\tpostgresId: string,\n\t\texternalPort: number | null,\n\t): Promise<void> {\n\t\tawait this.post('postgres.saveExternalPort', { postgresId, externalPort });\n\t}\n\n\t/**\n\t * Update Postgres configuration\n\t */\n\tasync updatePostgres(\n\t\tpostgresId: string,\n\t\tupdates: Partial<DokployPostgresUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('postgres.update', { postgresId, ...updates });\n\t}\n\n\t/**\n\t * Delete a Postgres database\n\t */\n\tasync deletePostgres(postgresId: string): Promise<void> {\n\t\tawait this.post('postgres.remove', { postgresId });\n\t}\n\n\t// ============================================\n\t// Redis endpoints\n\t// ============================================\n\n\t/**\n\t * List all Redis instances in a project\n\t */\n\tasync listRedis(projectId: string): Promise<DokployRedis[]> {\n\t\ttry {\n\t\t\treturn await this.get<DokployRedis[]>(`redis.all?projectId=${projectId}`);\n\t\t} catch {\n\t\t\t// Fallback: endpoint might not exist in older Dokploy versions\n\t\t\treturn [];\n\t\t}\n\t}\n\n\t/**\n\t * Find a Redis instance by name in a project\n\t */\n\tasync findRedisByName(\n\t\tprojectId: string,\n\t\tname: string,\n\t): Promise<DokployRedis | undefined> {\n\t\tconst instances = await this.listRedis(projectId);\n\t\tconst normalizedName = name.toLowerCase().replace(/[^a-z0-9-]/g, '-');\n\t\treturn instances.find(\n\t\t\t(redis) => redis.name === name || redis.appName === normalizedName,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new Redis instance\n\t */\n\tasync createRedis(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tappName?: string;\n\t\t\tdatabasePassword?: string;\n\t\t\tdockerImage?: string;\n\t\t\tdescription?: string;\n\t\t},\n\t): Promise<DokployRedis> {\n\t\treturn this.post<DokployRedis>('redis.create', {\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\tappName:\n\t\t\t\toptions?.appName ?? name.toLowerCase().replace(/[^a-z0-9-]/g, '-'),\n\t\t\tdatabasePassword: options?.databasePassword,\n\t\t\tdockerImage: options?.dockerImage ?? 'redis:8',\n\t\t\tdescription: options?.description ?? `Redis instance for ${name}`,\n\t\t});\n\t}\n\n\t/**\n\t * Find or create a Redis instance by name\n\t */\n\tasync findOrCreateRedis(\n\t\tname: string,\n\t\tprojectId: string,\n\t\tenvironmentId: string,\n\t\toptions?: {\n\t\t\tdatabasePassword?: string;\n\t\t},\n\t): Promise<{ redis: DokployRedis; created: boolean }> {\n\t\tconst existing = await this.findRedisByName(projectId, name);\n\t\tif (existing) {\n\t\t\treturn { redis: existing, created: false };\n\t\t}\n\t\tconst redis = await this.createRedis(\n\t\t\tname,\n\t\t\tprojectId,\n\t\t\tenvironmentId,\n\t\t\toptions,\n\t\t);\n\t\treturn { redis, created: true };\n\t}\n\n\t/**\n\t * Get a Redis instance by ID\n\t */\n\tasync getRedis(redisId: string): Promise<DokployRedis> {\n\t\treturn this.get<DokployRedis>(`redis.one?redisId=${redisId}`);\n\t}\n\n\t/**\n\t * Deploy a Redis instance\n\t */\n\tasync deployRedis(redisId: string): Promise<void> {\n\t\tawait this.post('redis.deploy', { redisId });\n\t}\n\n\t/**\n\t * Save environment variables for Redis\n\t */\n\tasync saveRedisEnv(redisId: string, env: string): Promise<void> {\n\t\tawait this.post('redis.saveEnvironment', { redisId, env });\n\t}\n\n\t/**\n\t * Set external port for Redis (for external access)\n\t */\n\tasync saveRedisExternalPort(\n\t\tredisId: string,\n\t\texternalPort: number | null,\n\t): Promise<void> {\n\t\tawait this.post('redis.saveExternalPort', { redisId, externalPort });\n\t}\n\n\t/**\n\t * Update Redis configuration\n\t */\n\tasync updateRedis(\n\t\tredisId: string,\n\t\tupdates: Partial<DokployRedisUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('redis.update', { redisId, ...updates });\n\t}\n\n\t/**\n\t * Delete a Redis instance\n\t */\n\tasync deleteRedis(redisId: string): Promise<void> {\n\t\tawait this.post('redis.remove', { redisId });\n\t}\n\n\t// ============================================\n\t// Domain endpoints\n\t// ============================================\n\n\t/**\n\t * Create a new domain for an application\n\t */\n\tasync createDomain(options: DokployDomainCreate): Promise<DokployDomain> {\n\t\treturn this.post<DokployDomain>(\n\t\t\t'domain.create',\n\t\t\toptions as unknown as Record<string, unknown>,\n\t\t);\n\t}\n\n\t/**\n\t * Update an existing domain\n\t */\n\tasync updateDomain(\n\t\tdomainId: string,\n\t\tupdates: Partial<DokployDomainCreate>,\n\t): Promise<void> {\n\t\tawait this.post('domain.update', { domainId, ...updates });\n\t}\n\n\t/**\n\t * Delete a domain\n\t */\n\tasync deleteDomain(domainId: string): Promise<void> {\n\t\tawait this.post('domain.delete', { domainId });\n\t}\n\n\t/**\n\t * Get a domain by ID\n\t */\n\tasync getDomain(domainId: string): Promise<DokployDomain> {\n\t\treturn this.get<DokployDomain>(`domain.one?domainId=${domainId}`);\n\t}\n\n\t/**\n\t * Get all domains for an application\n\t */\n\tasync getDomainsByApplicationId(\n\t\tapplicationId: string,\n\t): Promise<DokployDomain[]> {\n\t\treturn this.get<DokployDomain[]>(\n\t\t\t`domain.byApplicationId?applicationId=${applicationId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Validate a domain and trigger SSL certificate generation\n\t *\n\t * This should be called after DNS records are created and propagated.\n\t * It triggers Let's Encrypt certificate generation for HTTPS domains.\n\t *\n\t * @param domain - The domain hostname to validate (e.g., 'api.example.com')\n\t */\n\tasync validateDomain(\n\t\tdomain: string,\n\t): Promise<{ isValid: boolean; resolvedIp: string }> {\n\t\treturn this.post<{ isValid: boolean; resolvedIp: string }>(\n\t\t\t'domain.validateDomain',\n\t\t\t{ domain },\n\t\t);\n\t}\n\n\t/**\n\t * Auto-generate a domain name for an application\n\t */\n\tasync generateDomain(\n\t\tappName: string,\n\t\tserverId?: string,\n\t): Promise<{ domain: string }> {\n\t\treturn this.post<{ domain: string }>('domain.generateDomain', {\n\t\t\tappName,\n\t\t\tserverId,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Destination endpoints (backup storage)\n\t// ============================================\n\n\t/**\n\t * List all backup destinations\n\t */\n\tasync listDestinations(): Promise<DokployDestination[]> {\n\t\treturn this.get<DokployDestination[]>('destination.all');\n\t}\n\n\t/**\n\t * Get a destination by ID\n\t */\n\tasync getDestination(destinationId: string): Promise<DokployDestination> {\n\t\treturn this.get<DokployDestination>(\n\t\t\t`destination.one?destinationId=${destinationId}`,\n\t\t);\n\t}\n\n\t/**\n\t * Create a new S3 backup destination\n\t */\n\tasync createDestination(\n\t\toptions: DokployDestinationCreate,\n\t): Promise<DokployDestination> {\n\t\treturn this.post<DokployDestination>('destination.create', { ...options });\n\t}\n\n\t/**\n\t * Find a destination by name\n\t */\n\tasync findDestinationByName(\n\t\tname: string,\n\t): Promise<DokployDestination | undefined> {\n\t\tconst destinations = await this.listDestinations();\n\t\treturn destinations.find((d) => d.name === name);\n\t}\n\n\t/**\n\t * Find or create a destination by name\n\t */\n\tasync findOrCreateDestination(\n\t\tname: string,\n\t\toptions: Omit<DokployDestinationCreate, 'name'>,\n\t): Promise<{ destination: DokployDestination; created: boolean }> {\n\t\tconst existing = await this.findDestinationByName(name);\n\t\tif (existing) {\n\t\t\treturn { destination: existing, created: false };\n\t\t}\n\t\tconst destination = await this.createDestination({ name, ...options });\n\t\treturn { destination, created: true };\n\t}\n\n\t/**\n\t * Update a destination\n\t */\n\tasync updateDestination(\n\t\tdestinationId: string,\n\t\tupdates: Partial<DokployDestinationCreate>,\n\t): Promise<void> {\n\t\tawait this.post('destination.update', { destinationId, ...updates });\n\t}\n\n\t/**\n\t * Delete a destination\n\t */\n\tasync deleteDestination(destinationId: string): Promise<void> {\n\t\tawait this.post('destination.remove', { destinationId });\n\t}\n\n\t/**\n\t * Test connection to a destination\n\t */\n\tasync testDestinationConnection(\n\t\tdestinationId: string,\n\t): Promise<{ success: boolean }> {\n\t\treturn this.post<{ success: boolean }>('destination.testConnection', {\n\t\t\tdestinationId,\n\t\t});\n\t}\n\n\t// ============================================\n\t// Backup endpoints (scheduled backups)\n\t// ============================================\n\n\t/**\n\t * Create a backup schedule for postgres\n\t */\n\tasync createPostgresBackup(\n\t\toptions: DokployBackupCreate,\n\t): Promise<DokployBackup> {\n\t\treturn this.post<DokployBackup>('backup.create', {\n\t\t\t...options,\n\t\t\tdatabaseType: 'postgres',\n\t\t});\n\t}\n\n\t/**\n\t * List backups for a postgres database\n\t */\n\tasync listPostgresBackups(postgresId: string): Promise<DokployBackup[]> {\n\t\treturn this.get<DokployBackup[]>(\n\t\t\t`backup.all?postgresId=${postgresId}&databaseType=postgres`,\n\t\t);\n\t}\n\n\t/**\n\t * Get a backup by ID\n\t */\n\tasync getBackup(backupId: string): Promise<DokployBackup> {\n\t\treturn this.get<DokployBackup>(`backup.one?backupId=${backupId}`);\n\t}\n\n\t/**\n\t * Update a backup schedule\n\t */\n\tasync updateBackup(\n\t\tbackupId: string,\n\t\tupdates: Partial<DokployBackupUpdate>,\n\t): Promise<void> {\n\t\tawait this.post('backup.update', { backupId, ...updates });\n\t}\n\n\t/**\n\t * Delete a backup schedule\n\t */\n\tasync deleteBackup(backupId: string): Promise<void> {\n\t\tawait this.post('backup.remove', { backupId });\n\t}\n\n\t/**\n\t * Trigger a manual backup run\n\t */\n\tasync runBackupManually(backupId: string): Promise<void> {\n\t\tawait this.post('backup.manualBackup', { backupId });\n\t}\n}\n\n// ============================================\n// Type definitions for Dokploy API responses\n// ============================================\n\nexport interface DokployProject {\n\tprojectId: string;\n\tname: string;\n\tdescription: string | null;\n\tcreatedAt?: string;\n\tadminId?: string;\n}\n\nexport interface DokployEnvironment {\n\tenvironmentId: string;\n\tname: string;\n\tdescription: string | null;\n}\n\nexport interface DokployProjectDetails extends DokployProject {\n\tenvironments: DokployEnvironment[];\n}\n\nexport interface DokployApplication {\n\tapplicationId: string;\n\tname: string;\n\tappName: string;\n\tprojectId: string;\n\tenvironmentId?: string;\n}\n\nexport interface DokployApplicationUpdate {\n\tregistryId: string;\n\tdockerImage: string;\n\tsourceType: 'docker';\n}\n\nexport interface DokployRegistry {\n\tregistryId: string;\n\tregistryName: string;\n\tregistryUrl: string;\n\tusername: string;\n\timagePrefix: string | null;\n}\n\nexport interface DokployPostgres {\n\tpostgresId: string;\n\tname: string;\n\tappName: string;\n\tdatabaseName: string;\n\tdatabaseUser: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string | null;\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplicationStatus: 'idle' | 'running' | 'done' | 'error';\n\texternalPort: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployPostgresUpdate {\n\tname: string;\n\tappName: string;\n\tdatabaseName: string;\n\tdatabaseUser: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string;\n}\n\nexport interface DokployRedis {\n\tredisId: string;\n\tname: string;\n\tappName: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string | null;\n\tprojectId: string;\n\tenvironmentId: string;\n\tapplicationStatus: 'idle' | 'running' | 'done' | 'error';\n\texternalPort: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployRedisUpdate {\n\tname: string;\n\tappName: string;\n\tdatabasePassword: string;\n\tdockerImage: string;\n\tdescription: string;\n}\n\nexport type DokployCertificateType = 'letsencrypt' | 'none' | 'custom';\nexport type DokployDomainType = 'application' | 'compose' | 'preview';\n\nexport interface DokployDomainCreate {\n\t/** Domain hostname (e.g., 'api.example.com') */\n\thost: string;\n\t/** URL path (optional, e.g., '/api') */\n\tpath?: string | null;\n\t/** Container port to route to (1-65535) */\n\tport?: number | null;\n\t/** Enable HTTPS */\n\thttps?: boolean;\n\t/** Associated application ID */\n\tapplicationId?: string | null;\n\t/** Certificate type for HTTPS */\n\tcertificateType?: DokployCertificateType;\n\t/** Custom certificate resolver name */\n\tcustomCertResolver?: string | null;\n\t/** Docker Compose service ID */\n\tcomposeId?: string | null;\n\t/** Service name for compose */\n\tserviceName?: string | null;\n\t/** Domain type */\n\tdomainType?: DokployDomainType | null;\n\t/** Preview deployment ID */\n\tpreviewDeploymentId?: string | null;\n\t/** Internal routing path */\n\tinternalPath?: string | null;\n\t/** Strip path from forwarded requests */\n\tstripPath?: boolean;\n}\n\nexport interface DokployDomain extends DokployDomainCreate {\n\tdomainId: string;\n\tcreatedAt?: string;\n}\n\n// ============================================\n// Destination types (backup storage)\n// ============================================\n\nexport interface DokployDestination {\n\tdestinationId: string;\n\tname: string;\n\taccessKey: string;\n\tbucket: string;\n\tregion: string;\n\tendpoint: string | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployDestinationCreate {\n\t/** User-friendly name for the destination */\n\tname: string;\n\t/** S3 access key ID */\n\taccessKey: string;\n\t/** S3 secret access key */\n\tsecretAccessKey: string;\n\t/** S3 bucket name */\n\tbucket: string;\n\t/** AWS region (e.g., 'us-east-1') */\n\tregion: string;\n\t/** Optional endpoint for S3-compatible services */\n\tendpoint?: string;\n}\n\n// ============================================\n// Backup types (scheduled backups)\n// ============================================\n\nexport type DokployDatabaseType = 'postgres' | 'mysql' | 'mariadb' | 'mongo';\n\nexport interface DokployBackup {\n\tbackupId: string;\n\tschedule: string;\n\tprefix: string;\n\tenabled: boolean;\n\tdestinationId: string;\n\tpostgresId?: string;\n\tdatabaseType: DokployDatabaseType;\n\tkeepLatestCount: number | null;\n\tcreatedAt?: string;\n}\n\nexport interface DokployBackupCreate {\n\t/** Cron schedule (e.g., '0 2 * * *' for 2 AM daily) */\n\tschedule: string;\n\t/** Backup file prefix (e.g., 'production/postgres') */\n\tprefix: string;\n\t/** Destination ID for backup storage */\n\tdestinationId: string;\n\t/** Database name to backup */\n\tdatabase: string;\n\t/** Postgres instance ID */\n\tpostgresId: string;\n\t/** Enable/disable backup (default: true) */\n\tenabled?: boolean;\n\t/** Number of backups to keep (default: keep all) */\n\tkeepLatestCount?: number;\n}\n\nexport interface DokployBackupUpdate {\n\tschedule?: string;\n\tprefix?: string;\n\tenabled?: boolean;\n\tkeepLatestCount?: number;\n}\n\n/**\n * Create a Dokploy API client from stored credentials or environment\n */\nexport async function createDokployApi(\n\tendpoint?: string,\n): Promise<DokployApi | null> {\n\tconst { getDokployCredentials } = await import('../auth/credentials');\n\n\t// Try environment variable first\n\tconst envToken = process.env.DOKPLOY_API_TOKEN;\n\tconst envEndpoint = endpoint || process.env.DOKPLOY_ENDPOINT;\n\n\tif (envToken && envEndpoint) {\n\t\treturn new DokployApi({ baseUrl: envEndpoint, token: envToken });\n\t}\n\n\t// Fall back to stored credentials\n\tconst creds = await getDokployCredentials();\n\tif (creds) {\n\t\treturn new DokployApi({\n\t\t\tbaseUrl: endpoint || creds.endpoint,\n\t\t\ttoken: creds.token,\n\t\t});\n\t}\n\n\treturn null;\n}\n"],"mappings":";;AAkBA,IAAa,kBAAb,cAAqC,MAAM;CAC1C,YACCA,SACOC,QACAC,YACAC,QACN;AACD,QAAM,QAAQ;EAJP;EACA;EACA;AAGP,OAAK,OAAO;CACZ;AACD;;;;AAKD,IAAa,aAAb,MAAwB;CACvB,AAAQ;CACR,AAAQ;CAER,YAAYC,SAA4B;AACvC,OAAK,UAAU,QAAQ,QAAQ,QAAQ,OAAO,GAAG;AACjD,OAAK,QAAQ,QAAQ;CACrB;;;;CAKD,MAAM,IAAOC,UAA8B;AAC1C,SAAO,KAAK,QAAW,OAAO,SAAS;CACvC;;;;CAKD,MAAM,KAAQA,UAAkBC,MAA4C;AAC3E,SAAO,KAAK,QAAW,QAAQ,UAAU,KAAK;CAC9C;;;;CAKD,MAAc,QACbC,QACAF,UACAC,MACa;EACb,MAAM,OAAO,EAAE,KAAK,QAAQ,OAAO,SAAS;EAE5C,MAAM,WAAW,MAAM,MAAM,KAAK;GACjC;GACA,SAAS;IACR,gBAAgB;IAChB,aAAa,KAAK;GAClB;GACD,MAAM,OAAO,KAAK,UAAU,KAAK;EACjC,EAAC;AAEF,OAAK,SAAS,IAAI;GACjB,IAAI,gBAAgB,qBAAqB,SAAS,OAAO,GAAG,SAAS,WAAW;GAChF,IAAIE;AAEJ,OAAI;IACH,MAAM,YAAa,MAAM,SAAS,MAAM;AACxC,QAAI,UAAU,QACb,iBAAgB,qBAAqB,UAAU,QAAQ;AAExD,QAAI,UAAU,QAAQ,QAAQ;AAC7B,cAAS,UAAU;AACnB,sBAAiB,cAAc,UAAU,OAAO,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,KAAK,KAAK,CAAC;IACjF;GACD,QAAO,CAEP;AAED,SAAM,IAAI,gBACT,cACA,SAAS,QACT,SAAS,YACT;EAED;EAGD,MAAM,OAAO,MAAM,SAAS,MAAM;AAClC,OAAK,QAAQ,KAAK,MAAM,KAAK,GAC5B;AAED,SAAO,KAAK,MAAM,KAAK;CACvB;;;;CAKD,MAAM,gBAAkC;AACvC,MAAI;AACH,SAAM,KAAK,IAAI,cAAc;AAC7B,UAAO;EACP,QAAO;AACP,UAAO;EACP;CACD;;;;CASD,MAAM,eAA0C;AAC/C,SAAO,KAAK,IAAsB,cAAc;CAChD;;;;CAKD,MAAM,WAAWC,WAAmD;AACnE,SAAO,KAAK,KACV,wBAAwB,UAAU,EACnC;CACD;;;;CAKD,MAAM,cACLC,MACAC,aACwE;AACxE,SAAO,KAAK,KAGT,kBAAkB;GACpB;GACA,aAAa,gBAAgB;EAC7B,EAAC;CACF;;;;CAKD,MAAM,cAAcF,WAAkC;AACrD,QAAM,KAAK,KAAK,kBAAkB,EAAE,UAAW,EAAC;CAChD;;;;CASD,MAAM,kBACLA,WACAC,MACAC,aAC8B;AAC9B,SAAO,KAAK,KAAyB,sBAAsB;GAC1D;GACA;GACA,aAAa,gBAAgB,EAAE,KAAK;EACpC,EAAC;CACF;;;;CASD,MAAM,iBAAiBF,WAAkD;AACxE,MAAI;AACH,UAAO,MAAM,KAAK,KAChB,4BAA4B,UAAU,EACvC;EACD,QAAO;AAEP,UAAO,CAAE;EACT;CACD;;;;CAKD,MAAM,sBACLA,WACAC,MAC0C;EAC1C,MAAM,eAAe,MAAM,KAAK,iBAAiB,UAAU;EAC3D,MAAM,iBAAiB,KAAK,aAAa,CAAC,QAAQ,eAAe,IAAI;AACrE,SAAO,aAAa,KACnB,CAAC,QAAQ,IAAI,SAAS,QAAQ,IAAI,YAAY,eAC9C;CACD;;;;CAKD,MAAM,kBACLA,MACAD,WACAG,eAC8B;AAC9B,SAAO,KAAK,KAAyB,sBAAsB;GAC1D;GACA;GACA;GACA,SAAS,KAAK,aAAa,CAAC,QAAQ,eAAe,IAAI;EACvD,EAAC;CACF;;;;CAKD,MAAM,wBACLF,MACAD,WACAG,eACiE;EACjE,MAAM,WAAW,MAAM,KAAK,sBAAsB,WAAW,KAAK;AAClE,MAAI,SACH,QAAO;GAAE,aAAa;GAAU,SAAS;EAAO;EAEjD,MAAM,cAAc,MAAM,KAAK,kBAC9B,MACA,WACA,cACA;AACD,SAAO;GAAE;GAAa,SAAS;EAAM;CACrC;;;;CAKD,MAAM,eACLC,eACqC;AACrC,MAAI;AACH,UAAO,MAAM,KAAK,KAChB,gCAAgC,cAAc,EAC/C;EACD,QAAO;AAEP,UAAO;EACP;CACD;;;;CAKD,MAAM,kBACLA,eACAC,SACgB;AAChB,QAAM,KAAK,KAAK,sBAAsB;GACrC;GACA,GAAG;EACH,EAAC;CACF;;;;CAKD,MAAM,mBAAmBD,eAAuBE,KAA4B;AAC3E,QAAM,KAAK,KAAK,+BAA+B;GAC9C;GACA;EACA,EAAC;CACF;;;;;;;;CASD,MAAM,mBACLF,eACAG,aACAC,SAUgB;AAChB,QAAM,KAAK,KAAK,kCAAkC;GACjD;GACA;GACA,GAAG;EACH,EAAC;CACF;;;;CAKD,MAAM,kBAAkBJ,eAAsC;AAC7D,QAAM,KAAK,KAAK,sBAAsB,EAAE,cAAe,EAAC;CACxD;;;;CAKD,MAAM,kBAAkBA,eAAsC;AAC7D,QAAM,KAAK,KAAK,sBAAsB,EAAE,cAAe,EAAC;CACxD;;;;CASD,MAAM,iBAA6C;AAClD,SAAO,KAAK,IAAuB,eAAe;CAClD;;;;CAKD,MAAM,eACLK,cACAC,aACAC,UACAC,UACAC,SAG2B;AAC3B,SAAO,KAAK,KAAsB,mBAAmB;GACpD;GACA;GACA;GACA;GACA,aAAa,SAAS;EACtB,EAAC;CACF;;;;CAKD,MAAM,YAAYC,YAA8C;AAC/D,SAAO,KAAK,KAAsB,0BAA0B,WAAW,EAAE;CACzE;;;;CAKD,MAAM,eACLA,YACAC,SAOgB;AAChB,QAAM,KAAK,KAAK,mBAAmB;GAAE;GAAY,GAAG;EAAS,EAAC;CAC9D;;;;CAKD,MAAM,eAAeD,YAAmC;AACvD,QAAM,KAAK,KAAK,mBAAmB,EAAE,WAAY,EAAC;CAClD;;;;CASD,MAAM,aAAad,WAA+C;AACjE,MAAI;AACH,UAAO,MAAM,KAAK,KAChB,yBAAyB,UAAU,EACpC;EACD,QAAO;AAEP,UAAO,CAAE;EACT;CACD;;;;CAKD,MAAM,mBACLA,WACAC,MACuC;EACvC,MAAM,YAAY,MAAM,KAAK,aAAa,UAAU;EACpD,MAAM,iBAAiB,KAAK,aAAa,CAAC,QAAQ,eAAe,IAAI;AACrE,SAAO,UAAU,KAChB,CAAC,OAAO,GAAG,SAAS,QAAQ,GAAG,YAAY,eAC3C;CACD;;;;CAKD,MAAM,eACLA,MACAD,WACAG,eACAa,SAQ2B;AAC3B,SAAO,KAAK,KAAsB,mBAAmB;GACpD;GACA;GACA;GACA,SACC,SAAS,WAAW,KAAK,aAAa,CAAC,QAAQ,eAAe,IAAI;GACnE,cAAc,SAAS;GACvB,cAAc,SAAS,gBAAgB;GACvC,kBAAkB,SAAS;GAC3B,aAAa,SAAS,eAAe;GACrC,aAAa,SAAS,gBAAgB,wBAAwB,KAAK;EACnE,EAAC;CACF;;;;CAKD,MAAM,qBACLf,MACAD,WACAG,eACAc,SAI2D;EAC3D,MAAM,WAAW,MAAM,KAAK,mBAAmB,WAAW,KAAK;AAC/D,MAAI,SACH,QAAO;GAAE,UAAU;GAAU,SAAS;EAAO;EAE9C,MAAM,WAAW,MAAM,KAAK,eAC3B,MACA,WACA,eACA,QACA;AACD,SAAO;GAAE;GAAU,SAAS;EAAM;CAClC;;;;CAKD,MAAM,YAAYC,YAA8C;AAC/D,SAAO,KAAK,KAAsB,0BAA0B,WAAW,EAAE;CACzE;;;;CAKD,MAAM,eAAeA,YAAmC;AACvD,QAAM,KAAK,KAAK,mBAAmB,EAAE,WAAY,EAAC;CAClD;;;;CAKD,MAAM,gBAAgBA,YAAoBZ,KAA4B;AACrE,QAAM,KAAK,KAAK,4BAA4B;GAAE;GAAY;EAAK,EAAC;CAChE;;;;CAKD,MAAM,yBACLY,YACAC,cACgB;AAChB,QAAM,KAAK,KAAK,6BAA6B;GAAE;GAAY;EAAc,EAAC;CAC1E;;;;CAKD,MAAM,eACLD,YACAE,SACgB;AAChB,QAAM,KAAK,KAAK,mBAAmB;GAAE;GAAY,GAAG;EAAS,EAAC;CAC9D;;;;CAKD,MAAM,eAAeF,YAAmC;AACvD,QAAM,KAAK,KAAK,mBAAmB,EAAE,WAAY,EAAC;CAClD;;;;CASD,MAAM,UAAUlB,WAA4C;AAC3D,MAAI;AACH,UAAO,MAAM,KAAK,KAAqB,sBAAsB,UAAU,EAAE;EACzE,QAAO;AAEP,UAAO,CAAE;EACT;CACD;;;;CAKD,MAAM,gBACLA,WACAC,MACoC;EACpC,MAAM,YAAY,MAAM,KAAK,UAAU,UAAU;EACjD,MAAM,iBAAiB,KAAK,aAAa,CAAC,QAAQ,eAAe,IAAI;AACrE,SAAO,UAAU,KAChB,CAAC,UAAU,MAAM,SAAS,QAAQ,MAAM,YAAY,eACpD;CACD;;;;CAKD,MAAM,YACLA,MACAD,WACAG,eACAkB,SAMwB;AACxB,SAAO,KAAK,KAAmB,gBAAgB;GAC9C;GACA;GACA;GACA,SACC,SAAS,WAAW,KAAK,aAAa,CAAC,QAAQ,eAAe,IAAI;GACnE,kBAAkB,SAAS;GAC3B,aAAa,SAAS,eAAe;GACrC,aAAa,SAAS,gBAAgB,qBAAqB,KAAK;EAChE,EAAC;CACF;;;;CAKD,MAAM,kBACLpB,MACAD,WACAG,eACAmB,SAGqD;EACrD,MAAM,WAAW,MAAM,KAAK,gBAAgB,WAAW,KAAK;AAC5D,MAAI,SACH,QAAO;GAAE,OAAO;GAAU,SAAS;EAAO;EAE3C,MAAM,QAAQ,MAAM,KAAK,YACxB,MACA,WACA,eACA,QACA;AACD,SAAO;GAAE;GAAO,SAAS;EAAM;CAC/B;;;;CAKD,MAAM,SAASC,SAAwC;AACtD,SAAO,KAAK,KAAmB,oBAAoB,QAAQ,EAAE;CAC7D;;;;CAKD,MAAM,YAAYA,SAAgC;AACjD,QAAM,KAAK,KAAK,gBAAgB,EAAE,QAAS,EAAC;CAC5C;;;;CAKD,MAAM,aAAaA,SAAiBjB,KAA4B;AAC/D,QAAM,KAAK,KAAK,yBAAyB;GAAE;GAAS;EAAK,EAAC;CAC1D;;;;CAKD,MAAM,sBACLiB,SACAJ,cACgB;AAChB,QAAM,KAAK,KAAK,0BAA0B;GAAE;GAAS;EAAc,EAAC;CACpE;;;;CAKD,MAAM,YACLI,SACAC,SACgB;AAChB,QAAM,KAAK,KAAK,gBAAgB;GAAE;GAAS,GAAG;EAAS,EAAC;CACxD;;;;CAKD,MAAM,YAAYD,SAAgC;AACjD,QAAM,KAAK,KAAK,gBAAgB,EAAE,QAAS,EAAC;CAC5C;;;;CASD,MAAM,aAAaE,SAAsD;AACxE,SAAO,KAAK,KACX,iBACA,QACA;CACD;;;;CAKD,MAAM,aACLC,UACAC,SACgB;AAChB,QAAM,KAAK,KAAK,iBAAiB;GAAE;GAAU,GAAG;EAAS,EAAC;CAC1D;;;;CAKD,MAAM,aAAaD,UAAiC;AACnD,QAAM,KAAK,KAAK,iBAAiB,EAAE,SAAU,EAAC;CAC9C;;;;CAKD,MAAM,UAAUA,UAA0C;AACzD,SAAO,KAAK,KAAoB,sBAAsB,SAAS,EAAE;CACjE;;;;CAKD,MAAM,0BACLtB,eAC2B;AAC3B,SAAO,KAAK,KACV,uCAAuC,cAAc,EACtD;CACD;;;;;;;;;CAUD,MAAM,eACLwB,QACoD;AACpD,SAAO,KAAK,KACX,yBACA,EAAE,OAAQ,EACV;CACD;;;;CAKD,MAAM,eACLC,SACAC,UAC8B;AAC9B,SAAO,KAAK,KAAyB,yBAAyB;GAC7D;GACA;EACA,EAAC;CACF;;;;CASD,MAAM,mBAAkD;AACvD,SAAO,KAAK,IAA0B,kBAAkB;CACxD;;;;CAKD,MAAM,eAAeC,eAAoD;AACxE,SAAO,KAAK,KACV,gCAAgC,cAAc,EAC/C;CACD;;;;CAKD,MAAM,kBACLC,SAC8B;AAC9B,SAAO,KAAK,KAAyB,sBAAsB,EAAE,GAAG,QAAS,EAAC;CAC1E;;;;CAKD,MAAM,sBACL/B,MAC0C;EAC1C,MAAM,eAAe,MAAM,KAAK,kBAAkB;AAClD,SAAO,aAAa,KAAK,CAAC,MAAM,EAAE,SAAS,KAAK;CAChD;;;;CAKD,MAAM,wBACLA,MACAgC,SACiE;EACjE,MAAM,WAAW,MAAM,KAAK,sBAAsB,KAAK;AACvD,MAAI,SACH,QAAO;GAAE,aAAa;GAAU,SAAS;EAAO;EAEjD,MAAM,cAAc,MAAM,KAAK,kBAAkB;GAAE;GAAM,GAAG;EAAS,EAAC;AACtE,SAAO;GAAE;GAAa,SAAS;EAAM;CACrC;;;;CAKD,MAAM,kBACLF,eACAG,SACgB;AAChB,QAAM,KAAK,KAAK,sBAAsB;GAAE;GAAe,GAAG;EAAS,EAAC;CACpE;;;;CAKD,MAAM,kBAAkBH,eAAsC;AAC7D,QAAM,KAAK,KAAK,sBAAsB,EAAE,cAAe,EAAC;CACxD;;;;CAKD,MAAM,0BACLA,eACgC;AAChC,SAAO,KAAK,KAA2B,8BAA8B,EACpE,cACA,EAAC;CACF;;;;CASD,MAAM,qBACLI,SACyB;AACzB,SAAO,KAAK,KAAoB,iBAAiB;GAChD,GAAG;GACH,cAAc;EACd,EAAC;CACF;;;;CAKD,MAAM,oBAAoBjB,YAA8C;AACvE,SAAO,KAAK,KACV,wBAAwB,WAAW,wBACpC;CACD;;;;CAKD,MAAM,UAAUkB,UAA0C;AACzD,SAAO,KAAK,KAAoB,sBAAsB,SAAS,EAAE;CACjE;;;;CAKD,MAAM,aACLA,UACAC,SACgB;AAChB,QAAM,KAAK,KAAK,iBAAiB;GAAE;GAAU,GAAG;EAAS,EAAC;CAC1D;;;;CAKD,MAAM,aAAaD,UAAiC;AACnD,QAAM,KAAK,KAAK,iBAAiB,EAAE,SAAU,EAAC;CAC9C;;;;CAKD,MAAM,kBAAkBA,UAAiC;AACxD,QAAM,KAAK,KAAK,uBAAuB,EAAE,SAAU,EAAC;CACpD;AACD"}
@@ -594,4 +594,4 @@ var DokployApi = class {
594
594
 
595
595
  //#endregion
596
596
  export { DokployApi, DokployApiError };
597
- //# sourceMappingURL=dokploy-api-BIT0683o.mjs.map
597
+ //# sourceMappingURL=dokploy-api-sfZFlWuE.mjs.map