@agentuity/cli 0.0.42 → 0.0.44

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 (249) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +1 -1
  3. package/bin/cli.ts +7 -5
  4. package/dist/api.d.ts +3 -3
  5. package/dist/api.d.ts.map +1 -1
  6. package/dist/auth.d.ts +10 -2
  7. package/dist/auth.d.ts.map +1 -1
  8. package/dist/banner.d.ts.map +1 -1
  9. package/dist/cli.d.ts.map +1 -1
  10. package/dist/cmd/auth/api.d.ts +4 -4
  11. package/dist/cmd/auth/api.d.ts.map +1 -1
  12. package/dist/cmd/auth/index.d.ts.map +1 -1
  13. package/dist/cmd/auth/login.d.ts.map +1 -1
  14. package/dist/cmd/auth/signup.d.ts.map +1 -1
  15. package/dist/cmd/auth/ssh/add.d.ts +2 -0
  16. package/dist/cmd/auth/ssh/add.d.ts.map +1 -0
  17. package/dist/cmd/auth/ssh/api.d.ts +16 -0
  18. package/dist/cmd/auth/ssh/api.d.ts.map +1 -0
  19. package/dist/cmd/auth/ssh/delete.d.ts +2 -0
  20. package/dist/cmd/auth/ssh/delete.d.ts.map +1 -0
  21. package/dist/cmd/auth/ssh/index.d.ts +3 -0
  22. package/dist/cmd/auth/ssh/index.d.ts.map +1 -0
  23. package/dist/cmd/auth/ssh/list.d.ts +2 -0
  24. package/dist/cmd/auth/ssh/list.d.ts.map +1 -0
  25. package/dist/cmd/auth/whoami.d.ts +2 -0
  26. package/dist/cmd/auth/whoami.d.ts.map +1 -0
  27. package/dist/cmd/bundle/ast.d.ts +14 -3
  28. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  29. package/dist/cmd/bundle/ast.test.d.ts +2 -0
  30. package/dist/cmd/bundle/ast.test.d.ts.map +1 -0
  31. package/dist/cmd/bundle/bundler.d.ts +6 -1
  32. package/dist/cmd/bundle/bundler.d.ts.map +1 -1
  33. package/dist/cmd/bundle/file.d.ts.map +1 -1
  34. package/dist/cmd/bundle/fix-duplicate-exports.d.ts +2 -0
  35. package/dist/cmd/bundle/fix-duplicate-exports.d.ts.map +1 -0
  36. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts +2 -0
  37. package/dist/cmd/bundle/fix-duplicate-exports.test.d.ts.map +1 -0
  38. package/dist/cmd/bundle/index.d.ts +1 -1
  39. package/dist/cmd/bundle/index.d.ts.map +1 -1
  40. package/dist/cmd/bundle/plugin.d.ts +2 -0
  41. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  42. package/dist/cmd/cloud/deploy.d.ts.map +1 -0
  43. package/dist/cmd/cloud/domain.d.ts +17 -0
  44. package/dist/cmd/cloud/domain.d.ts.map +1 -0
  45. package/dist/cmd/cloud/index.d.ts.map +1 -0
  46. package/dist/cmd/cloud/resource/add.d.ts +2 -0
  47. package/dist/cmd/cloud/resource/add.d.ts.map +1 -0
  48. package/dist/cmd/cloud/resource/delete.d.ts +2 -0
  49. package/dist/cmd/cloud/resource/delete.d.ts.map +1 -0
  50. package/dist/cmd/cloud/resource/index.d.ts +3 -0
  51. package/dist/cmd/cloud/resource/index.d.ts.map +1 -0
  52. package/dist/cmd/cloud/resource/list.d.ts +2 -0
  53. package/dist/cmd/cloud/resource/list.d.ts.map +1 -0
  54. package/dist/cmd/cloud/scp/download.d.ts +2 -0
  55. package/dist/cmd/cloud/scp/download.d.ts.map +1 -0
  56. package/dist/cmd/cloud/scp/index.d.ts +3 -0
  57. package/dist/cmd/cloud/scp/index.d.ts.map +1 -0
  58. package/dist/cmd/cloud/scp/upload.d.ts +2 -0
  59. package/dist/cmd/cloud/scp/upload.d.ts.map +1 -0
  60. package/dist/cmd/cloud/ssh.d.ts +2 -0
  61. package/dist/cmd/cloud/ssh.d.ts.map +1 -0
  62. package/dist/cmd/dev/api.d.ts +18 -0
  63. package/dist/cmd/dev/api.d.ts.map +1 -0
  64. package/dist/cmd/dev/download.d.ts +11 -0
  65. package/dist/cmd/dev/download.d.ts.map +1 -0
  66. package/dist/cmd/dev/index.d.ts.map +1 -1
  67. package/dist/cmd/dev/templates.d.ts +3 -0
  68. package/dist/cmd/dev/templates.d.ts.map +1 -0
  69. package/dist/cmd/env/delete.d.ts +2 -0
  70. package/dist/cmd/env/delete.d.ts.map +1 -0
  71. package/dist/cmd/env/get.d.ts +2 -0
  72. package/dist/cmd/env/get.d.ts.map +1 -0
  73. package/dist/cmd/env/import.d.ts +2 -0
  74. package/dist/cmd/env/import.d.ts.map +1 -0
  75. package/dist/cmd/env/index.d.ts +2 -0
  76. package/dist/cmd/env/index.d.ts.map +1 -0
  77. package/dist/cmd/env/list.d.ts.map +1 -0
  78. package/dist/cmd/env/pull.d.ts +2 -0
  79. package/dist/cmd/env/pull.d.ts.map +1 -0
  80. package/dist/cmd/env/push.d.ts +2 -0
  81. package/dist/cmd/env/push.d.ts.map +1 -0
  82. package/dist/cmd/env/set.d.ts +2 -0
  83. package/dist/cmd/env/set.d.ts.map +1 -0
  84. package/dist/cmd/profile/show.d.ts.map +1 -1
  85. package/dist/cmd/project/create.d.ts.map +1 -1
  86. package/dist/cmd/project/delete.d.ts.map +1 -1
  87. package/dist/cmd/project/download.d.ts +1 -1
  88. package/dist/cmd/project/download.d.ts.map +1 -1
  89. package/dist/cmd/project/list.d.ts.map +1 -1
  90. package/dist/cmd/project/show.d.ts.map +1 -1
  91. package/dist/cmd/project/template-flow.d.ts +5 -1
  92. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  93. package/dist/cmd/secret/delete.d.ts +2 -0
  94. package/dist/cmd/secret/delete.d.ts.map +1 -0
  95. package/dist/cmd/secret/get.d.ts +2 -0
  96. package/dist/cmd/secret/get.d.ts.map +1 -0
  97. package/dist/cmd/secret/import.d.ts +2 -0
  98. package/dist/cmd/secret/import.d.ts.map +1 -0
  99. package/dist/cmd/secret/index.d.ts +2 -0
  100. package/dist/cmd/secret/index.d.ts.map +1 -0
  101. package/dist/cmd/secret/list.d.ts +2 -0
  102. package/dist/cmd/secret/list.d.ts.map +1 -0
  103. package/dist/cmd/secret/pull.d.ts +2 -0
  104. package/dist/cmd/secret/pull.d.ts.map +1 -0
  105. package/dist/cmd/secret/push.d.ts +2 -0
  106. package/dist/cmd/secret/push.d.ts.map +1 -0
  107. package/dist/cmd/secret/set.d.ts +2 -0
  108. package/dist/cmd/secret/set.d.ts.map +1 -0
  109. package/dist/cmd/version/index.d.ts.map +1 -1
  110. package/dist/config.d.ts +11 -3
  111. package/dist/config.d.ts.map +1 -1
  112. package/dist/crypto/box.d.ts +65 -0
  113. package/dist/crypto/box.d.ts.map +1 -0
  114. package/dist/crypto/box.test.d.ts +2 -0
  115. package/dist/crypto/box.test.d.ts.map +1 -0
  116. package/dist/download.d.ts.map +1 -1
  117. package/dist/env-util.d.ts +67 -0
  118. package/dist/env-util.d.ts.map +1 -0
  119. package/dist/env-util.test.d.ts +2 -0
  120. package/dist/env-util.test.d.ts.map +1 -0
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/schema-parser.d.ts.map +1 -1
  124. package/dist/steps.d.ts +4 -1
  125. package/dist/steps.d.ts.map +1 -1
  126. package/dist/terminal.d.ts.map +1 -1
  127. package/dist/tui.d.ts +32 -2
  128. package/dist/tui.d.ts.map +1 -1
  129. package/dist/types.d.ts +250 -127
  130. package/dist/types.d.ts.map +1 -1
  131. package/dist/utils/detectSubagent.d.ts +15 -0
  132. package/dist/utils/detectSubagent.d.ts.map +1 -0
  133. package/dist/utils/zip.d.ts +7 -0
  134. package/dist/utils/zip.d.ts.map +1 -0
  135. package/package.json +11 -3
  136. package/src/api-errors.md +2 -2
  137. package/src/api.ts +12 -7
  138. package/src/auth.ts +116 -7
  139. package/src/banner.ts +13 -6
  140. package/src/cli.ts +709 -36
  141. package/src/cmd/auth/api.ts +10 -16
  142. package/src/cmd/auth/index.ts +3 -1
  143. package/src/cmd/auth/login.ts +24 -8
  144. package/src/cmd/auth/signup.ts +15 -11
  145. package/src/cmd/auth/ssh/add.ts +263 -0
  146. package/src/cmd/auth/ssh/api.ts +94 -0
  147. package/src/cmd/auth/ssh/delete.ts +102 -0
  148. package/src/cmd/auth/ssh/index.ts +10 -0
  149. package/src/cmd/auth/ssh/list.ts +74 -0
  150. package/src/cmd/auth/whoami.ts +69 -0
  151. package/src/cmd/bundle/ast.test.ts +565 -0
  152. package/src/cmd/bundle/ast.ts +457 -44
  153. package/src/cmd/bundle/bundler.ts +255 -57
  154. package/src/cmd/bundle/file.ts +6 -12
  155. package/src/cmd/bundle/fix-duplicate-exports.test.ts +387 -0
  156. package/src/cmd/bundle/fix-duplicate-exports.ts +204 -0
  157. package/src/cmd/bundle/index.ts +11 -11
  158. package/src/cmd/bundle/patch/aisdk.ts +1 -1
  159. package/src/cmd/bundle/plugin.ts +373 -53
  160. package/src/cmd/cloud/deploy.ts +336 -0
  161. package/src/cmd/cloud/domain.ts +92 -0
  162. package/src/cmd/cloud/index.ts +11 -0
  163. package/src/cmd/cloud/resource/add.ts +56 -0
  164. package/src/cmd/cloud/resource/delete.ts +120 -0
  165. package/src/cmd/cloud/resource/index.ts +11 -0
  166. package/src/cmd/cloud/resource/list.ts +69 -0
  167. package/src/cmd/cloud/scp/download.ts +59 -0
  168. package/src/cmd/cloud/scp/index.ts +9 -0
  169. package/src/cmd/cloud/scp/upload.ts +62 -0
  170. package/src/cmd/cloud/ssh.ts +68 -0
  171. package/src/cmd/dev/api.ts +46 -0
  172. package/src/cmd/dev/download.ts +111 -0
  173. package/src/cmd/dev/index.ts +362 -34
  174. package/src/cmd/dev/templates.ts +84 -0
  175. package/src/cmd/env/delete.ts +47 -0
  176. package/src/cmd/env/get.ts +53 -0
  177. package/src/cmd/env/import.ts +102 -0
  178. package/src/cmd/env/index.ts +22 -0
  179. package/src/cmd/env/list.ts +56 -0
  180. package/src/cmd/env/pull.ts +80 -0
  181. package/src/cmd/env/push.ts +37 -0
  182. package/src/cmd/env/set.ts +71 -0
  183. package/src/cmd/index.ts +2 -2
  184. package/src/cmd/profile/show.ts +15 -6
  185. package/src/cmd/project/create.ts +7 -2
  186. package/src/cmd/project/delete.ts +75 -18
  187. package/src/cmd/project/download.ts +3 -3
  188. package/src/cmd/project/list.ts +8 -8
  189. package/src/cmd/project/show.ts +3 -7
  190. package/src/cmd/project/template-flow.ts +186 -48
  191. package/src/cmd/secret/delete.ts +40 -0
  192. package/src/cmd/secret/get.ts +54 -0
  193. package/src/cmd/secret/import.ts +64 -0
  194. package/src/cmd/secret/index.ts +22 -0
  195. package/src/cmd/secret/list.ts +56 -0
  196. package/src/cmd/secret/pull.ts +78 -0
  197. package/src/cmd/secret/push.ts +37 -0
  198. package/src/cmd/secret/set.ts +45 -0
  199. package/src/cmd/version/index.ts +2 -1
  200. package/src/config.ts +257 -27
  201. package/src/crypto/box.test.ts +431 -0
  202. package/src/crypto/box.ts +477 -0
  203. package/src/download.ts +1 -0
  204. package/src/env-util.test.ts +194 -0
  205. package/src/env-util.ts +290 -0
  206. package/src/index.ts +5 -1
  207. package/src/schema-parser.ts +2 -3
  208. package/src/steps.ts +144 -10
  209. package/src/terminal.ts +24 -23
  210. package/src/tui.ts +208 -68
  211. package/src/types.ts +292 -202
  212. package/src/utils/detectSubagent.ts +31 -0
  213. package/src/utils/zip.ts +38 -0
  214. package/dist/cmd/example/create-user.d.ts +0 -2
  215. package/dist/cmd/example/create-user.d.ts.map +0 -1
  216. package/dist/cmd/example/create.d.ts +0 -2
  217. package/dist/cmd/example/create.d.ts.map +0 -1
  218. package/dist/cmd/example/deploy.d.ts.map +0 -1
  219. package/dist/cmd/example/index.d.ts.map +0 -1
  220. package/dist/cmd/example/list.d.ts.map +0 -1
  221. package/dist/cmd/example/optional-auth.d.ts +0 -3
  222. package/dist/cmd/example/optional-auth.d.ts.map +0 -1
  223. package/dist/cmd/example/run-command.d.ts +0 -2
  224. package/dist/cmd/example/run-command.d.ts.map +0 -1
  225. package/dist/cmd/example/sound.d.ts +0 -3
  226. package/dist/cmd/example/sound.d.ts.map +0 -1
  227. package/dist/cmd/example/spinner.d.ts +0 -2
  228. package/dist/cmd/example/spinner.d.ts.map +0 -1
  229. package/dist/cmd/example/steps.d.ts +0 -2
  230. package/dist/cmd/example/steps.d.ts.map +0 -1
  231. package/dist/cmd/example/version.d.ts +0 -2
  232. package/dist/cmd/example/version.d.ts.map +0 -1
  233. package/dist/logger.d.ts +0 -24
  234. package/dist/logger.d.ts.map +0 -1
  235. package/src/cmd/example/create-user.ts +0 -38
  236. package/src/cmd/example/create.ts +0 -31
  237. package/src/cmd/example/deploy.ts +0 -36
  238. package/src/cmd/example/index.ts +0 -29
  239. package/src/cmd/example/list.ts +0 -32
  240. package/src/cmd/example/optional-auth.ts +0 -38
  241. package/src/cmd/example/run-command.ts +0 -45
  242. package/src/cmd/example/sound.ts +0 -14
  243. package/src/cmd/example/spinner.ts +0 -44
  244. package/src/cmd/example/steps.ts +0 -66
  245. package/src/cmd/example/version.ts +0 -13
  246. package/src/logger.ts +0 -235
  247. /package/dist/cmd/{example → cloud}/deploy.d.ts +0 -0
  248. /package/dist/cmd/{example → cloud}/index.d.ts +0 -0
  249. /package/dist/cmd/{example → env}/list.d.ts +0 -0
