@agenticmail/enterprise 0.5.93 → 0.5.95

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.
@@ -3745,36 +3745,91 @@ function DeploymentSection(props) {
3745
3745
  var deployForm = _deployForm[0]; var setDeployForm = _deployForm[1];
3746
3746
 
3747
3747
  var startDeployEdit = function() {
3748
+ var cloud = deployment.config?.cloud || {};
3749
+ var docker = deployment.config?.docker || {};
3750
+ var vps = deployment.config?.vps || {};
3751
+ var aws = deployment.config?.aws || {};
3752
+ var gcp = deployment.config?.gcp || {};
3753
+ var az = deployment.config?.azure || {};
3754
+ var rail = deployment.config?.railway || {};
3748
3755
  setDeployForm({
3749
- target: deployment.target || 'docker',
3750
- region: deployment.region || 'iad',
3751
- imageTag: (deployment.config?.docker?.tag) || deployment.imageTag || 'latest',
3752
- memory: (deployment.config?.docker?.memory) || deployment.memory || '512m',
3753
- cpu: (deployment.config?.docker?.cpu) || deployment.cpu || '0.5',
3754
- ports: (deployment.config?.docker?.ports || [3000]).join(', '),
3756
+ target: deployment.target || 'fly',
3757
+ region: deployment.region || cloud.region || 'iad',
3758
+ // Fly.io
3759
+ flyApiToken: cloud.apiToken || '',
3760
+ flyAppName: cloud.appName || '',
3761
+ flyOrg: cloud.org || 'personal',
3762
+ flyVmSize: cloud.vmSize || 'shared-cpu-1x',
3763
+ flyVmMemory: cloud.vmMemory || '256',
3764
+ // Docker
3765
+ dockerImage: docker.image || 'agenticmail/agent',
3766
+ dockerTag: docker.tag || 'latest',
3767
+ dockerMemory: docker.memory || '512m',
3768
+ dockerCpu: docker.cpu || '0.5',
3769
+ dockerPorts: (docker.ports || [3000]).join(', '),
3770
+ dockerNetwork: docker.network || '',
3771
+ dockerRestart: docker.restart || 'unless-stopped',
3772
+ // VPS
3773
+ vpsHost: vps.host || '',
3774
+ vpsPort: vps.port || '22',
3775
+ vpsUser: vps.user || 'root',
3776
+ vpsKeyPath: vps.keyPath || '~/.ssh/id_rsa',
3777
+ vpsWorkDir: vps.workDir || '/opt/agenticmail',
3778
+ // AWS
3779
+ awsRegion: aws.region || 'us-east-1',
3780
+ awsAccessKeyId: aws.accessKeyId || '',
3781
+ awsSecretAccessKey: aws.secretAccessKey || '',
3782
+ awsInstanceType: aws.instanceType || 't3.micro',
3783
+ awsAmi: aws.ami || '',
3784
+ awsSubnetId: aws.subnetId || '',
3785
+ awsSecurityGroupId: aws.securityGroupId || '',
3786
+ awsKeyPairName: aws.keyPairName || '',
3787
+ // GCP
3788
+ gcpProject: gcp.projectId || '',
3789
+ gcpRegion: gcp.region || 'us-central1',
3790
+ gcpZone: gcp.zone || 'us-central1-a',
3791
+ gcpMachineType: gcp.machineType || 'e2-micro',
3792
+ gcpServiceAccountKey: gcp.serviceAccountKey || '',
3793
+ // Azure
3794
+ azureSubscriptionId: az.subscriptionId || '',
3795
+ azureResourceGroup: az.resourceGroup || '',
3796
+ azureRegion: az.region || 'eastus',
3797
+ azureVmSize: az.vmSize || 'Standard_B1s',
3798
+ azureTenantId: az.tenantId || '',
3799
+ azureClientId: az.clientId || '',
3800
+ azureClientSecret: az.clientSecret || '',
3801
+ // Railway
3802
+ railwayApiToken: rail.apiToken || '',
3803
+ railwayProjectId: rail.projectId || '',
3804
+ railwayServiceName: rail.serviceName || '',
3755
3805
  });
3756
3806
  setEditingDeploy(true);
3757
3807
  };
3758
3808
 
3759
3809
  var saveDeploy = function() {
3760
3810
  setSavingDeploy(true);
3811
+ var t = deployForm.target;
3812
+ var deployConfig = {};
3813
+ if (t === 'fly') {
3814
+ deployConfig = { cloud: { provider: 'fly', region: deployForm.region || 'iad', apiToken: deployForm.flyApiToken || undefined, appName: deployForm.flyAppName || undefined, org: deployForm.flyOrg || 'personal', vmSize: deployForm.flyVmSize || 'shared-cpu-1x', vmMemory: deployForm.flyVmMemory || '256' } };
3815
+ } else if (t === 'docker') {
3816
+ deployConfig = { docker: { image: deployForm.dockerImage || 'agenticmail/agent', tag: deployForm.dockerTag || 'latest', ports: (deployForm.dockerPorts || '3000').split(',').map(function(p) { return parseInt(p.trim()) || 3000; }), memory: deployForm.dockerMemory || '512m', cpu: deployForm.dockerCpu || '0.5', network: deployForm.dockerNetwork || undefined, restart: deployForm.dockerRestart || 'unless-stopped' } };
3817
+ } else if (t === 'vps') {
3818
+ deployConfig = { vps: { host: deployForm.vpsHost, port: parseInt(deployForm.vpsPort) || 22, user: deployForm.vpsUser || 'root', keyPath: deployForm.vpsKeyPath || '~/.ssh/id_rsa', workDir: deployForm.vpsWorkDir || '/opt/agenticmail' } };
3819
+ } else if (t === 'aws') {
3820
+ deployConfig = { aws: { region: deployForm.awsRegion || 'us-east-1', accessKeyId: deployForm.awsAccessKeyId || undefined, secretAccessKey: deployForm.awsSecretAccessKey || undefined, instanceType: deployForm.awsInstanceType || 't3.micro', ami: deployForm.awsAmi || undefined, subnetId: deployForm.awsSubnetId || undefined, securityGroupId: deployForm.awsSecurityGroupId || undefined, keyPairName: deployForm.awsKeyPairName || undefined } };
3821
+ } else if (t === 'gcp') {
3822
+ deployConfig = { gcp: { projectId: deployForm.gcpProject, region: deployForm.gcpRegion || 'us-central1', zone: deployForm.gcpZone || 'us-central1-a', machineType: deployForm.gcpMachineType || 'e2-micro', serviceAccountKey: deployForm.gcpServiceAccountKey || undefined } };
3823
+ } else if (t === 'azure') {
3824
+ deployConfig = { azure: { subscriptionId: deployForm.azureSubscriptionId, resourceGroup: deployForm.azureResourceGroup, region: deployForm.azureRegion || 'eastus', vmSize: deployForm.azureVmSize || 'Standard_B1s', tenantId: deployForm.azureTenantId || undefined, clientId: deployForm.azureClientId || undefined, clientSecret: deployForm.azureClientSecret || undefined } };
3825
+ } else if (t === 'railway') {
3826
+ deployConfig = { railway: { apiToken: deployForm.railwayApiToken || undefined, projectId: deployForm.railwayProjectId || undefined, serviceName: deployForm.railwayServiceName || undefined, region: deployForm.region || undefined } };
3827
+ }
3761
3828
  var updates = {
3762
3829
  deployment: {
3763
- target: deployForm.target,
3830
+ target: t,
3764
3831
  region: deployForm.region,
3765
- imageTag: deployForm.imageTag,
3766
- memory: deployForm.memory,
3767
- cpu: deployForm.cpu,
3768
- config: {
3769
- docker: {
3770
- image: 'agenticmail/agent',
3771
- tag: deployForm.imageTag,
3772
- ports: deployForm.ports.split(',').map(function(p) { return parseInt(p.trim()) || 3000; }),
3773
- memory: deployForm.memory,
3774
- cpu: deployForm.cpu,
3775
- restart: 'unless-stopped',
3776
- }
3777
- }
3832
+ config: deployConfig
3778
3833
  }
3779
3834
  };
3780
3835
  var isRunning = ea.state === 'running' || ea.state === 'active' || ea.state === 'degraded';
@@ -3843,41 +3898,331 @@ function DeploymentSection(props) {
3843
3898
  h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Target'),
3844
3899
  h('select', { className: 'input', value: deployForm.target, onChange: function(e) { setDf('target', e.target.value); } },
3845
3900
  h('option', { value: 'fly' }, 'Fly.io'),
3846
- h('option', { value: 'docker' }, 'Docker'),
3901
+ h('option', { value: 'aws' }, 'AWS (EC2)'),
3902
+ h('option', { value: 'gcp' }, 'Google Cloud (GCE)'),
3903
+ h('option', { value: 'azure' }, 'Microsoft Azure'),
3847
3904
  h('option', { value: 'railway' }, 'Railway'),
3848
- h('option', { value: 'vps' }, 'VPS / Server'),
3849
- h('option', { value: 'local' }, 'Local')
3905
+ h('option', { value: 'docker' }, 'Docker'),
3906
+ h('option', { value: 'vps' }, 'VPS / Bare Metal'),
3907
+ h('option', { value: 'local' }, 'Local (In-Process)')
3850
3908
  )
3851
3909
  ),
3910
+ // Region selector for cloud providers
3852
3911
  (deployForm.target === 'fly' || deployForm.target === 'railway') && h('div', { className: 'form-group' },
3853
3912
  h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Region'),
3854
3913
  h('select', { className: 'input', value: deployForm.region, onChange: function(e) { setDf('region', e.target.value); } },
3855
- h('option', { value: 'iad' }, 'Ashburn (iad)'),
3856
- h('option', { value: 'ord' }, 'Chicago (ord)'),
3857
- h('option', { value: 'lax' }, 'Los Angeles (lax)'),
3914
+ h('option', { value: 'iad' }, 'Ashburn, VA (iad)'),
3915
+ h('option', { value: 'ord' }, 'Chicago, IL (ord)'),
3916
+ h('option', { value: 'dfw' }, 'Dallas, TX (dfw)'),
3917
+ h('option', { value: 'lax' }, 'Los Angeles, CA (lax)'),
3918
+ h('option', { value: 'sea' }, 'Seattle, WA (sea)'),
3919
+ h('option', { value: 'sjc' }, 'San Jose, CA (sjc)'),
3920
+ h('option', { value: 'yyz' }, 'Toronto (yyz)'),
3858
3921
  h('option', { value: 'lhr' }, 'London (lhr)'),
3859
3922
  h('option', { value: 'ams' }, 'Amsterdam (ams)'),
3860
- h('option', { value: 'nrt' }, 'Tokyo (nrt)')
3923
+ h('option', { value: 'fra' }, 'Frankfurt (fra)'),
3924
+ h('option', { value: 'cdg' }, 'Paris (cdg)'),
3925
+ h('option', { value: 'waw' }, 'Warsaw (waw)'),
3926
+ h('option', { value: 'nrt' }, 'Tokyo (nrt)'),
3927
+ h('option', { value: 'sin' }, 'Singapore (sin)'),
3928
+ h('option', { value: 'hkg' }, 'Hong Kong (hkg)'),
3929
+ h('option', { value: 'syd' }, 'Sydney (syd)'),
3930
+ h('option', { value: 'gru' }, 'São Paulo (gru)'),
3931
+ h('option', { value: 'jnb' }, 'Johannesburg (jnb)')
3932
+ )
3933
+ )
3934
+ ),
3935
+
3936
+ // ── Fly.io ──────────────────────────────────────────
3937
+ deployForm.target === 'fly' && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
3938
+ h('div', { className: 'form-group' },
3939
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'API Token'),
3940
+ h('input', { className: 'input', type: 'password', value: deployForm.flyApiToken, onChange: function(e) { setDf('flyApiToken', e.target.value); }, placeholder: 'fo1_...' }),
3941
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } }, 'From fly.io/user/personal_access_tokens')
3942
+ ),
3943
+ h('div', { className: 'form-group' },
3944
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'App Name'),
3945
+ h('input', { className: 'input', value: deployForm.flyAppName, onChange: function(e) { setDf('flyAppName', e.target.value); }, placeholder: 'Auto-generated if empty' })
3946
+ ),
3947
+ h('div', { className: 'form-group' },
3948
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Organization'),
3949
+ h('input', { className: 'input', value: deployForm.flyOrg, onChange: function(e) { setDf('flyOrg', e.target.value); }, placeholder: 'personal' })
3950
+ ),
3951
+ h('div', { className: 'form-group' },
3952
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'VM Size'),
3953
+ h('select', { className: 'input', value: deployForm.flyVmSize, onChange: function(e) { setDf('flyVmSize', e.target.value); } },
3954
+ h('option', { value: 'shared-cpu-1x' }, 'Shared 1x (256MB) — $1.94/mo'),
3955
+ h('option', { value: 'shared-cpu-2x' }, 'Shared 2x (512MB) — $3.88/mo'),
3956
+ h('option', { value: 'shared-cpu-4x' }, 'Shared 4x (1GB) — $7.76/mo'),
3957
+ h('option', { value: 'shared-cpu-8x' }, 'Shared 8x (2GB) — $15.52/mo'),
3958
+ h('option', { value: 'performance-1x' }, 'Performance 1x (2GB) — $29.04/mo'),
3959
+ h('option', { value: 'performance-2x' }, 'Performance 2x (4GB) — $58.09/mo'),
3960
+ h('option', { value: 'performance-4x' }, 'Performance 4x (8GB) — $116.18/mo'),
3961
+ h('option', { value: 'performance-8x' }, 'Performance 8x (16GB) — $232.36/mo')
3962
+ )
3963
+ )
3964
+ ),
3965
+
3966
+ // ── AWS EC2 ─────────────────────────────────────────
3967
+ deployForm.target === 'aws' && h(Fragment, null,
3968
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
3969
+ h('div', { className: 'form-group' },
3970
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Access Key ID'),
3971
+ h('input', { className: 'input', type: 'password', value: deployForm.awsAccessKeyId, onChange: function(e) { setDf('awsAccessKeyId', e.target.value); }, placeholder: 'AKIA...' })
3972
+ ),
3973
+ h('div', { className: 'form-group' },
3974
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Secret Access Key'),
3975
+ h('input', { className: 'input', type: 'password', value: deployForm.awsSecretAccessKey, onChange: function(e) { setDf('awsSecretAccessKey', e.target.value); }, placeholder: '••••••••' })
3976
+ )
3977
+ ),
3978
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginTop: 12 } },
3979
+ h('div', { className: 'form-group' },
3980
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Region'),
3981
+ h('select', { className: 'input', value: deployForm.awsRegion, onChange: function(e) { setDf('awsRegion', e.target.value); } },
3982
+ h('option', { value: 'us-east-1' }, 'US East (N. Virginia)'),
3983
+ h('option', { value: 'us-east-2' }, 'US East (Ohio)'),
3984
+ h('option', { value: 'us-west-1' }, 'US West (N. California)'),
3985
+ h('option', { value: 'us-west-2' }, 'US West (Oregon)'),
3986
+ h('option', { value: 'eu-west-1' }, 'EU (Ireland)'),
3987
+ h('option', { value: 'eu-west-2' }, 'EU (London)'),
3988
+ h('option', { value: 'eu-central-1' }, 'EU (Frankfurt)'),
3989
+ h('option', { value: 'ap-southeast-1' }, 'Asia Pacific (Singapore)'),
3990
+ h('option', { value: 'ap-northeast-1' }, 'Asia Pacific (Tokyo)'),
3991
+ h('option', { value: 'ap-south-1' }, 'Asia Pacific (Mumbai)'),
3992
+ h('option', { value: 'sa-east-1' }, 'South America (São Paulo)'),
3993
+ h('option', { value: 'af-south-1' }, 'Africa (Cape Town)')
3994
+ )
3995
+ ),
3996
+ h('div', { className: 'form-group' },
3997
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Instance Type'),
3998
+ h('select', { className: 'input', value: deployForm.awsInstanceType, onChange: function(e) { setDf('awsInstanceType', e.target.value); } },
3999
+ h('option', { value: 't3.micro' }, 't3.micro (1 vCPU, 1GB) — ~$7.59/mo'),
4000
+ h('option', { value: 't3.small' }, 't3.small (2 vCPU, 2GB) — ~$15.18/mo'),
4001
+ h('option', { value: 't3.medium' }, 't3.medium (2 vCPU, 4GB) — ~$30.37/mo'),
4002
+ h('option', { value: 't3.large' }, 't3.large (2 vCPU, 8GB) — ~$60.74/mo'),
4003
+ h('option', { value: 'm5.large' }, 'm5.large (2 vCPU, 8GB) — ~$69.12/mo'),
4004
+ h('option', { value: 'm5.xlarge' }, 'm5.xlarge (4 vCPU, 16GB) — ~$138.24/mo'),
4005
+ h('option', { value: 'c5.large' }, 'c5.large (2 vCPU, 4GB) — ~$61.20/mo'),
4006
+ h('option', { value: 'c5.xlarge' }, 'c5.xlarge (4 vCPU, 8GB) — ~$122.40/mo')
4007
+ )
4008
+ ),
4009
+ h('div', { className: 'form-group' },
4010
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Key Pair Name'),
4011
+ h('input', { className: 'input', value: deployForm.awsKeyPairName, onChange: function(e) { setDf('awsKeyPairName', e.target.value); }, placeholder: 'my-keypair' })
4012
+ )
4013
+ ),
4014
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginTop: 12 } },
4015
+ h('div', { className: 'form-group' },
4016
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'AMI ID (optional)'),
4017
+ h('input', { className: 'input', value: deployForm.awsAmi, onChange: function(e) { setDf('awsAmi', e.target.value); }, placeholder: 'ami-... (default: Ubuntu 22.04)' })
4018
+ ),
4019
+ h('div', { className: 'form-group' },
4020
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Subnet ID (optional)'),
4021
+ h('input', { className: 'input', value: deployForm.awsSubnetId, onChange: function(e) { setDf('awsSubnetId', e.target.value); }, placeholder: 'subnet-...' })
4022
+ ),
4023
+ h('div', { className: 'form-group' },
4024
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Security Group (optional)'),
4025
+ h('input', { className: 'input', value: deployForm.awsSecurityGroupId, onChange: function(e) { setDf('awsSecurityGroupId', e.target.value); }, placeholder: 'sg-...' })
4026
+ )
4027
+ )
4028
+ ),
4029
+
4030
+ // ── Google Cloud GCE ────────────────────────────────
4031
+ deployForm.target === 'gcp' && h(Fragment, null,
4032
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
4033
+ h('div', { className: 'form-group' },
4034
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Project ID'),
4035
+ h('input', { className: 'input', value: deployForm.gcpProject, onChange: function(e) { setDf('gcpProject', e.target.value); }, placeholder: 'my-project-123' })
4036
+ ),
4037
+ h('div', { className: 'form-group' },
4038
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Service Account Key (JSON)'),
4039
+ h('input', { className: 'input', type: 'password', value: deployForm.gcpServiceAccountKey, onChange: function(e) { setDf('gcpServiceAccountKey', e.target.value); }, placeholder: 'Paste JSON key or path' })
4040
+ )
4041
+ ),
4042
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginTop: 12 } },
4043
+ h('div', { className: 'form-group' },
4044
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Region'),
4045
+ h('select', { className: 'input', value: deployForm.gcpRegion, onChange: function(e) { setDf('gcpRegion', e.target.value); } },
4046
+ h('option', { value: 'us-central1' }, 'US Central (Iowa)'),
4047
+ h('option', { value: 'us-east1' }, 'US East (S. Carolina)'),
4048
+ h('option', { value: 'us-west1' }, 'US West (Oregon)'),
4049
+ h('option', { value: 'europe-west1' }, 'EU West (Belgium)'),
4050
+ h('option', { value: 'europe-west2' }, 'EU West (London)'),
4051
+ h('option', { value: 'europe-west3' }, 'EU West (Frankfurt)'),
4052
+ h('option', { value: 'asia-east1' }, 'Asia East (Taiwan)'),
4053
+ h('option', { value: 'asia-northeast1' }, 'Asia NE (Tokyo)'),
4054
+ h('option', { value: 'asia-southeast1' }, 'Asia SE (Singapore)'),
4055
+ h('option', { value: 'australia-southeast1' }, 'Australia (Sydney)'),
4056
+ h('option', { value: 'southamerica-east1' }, 'South America (São Paulo)')
4057
+ )
4058
+ ),
4059
+ h('div', { className: 'form-group' },
4060
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Zone'),
4061
+ h('input', { className: 'input', value: deployForm.gcpZone, onChange: function(e) { setDf('gcpZone', e.target.value); }, placeholder: 'us-central1-a' })
4062
+ ),
4063
+ h('div', { className: 'form-group' },
4064
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Machine Type'),
4065
+ h('select', { className: 'input', value: deployForm.gcpMachineType, onChange: function(e) { setDf('gcpMachineType', e.target.value); } },
4066
+ h('option', { value: 'e2-micro' }, 'e2-micro (0.25 vCPU, 1GB) — ~$6.11/mo'),
4067
+ h('option', { value: 'e2-small' }, 'e2-small (0.5 vCPU, 2GB) — ~$12.23/mo'),
4068
+ h('option', { value: 'e2-medium' }, 'e2-medium (1 vCPU, 4GB) — ~$24.46/mo'),
4069
+ h('option', { value: 'e2-standard-2' }, 'e2-standard-2 (2 vCPU, 8GB) — ~$48.92/mo'),
4070
+ h('option', { value: 'e2-standard-4' }, 'e2-standard-4 (4 vCPU, 16GB) — ~$97.83/mo'),
4071
+ h('option', { value: 'n2-standard-2' }, 'n2-standard-2 (2 vCPU, 8GB) — ~$56.52/mo'),
4072
+ h('option', { value: 'c2-standard-4' }, 'c2-standard-4 (4 vCPU, 16GB) — ~$124.49/mo')
4073
+ )
4074
+ )
4075
+ )
4076
+ ),
4077
+
4078
+ // ── Microsoft Azure ─────────────────────────────────
4079
+ deployForm.target === 'azure' && h(Fragment, null,
4080
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } },
4081
+ h('div', { className: 'form-group' },
4082
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Subscription ID'),
4083
+ h('input', { className: 'input', value: deployForm.azureSubscriptionId, onChange: function(e) { setDf('azureSubscriptionId', e.target.value); }, placeholder: 'xxxxxxxx-xxxx-...' })
4084
+ ),
4085
+ h('div', { className: 'form-group' },
4086
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Resource Group'),
4087
+ h('input', { className: 'input', value: deployForm.azureResourceGroup, onChange: function(e) { setDf('azureResourceGroup', e.target.value); }, placeholder: 'my-resource-group' })
4088
+ ),
4089
+ h('div', { className: 'form-group' },
4090
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Region'),
4091
+ h('select', { className: 'input', value: deployForm.azureRegion, onChange: function(e) { setDf('azureRegion', e.target.value); } },
4092
+ h('option', { value: 'eastus' }, 'East US'),
4093
+ h('option', { value: 'eastus2' }, 'East US 2'),
4094
+ h('option', { value: 'westus2' }, 'West US 2'),
4095
+ h('option', { value: 'westus3' }, 'West US 3'),
4096
+ h('option', { value: 'centralus' }, 'Central US'),
4097
+ h('option', { value: 'northeurope' }, 'North Europe (Ireland)'),
4098
+ h('option', { value: 'westeurope' }, 'West Europe (Netherlands)'),
4099
+ h('option', { value: 'uksouth' }, 'UK South'),
4100
+ h('option', { value: 'germanywestcentral' }, 'Germany West Central'),
4101
+ h('option', { value: 'eastasia' }, 'East Asia (Hong Kong)'),
4102
+ h('option', { value: 'southeastasia' }, 'Southeast Asia (Singapore)'),
4103
+ h('option', { value: 'japaneast' }, 'Japan East'),
4104
+ h('option', { value: 'australiaeast' }, 'Australia East'),
4105
+ h('option', { value: 'brazilsouth' }, 'Brazil South'),
4106
+ h('option', { value: 'southafricanorth' }, 'South Africa North')
4107
+ )
4108
+ )
4109
+ ),
4110
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginTop: 12 } },
4111
+ h('div', { className: 'form-group' },
4112
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'VM Size'),
4113
+ h('select', { className: 'input', value: deployForm.azureVmSize, onChange: function(e) { setDf('azureVmSize', e.target.value); } },
4114
+ h('option', { value: 'Standard_B1s' }, 'B1s (1 vCPU, 1GB) — ~$7.59/mo'),
4115
+ h('option', { value: 'Standard_B1ms' }, 'B1ms (1 vCPU, 2GB) — ~$15.11/mo'),
4116
+ h('option', { value: 'Standard_B2s' }, 'B2s (2 vCPU, 4GB) — ~$30.37/mo'),
4117
+ h('option', { value: 'Standard_B2ms' }, 'B2ms (2 vCPU, 8GB) — ~$60.74/mo'),
4118
+ h('option', { value: 'Standard_D2s_v5' }, 'D2s v5 (2 vCPU, 8GB) — ~$70.08/mo'),
4119
+ h('option', { value: 'Standard_D4s_v5' }, 'D4s v5 (4 vCPU, 16GB) — ~$140.16/mo'),
4120
+ h('option', { value: 'Standard_F2s_v2' }, 'F2s v2 (2 vCPU, 4GB) — ~$61.25/mo'),
4121
+ h('option', { value: 'Standard_E2s_v5' }, 'E2s v5 (2 vCPU, 16GB) — ~$91.98/mo')
4122
+ )
4123
+ ),
4124
+ h('div', { className: 'form-group' },
4125
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Tenant ID (optional)'),
4126
+ h('input', { className: 'input', type: 'password', value: deployForm.azureTenantId, onChange: function(e) { setDf('azureTenantId', e.target.value); }, placeholder: 'For service principal auth' })
4127
+ )
4128
+ ),
4129
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginTop: 12 } },
4130
+ h('div', { className: 'form-group' },
4131
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Client ID (optional)'),
4132
+ h('input', { className: 'input', type: 'password', value: deployForm.azureClientId, onChange: function(e) { setDf('azureClientId', e.target.value); }, placeholder: 'App registration client ID' })
4133
+ ),
4134
+ h('div', { className: 'form-group' },
4135
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Client Secret (optional)'),
4136
+ h('input', { className: 'input', type: 'password', value: deployForm.azureClientSecret, onChange: function(e) { setDf('azureClientSecret', e.target.value); }, placeholder: '••••••••' })
4137
+ )
4138
+ )
4139
+ ),
4140
+
4141
+ // ── Railway ─────────────────────────────────────────
4142
+ deployForm.target === 'railway' && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } },
4143
+ h('div', { className: 'form-group' },
4144
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'API Token'),
4145
+ h('input', { className: 'input', type: 'password', value: deployForm.railwayApiToken, onChange: function(e) { setDf('railwayApiToken', e.target.value); }, placeholder: 'railway_...' }),
4146
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } }, 'From railway.app/account/tokens')
4147
+ ),
4148
+ h('div', { className: 'form-group' },
4149
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Project ID (optional)'),
4150
+ h('input', { className: 'input', value: deployForm.railwayProjectId, onChange: function(e) { setDf('railwayProjectId', e.target.value); }, placeholder: 'Auto-created if empty' })
4151
+ ),
4152
+ h('div', { className: 'form-group' },
4153
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Service Name'),
4154
+ h('input', { className: 'input', value: deployForm.railwayServiceName, onChange: function(e) { setDf('railwayServiceName', e.target.value); }, placeholder: 'agenticmail-agent' })
4155
+ )
4156
+ ),
4157
+
4158
+ // ── Docker ──────────────────────────────────────────
4159
+ deployForm.target === 'docker' && h(Fragment, null,
4160
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } },
4161
+ h('div', { className: 'form-group' },
4162
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Image'),
4163
+ h('input', { className: 'input', value: deployForm.dockerImage, onChange: function(e) { setDf('dockerImage', e.target.value); }, placeholder: 'agenticmail/agent' })
4164
+ ),
4165
+ h('div', { className: 'form-group' },
4166
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Tag'),
4167
+ h('input', { className: 'input', value: deployForm.dockerTag, onChange: function(e) { setDf('dockerTag', e.target.value); }, placeholder: 'latest' })
4168
+ ),
4169
+ h('div', { className: 'form-group' },
4170
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Ports'),
4171
+ h('input', { className: 'input', value: deployForm.dockerPorts, onChange: function(e) { setDf('dockerPorts', e.target.value); }, placeholder: '3000' })
4172
+ )
4173
+ ),
4174
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16, marginTop: 12 } },
4175
+ h('div', { className: 'form-group' },
4176
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Memory'),
4177
+ h('input', { className: 'input', value: deployForm.dockerMemory, onChange: function(e) { setDf('dockerMemory', e.target.value); }, placeholder: '512m' })
4178
+ ),
4179
+ h('div', { className: 'form-group' },
4180
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'CPU'),
4181
+ h('input', { className: 'input', value: deployForm.dockerCpu, onChange: function(e) { setDf('dockerCpu', e.target.value); }, placeholder: '0.5' })
4182
+ ),
4183
+ h('div', { className: 'form-group' },
4184
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Network'),
4185
+ h('input', { className: 'input', value: deployForm.dockerNetwork, onChange: function(e) { setDf('dockerNetwork', e.target.value); }, placeholder: 'bridge (default)' })
4186
+ ),
4187
+ h('div', { className: 'form-group' },
4188
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Restart Policy'),
4189
+ h('select', { className: 'input', value: deployForm.dockerRestart, onChange: function(e) { setDf('dockerRestart', e.target.value); } },
4190
+ h('option', { value: 'unless-stopped' }, 'Unless Stopped'),
4191
+ h('option', { value: 'always' }, 'Always'),
4192
+ h('option', { value: 'on-failure' }, 'On Failure'),
4193
+ h('option', { value: 'no' }, 'Never')
4194
+ )
3861
4195
  )
