@agentuity/cli 0.0.101 → 0.0.102

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 (210) hide show
  1. package/AGENTS.md +19 -188
  2. package/bin/cli.ts +13 -6
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +41 -12
  5. package/dist/cli.js.map +1 -1
  6. package/dist/cmd/ai/index.d.ts.map +1 -1
  7. package/dist/cmd/ai/index.js +6 -1
  8. package/dist/cmd/ai/index.js.map +1 -1
  9. package/dist/cmd/ai/prompt/agent.d.ts +7 -0
  10. package/dist/cmd/ai/prompt/agent.d.ts.map +1 -1
  11. package/dist/cmd/ai/prompt/agent.js +12 -323
  12. package/dist/cmd/ai/prompt/agent.js.map +1 -1
  13. package/dist/cmd/ai/prompt/api.d.ts +7 -0
  14. package/dist/cmd/ai/prompt/api.d.ts.map +1 -1
  15. package/dist/cmd/ai/prompt/api.js +12 -260
  16. package/dist/cmd/ai/prompt/api.js.map +1 -1
  17. package/dist/cmd/ai/prompt/version.d.ts +35 -0
  18. package/dist/cmd/ai/prompt/version.d.ts.map +1 -0
  19. package/dist/cmd/ai/prompt/version.js +55 -0
  20. package/dist/cmd/ai/prompt/version.js.map +1 -0
  21. package/dist/cmd/ai/prompt/web.d.ts +7 -0
  22. package/dist/cmd/ai/prompt/web.d.ts.map +1 -1
  23. package/dist/cmd/ai/prompt/web.js +12 -283
  24. package/dist/cmd/ai/prompt/web.js.map +1 -1
  25. package/dist/cmd/ai/skills/generate.d.ts +3 -0
  26. package/dist/cmd/ai/skills/generate.d.ts.map +1 -0
  27. package/dist/cmd/ai/skills/generate.js +65 -0
  28. package/dist/cmd/ai/skills/generate.js.map +1 -0
  29. package/dist/cmd/ai/skills/generator.d.ts +4 -0
  30. package/dist/cmd/ai/skills/generator.d.ts.map +1 -0
  31. package/dist/cmd/ai/skills/generator.js +402 -0
  32. package/dist/cmd/ai/skills/generator.js.map +1 -0
  33. package/dist/cmd/ai/skills/index.d.ts +4 -0
  34. package/dist/cmd/ai/skills/index.d.ts.map +1 -0
  35. package/dist/cmd/ai/skills/index.js +21 -0
  36. package/dist/cmd/ai/skills/index.js.map +1 -0
  37. package/dist/cmd/auth/signup.d.ts.map +1 -1
  38. package/dist/cmd/auth/signup.js +1 -0
  39. package/dist/cmd/auth/signup.js.map +1 -1
  40. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  41. package/dist/cmd/build/entry-generator.js +40 -5
  42. package/dist/cmd/build/entry-generator.js.map +1 -1
  43. package/dist/cmd/build/vite/bun-dev-server.d.ts +7 -1
  44. package/dist/cmd/build/vite/bun-dev-server.d.ts.map +1 -1
  45. package/dist/cmd/build/vite/bun-dev-server.js +52 -26
  46. package/dist/cmd/build/vite/bun-dev-server.js.map +1 -1
  47. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  48. package/dist/cmd/build/vite/metadata-generator.js +58 -7
  49. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  50. package/dist/cmd/build/vite/prompt-generator.d.ts +23 -0
  51. package/dist/cmd/build/vite/prompt-generator.d.ts.map +1 -0
  52. package/dist/cmd/build/vite/prompt-generator.js +123 -0
  53. package/dist/cmd/build/vite/prompt-generator.js.map +1 -0
  54. package/dist/cmd/build/vite/registry-generator.d.ts.map +1 -1
  55. package/dist/cmd/build/vite/registry-generator.js +28 -11
  56. package/dist/cmd/build/vite/registry-generator.js.map +1 -1
  57. package/dist/cmd/build/vite/server-bundler.d.ts +4 -0
  58. package/dist/cmd/build/vite/server-bundler.d.ts.map +1 -1
  59. package/dist/cmd/build/vite/server-bundler.js +45 -16
  60. package/dist/cmd/build/vite/server-bundler.js.map +1 -1
  61. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  62. package/dist/cmd/build/vite/vite-asset-server-config.js +4 -0
  63. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  64. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  65. package/dist/cmd/build/vite/vite-builder.js +99 -87
  66. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  67. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  68. package/dist/cmd/cloud/deploy.js +78 -27
  69. package/dist/cmd/cloud/deploy.js.map +1 -1
  70. package/dist/cmd/cloud/keyvalue/create-namespace.d.ts.map +1 -1
  71. package/dist/cmd/cloud/keyvalue/create-namespace.js +3 -1
  72. package/dist/cmd/cloud/keyvalue/create-namespace.js.map +1 -1
  73. package/dist/cmd/cloud/keyvalue/delete-namespace.d.ts.map +1 -1
  74. package/dist/cmd/cloud/keyvalue/delete-namespace.js +3 -1
  75. package/dist/cmd/cloud/keyvalue/delete-namespace.js.map +1 -1
  76. package/dist/cmd/cloud/keyvalue/delete.d.ts.map +1 -1
  77. package/dist/cmd/cloud/keyvalue/delete.js +3 -1
  78. package/dist/cmd/cloud/keyvalue/delete.js.map +1 -1
  79. package/dist/cmd/cloud/keyvalue/set.d.ts.map +1 -1
  80. package/dist/cmd/cloud/keyvalue/set.js +4 -2
  81. package/dist/cmd/cloud/keyvalue/set.js.map +1 -1
  82. package/dist/cmd/cloud/stream/get.d.ts.map +1 -1
  83. package/dist/cmd/cloud/stream/get.js +2 -13
  84. package/dist/cmd/cloud/stream/get.js.map +1 -1
  85. package/dist/cmd/cloud/vector/delete-namespace.d.ts +3 -0
  86. package/dist/cmd/cloud/vector/delete-namespace.d.ts.map +1 -0
  87. package/dist/cmd/cloud/vector/delete-namespace.js +77 -0
  88. package/dist/cmd/cloud/vector/delete-namespace.js.map +1 -0
  89. package/dist/cmd/cloud/vector/index.d.ts.map +1 -1
  90. package/dist/cmd/cloud/vector/index.js +21 -4
  91. package/dist/cmd/cloud/vector/index.js.map +1 -1
  92. package/dist/cmd/cloud/vector/list-namespaces.d.ts +3 -0
  93. package/dist/cmd/cloud/vector/list-namespaces.d.ts.map +1 -0
  94. package/dist/cmd/cloud/vector/list-namespaces.js +42 -0
  95. package/dist/cmd/cloud/vector/list-namespaces.js.map +1 -0
  96. package/dist/cmd/cloud/vector/stats.d.ts +3 -0
  97. package/dist/cmd/cloud/vector/stats.d.ts.map +1 -0
  98. package/dist/cmd/cloud/vector/stats.js +142 -0
  99. package/dist/cmd/cloud/vector/stats.js.map +1 -0
  100. package/dist/cmd/cloud/vector/upsert.d.ts +3 -0
  101. package/dist/cmd/cloud/vector/upsert.d.ts.map +1 -0
  102. package/dist/cmd/cloud/vector/upsert.js +192 -0
  103. package/dist/cmd/cloud/vector/upsert.js.map +1 -0
  104. package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
  105. package/dist/cmd/dev/file-watcher.js +90 -31
  106. package/dist/cmd/dev/file-watcher.js.map +1 -1
  107. package/dist/cmd/dev/index.d.ts.map +1 -1
  108. package/dist/cmd/dev/index.js +213 -57
  109. package/dist/cmd/dev/index.js.map +1 -1
  110. package/dist/cmd/dev/skills.d.ts +10 -0
  111. package/dist/cmd/dev/skills.d.ts.map +1 -0
  112. package/dist/cmd/dev/skills.js +57 -0
  113. package/dist/cmd/dev/skills.js.map +1 -0
  114. package/dist/cmd/dev/sync.js +7 -7
  115. package/dist/cmd/dev/sync.js.map +1 -1
  116. package/dist/cmd/index.d.ts.map +1 -1
  117. package/dist/cmd/index.js +1 -0
  118. package/dist/cmd/index.js.map +1 -1
  119. package/dist/cmd/project/create.d.ts.map +1 -1
  120. package/dist/cmd/project/create.js +3 -0
  121. package/dist/cmd/project/create.js.map +1 -1
  122. package/dist/cmd/project/template-flow.d.ts +1 -0
  123. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  124. package/dist/cmd/project/template-flow.js +30 -5
  125. package/dist/cmd/project/template-flow.js.map +1 -1
  126. package/dist/cmd/setup/index.d.ts.map +1 -1
  127. package/dist/cmd/setup/index.js +1 -0
  128. package/dist/cmd/setup/index.js.map +1 -1
  129. package/dist/cmd/upgrade/index.d.ts +15 -0
  130. package/dist/cmd/upgrade/index.d.ts.map +1 -1
  131. package/dist/cmd/upgrade/index.js +59 -4
  132. package/dist/cmd/upgrade/index.js.map +1 -1
  133. package/dist/domain.d.ts +45 -0
  134. package/dist/domain.d.ts.map +1 -0
  135. package/dist/domain.js +200 -0
  136. package/dist/domain.js.map +1 -0
  137. package/dist/schema-generator.d.ts +2 -0
  138. package/dist/schema-generator.d.ts.map +1 -1
  139. package/dist/schema-generator.js +18 -0
  140. package/dist/schema-generator.js.map +1 -1
  141. package/dist/steps.d.ts +1 -1
  142. package/dist/steps.d.ts.map +1 -1
  143. package/dist/steps.js +16 -5
  144. package/dist/steps.js.map +1 -1
  145. package/dist/tui/prompt.d.ts +1 -2
  146. package/dist/tui/prompt.d.ts.map +1 -1
  147. package/dist/tui/prompt.js +8 -4
  148. package/dist/tui/prompt.js.map +1 -1
  149. package/dist/tui.d.ts +16 -0
  150. package/dist/tui.d.ts.map +1 -1
  151. package/dist/tui.js +23 -2
  152. package/dist/tui.js.map +1 -1
  153. package/dist/types.d.ts +9 -2
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +3 -3
  156. package/dist/types.js.map +1 -1
  157. package/package.json +4 -4
  158. package/src/cli.ts +47 -12
  159. package/src/cmd/ai/index.ts +6 -1
  160. package/src/cmd/ai/prompt/agent.md +306 -0
  161. package/src/cmd/ai/prompt/agent.ts +12 -322
  162. package/src/cmd/ai/prompt/api.md +360 -0
  163. package/src/cmd/ai/prompt/api.ts +13 -260
  164. package/src/cmd/ai/prompt/version.ts +61 -0
  165. package/src/cmd/ai/prompt/web.md +509 -0
  166. package/src/cmd/ai/prompt/web.ts +12 -282
  167. package/src/cmd/ai/skills/generate.ts +75 -0
  168. package/src/cmd/ai/skills/generator.ts +519 -0
  169. package/src/cmd/ai/skills/index.ts +23 -0
  170. package/src/cmd/auth/signup.ts +1 -0
  171. package/src/cmd/build/entry-generator.ts +43 -7
  172. package/src/cmd/build/vite/bun-dev-server.ts +57 -27
  173. package/src/cmd/build/vite/metadata-generator.ts +73 -7
  174. package/src/cmd/build/vite/prompt-generator.ts +169 -0
  175. package/src/cmd/build/vite/registry-generator.ts +33 -10
  176. package/src/cmd/build/vite/server-bundler.ts +53 -22
  177. package/src/cmd/build/vite/vite-asset-server-config.ts +5 -0
  178. package/src/cmd/build/vite/vite-builder.ts +107 -87
  179. package/src/cmd/cloud/deploy.ts +99 -31
  180. package/src/cmd/cloud/keyvalue/create-namespace.ts +3 -1
  181. package/src/cmd/cloud/keyvalue/delete-namespace.ts +3 -1
  182. package/src/cmd/cloud/keyvalue/delete.ts +3 -1
  183. package/src/cmd/cloud/keyvalue/set.ts +4 -2
  184. package/src/cmd/cloud/stream/get.ts +2 -9
  185. package/src/cmd/cloud/vector/delete-namespace.ts +89 -0
  186. package/src/cmd/cloud/vector/index.ts +21 -4
  187. package/src/cmd/cloud/vector/list-namespaces.ts +46 -0
  188. package/src/cmd/cloud/vector/stats.ts +160 -0
  189. package/src/cmd/cloud/vector/upsert.ts +216 -0
  190. package/src/cmd/dev/file-watcher.ts +101 -32
  191. package/src/cmd/dev/index.ts +304 -111
  192. package/src/cmd/dev/skills.ts +82 -0
  193. package/src/cmd/dev/sync.ts +7 -7
  194. package/src/cmd/index.ts +1 -0
  195. package/src/cmd/project/create.ts +3 -0
  196. package/src/cmd/project/template-flow.ts +37 -5
  197. package/src/cmd/setup/index.ts +1 -0
  198. package/src/cmd/upgrade/index.ts +68 -4
  199. package/src/domain.ts +273 -0
  200. package/src/schema-generator.ts +23 -0
  201. package/src/steps.ts +16 -5
  202. package/src/tui/prompt.ts +11 -5
  203. package/src/tui.ts +21 -2
  204. package/src/types/md.d.ts +8 -0
  205. package/src/types.ts +12 -3
  206. package/dist/cmd/cloud/domain.d.ts +0 -17
  207. package/dist/cmd/cloud/domain.d.ts.map +0 -1
  208. package/dist/cmd/cloud/domain.js +0 -79
  209. package/dist/cmd/cloud/domain.js.map +0 -1
  210. package/src/cmd/cloud/domain.ts +0 -100
