@agentuity/cli 1.0.9 → 1.0.11

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 (138) hide show
  1. package/dist/cmd/build/ast.d.ts +1 -1
  2. package/dist/cmd/build/ast.d.ts.map +1 -1
  3. package/dist/cmd/build/ast.js +103 -5
  4. package/dist/cmd/build/ast.js.map +1 -1
  5. package/dist/cmd/build/entry-generator.d.ts.map +1 -1
  6. package/dist/cmd/build/entry-generator.js +1 -8
  7. package/dist/cmd/build/entry-generator.js.map +1 -1
  8. package/dist/cmd/build/vite/config-loader.d.ts +9 -0
  9. package/dist/cmd/build/vite/config-loader.d.ts.map +1 -1
  10. package/dist/cmd/build/vite/config-loader.js +30 -0
  11. package/dist/cmd/build/vite/config-loader.js.map +1 -1
  12. package/dist/cmd/build/vite/index.d.ts +2 -0
  13. package/dist/cmd/build/vite/index.d.ts.map +1 -1
  14. package/dist/cmd/build/vite/index.js +2 -1
  15. package/dist/cmd/build/vite/index.js.map +1 -1
  16. package/dist/cmd/build/vite/metadata-generator.d.ts +4 -1
  17. package/dist/cmd/build/vite/metadata-generator.d.ts.map +1 -1
  18. package/dist/cmd/build/vite/metadata-generator.js +1 -0
  19. package/dist/cmd/build/vite/metadata-generator.js.map +1 -1
  20. package/dist/cmd/build/vite/route-discovery.d.ts.map +1 -1
  21. package/dist/cmd/build/vite/route-discovery.js +23 -1
  22. package/dist/cmd/build/vite/route-discovery.js.map +1 -1
  23. package/dist/cmd/build/vite/vite-asset-server-config.d.ts.map +1 -1
  24. package/dist/cmd/build/vite/vite-asset-server-config.js +19 -14
  25. package/dist/cmd/build/vite/vite-asset-server-config.js.map +1 -1
  26. package/dist/cmd/build/vite/vite-builder.d.ts +2 -0
  27. package/dist/cmd/build/vite/vite-builder.d.ts.map +1 -1
  28. package/dist/cmd/build/vite/vite-builder.js +13 -8
  29. package/dist/cmd/build/vite/vite-builder.js.map +1 -1
  30. package/dist/cmd/build/vite-bundler.d.ts +2 -0
  31. package/dist/cmd/build/vite-bundler.d.ts.map +1 -1
  32. package/dist/cmd/build/vite-bundler.js +2 -1
  33. package/dist/cmd/build/vite-bundler.js.map +1 -1
  34. package/dist/cmd/cloud/db/list.d.ts.map +1 -1
  35. package/dist/cmd/cloud/db/list.js +14 -1
  36. package/dist/cmd/cloud/db/list.js.map +1 -1
  37. package/dist/cmd/cloud/deploy.d.ts.map +1 -1
  38. package/dist/cmd/cloud/deploy.js +36 -22
  39. package/dist/cmd/cloud/deploy.js.map +1 -1
  40. package/dist/cmd/cloud/queue/list.d.ts.map +1 -1
  41. package/dist/cmd/cloud/queue/list.js +10 -0
  42. package/dist/cmd/cloud/queue/list.js.map +1 -1
  43. package/dist/cmd/cloud/sandbox/list.d.ts.map +1 -1
  44. package/dist/cmd/cloud/sandbox/list.js +10 -0
  45. package/dist/cmd/cloud/sandbox/list.js.map +1 -1
  46. package/dist/cmd/cloud/sandbox/runtime/list.d.ts.map +1 -1
  47. package/dist/cmd/cloud/sandbox/runtime/list.js +10 -0
  48. package/dist/cmd/cloud/sandbox/runtime/list.js.map +1 -1
  49. package/dist/cmd/cloud/sandbox/snapshot/list.d.ts.map +1 -1
  50. package/dist/cmd/cloud/sandbox/snapshot/list.js +10 -0
  51. package/dist/cmd/cloud/sandbox/snapshot/list.js.map +1 -1
  52. package/dist/cmd/cloud/session/get.js +12 -12
  53. package/dist/cmd/cloud/session/get.js.map +1 -1
  54. package/dist/cmd/cloud/session/list.d.ts.map +1 -1
  55. package/dist/cmd/cloud/session/list.js +14 -4
  56. package/dist/cmd/cloud/session/list.js.map +1 -1
  57. package/dist/cmd/cloud/storage/list.d.ts.map +1 -1
  58. package/dist/cmd/cloud/storage/list.js +14 -1
  59. package/dist/cmd/cloud/storage/list.js.map +1 -1
  60. package/dist/cmd/cloud/stream/list.d.ts.map +1 -1
  61. package/dist/cmd/cloud/stream/list.js +10 -0
  62. package/dist/cmd/cloud/stream/list.js.map +1 -1
  63. package/dist/cmd/cloud/thread/list.d.ts.map +1 -1
  64. package/dist/cmd/cloud/thread/list.js +10 -0
  65. package/dist/cmd/cloud/thread/list.js.map +1 -1
  66. package/dist/cmd/dev/download.d.ts.map +1 -1
  67. package/dist/cmd/dev/download.js +63 -53
  68. package/dist/cmd/dev/download.js.map +1 -1
  69. package/dist/cmd/project/domain/check.d.ts +2 -0
  70. package/dist/cmd/project/domain/check.d.ts.map +1 -0
  71. package/dist/cmd/project/domain/check.js +131 -0
  72. package/dist/cmd/project/domain/check.js.map +1 -0
  73. package/dist/cmd/project/domain/index.d.ts +2 -0
  74. package/dist/cmd/project/domain/index.d.ts.map +1 -0
  75. package/dist/cmd/project/domain/index.js +20 -0
  76. package/dist/cmd/project/domain/index.js.map +1 -0
  77. package/dist/cmd/project/hostname/get.d.ts +2 -0
  78. package/dist/cmd/project/hostname/get.d.ts.map +1 -0
  79. package/dist/cmd/project/hostname/get.js +50 -0
  80. package/dist/cmd/project/hostname/get.js.map +1 -0
  81. package/dist/cmd/project/hostname/index.d.ts +2 -0
  82. package/dist/cmd/project/hostname/index.d.ts.map +1 -0
  83. package/dist/cmd/project/hostname/index.js +18 -0
  84. package/dist/cmd/project/hostname/index.js.map +1 -0
  85. package/dist/cmd/project/hostname/set.d.ts +2 -0
  86. package/dist/cmd/project/hostname/set.d.ts.map +1 -0
  87. package/dist/cmd/project/hostname/set.js +100 -0
  88. package/dist/cmd/project/hostname/set.js.map +1 -0
  89. package/dist/cmd/project/index.d.ts.map +1 -1
  90. package/dist/cmd/project/index.js +12 -0
  91. package/dist/cmd/project/index.js.map +1 -1
  92. package/dist/index.d.ts +1 -1
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +1 -1
  95. package/dist/index.js.map +1 -1
  96. package/dist/steps.d.ts +9 -0
  97. package/dist/steps.d.ts.map +1 -1
  98. package/dist/steps.js +131 -71
  99. package/dist/steps.js.map +1 -1
  100. package/dist/tui.d.ts +2 -2
  101. package/dist/tui.d.ts.map +1 -1
  102. package/dist/tui.js +6 -4
  103. package/dist/tui.js.map +1 -1
  104. package/dist/types.d.ts +4 -2
  105. package/dist/types.d.ts.map +1 -1
  106. package/dist/types.js.map +1 -1
  107. package/package.json +6 -7
  108. package/src/cmd/build/ast.ts +141 -5
  109. package/src/cmd/build/entry-generator.ts +1 -8
  110. package/src/cmd/build/vite/config-loader.ts +35 -0
  111. package/src/cmd/build/vite/index.ts +4 -0
  112. package/src/cmd/build/vite/metadata-generator.ts +5 -1
  113. package/src/cmd/build/vite/route-discovery.ts +34 -1
  114. package/src/cmd/build/vite/vite-asset-server-config.ts +22 -13
  115. package/src/cmd/build/vite/vite-builder.ts +20 -8
  116. package/src/cmd/build/vite-bundler.ts +4 -0
  117. package/src/cmd/cloud/db/list.ts +14 -1
  118. package/src/cmd/cloud/deploy.ts +46 -21
  119. package/src/cmd/cloud/queue/list.ts +10 -0
  120. package/src/cmd/cloud/sandbox/list.ts +10 -0
  121. package/src/cmd/cloud/sandbox/runtime/list.ts +10 -0
  122. package/src/cmd/cloud/sandbox/snapshot/list.ts +10 -0
  123. package/src/cmd/cloud/session/get.ts +12 -12
  124. package/src/cmd/cloud/session/list.ts +28 -18
  125. package/src/cmd/cloud/storage/list.ts +14 -1
  126. package/src/cmd/cloud/stream/list.ts +18 -8
  127. package/src/cmd/cloud/thread/list.ts +15 -5
  128. package/src/cmd/dev/download.ts +76 -78
  129. package/src/cmd/project/domain/check.ts +146 -0
  130. package/src/cmd/project/domain/index.ts +20 -0
  131. package/src/cmd/project/hostname/get.ts +54 -0
  132. package/src/cmd/project/hostname/index.ts +18 -0
  133. package/src/cmd/project/hostname/set.ts +123 -0
  134. package/src/cmd/project/index.ts +12 -0
  135. package/src/index.ts +1 -1
  136. package/src/steps.ts +139 -74
  137. package/src/tui.ts +6 -4
  138. package/src/types.ts +4 -2