package/src/config.ts CHANGED
@@ -1,18 +1,29 @@
1
+ import { z } from 'zod';
2
+ import { existsSync } from 'node:fs';
3
+ import type { Logger } from '@agentuity/core';
4
+ import {
5
+ BuildMetadataSchema,
6
+ type BuildMetadata,
7
+ getServiceUrls,
8
+ APIClient as ServerAPIClient,
9
+ } from '@agentuity/server';
1
10
  import { YAML } from 'bun';
2
- import { join, extname, basename } from 'node:path';
11
+ import { join, extname, basename, resolve, normalize } from 'node:path';
3
12
  import { homedir } from 'node:os';
4
13
  import { mkdir, readdir, readFile, writeFile, chmod } from 'node:fs/promises';
14
+ import JSON5 from 'json5';
5
15
  import type { Config, Profile, AuthData } from './types';
6
- import { ConfigSchema, ProjectSchema, BuildMetadataSchema, type BuildMetadata } from './types';
16
+ import { ConfigSchema, ProjectSchema } from './types';
7
17
  import * as tui from './tui';
8
- import { z } from 'zod';
18
+
19
+ export const defaultProfileName = 'production';
9
20
 
10
21
  export function getDefaultConfigDir(): string {
11
22
  return join(homedir(), '.config', 'agentuity');
12
23
  }