3862
4196
  )
3863
4197
  ),
3864
- (deployForm.target === 'docker' || deployForm.target === 'fly') && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16 } },
4198
+
4199
+ // ── VPS / Bare Metal ────────────────────────────────
4200
+ deployForm.target === 'vps' && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } },
4201
+ h('div', { className: 'form-group' },
4202
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Host'),
4203
+ h('input', { className: 'input', value: deployForm.vpsHost, onChange: function(e) { setDf('vpsHost', e.target.value); }, placeholder: '192.168.1.100 or hostname' })
4204
+ ),
3865
4205
  h('div', { className: 'form-group' },
3866
- h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Image Tag'),
3867
- h('input', { className: 'input', value: deployForm.imageTag, onChange: function(e) { setDf('imageTag', e.target.value); } })
4206
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'SSH Port'),
4207
+ h('input', { className: 'input', type: 'number', value: deployForm.vpsPort, onChange: function(e) { setDf('vpsPort', e.target.value); }, placeholder: '22' })
3868
4208
  ),
3869
4209
  h('div', { className: 'form-group' },
3870
- h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Memory'),
3871
- h('input', { className: 'input', value: deployForm.memory, onChange: function(e) { setDf('memory', e.target.value); }, placeholder: '512m' })
4210
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'User'),
4211
+ h('input', { className: 'input', value: deployForm.vpsUser, onChange: function(e) { setDf('vpsUser', e.target.value); }, placeholder: 'root' })
3872
4212
  ),
