@agentuity/cli 2.0.12 → 2.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-detection.d.ts.map +1 -1
- package/dist/agent-detection.js +1 -0
- package/dist/agent-detection.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +15 -8
- package/dist/cli.js.map +1 -1
- package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/create.js +46 -4
- package/dist/cmd/cloud/sandbox/create.js.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/exec.js +4 -3
- package/dist/cmd/cloud/sandbox/exec.js.map +1 -1
- package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -1
- package/dist/cmd/cloud/sandbox/run.js +9 -5
- package/dist/cmd/cloud/sandbox/run.js.map +1 -1
- package/dist/cmd/cloud/sandbox/snapshot/create.js +4 -4
- package/dist/cmd/cloud/sandbox/snapshot/create.js.map +1 -1
- package/dist/cmd/coder/start.d.ts.map +1 -1
- package/dist/cmd/coder/start.js +1 -0
- package/dist/cmd/coder/start.js.map +1 -1
- package/dist/cmd/coder/workspace/common.d.ts +29 -0
- package/dist/cmd/coder/workspace/common.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/common.js +83 -0
- package/dist/cmd/coder/workspace/common.js.map +1 -0
- package/dist/cmd/coder/workspace/create.d.ts.map +1 -1
- package/dist/cmd/coder/workspace/create.js +34 -37
- package/dist/cmd/coder/workspace/create.js.map +1 -1
- package/dist/cmd/coder/workspace/get.d.ts.map +1 -1
- package/dist/cmd/coder/workspace/get.js +2 -5
- package/dist/cmd/coder/workspace/get.js.map +1 -1
- package/dist/cmd/coder/workspace/index.d.ts.map +1 -1
- package/dist/cmd/coder/workspace/index.js +10 -0
- package/dist/cmd/coder/workspace/index.js.map +1 -1
- package/dist/cmd/coder/workspace/list.d.ts.map +1 -1
- package/dist/cmd/coder/workspace/list.js +4 -0
- package/dist/cmd/coder/workspace/list.js.map +1 -1
- package/dist/cmd/coder/workspace/refresh.d.ts +2 -0
- package/dist/cmd/coder/workspace/refresh.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/refresh.js +59 -0
- package/dist/cmd/coder/workspace/refresh.js.map +1 -0
- package/dist/cmd/coder/workspace/update.d.ts +2 -0
- package/dist/cmd/coder/workspace/update.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/update.js +131 -0
- package/dist/cmd/coder/workspace/update.js.map +1 -0
- package/dist/cmd/coder/workspace/validate-dependencies.d.ts +2 -0
- package/dist/cmd/coder/workspace/validate-dependencies.d.ts.map +1 -0
- package/dist/cmd/coder/workspace/validate-dependencies.js +70 -0
- package/dist/cmd/coder/workspace/validate-dependencies.js.map +1 -0
- package/dist/cmd/project/random-name.d.ts +17 -0
- package/dist/cmd/project/random-name.d.ts.map +1 -0
- package/dist/cmd/project/random-name.js +144 -0
- package/dist/cmd/project/random-name.js.map +1 -0
- package/dist/cmd/project/template-flow.d.ts.map +1 -1
- package/dist/cmd/project/template-flow.js +181 -153
- package/dist/cmd/project/template-flow.js.map +1 -1
- package/dist/composite-logger.d.ts.map +1 -1
- package/dist/composite-logger.js +19 -0
- package/dist/composite-logger.js.map +1 -1
- package/dist/config.d.ts +18 -16
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +46 -16
- package/dist/config.js.map +1 -1
- package/dist/tui/prompt.d.ts +29 -0
- package/dist/tui/prompt.d.ts.map +1 -1
- package/dist/tui/prompt.js +180 -8
- package/dist/tui/prompt.js.map +1 -1
- package/package.json +7 -7
- package/src/agent-detection.ts +1 -0
- package/src/cli.ts +30 -8
- package/src/cmd/cloud/sandbox/create.ts +57 -4
- package/src/cmd/cloud/sandbox/exec.ts +4 -3
- package/src/cmd/cloud/sandbox/run.ts +9 -5
- package/src/cmd/cloud/sandbox/snapshot/create.ts +6 -6
- package/src/cmd/coder/start.ts +1 -0
- package/src/cmd/coder/workspace/common.ts +103 -0
- package/src/cmd/coder/workspace/create.ts +39 -43
- package/src/cmd/coder/workspace/get.ts +2 -5
- package/src/cmd/coder/workspace/index.ts +10 -0
- package/src/cmd/coder/workspace/list.ts +4 -0
- package/src/cmd/coder/workspace/refresh.ts +63 -0
- package/src/cmd/coder/workspace/update.ts +154 -0
- package/src/cmd/coder/workspace/validate-dependencies.ts +75 -0
- package/src/cmd/project/random-name.ts +152 -0
- package/src/cmd/project/template-flow.ts +199 -161
- package/src/composite-logger.ts +20 -0
- package/src/config.ts +69 -19
- package/src/tui/prompt.ts +214 -8
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates project-derived suggestions for resource names (DB / S3 bucket).
|
|
3
|
+
*
|
|
4
|
+
* The CLI shows these as a dim "press Enter to use ..." default in the create flow.
|
|
5
|
+
* If the user presses Enter, the suggestion is sent to the server. Otherwise the
|
|
6
|
+
* server is responsible for assigning a name when none is provided.
|
|
7
|
+
*
|
|
8
|
+
* Both suggestions are validated against the same rules the server enforces
|
|
9
|
+
* (`validateBucketName` / `validateDatabaseName` from `@agentuity/server`) so the
|
|
10
|
+
* happy path can never produce an invalid suggestion.
|
|
11
|
+
*/
|
|
12
|
+
import { validateBucketName, validateDatabaseName } from '@agentuity/server';
|
|
13
|
+
const BUCKET_MAX = 63;
|
|
14
|
+
const BUCKET_MIN = 3;
|
|
15
|
+
const DB_MAX = 63;
|
|
16
|
+
/** 3 lowercase alphanumeric chars, e.g. "k7p". */
|
|
17
|
+
function shortSuffix() {
|
|
18
|
+
// toString(36) yields [0-9a-z]; slice 3 chars after the "0." prefix.
|
|
19
|
+
const s = Math.random().toString(36).slice(2, 5);
|
|
20
|
+
// Pad in the (extremely unlikely) case the slice is shorter than 3 chars.
|
|
21
|
+
return s.length === 3 ? s : (s + '000').slice(0, 3);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Sanitize a project name into the bucket-name alphabet:
|
|
25
|
+
* - lowercase
|
|
26
|
+
* - spaces / underscores / dots → hyphens
|
|
27
|
+
* - drop anything else
|
|
28
|
+
* - collapse and trim hyphens
|
|
29
|
+
* - strip reserved prefixes (`agentuity*`, `ag-*`, `ago-*`, `xn--`)
|
|
30
|
+
*/
|
|
31
|
+
function sanitizeForBucket(name) {
|
|
32
|
+
let out = name
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.trim()
|
|
35
|
+
.replace(/[\s_.]+/g, '-')
|
|
36
|
+
.replace(/[^a-z0-9-]/g, '')
|
|
37
|
+
.replace(/-+/g, '-')
|
|
38
|
+
.replace(/^-+|-+$/g, '');
|
|
39
|
+
// Strip reserved prefixes (rules from validateBucketName).
|
|
40
|
+
while (out.startsWith('agentuity') ||
|
|
41
|
+
out.startsWith('ag-') ||
|
|
42
|
+
out.startsWith('ago-') ||
|
|
43
|
+
out.startsWith('xn--')) {
|
|
44
|
+
if (out.startsWith('agentuity'))
|
|
45
|
+
out = out.slice('agentuity'.length);
|
|
46
|
+
else if (out.startsWith('ago-'))
|
|
47
|
+
out = out.slice('ago-'.length);
|
|
48
|
+
else if (out.startsWith('ag-'))
|
|
49
|
+
out = out.slice('ag-'.length);
|
|
50
|
+
else if (out.startsWith('xn--'))
|
|
51
|
+
out = out.slice('xn--'.length);
|
|
52
|
+
out = out.replace(/^-+/, '');
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Sanitize a project name into the database-name alphabet:
|
|
58
|
+
* - lowercase
|
|
59
|
+
* - non `[a-z0-9_]` → `_`
|
|
60
|
+
* - collapse and trim underscores
|
|
61
|
+
* - ensure it starts with a letter or underscore (prepend `p_` otherwise)
|
|
62
|
+
* - strip reserved `pg_` prefix
|
|
63
|
+
*/
|
|
64
|
+
function sanitizeForDatabase(name) {
|
|
65
|
+
let out = name
|
|
66
|
+
.toLowerCase()
|
|
67
|
+
.trim()
|
|
68
|
+
.replace(/[^a-z0-9_]+/g, '_')
|
|
69
|
+
.replace(/_+/g, '_')
|
|
70
|
+
.replace(/^_+|_+$/g, '');
|
|
71
|
+
if (!/^[a-z_]/.test(out)) {
|
|
72
|
+
out = out.length > 0 ? `p_${out}` : '';
|
|
73
|
+
}
|
|
74
|
+
while (out.startsWith('pg_')) {
|
|
75
|
+
out = out.slice(3).replace(/^_+/, '');
|
|
76
|
+
if (!/^[a-z_]/.test(out) && out.length > 0)
|
|
77
|
+
out = `p_${out}`;
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Truncate so the final string (`<base>-<suffixWithDash>`) fits within `max` chars
|
|
83
|
+
* while keeping the suffix intact and not ending on a hyphen.
|
|
84
|
+
*/
|
|
85
|
+
function truncateBaseHyphen(base, suffixWithDash, max) {
|
|
86
|
+
const room = max - suffixWithDash.length;
|
|
87
|
+
if (room <= 0)
|
|
88
|
+
return '';
|
|
89
|
+
let out = base.slice(0, room);
|
|
90
|
+
out = out.replace(/-+$/, '');
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
/** Same as `truncateBaseHyphen` but for underscore-joined names (database). */
|
|
94
|
+
function truncateBaseUnderscore(base, suffixWithUnderscore, max) {
|
|
95
|
+
const room = max - suffixWithUnderscore.length;
|
|
96
|
+
if (room <= 0)
|
|
97
|
+
return '';
|
|
98
|
+
let out = base.slice(0, room);
|
|
99
|
+
out = out.replace(/_+$/, '');
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Generate a suggested S3 bucket name derived from the project name.
|
|
104
|
+
* Format: `<sanitized-project>-storage-<3char>` (≤ 63 chars).
|
|
105
|
+
*
|
|
106
|
+
* Falls back to `bucket-<3char><3char>` if the project name produces nothing usable.
|
|
107
|
+
* Always returns a value that passes `validateBucketName`.
|
|
108
|
+
*/
|
|
109
|
+
export function suggestBucketName(projectName) {
|
|
110
|
+
const sanitized = sanitizeForBucket(projectName);
|
|
111
|
+
// Try a few times in case sanitization + suffix happens to land on something invalid.
|
|
112
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
113
|
+
const suffix = `-storage-${shortSuffix()}`;
|
|
114
|
+
const base = truncateBaseHyphen(sanitized, suffix, BUCKET_MAX);
|
|
115
|
+
const candidate = base.length > 0 ? `${base}${suffix}` : `bucket${suffix}`;
|
|
116
|
+
if (candidate.length >= BUCKET_MIN && validateBucketName(candidate).valid) {
|
|
117
|
+
return candidate;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Pure fallback: short, always-valid generic name.
|
|
121
|
+
const fallback = `bucket-${shortSuffix()}${shortSuffix()}`;
|
|
122
|
+
return validateBucketName(fallback).valid ? fallback : `bucket-${shortSuffix()}aaa`;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Generate a suggested PostgreSQL database name derived from the project name.
|
|
126
|
+
* Format: `<sanitized-project>_db_<3char>` (≤ 63 chars).
|
|
127
|
+
*
|
|
128
|
+
* Falls back to `db_<3char><3char>` if the project name produces nothing usable.
|
|
129
|
+
* Always returns a value that passes `validateDatabaseName`.
|
|
130
|
+
*/
|
|
131
|
+
export function suggestDatabaseName(projectName) {
|
|
132
|
+
const sanitized = sanitizeForDatabase(projectName);
|
|
133
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
134
|
+
const suffix = `_db_${shortSuffix()}`;
|
|
135
|
+
const base = truncateBaseUnderscore(sanitized, suffix, DB_MAX);
|
|
136
|
+
const candidate = base.length > 0 ? `${base}${suffix}` : `db${suffix}`;
|
|
137
|
+
if (validateDatabaseName(candidate).valid) {
|
|
138
|
+
return candidate;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const fallback = `db_${shortSuffix()}${shortSuffix()}`;
|
|
142
|
+
return validateDatabaseName(fallback).valid ? fallback : `db_${shortSuffix()}aaa`;
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=random-name.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"random-name.js","sourceRoot":"","sources":["../../../src/cmd/project/random-name.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"template-flow.d.ts","sourceRoot":"","sources":["../../../src/cmd/project/template-flow.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAY9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAe3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"template-flow.d.ts","sourceRoot":"","sources":["../../../src/cmd/project/template-flow.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAY9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAe3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAUpD,UAAU,iBAAiB;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAynBzF"}
|
|
@@ -12,7 +12,10 @@ import * as tui from '../../tui';
|
|
|
12
12
|
import { createPrompt, note } from '../../tui';
|
|
13
13
|
import { getGithubBotIdentity } from '../git/api';
|
|
14
14
|
import { downloadTemplate, initGitRepo, setupProject } from './download';
|
|
15
|
+
import { suggestBucketName, suggestDatabaseName } from './random-name';
|
|
15
16
|
import { fetchTemplates } from './templates';
|
|
17
|
+
// Domain validator shared between the multi-select branch and the standalone prompt.
|
|
18
|
+
const DOMAIN_REGEX = /^(?=.{1,253}$)(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[A-Za-z]{2,63}$/;
|
|
16
19
|
export async function runCreateFlow(options) {
|
|
17
20
|
const { projectName: initialProjectName, dir: targetDir, template: initialTemplate, templateDir, templateBranch, skipPrompts, logger, auth, config, orgId: selectedOrgId, region, apiClient, domains, database: databaseOption, storage: storageOption, } = options;
|
|
18
21
|
const isHeadless = !process.stdin.isTTY || !process.stdout.isTTY;
|
|
@@ -187,7 +190,7 @@ export async function runCreateFlow(options) {
|
|
|
187
190
|
error: 'Project setup completed with errors',
|
|
188
191
|
};
|
|
189
192
|
}
|
|
190
|
-
//
|
|
193
|
+
// Resource provisioning gates
|
|
191
194
|
const canProvision = auth && apiClient && catalystClient && orgId && region;
|
|
192
195
|
// Only count as resource flags if actually requesting provisioning (not explicit skip)
|
|
193
196
|
const hasResourceFlags = (databaseOption !== undefined && databaseOption.toLowerCase() !== 'skip') ||
|
|
@@ -204,11 +207,56 @@ export async function runCreateFlow(options) {
|
|
|
204
207
|
'Remove --no-register or omit --database/--storage flags.', ErrorCode.VALIDATION_FAILED);
|
|
205
208
|
}
|
|
206
209
|
if (canProvision) {
|
|
207
|
-
//
|
|
210
|
+
// CLI flags pre-resolve their respective per-resource decision; the multi-select
|
|
211
|
+
// is only used for resources where the user didn't pass a flag.
|
|
212
|
+
const dbFlagAction = resolveFlagAction(databaseOption, 'database');
|
|
213
|
+
const storageFlagAction = resolveFlagAction(storageOption, 'storage');
|
|
214
|
+
const domainFlagProvided = (domains?.length ?? 0) > 0;
|
|
215
|
+
// Determine which resources should run through the configuration phase.
|
|
216
|
+
// In interactive mode, ask the user via a single multi-select.
|
|
217
|
+
// In headless / non-interactive mode, only flagged resources are considered.
|
|
218
|
+
let wantDb = dbFlagAction !== undefined && dbFlagAction !== 'Skip';
|
|
219
|
+
let wantStorage = storageFlagAction !== undefined && storageFlagAction !== 'Skip';
|
|
220
|
+
let wantDomain = domainFlagProvided;
|
|
221
|
+
if (isInteractive) {
|
|
222
|
+
// Build multi-select options dynamically: only show resources the user hasn't
|
|
223
|
+
// already decided about via CLI flags. If all three came from flags, skip the prompt.
|
|
224
|
+
const msOptions = [];
|
|
225
|
+
if (dbFlagAction === undefined) {
|
|
226
|
+
msOptions.push({ value: 'database', label: 'SQL Database', hint: 'PostgreSQL' });
|
|
227
|
+
}
|
|
228
|
+
if (storageFlagAction === undefined) {
|
|
229
|
+
msOptions.push({ value: 'storage', label: 'Storage Bucket', hint: 'S3-compatible' });
|
|
230
|
+
}
|
|
231
|
+
if (!domainFlagProvided) {
|
|
232
|
+
msOptions.push({ value: 'domain', label: 'Custom Domain', hint: 'BYO domain' });
|
|
233
|
+
}
|
|
234
|
+
if (msOptions.length > 0) {
|
|
235
|
+
const picked = await prompt.multiselect({
|
|
236
|
+
message: 'What would you like to set up? (all optional)',
|
|
237
|
+
options: msOptions,
|
|
238
|
+
initial: [],
|
|
239
|
+
});
|
|
240
|
+
if (dbFlagAction === undefined)
|
|
241
|
+
wantDb = picked.includes('database');
|
|
242
|
+
if (storageFlagAction === undefined)
|
|
243
|
+
wantStorage = picked.includes('storage');
|
|
244
|
+
if (!domainFlagProvided)
|
|
245
|
+
wantDomain = picked.includes('domain');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
// Fetch existing resources only if we'll actually need them.
|
|
249
|
+
// Need them when:
|
|
250
|
+
// - user wants db/storage in interactive mode (to offer "use existing")
|
|
251
|
+
// - a CLI flag pointed at an existing resource by name
|
|
208
252
|
let resources;
|
|
209
|
-
const needResources = isInteractive ||
|
|
210
|
-
(databaseOption
|
|
211
|
-
|
|
253
|
+
const needResources = (isInteractive && (wantDb || wantStorage)) ||
|
|
254
|
+
(databaseOption !== undefined &&
|
|
255
|
+
dbFlagAction !== 'Create New' &&
|
|
256
|
+
dbFlagAction !== 'Skip') ||
|
|
257
|
+
(storageOption !== undefined &&
|
|
258
|
+
storageFlagAction !== 'Create New' &&
|
|
259
|
+
storageFlagAction !== 'Skip');
|
|
212
260
|
if (needResources) {
|
|
213
261
|
resources = await tui.spinner({
|
|
214
262
|
message: 'Fetching resources',
|
|
@@ -221,204 +269,163 @@ export async function runCreateFlow(options) {
|
|
|
221
269
|
logger.debug(`Resources for org ${orgId} in region ${region}: ${resources.db.length} databases, ${resources.s3.length} storage buckets`);
|
|
222
270
|
logger.debug(`Database names: ${resources.db.map((d) => d.name).join(', ') || '(none)'}`);
|
|
223
271
|
logger.debug(`Storage buckets: ${resources.s3.map((b) => b.bucket_name).join(', ') || '(none)'}`);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
db_action = 'Create New';
|
|
272
|
+
// Validate flag-supplied resource names against the fetched list.
|
|
273
|
+
if (databaseOption !== undefined &&
|
|
274
|
+
dbFlagAction !== 'Create New' &&
|
|
275
|
+
dbFlagAction !== 'Skip' &&
|
|
276
|
+
!resources.db.find((d) => d.name === dbFlagAction)) {
|
|
277
|
+
logger.fatal(`Database '${databaseOption}' not found. Use 'new' to create a new database or 'skip' to skip.`, ErrorCode.RESOURCE_NOT_FOUND);
|
|
231
278
|
}
|
|
232
|
-
|
|
233
|
-
|
|
279
|
+
if (storageOption !== undefined &&
|
|
280
|
+
storageFlagAction !== 'Create New' &&
|
|
281
|
+
storageFlagAction !== 'Skip' &&
|
|
282
|
+
!resources.s3.find((b) => b.bucket_name === storageFlagAction)) {
|
|
283
|
+
logger.fatal(`Storage bucket '${storageOption}' not found. Use 'new' to create a new bucket or 'skip' to skip.`, ErrorCode.RESOURCE_NOT_FOUND);
|
|
234
284
|
}
|
|
235
|
-
else {
|
|
236
|
-
// Existing database name - validate it exists
|
|
237
|
-
const existingDb = resources?.db.find((d) => d.name === databaseOption);
|
|
238
|
-
if (!existingDb) {
|
|
239
|
-
logger.fatal(`Database '${databaseOption}' not found. Use 'new' to create a new database or 'skip' to skip.`, ErrorCode.RESOURCE_NOT_FOUND);
|
|
240
|
-
}
|
|
241
|
-
db_action = databaseOption;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
else if (isInteractive) {
|
|
245
|
-
db_action = await prompt.select({
|
|
246
|
-
message: 'Create SQL Database?',
|
|
247
|
-
options: [
|
|
248
|
-
{ value: 'Skip', label: 'Skip or Setup later' },
|
|
249
|
-
{ value: 'Create New', label: 'Create a new database' },
|
|
250
|
-
...resources.db.map((db) => ({
|
|
251
|
-
value: db.name,
|
|
252
|
-
label: `Use database: ${tui.tuiColors.primary(db.name)}`,
|
|
253
|
-
})),
|
|
254
|
-
],
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
// Headless without flag - skip
|
|
259
|
-
db_action = 'Skip';
|
|
260
285
|
}
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
if (
|
|
264
|
-
|
|
265
|
-
if (
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
286
|
+
// === Configure each selected resource: Database → Storage → Domain ===
|
|
287
|
+
// Database
|
|
288
|
+
if (wantDb) {
|
|
289
|
+
let dbAction = dbFlagAction;
|
|
290
|
+
if (dbAction === undefined && isInteractive) {
|
|
291
|
+
const existing = resources?.db ?? [];
|
|
292
|
+
if (existing.length > 0) {
|
|
293
|
+
dbAction = await prompt.select({
|
|
294
|
+
message: 'SQL Database',
|
|
295
|
+
options: [
|
|
296
|
+
{ value: 'Create New', label: 'Create a new database' },
|
|
297
|
+
...existing.map((db) => ({
|
|
298
|
+
value: db.name,
|
|
299
|
+
label: `Use database: ${tui.tuiColors.primary(db.name)}`,
|
|
300
|
+
})),
|
|
301
|
+
],
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
// No existing databases — user already opted in via the multi-select, so create new.
|
|
306
|
+
dbAction = 'Create New';
|
|
276
307
|
}
|
|
277
|
-
s3_action = storageOption;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
else if (isInteractive) {
|
|
281
|
-
s3_action = await prompt.select({
|
|
282
|
-
message: 'Create Storage Bucket?',
|
|
283
|
-
options: [
|
|
284
|
-
{ value: 'Skip', label: 'Skip or Setup later' },
|
|
285
|
-
{ value: 'Create New', label: 'Create a new bucket' },
|
|
286
|
-
...resources.s3.map((bucket) => ({
|
|
287
|
-
value: bucket.bucket_name,
|
|
288
|
-
label: `Use bucket: ${tui.tuiColors.primary(bucket.bucket_name)}`,
|
|
289
|
-
})),
|
|
290
|
-
],
|
|
291
|
-
});
|
|
292
|
-
}
|
|
293
|
-
else {
|
|
294
|
-
// Headless without flag - skip
|
|
295
|
-
s3_action = 'Skip';
|
|
296
|
-
}
|
|
297
|
-
// Custom DNS: only prompt in interactive mode if not already provided
|
|
298
|
-
if (!domains?.length && isInteractive) {
|
|
299
|
-
const customDns = await prompt.text({
|
|
300
|
-
message: 'Setup custom DNS?',
|
|
301
|
-
hint: 'Enter a domain name or press Enter to skip',
|
|
302
|
-
validate: (val) => val === ''
|
|
303
|
-
? true
|
|
304
|
-
: /^(?=.{1,253}$)(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[A-Za-z]{2,63}$/.test(val),
|
|
305
|
-
});
|
|
306
|
-
if (customDns) {
|
|
307
|
-
_domains = [customDns];
|
|
308
308
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
case 'Create New': {
|
|
313
|
-
let bucketName;
|
|
314
|
-
let bucketDescription;
|
|
315
|
-
// Only prompt for name/description in interactive mode
|
|
309
|
+
if (dbAction === 'Create New') {
|
|
310
|
+
let dbName;
|
|
311
|
+
let dbDescription;
|
|
316
312
|
if (isInteractive) {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
313
|
+
const suggestion = suggestDatabaseName(projectName);
|
|
314
|
+
const dbNameInput = await prompt.text({
|
|
315
|
+
message: 'Database name',
|
|
316
|
+
hint: 'Optional · lowercase letters, digits, underscores',
|
|
317
|
+
placeholder: suggestion,
|
|
320
318
|
validate: (value) => {
|
|
321
319
|
const trimmed = value.trim();
|
|
322
320
|
if (trimmed === '')
|
|
323
321
|
return true;
|
|
324
|
-
const result =
|
|
322
|
+
const result = validateDatabaseName(trimmed);
|
|
325
323
|
return result.valid ? true : result.error;
|
|
326
324
|
},
|
|
327
325
|
});
|
|
328
|
-
|
|
329
|
-
|
|
326
|
+
dbName = dbNameInput.trim() || undefined;
|
|
327
|
+
dbDescription =
|
|
330
328
|
(await prompt.text({
|
|
331
|
-
message: '
|
|
332
|
-
hint: 'Optional
|
|
329
|
+
message: 'Database description',
|
|
330
|
+
hint: 'Optional · press Enter to skip',
|
|
333
331
|
})) || undefined;
|
|
334
332
|
}
|
|
335
333
|
const created = await tui.spinner({
|
|
336
|
-
message: 'Provisioning New
|
|
334
|
+
message: 'Provisioning New SQL Database',
|
|
337
335
|
clearOnSuccess: true,
|
|
338
336
|
callback: async () => {
|
|
339
337
|
return createResources(catalystClient, orgId, region, [
|
|
340
|
-
{
|
|
341
|
-
type: 's3',
|
|
342
|
-
name: bucketName,
|
|
343
|
-
description: bucketDescription,
|
|
344
|
-
},
|
|
338
|
+
{ type: 'db', name: dbName, description: dbDescription },
|
|
345
339
|
]);
|
|
346
340
|
},
|
|
347
341
|
});
|
|
348
|
-
|
|
349
|
-
if (created[0]?.env) {
|
|
342
|
+
if (created[0]?.env)
|
|
350
343
|
Object.assign(resourceEnvVars, created[0].env);
|
|
351
|
-
}
|
|
352
|
-
break;
|
|
353
344
|
}
|
|
354
|
-
|
|
355
|
-
|
|
345
|
+
else if (dbAction && dbAction !== 'Skip') {
|
|
346
|
+
// Existing database selected — reuse its env vars.
|
|
347
|
+
const selectedDb = resources?.db.find((d) => d.name === dbAction);
|
|
348
|
+
if (selectedDb?.env)
|
|
349
|
+
Object.assign(resourceEnvVars, selectedDb.env);
|
|
356
350
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
351
|
+
}
|
|
352
|
+
// Storage
|
|
353
|
+
if (wantStorage) {
|
|
354
|
+
let s3Action = storageFlagAction;
|
|
355
|
+
if (s3Action === undefined && isInteractive) {
|
|
356
|
+
const existing = resources?.s3 ?? [];
|
|
357
|
+
if (existing.length > 0) {
|
|
358
|
+
s3Action = await prompt.select({
|
|
359
|
+
message: 'Storage Bucket',
|
|
360
|
+
options: [
|
|
361
|
+
{ value: 'Create New', label: 'Create a new bucket' },
|
|
362
|
+
...existing.map((bucket) => ({
|
|
363
|
+
value: bucket.bucket_name,
|
|
364
|
+
label: `Use bucket: ${tui.tuiColors.primary(bucket.bucket_name)}`,
|
|
365
|
+
})),
|
|
366
|
+
],
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
s3Action = 'Create New';
|
|
362
371
|
}
|
|
363
|
-
break;
|
|
364
372
|
}
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
case 'Create New': {
|
|
369
|
-
let dbName;
|
|
370
|
-
let dbDescription;
|
|
371
|
-
// Only prompt for name/description in interactive mode
|
|
373
|
+
if (s3Action === 'Create New') {
|
|
374
|
+
let bucketName;
|
|
375
|
+
let bucketDescription;
|
|
372
376
|
if (isInteractive) {
|
|
373
|
-
const
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
const suggestion = suggestBucketName(projectName);
|
|
378
|
+
const bucketNameInput = await prompt.text({
|
|
379
|
+
message: 'Bucket name',
|
|
380
|
+
hint: 'Optional · lowercase letters, digits, hyphens',
|
|
381
|
+
placeholder: suggestion,
|
|
376
382
|
validate: (value) => {
|
|
377
383
|
const trimmed = value.trim();
|
|
378
384
|
if (trimmed === '')
|
|
379
385
|
return true;
|
|
380
|
-
const result =
|
|
386
|
+
const result = validateBucketName(trimmed);
|
|
381
387
|
return result.valid ? true : result.error;
|
|
382
388
|
},
|
|
383
389
|
});
|
|
384
|
-
|
|
385
|
-
|
|
390
|
+
bucketName = bucketNameInput.trim() || undefined;
|
|
391
|
+
bucketDescription =
|
|
386
392
|
(await prompt.text({
|
|
387
|
-
message: '
|
|
388
|
-
hint: 'Optional
|
|
393
|
+
message: 'Bucket description',
|
|
394
|
+
hint: 'Optional · press Enter to skip',
|
|
389
395
|
})) || undefined;
|
|
390
396
|
}
|
|
391
397
|
const created = await tui.spinner({
|
|
392
|
-
message: 'Provisioning New
|
|
398
|
+
message: 'Provisioning New Bucket',
|
|
393
399
|
clearOnSuccess: true,
|
|
394
400
|
callback: async () => {
|
|
395
401
|
return createResources(catalystClient, orgId, region, [
|
|
396
|
-
{
|
|
397
|
-
type: 'db',
|
|
398
|
-
name: dbName,
|
|
399
|
-
description: dbDescription,
|
|
400
|
-
},
|
|
402
|
+
{ type: 's3', name: bucketName, description: bucketDescription },
|
|
401
403
|
]);
|
|
402
404
|
},
|
|
403
405
|
});
|
|
404
|
-
|
|
405
|
-
if (created[0]?.env) {
|
|
406
|
+
if (created[0]?.env)
|
|
406
407
|
Object.assign(resourceEnvVars, created[0].env);
|
|
407
|
-
}
|
|
408
|
-
break;
|
|
409
408
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
// User selected an existing database - get env vars from the resources list
|
|
415
|
-
const selectedDb = resources?.db.find((d) => d.name === db_action);
|
|
416
|
-
if (selectedDb?.env) {
|
|
417
|
-
Object.assign(resourceEnvVars, selectedDb.env);
|
|
418
|
-
}
|
|
419
|
-
break;
|
|
409
|
+
else if (s3Action && s3Action !== 'Skip') {
|
|
410
|
+
const selectedBucket = resources?.s3.find((b) => b.bucket_name === s3Action);
|
|
411
|
+
if (selectedBucket?.env)
|
|
412
|
+
Object.assign(resourceEnvVars, selectedBucket.env);
|
|
420
413
|
}
|
|
421
414
|
}
|
|
415
|
+
// Custom Domain
|
|
416
|
+
if (wantDomain && !domainFlagProvided && isInteractive) {
|
|
417
|
+
const customDns = await prompt.text({
|
|
418
|
+
message: 'Custom domain',
|
|
419
|
+
hint: 'e.g. agents.example.com',
|
|
420
|
+
validate: (val) => val === ''
|
|
421
|
+
? 'Domain is required (or go back and uncheck Custom Domain)'
|
|
422
|
+
: DOMAIN_REGEX.test(val)
|
|
423
|
+
? true
|
|
424
|
+
: 'Invalid domain',
|
|
425
|
+
});
|
|
426
|
+
if (customDns)
|
|
427
|
+
_domains = [customDns];
|
|
428
|
+
}
|
|
422
429
|
}
|
|
423
430
|
let projectId;
|
|
424
431
|
if (auth && apiClient && orgId) {
|
|
@@ -553,6 +560,27 @@ export async function runCreateFlow(options) {
|
|
|
553
560
|
error: setupResult.success ? undefined : 'Project setup completed with errors',
|
|
554
561
|
};
|
|
555
562
|
}
|
|
563
|
+
/**
|
|
564
|
+
* Normalize a CLI flag value (`--database` / `--storage`) into the same
|
|
565
|
+
* action vocabulary the interactive flow uses:
|
|
566
|
+
* - 'new' -> 'Create New'
|
|
567
|
+
* - 'skip' -> 'Skip'
|
|
568
|
+
* - any other string -> treated as an existing-resource name (returned as-is)
|
|
569
|
+
* - undefined -> undefined (no flag passed; multi-select decides)
|
|
570
|
+
*
|
|
571
|
+
* The existence check for named resources happens later, after the resource
|
|
572
|
+
* list is fetched.
|
|
573
|
+
*/
|
|
574
|
+
function resolveFlagAction(flag, _kind) {
|
|
575
|
+
if (flag === undefined)
|
|
576
|
+
return undefined;
|
|
577
|
+
const lower = flag.toLowerCase();
|
|
578
|
+
if (lower === 'new')
|
|
579
|
+
return 'Create New';
|
|
580
|
+
if (lower === 'skip')
|
|
581
|
+
return 'Skip';
|
|
582
|
+
return flag;
|
|
583
|
+
}
|
|
556
584
|
/**
|
|
557
585
|
* Sanitize a project name to create a safe directory/package name
|
|
558
586
|
* - Converts to lowercase
|