@heramb1/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/commands/deploy.d.ts +9 -0
  2. package/dist/commands/deploy.d.ts.map +1 -0
  3. package/dist/commands/deploy.js +82 -0
  4. package/dist/commands/deploy.js.map +1 -0
  5. package/dist/commands/env.d.ts +3 -0
  6. package/dist/commands/env.d.ts.map +1 -0
  7. package/dist/commands/env.js +52 -0
  8. package/dist/commands/env.js.map +1 -0
  9. package/dist/commands/init.d.ts +2 -0
  10. package/dist/commands/init.d.ts.map +1 -0
  11. package/dist/commands/init.js +82 -0
  12. package/dist/commands/init.js.map +1 -0
  13. package/dist/commands/logs.d.ts +2 -0
  14. package/dist/commands/logs.d.ts.map +1 -0
  15. package/dist/commands/logs.js +27 -0
  16. package/dist/commands/logs.js.map +1 -0
  17. package/dist/commands/migrate.d.ts +8 -0
  18. package/dist/commands/migrate.d.ts.map +1 -0
  19. package/dist/commands/migrate.js +84 -0
  20. package/dist/commands/migrate.js.map +1 -0
  21. package/dist/commands/plan.d.ts +2 -0
  22. package/dist/commands/plan.d.ts.map +1 -0
  23. package/dist/commands/plan.js +55 -0
  24. package/dist/commands/plan.js.map +1 -0
  25. package/dist/commands/status.d.ts +2 -0
  26. package/dist/commands/status.d.ts.map +1 -0
  27. package/dist/commands/status.js +36 -0
  28. package/dist/commands/status.js.map +1 -0
  29. package/dist/commands/ws.d.ts +2 -0
  30. package/dist/commands/ws.d.ts.map +1 -0
  31. package/dist/commands/ws.js +37 -0
  32. package/dist/commands/ws.js.map +1 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +72 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/utils/config.d.ts +6 -0
  38. package/dist/utils/config.d.ts.map +1 -0
  39. package/dist/utils/config.js +23 -0
  40. package/dist/utils/config.js.map +1 -0
  41. package/dist/utils/connect.d.ts +7 -0
  42. package/dist/utils/connect.d.ts.map +1 -0
  43. package/dist/utils/connect.js +101 -0
  44. package/dist/utils/connect.js.map +1 -0
  45. package/dist/utils/credentials.d.ts +8 -0
  46. package/dist/utils/credentials.d.ts.map +1 -0
  47. package/dist/utils/credentials.js +56 -0
  48. package/dist/utils/credentials.js.map +1 -0
  49. package/dist/utils/gitignore.d.ts +5 -0
  50. package/dist/utils/gitignore.d.ts.map +1 -0
  51. package/dist/utils/gitignore.js +35 -0
  52. package/dist/utils/gitignore.js.map +1 -0
  53. package/dist/utils/output.d.ts +23 -0
  54. package/dist/utils/output.d.ts.map +1 -0
  55. package/dist/utils/output.js +53 -0
  56. package/dist/utils/output.js.map +1 -0
  57. package/dist/utils/preview.d.ts +7 -0
  58. package/dist/utils/preview.d.ts.map +1 -0
  59. package/dist/utils/preview.js +31 -0
  60. package/dist/utils/preview.js.map +1 -0
  61. package/dist/utils/provider-pick.d.ts +5 -0
  62. package/dist/utils/provider-pick.d.ts.map +1 -0
  63. package/dist/utils/provider-pick.js +110 -0
  64. package/dist/utils/provider-pick.js.map +1 -0
  65. package/dist/utils/registry.d.ts +3 -0
  66. package/dist/utils/registry.d.ts.map +1 -0
  67. package/dist/utils/registry.js +42 -0
  68. package/dist/utils/registry.js.map +1 -0
  69. package/dist/utils/stack-detector.d.ts +4 -0
  70. package/dist/utils/stack-detector.d.ts.map +1 -0
  71. package/dist/utils/stack-detector.js +141 -0
  72. package/dist/utils/stack-detector.js.map +1 -0
  73. package/package.json +45 -0