@@ -29,12 +29,14 @@ import {
29
29
  filterAgentuitySdkKeys,
30
30
  splitEnvAndSecrets,
31
31
  } from '../../env-util';
32
+ import { promptForDNS } from '../../domain';
32
33
 
33
34
  type ResourcesTypes = z.infer<typeof Resources>;
34
35
 
35
36
  interface CreateFlowOptions {
36
37
  projectName?: string;
37
38
  dir?: string;
39
+ domains?: string[];
38
40
  template?: string;
39
41
  templateDir?: string;
40
42
  templateBranch?: string;
@@ -63,6 +65,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
63
65
  orgId: selectedOrgId,
64
66
  region,
65
67
  apiClient,
68
+ domains,
66
69
  } = options;
67
70
 
68
71
  // Fetch available templates
@@ -105,7 +108,8 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
105
108
  if (!projectName && !skipPrompts) {
106
109
  projectName = await prompt.text({
107
110
  message: 'What is the name of your project?',
108
- initial: 'My First Agent',
111
+ hint: 'The name must be unique for your organization',
112
+ initial: '',
109
113
  validate: async (value: string) => {
110
114
  if (!value || value.trim().length === 0) {
111
115
  return 'Project name is required';
@@ -249,6 +253,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
249
253
  }
250
254
 
251
255
  const resourceConfig: ResourcesTypes = Resources.parse({});
256
+ let _domains = domains;
252
257
 
253
258
  if (auth && apiClient && catalystClient && orgId && region && !skipPrompts) {
254
259
  // Fetch resources for selected org and region using Catalyst API
@@ -265,8 +270,8 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
265
270
  const db_action = await prompt.select({
266
271
  message: 'Create SQL Database?',
267
272
  options: [
268
- { value: 'Create New', label: 'Create a new (free)' },
269
273
  { value: 'Skip', label: 'Skip or Setup later' },
274
+ { value: 'Create New', label: 'Create a new database' },
270
275
  ...resources.db.map((db) => ({
271
276
  value: db.name,
272
277
  label: `Use database: ${tui.tuiColors.primary(db.name)}`,
@@ -277,8 +282,8 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
277
282
  const s3_action = await prompt.select({
278
283
  message: 'Create Storage Bucket?',
279
284
  options: [
280
- { value: 'Create New', label: 'Create a new (free)' },
281
285
  { value: 'Skip', label: 'Skip or Setup later' },
286
+ { value: 'Create New', label: 'Create a new bucket' },
282
287
  ...resources.s3.map((bucket) => ({
283
288
  value: bucket.bucket_name,
284
289
  label: `Use bucket: ${tui.tuiColors.primary(bucket.bucket_name)}`,
@@ -286,6 +291,22 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
286
291
  ],
287
292
  });
288
293
 
294
+ if (!domains?.length) {
295
+ const customDns = await prompt.text({
296
+ message: 'Setup custom DNS?',
297
+ hint: 'Enter a domain name or press Enter to skip',
298
+ validate: (val: string) =>
299
+ val === ''
300
+ ? true
301
+ : /^(?=.{1,253}$)(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[A-Za-z]{2,63}$/.test(
302
+ val
303
+ ),
304
+ });
305
+ if (customDns) {
306
+ _domains = [customDns];
307
+ }
308
+ }
309
+
289
310
  const choices = { db_action, s3_action };
290
311
  switch (choices.s3_action) {
291
312
  case 'Create New': {
@@ -329,9 +350,9 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
329
350
  }
330
351
  }
331
352
 
332
- if (auth && apiClient && orgId) {
333
- let projectId: string | undefined;
353
+ let projectId: string | undefined;
334
354
 
355
+ if (auth && apiClient && orgId) {
335
356
  const cloudRegion = region ?? process.env.AGENTUITY_REGION ?? 'usc';
336
357
 
337
358
  const pkgJsonPath = resolve(dest, 'package.json');
@@ -355,6 +376,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
355
376
  tags: tags.length > 0 ? tags : undefined,
356
377
  orgId,
357
378
  cloudRegion,
379
+ domains: _domains,
358
380
  });
359
381
  projectId = project.id;
360
382
  return createProjectConfig(dest, {
@@ -363,6 +385,7 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
363
385
  sdkKey: project.sdkKey,
364
386
  deployment: {
365
387
  resources: resourceConfig,
388
+ domains: _domains,
366
389
  },
367
390
  region: cloudRegion,
368
391
  });
@@ -426,6 +449,15 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
426
449
  }
427
450
 
428
451
  playSound();
452
+
453
+ if (process.stdin.isTTY && !skipPrompts && _domains?.length && projectId) {
454
+ tui.newline();
455
+ const ok = await tui.confirm('Would you like to configure DNS now?', true);
456
+ if (ok) {
457
+ tui.newline();
458
+ await promptForDNS(projectId, _domains, config);
459
+ }
460
+ }
429
461
  }
430
462
 
431
463
  /**
@@ -9,6 +9,7 @@ export const command = createCommand({
9
9
  description: 'Display first-run setup information (internal use)',
10
10
  hidden: true,
11
11
  skipUpgradeCheck: true,
12
+ skipSkill: true,
12
13
  tags: ['read-only', 'fast'],
13
14
  optional: { auth: true },
14
15
  schema: {
@@ -6,9 +6,39 @@ import { ErrorCode, createError, exitWithError } from '../../errors';
6
6
  import * as tui from '../../tui';
7
7
  import { downloadWithProgress } from '../../download';
8
8
  import { $ } from 'bun';
9
- import { join } from 'node:path';
9
+ import { join, dirname } from 'node:path';
10
10
  import { tmpdir } from 'node:os';
11
11
  import { randomUUID } from 'node:crypto';
12
+ import { access, constants } from 'node:fs/promises';
13
+ import { StructuredError } from '@agentuity/core';
14
+
15
+ export const PermissionError = StructuredError('PermissionError')<{
16
+ binaryPath: string;
17
+ reason: string;
18
+ }>();
19
+
20
+ async function checkWritePermission(binaryPath: string): Promise<void> {
21
+ try {
22
+ await access(binaryPath, constants.W_OK);
23
+ } catch {
24
+ throw new PermissionError({
25
+ binaryPath,
26
+ reason: `Cannot write to ${binaryPath}. You may need to run with elevated permissions (e.g., sudo) or reinstall to a user-writable location.`,
27
+ message: `Permission denied: Cannot write to ${binaryPath}`,
28
+ });
29
+ }
30
+
31
+ const parentDir = dirname(binaryPath);
32
+ try {
33
+ await access(parentDir, constants.W_OK);
34
+ } catch {
35
+ throw new PermissionError({
36
+ binaryPath,
37
+ reason: `Cannot write to directory ${parentDir}. You may need to run with elevated permissions (e.g., sudo) or reinstall to a user-writable location.`,
38
+ message: `Permission denied: Cannot write to directory ${parentDir}`,
39
+ });
40
+ }
41
+ }
12
42
 
13
43
  const UpgradeOptionsSchema = z.object({
14
44
  force: z.boolean().optional().describe('Force upgrade even if version is the same'),
@@ -316,7 +346,7 @@ export const command = createCommand({
316
346
  };
317
347
  }
318
348
 
319
- // Confirm upgrade
349
+ // Show version info
320
350
  if (!force) {
321
351
  tui.info(`Current version: ${tui.muted(normalizedCurrent)}`);
322
352
  tui.info(`Latest version: ${tui.bold(normalizedLatest)}`);
@@ -328,7 +358,43 @@ export const command = createCommand({
328
358
  }
329
359
  tui.success(`Release notes: ${tui.link(getReleaseUrl(latestVersion))}`);
330
360
  tui.newline();
361
+ }
331
362
 
363
+ // Check write permissions before prompting - fail early with helpful message
364
+ try {
365
+ await checkWritePermission(currentBinaryPath);
366
+ } catch (error) {
367
+ if (error instanceof PermissionError) {
368
+ tui.error('Unable to upgrade: permission denied');
369
+ tui.newline();
370
+ tui.warning(`The CLI binary at ${tui.bold(error.binaryPath)} is not writable.`);
371
+ tui.newline();
372
+ if (process.env.AGENTUITY_RUNTIME) {
373
+ console.log('You cannot self-upgrade the agentuity cli in the cloud runtime.');
374
+ console.log('The runtime will automatically update the cli and other software');
375
+ console.log('within a day or so. If you need assistance, please contact us');
376
+ console.log('at support@agentuity.com.');
377
+ } else {
378
+ console.log('To fix this, you can either:');
379
+ console.log(
380
+ ` 1. Run with elevated permissions: ${tui.muted('sudo agentuity upgrade')}`
381
+ );
382
+ console.log(` 2. Reinstall to a user-writable location`);
383
+ }
384
+ tui.newline();
385
+ exitWithError(
386
+ createError(ErrorCode.PERMISSION_DENIED, 'Upgrade failed: permission denied', {
387
+ path: error.binaryPath,
388
+ }),
389
+ logger,
390
+ options.errorFormat
391
+ );
392
+ }
393
+ throw error;
394
+ }
395
+
396
+ // Confirm upgrade
397
+ if (!force) {
332
398
  const shouldUpgrade = await tui.confirm('Do you want to upgrade?', true);
333
399
 
334
400
  if (!shouldUpgrade) {
@@ -383,13 +449,11 @@ export const command = createCommand({
383
449
  message,
384
450
  };
385
451
  } catch (error) {
386
- // Parse validation errors to extract stdout/stderr if present
387
452
  let errorDetails: Record<string, unknown> = {
388
453
  error: error instanceof Error ? error.message : 'Unknown error',
389
454
  };
390
455
 
391
456
  if (error instanceof Error && error.message.includes('Binary validation failed')) {
392
- // Extract stdout/stderr from validation error
393
457
  const match = error.message.match(
394
458
  /Failed with exit code (\d+)\n(stdout: .+\n)?(stderr: .+)?/s
395
459
  );
package/src/domain.ts ADDED
@@ -0,0 +1,273 @@
1
+ import type { Config } from './types';
2
+ import { StructuredError } from '@agentuity/core';
3
+ import * as tui from './tui';
4
+
5
+ interface BaseDNSResult {
6
+ domain: string;
7
+ target: string;
8
+ recordType: string;
9
+ }
10
+
11
+ interface DNSSuccess extends BaseDNSResult {
12
+ success: true;
13
+ }
14
+
15
+ interface DNSPending extends BaseDNSResult {
16
+ success: true;
17
+ pending: true;
18
+ }
19
+
20
+ interface DNSMissing extends BaseDNSResult {
21
+ success: false;
22
+ pending: false;
23
+ }
24
+
25
+ interface DNSError extends BaseDNSResult {
26
+ success: false;
27
+ error: string;
28
+ }
29
+
30
+ interface DNSMisconfigured extends BaseDNSResult {
31
+ success: false;
32
+ misconfigured: string;
33
+ }
34
+
35
+ export type DNSResult = DNSSuccess | DNSPending | DNSMissing | DNSError | DNSMisconfigured;
36
+ export type DNSFailed = DNSPending | DNSMissing | DNSError | DNSMisconfigured;
37
+
38
+ export function isMisconfigured(x: DNSResult): x is DNSMisconfigured {
39
+ return 'misconfigured' in x && !!x.misconfigured;
40
+ }
41
+
42
+ export function isMissing(x: DNSResult): x is DNSMissing {
43
+ return 'pending' in x && x.pending === false && 'success' in x && x.success === false;
44
+ }
45
+
46
+ export function isError(x: DNSResult): x is DNSError {
47
+ return 'error' in x && !!x.error;
48
+ }
49
+
50
+ export function isPending(x: DNSResult): x is DNSPending {
51
+ return 'pending' in x && x.pending === true && x.success === true;
52
+ }
53
+
54
+ export function isSuccess(x: DNSResult): x is DNSSuccess {
55
+ return x.success == true && !('pending' in x) && !('error' in x) && !('misconfigured' in x);
56
+ }
57
+
58
+ const timeoutMs = 5000;
59
+
60
+ const DNSTimeoutError = StructuredError(
61
+ 'DNSTimeoutError',
62
+ `DNS lookup timed out after ${timeoutMs}ms`
63
+ );
64
+
65
+ interface CFRecord {
66
+ Answer?: {
67
+ data: string;
68
+ }[];
69
+ }
70
+
71
+ async function fetchDNSRecord(name: string, type: string): Promise<string | null> {
72
+ const params = new URLSearchParams();
73
+ params.set('name', name);
74
+ params.set('type', type);
75
+ const res = await fetch(`https://cloudflare-dns.com/dns-query?${params.toString()}`, {
76
+ headers: {
77
+ Accept: 'application/dns-json',
78
+ },
79
+ });
80
+ if (res.ok) {
81
+ const result = (await res.json()) as CFRecord;
82
+ if (result?.Answer?.length) {
83
+ return result.Answer[0].data.replace(/\.$/, ''); // DNS records end with . so we remove that
84
+ }
85
+ }
86
+ return null;
87
+ }
88
+
89
+ const LOCAL_DNS = 'agentuity.io';
90
+ const PRODUCTION_DNS = 'agentuity.run';
91
+
92
+ /**
93
+ * This function will check for each of the custom domains and make sure they are correctly
94
+ * configured in DNS
95
+ *
96
+ * @param projectId the project id
97
+ * @param config Config
98
+ * @param domains array of domains to check
99
+ * @returns
100
+ */
101
+ export async function checkCustomDomainForDNS(
102
+ projectId: string,
103
+ domains: string[],
104
+ config?: Config | null
105
+ ): Promise<DNSResult[]> {
106
+ const suffix = config?.overrides?.api_url?.includes('agentuity.io') ? LOCAL_DNS : PRODUCTION_DNS;
107
+ const id = Bun.hash.xxHash64(projectId).toString(16);
108
+ const proxy = `p${id}.${suffix}`;
109
+
110
+ return Promise.all(
111
+ domains.map(async (domain) => {
112
+ try {
113
+ let timeoutId: Timer | undefined;
114
+
115
+ const timeoutPromise = new Promise<never>((_, reject) => {
116
+ timeoutId = setTimeout(() => {
117
+ reject(new DNSTimeoutError());
118
+ }, timeoutMs);
119
+ });
120
+
121
+ const result = await Promise.race([
122
+ fetchDNSRecord(domain, 'CNAME'),
123
+ timeoutPromise,
124
+ ]).finally(() => {
125
+ if (timeoutId) clearTimeout(timeoutId);
126
+ });
127
+
128
+ if (result) {
129
+ if (result === proxy) {
130
+ return {
131
+ domain,
132
+ target: proxy,
133
+ recordType: 'CNAME',
134
+ success: true,
135
+ } as DNSSuccess;
136
+ }
137
+ return {
138
+ domain,
139
+ target: proxy,
140
+ recordType: 'CNAME',
141
+ success: false,
142
+ misconfigured: `CNAME record is ${result}`,
143
+ } as DNSMisconfigured;
144
+ }
145
+ } catch (ex) {
146
+ const _ex = ex as { code: string; message?: string };
147
+ if (_ex.message?.includes('timed out')) {
148
+ return {
149
+ domain,
150
+ target: proxy,
151
+ recordType: 'CNAME',
152
+ success: false,
153
+ error: `DNS lookup timed out after 5 seconds. Please check your DNS configuration.`,
154
+ } as DNSError;
155
+ }
156
+ if (_ex.code !== 'ENOTFOUND') {
157
+ const errMsg =
158
+ ex instanceof Error
159
+ ? ex.message
160
+ : typeof ex === 'string'
161
+ ? ex
162
+ : JSON.stringify(ex);
163
+ return {
164
+ domain,
165
+ target: proxy,
166
+ recordType: 'CNAME',
167
+ success: false,
168
+ error: errMsg,
169
+ } as DNSError;
170
+ }
171
+ }
172
+ return {
173
+ domain,
174
+ success: false,
175
+ target: proxy,
176
+ recordType: 'CNAME',
177
+ pending: false,
178
+ } as DNSMissing;
179
+ })
180
+ );
181
+ }
182
+
183
+ export async function promptForDNS(
184
+ projectId: string,
185
+ domains: string[],
186
+ config?: Config,
187
+ resumeFn?: () => () => void
188
+ ) {
189
+ let paused = false;
190
+ let resume: (() => void) | undefined;
191
+ for (;;) {
192
+ const result = await checkCustomDomainForDNS(projectId, domains, config);
193
+ const failed = result.filter((x): x is DNSFailed => !isSuccess(x));
194
+ if (failed.length) {
195
+ const records: {
196
+ domain: string;
197
+ type: string;
198
+ target: string;
199
+ status: string;
200
+ }[] = [];
201
+ result.forEach((r) => {
202
+ if (isSuccess(r)) {
203
+ records.push({
204
+ domain: r.domain,
205
+ type: r.recordType,
206
+ target: r.target,
207
+ status: tui.colorSuccess(`${tui.ICONS.success} Configured`),
208
+ });
209
+ }
210
+ });
211
+ if (!paused) {
212
+ resume = resumeFn?.();
213
+ paused = true;
214
+ }
215
+ tui.error(
216
+ `You have ${tui.plural(failed.length, 'a ', '')}DNS configuration ${tui.plural(failed.length, 'issue', 'issues')} that must be resolved before deploying:`
217
+ );
218
+ for (const r of failed) {
219
+ if (isError(r)) {
220
+ resume?.();
221
+ throw new Error(r.error);
222
+ } else if (isMisconfigured(r)) {
223
+ records.push({
224
+ domain: r.domain,
225
+ type: r.recordType,
226
+ target: r.target,
227
+ status: tui.colorWarning(`${tui.ICONS.error} ${r.misconfigured}`),
228
+ });
229
+ } else if (isPending(r)) {
230
+ records.push({
231
+ domain: r.domain,
232
+ type: r.recordType,
233
+ target: r.target,
234
+ status: tui.colorWarning('⌛️ Pending'),
235
+ });
236
+ } else if (isMissing(r)) {
237
+ records.push({
238
+ domain: r.domain,
239
+ type: r.recordType,
240
+ target: r.target,
241
+ status: tui.colorError(`${tui.ICONS.error} Missing`),
242
+ });
243
+ }
244
+ }
245
+
246
+ let linesShown = 2; // header + footer
247
+ for (const record of records) {
248
+ console.log();
249
+ console.log(`${tui.colorInfo('Domain:')} ${tui.colorPrimary(record.domain)}`);
250
+ console.log(`${tui.colorInfo('Type:')} ${tui.colorPrimary(record.type)}`);
251
+ console.log(`${tui.colorInfo('Target:')} ${tui.colorPrimary(record.target)}`);
252
+ console.log(`${tui.colorInfo('Status:')} ${tui.colorPrimary(record.status)}`);
253
+ console.log();
254
+ linesShown += 6;
255
+ }
256
+
257
+ // await tui.waitForAnyKey('Press any key to check again or ctrl+c to cancel...');
258
+ await tui.spinner({
259
+ message: 'Checking again in 5s...',
260
+ clearOnSuccess: true,
261
+ callback: () => {
262
+ return Bun.sleep(5000);
263
+ },
264
+ });
265
+ tui.clearLastLines(linesShown);
266
+ linesShown = 0;
267
+ continue;
268
+ }
269
+ tui.clearLastLines(1);
270
+ resume?.();
271
+ break;
272
+ }
273
+ }
@@ -48,6 +48,8 @@ export interface SchemaCommand {
48
48
  prerequisites?: string[];
49
49
  pagination?: SchemaPagination;
50
50
  tags?: string[];
51
+ skipSkill?: boolean;
52
+ toplevel?: boolean;
51
53
  subcommands?: SchemaCommand[];
52
54
  requires?: {
53
55
  auth?: boolean;
@@ -121,6 +123,13 @@ function extractCommandSchema(def: CommandDefinition): SchemaCommand {
121
123
  schema.tags = (def as any).tags;
122
124
  }
123
125
 
126
+ // Extract skipSkill
127
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
128
+ if ((def as any).skipSkill !== undefined) {
129
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
130
+ schema.skipSkill = (def as any).skipSkill;
131
+ }
132
+
124
133
  // Extract requires/optional
125
134
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
126
135
  const d = def as any;
@@ -202,6 +211,20 @@ function extractSubcommandSchema(def: SubcommandDefinition): SchemaCommand {
202
211
  schema.tags = (def as any).tags;
203
212
  }
204
213
 
214
+ // Extract skipSkill
215
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
216
+ if ((def as any).skipSkill !== undefined) {
217
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
218
+ schema.skipSkill = (def as any).skipSkill;
219
+ }
220
+
221
+ // Extract toplevel
222
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
223
+ if ((def as any).toplevel !== undefined) {
224
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
225
+ schema.toplevel = (def as any).toplevel;
226
+ }
227
+
205
228
  // Extract requires/optional
206
229
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
207
230
  const d = def as any;
package/src/steps.ts CHANGED
@@ -8,6 +8,7 @@
8
8
  import type { ColorScheme } from './terminal';
9
9
  import type { LogLevel } from './types';
10
10
  import { ValidationInputError, ValidationOutputError, type IssuesType } from '@agentuity/server';
11
+ import { clearLastLines } from './tui';
11
12
 
12
13
  // Spinner frames
13
14
  const FRAMES = ['◐', '◓', '◑', '◒'];
@@ -210,7 +211,7 @@ function enablePauseResume(
210
211
  * Pause step rendering for interactive input
211
212
  * Returns resume function
212
213
  */
213
- export function pauseStepUI(): () => void {
214
+ export function pauseStepUI(clear = false): () => void {
214
215
  if (!process.stdout.isTTY || !getTotalLinesFn) {
215
216
  return () => {}; // No-op if not TTY or not in step context
216
217
  }
@@ -232,8 +233,16 @@ export function pauseStepUI(): () => void {
232
233
  }) as typeof process.stderr.write;
233
234
 
234
235
  // Show cursor and add newline for separation
235
- process.stdout.write('\x1B[?25h');
236
- process.stdout.write('\n');
236
+ if (clear) {
237
+ const lines = getTotalLinesFn();
238
+ if (lines) {
239
+ clearLastLines(lines + 1, originalStdoutWrite);
240
+ }
241
+ }
242
+ originalStdoutWrite('\x1B[?25h');
243
+ if (!clear) {
244
+ originalStdoutWrite('\n');
245
+ }
237
246
 
238
247
  // Return resume function
239
248
  return () => {
@@ -244,8 +253,10 @@ export function pauseStepUI(): () => void {
244
253
  process.stderr.write = originalStderrWrite;
245
254
 
246
255
  // Restore cursor to saved position (where steps began)
247
- process.stdout.write('\x1B[u'); // Restore cursor position
248
- process.stdout.write('\x1B[0J'); // Clear from saved position to end of screen
256
+ if (!clear) {
257
+ process.stdout.write('\x1B[u'); // Restore cursor position
258
+ process.stdout.write('\x1B[0J'); // Clear from saved position to end of screen
259
+ }
249
260
  process.stdout.write('\x1B[?25l'); // Hide cursor
250
261
 
251
262
  // Force immediate re-render (cursor already at step start)
package/src/tui/prompt.ts CHANGED
@@ -13,7 +13,7 @@ interface KeypressEvent {
13
13
  export interface TextOptions {
14
14
  message: string;
15
15
  initial?: string;
16
- placeholder?: string;
16
+ hint?: string;
17
17
  validate?: (value: string) => boolean | string | Promise<boolean | string>;
18
18
  }
19
19
 
@@ -54,7 +54,6 @@ interface PromptState {
54
54
  */
55
55
  export class PromptFlow {
56
56
  private states: PromptState[] = [];
57
- private hasIntro = false;
58
57
 
59
58
  /**
60
59
  * Display intro banner
@@ -62,7 +61,6 @@ export class PromptFlow {
62
61
  intro(title: string): void {
63
62
  console.log(`${colors.secondary(symbols.squareTL)} ${colors.inverseCyan(` ⨺ ${title} `)}`);
64
63
  console.log(colors.secondary(symbols.bar));
65
- this.hasIntro = true;
66
64
  }
67
65
 
68
66
  /**
@@ -96,10 +94,16 @@ export class PromptFlow {
96
94
  });
97
95
 
98
96
  let hasError = false;
97
+ let hadValidationError = false;
99
98
 
100
99
  const showPrompt = () => {
101
100
  // Show prompt with active symbol
102
101
  process.stdout.write(`${colors.active(symbols.active)} ${message}\n`);
102
+ if (options.hint) {
103
+ process.stdout.write(
104
+ `${colors.secondary(symbols.bar)} ${colors.muted(options.hint)}\n`
105
+ );
106
+ }
103
107
  // Use readline's prompt for the input line
104
108
  rl.prompt();
105
109
  };
@@ -108,7 +112,8 @@ export class PromptFlow {
108
112
 
109
113
  rl.on('line', async (input) => {
110
114
  const trimmed = input.trim();
111
- const value = trimmed.length > 0 ? trimmed : initial;
115
+ // After a validation error, require explicit input - don't fall back to initial
116
+ const value = trimmed.length > 0 ? trimmed : hadValidationError ? '' : initial;
112
117
 
113
118
  // Validate
114
119
  if (validate) {
@@ -129,6 +134,7 @@ export class PromptFlow {
129
134
  // Use readline's prompt for the input line
130
135
  rl.prompt();
131
136
  hasError = true;
137
+ hadValidationError = true;
132
138
  return;
133
139
  }
134
140
  } catch (error) {
@@ -156,7 +162,7 @@ export class PromptFlow {
156
162
  }
157
163
 
158
164
  // Clear all lines and show completed state
159
- const linesToClear = hasError ? 3 : 2;
165
+ const linesToClear = hasError ? 4 : 3;
160
166
  readline.moveCursor(process.stdout, 0, -linesToClear);
161
167
  readline.clearScreenDown(process.stdout);
162
168