@agentuity/cli 0.0.35 → 0.0.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/AGENTS.md +2 -2
  2. package/README.md +4 -4
  3. package/dist/api.d.ts +6 -22
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/auth.d.ts +0 -2
  6. package/dist/auth.d.ts.map +1 -1
  7. package/dist/banner.d.ts.map +1 -1
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cmd/auth/api.d.ts.map +1 -1
  10. package/dist/cmd/auth/login.d.ts +1 -2
  11. package/dist/cmd/auth/login.d.ts.map +1 -1
  12. package/dist/cmd/auth/logout.d.ts +1 -2
  13. package/dist/cmd/auth/logout.d.ts.map +1 -1
  14. package/dist/cmd/auth/signup.d.ts +1 -2
  15. package/dist/cmd/auth/signup.d.ts.map +1 -1
  16. package/dist/cmd/bundle/ast.d.ts +2 -0
  17. package/dist/cmd/bundle/ast.d.ts.map +1 -1
  18. package/dist/cmd/bundle/bundler.d.ts +1 -0
  19. package/dist/cmd/bundle/bundler.d.ts.map +1 -1
  20. package/dist/cmd/bundle/patch/index.d.ts.map +1 -1
  21. package/dist/cmd/bundle/patch/llm.d.ts +3 -0
  22. package/dist/cmd/bundle/patch/llm.d.ts.map +1 -0
  23. package/dist/cmd/bundle/plugin.d.ts.map +1 -1
  24. package/dist/cmd/dev/index.d.ts.map +1 -1
  25. package/dist/cmd/index.d.ts.map +1 -1
  26. package/dist/cmd/project/create.d.ts.map +1 -1
  27. package/dist/cmd/project/delete.d.ts.map +1 -1
  28. package/dist/cmd/project/download.d.ts.map +1 -1
  29. package/dist/cmd/project/list.d.ts.map +1 -1
  30. package/dist/cmd/project/show.d.ts.map +1 -1
  31. package/dist/cmd/project/template-flow.d.ts +3 -0
  32. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  33. package/dist/config.d.ts +11 -2
  34. package/dist/config.d.ts.map +1 -1
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/logger.d.ts +1 -1
  38. package/dist/logger.d.ts.map +1 -1
  39. package/dist/sound.d.ts.map +1 -1
  40. package/dist/tui.d.ts +16 -7
  41. package/dist/tui.d.ts.map +1 -1
  42. package/dist/types.d.ts +70 -7
  43. package/dist/types.d.ts.map +1 -1
  44. package/package.json +3 -2
  45. package/src/api.ts +27 -138
  46. package/src/auth.ts +87 -71
  47. package/src/banner.ts +7 -2
  48. package/src/cli.ts +7 -16
  49. package/src/cmd/auth/api.ts +40 -29
  50. package/src/cmd/auth/login.ts +7 -20
  51. package/src/cmd/auth/logout.ts +3 -3
  52. package/src/cmd/auth/signup.ts +6 -6
  53. package/src/cmd/bundle/ast.ts +169 -4
  54. package/src/cmd/bundle/bundler.ts +1 -0
  55. package/src/cmd/bundle/patch/index.ts +4 -0
  56. package/src/cmd/bundle/patch/llm.ts +36 -0
  57. package/src/cmd/bundle/plugin.ts +42 -1
  58. package/src/cmd/dev/index.ts +100 -1
  59. package/src/cmd/example/optional-auth.ts +1 -1
  60. package/src/cmd/index.ts +1 -0
  61. package/src/cmd/profile/README.md +1 -1
  62. package/src/cmd/project/create.ts +10 -2
  63. package/src/cmd/project/delete.ts +43 -2
  64. package/src/cmd/project/download.ts +17 -0
  65. package/src/cmd/project/list.ts +33 -2
  66. package/src/cmd/project/show.ts +35 -3
  67. package/src/cmd/project/template-flow.ts +60 -5
  68. package/src/config.ts +77 -5
  69. package/src/index.ts +2 -2
  70. package/src/logger.ts +1 -1
  71. package/src/sound.ts +9 -3
  72. package/src/tui.ts +234 -104
  73. package/src/types.ts +97 -34
@@ -1,12 +1,44 @@
1
+ import { z } from 'zod';
1
2
  import { createSubcommand } from '../../types';