package/dist/index.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { initCommand } from './commands/init.js';
4
+ import { deployCommand } from './commands/deploy.js';
5
+ import { statusCommand } from './commands/status.js';
6
+ import { envPushCommand, envPullCommand } from './commands/env.js';
7
+ import { wsTestCommand } from './commands/ws.js';
8
+ import { logsCommand } from './commands/logs.js';
9
+ import { migrateCommand } from './commands/migrate.js';
10
+ import { planCommand } from './commands/plan.js';
11
+ const program = new Command();
12
+ program
13
+ .name('heramb')
14
+ .description('Removes deploy obstacles — orchestration across any platform')
15
+ .version('0.1.0');
16
+ program
17
+ .command('plan')
18
+ .description('Scan project and recommend deployment platforms')
19
+ .action(planCommand);
20
+ program
21
+ .command('init')
22
+ .description('Detect stack, create config, validate tokens')
23
+ .action(initCommand);
24
+ program
25
+ .command('deploy')
26
+ .description('Full orchestrated deploy')
27
+ .option('--backend-only', 'Skip frontend deploy')
28
+ .option('--frontend-only', 'Skip backend and Redis')
29
+ .option('--env <environment>', 'Deploy environment: production or preview', 'production')
30
+ .option('--branch <name>', 'Git branch for preview deploys')
31
+ .action((opts) => deployCommand({
32
+ backendOnly: opts.backendOnly,
33
+ frontendOnly: opts.frontendOnly,
34
+ env: opts.env === 'preview' ? 'preview' : 'production',
35
+ branch: opts.branch,
36
+ }));
37
+ program
38
+ .command('status')
39
+ .description('Unified status across all providers')
40
+ .action(statusCommand);
41
+ const envCmd = program.command('env').description('Sync environment variables');
42
+ envCmd
43
+ .command('push')
44
+ .description('Sync local .env to all services')
45
+ .action(envPushCommand);
46
+ envCmd
47
+ .command('pull')
48
+ .description('Pull remote env vars to local .env')
49
+ .action(envPullCommand);
50
+ const wsCmd = program.command('ws').description('WebSocket utilities');
51
+ wsCmd
52
+ .command('test [url]')
53
+ .description('WebSocket smoke test')
54
+ .action(wsTestCommand);
55
+ program
56
+ .command('logs <service>')
57
+ .description('Tail logs from a service')
58
+ .action(logsCommand);
59
+ program
60
+ .command('migrate [service]')
61
+ .description('Move a service to another provider')
62
+ .option('--from <provider>', 'Source provider (e.g. heroku)')
63
+ .option('--to <provider>', 'Target provider (e.g. render)')
64
+ .option('--dry-run', 'Show migration plan without changing config')
65
+ .action((service, opts) => migrateCommand({
66
+ service,
67
+ from: opts.from,
68
+ to: opts.to,
69
+ dryRun: opts.dryRun,
70
+ }));
71
+ program.parse();
72
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type HerambConfig } from '@heramb1/core';
2
+ export declare function findConfigPath(cwd?: string): string;
3
+ export declare function loadHerambConfig(cwd?: string): HerambConfig;
4
+ export declare function saveHerambConfig(config: HerambConfig, cwd?: string): void;
5
+ export declare function configExists(cwd?: string): boolean;
6
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,eAAe,CAAC;AAI9D,wBAAgB,cAAc,CAAC,GAAG,SAAgB,GAAG,MAAM,CAE1D;AAED,wBAAgB,gBAAgB,CAAC,GAAG,SAAgB,GAAG,YAAY,CAOlE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,SAAgB,GAAG,IAAI,CAGhF;AAED,wBAAgB,YAAY,CAAC,GAAG,SAAgB,GAAG,OAAO,CAEzD"}
@@ -0,0 +1,23 @@
1
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { loadConfig } from '@heramb1/core';
4
+ const CONFIG_FILE = 'heramb.config.json';
5
+ export function findConfigPath(cwd = process.cwd()) {
6
+ return resolve(cwd, CONFIG_FILE);
7
+ }
8
+ export function loadHerambConfig(cwd = process.cwd()) {
9
+ const path = findConfigPath(cwd);
10
+ if (!existsSync(path)) {
11
+ throw new Error(`No ${CONFIG_FILE} found — run \`heramb init\` first`);
12
+ }
13
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
14
+ return loadConfig(raw);
15
+ }
16
+ export function saveHerambConfig(config, cwd = process.cwd()) {
17
+ const path = findConfigPath(cwd);
18
+ writeFileSync(path, JSON.stringify(config, null, 2) + '\n');
19
+ }
20
+ export function configExists(cwd = process.cwd()) {
21
+ return existsSync(findConfigPath(cwd));
22
+ }
23
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAqB,MAAM,eAAe,CAAC;AAE9D,MAAM,WAAW,GAAG,oBAAoB,CAAC;AAEzC,MAAM,UAAU,cAAc,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAChD,OAAO,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAClD,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,MAAM,WAAW,oCAAoC,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACpD,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,MAAoB,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACxE,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACjC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IAC9C,OAAO,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { StackDetection } from '@heramb1/core';
2
+ export declare function connectProviders(providers: string[], options?: {
3
+ skipExisting?: boolean;
4
+ }): Promise<string[]>;
5
+ export declare function providersForStack(stack: StackDetection): string[];
6
+ export declare function promptProviderConnect(provider: string): Promise<void>;
7
+ //# sourceMappingURL=connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/utils/connect.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAIpD,wBAAsB,gBAAgB,CACpC,SAAS,EAAE,MAAM,EAAE,EACnB,OAAO,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GACnC,OAAO,CAAC,MAAM,EAAE,CAAC,CAcnB;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,cAAc,GAAG,MAAM,EAAE,CASjE;AAED,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiF3E"}
@@ -0,0 +1,101 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import { buildDeployPlan, getProvider, providersNeededForPlan, topProviderForRole, deriveRequirements } from '@heramb1/core';
4
+ import { saveCredential, getProviderToken } from './credentials.js';
5
+ export async function connectProviders(providers, options) {
6
+ const connected = [];
7
+ for (const provider of providers) {
8
+ if (options?.skipExisting && getProviderToken(provider)) {
9
+ connected.push(provider);
10
+ continue;
11
+ }
12
+ await promptProviderConnect(provider);
13
+ connected.push(provider);
14
+ }
15
+ return connected;
16
+ }
17
+ export function providersForStack(stack) {
18
+ const plan = buildDeployPlan(stack);
19
+ const req = deriveRequirements(stack);
20
+ const choices = {
21
+ frontend: topProviderForRole('frontend', req)?.provider ?? '',
22
+ backend: topProviderForRole('backend', req)?.provider ?? '',
23
+ cache: topProviderForRole('cache', req)?.provider ?? '',
24
+ };
25
+ return providersNeededForPlan(plan, choices);
26
+ }
27
+ export async function promptProviderConnect(provider) {
28
+ const guide = getProvider(provider);
29
+ if (!guide) {
30
+ throw new Error(`Unknown provider: ${provider}`);
31
+ }
32
+ if (getProviderToken(provider)) {
33
+ console.log(chalk.green('✓') + ` ${guide.name} already connected`);
34
+ return;
35
+ }
36
+ console.log(chalk.cyan(`\n→ Connect ${guide.name}`));
37
+ console.log(chalk.dim(` Sign up: ${guide.signupUrl}`));
38
+ console.log(chalk.dim(` Get key: ${guide.tokenUrl}\n`));
39
+ const { hasAccount } = await inquirer.prompt([
40
+ {
41
+ type: 'confirm',
42
+ name: 'hasAccount',
43
+ message: `Do you already have a ${guide.name} account?`,
44
+ default: false,
45
+ },
46
+ ]);
47
+ if (!hasAccount) {
48
+ console.log(chalk.yellow('⚠') +
49
+ ` Create your account at ${chalk.underline(guide.signupUrl)} then paste your API key below.`);
50
+ }
51
+ switch (provider) {
52
+ case 'upstash': {
53
+ const { email, apiKey } = await inquirer.prompt([
54
+ {
55
+ type: 'input',
56
+ name: 'email',
57
+ message: 'Upstash account email:',
58
+ validate: (v) => v.includes('@') || 'Valid email required',
59
+ },
60
+ {
61
+ type: 'password',
62
+ name: 'apiKey',
63
+ message: 'Upstash API key:',
64
+ validate: (v) => v.length > 0 || 'API key required',
65
+ },
66
+ ]);
67
+ saveCredential('upstash', { email, apiKey, token: apiKey });
68
+ break;
69
+ }
70
+ case 'vercel': {
71
+ const { token, teamId } = await inquirer.prompt([
72
+ {
73
+ type: 'password',
74
+ name: 'token',
75
+ message: 'Vercel API token:',
76
+ validate: (v) => v.length > 0 || 'Token required',
77
+ },
78
+ {
79
+ type: 'input',
80
+ name: 'teamId',
81
+ message: 'Vercel team ID (optional):',
82
+ },
83
+ ]);
84
+ saveCredential('vercel', { token, ...(teamId ? { teamId } : {}) });
85
+ break;
86
+ }
87
+ default: {
88
+ const { token } = await inquirer.prompt([
89
+ {
90
+ type: 'password',
91
+ name: 'token',
92
+ message: `${guide.name} API token:`,
93
+ validate: (v) => v.length > 0 || 'Token required',
94
+ },
95
+ ]);
96
+ saveCredential(provider, { token });
97
+ }
98
+ }
99
+ console.log(chalk.green('✓') + ` ${guide.name} connected`);
100
+ }
101
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/utils/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Credentials } from '@heramb1/core';
2
+ export declare function loadCredentials(): Credentials;
3
+ export declare function saveCredential(provider: string, data: Record<string, string>): void;
4
+ export declare function getProviderToken(provider: string): string | undefined;
5
+ export declare function getProviderCredential(provider: string, field: string): string | undefined;
6
+ export declare function requireProviderToken(provider: string): string;
7
+ export declare function getCredentialsPath(): string;
8
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../src/utils/credentials.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AASjD,wBAAgB,eAAe,IAAI,WAAW,CAE7C;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAInF;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAkBrE;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAYzF;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAM7D;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
@@ -0,0 +1,56 @@
1
+ import Conf from 'conf';
2
+ const store = new Conf({
3
+ projectName: 'heramb',
4
+ cwd: `${process.env.HOME ?? process.env.USERPROFILE}/.heramb`,
5
+ configName: 'keys',
6
+ defaults: { keys: {} },
7
+ });
8
+ export function loadCredentials() {
9
+ return store.get('keys');
10
+ }
11
+ export function saveCredential(provider, data) {
12
+ const keys = loadCredentials();
13
+ keys[provider] = { ...keys[provider], ...data };
14
+ store.set('keys', keys);
15
+ }
16
+ export function getProviderToken(provider) {
17
+ const fromStore = loadCredentials()[provider]?.token;
18
+ if (fromStore)
19
+ return fromStore;
20
+ const envKeys = {
21
+ railway: ['RAILWAY_TOKEN'],
22
+ vercel: ['VERCEL_TOKEN'],
23
+ upstash: ['UPSTASH_API_KEY', 'UPSTASH_TOKEN'],
24
+ render: ['RENDER_API_KEY'],
25
+ netlify: ['NETLIFY_TOKEN'],
26
+ heroku: ['HEROKU_API_KEY'],
27
+ };
28
+ for (const key of envKeys[provider] ?? []) {
29
+ if (process.env[key])
30
+ return process.env[key];
31
+ }
32
+ return undefined;
33
+ }
34
+ export function getProviderCredential(provider, field) {
35
+ const fromStore = loadCredentials()[provider]?.[field];
36
+ if (fromStore)
37
+ return fromStore;
38
+ if (provider === 'upstash' && field === 'email') {
39
+ return process.env.UPSTASH_EMAIL;
40
+ }
41
+ if (provider === 'vercel' && field === 'teamId') {
42
+ return process.env.VERCEL_TEAM_ID;
43
+ }
44
+ return undefined;
45
+ }
46
+ export function requireProviderToken(provider) {
47
+ const token = getProviderToken(provider);
48
+ if (!token) {
49
+ throw new Error(`Key not found — run \`heramb init\` to add your ${provider} token`);
50
+ }
51
+ return token;
52
+ }
53
+ export function getCredentialsPath() {
54
+ return store.path;
55
+ }
56
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../src/utils/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAwB;IAC5C,WAAW,EAAE,QAAQ;IACrB,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,UAAU;IAC7D,UAAU,EAAE,MAAM;IAClB,QAAQ,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE;CACvB,CAAC,CAAC;AAEH,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,IAA4B;IAC3E,MAAM,IAAI,GAAG,eAAe,EAAE,CAAC;IAC/B,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC;IAChD,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC;IACrD,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAEhC,MAAM,OAAO,GAA6B;QACxC,OAAO,EAAE,CAAC,eAAe,CAAC;QAC1B,MAAM,EAAE,CAAC,cAAc,CAAC;QACxB,OAAO,EAAE,CAAC,iBAAiB,EAAE,eAAe,CAAC;QAC7C,MAAM,EAAE,CAAC,gBAAgB,CAAC;QAC1B,OAAO,EAAE,CAAC,eAAe,CAAC;QAC1B,MAAM,EAAE,CAAC,gBAAgB,CAAC;KAC3B,CAAC;IAEF,KAAK,MAAM,GAAG,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,IAAI,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAChD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,QAAgB,EAAE,KAAa;IACnE,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,SAAS;QAAE,OAAO,SAAS,CAAC;IAEhC,IAAI,QAAQ,KAAK,SAAS,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QAChD,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC;IACnC,CAAC;IACD,IAAI,QAAQ,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IACpC,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,QAAgB;IACnD,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,mDAAmD,QAAQ,QAAQ,CAAC,CAAC;IACvF,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export declare const HERAMB_GITIGNORE_SECTION = "# Heramb \u2014 secrets (do not commit)";
2
+ export declare const HERAMB_GITIGNORE_ENTRIES: string[];
3
+ export type EnsureGitignoreResult = 'created' | 'updated' | 'unchanged';
4
+ export declare function ensureGitignore(cwd?: string): EnsureGitignoreResult;
5
+ //# sourceMappingURL=gitignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../src/utils/gitignore.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,wBAAwB,4CAAuC,CAAC;AAE7E,eAAO,MAAM,wBAAwB,UAMpC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,CAAC;AAUxE,wBAAgB,eAAe,CAAC,GAAG,SAAgB,GAAG,qBAAqB,CAsB1E"}
@@ -0,0 +1,35 @@
1
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ export const HERAMB_GITIGNORE_SECTION = '# Heramb — secrets (do not commit)';
4
+ export const HERAMB_GITIGNORE_ENTRIES = [
5
+ '.env',
6
+ '.env.*',
7
+ '!.env.example',
8
+ '.heramb/',
9
+ 'keys.json',
10
+ ];
11
+ function hasGitignoreLine(content, entry) {
12
+ return content.split('\n').some((line) => line.trim() === entry);
13
+ }
14
+ function formatBlock(entries) {
15
+ return `${HERAMB_GITIGNORE_SECTION}\n${entries.join('\n')}\n`;
16
+ }
17
+ export function ensureGitignore(cwd = process.cwd()) {
18
+ const gitignorePath = join(cwd, '.gitignore');
19
+ if (!existsSync(gitignorePath)) {
20
+ writeFileSync(gitignorePath, formatBlock(HERAMB_GITIGNORE_ENTRIES));
21
+ return 'created';
22
+ }
23
+ const content = readFileSync(gitignorePath, 'utf-8');
24
+ const missing = HERAMB_GITIGNORE_ENTRIES.filter((entry) => !hasGitignoreLine(content, entry));
25
+ if (missing.length === 0) {
26
+ return 'unchanged';
27
+ }
28
+ const hasSection = content.includes(HERAMB_GITIGNORE_SECTION);
29
+ const suffix = hasSection
30
+ ? `\n${missing.join('\n')}\n`
31
+ : `\n${formatBlock(missing)}`;
32
+ writeFileSync(gitignorePath, content.trimEnd() + suffix);
33
+ return 'updated';
34
+ }
35
+ //# sourceMappingURL=gitignore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.js","sourceRoot":"","sources":["../../src/utils/gitignore.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,CAAC,MAAM,wBAAwB,GAAG,oCAAoC,CAAC;AAE7E,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,MAAM;IACN,QAAQ;IACR,eAAe;IACf,UAAU;IACV,WAAW;CACZ,CAAC;AAIF,SAAS,gBAAgB,CAAC,OAAe,EAAE,KAAa;IACtD,OAAO,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,KAAK,CAAC,CAAC;AACnE,CAAC;AAED,SAAS,WAAW,CAAC,OAAiB;IACpC,OAAO,GAAG,wBAAwB,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE;IACjD,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAE9C,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/B,aAAa,CAAC,aAAa,EAAE,WAAW,CAAC,wBAAwB,CAAC,CAAC,CAAC;QACpE,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,MAAM,OAAO,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,OAAO,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAE9F,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC;IAC9D,MAAM,MAAM,GAAG,UAAU;QACvB,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;QAC7B,CAAC,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;IAEhC,aAAa,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,EAAE,GAAG,MAAM,CAAC,CAAC;IACzD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,23 @@
1
+ export declare function printBanner(): void;
2
+ export declare function printUnlock(report: {
3
+ aiCalls: number;
4
+ tokensUsed: number;
5
+ resolved: Record<string, string>;
6
+ }): void;
7
+ export declare function printPreviewReady(report: {
8
+ aiCalls: number;
9
+ tokensUsed: number;
10
+ resolved: Record<string, string>;
11
+ steps?: Array<{
12
+ step: {
13
+ type: string;
14
+ };
15
+ success: boolean;
16
+ value?: string;
17
+ }>;
18
+ }): void;
19
+ export declare function printError(message: string): void;
20
+ export declare function formatStepLabel(index: number, total: number, label: string): string;
21
+ export declare function formatStepSuccess(value?: string): string;
22
+ export declare function formatStepFailure(error?: string): string;
23
+ //# sourceMappingURL=output.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,IAAI,IAAI,CAGlC;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,GAAG,IAAI,CAGP;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7E,GAAG,IAAI,CAoBP;AAqBD,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEhD;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAExD"}
@@ -0,0 +1,53 @@
1
+ import chalk from 'chalk';
2
+ export function printBanner() {
3
+ console.log(chalk.bold.cyan('\nHeramb 🐘'));
4
+ console.log(chalk.dim('Removes the obstacles between your code and production\n'));
5
+ }
6
+ export function printUnlock(report) {
7
+ console.log(chalk.green.bold('\n🔑 Unlocked\n'));
8
+ printDeploySummary(report);
9
+ }
10
+ export function printPreviewReady(report) {
11
+ console.log(chalk.green.bold('\nPreview environment ready 🔑\n'));
12
+ if (report.resolved['FRONTEND_URL']) {
13
+ console.log(` Frontend : ${report.resolved['FRONTEND_URL']}`);
14
+ }
15
+ if (report.resolved['BACKEND_URL']) {
16
+ console.log(` Backend : ${report.resolved['BACKEND_URL']}`);
17
+ }
18
+ if (report.resolved['REDIS_URL']) {
19
+ console.log(` Redis : provisioned (preview tier)`);
20
+ }
21
+ const wsStep = report.steps?.find((s) => s.step.type === 'test' && s.success);
22
+ if (wsStep?.value) {
23
+ console.log(` WS test : ✓ ${wsStep.value}`);
24
+ }
25
+ console.log('');
26
+ printDeploySummary(report);
27
+ }
28
+ function printDeploySummary(report) {
29
+ console.log(chalk.dim('─'.repeat(40)));
30
+ console.log(`AI calls this deploy : ${report.aiCalls}`);
31
+ console.log(`Tokens used : ${report.tokensUsed}`);
32
+ console.log(`Cost : $${(report.tokensUsed * 0.000002).toFixed(2)}`);
33
+ console.log(chalk.dim('─'.repeat(40)));
34
+ if (report.resolved['FRONTEND_URL']) {
35
+ console.log(chalk.cyan(`Frontend: ${report.resolved['FRONTEND_URL']}`));
36
+ }
37
+ if (report.resolved['BACKEND_URL']) {
38
+ console.log(chalk.cyan(`Backend: ${report.resolved['BACKEND_URL']}`));
39
+ }
40
+ }
41
+ export function printError(message) {
42
+ console.error(chalk.red(`\n✗ ${message}\n`));
43
+ }
44
+ export function formatStepLabel(index, total, label) {
45
+ return chalk.dim(`[${index}/${total}]`) + ' ' + label;
46
+ }
47
+ export function formatStepSuccess(value) {
48
+ return chalk.green('✓') + (value ? chalk.dim(` ${value}`) : '');
49
+ }
50
+ export function formatStepFailure(error) {
51
+ return chalk.red('✗') + (error ? chalk.red(` ${error}`) : '');
52
+ }
53
+ //# sourceMappingURL=output.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/utils/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { HerambConfig } from '@heramb1/core';
2
+ export type DeployEnv = 'production' | 'preview';
3
+ export declare function slugifyBranch(branch: string): string;
4
+ export declare function resolveBranch(explicit?: string): string | undefined;
5
+ /** Scope config to a preview branch — isolated Redis and service names */
6
+ export declare function applyPreviewContext(config: HerambConfig, branch: string): HerambConfig;
7
+ //# sourceMappingURL=preview.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/utils/preview.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,MAAM,MAAM,SAAS,GAAG,YAAY,GAAG,SAAS,CAAC;AAEjD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAQpD;AAED,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQnE;AAED,0EAA0E;AAC1E,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,GAAG,YAAY,CAatF"}
@@ -0,0 +1,31 @@
1
+ export function slugifyBranch(branch) {
2
+ return branch
3
+ .replace(/^refs\/heads\//, '')
4
+ .replace(/[^a-z0-9-]/gi, '-')
5
+ .replace(/-+/g, '-')
6
+ .replace(/^-|-$/g, '')
7
+ .toLowerCase()
8
+ .slice(0, 40);
9
+ }
10
+ export function resolveBranch(explicit) {
11
+ if (explicit)
12
+ return explicit;
13
+ return (process.env.GITHUB_HEAD_REF ??
14
+ process.env.GITHUB_REF_NAME ??
15
+ process.env.CI_BRANCH ??
16
+ undefined);
17
+ }
18
+ /** Scope config to a preview branch — isolated Redis and service names */
19
+ export function applyPreviewContext(config, branch) {
20
+ const slug = slugifyBranch(branch);
21
+ const scoped = structuredClone(config);
22
+ scoped.project = `${config.project}-${slug}`;
23
+ for (const svc of Object.values(scoped.services)) {
24
+ if ('provider' in svc) {
25
+ svc.serviceId = undefined;
26
+ svc.projectId = svc.projectId ? `${svc.projectId}-${slug}` : undefined;
27
+ }
28
+ }
29
+ return scoped;
30
+ }
31
+ //# sourceMappingURL=preview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/utils/preview.ts"],"names":[],"mappings":"AAIA,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,MAAM;SACV,OAAO,CAAC,gBAAgB,EAAE,EAAE,CAAC;SAC7B,OAAO,CAAC,cAAc,EAAE,GAAG,CAAC;SAC5B,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,WAAW,EAAE;SACb,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAAiB;IAC7C,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,eAAe;QAC3B,OAAO,CAAC,GAAG,CAAC,eAAe;QAC3B,OAAO,CAAC,GAAG,CAAC,SAAS;QACrB,SAAS,CACV,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,mBAAmB,CAAC,MAAoB,EAAE,MAAc;IACtE,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACnC,MAAM,MAAM,GAAiB,eAAe,CAAC,MAAM,CAAC,CAAC;IACrD,MAAM,CAAC,OAAO,GAAG,GAAG,MAAM,CAAC,OAAO,IAAI,IAAI,EAAE,CAAC;IAE7C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjD,IAAI,UAAU,IAAI,GAAG,EAAE,CAAC;YACtB,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;YAC1B,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QACzE,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { type DeployPlan, type ProjectRequirements, type ServiceRole } from '@heramb1/core';
2
+ import type { HerambConfig, StackDetection } from '@heramb1/core';
3
+ export declare function pickProviders(plan: DeployPlan): Promise<Record<ServiceRole, string>>;
4
+ export declare function generateConfigFromChoices(projectName: string, stack: StackDetection, choices: Record<ServiceRole, string>, req: ProjectRequirements): HerambConfig;
5
+ //# sourceMappingURL=provider-pick.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-pick.d.ts","sourceRoot":"","sources":["../../src/utils/provider-pick.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAIjB,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAElE,wBAAsB,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CA6D1F;AAED,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,cAAc,EACrB,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,EACpC,GAAG,EAAE,mBAAmB,GACvB,YAAY,CAoDd"}
@@ -0,0 +1,110 @@
1
+ import inquirer from 'inquirer';
2
+ import chalk from 'chalk';
3
+ import { validateProviderChoice, recommendForRole, getProvider, } from '@heramb1/core';
4
+ export async function pickProviders(plan) {
5
+ const choices = {};
6
+ const req = plan.requirements;
7
+ for (const svc of plan.services) {
8
+ const ranked = svc.recommendations.filter((r) => r.score > 0);
9
+ const defaultChoice = ranked.find((r) => r.implemented)?.provider ?? ranked[0]?.provider ?? 'railway';
10
+ console.log(chalk.cyan(`\n→ Where should Heramb deploy your ${svc.label.toLowerCase()}?`));
11
+ for (const warning of svc.warnings.slice(0, 2)) {
12
+ console.log(chalk.yellow(' ⚠') + ` ${warning}`);
13
+ }
14
+ const { provider } = await inquirer.prompt([
15
+ {
16
+ type: 'list',
17
+ name: 'provider',
18
+ message: `${svc.label} platform:`,
19
+ default: defaultChoice,
20
+ choices: svc.recommendations.slice(0, 8).map((r) => {
21
+ const tags = [];
22
+ if (r.implemented)
23
+ tags.push('ready');
24
+ if (r.score > 0 && r === ranked[0])
25
+ tags.push('recommended');
26
+ if (r.blockers.length > 0)
27
+ tags.push('blocked');
28
+ const suffix = tags.length > 0 ? chalk.dim(` (${tags.join(', ')})`) : '';
29
+ const label = r.score > 0 ? r.name : `${r.name} — ${r.blockers[0] ?? 'unsuitable'}`;
30
+ return {
31
+ name: `${label}${suffix}`,
32
+ value: r.provider,
33
+ disabled: r.blockers.length > 0 ? r.blockers[0] : false,
34
+ };
35
+ }),
36
+ },
37
+ ]);
38
+ const validation = validateProviderChoice(svc.role, provider, req);
39
+ for (const warning of validation.warnings) {
40
+ console.log(chalk.yellow(' ⚠') + ` ${warning}`);
41
+ }
42
+ if (!validation.ok) {
43
+ console.log(chalk.red(' ✗') + ` ${validation.blockers.join('; ')}`);
44
+ console.log(chalk.dim(' Pick a different platform for this role.\n'));
45
+ const fallback = recommendForRole(svc.role, req).find((r) => r.implemented && r.score > 0);
46
+ if (!fallback) {
47
+ throw new Error(`No suitable platform for ${svc.label}`);
48
+ }
49
+ choices[svc.role] = fallback.provider;
50
+ console.log(chalk.dim(` Using ${fallback.name} instead.`));
51
+ }
52
+ else {
53
+ choices[svc.role] = provider;
54
+ }
55
+ const guide = getProvider(provider);
56
+ if (guide) {
57
+ console.log(chalk.dim(` ${guide.name}: ${guide.signupUrl}`));
58
+ }
59
+ }
60
+ return choices;
61
+ }
62
+ export function generateConfigFromChoices(projectName, stack, choices, req) {
63
+ const config = {
64
+ project: projectName,
65
+ services: {},
66
+ wsConfig: {
67
+ pingInterval: 25000,
68
+ transport: 'websocket-only',
69
+ },
70
+ env: {
71
+ manual: ['JWT_SECRET'],
72
+ },
73
+ };
74
+ if (req.redis && choices.cache) {
75
+ config.services.redis = {
76
+ provider: choices.cache,
77
+ produces: ['REDIS_URL'],
78
+ };
79
+ }
80
+ if (stack.backend && choices.backend) {
81
+ config.services.backend = {
82
+ provider: choices.backend,
83
+ path: stack.backend.path,
84
+ needs: req.redis ? ['REDIS_URL'] : [],
85
+ produces: ['BACKEND_URL'],
86
+ derives: req.websocket
87
+ ? { WS_URL: 'BACKEND_URL → wss://' }
88
+ : undefined,
89
+ };
90
+ }
91
+ if (stack.frontend && choices.frontend) {
92
+ const isVite = stack.frontend.framework === 'vite';
93
+ config.services.frontend = {
94
+ provider: choices.frontend,
95
+ path: stack.frontend.path,
96
+ needs: isVite
97
+ ? ['VITE_API_URL']
98
+ : ['NEXT_PUBLIC_API_URL', ...(req.websocket ? ['NEXT_PUBLIC_WS_URL'] : [])],
99
+ produces: ['FRONTEND_URL'],
100
+ };
101
+ config.services['backend-cors'] = {
102
+ action: 'patch-env',
103
+ target: 'backend',
104
+ vars: isVite ? { WEB_URL: 'FRONTEND_URL' } : { CORS_ORIGIN: 'FRONTEND_URL' },
105
+ then: 'redeploy',
106
+ };
107
+ }
108
+ return config;
109
+ }
110
+ //# sourceMappingURL=provider-pick.js.map