3873
4213
  h('div', { className: 'form-group' },
3874
- h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'CPU'),
3875
- h('input', { className: 'input', value: deployForm.cpu, onChange: function(e) { setDf('cpu', e.target.value); }, placeholder: '0.5' })
4214
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'SSH Key Path'),
4215
+ h('input', { className: 'input', value: deployForm.vpsKeyPath, onChange: function(e) { setDf('vpsKeyPath', e.target.value); }, placeholder: '~/.ssh/id_rsa' })
3876
4216
  ),
3877
4217
  h('div', { className: 'form-group' },
3878
- h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Ports'),
3879
- h('input', { className: 'input', value: deployForm.ports, onChange: function(e) { setDf('ports', e.target.value); }, placeholder: '3000' })
4218
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Work Directory'),
4219
+ h('input', { className: 'input', value: deployForm.vpsWorkDir, onChange: function(e) { setDf('vpsWorkDir', e.target.value); }, placeholder: '/opt/agenticmail' })
3880
4220
  )
4221
+ ),
4222
+
4223
+ // ── Local ───────────────────────────────────────────
4224
+ deployForm.target === 'local' && h('div', { style: { padding: 16, background: 'var(--bg-tertiary)', borderRadius: 8, fontSize: 13, color: 'var(--text-muted)' } },
4225
+ 'Agent will run in-process on this server. No external deployment required. Best for development and testing.'
3881
4226
  )