@@ -12,8 +12,8 @@ const SessionListResponseSchema = z.array(
12
12
  created_at: z.string().describe('Creation timestamp'),
13
13
  success: z.boolean().describe('Whether the session succeeded'),
14
14
  duration: z.number().nullable().describe('Duration in nanoseconds'),
15
- method: z.string().describe('HTTP method'),
16
- url: z.string().describe('Request URL'),
15
+ method: z.string().nullable().describe('HTTP method'),
16
+ url: z.string().nullable().describe('Request URL'),
17
17
  trigger: z.string().describe('Trigger type'),
18
18
  env: z.string().describe('Environment'),
19
19
  })
@@ -91,6 +91,14 @@ export const listSubcommand = createSubcommand({
91
91
  success: z.coerce.boolean().optional().describe('Filter by success status (true/false)'),
92
92
  startAfter: z.string().optional().describe('Filter by start time after (ISO 8601)'),
93
93
  startBefore: z.string().optional().describe('Filter by start time before (ISO 8601)'),
94
+ sort: z
95
+ .enum(['created', 'updated', 'duration', 'startTime'])
96
+ .optional()
97
+ .describe('field to sort by (default: created)'),
98
+ direction: z
99
+ .enum(['asc', 'desc'])
100
+ .optional()
101
+ .describe('sort direction (default: desc)'),
94
102
  }),
