@stacksjs/ts-cloud 0.7.21 → 0.7.23

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 (38) hide show
  1. package/dist/bin/cli.js +288 -286
  2. package/dist/{chunk-49nmy775.js → chunk-0tfwdnmf.js} +9 -6
  3. package/dist/{chunk-xgnxz5dz.js → chunk-5cfcv2s2.js} +115 -9
  4. package/dist/{chunk-qgnyzfmx.js → chunk-5mejsd43.js} +1 -1
  5. package/dist/{chunk-296n8hvt.js → chunk-c3yy2xyq.js} +2 -2
  6. package/dist/{chunk-eapb9et8.js → chunk-khfnaap2.js} +4 -4
  7. package/dist/deploy/dashboard-config-module.d.ts +40 -0
  8. package/dist/deploy/dashboard-config-module.test.d.ts +1 -0
  9. package/dist/deploy/index.js +4 -4
  10. package/dist/deploy/management-dashboard.d.ts +48 -15
  11. package/dist/drivers/index.js +2 -2
  12. package/dist/drivers/shared/deploy-script.d.ts +7 -0
  13. package/dist/index.js +8 -4
  14. package/dist/ui/index.html +3 -3
  15. package/dist/ui/server/actions.html +3 -3
  16. package/dist/ui/server/backups.html +3 -3
  17. package/dist/ui/server/database.html +3 -3
  18. package/dist/ui/server/deployments.html +3 -3
  19. package/dist/ui/server/firewall.html +3 -3
  20. package/dist/ui/server/logs.html +3 -3
  21. package/dist/ui/server/services.html +3 -3
  22. package/dist/ui/server/sites.html +3 -3
  23. package/dist/ui/server/ssh-keys.html +3 -3
  24. package/dist/ui/server/team.html +3 -3
  25. package/dist/ui/server/terminal.html +3 -3
  26. package/dist/ui/server/workers.html +3 -3
  27. package/dist/ui/serverless/alarms.html +3 -3
  28. package/dist/ui/serverless/assets.html +3 -3
  29. package/dist/ui/serverless/data.html +3 -3
  30. package/dist/ui/serverless/deployments.html +3 -3
  31. package/dist/ui/serverless/functions.html +3 -3
  32. package/dist/ui/serverless/logs.html +3 -3
  33. package/dist/ui/serverless/queues.html +3 -3
  34. package/dist/ui/serverless/scheduler.html +3 -3
  35. package/dist/ui/serverless/secrets.html +3 -3
  36. package/dist/ui/serverless/traces.html +3 -3
  37. package/dist/ui/serverless.html +3 -3
  38. package/package.json +3 -3
@@ -21901,6 +21901,8 @@ function createDashboardSite(options) {
21901
21901
  }
21902
21902
  };
21903
21903
  }