3882
4227
  )
3883
4228
  ),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.93",
3
+ "version": "0.5.95",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3745,36 +3745,91 @@ function DeploymentSection(props) {
3745
3745
  var deployForm = _deployForm[0]; var setDeployForm = _deployForm[1];
3746
3746
 
3747
3747
  var startDeployEdit = function() {
3748
+ var cloud = deployment.config?.cloud || {};
3749
+ var docker = deployment.config?.docker || {};
3750
+ var vps = deployment.config?.vps || {};
3751
+ var aws = deployment.config?.aws || {};
3752
+ var gcp = deployment.config?.gcp || {};
3753
+ var az = deployment.config?.azure || {};
3754
+ var rail = deployment.config?.railway || {};
3748
3755
  setDeployForm({
3749
- target: deployment.target || 'docker',
3750
- region: deployment.region || 'iad',
3751
- imageTag: (deployment.config?.docker?.tag) || deployment.imageTag || 'latest',
3752
- memory: (deployment.config?.docker?.memory) || deployment.memory || '512m',
3753
- cpu: (deployment.config?.docker?.cpu) || deployment.cpu || '0.5',
3754
- ports: (deployment.config?.docker?.ports || [3000]).join(', '),
3756
+ target: deployment.target || 'fly',
3757
+ region: deployment.region || cloud.region || 'iad',
3758
+ // Fly.io
3759
+ flyApiToken: cloud.apiToken || '',
3760
+ flyAppName: cloud.appName || '',
3761
+ flyOrg: cloud.org || 'personal',
3762
+ flyVmSize: cloud.vmSize || 'shared-cpu-1x',
3763
+ flyVmMemory: cloud.vmMemory || '256',
3764
+ // Docker
3765
+ dockerImage: docker.image || 'agenticmail/agent',
3766
+ dockerTag: docker.tag || 'latest',
3767
+ dockerMemory: docker.memory || '512m',
3768
+ dockerCpu: docker.cpu || '0.5',
3769
+ dockerPorts: (docker.ports || [3000]).join(', '),
3770
+ dockerNetwork: docker.network || '',
3771
+ dockerRestart: docker.restart || 'unless-stopped',
3772
+ // VPS
3773
+ vpsHost: vps.host || '',
3774
+ vpsPort: vps.port || '22',
3775
+ vpsUser: vps.user || 'root',
3776
+ vpsKeyPath: vps.keyPath || '~/.ssh/id_rsa',
3777
+ vpsWorkDir: vps.workDir || '/opt/agenticmail',
3778
+ // AWS
3779
+ awsRegion: aws.region || 'us-east-1',
3780
+ awsAccessKeyId: aws.accessKeyId || '',
3781
+ awsSecretAccessKey: aws.secretAccessKey || '',
3782
+ awsInstanceType: aws.instanceType || 't3.micro',
3783
+ awsAmi: aws.ami || '',
3784
+ awsSubnetId: aws.subnetId || '',
3785
+ awsSecurityGroupId: aws.securityGroupId || '',
3786
+ awsKeyPairName: aws.keyPairName || '',
3787
+ // GCP
3788
+ gcpProject: gcp.projectId || '',
3789
+ gcpRegion: gcp.region || 'us-central1',
3790
+ gcpZone: gcp.zone || 'us-central1-a',
3791
+ gcpMachineType: gcp.machineType || 'e2-micro',
3792
+ gcpServiceAccountKey: gcp.serviceAccountKey || '',
3793
+ // Azure
3794
+ azureSubscriptionId: az.subscriptionId || '',
3795
+ azureResourceGroup: az.resourceGroup || '',
3796
+ azureRegion: az.region || 'eastus',
3797
+ azureVmSize: az.vmSize || 'Standard_B1s',
3798
+ azureTenantId: az.tenantId || '',
3799
+ azureClientId: az.clientId || '',
3800
+ azureClientSecret: az.clientSecret || '',
3801
+ // Railway
3802
+ railwayApiToken: rail.apiToken || '',
3803
+ railwayProjectId: rail.projectId || '',
3804
+ railwayServiceName: rail.serviceName || '',
3755
3805
  });
3756
3806
  setEditingDeploy(true);
3757
3807
  };
3758
3808
 
3759
3809
  var saveDeploy = function() {
3760
3810
  setSavingDeploy(true);
3811
+ var t = deployForm.target;
3812
+ var deployConfig = {};
3813
+ if (t === 'fly') {
3814
+ deployConfig = { cloud: { provider: 'fly', region: deployForm.region || 'iad', apiToken: deployForm.flyApiToken || undefined, appName: deployForm.flyAppName || undefined, org: deployForm.flyOrg || 'personal', vmSize: deployForm.flyVmSize || 'shared-cpu-1x', vmMemory: deployForm.flyVmMemory || '256' } };
3815
+ } else if (t === 'docker') {
3816
+ deployConfig = { docker: { image: deployForm.dockerImage || 'agenticmail/agent', tag: deployForm.dockerTag || 'latest', ports: (deployForm.dockerPorts || '3000').split(',').map(function(p) { return parseInt(p.trim()) || 3000; }), memory: deployForm.dockerMemory || '512m', cpu: deployForm.dockerCpu || '0.5', network: deployForm.dockerNetwork || undefined, restart: deployForm.dockerRestart || 'unless-stopped' } };
3817
+ } else if (t === 'vps') {
3818
+ deployConfig = { vps: { host: deployForm.vpsHost, port: parseInt(deployForm.vpsPort) || 22, user: deployForm.vpsUser || 'root', keyPath: deployForm.vpsKeyPath || '~/.ssh/id_rsa', workDir: deployForm.vpsWorkDir || '/opt/agenticmail' } };
3819
+ } else if (t === 'aws') {
3820
+ deployConfig = { aws: { region: deployForm.awsRegion || 'us-east-1', accessKeyId: deployForm.awsAccessKeyId || undefined, secretAccessKey: deployForm.awsSecretAccessKey || undefined, instanceType: deployForm.awsInstanceType || 't3.micro', ami: deployForm.awsAmi || undefined, subnetId: deployForm.awsSubnetId || undefined, securityGroupId: deployForm.awsSecurityGroupId || undefined, keyPairName: deployForm.awsKeyPairName || undefined } };
3821
+ } else if (t === 'gcp') {
3822
+ deployConfig = { gcp: { projectId: deployForm.gcpProject, region: deployForm.gcpRegion || 'us-central1', zone: deployForm.gcpZone || 'us-central1-a', machineType: deployForm.gcpMachineType || 'e2-micro', serviceAccountKey: deployForm.gcpServiceAccountKey || undefined } };
3823
+ } else if (t === 'azure') {
3824
+ deployConfig = { azure: { subscriptionId: deployForm.azureSubscriptionId, resourceGroup: deployForm.azureResourceGroup, region: deployForm.azureRegion || 'eastus', vmSize: deployForm.azureVmSize || 'Standard_B1s', tenantId: deployForm.azureTenantId || undefined, clientId: deployForm.azureClientId || undefined, clientSecret: deployForm.azureClientSecret || undefined } };
3825
+ } else if (t === 'railway') {
3826
+ deployConfig = { railway: { apiToken: deployForm.railwayApiToken || undefined, projectId: deployForm.railwayProjectId || undefined, serviceName: deployForm.railwayServiceName || undefined, region: deployForm.region || undefined } };
3827
+ }
3761
3828
  var updates = {
3762
3829
  deployment: {
3763
- target: deployForm.target,
3830
+ target: t,
3764
3831
  region: deployForm.region,
3765
- imageTag: deployForm.imageTag,
3766
- memory: deployForm.memory,
3767
- cpu: deployForm.cpu,
3768
- config: {
3769
- docker: {
3770
- image: 'agenticmail/agent',
3771
- tag: deployForm.imageTag,
3772
- ports: deployForm.ports.split(',').map(function(p) { return parseInt(p.trim()) || 3000; }),
3773
- memory: deployForm.memory,
3774
- cpu: deployForm.cpu,
3775
- restart: 'unless-stopped',
3776
- }
3777
- }
3832
+ config: deployConfig
3778
3833
  }
3779
3834
  };
3780
3835
  var isRunning = ea.state === 'running' || ea.state === 'active' || ea.state === 'degraded';
@@ -3843,41 +3898,331 @@ function DeploymentSection(props) {
3843
3898
  h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Target'),
3844
3899
  h('select', { className: 'input', value: deployForm.target, onChange: function(e) { setDf('target', e.target.value); } },
3845
3900
  h('option', { value: 'fly' }, 'Fly.io'),
3846
- h('option', { value: 'docker' }, 'Docker'),
3901
+ h('option', { value: 'aws' }, 'AWS (EC2)'),
3902
+ h('option', { value: 'gcp' }, 'Google Cloud (GCE)'),
3903
+ h('option', { value: 'azure' }, 'Microsoft Azure'),
3847
3904
  h('option', { value: 'railway' }, 'Railway'),
3848
- h('option', { value: 'vps' }, 'VPS / Server'),
3849
- h('option', { value: 'local' }, 'Local')
3905
+ h('option', { value: 'docker' }, 'Docker'),
3906
+ h('option', { value: 'vps' }, 'VPS / Bare Metal'),
3907
+ h('option', { value: 'local' }, 'Local (In-Process)')
3850
3908
  )
3851
3909
  ),
3910
+ // Region selector for cloud providers
3852
3911
  (deployForm.target === 'fly' || deployForm.target === 'railway') && h('div', { className: 'form-group' },
3853
3912
  h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Region'),
3854
3913
  h('select', { className: 'input', value: deployForm.region, onChange: function(e) { setDf('region', e.target.value); } },
3855
- h('option', { value: 'iad' }, 'Ashburn (iad)'),
3856
- h('option', { value: 'ord' }, 'Chicago (ord)'),
3857
- h('option', { value: 'lax' }, 'Los Angeles (lax)'),
3914
+ h('option', { value: 'iad' }, 'Ashburn, VA (iad)'),
3915
+ h('option', { value: 'ord' }, 'Chicago, IL (ord)'),
3916
+ h('option', { value: 'dfw' }, 'Dallas, TX (dfw)'),
3917
+ h('option', { value: 'lax' }, 'Los Angeles, CA (lax)'),
3918
+ h('option', { value: 'sea' }, 'Seattle, WA (sea)'),
3919
+ h('option', { value: 'sjc' }, 'San Jose, CA (sjc)'),
3920
+ h('option', { value: 'yyz' }, 'Toronto (yyz)'),
3858
3921
  h('option', { value: 'lhr' }, 'London (lhr)'),
3859
3922
  h('option', { value: 'ams' }, 'Amsterdam (ams)'),
3860
- h('option', { value: 'nrt' }, 'Tokyo (nrt)')
3923
+ h('option', { value: 'fra' }, 'Frankfurt (fra)'),
3924
+ h('option', { value: 'cdg' }, 'Paris (cdg)'),
3925
+ h('option', { value: 'waw' }, 'Warsaw (waw)'),
3926
+ h('option', { value: 'nrt' }, 'Tokyo (nrt)'),
3927
+ h('option', { value: 'sin' }, 'Singapore (sin)'),
3928
+ h('option', { value: 'hkg' }, 'Hong Kong (hkg)'),
3929
+ h('option', { value: 'syd' }, 'Sydney (syd)'),
3930
+ h('option', { value: 'gru' }, 'São Paulo (gru)'),
3931
+ h('option', { value: 'jnb' }, 'Johannesburg (jnb)')
3932
+ )
3933
+ )
3934
+ ),
3935
+
3936
+ // ── Fly.io ──────────────────────────────────────────
3937
+ deployForm.target === 'fly' && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
3938
+ h('div', { className: 'form-group' },
3939
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'API Token'),
3940
+ h('input', { className: 'input', type: 'password', value: deployForm.flyApiToken, onChange: function(e) { setDf('flyApiToken', e.target.value); }, placeholder: 'fo1_...' }),
3941
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } }, 'From fly.io/user/personal_access_tokens')
3942
+ ),
3943
+ h('div', { className: 'form-group' },
3944
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'App Name'),
3945
+ h('input', { className: 'input', value: deployForm.flyAppName, onChange: function(e) { setDf('flyAppName', e.target.value); }, placeholder: 'Auto-generated if empty' })
3946
+ ),
3947
+ h('div', { className: 'form-group' },
3948
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Organization'),
3949
+ h('input', { className: 'input', value: deployForm.flyOrg, onChange: function(e) { setDf('flyOrg', e.target.value); }, placeholder: 'personal' })
3950
+ ),
3951
+ h('div', { className: 'form-group' },
3952
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'VM Size'),
3953
+ h('select', { className: 'input', value: deployForm.flyVmSize, onChange: function(e) { setDf('flyVmSize', e.target.value); } },
3954
+ h('option', { value: 'shared-cpu-1x' }, 'Shared 1x (256MB) — $1.94/mo'),
3955
+ h('option', { value: 'shared-cpu-2x' }, 'Shared 2x (512MB) — $3.88/mo'),
3956
+ h('option', { value: 'shared-cpu-4x' }, 'Shared 4x (1GB) — $7.76/mo'),
3957
+ h('option', { value: 'shared-cpu-8x' }, 'Shared 8x (2GB) — $15.52/mo'),
3958
+ h('option', { value: 'performance-1x' }, 'Performance 1x (2GB) — $29.04/mo'),
3959
+ h('option', { value: 'performance-2x' }, 'Performance 2x (4GB) — $58.09/mo'),
3960
+ h('option', { value: 'performance-4x' }, 'Performance 4x (8GB) — $116.18/mo'),
3961
+ h('option', { value: 'performance-8x' }, 'Performance 8x (16GB) — $232.36/mo')
3962
+ )
3963
+ )
3964
+ ),
3965
+
3966
+ // ── AWS EC2 ─────────────────────────────────────────
3967
+ deployForm.target === 'aws' && h(Fragment, null,
3968
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
3969
+ h('div', { className: 'form-group' },
3970
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Access Key ID'),
3971
+ h('input', { className: 'input', type: 'password', value: deployForm.awsAccessKeyId, onChange: function(e) { setDf('awsAccessKeyId', e.target.value); }, placeholder: 'AKIA...' })
3972
+ ),
3973
+ h('div', { className: 'form-group' },
3974
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Secret Access Key'),
3975
+ h('input', { className: 'input', type: 'password', value: deployForm.awsSecretAccessKey, onChange: function(e) { setDf('awsSecretAccessKey', e.target.value); }, placeholder: '••••••••' })
3976
+ )
3977
+ ),
3978
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginTop: 12 } },
3979
+ h('div', { className: 'form-group' },
3980
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Region'),
3981
+ h('select', { className: 'input', value: deployForm.awsRegion, onChange: function(e) { setDf('awsRegion', e.target.value); } },
3982
+ h('option', { value: 'us-east-1' }, 'US East (N. Virginia)'),
3983
+ h('option', { value: 'us-east-2' }, 'US East (Ohio)'),
3984
+ h('option', { value: 'us-west-1' }, 'US West (N. California)'),
3985
+ h('option', { value: 'us-west-2' }, 'US West (Oregon)'),
3986
+ h('option', { value: 'eu-west-1' }, 'EU (Ireland)'),
3987
+ h('option', { value: 'eu-west-2' }, 'EU (London)'),
3988
+ h('option', { value: 'eu-central-1' }, 'EU (Frankfurt)'),
3989
+ h('option', { value: 'ap-southeast-1' }, 'Asia Pacific (Singapore)'),
3990
+ h('option', { value: 'ap-northeast-1' }, 'Asia Pacific (Tokyo)'),
3991
+ h('option', { value: 'ap-south-1' }, 'Asia Pacific (Mumbai)'),
3992
+ h('option', { value: 'sa-east-1' }, 'South America (São Paulo)'),
3993
+ h('option', { value: 'af-south-1' }, 'Africa (Cape Town)')
3994
+ )
3995
+ ),
3996
+ h('div', { className: 'form-group' },
3997
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Instance Type'),
3998
+ h('select', { className: 'input', value: deployForm.awsInstanceType, onChange: function(e) { setDf('awsInstanceType', e.target.value); } },
3999
+ h('option', { value: 't3.micro' }, 't3.micro (1 vCPU, 1GB) — ~$7.59/mo'),
4000
+ h('option', { value: 't3.small' }, 't3.small (2 vCPU, 2GB) — ~$15.18/mo'),
4001
+ h('option', { value: 't3.medium' }, 't3.medium (2 vCPU, 4GB) — ~$30.37/mo'),
4002
+ h('option', { value: 't3.large' }, 't3.large (2 vCPU, 8GB) — ~$60.74/mo'),
4003
+ h('option', { value: 'm5.large' }, 'm5.large (2 vCPU, 8GB) — ~$69.12/mo'),
4004
+ h('option', { value: 'm5.xlarge' }, 'm5.xlarge (4 vCPU, 16GB) — ~$138.24/mo'),
4005
+ h('option', { value: 'c5.large' }, 'c5.large (2 vCPU, 4GB) — ~$61.20/mo'),
4006
+ h('option', { value: 'c5.xlarge' }, 'c5.xlarge (4 vCPU, 8GB) — ~$122.40/mo')
4007
+ )
4008
+ ),
4009
+ h('div', { className: 'form-group' },
4010
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Key Pair Name'),
4011
+ h('input', { className: 'input', value: deployForm.awsKeyPairName, onChange: function(e) { setDf('awsKeyPairName', e.target.value); }, placeholder: 'my-keypair' })
4012
+ )
4013
+ ),
4014
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginTop: 12 } },
4015
+ h('div', { className: 'form-group' },
4016
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'AMI ID (optional)'),
4017
+ h('input', { className: 'input', value: deployForm.awsAmi, onChange: function(e) { setDf('awsAmi', e.target.value); }, placeholder: 'ami-... (default: Ubuntu 22.04)' })
4018
+ ),
4019
+ h('div', { className: 'form-group' },
4020
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Subnet ID (optional)'),
4021
+ h('input', { className: 'input', value: deployForm.awsSubnetId, onChange: function(e) { setDf('awsSubnetId', e.target.value); }, placeholder: 'subnet-...' })
4022
+ ),
4023
+ h('div', { className: 'form-group' },
4024
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Security Group (optional)'),
4025
+ h('input', { className: 'input', value: deployForm.awsSecurityGroupId, onChange: function(e) { setDf('awsSecurityGroupId', e.target.value); }, placeholder: 'sg-...' })
4026
+ )
4027
+ )
4028
+ ),
4029
+
4030
+ // ── Google Cloud GCE ────────────────────────────────
4031
+ deployForm.target === 'gcp' && h(Fragment, null,
4032
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 } },
4033
+ h('div', { className: 'form-group' },
4034
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Project ID'),
4035
+ h('input', { className: 'input', value: deployForm.gcpProject, onChange: function(e) { setDf('gcpProject', e.target.value); }, placeholder: 'my-project-123' })
4036
+ ),
4037
+ h('div', { className: 'form-group' },
4038
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Service Account Key (JSON)'),
4039
+ h('input', { className: 'input', type: 'password', value: deployForm.gcpServiceAccountKey, onChange: function(e) { setDf('gcpServiceAccountKey', e.target.value); }, placeholder: 'Paste JSON key or path' })
4040
+ )
4041
+ ),
4042
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16, marginTop: 12 } },
4043
+ h('div', { className: 'form-group' },
4044
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Region'),
4045
+ h('select', { className: 'input', value: deployForm.gcpRegion, onChange: function(e) { setDf('gcpRegion', e.target.value); } },
4046
+ h('option', { value: 'us-central1' }, 'US Central (Iowa)'),
4047
+ h('option', { value: 'us-east1' }, 'US East (S. Carolina)'),
4048
+ h('option', { value: 'us-west1' }, 'US West (Oregon)'),
4049
+ h('option', { value: 'europe-west1' }, 'EU West (Belgium)'),
4050
+ h('option', { value: 'europe-west2' }, 'EU West (London)'),
4051
+ h('option', { value: 'europe-west3' }, 'EU West (Frankfurt)'),
4052
+ h('option', { value: 'asia-east1' }, 'Asia East (Taiwan)'),
4053
+ h('option', { value: 'asia-northeast1' }, 'Asia NE (Tokyo)'),
4054
+ h('option', { value: 'asia-southeast1' }, 'Asia SE (Singapore)'),
4055
+ h('option', { value: 'australia-southeast1' }, 'Australia (Sydney)'),
4056
+ h('option', { value: 'southamerica-east1' }, 'South America (São Paulo)')
4057
+ )
4058
+ ),
4059
+ h('div', { className: 'form-group' },
4060
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Zone'),
4061
+ h('input', { className: 'input', value: deployForm.gcpZone, onChange: function(e) { setDf('gcpZone', e.target.value); }, placeholder: 'us-central1-a' })
4062
+ ),
4063
+ h('div', { className: 'form-group' },
4064
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Machine Type'),
4065
+ h('select', { className: 'input', value: deployForm.gcpMachineType, onChange: function(e) { setDf('gcpMachineType', e.target.value); } },
4066
+ h('option', { value: 'e2-micro' }, 'e2-micro (0.25 vCPU, 1GB) — ~$6.11/mo'),
4067
+ h('option', { value: 'e2-small' }, 'e2-small (0.5 vCPU, 2GB) — ~$12.23/mo'),
4068
+ h('option', { value: 'e2-medium' }, 'e2-medium (1 vCPU, 4GB) — ~$24.46/mo'),
4069
+ h('option', { value: 'e2-standard-2' }, 'e2-standard-2 (2 vCPU, 8GB) — ~$48.92/mo'),
4070
+ h('option', { value: 'e2-standard-4' }, 'e2-standard-4 (4 vCPU, 16GB) — ~$97.83/mo'),
4071
+ h('option', { value: 'n2-standard-2' }, 'n2-standard-2 (2 vCPU, 8GB) — ~$56.52/mo'),
4072
+ h('option', { value: 'c2-standard-4' }, 'c2-standard-4 (4 vCPU, 16GB) — ~$124.49/mo')
4073
+ )
4074
+ )
4075
+ )
4076
+ ),
4077
+
4078
+ // ── Microsoft Azure ─────────────────────────────────
4079
+ deployForm.target === 'azure' && h(Fragment, null,
4080
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } },
4081
+ h('div', { className: 'form-group' },
4082
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Subscription ID'),
4083
+ h('input', { className: 'input', value: deployForm.azureSubscriptionId, onChange: function(e) { setDf('azureSubscriptionId', e.target.value); }, placeholder: 'xxxxxxxx-xxxx-...' })
4084
+ ),
4085
+ h('div', { className: 'form-group' },
4086
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Resource Group'),
4087
+ h('input', { className: 'input', value: deployForm.azureResourceGroup, onChange: function(e) { setDf('azureResourceGroup', e.target.value); }, placeholder: 'my-resource-group' })
4088
+ ),
4089
+ h('div', { className: 'form-group' },
4090
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Region'),
4091
+ h('select', { className: 'input', value: deployForm.azureRegion, onChange: function(e) { setDf('azureRegion', e.target.value); } },
4092
+ h('option', { value: 'eastus' }, 'East US'),
4093
+ h('option', { value: 'eastus2' }, 'East US 2'),
4094
+ h('option', { value: 'westus2' }, 'West US 2'),
4095
+ h('option', { value: 'westus3' }, 'West US 3'),
4096
+ h('option', { value: 'centralus' }, 'Central US'),
4097
+ h('option', { value: 'northeurope' }, 'North Europe (Ireland)'),
4098
+ h('option', { value: 'westeurope' }, 'West Europe (Netherlands)'),
4099
+ h('option', { value: 'uksouth' }, 'UK South'),
4100
+ h('option', { value: 'germanywestcentral' }, 'Germany West Central'),
4101
+ h('option', { value: 'eastasia' }, 'East Asia (Hong Kong)'),
4102
+ h('option', { value: 'southeastasia' }, 'Southeast Asia (Singapore)'),
4103
+ h('option', { value: 'japaneast' }, 'Japan East'),
4104
+ h('option', { value: 'australiaeast' }, 'Australia East'),
4105
+ h('option', { value: 'brazilsouth' }, 'Brazil South'),
4106
+ h('option', { value: 'southafricanorth' }, 'South Africa North')
4107
+ )
4108
+ )
4109
+ ),
4110
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginTop: 12 } },
4111
+ h('div', { className: 'form-group' },
4112
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'VM Size'),
4113
+ h('select', { className: 'input', value: deployForm.azureVmSize, onChange: function(e) { setDf('azureVmSize', e.target.value); } },
4114
+ h('option', { value: 'Standard_B1s' }, 'B1s (1 vCPU, 1GB) — ~$7.59/mo'),
4115
+ h('option', { value: 'Standard_B1ms' }, 'B1ms (1 vCPU, 2GB) — ~$15.11/mo'),
4116
+ h('option', { value: 'Standard_B2s' }, 'B2s (2 vCPU, 4GB) — ~$30.37/mo'),
4117
+ h('option', { value: 'Standard_B2ms' }, 'B2ms (2 vCPU, 8GB) — ~$60.74/mo'),
4118
+ h('option', { value: 'Standard_D2s_v5' }, 'D2s v5 (2 vCPU, 8GB) — ~$70.08/mo'),
4119
+ h('option', { value: 'Standard_D4s_v5' }, 'D4s v5 (4 vCPU, 16GB) — ~$140.16/mo'),
4120
+ h('option', { value: 'Standard_F2s_v2' }, 'F2s v2 (2 vCPU, 4GB) — ~$61.25/mo'),
4121
+ h('option', { value: 'Standard_E2s_v5' }, 'E2s v5 (2 vCPU, 16GB) — ~$91.98/mo')
4122
+ )
4123
+ ),
4124
+ h('div', { className: 'form-group' },
4125
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Tenant ID (optional)'),
4126
+ h('input', { className: 'input', type: 'password', value: deployForm.azureTenantId, onChange: function(e) { setDf('azureTenantId', e.target.value); }, placeholder: 'For service principal auth' })
4127
+ )
4128
+ ),
4129
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16, marginTop: 12 } },
4130
+ h('div', { className: 'form-group' },
4131
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Client ID (optional)'),
4132
+ h('input', { className: 'input', type: 'password', value: deployForm.azureClientId, onChange: function(e) { setDf('azureClientId', e.target.value); }, placeholder: 'App registration client ID' })
4133
+ ),
4134
+ h('div', { className: 'form-group' },
4135
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Client Secret (optional)'),
4136
+ h('input', { className: 'input', type: 'password', value: deployForm.azureClientSecret, onChange: function(e) { setDf('azureClientSecret', e.target.value); }, placeholder: '••••••••' })
4137
+ )
4138
+ )
4139
+ ),
4140
+
4141
+ // ── Railway ─────────────────────────────────────────
4142
+ deployForm.target === 'railway' && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } },
4143
+ h('div', { className: 'form-group' },
4144
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'API Token'),
4145
+ h('input', { className: 'input', type: 'password', value: deployForm.railwayApiToken, onChange: function(e) { setDf('railwayApiToken', e.target.value); }, placeholder: 'railway_...' }),
4146
+ h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 2 } }, 'From railway.app/account/tokens')
4147
+ ),
4148
+ h('div', { className: 'form-group' },
4149
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Project ID (optional)'),
4150
+ h('input', { className: 'input', value: deployForm.railwayProjectId, onChange: function(e) { setDf('railwayProjectId', e.target.value); }, placeholder: 'Auto-created if empty' })
4151
+ ),
4152
+ h('div', { className: 'form-group' },
4153
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Service Name'),
4154
+ h('input', { className: 'input', value: deployForm.railwayServiceName, onChange: function(e) { setDf('railwayServiceName', e.target.value); }, placeholder: 'agenticmail-agent' })
4155
+ )
4156
+ ),
4157
+
4158
+ // ── Docker ──────────────────────────────────────────
4159
+ deployForm.target === 'docker' && h(Fragment, null,
4160
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } },
4161
+ h('div', { className: 'form-group' },
4162
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Image'),
4163
+ h('input', { className: 'input', value: deployForm.dockerImage, onChange: function(e) { setDf('dockerImage', e.target.value); }, placeholder: 'agenticmail/agent' })
4164
+ ),
4165
+ h('div', { className: 'form-group' },
4166
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Tag'),
4167
+ h('input', { className: 'input', value: deployForm.dockerTag, onChange: function(e) { setDf('dockerTag', e.target.value); }, placeholder: 'latest' })
4168
+ ),
4169
+ h('div', { className: 'form-group' },
4170
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Ports'),
4171
+ h('input', { className: 'input', value: deployForm.dockerPorts, onChange: function(e) { setDf('dockerPorts', e.target.value); }, placeholder: '3000' })
4172
+ )
4173
+ ),
4174
+ h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16, marginTop: 12 } },
4175
+ h('div', { className: 'form-group' },
4176
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Memory'),
4177
+ h('input', { className: 'input', value: deployForm.dockerMemory, onChange: function(e) { setDf('dockerMemory', e.target.value); }, placeholder: '512m' })
4178
+ ),
4179
+ h('div', { className: 'form-group' },
4180
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'CPU'),
4181
+ h('input', { className: 'input', value: deployForm.dockerCpu, onChange: function(e) { setDf('dockerCpu', e.target.value); }, placeholder: '0.5' })
4182
+ ),
4183
+ h('div', { className: 'form-group' },
4184
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Network'),
4185
+ h('input', { className: 'input', value: deployForm.dockerNetwork, onChange: function(e) { setDf('dockerNetwork', e.target.value); }, placeholder: 'bridge (default)' })
4186
+ ),
4187
+ h('div', { className: 'form-group' },
4188
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Restart Policy'),
4189
+ h('select', { className: 'input', value: deployForm.dockerRestart, onChange: function(e) { setDf('dockerRestart', e.target.value); } },
4190
+ h('option', { value: 'unless-stopped' }, 'Unless Stopped'),
4191
+ h('option', { value: 'always' }, 'Always'),
4192
+ h('option', { value: 'on-failure' }, 'On Failure'),
4193
+ h('option', { value: 'no' }, 'Never')
4194
+ )
3861
4195
  )