13
24
 
14
25
  export function getDefaultConfigPath(): string {
15
- return join(getDefaultConfigDir(), 'production.yaml');
26
+ return join(getDefaultConfigDir(), defaultProfileName + '.yaml');
16
27
  }
17
28
 
18
29
  export function getProfilePath(): string {
@@ -71,7 +82,7 @@ export async function fetchProfiles(): Promise<Profile[]> {
71
82
  const content = await readFile(filePath, 'utf-8');
72
83
  const match = nameRegex.exec(content);
73
84
 
74
- if (match && match[1]) {
85
+ if (match?.[1]) {
75
86
  profiles.push({
76
87
  name: match[1],
77
88
  filename: filePath,
@@ -103,22 +114,24 @@ export async function loadConfig(customPath?: string): Promise<Config | null> {
103
114
  try {
104
115
  const file = Bun.file(configPath);
105
116
  const exists = await file.exists();
117
+ let result: ReturnType<typeof ConfigSchema.safeParse>;
106
118
 
107
- if (!exists) {
108
- return null;
109
- }
119
+ if (exists) {
120
+ const content = await file.text();
121
+ const config = YAML.parse(content);
110
122
 
111
- const content = await file.text();
112
- const config = YAML.parse(content);
123
+ // check to see if this is a legacy config file that might not have the required name
124
+ // and in this case we can just use the filename
125
+ const _config = config as { name?: string };
126
+ if (!_config.name) {
127
+ _config.name = basename(configPath).replace(extname(configPath), '');
128
+ }
113
129
 
114
- // check to see if this is a legacy config file that might not have the required name
115
- // and in this case we can just use the filename
116
- const _config = config as { name?: string };
117
- if (!_config.name) {
118
- _config.name = basename(configPath).replace(extname(configPath), '');
130
+ result = ConfigSchema.safeParse(config);
131
+ } else {
132
+ result = ConfigSchema.safeParse({ name: defaultProfileName });
119
133
  }
120
134
 
121
- const result = ConfigSchema.safeParse(config);
122
135
  if (!result.success) {
123
136
  tui.error(`Invalid config in ${configPath}:`);
124
137
  for (const issue of result.error.issues) {
@@ -128,6 +141,36 @@ export async function loadConfig(customPath?: string): Promise<Config | null> {
128
141
  process.exit(1);
129
142
  }
130
143
 
144
+ // allow environment variables to override
145
+ const overrides = result.data.overrides ?? ConfigSchema.shape.overrides.parse({});
146
+ if (overrides) {
147
+ if (process.env.AGENTUITY_API_URL) {
148
+ overrides.api_url = process.env.AGENTUITY_API_URL;
149
+ }
150
+ if (process.env.AGENTUITY_APP_URL) {
151
+ overrides.app_url = process.env.AGENTUITY_APP_URL;
152
+ }
153
+ if (process.env.AGENTUITY_CATALYST_URL) {
154
+ overrides.catalyst_url = process.env.AGENTUITY_CATALYST_URL;
155
+ }
156
+ if (process.env.AGENTUITY_TRANSPORT_URL) {
157
+ overrides.transport_url = process.env.AGENTUITY_TRANSPORT_URL;
158
+ }
159
+ if (process.env.AGENTUITY_KEYVALUE_URL) {
160
+ overrides.kv_url = process.env.AGENTUITY_KEYVALUE_URL;
161
+ }
162
+ if (process.env.AGENTUITY_OBJECTSTORE_URL) {
163
+ overrides.object_url = process.env.AGENTUITY_OBJECTSTORE_URL;
164
+ }
165
+ if (process.env.AGENTUITY_VECTOR_URL) {
166
+ overrides.vector_url = process.env.AGENTUITY_VECTOR_URL;
167
+ }
168
+ if (process.env.AGENTUITY_STREAM_URL) {
169
+ overrides.stream_url = process.env.AGENTUITY_STREAM_URL;
170
+ }
171
+ result.data.overrides = overrides;
172
+ }
173
+
131
174
  return result.data;
132
175
  } catch (error) {
133
176
  if (error instanceof Error) {
@@ -189,8 +232,18 @@ export async function saveConfig(config: Config, customPath?: string): Promise<v
189
232
  await chmod(configPath, 0o600);
190
233
  }
191
234
 
235
+ async function getOrInitConfig(): Promise<Config> {
236
+ const config = await loadConfig();
237
+ if (config) {
238
+ return config;
239
+ }
240
+ const profilePath = await getProfile();
241
+ const name = basename(profilePath, '.yaml');
242
+ return { name };
243
+ }
244
+
192
245
  export async function saveAuth(auth: AuthData): Promise<void> {
193
- const config = (await loadConfig()) || { name: 'default' };
246
+ const config = await getOrInitConfig();
194
247
  config.auth = {
195
248
  api_key: auth.apiKey,
196
249
  user_id: auth.userId,
@@ -202,7 +255,7 @@ export async function saveAuth(auth: AuthData): Promise<void> {
202
255
  }
203
256
 
204
257
  export async function clearAuth(): Promise<void> {
205
- const config = (await loadConfig()) || { name: 'default' };
258
+ const config = await getOrInitConfig();
206
259
  config.auth = {
207
260
  api_key: '',
208
261
  user_id: '',
@@ -213,7 +266,31 @@ export async function clearAuth(): Promise<void> {
213
266
  await saveConfig(config);
214
267
  }
215
268
 
269
+ export async function saveProjectDir(projectDir: string): Promise<void> {
270
+ const config = await getOrInitConfig();
271
+ config.preferences = config.preferences || {};
272
+ const normalized = resolve(normalize(projectDir));
273
+ (config.preferences as Record<string, unknown>).project_dir = normalized;
274
+ await saveConfig(config);
275
+ }
276
+
277
+ export async function saveOrgId(orgId: string): Promise<void> {
278
+ const config = await getOrInitConfig();
279
+ config.preferences = config.preferences || {};
280
+ (config.preferences as Record<string, unknown>).orgId = orgId;
281
+ await saveConfig(config);
282
+ }
283
+
216
284
  export async function getAuth(): Promise<AuthData | null> {
285
+ // allow automated login from environment variables if present
286
+ if (process.env.AGENTUITY_CLI_API_KEY && process.env.AGENTUITY_USER_ID) {
287
+ return {
288
+ apiKey: process.env.AGENTUITY_CLI_API_KEY,
289
+ userId: process.env.AGENTUITY_USER_ID,
290
+ expires: new Date(Date.now() + 30 * 60_000),
291
+ };
292
+ }
293
+
217
294
  const config = await loadConfig();
218
295
  if (!config) return null;
219
296
  const auth = config.auth as { api_key?: string; user_id?: string; expires?: number } | undefined;
@@ -255,6 +332,81 @@ function getPlaceholderValue(schema: z.ZodTypeAny): string {
255
332
  }
256
333
  }
257
334
 
335
+ function extractDefaultValue(schema: z.ZodTypeAny): unknown {
336
+ let unwrapped = schema;
337
+
338
+ // Unwrap optional layers
339
+ while (unwrapped instanceof z.ZodOptional) {
340
+ unwrapped = (unwrapped._def as unknown as { innerType: z.ZodTypeAny }).innerType;
341
+ }
342
+
343
+ // Check if it's a ZodDefault (has defaultValue in def or _def)
344
+ const checkDef = (obj: unknown): unknown => {
345
+ if (typeof obj !== 'object' || obj === null) return undefined;
346
+ const anyObj = obj as Record<string, unknown>;
347
+
348
+ // Check `def` property first (used in some Zod versions)
349
+ if ('def' in anyObj && typeof anyObj.def === 'object' && anyObj.def !== null) {
350
+ const def = anyObj.def as Record<string, unknown>;
351
+ if (def.type === 'default' && 'defaultValue' in def) {
352
+ const val = def.defaultValue;
353
+ return typeof val === 'function' ? (val as () => unknown)() : val;
354
+ }
355
+ }
356
+
357
+ // Check `_def` property (standard Zod property)
358
+ if ('_def' in anyObj && typeof anyObj._def === 'object' && anyObj._def !== null) {
359
+ const def = anyObj._def as Record<string, unknown>;
360
+ if (def.type === 'default' && 'defaultValue' in def) {
361
+ const val = def.defaultValue;
362
+ return typeof val === 'function' ? (val as () => unknown)() : val;
363
+ }
364
+ }
365
+
366
+ return undefined;
367
+ };
368
+
369
+ return checkDef(unwrapped);
370
+ }
371
+
372
+ function getValueWithDefaults(schema: z.ZodTypeAny, providedValue: unknown): unknown {
373
+ // If value is explicitly provided, use it
374
+ if (providedValue !== undefined) {
375
+ return providedValue;
376
+ }
377
+
378
+ // Try to extract default value
379
+ const defaultValue = extractDefaultValue(schema);
380
+ if (defaultValue !== undefined) {
381
+ return defaultValue;
382
+ }
383
+
384
+ // For optional fields without defaults, check if it's an object
385
+ let unwrapped = schema;
386
+ if (schema instanceof z.ZodOptional) {
387
+ unwrapped = (schema._def as unknown as { innerType: z.ZodTypeAny }).innerType;
388
+ }
389
+
390
+ // If it's an object schema, recursively populate defaults
391
+ if (unwrapped instanceof z.ZodObject) {
392
+ const shape = unwrapped.shape;
393
+ const result: Record<string, unknown> = {};
394
+ let hasAnyDefaults = false;
395
+
396
+ for (const [key, fieldSchema] of Object.entries(shape)) {
397
+ const fieldValue = getValueWithDefaults(fieldSchema as z.ZodTypeAny, undefined);
398
+ if (fieldValue !== undefined) {
399
+ result[key] = fieldValue;
400
+ hasAnyDefaults = true;
401
+ }
402
+ }
403
+
404
+ return hasAnyDefaults ? result : undefined;
405
+ }
406
+
407
+ return undefined;
408
+ }
409
+
258
410
  export function generateYAMLTemplate(name: string): string {
259
411
  const lines: string[] = [];
260
412
 
@@ -325,15 +477,61 @@ class ProjectConfigNotFoundExpection extends Error {
325
477
 
326
478
  type ProjectConfig = z.infer<typeof ProjectSchema>;
327
479
 
328
- export async function loadProjectConfig(dir: string): Promise<ProjectConfig> {
329
- const configPath = join(dir, 'agentuity.json');
480
+ function generateJSON5WithComments(
481
+ schema: z.ZodObject<z.ZodRawShape>,
482
+ data: Record<string, unknown>
483
+ ): string {
484
+ const lines: string[] = ['{'];
485
+ const shape = schema.shape;
486
+ const keys = Object.keys(shape);
487
+
488
+ for (let i = 0; i < keys.length; i++) {
489
+ const key = keys[i];
490
+ const fieldSchema = shape[key] as z.ZodTypeAny;
491
+ const description = getSchemaDescription(fieldSchema);
492
+ const providedValue = data[key];
493
+
494
+ if (description) {
495
+ lines.push(` // ${description}`);
496
+ }
497
+
498
+ // Get value with defaults applied
499
+ const valueWithDefaults = getValueWithDefaults(fieldSchema, providedValue);
500
+ const safeValue = valueWithDefaults === undefined ? null : valueWithDefaults;
501
+ const jsonValue = JSON.stringify(safeValue, null, 2).replace(/\n/g, '\n ');
502
+ const comma = i < keys.length - 1 ? ',' : '';
503
+ lines.push(` ${JSON.stringify(key)}: ${jsonValue}${comma}`);
504
+ }
505
+
506
+ lines.push('}');
507
+ return lines.join('\n');
508
+ }
509
+
510
+ export async function loadProjectConfig(
511
+ dir: string,
512
+ config?: Config | null
513
+ ): Promise<ProjectConfig> {
514
+ let configPath = join(dir, 'agentuity.json');
515
+
516
+ // Check for profile-specific override if config is provided
517
+ if (config?.name) {
518
+ const profileConfigPath = join(dir, `agentuity.${config.name}.json`);
519
+ if (await Bun.file(profileConfigPath).exists()) {
520
+ configPath = profileConfigPath;
521
+ }
522
+ }
523
+
330
524
  const file = Bun.file(configPath);
331
525
  if (!(await file.exists())) {
526
+ // TODO: check to see if a valid project that was created unauthenticated
527
+ // and then if so:
528
+ // 1. if authentication, offer to import the project
529
+ // 2. tell them that they need to login to use the command and import the project
332
530
  throw new ProjectConfigNotFoundExpection();
333
531
  }
334
532
  const text = await file.text();
335
- const config = JSON.parse(text);
336
- const result = ProjectSchema.safeParse(config);
533
+ const parsedConfig = JSON5.parse(text);
534
+ const result = ProjectSchema.safeParse(parsedConfig);
337
535
  if (!result.success) {
338
536
  tui.error(`Invalid project config at ${configPath}:`);
339
537
  for (const issue of result.error.issues) {
@@ -350,16 +548,18 @@ type InitialProjectConfig = ProjectConfig & {
350
548
  };
351
549
 
352
550
  export async function createProjectConfig(dir: string, config: InitialProjectConfig) {
353
- // Create a sanitized config without the apiKey for agentuity.json
354
551
  const { apiKey, ...sanitizedConfig } = config;
355
552
 
356
553
  const configPath = join(dir, 'agentuity.json');
357
- const configFile = Bun.file(configPath);
358
- await configFile.write(JSON.stringify(sanitizedConfig, null, 2));
554
+ const json5Content = generateJSON5WithComments(ProjectSchema, sanitizedConfig);
555
+ await Bun.write(configPath, json5Content + '\n');
359
556
 
360
557
  const envPath = join(dir, '.env');
361
- const envFile = Bun.file(envPath);
362
- await envFile.write(`AGENTUITY_SDK_KEY=${apiKey}`);
558
+ const comment =
559
+ '# AGENTUITY_SDK_KEY is a sensitive value and should not be committed to version control.';
560
+ const content = `${comment}\nAGENTUITY_SDK_KEY=${apiKey}\n`;
561
+ await Bun.write(envPath, content);
562
+ await chmod(envPath, 0o600);
363
563
  }
364
564
 
365
565
  export async function loadBuildMetadata(dir: string): Promise<BuildMetadata> {
@@ -381,3 +581,33 @@ export async function loadBuildMetadata(dir: string): Promise<BuildMetadata> {
381
581
  }
382
582
  return result.data;
383
583
  }
584
+
585
+ export async function loadDevelopmentProjectSDKKey(
586
+ projectDir: string
587
+ ): Promise<string | undefined> {
588
+ const files: string[] = ['.env.development', '.env.local', '.env'];
589
+ for (const filename of files) {
590
+ const fn = join(projectDir, filename);
591
+ if (existsSync(fn)) {
592
+ const buf = await Bun.file(fn).text();
593
+ const tok = buf.split(/\n/);
594
+ for (const t of tok) {
595
+ if (t.charAt(0) !== '#' && t.startsWith('AGENTUITY_SDK_KEY=')) {
596
+ const i = t.indexOf('=');
597
+ return t.substring(i + 1).trim();
598
+ }
599
+ }
600
+ }
601
+ }
602
+ }
603
+
604
+ export function getCatalystAPIClient(config: Config | null, logger: Logger, auth: AuthData) {
605
+ const serviceUrls = getServiceUrls();
606
+ const catalystUrl = config?.overrides?.catalyst_url ?? serviceUrls.catalyst;
607
+ return new ServerAPIClient(catalystUrl, logger, auth.apiKey);
608
+ }
609
+
610
+ export function getIONHost(config: Config | null) {
611
+ const url = new URL(config?.overrides?.ion_url ?? 'https://ion.agentuity.cloud');
612
+ return url.hostname;
613
+ }