21904
+ var DASHBOARD_STATE_DIR = ".ts-cloud";
21905
+ var DASHBOARD_ENTRY = "./node_modules/@stacksjs/ts-cloud/dist/bin/cli.js";
21904
21906
  function apexOf(domain) {
21905
21907
  const parts = domain.split(".").filter(Boolean);
21906
21908
  return parts.length <= 2 ? domain : parts.slice(-2).join(".");
@@ -21963,7 +21965,7 @@ function resolveManagementDashboardSites(config, environment, opts) {
21963
21965
  return [];
21964
21966
  const auth2 = opts.password ? { auth: { username: opts.username || "admin", password: opts.password, realm: opts.realm } } : {};
21965
21967
  const build = opts.build === false || opts.build === undefined ? {} : { build: opts.build };
21966
- if (opts.live) {
21968
+ if (opts.live !== false) {
21967
21969
  const domain = resolveDashboardDomain(config, environment, opts.domain);
21968
21970
  if (!domain)
21969
21971
  return [];
@@ -21972,11 +21974,12 @@ function resolveManagementDashboardSites(config, environment, opts) {
21972
21974
  root: opts.uiRoot,
21973
21975
  deploy: "server",
21974
21976
  domain,
21975
- start: `cloud dashboard:serve --box --host 127.0.0.1 --port ${port}`,
21977
+ preStart: ["bun install --production --no-save"],
21978
+ start: `bun ${DASHBOARD_ENTRY} dashboard:serve --box --host 127.0.0.1 --port ${port}`,
21976
21979
  port,
21977
- ssl: { provider: "letsencrypt" },
21978
- ...build,
21979
- ...auth2
21980
+ sharedPaths: [DASHBOARD_STATE_DIR],
21981
+ healthCheck: { path: "/login" },
21982
+ ssl: { provider: "letsencrypt" }
21980
21983
  };
21981
21984
  return [{ name: managementDashboardSiteName(domain), site }];
21982
21985
  }
@@ -46152,4 +46155,4 @@ class ResourceManagementManager {
46152
46155
  }
46153
46156
  var resourceManagementManager = new ResourceManagementManager;
46154
46157
 
46155
- export { detectServiceRegion, signRequest, signRequestAsync, createPresignedUrl, createPresignedUrlAsync, makeAWSRequest, makeAWSRequestOnce, makeAWSRequestAsync, parseXMLResponse, parseJSONResponse, clearSigningKeyCache, getSigningKeyCacheSize, isNodeCryptoAvailable, isWebCryptoAvailable, QueuePresets, RealtimePresets, detectDeploymentTargets, resolveDeploymentMode, deploymentCoexistenceError, resolveCloudProvider, TemplateBuilder, Pseudo, generateResourceName, generateLogicalId, getTimestamp, sanitizeName, resolveProjectStackName, resolveSiteStackName, resolveSiteResourceName, resolveSiteBucketName, resolveStorageBucketName, resolveDeployBucketName, resolveServerlessAppStackName, resolveServerlessArtifactBucketName, resolveServerlessAssetBucketName, DependencyGraph, analyzeStackDiff, formatDiff, Storage, Registry, CDN, DNS, Security, Compute, Network, FileSystem, Email, exports_incoming_call, Phone, Queue, JobLoader, StacksIntegration, exports_send, SMS, AI, Database, Cache, Permissions, ApiGateway, Messaging, Workflow, Monitoring, Auth, Deployment, AssetHasher, Secrets, ParameterStore, Search, Redirects, Communication, Fn2, Arn, AWS_PSEUDO_PARAMETERS, CloudFormationBuilder, buildCloudFormationTemplate, createStaticSitePreset, createNodeJsServerPreset, createNodeJsServerlessPreset, createServerlessNodePreset, createFullStackAppPreset, createApiBackendPreset, createWordPressPreset, createJamstackPreset, createMicroservicesPreset, createRealtimeAppPreset, createDataPipelinePreset, createMLApiPreset, createTraditionalWebAppPreset, createLaravelPreset, createServerlessLaravelPreset, createDashboardSite, resolveDashboardDomain, resolveDashboardDomains, hasManagementDashboardSite, MANAGEMENT_DASHBOARD_SITE_PREFIX, managementDashboardSiteName, isManagementDashboardSiteName, resolveManagementDashboardSites, resolveManagementDashboardSite, extendPreset, composePresets, createPreset, mergeInfrastructure, withMonitoring, withSecurity, withDatabase, withCache, withCDN, withQueue, fromEnvironment, fromSharedCredentials, fromEC2Metadata, fromECSMetadata, fromWebIdentity, getCredentials, createCredentialProvider, resolveCredentials, resolveRegion, getAccountId, S3Error, createS3Client, CloudError, ConfigurationError, CredentialError, DeploymentError, ValidationError, AWSAPIError, ErrorCodes, getErrorDetails, createError, DebugLogger, validateConfiguration, detectMisconfigurations, validateCredentials, checkIAMPermissions, getRequiredPermissions, suggestIAMPolicy, DEFAULT_SERVICE_LIMITS, checkServiceQuotas, getQuotaUsageSummary, suggestQuotaIncrease, FileCache, TemplateCache, templateCache, hashFile, hashString, hashBuffer, hashDirectory, hashManifest, quickHash, findChangedFiles, HashCache, parallel, batch, parallelMap, parallelWithRetry, sequence, withTimeout, RateLimiter, chunk, processInChunks, diffTemplates, getDiffSummary, requiresReplacement, categorizeChanges, getDeploymentStrategy, getDiffStats, cloud_config_schema_default, validateAgainstSchema, defaultLocalConfig, getLocalConfig, isLocalDevelopment, getLocalEndpoint, getLocalCredentials, getLocalEnvVars, MockCloudFormation, MockS3, MockDynamoDB, createMockAWS, PreviewEnvironmentManager, previewManager, generatePreviewWorkflow, generateCleanupWorkflow, generateCostReportWorkflow, PreviewNotificationService, previewNotifications, formatTable, formatTree, formatProgressBar, formatBytes, formatDuration, formatList, formatKeyValue, ProgressBar, MultiStepProgress, Spinner, TaskList, suggestCommand, formatSuggestion, isLikelyTypo, getContextualHelp, searchCommands, autocomplete, suggestFlags, formatFlagSuggestions, getCommandUsage, validateCommand, REPL, REPLContext, REPLCommandBuilder, CommandHistory, formatHistory, formatHistoryStats, MultiRegionManager, multiRegionManager, CrossRegionReferenceManager, GlobalResourceManager, RegionPairManager, StackDependencyManager, crossRegionReferenceManager, globalResourceManager, regionPairManager, stackDependencyManager, AWS_REGIONS, getRegion, getAllRegions, getRegionsByLocation, getRegionsByCompliance, getRegionsByPricingTier, isValidRegion, getClosestRegion, suggestRegions, suggestRegionPairs, formatRegion, formatRegionList, getRegionStats, MultiAccountManager, OrganizationManager, multiAccountManager, organizationManager, RECOMMENDED_ACCOUNT_STRUCTURES, RECOMMENDED_SCPS, COMMON_CROSS_ACCOUNT_ROLES, getRecommendedStructure, generateCrossAccountRoleCF, validateAccountStructure, formatAccountStructure, generateDeploymentWorkflow, generateMultiEnvWorkflow, generatePRPreviewWorkflow, generateScheduledWorkflow, generateMatrixWorkflow, generateDeploymentPipeline, generateMultiEnvPipeline, generatePreviewPipeline, generateScheduledPipeline, generateManualPipeline, generateDeploymentConfig, generateMultiEnvConfig, generateScheduledConfig, generateApprovalConfig, generateParallelConfig, BackupManager, backupManager, DisasterRecoveryManager, drManager, AWSConfigManager, awsConfigManager, CloudTrailManager, cloudTrailManager, GuardDutyManager, guardDutyManager, SecurityHubManager, securityHubManager, BlueGreenManager, blueGreenManager, CanaryManager, canaryManager, ABTestManager, abTestManager, ProgressiveDeploymentManager, progressiveDeploymentManager, XRayManager, xrayManager, MetricsManager, metricsManager, LogsManager, logsManager, SyntheticsManager, syntheticsManager, MigrationManager, migrationManager, ReplicaManager, replicaManager, PerformanceManager, performanceManager, DatabaseUserManager, databaseUserManager, SecretsRotationManager, secretsRotationManager, SecretsManager, secretsManager, CertificateManager, certificateManager, SecurityScanningManager, securityScanningManager, ImageScanningManager, imageScanningManager, BuildOptimizationManager, buildOptimizationManager, ContainerRegistryManager, containerRegistryManager, ServiceMeshManager, serviceMeshManager, LambdaLayersManager, lambdaLayersManager, LambdaVersionsManager, lambdaVersionsManager, LambdaConcurrencyManager, lambdaConcurrencyManager, LambdaDestinationsManager, lambdaDestinationsManager, LambdaVPCManager, lambdaVPCManager, LambdaDLQManager, lambdaDLQManager, Route53RoutingManager, route53RoutingManager, DNSSECManager, dnssecManager, Route53ResolverManager, route53ResolverManager, BounceComplaintHandler, bounceComplaintHandler, EmailAnalyticsManager, emailAnalyticsManager, SenderReputationManager, senderReputationManager, EmailTemplateManager, emailTemplateManager, exports_advanced, exports_inbound, exports_advanced2, exports_advanced3, FIFOQueueManager, fifoQueueManager, DLQMonitoringManager, dlqMonitoringManager, BatchProcessingManager, batchProcessingManager, QueueManagementManager, queueManagementManager, createZip, generateBootstrap, runBuildHooks, sha256, artifactKey, packageServerlessApp, MANAGED_NODE_VERSIONS, resolveServerlessRuntime, resolveQueues, resolveQueueNames, composeServerlessAppTemplate, PHP_LAYER_EXTENSIONS, phpLayerPackages, phpLayerBuildStage, generatePhpLayerDockerfile, generateAppImageDockerfile, sharedRuntimeLoop, buildNodeRuntimeLayerZip, buildBunRuntimeLayerZip, resolveApp, eventToRequest, responseToResult, createHttpHandler, createQueueHandler, createCliHandler, createHandlers, generatePhpFpmConfig, phpRuntimeLayerAssets, laravelServerlessEnvDefaults, LARAVEL_SERVERLESS_BUILD_STEPS, buildPhpRuntimeLayerZip, PHP_DEFAULT_EXCLUDES, runPhpBuildHooks, collectPhpAppEntries, packagePhpApp, StaticSiteManager, staticSiteManager, StorageAdvancedManager, storageAdvancedManager, HealthCheckManager, healthCheckManager, NetworkSecurityManager, networkSecurityManager, ResourceManagementManager, resourceManagementManager };
46158
+ export { detectServiceRegion, signRequest, signRequestAsync, createPresignedUrl, createPresignedUrlAsync, makeAWSRequest, makeAWSRequestOnce, makeAWSRequestAsync, parseXMLResponse, parseJSONResponse, clearSigningKeyCache, getSigningKeyCacheSize, isNodeCryptoAvailable, isWebCryptoAvailable, QueuePresets, RealtimePresets, detectDeploymentTargets, resolveDeploymentMode, deploymentCoexistenceError, resolveCloudProvider, TemplateBuilder, Pseudo, generateResourceName, generateLogicalId, getTimestamp, sanitizeName, resolveProjectStackName, resolveSiteStackName, resolveSiteResourceName, resolveSiteBucketName, resolveStorageBucketName, resolveDeployBucketName, resolveServerlessAppStackName, resolveServerlessArtifactBucketName, resolveServerlessAssetBucketName, DependencyGraph, analyzeStackDiff, formatDiff, Storage, Registry, CDN, DNS, Security, Compute, Network, FileSystem, Email, exports_incoming_call, Phone, Queue, JobLoader, StacksIntegration, exports_send, SMS, AI, Database, Cache, Permissions, ApiGateway, Messaging, Workflow, Monitoring, Auth, Deployment, AssetHasher, Secrets, ParameterStore, Search, Redirects, Communication, Fn2, Arn, AWS_PSEUDO_PARAMETERS, CloudFormationBuilder, buildCloudFormationTemplate, createStaticSitePreset, createNodeJsServerPreset, createNodeJsServerlessPreset, createServerlessNodePreset, createFullStackAppPreset, createApiBackendPreset, createWordPressPreset, createJamstackPreset, createMicroservicesPreset, createRealtimeAppPreset, createDataPipelinePreset, createMLApiPreset, createTraditionalWebAppPreset, createLaravelPreset, createServerlessLaravelPreset, createDashboardSite, DASHBOARD_STATE_DIR, DASHBOARD_ENTRY, resolveDashboardDomain, resolveDashboardDomains, hasManagementDashboardSite, MANAGEMENT_DASHBOARD_SITE_PREFIX, managementDashboardSiteName, isManagementDashboardSiteName, resolveManagementDashboardSites, resolveManagementDashboardSite, extendPreset, composePresets, createPreset, mergeInfrastructure, withMonitoring, withSecurity, withDatabase, withCache, withCDN, withQueue, fromEnvironment, fromSharedCredentials, fromEC2Metadata, fromECSMetadata, fromWebIdentity, getCredentials, createCredentialProvider, resolveCredentials, resolveRegion, getAccountId, S3Error, createS3Client, CloudError, ConfigurationError, CredentialError, DeploymentError, ValidationError, AWSAPIError, ErrorCodes, getErrorDetails, createError, DebugLogger, validateConfiguration, detectMisconfigurations, validateCredentials, checkIAMPermissions, getRequiredPermissions, suggestIAMPolicy, DEFAULT_SERVICE_LIMITS, checkServiceQuotas, getQuotaUsageSummary, suggestQuotaIncrease, FileCache, TemplateCache, templateCache, hashFile, hashString, hashBuffer, hashDirectory, hashManifest, quickHash, findChangedFiles, HashCache, parallel, batch, parallelMap, parallelWithRetry, sequence, withTimeout, RateLimiter, chunk, processInChunks, diffTemplates, getDiffSummary, requiresReplacement, categorizeChanges, getDeploymentStrategy, getDiffStats, cloud_config_schema_default, validateAgainstSchema, defaultLocalConfig, getLocalConfig, isLocalDevelopment, getLocalEndpoint, getLocalCredentials, getLocalEnvVars, MockCloudFormation, MockS3, MockDynamoDB, createMockAWS, PreviewEnvironmentManager, previewManager, generatePreviewWorkflow, generateCleanupWorkflow, generateCostReportWorkflow, PreviewNotificationService, previewNotifications, formatTable, formatTree, formatProgressBar, formatBytes, formatDuration, formatList, formatKeyValue, ProgressBar, MultiStepProgress, Spinner, TaskList, suggestCommand, formatSuggestion, isLikelyTypo, getContextualHelp, searchCommands, autocomplete, suggestFlags, formatFlagSuggestions, getCommandUsage, validateCommand, REPL, REPLContext, REPLCommandBuilder, CommandHistory, formatHistory, formatHistoryStats, MultiRegionManager, multiRegionManager, CrossRegionReferenceManager, GlobalResourceManager, RegionPairManager, StackDependencyManager, crossRegionReferenceManager, globalResourceManager, regionPairManager, stackDependencyManager, AWS_REGIONS, getRegion, getAllRegions, getRegionsByLocation, getRegionsByCompliance, getRegionsByPricingTier, isValidRegion, getClosestRegion, suggestRegions, suggestRegionPairs, formatRegion, formatRegionList, getRegionStats, MultiAccountManager, OrganizationManager, multiAccountManager, organizationManager, RECOMMENDED_ACCOUNT_STRUCTURES, RECOMMENDED_SCPS, COMMON_CROSS_ACCOUNT_ROLES, getRecommendedStructure, generateCrossAccountRoleCF, validateAccountStructure, formatAccountStructure, generateDeploymentWorkflow, generateMultiEnvWorkflow, generatePRPreviewWorkflow, generateScheduledWorkflow, generateMatrixWorkflow, generateDeploymentPipeline, generateMultiEnvPipeline, generatePreviewPipeline, generateScheduledPipeline, generateManualPipeline, generateDeploymentConfig, generateMultiEnvConfig, generateScheduledConfig, generateApprovalConfig, generateParallelConfig, BackupManager, backupManager, DisasterRecoveryManager, drManager, AWSConfigManager, awsConfigManager, CloudTrailManager, cloudTrailManager, GuardDutyManager, guardDutyManager, SecurityHubManager, securityHubManager, BlueGreenManager, blueGreenManager, CanaryManager, canaryManager, ABTestManager, abTestManager, ProgressiveDeploymentManager, progressiveDeploymentManager, XRayManager, xrayManager, MetricsManager, metricsManager, LogsManager, logsManager, SyntheticsManager, syntheticsManager, MigrationManager, migrationManager, ReplicaManager, replicaManager, PerformanceManager, performanceManager, DatabaseUserManager, databaseUserManager, SecretsRotationManager, secretsRotationManager, SecretsManager, secretsManager, CertificateManager, certificateManager, SecurityScanningManager, securityScanningManager, ImageScanningManager, imageScanningManager, BuildOptimizationManager, buildOptimizationManager, ContainerRegistryManager, containerRegistryManager, ServiceMeshManager, serviceMeshManager, LambdaLayersManager, lambdaLayersManager, LambdaVersionsManager, lambdaVersionsManager, LambdaConcurrencyManager, lambdaConcurrencyManager, LambdaDestinationsManager, lambdaDestinationsManager, LambdaVPCManager, lambdaVPCManager, LambdaDLQManager, lambdaDLQManager, Route53RoutingManager, route53RoutingManager, DNSSECManager, dnssecManager, Route53ResolverManager, route53ResolverManager, BounceComplaintHandler, bounceComplaintHandler, EmailAnalyticsManager, emailAnalyticsManager, SenderReputationManager, senderReputationManager, EmailTemplateManager, emailTemplateManager, exports_advanced, exports_inbound, exports_advanced2, exports_advanced3, FIFOQueueManager, fifoQueueManager, DLQMonitoringManager, dlqMonitoringManager, BatchProcessingManager, batchProcessingManager, QueueManagementManager, queueManagementManager, createZip, generateBootstrap, runBuildHooks, sha256, artifactKey, packageServerlessApp, MANAGED_NODE_VERSIONS, resolveServerlessRuntime, resolveQueues, resolveQueueNames, composeServerlessAppTemplate, PHP_LAYER_EXTENSIONS, phpLayerPackages, phpLayerBuildStage, generatePhpLayerDockerfile, generateAppImageDockerfile, sharedRuntimeLoop, buildNodeRuntimeLayerZip, buildBunRuntimeLayerZip, resolveApp, eventToRequest, responseToResult, createHttpHandler, createQueueHandler, createCliHandler, createHandlers, generatePhpFpmConfig, phpRuntimeLayerAssets, laravelServerlessEnvDefaults, LARAVEL_SERVERLESS_BUILD_STEPS, buildPhpRuntimeLayerZip, PHP_DEFAULT_EXCLUDES, runPhpBuildHooks, collectPhpAppEntries, packagePhpApp, StaticSiteManager, staticSiteManager, StorageAdvancedManager, storageAdvancedManager, HealthCheckManager, healthCheckManager, NetworkSecurityManager, networkSecurityManager, ResourceManagementManager, resourceManagementManager };
@@ -5,7 +5,7 @@ import {
5
5
  resolveCloudProvider,
6
6
  resolveManagementDashboardSites,
7
7
  resolveProjectStackName
8
- } from "./chunk-49nmy775.js";
8
+ } from "./chunk-0tfwdnmf.js";
9
9
  import {
10
10
  EC2Client,
11
11
  SSMClient
@@ -3579,7 +3579,9 @@ function releasePaths(base, releaseId) {
3579
3579
  }
3580
3580
  function isFileSharedPath(p) {
3581
3581
  const name = p.split("/").pop() || p;
3582
- return name.startsWith(".") || /\.[a-z0-9]+$/i.test(name);
3582
+ if (name === ".env" || name.startsWith(".env."))
3583
+ return true;
3584
+ return /\.[a-z0-9]+$/i.test(name);
3583
3585
  }
3584
3586
  function buildEnsureReleaseLayout(paths, sharedPaths = DEFAULT_SHARED_PATHS) {
3585
3587
  const lines = [
@@ -3706,7 +3708,7 @@ function buildSiteDeployScript(options) {
3706
3708
  const paths = releasePaths(base, releaseId);
3707
3709
  const unitBase = `${slug}-${siteName}`;
3708
3710
  const serviceName = `${unitBase}.service`;
3709
- const sharedPaths = [".env"];
3711
+ const sharedPaths = [...new Set([".env", ...options.sharedPaths ?? []])];
3710
3712
  const envFile = formatEnvFile(envEntries);
3711
3713
  const preStart = preStartCommands.length > 0 ? [`cd ${paths.release}`, ...preStartCommands] : [];
3712
3714
  const stageRelease = [
@@ -3827,6 +3829,48 @@ import { chmodSync, existsSync as existsSync2, mkdirSync, readFileSync as readFi
3827
3829
  import { tmpdir } from "node:os";
3828
3830
  import { dirname, isAbsolute, join as join4 } from "node:path";
3829
3831
  import { fileURLToPath } from "node:url";
3832
+
3833
+ // src/deploy/dashboard-config-module.ts
3834
+ function blankValues(record) {
3835
+ if (!record || typeof record !== "object")
3836
+ return record;
3837
+ return Object.fromEntries(Object.keys(record).map((key) => [key, ""]));
3838
+ }
3839
+ function redactForDashboard(config) {
3840
+ const safe = JSON.parse(JSON.stringify(config ?? {}));
3841
+ if (safe.hetzner)
3842
+ delete safe.hetzner.apiToken;
3843
+ delete safe.aws;
3844
+ for (const site of Object.values(safe.sites ?? {})) {
3845
+ if (site?.env)
3846
+ site.env = blankValues(site.env);
3847
+ if (site?.environment)
3848
+ site.environment = blankValues(site.environment);
3849
+ }
3850
+ for (const env2 of Object.values(safe.environments ?? {})) {
3851
+ if (env2?.variables)
3852
+ env2.variables = blankValues(env2.variables);
3853
+ }
3854
+ for (const db of Object.values(safe.infrastructure?.databases ?? {})) {
3855
+ if (db?.password)
3856
+ db.password = "";
3857
+ }
3858
+ return safe;
3859
+ }
3860
+ function serializeDashboardConfig(config) {
3861
+ const safe = redactForDashboard(config);
3862
+ return [
3863
+ "// Generated by ts-cloud for the management dashboard — do not edit.",
3864
+ "//",
3865
+ "// This is the project's resolved cloud config, inlined so it needs no",
3866
+ "// imports on the box. Credentials are deliberately absent.",
3867
+ `export default ${JSON.stringify(safe, null, 2)}`,
3868
+ ""
3869
+ ].join(`
3870
+ `);
3871
+ }
3872
+
3873
+ // src/deploy/management-dashboard.ts
3830
3874
  var MANAGEMENT_DASHBOARD_SITE = "dashboard";
3831
3875
  var DASHBOARD_CREDENTIALS_FILE = join4(".ts-cloud", "dashboard-credentials.json");
3832
3876
  function generatePassword() {
@@ -3862,6 +3906,44 @@ var noopLogger = { info: () => {}, warn: () => {} };
3862
3906
  function truthy(v) {
3863
3907
  return v != null && v !== "" && v !== "0" && v.toLowerCase() !== "false";
3864
3908
  }
3909
+ var LIVE_STAGE_DIR = join4(".ts-cloud", "dashboard-release");
3910
+ function resolveDashboardVersion() {
3911
+ const explicit = process.env.TS_CLOUD_UI_VERSION?.trim();
3912
+ if (explicit)
3913
+ return explicit;
3914
+ const here = dirname(fileURLToPath(import.meta.url));
3915
+ for (const candidate of [join4(here, "..", "..", "package.json"), join4(here, "..", "package.json")]) {
3916
+ try {
3917
+ const pkg = JSON.parse(readFileSync3(candidate, "utf8"));
3918
+ if (pkg.name === "@stacksjs/ts-cloud" && pkg.version)
3919
+ return `^${pkg.version}`;
3920
+ } catch {}
3921
+ }
3922
+ return "latest";
3923
+ }
3924
+ function stageLiveDashboardRoot(config, cwd, logger) {
3925
+ if (!config?.project?.slug) {
3926
+ logger.warn("Management dashboard: the cloud config has no project slug — skipping the live dashboard.");
3927
+ return null;
3928
+ }
3929
+ const stage = join4(cwd, LIVE_STAGE_DIR);
3930
+ try {
3931
+ mkdirSync(stage, { recursive: true });
3932
+ writeFileSync(join4(stage, "cloud.config.ts"), serializeDashboardConfig(config));
3933
+ const pkg = {
3934
+ name: "ts-cloud-dashboard",
3935
+ private: true,
3936
+ type: "module",
3937
+ dependencies: { "@stacksjs/ts-cloud": resolveDashboardVersion() }
3938
+ };
3939
+ writeFileSync(join4(stage, "package.json"), `${JSON.stringify(pkg, null, 2)}
3940
+ `);
3941
+ return LIVE_STAGE_DIR;
3942
+ } catch (error) {
3943
+ logger.warn(`Management dashboard: could not stage the live release (${error?.message ?? error}) — skipping.`);
3944
+ return null;
3945
+ }
3946
+ }
3865
3947
  function resolveUiSource(cwd) {
3866
3948
  if (existsSync2(join4(cwd, "packages", "ui", "pages")) || existsSync2(join4(cwd, "packages", "ui", "package.json"))) {
3867
3949
  return { uiRoot: "packages/ui/dist", build: "cd packages/ui && bun install && bun run build" };
@@ -3888,6 +3970,32 @@ function ensureManagementDashboard(config, options = {}) {
3888
3970
  }
3889
3971
  if (hasManagementDashboardSite(config))
3890
3972
  return config;
3973
+ const environment = config.environments && Object.keys(config.environments)[0];
3974
+ const domain = process.env.TS_CLOUD_UI_DOMAIN?.trim() || undefined;
3975
+ const port = Number(process.env.TS_CLOUD_UI_PORT) || undefined;
3976
+ const sites = { ...config.sites ?? {} };
3977
+ if (!truthy(process.env.TS_CLOUD_UI_STATIC)) {
3978
+ const uiRoot = stageLiveDashboardRoot(config, cwd, logger);
3979
+ if (!uiRoot)
3980
+ return config;
3981
+ const resolved2 = resolveManagementDashboardSites(config, environment ?? "production", {
3982
+ uiRoot,
3983
+ build: false,
3984
+ domain,
3985
+ port,
3986
+ live: true
3987
+ });
3988
+ if (resolved2.length === 0) {
3989
+ logger.info("Management dashboard: no domain resolved (set TS_CLOUD_UI_DOMAIN or configure a site domain) — skipping.");
3990
+ return config;
3991
+ }
3992
+ for (const { name, site } of resolved2) {
3993
+ sites[name] = site;
3994
+ logger.info(`Management dashboard → https://${site.domain} (sign in with your ts-cloud account; the first deploy prints an admin password once)`);
3995
+ }
3996
+ config.sites = sites;
3997
+ return config;
3998
+ }
3891
3999
  const ui = resolveUiSource(cwd);
3892
4000
  if (!ui) {
3893
4001
  logger.info("Management dashboard: UI not found (no local ui/ or packaged dist/ui) — skipping auto-deploy.");
@@ -3895,17 +4003,14 @@ function ensureManagementDashboard(config, options = {}) {
3895
4003
  }
3896
4004
  const username = process.env.TS_CLOUD_UI_USERNAME?.trim() || "admin";
3897
4005
  const auth = resolveDashboardAuth(cwd, username, logger);
3898
- const environment = config.environments && Object.keys(config.environments)[0];
3899
- const live = truthy(process.env.TS_CLOUD_UI_LIVE);
3900
- const port = Number(process.env.TS_CLOUD_UI_PORT) || undefined;
3901
4006
  const resolved = resolveManagementDashboardSites(config, environment ?? "production", {
3902
4007
  uiRoot: ui.uiRoot,
3903
4008
  build: ui.build,
3904
- domain: process.env.TS_CLOUD_UI_DOMAIN?.trim() || undefined,
4009
+ domain,
3905
4010
  username,
3906
4011
  password: auth.password,
3907
4012
  realm: process.env.TS_CLOUD_UI_REALM?.trim() || undefined,
3908
- live,
4013
+ live: false,
3909
4014
  port
3910
4015
  });
3911
4016
  if (resolved.length === 0) {
@@ -3913,7 +4018,7 @@ function ensureManagementDashboard(config, options = {}) {
3913
4018
  return config;
3914
4019
  }
3915
4020
  const authNote = auth.source === "public" ? "NO AUTH — TS_CLOUD_UI_PUBLIC is set (dashboard is publicly reachable)" : auth.source === "env" ? "htpasswd-protected (TS_CLOUD_UI_PASSWORD)" : `htpasswd-protected (auto-generated — see ${DASHBOARD_CREDENTIALS_FILE})`;
3916
- const sites = { ...config.sites ?? {} };
4021
+ logger.warn("Management dashboard: static mode — one shared password, and no per-site collaborators. Unset TS_CLOUD_UI_STATIC for the live dashboard.");
3917
4022
  for (const { name, site } of resolved) {
3918
4023
  sites[name] = site;
3919
4024
  logger.info(`Management dashboard → https://${site.domain} (${authNote})`);
@@ -4566,6 +4671,7 @@ async function deploySiteRelease(driver, options, logger = noopLogger2) {
4566
4671
  envEntries: site.env || {},
4567
4672
  port: site.port,
4568
4673
  preStartCommands: site.preStart,
4674
+ sharedPaths: site.sharedPaths,
4569
4675
  zeroDowntime: site.zeroDowntime !== false,
4570
4676
  healthCheckPath: site.healthCheck?.path
4571
4677
  });
@@ -8,7 +8,7 @@ import {
8
8
  phpRuntimeLayerAssets,
9
9
  runPhpBuildHooks,
10
10
  sha256
11
- } from "./chunk-49nmy775.js";
11
+ } from "./chunk-0tfwdnmf.js";
12
12
 
13
13
  // src/deploy/serverless-image.ts
14
14
  import { execFileSync } from "node:child_process";
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  buildAndPushServerlessImage
3
- } from "./chunk-qgnyzfmx.js";
3
+ } from "./chunk-5mejsd43.js";
4
4
  import"./chunk-hsk6fe6x.js";
5
- import"./chunk-49nmy775.js";
5
+ import"./chunk-0tfwdnmf.js";
6
6
  import"./chunk-arsh1g5h.js";
7
7
  import"./chunk-v0bahtg2.js";
8
8
  export {
@@ -6,7 +6,7 @@ import {
6
6
  step,
7
7
  success,
8
8
  warn
9
- } from "./chunk-qgnyzfmx.js";
9
+ } from "./chunk-5mejsd43.js";
10
10
  import {
11
11
  CloudWatchLogsClient,
12
12
  CostExplorerClient,
@@ -28,7 +28,7 @@ import {
28
28
  resolveSiteKind,
29
29
  resolveUiSource,
30
30
  siteInstallBase
31
- } from "./chunk-xgnxz5dz.js";
31
+ } from "./chunk-5cfcv2s2.js";
32
32
  import {
33
33
  artifactKey,
34
34
  composeServerlessAppTemplate,
@@ -42,7 +42,7 @@ import {
42
42
  resolveServerlessArtifactBucketName,
43
43
  resolveServerlessAssetBucketName,
44
44
  resolveServerlessRuntime
45
- } from "./chunk-49nmy775.js";
45
+ } from "./chunk-0tfwdnmf.js";
46
46
  import {
47
47
  deployStaticSiteWithExternalDnsFull
48
48
  } from "./chunk-d7p84vz5.js";
@@ -347,7 +347,7 @@ async function deployServerlessApp(config, environment, opts = {}) {
347
347
  }
348
348
  if (imageMode) {
349
349
  step("Building + pushing container image");
350
- const { buildAndPushServerlessImage } = await import("./chunk-296n8hvt.js");
350
+ const { buildAndPushServerlessImage } = await import("./chunk-c3yy2xyq.js");
351
351
  const built = await buildAndPushServerlessImage({
352
352
  app,
353
353
  projectRoot,
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Serialize the resolved cloud config into a self-contained module for the
3
+ * dashboard to read on the box.
4
+ *
5
+ * Copying the project's `cloud.config.ts` verbatim does not work: a real config
6
+ * imports things. Stacks' does
7
+ * `import { servers } from '~/cloud/servers'` and `import { env } from
8
+ * '@stacksjs/env'` — neither the path alias nor the package exists in the
9
+ * dashboard's release dir, so the import would throw and the dashboard would
10
+ * come up describing nothing.
11
+ *
12
+ * The deploy machine has already resolved the config to a plain object, so we
13
+ * ship THAT as a literal. No imports, no aliases, nothing to resolve.
14
+ *
15
+ * Credentials are stripped on the way out (see {@link redactForDashboard}).
16
+ */
17
+ import type { CloudConfig } from '@ts-cloud/core';
18
+ /**
19
+ * Strip everything the dashboard does not need to read.
20
+ *
21
+ * Two rules:
22
+ *
23
+ * 1. **No credentials.** The Hetzner API token is the big one: it controls the
24
+ * whole cloud account (create and destroy any server), and unlike a site's
25
+ * own secrets it is otherwise NOT on the box at all. Putting it in the
26
+ * dashboard's release would turn a dashboard compromise into an account
27
+ * compromise. AWS credentials go for the same reason.
28
+ *
29
+ * 2. **Env keys, not env values.** The dashboard only ever displays which env
30
+ * vars a site defines, never their values — so ship the keys and blank the
31
+ * values. The running site has its own `.env` on the box regardless; there
32
+ * is no reason for a second copy to sit in the dashboard's directory.
33
+ */
34
+ export declare function redactForDashboard(config: CloudConfig): Record<string, any>;
35
+ /**
36
+ * The `cloud.config.ts` module text shipped in the dashboard's release. Reads
37
+ * as a plain literal so bun can import it on the box with nothing installed
38
+ * beyond the ts-cloud package itself.
39
+ */
40
+ export declare function serializeDashboardConfig(config: CloudConfig): string;
@@ -0,0 +1 @@
1
+ export {};
@@ -12,10 +12,10 @@ import {
12
12
  sanitizeCloudConfig,
13
13
  setMaintenance,
14
14
  startLocalDashboardServer
15
- } from "../chunk-eapb9et8.js";
15
+ } from "../chunk-khfnaap2.js";
16
16
  import {
17
17
  buildAndPushServerlessImage
18
- } from "../chunk-qgnyzfmx.js";
18
+ } from "../chunk-5mejsd43.js";
19
19
  import"../chunk-b82pbxyp.js";
20
20
  import"../chunk-hsk6fe6x.js";
21
21
  import {
@@ -30,8 +30,8 @@ import {
30
30
  resolveUiSource,
31
31
  siteInstallBase,
32
32
  validateDeploymentConfig
33
- } from "../chunk-xgnxz5dz.js";
34
- import"../chunk-49nmy775.js";
33
+ } from "../chunk-5cfcv2s2.js";
34
+ import"../chunk-0tfwdnmf.js";
35
35
  import"../chunk-93hjhs78.js";
36
36
  import {
37
37
  deleteStaticSite,
@@ -1,23 +1,34 @@
1
1
  /**
2
- * Auto-deploy of the ts-cloud management dashboard (the `@ts-cloud/ui` stx app)
3
- * on every server provision/deploy.
2
+ * Auto-deploy of the ts-cloud management dashboard on every server
3
+ * provision/deploy, so every box ships with a cockpit.
4
4
  *
5
- * Resolves the UI directory (the repo's local `packages/ui/`, else the prebuilt
6
- * UI that ships inside the installed package at `dist/ui`), derives a `dashboard.<apex>`
7
- * host, and injects it into `config.sites` as a server-static site. It is served
8
- * SECURE BY DEFAULT: the dashboard is served behind htpasswd on every deploy.
9
- * The password is resolved as: `TS_CLOUD_UI_PASSWORD` when set, else a strong
10
- * auto-generated one (persisted to `.ts-cloud/dashboard-credentials.json` so it
11
- * stays stable across deploys and printed once in the deploy log). Serving the
12
- * dashboard publicly is an explicit, deliberate opt-in via `TS_CLOUD_UI_PUBLIC`.
5
+ * **Live (the default).** The dashboard runs as a service on the box
6
+ * (`cloud dashboard:serve --box`) behind the proxy. It serves live data and the
7
+ * control API, and authenticates itself: a login page, sessions, and per-site
8
+ * collaborator grants. The release is tiny the project's cloud config plus a
9
+ * `package.json` and the box installs `@stacksjs/ts-cloud` from npm, which
10
+ * carries both the CLI and the UI it serves.
11
+ *
12
+ * On the first live deploy the box mints an admin and prints the password once
13
+ * into the deploy log. Users and the session key live in the site's shared
14
+ * `.ts-cloud/`, so they survive later deploys.
15
+ *
16
+ * **Static (`TS_CLOUD_UI_STATIC`).** The built UI shipped as files behind
17
+ * htpasswd. One shared password, all data baked in at build time, and therefore
18
+ * no collaborators. Kept for boxes that cannot run the service.
13
19
  *
14
20
  * Env:
21
+ * - `TS_CLOUD_UI_STATIC` set truthy for the old static + htpasswd model
22
+ * - `TS_CLOUD_UI_DOMAIN` explicit dashboard host (else `dashboard.<apex>`)
23
+ * - `TS_CLOUD_UI_PORT` loopback port for the live service (default 7676)
24
+ * - `TS_CLOUD_UI_VERSION` ts-cloud version the box installs (default: this one)
25
+ * - `TS_CLOUD_UI_DISABLE` set truthy to skip auto-deploy
26
+ *
27
+ * Static mode only:
15
28
  * - `TS_CLOUD_UI_PASSWORD` htpasswd password (unset ⇒ auto-generated + saved)
16
29
  * - `TS_CLOUD_UI_PUBLIC` set truthy to serve WITHOUT auth (opt-out, insecure)
17
30
  * - `TS_CLOUD_UI_USERNAME` htpasswd user (default `admin`)
18
- * - `TS_CLOUD_UI_DOMAIN` explicit dashboard host (else `dashboard.<apex>`)
19
31
  * - `TS_CLOUD_UI_REALM` browser auth realm
20
- * - `TS_CLOUD_UI_DISABLE` set truthy to skip auto-deploy
21
32
  */
22
33
  import type { CloudConfig } from '@ts-cloud/core';
23
34
  /**
@@ -51,10 +62,32 @@ export interface EnsureDashboardLogger {
51
62
  info: (msg: string) => void;
52
63
  warn: (msg: string) => void;
53
64
  }
65
+ /** Where the live dashboard's release is staged, inside the project checkout. */
66
+ export declare const LIVE_STAGE_DIR: string;
67
+ /**
68
+ * The ts-cloud version the box should install. Reads this package's own version
69
+ * so a box runs a dashboard matching the CLI that deployed it, rather than
70
+ * drifting to whatever `latest` happens to be mid-deploy.
71
+ */
72
+ export declare function resolveDashboardVersion(): string;
73
+ /**
74
+ * Stage the live dashboard's release: the RESOLVED cloud config inlined as a
75
+ * self-contained module, plus a `package.json` whose install pulls the CLI and
76
+ * UI from npm.
77
+ *
78
+ * The config is serialized rather than copied because a real one imports things
79
+ * that do not exist on the box — Stacks' does `import { servers } from
80
+ * '~/cloud/servers'` — and the import would throw, leaving the dashboard with
81
+ * nothing to describe. Credentials are stripped on the way out.
82
+ *
83
+ * Returns the staged directory relative to `cwd`, or null when staging fails.
84
+ */
85
+ export declare function stageLiveDashboardRoot(config: CloudConfig, cwd: string, logger: EnsureDashboardLogger): string | null;
54
86
  /**
55
- * Resolve the UI source to ship. Prefers the repo's local `packages/ui/` (built
56
- * on the deploy machine), then the prebuilt UI bundled in the installed package.
57
- * Returns `{ uiRoot, build }` or null when no UI is available.
87
+ * Resolve the UI source to ship (STATIC mode only — the live dashboard gets its
88
+ * UI from the npm package it installs on the box). Prefers the repo's local
89
+ * `packages/ui/` (built on the deploy machine), then the prebuilt UI bundled in
90
+ * the installed package. Returns `{ uiRoot, build }` or null when unavailable.
58
91
  */
59
92
  export declare function resolveUiSource(cwd: string): {
60
93
  uiRoot: string;
@@ -48,8 +48,8 @@ import {
48
48
  waitForCloudInit,
49
49
  waitForSsh,
50
50
  wrapCloudInitUserData
51
- } from "../chunk-xgnxz5dz.js";
52
- import"../chunk-49nmy775.js";
51
+ } from "../chunk-5cfcv2s2.js";
52
+ import"../chunk-0tfwdnmf.js";
53
53
  import"../chunk-93hjhs78.js";
54
54
  import"../chunk-qpj3edwz.js";
55
55
  import"../chunk-vd87cpvn.js";
@@ -25,6 +25,13 @@ export interface BuildSiteDeployScriptOptions {
25
25
  * `node_modules`.
26
26
  */
27
27
  preStartCommands?: string[];
28
+ /**
29
+ * Extra paths kept in `shared/` and symlinked into each release, so they
30
+ * survive a deploy. `.env` is always shared; anything the app WRITES and must
31
+ * keep (a state directory, a database file) has to be listed here or the next
32
+ * release silently starts from empty.
33
+ */
34
+ sharedPaths?: readonly string[];
28
35
  /**
29
36
  * True zero-downtime cutover for ported sites: the new release runs as its
30
37
  * own systemd instance (`<slug>-<site>@<releaseId>`) that binds the same
package/dist/index.js CHANGED
@@ -55,10 +55,10 @@ import {
55
55
  sanitizeCloudConfig,
56
56
  setMaintenance,
57
57
  startLocalDashboardServer
58
- } from "./chunk-eapb9et8.js";
58
+ } from "./chunk-khfnaap2.js";
59
59
  import {
60
60
  buildAndPushServerlessImage
61
- } from "./chunk-qgnyzfmx.js";
61
+ } from "./chunk-5mejsd43.js";
62
62
  import {
63
63
  CloudWatchLogsClient,
64
64
  EFSClient,
@@ -105,7 +105,7 @@ import {
105
105
  waitForCloudInit,
106
106
  waitForSsh,
107
107
  wrapCloudInitUserData
108
- } from "./chunk-xgnxz5dz.js";
108
+ } from "./chunk-5cfcv2s2.js";
109
109
  import {
110
110
  ABTestManager,
111
111
  AI,
@@ -137,6 +137,8 @@ import {
137
137
  ContainerRegistryManager,
138
138
  CredentialError,
139
139
  CrossRegionReferenceManager,
140
+ DASHBOARD_ENTRY,
141
+ DASHBOARD_STATE_DIR,
140
142
  DEFAULT_SERVICE_LIMITS,
141
143
  DLQMonitoringManager,
142
144
  DNS,
@@ -504,7 +506,7 @@ import {
504
506
  withSecurity,
505
507
  withTimeout,
506
508
  xrayManager
507
- } from "./chunk-49nmy775.js";
509
+ } from "./chunk-0tfwdnmf.js";
508
510
  import {
509
511
  EC2Client,
510
512
  SSMClient
@@ -5118,6 +5120,8 @@ export {
5118
5120
  DNS,
5119
5121
  DLQMonitoringManager,
5120
5122
  DEFAULT_SERVICE_LIMITS,
5123
+ DASHBOARD_STATE_DIR,
5124
+ DASHBOARD_ENTRY,
5121
5125
  DASHBOARD_CREDENTIALS_FILE,
5122
5126
  CrossRegionReferenceManager,
5123
5127
  CredentialError,