3862
4196
  )
3863
4197
  ),
3864
- (deployForm.target === 'docker' || deployForm.target === 'fly') && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr 1fr', gap: 16 } },
4198
+
4199
+ // ── VPS / Bare Metal ────────────────────────────────
4200
+ deployForm.target === 'vps' && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 16 } },
4201
+ h('div', { className: 'form-group' },
4202
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Host'),
4203
+ h('input', { className: 'input', value: deployForm.vpsHost, onChange: function(e) { setDf('vpsHost', e.target.value); }, placeholder: '192.168.1.100 or hostname' })
4204
+ ),
3865
4205
  h('div', { className: 'form-group' },
3866
- h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Image Tag'),
3867
- h('input', { className: 'input', value: deployForm.imageTag, onChange: function(e) { setDf('imageTag', e.target.value); } })
4206
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'SSH Port'),
4207
+ h('input', { className: 'input', type: 'number', value: deployForm.vpsPort, onChange: function(e) { setDf('vpsPort', e.target.value); }, placeholder: '22' })
3868
4208
  ),
3869
4209
  h('div', { className: 'form-group' },
3870
- h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Memory'),
3871
- h('input', { className: 'input', value: deployForm.memory, onChange: function(e) { setDf('memory', e.target.value); }, placeholder: '512m' })
4210
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'User'),
4211
+ h('input', { className: 'input', value: deployForm.vpsUser, onChange: function(e) { setDf('vpsUser', e.target.value); }, placeholder: 'root' })
3872
4212
  ),