3
+ import * as tui from '../../tui';
4
+ import { projectGet } from '@agentuity/server';
5
+ import { getAPIBaseURL, APIClient } from '../../api';
2
6
 
3
7
  export const showSubcommand = createSubcommand({
4
8
  name: 'show',
5
- description: 'Show project details',
9
+ aliases: ['get'],
10
+ description: 'Show project detail',
6
11
  requiresAuth: true,
12
+ schema: {
13
+ args: z.object({
14
+ id: z.string().describe('the project id'),
15
+ }),
16
+ options: z.object({
17
+ format: z
18
+ .enum(['json', 'table'])
19
+ .optional()
20
+ .describe('the output format: json, table (default)'),
21
+ }),
22
+ },
7
23
 
8
24
  async handler(ctx) {
9
- const { logger } = ctx;
10
- logger.info('TODO: Implement project show functionality');
25
+ const { opts, args, config } = ctx;
26
+
27
+ const apiUrl = getAPIBaseURL(config);
28
+ const client = new APIClient(apiUrl, config);
29
+
30
+ const project = await tui.spinner('Fetching project', () => {
31
+ return projectGet(client!, { id: args.id, mask: true });
32
+ });
33
+
34
+ if (!project) {
35
+ tui.fatal('Project not found');
36
+ }
37
+
38
+ if (opts?.format === 'json') {
39
+ console.log(JSON.stringify(project, null, 2));
40
+ } else {
41
+ console.table([project], ['id', 'orgId']);
42
+ }
11
43
  },
12
44
  });
@@ -3,12 +3,21 @@ import { existsSync, readdirSync, rmSync, statSync } from 'node:fs';
3
3
  import { cwd } from 'node:process';
4
4
  import { homedir } from 'node:os';
5
5
  import enquirer from 'enquirer';
6
+ import {
7
+ projectCreate,
8
+ projectExists,
9
+ listOrganizations,
10
+ type OrganizationList,
11
+ } from '@agentuity/server';
6
12
  import type { Logger } from '../../logger';
7
13
  import * as tui from '../../tui';
8
14
  import { playSound } from '../../sound';
9
15
  import { fetchTemplates, type TemplateInfo } from './templates';
10
16
  import { downloadTemplate, setupProject } from './download';
11
17
  import { showBanner } from '../../banner';
18
+ import type { AuthData, Config } from '../../types';
19
+ import { getAPIBaseURL, APIClient } from '../../api';
20
+ import { createProjectConfig } from '../../config';
12
21
 
13
22
  interface CreateFlowOptions {
14
23
  projectName?: string;
@@ -20,6 +29,8 @@ interface CreateFlowOptions {
20
29
  noBuild: boolean;
21
30
  skipPrompts: boolean;
22
31
  logger: Logger;
32
+ auth?: AuthData;
33
+ config?: Config;
23
34
  }
24
35
 
25
36
  export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
@@ -31,6 +42,8 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
31
42
  templateBranch,
32
43
  skipPrompts,
33
44
  logger,
45
+ auth,
46
+ config,
34
47
  } = options;
35
48
 
36
49
  showBanner();
@@ -40,10 +53,8 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
40
53
  tui.info(`📋 Loading templates from local directory: ${templateDir}...\n`);
41
54
  }
42
55
 