95
103
  response: SessionListResponseSchema,
96
104
  },
@@ -109,20 +117,22 @@ export const listSubcommand = createSubcommand({
109
117
  const projectId = opts.all || opts.orgId ? undefined : opts.projectId || project?.projectId;
110
118
 
111
119
  try {
112
- const sessions = await sessionList(catalystClient, {
113
- count: opts.count,
114
- orgId: opts?.orgId,
115
- projectId,
116
- deploymentId: opts.deploymentId,
117
- trigger: opts.trigger,
118
- env: opts.env,
119
- devmode: opts.devmode,
120
- success: opts.success,
121
- threadId: opts.threadId,
122
- agentIdentifier: opts.agentIdentifier,
123
- startAfter: opts.startAfter,
124
- startBefore: opts.startBefore,
125
- });
120
+ const sessions = await sessionList(catalystClient, {
121
+ count: opts.count,
122
+ orgId: opts?.orgId,
123
+ projectId,
124
+ deploymentId: opts.deploymentId,
125
+ trigger: opts.trigger,
126
+ env: opts.env,
127
+ devmode: opts.devmode,
128
+ success: opts.success,
129
+ threadId: opts.threadId,
130
+ agentIdentifier: opts.agentIdentifier,
131
+ startAfter: opts.startAfter,
132
+ startBefore: opts.startBefore,
133
+ sort: opts.sort,
134
+ direction: opts.direction,
135
+ });
126
136
 
127
137
  const result = sessions.map((s) => ({
128
138
  id: s.id,
@@ -146,13 +156,13 @@ export const listSubcommand = createSubcommand({
146
156
  }
147
157
 
148
158
  const tableData = sessions.map((s) => {
149
- const urlPath = new URL(s.url).pathname;
159
+ const urlPath = s.url ? new URL(s.url).pathname : '-';
150
160
  return {
151
161
  ID: s.id,
152
162
  Created: new Date(s.created_at).toLocaleString(),
153
163
  Success: s.success ? '✓' : '✗',
154
164
  Duration: s.duration ? `${(s.duration / 1_000_000).toFixed(0)}ms` : '-',
155
- Method: s.method,
165
+ Method: s.method ?? '-',
156
166
  Path: urlPath.length > 50 ? urlPath.substring(0, 47) + '...' : urlPath,
157
167
  Trigger: s.trigger,
158
168
  Env: s.env,
@@ -77,6 +77,14 @@ export const listSubcommand = createSubcommand({
77
77
  'Show credentials in plain text (default: masked in terminal, unmasked in JSON)'
78
78
  ),
79
79
  nameOnly: z.boolean().optional().describe('Print the name only'),
80
+ sort: z
81
+ .enum(['name', 'created'])
82
+ .optional()
83
+ .describe('field to sort by (default: created)'),
84
+ direction: z
85
+ .enum(['asc', 'desc'])
86
+ .optional()
87
+ .describe('sort direction (default: desc)'),
80
88
  }),
81
89
  response: StorageListResponseSchema,
82
90
  },
@@ -95,7 +103,12 @@ export const listSubcommand = createSubcommand({
95
103
  message: 'Fetching storage',
96
104
  clearOnSuccess: true,
97
105
  callback: async () => {
98
- return listOrgResources(catalystClient, { type: 's3', orgId: opts?.orgId });
106
+ return listOrgResources(catalystClient, {
107
+ type: 's3',
108
+ orgId: opts?.orgId,
109
+ sort: opts?.sort,
110
+ direction: opts?.direction,
111
+ });
99
112
  },
100
113
  });
101
114
 
@@ -67,6 +67,14 @@ export const listSubcommand = createCommand({
67
67
  .describe('filter by metadata (format: key=value or key1=value1,key2=value2)'),
68
68
  projectId: z.string().optional().describe('filter by project ID'),
69
69
  orgId: z.string().optional().describe('filter by organization ID'),
70
+ sort: z
71
+ .enum(['name', 'created', 'updated', 'size'])
72
+ .optional()
73
+ .describe('field to sort by (default: created)'),
74
+ direction: z
75
+ .enum(['asc', 'desc'])
76
+ .optional()
77
+ .describe('sort direction (default: desc)'),
70
78
  }),
71
79
  response: ListStreamsResponseSchema,
72
80
  },
@@ -120,14 +128,16 @@ export const listSubcommand = createCommand({
120
128
  }
121
129
 
122
130
  try {
123
- const result = await streamList(apiClient, {
124
- limit: opts.size,
125
- offset: opts.offset,
126
- namespace: opts.namespace,
127
- metadata: metadataFilter,
128
- projectId,
129
- orgId: opts.orgId,
130
- });
131
+ const result = await streamList(apiClient, {
132
+ limit: opts.size,
133
+ offset: opts.offset,
134
+ namespace: opts.namespace,
135
+ metadata: metadataFilter,
136
+ projectId,
137
+ orgId: opts.orgId,
138
+ sort: opts.sort,
139
+ direction: opts.direction,
140
+ });
131
141
 
132
142
  if (options.json) {
133
143
  console.log(JSON.stringify(result, null, 2));
@@ -63,6 +63,14 @@ export const listSubcommand = createSubcommand({
63
63
  orgId: z.string().optional().describe('Filter by organization ID'),
64
64
  projectId: z.string().optional().describe('Filter by project ID'),
65
65
  all: z.boolean().optional().describe('List all threads regardless of project context'),
66
+ sort: z
67
+ .enum(['created', 'updated'])
68
+ .optional()
69
+ .describe('field to sort by (default: created)'),
70
+ direction: z
71
+ .enum(['asc', 'desc'])
72
+ .optional()
73
+ .describe('sort direction (default: desc)'),
66
74
  }),
67
75
  response: ThreadListResponseSchema,
68
76
  },
@@ -78,11 +86,13 @@ export const listSubcommand = createSubcommand({
78
86
  const orgId = opts.orgId;
79
87
 
80
88
  try {
81
- const threads = await threadList(catalystClient, {
82
- count: opts.count,
83
- orgId,
84
- projectId,
85
- });
89
+ const threads = await threadList(catalystClient, {
90
+ count: opts.count,
91
+ orgId,
92
+ projectId,
93
+ sort: opts.sort,
94
+ direction: opts.direction,
95
+ });
86
96
 
87
97
  const result = threads.map((t: Thread) => ({
88
98
  id: t.id,
@@ -1,118 +1,116 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { existsSync, createReadStream, mkdirSync, rmSync } from 'node:fs';
2
+ import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { tmpdir, platform } from 'node:os';
4
4
  import { join, dirname } from 'node:path';
5
5
  import * as tar from 'tar';
6
- import { downloadRelease } from '@terascope/fetch-github-release';
6
+ import { StructuredError } from '@agentuity/core';
7
7
  import { spinner } from '../../tui';
8
8
 
9
- const user = 'agentuity';
10
- const repo = 'gravity';
11
-
12
- function filterRelease(release: { prerelease: boolean }) {
13
- // Filter out prereleases.
14
- return release.prerelease === false;
15
- }
16
-
17
- function filterAsset(asset: { name: string }): boolean {
18
- // Filter out the release matching our os and architecture
19
- let arch: string = process.arch;
20
- if (arch === 'x64') {
21
- arch = 'x86_64';
22
- }
23
- return asset.name.includes(arch) && asset.name.includes(platform());
24
- }
25
-
26
9
  interface GravityClient {
27
10
  filename: string;
28
11
  version: string;
29
12
  }
30
13
 
14
+ const GravityVersionError = StructuredError('GravityVersionError')<{
15
+ status: number;
16
+ statusText: string;
17
+ }>();
18
+ const GravityDownloadError = StructuredError('GravityDownloadError')<{
19
+ status: number;
20
+ statusText: string;
21
+ }>();
22
+ const GravityExtractionError = StructuredError('GravityExtractionError')<{
23
+ path: string;
24
+ }>();
25
+
26
+ function getBaseURL(): string {
27
+ return process.env.AGENTUITY_SH_URL || 'https://agentuity.sh';
28
+ }
29
+
31
30
  /**
32
31
  *
33
32
  * @returns full path to the downloaded file
34
33
  */
35
34
  export async function download(gravityDir: string): Promise<GravityClient> {
36
- const outputdir = join(tmpdir(), randomUUID());
35
+ const baseURL = getBaseURL();
37
36
 
38
- const res = (await spinner({
37
+ // Step 1: Get the latest version from agentuity.sh
38
+ const tag = (await spinner({
39
39
  message: 'Checking Agentuity Gravity',
40
40
  callback: async () => {
41
- return downloadRelease(
42
- user,
43
- repo,
44
- outputdir,
45
- filterRelease,
46
- filterAsset,
47
- false,
48
- true,
49
- true,
50
- ''
51
- );
41
+ const resp = await fetch(`${baseURL}/release/gravity/version`, {
42
+ signal: AbortSignal.timeout(10_000),
43
+ });
44
+ if (!resp.ok) {
45
+ throw new GravityVersionError({
46
+ status: resp.status,
47
+ statusText: resp.statusText,
48
+ });
49
+ }
50
+ const text = (await resp.text()).trim();
51
+ return text.startsWith('v') ? text : `v${text}`;
52
52
  },
53
53
  clearOnSuccess: true,
54
- })) as { release: string; assetFileNames: string[] };
54
+ })) as string;
55
55
 
56
- const versionTok = res.release.split('@');
57
- const version = versionTok[1] ?? 'unknown';
56
+ const version = tag.startsWith('v') ? tag.slice(1) : tag;
58
57
  const releaseFilename = join(gravityDir, version, 'gravity');
59
- const mustDownload = !existsSync(releaseFilename);
60
58
 
61
- if (!mustDownload) {
59
+ // Step 2: Check if already downloaded
60
+ if (await Bun.file(releaseFilename).exists()) {
62
61
  return { filename: releaseFilename, version };
63
62
  }
64
63
 
65
- const downloadedFile = await spinner({
66
- message: `Downloading Gravity ${version}`,
67
- callback: async () => {
68
- const res = (await downloadRelease(
69
- user,
70
- repo,
71
- outputdir,
72
- filterRelease,
73
- filterAsset,
74
- false,
75
- true,
76
- false,
77
- ''
78
- )) as string[];
79
- const file = res[0];
80
- if (!file) {
81
- throw new Error('No file downloaded from release');
82
- }
83
- return file;
84
- },
85
- clearOnSuccess: true,
86
- });
64
+ // Step 3: Download the binary from agentuity.sh
65
+ const os = platform();
66
+ let arch: string = process.arch;
67
+ if (arch === 'x64') {
68
+ arch = 'x86_64';
69
+ }
87
70
 
88
- if (downloadedFile.endsWith('.tar.gz')) {
71
+ const tmpFile = join(tmpdir(), `${randomUUID()}.tar.gz`);
72
+
73
+ try {
89
74
  await spinner({
90
- message: 'Extracting release',
75
+ message: `Downloading Gravity ${version}`,
91
76
  callback: async () => {
92
- return new Promise<void>((resolve, reject) => {
93
- const input = createReadStream(downloadedFile);
94
- const downloadDir = dirname(releaseFilename);
95
- if (!existsSync(downloadDir)) {
96
- mkdirSync(downloadDir, { recursive: true });
97
- }
98
- input.on('finish', resolve);
99
- input.on('end', resolve);
100
- input.on('error', reject);
101
- input.pipe(tar.x({ C: downloadDir, chmod: true }));
77
+ const resp = await fetch(`${baseURL}/release/gravity/${tag}/${os}/${arch}`, {
78
+ signal: AbortSignal.timeout(60_000),
102
79
  });
80
+ if (!resp.ok) {
81
+ throw new GravityDownloadError({
82
+ status: resp.status,
83
+ statusText: resp.statusText,
84
+ });
85
+ }
86
+ const buffer = await resp.arrayBuffer();
87
+ writeFileSync(tmpFile, Buffer.from(buffer));
103
88
  },
104
89
  clearOnSuccess: true,
105
90
  });
106
- } else {
107
- // TODO:
108
- }
109
91
 
110
- if (existsSync(outputdir)) {
111
- rmSync(outputdir, { recursive: true });
92
+ // Step 4: Extract the tarball
93
+ await spinner({
94
+ message: 'Extracting release',
95
+ callback: async () => {
96
+ const downloadDir = dirname(releaseFilename);
97
+ if (!(await Bun.file(downloadDir).exists())) {
98
+ mkdirSync(downloadDir, { recursive: true });
99
+ }
100
+ await tar.x({ file: tmpFile, cwd: downloadDir, chmod: true });
101
+ },
102
+ clearOnSuccess: true,
103
+ });
104
+ } finally {
105
+ // Clean up temp file regardless of success or failure
106
+ if (await Bun.file(tmpFile).exists()) {
107
+ rmSync(tmpFile);
108
+ }
112
109
  }
113
110
 
114
- if (!existsSync(releaseFilename)) {
115
- throw new Error(`Failed to extract gravity binary to ${releaseFilename}`);
111
+ // Step 5: Verify the binary was extracted
112
+ if (!(await Bun.file(releaseFilename).exists())) {
113
+ throw new GravityExtractionError({ path: releaseFilename });
116
114
  }
117
115
 
118
116
  return { filename: releaseFilename, version };
@@ -0,0 +1,146 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { getCommand } from '../../../command-prefix';
5
+ import { loadProjectConfig } from '../../../config';
6
+ import { isJSONMode } from '../../../output';
7
+ import {
8
+ checkCustomDomainForDNS,
9
+ isSuccess,
10
+ isPending,
11
+ isMissing,
12
+ isMisconfigured,
13
+ isError,
14
+ } from '../../../domain';
15
+
16
+ export const checkSubcommand = createSubcommand({
17
+ name: 'check',
18
+ description: 'Check DNS configuration for custom domains',
19
+ tags: ['read-only', 'slow', 'requires-auth', 'requires-project'],
20
+ requires: { auth: true, project: true },
21
+ idempotent: true,
22
+ examples: [
23
+ {
24
+ command: getCommand('project domain check'),
25
+ description: 'Check all configured domains',
26
+ },
27
+ {
28
+ command: getCommand('project domain check --domain example.com'),
29
+ description: 'Check a specific domain',
30
+ },
31
+ ],
32
+ schema: {
33
+ options: z.object({
34
+ domain: z.string().optional().describe('Specific domain to check'),
35
+ }),
36
+ response: z.object({
37
+ domains: z.array(
38
+ z.object({
39
+ domain: z.string(),
40
+ recordType: z.string(),
41
+ target: z.string(),
42
+ status: z.string(),
43
+ success: z.boolean(),
44
+ })
45
+ ),
46
+ }),
47
+ },
48
+
49
+ async handler(ctx) {
50
+ const { opts, options, projectDir, config, project } = ctx;
51
+ const jsonMode = isJSONMode(options);
52
+
53
+ // Determine which domains to check
54
+ let domainsToCheck: string[];
55
+
56
+ if (opts?.domain) {
57
+ domainsToCheck = [opts.domain.toLowerCase().trim()];
58
+ } else {
59
+ const projectConfig = await loadProjectConfig(projectDir, config);
60
+ domainsToCheck = projectConfig.deployment?.domains ?? [];
61
+ }
62
+
63
+ if (domainsToCheck.length === 0) {
64
+ if (!jsonMode) {
65
+ tui.info('No custom domains configured for this project');
66
+ tui.info(`Use ${tui.bold(getCommand('project add domain <domain>'))} to add one`);
67
+ }
68
+ return { domains: [] };
69
+ }
70
+
71
+ const results = jsonMode
72
+ ? await checkCustomDomainForDNS(project.projectId, domainsToCheck, config)
73
+ : await tui.spinner({
74
+ message: `Checking DNS for ${domainsToCheck.length} ${tui.plural(domainsToCheck.length, 'domain', 'domains')}`,
75
+ clearOnSuccess: true,
76
+ callback: () => checkCustomDomainForDNS(project.projectId, domainsToCheck, config),
77
+ });
78
+
79
+ const domainResults = results.map((r) => {
80
+ let status: string;
81
+ let statusRaw: string;
82
+ let success = false;
83
+
84
+ if (isSuccess(r)) {
85
+ status = tui.colorSuccess(`${tui.ICONS.success} Configured`);
86
+ statusRaw = 'configured';
87
+ success = true;
88
+ } else if (isPending(r)) {
89
+ status = tui.colorWarning('⏳ Pending');
90
+ statusRaw = 'pending';
91
+ } else if (isMisconfigured(r)) {
92
+ status = tui.colorWarning(`${tui.ICONS.warning} ${r.misconfigured}`);
93
+ statusRaw = 'misconfigured';
94
+ } else if (isError(r)) {
95
+ status = tui.colorError(`${tui.ICONS.error} ${r.error}`);
96
+ statusRaw = 'error';
97
+ } else if (isMissing(r)) {
98
+ status = tui.colorError(`${tui.ICONS.error} Missing`);
99
+ statusRaw = 'missing';
100
+ } else {
101
+ status = tui.colorError(`${tui.ICONS.error} Unknown`);
102
+ statusRaw = 'unknown';
103
+ }
104
+
105
+ return {
106
+ domain: r.domain,
107
+ recordType: r.recordType,
108
+ target: r.target,
109
+ status,
110
+ statusRaw,
111
+ success,
112
+ };
113
+ });
114
+
115
+ if (!jsonMode) {
116
+ tui.newline();
117
+ for (const r of domainResults) {
118
+ console.log(` ${tui.colorInfo('Domain:')} ${tui.colorPrimary(r.domain)}`);
119
+ console.log(` ${tui.colorInfo('Type:')} ${tui.colorPrimary(r.recordType)}`);
120
+ console.log(` ${tui.colorInfo('Target:')} ${tui.colorPrimary(r.target)}`);
121
+ console.log(` ${tui.colorInfo('Status:')} ${r.status}`);
122
+ console.log();
123
+ }
124
+
125
+ const allGood = domainResults.every((r) => r.success);
126
+ if (allGood) {
127
+ tui.success('All domains are correctly configured');
128
+ } else {
129
+ const failCount = domainResults.filter((r) => !r.success).length;
130
+ tui.warning(
131
+ `${failCount} ${tui.plural(failCount, 'domain has', 'domains have')} DNS issues — add a CNAME record pointing to the target shown above`
132
+ );
133
+ }
134
+ }
135
+
136
+ return {
137
+ domains: domainResults.map((r) => ({
138
+ domain: r.domain,
139
+ recordType: r.recordType,
140
+ target: r.target,
141
+ status: r.statusRaw,
142
+ success: r.success,
143
+ })),
144
+ };
145
+ },
146
+ });
@@ -0,0 +1,20 @@
1
+ import { createCommand } from '../../../types';
2
+ import { checkSubcommand } from './check';
3
+ import { getCommand } from '../../../command-prefix';
4
+
5
+ export const domainCommand = createCommand({
6
+ name: 'domain',
7
+ description: 'Manage custom domains for the project',
8
+ tags: ['fast', 'requires-auth'],
9
+ examples: [
10
+ {
11
+ command: getCommand('project domain check'),
12
+ description: 'Check DNS for all custom domains',
13
+ },
14
+ {
15
+ command: getCommand('project domain check --domain example.com'),
16
+ description: 'Check DNS for a specific domain',
17
+ },
18
+ ],
19
+ subcommands: [checkSubcommand],
20
+ });
@@ -0,0 +1,54 @@
1
+ import { z } from 'zod';
2
+ import { createSubcommand } from '../../../types';
3
+ import * as tui from '../../../tui';
4
+ import { projectHostnameGet } from '@agentuity/server';
5
+ import { getCommand } from '../../../command-prefix';
6
+ import { isJSONMode } from '../../../output';
7
+
8
+ const HostnameGetResponseSchema = z.object({
9
+ hostname: z.string().nullable().describe('The vanity hostname'),
10
+ url: z.string().nullable().describe('The full URL'),
11
+ });
12
+
13
+ export const getSubcommand = createSubcommand({
14
+ name: 'get',
15
+ description: 'Show the current vanity hostname for the project',
16
+ tags: ['read-only', 'fast', 'requires-auth', 'requires-project'],
17
+ requires: { auth: true, apiClient: true, project: true },
18
+ idempotent: true,
19
+ examples: [
20
+ {
21
+ command: getCommand('project hostname get'),
22
+ description: 'Show current hostname',
23
+ },
24
+ ],
25
+ schema: {
26
+ response: HostnameGetResponseSchema,
27
+ },
28
+
29
+ async handler(ctx) {
30
+ const { apiClient, project, options } = ctx;
31
+ const jsonMode = isJSONMode(options);
32
+
33
+ const result = jsonMode
34
+ ? await projectHostnameGet(apiClient, { projectId: project.projectId })
35
+ : await tui.spinner('Fetching hostname', () => {
36
+ return projectHostnameGet(apiClient, { projectId: project.projectId });
37
+ });
38
+
39
+ if (!jsonMode) {
40
+ if (result.hostname) {
41
+ tui.success(`Hostname: ${tui.bold(result.hostname)}`);
42
+ tui.info(`URL: ${result.url}`);
43
+ } else {
44
+ tui.info('No vanity hostname set for this project');
45
+ tui.info(`Use ${tui.bold(getCommand('project hostname set <hostname>'))} to set one`);
46
+ }
47
+ }
48
+
49
+ return {
50
+ hostname: result.hostname,
51
+ url: result.url,
52
+ };
53
+ },
54
+ });
@@ -0,0 +1,18 @@
1
+ import { createCommand } from '../../../types';
2
+ import { getSubcommand } from './get';
3
+ import { setSubcommand } from './set';
4
+ import { getCommand } from '../../../command-prefix';
5
+
6
+ export const hostnameCommand = createCommand({
7
+ name: 'hostname',
8
+ description: 'Manage the project vanity hostname on agentuity.run',
9
+ tags: ['fast', 'requires-auth'],
10
+ examples: [
11
+ { command: getCommand('project hostname get'), description: 'Show current hostname' },
12
+ {
13
+ command: getCommand('project hostname set my-cool-api'),
14
+ description: 'Set a custom hostname',
15
+ },
16
+ ],
17
+ subcommands: [getSubcommand, setSubcommand],
18
+ });