3873
4213
  h('div', { className: 'form-group' },
3874
- h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'CPU'),
3875
- h('input', { className: 'input', value: deployForm.cpu, onChange: function(e) { setDf('cpu', e.target.value); }, placeholder: '0.5' })
4214
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'SSH Key Path'),
4215
+ h('input', { className: 'input', value: deployForm.vpsKeyPath, onChange: function(e) { setDf('vpsKeyPath', e.target.value); }, placeholder: '~/.ssh/id_rsa' })
3876
4216
  ),
3877
4217
  h('div', { className: 'form-group' },
3878
- h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Ports'),
3879
- h('input', { className: 'input', value: deployForm.ports, onChange: function(e) { setDf('ports', e.target.value); }, placeholder: '3000' })
4218
+ h('label', { style: { display: 'block', fontSize: 12, fontWeight: 600, color: 'var(--text-secondary)', marginBottom: 4 } }, 'Work Directory'),
4219
+ h('input', { className: 'input', value: deployForm.vpsWorkDir, onChange: function(e) { setDf('vpsWorkDir', e.target.value); }, placeholder: '/opt/agenticmail' })
3880
4220
  )
4221
+ ),
4222
+
4223
+ // ── Local ───────────────────────────────────────────
4224
+ deployForm.target === 'local' && h('div', { style: { padding: 16, background: 'var(--bg-tertiary)', borderRadius: 8, fontSize: 13, color: 'var(--text-muted)' } },
4225
+ 'Agent will run in-process on this server. No external deployment required. Best for development and testing.'
3881
4226
  )
3882
4227
  )
3883
4228
  ),