43
- let templates: TemplateInfo[] = [];
44
-
45
- await tui.spinner('Fetching templates', async () => {
46
- templates = await fetchTemplates(templateDir, templateBranch);
56
+ const templates = await tui.spinner('Fetching templates', async () => {
57
+ return fetchTemplates(templateDir, templateBranch);
47
58
  });
48
59
 
49
60
  if (templates.length === 0) {
@@ -52,16 +63,42 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
52
63
 
53
64
  // Step 2: Get project name
54
65
  let projectName = initialProjectName;
66
+
67
+ let orgs: OrganizationList | undefined;
68
+ let client: APIClient | undefined;
69
+ let orgId: string | undefined;
70
+
71
+ if (auth) {
72
+ const apiUrl = getAPIBaseURL(config);
73
+ client = new APIClient(apiUrl, config);
74
+ orgs = await tui.spinner('Fetching organizations', async () => {
75
+ const resp = await listOrganizations(client!);
76
+ if (resp.data) {
77
+ return resp.data;
78
+ }
79
+ });
80
+ if (!orgs) {
81
+ tui.fatal('no organizations could be found for your login');
82
+ }
83
+ orgId = await tui.selectOrganization(orgs, config?.preferences?.orgId);
84
+ }
85
+
55
86
  if (!projectName && !skipPrompts) {
56
87
  const response = await enquirer.prompt<{ name: string }>({
57
88
  type: 'input',
58
89
  name: 'name',
59
90
  message: 'What is the name of your project?',
60
91
  initial: 'My First Agent',
61
- validate: (value: string) => {
92
+ validate: async (value: string) => {
62
93
  if (!value || value.trim().length === 0) {
63
94
  return 'Project name is required';
64
95
  }
96
+ if (client) {
97
+ const exists = await projectExists(client, { name: value, organization_id: orgId! });
98
+ if (exists) {
99
+ return `Project with name '${value}' already exists in this organization`;
100
+ }
101
+ }
65
102
  return true;
66
103
  },
67
104
  });
@@ -163,6 +200,24 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
163
200
  logger,
164
201
  });
165
202
 
203
+ if (auth && client && orgId) {
204
+ await tui.spinner('Registering your project', async () => {
205
+ const res = await projectCreate(client, {
206
+ name: projectName,
207
+ organization_id: orgId,
208
+ provider: 'bunjs',
209
+ });
210
+ if (res.success && res.data) {
211
+ return createProjectConfig(dest, {
212
+ projectId: res.data.id,
213
+ orgId,
214
+ apiKey: res.data.api_key,
215
+ });
216
+ }
217
+ tui.fatal(res.message ?? 'failed to register project');
218
+ });
219
+ }
220
+
166
221
  // Step 8: Show completion message
167
222
  tui.success('✨ Project created successfully!\n');
168
223
  tui.info('Next steps:');
package/src/config.ts CHANGED
@@ -3,7 +3,7 @@ import { join, extname, basename } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { mkdir, readdir, readFile, writeFile, chmod } from 'node:fs/promises';
5
5
  import type { Config, Profile, AuthData } from './types';
6
- import { ConfigSchema } from './types';
6
+ import { ConfigSchema, ProjectSchema, BuildMetadataSchema, type BuildMetadata } from './types';
7
7
  import * as tui from './tui';
8
8
  import { z } from 'zod';
9
9
 
@@ -12,7 +12,7 @@ export function getDefaultConfigDir(): string {
12
12
  }
13
13
 
14
14
  export function getDefaultConfigPath(): string {
15
- return join(getDefaultConfigDir(), 'config.yaml');
15
+ return join(getDefaultConfigDir(), 'production.yaml');
16
16
  }
17
17
 
18
18
  export function getProfilePath(): string {
@@ -90,8 +90,15 @@ export async function fetchProfiles(): Promise<Profile[]> {
90
90
  return profiles;
91
91
  }
92
92
 
93
+ function expandTilde(path: string): string {
94
+ if (path.startsWith('~/')) {
95
+ return join(homedir(), path.slice(2));
96
+ }
97
+ return path;
98
+ }
99
+
93
100
  export async function loadConfig(customPath?: string): Promise<Config | null> {
94
- const configPath = customPath || (await getProfile());
101
+ const configPath = customPath ? expandTilde(customPath) : await getProfile();
95
102
 
96
103
  try {
97
104
  const file = Bun.file(configPath);
@@ -248,7 +255,7 @@ function getPlaceholderValue(schema: z.ZodTypeAny): string {
248
255
  }
249
256
  }
250
257
 
251
- function generateYAMLTemplate(name: string): string {
258
+ export function generateYAMLTemplate(name: string): string {
252
259
  const lines: string[] = [];
253
260
 
254
261
  // Add name (required)
@@ -308,4 +315,69 @@ function generateYAMLTemplate(name: string): string {
308
315
  return lines.join('\n');
309
316
  }
310
317
 
311
- export { generateYAMLTemplate };
318
+ class ProjectConfigNotFoundExpection extends Error {
319
+ public name: string;
320
+ constructor() {
321
+ super('project not found');
322
+ this.name = 'ProjectConfigNotFoundExpection';
323
+ }
324
+ }
325
+
326
+ type ProjectConfig = z.infer<typeof ProjectSchema>;
327
+
328
+ export async function loadProjectConfig(dir: string): Promise<ProjectConfig> {
329
+ const configPath = join(dir, 'agentuity.json');
330
+ const file = Bun.file(configPath);
331
+ if (!(await file.exists())) {
332
+ throw new ProjectConfigNotFoundExpection();
333
+ }
334
+ const text = await file.text();
335
+ const config = JSON.parse(text);
336
+ const result = ProjectSchema.safeParse(config);
337
+ if (!result.success) {
338
+ tui.error(`Invalid project config at ${configPath}:`);
339
+ for (const issue of result.error.issues) {
340
+ const path = issue.path.length > 0 ? issue.path.join('.') : 'root';
341
+ tui.bullet(`${path}: ${issue.message}`);
342
+ }
343
+ process.exit(1);
344
+ }
345
+ return result.data;
346
+ }
347
+
348
+ type InitialProjectConfig = ProjectConfig & {
349
+ apiKey: string;
350
+ };
351
+
352
+ export async function createProjectConfig(dir: string, config: InitialProjectConfig) {
353
+ // Create a sanitized config without the apiKey for agentuity.json
354
+ const { apiKey, ...sanitizedConfig } = config;
355
+
356
+ const configPath = join(dir, 'agentuity.json');
357
+ const configFile = Bun.file(configPath);
358
+ await configFile.write(JSON.stringify(sanitizedConfig, null, 2));
359
+
360
+ const envPath = join(dir, '.env');
361
+ const envFile = Bun.file(envPath);
362
+ await envFile.write(`AGENTUITY_SDK_KEY=${apiKey}`);
363
+ }
364
+
365
+ export async function loadBuildMetadata(dir: string): Promise<BuildMetadata> {
366
+ const filename = join(dir, 'agentuity.metadata.json');
367
+ const file = Bun.file(filename);
368
+ if (!(await file.exists())) {
369
+ throw new Error(`couldn't find ${filename}`);
370
+ }
371
+ const buffer = await file.text();
372
+ const config = JSON.parse(buffer);
373
+ const result = BuildMetadataSchema.safeParse(config);
374
+ if (!result.success) {
375
+ tui.error(`Invalid build metadata at ${filename}:`);
376
+ for (const issue of result.error.issues) {
377
+ const path = issue.path.length > 0 ? issue.path.join('.') : 'root';
378
+ tui.bullet(`${path}: ${issue.message}`);
379
+ }
380
+ process.exit(1);
381
+ }
382
+ return result.data;
383
+ }
package/src/index.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { createCLI, registerCommands } from './cli';
2
2
  export { validateRuntime, isBun } from './runtime';
3
3
  export { getVersion, getRevision, getPackageName, getPackage } from './version';
4
- export { requireAuth, optionalAuth, withAuth, withOptionalAuth } from './auth';
4
+ export { requireAuth, optionalAuth } from './auth';
5
5
  export {
6
6
  loadConfig,
7
7
  saveConfig,
@@ -16,7 +16,7 @@ export {
16
16
  clearAuth,
17
17
  getAuth,
18
18
  } from './config';
19
- export { APIClient, getAPIBaseURL, getAppBaseURL, UpgradeRequiredError } from './api';
19
+ export { APIClient, getAPIBaseURL, getAppBaseURL } from './api';
20
20
  export { Logger, logger } from './logger';
21
21
  export { showBanner } from './banner';
22
22
  export { discoverCommands } from './cmd';
package/src/logger.ts CHANGED
@@ -110,7 +110,7 @@ function getLogColors(scheme: ColorScheme): Record<LogLevel, LogColors> {
110
110
  }
111
111
 
112
112
  export class Logger {
113
- private level: LogLevel;
113
+ public level: LogLevel;
114
114
  private showTimestamp: boolean;
115
115
  private colorScheme: ColorScheme;
116
116
  private colors: Record<LogLevel, LogColors>;
package/src/sound.ts CHANGED
@@ -33,7 +33,13 @@ export function playSound(): void {
33
33
  return;
34
34
  }
35
35
 
36
- Bun.spawn(command, {
37
- stdio: ['ignore', 'ignore', 'ignore'],
38
- }).unref();
36
+ if (process.stdout.isTTY && Bun.which(command[0])) {
37
+ try {
38
+ Bun.spawn(command, {
39
+ stdio: ['ignore', 'ignore', 'ignore'],
40
+ }).unref();
41
+ } catch {
42
+ /* ignore */
43
+ }
44
+ }
39
45
  }