@agentuity/cli 0.1.10 → 0.1.12

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 (126) hide show
  1. package/dist/auth.d.ts.map +1 -1
  2. package/dist/auth.js +6 -10
  3. package/dist/auth.js.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +89 -41
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cmd/auth/login.d.ts.map +1 -1
  8. package/dist/cmd/auth/login.js +49 -5
  9. package/dist/cmd/auth/login.js.map +1 -1
  10. package/dist/cmd/build/patch/_util.d.ts.map +1 -1
  11. package/dist/cmd/build/patch/_util.js +6 -2
  12. package/dist/cmd/build/patch/_util.js.map +1 -1
  13. package/dist/cmd/cloud/agent/get.d.ts.map +1 -1
  14. package/dist/cmd/cloud/agent/get.js +10 -6
  15. package/dist/cmd/cloud/agent/get.js.map +1 -1
  16. package/dist/cmd/cloud/db/create.d.ts.map +1 -1
  17. package/dist/cmd/cloud/db/create.js +14 -3
  18. package/dist/cmd/cloud/db/create.js.map +1 -1
  19. package/dist/cmd/cloud/db/get.d.ts.map +1 -1
  20. package/dist/cmd/cloud/db/get.js +13 -3
  21. package/dist/cmd/cloud/db/get.js.map +1 -1
  22. package/dist/cmd/cloud/db/list.d.ts.map +1 -1
  23. package/dist/cmd/cloud/db/list.js +17 -25
  24. package/dist/cmd/cloud/db/list.js.map +1 -1
  25. package/dist/cmd/cloud/deployment/show.d.ts.map +1 -1
  26. package/dist/cmd/cloud/deployment/show.js +50 -37
  27. package/dist/cmd/cloud/deployment/show.js.map +1 -1
  28. package/dist/cmd/cloud/sandbox/create.d.ts.map +1 -1
  29. package/dist/cmd/cloud/sandbox/create.js +19 -2
  30. package/dist/cmd/cloud/sandbox/create.js.map +1 -1
  31. package/dist/cmd/cloud/sandbox/execution/get.d.ts.map +1 -1
  32. package/dist/cmd/cloud/sandbox/execution/get.js +14 -11
  33. package/dist/cmd/cloud/sandbox/execution/get.js.map +1 -1
  34. package/dist/cmd/cloud/sandbox/get.d.ts.map +1 -1
  35. package/dist/cmd/cloud/sandbox/get.js +48 -39
  36. package/dist/cmd/cloud/sandbox/get.js.map +1 -1
  37. package/dist/cmd/cloud/sandbox/list.d.ts.map +1 -1
  38. package/dist/cmd/cloud/sandbox/list.js +6 -1
  39. package/dist/cmd/cloud/sandbox/list.js.map +1 -1
  40. package/dist/cmd/cloud/sandbox/run.d.ts.map +1 -1
  41. package/dist/cmd/cloud/sandbox/run.js +5 -1
  42. package/dist/cmd/cloud/sandbox/run.js.map +1 -1
  43. package/dist/cmd/cloud/sandbox/snapshot/build.d.ts +5 -0
  44. package/dist/cmd/cloud/sandbox/snapshot/build.d.ts.map +1 -0
  45. package/dist/cmd/cloud/sandbox/snapshot/build.js +590 -0
  46. package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -0
  47. package/dist/cmd/cloud/sandbox/snapshot/generate.d.ts +3 -0
  48. package/dist/cmd/cloud/sandbox/snapshot/generate.d.ts.map +1 -0
  49. package/dist/cmd/cloud/sandbox/snapshot/generate.js +129 -0
  50. package/dist/cmd/cloud/sandbox/snapshot/generate.js.map +1 -0
  51. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
  52. package/dist/cmd/cloud/sandbox/snapshot/get.js +25 -6
  53. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
  54. package/dist/cmd/cloud/sandbox/snapshot/index.d.ts.map +1 -1
  55. package/dist/cmd/cloud/sandbox/snapshot/index.js +19 -1
  56. package/dist/cmd/cloud/sandbox/snapshot/index.js.map +1 -1
  57. package/dist/cmd/cloud/session/get.d.ts.map +1 -1
  58. package/dist/cmd/cloud/session/get.js +24 -23
  59. package/dist/cmd/cloud/session/get.js.map +1 -1
  60. package/dist/cmd/cloud/session/list.d.ts.map +1 -1
  61. package/dist/cmd/cloud/session/list.js +7 -2
  62. package/dist/cmd/cloud/session/list.js.map +1 -1
  63. package/dist/cmd/cloud/thread/get.d.ts.map +1 -1
  64. package/dist/cmd/cloud/thread/get.js +11 -8
  65. package/dist/cmd/cloud/thread/get.js.map +1 -1
  66. package/dist/cmd/cloud/thread/list.d.ts.map +1 -1
  67. package/dist/cmd/cloud/thread/list.js +6 -1
  68. package/dist/cmd/cloud/thread/list.js.map +1 -1
  69. package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
  70. package/dist/cmd/dev/file-watcher.js +2 -0
  71. package/dist/cmd/dev/file-watcher.js.map +1 -1
  72. package/dist/cmd/dev/index.d.ts.map +1 -1
  73. package/dist/cmd/dev/index.js +62 -4
  74. package/dist/cmd/dev/index.js.map +1 -1
  75. package/dist/cmd/project/auth/shared.js +1 -1
  76. package/dist/cmd/project/auth/shared.js.map +1 -1
  77. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  78. package/dist/cmd/project/template-flow.js +15 -1
  79. package/dist/cmd/project/template-flow.js.map +1 -1
  80. package/dist/cmd/setup/index.d.ts.map +1 -1
  81. package/dist/cmd/setup/index.js +40 -3
  82. package/dist/cmd/setup/index.js.map +1 -1
  83. package/dist/config.d.ts.map +1 -1
  84. package/dist/config.js +10 -5
  85. package/dist/config.js.map +1 -1
  86. package/dist/steps.d.ts.map +1 -1
  87. package/dist/steps.js +4 -2
  88. package/dist/steps.js.map +1 -1
  89. package/dist/tui.d.ts +11 -1
  90. package/dist/tui.d.ts.map +1 -1
  91. package/dist/tui.js +47 -12
  92. package/dist/tui.js.map +1 -1
  93. package/dist/utils/apt-validator.js +3 -3
  94. package/dist/utils/apt-validator.js.map +1 -1
  95. package/package.json +6 -6
  96. package/src/auth.ts +12 -11
  97. package/src/cli.ts +121 -43
  98. package/src/cmd/auth/login.ts +57 -5
  99. package/src/cmd/build/patch/_util.ts +6 -2
  100. package/src/cmd/cloud/agent/get.ts +14 -6
  101. package/src/cmd/cloud/db/create.ts +15 -3
  102. package/src/cmd/cloud/db/get.ts +14 -3
  103. package/src/cmd/cloud/db/list.ts +16 -26
  104. package/src/cmd/cloud/deployment/show.ts +53 -47
  105. package/src/cmd/cloud/sandbox/create.ts +20 -2
  106. package/src/cmd/cloud/sandbox/execution/get.ts +16 -13
  107. package/src/cmd/cloud/sandbox/get.ts +48 -38
  108. package/src/cmd/cloud/sandbox/list.ts +6 -1
  109. package/src/cmd/cloud/sandbox/run.ts +5 -1
  110. package/src/cmd/cloud/sandbox/snapshot/build.ts +723 -0
  111. package/src/cmd/cloud/sandbox/snapshot/generate.ts +136 -0
  112. package/src/cmd/cloud/sandbox/snapshot/get.ts +29 -6
  113. package/src/cmd/cloud/sandbox/snapshot/index.ts +19 -1
  114. package/src/cmd/cloud/session/get.ts +25 -29
  115. package/src/cmd/cloud/session/list.ts +7 -2
  116. package/src/cmd/cloud/thread/get.ts +12 -8
  117. package/src/cmd/cloud/thread/list.ts +6 -1
  118. package/src/cmd/dev/file-watcher.ts +2 -0
  119. package/src/cmd/dev/index.ts +76 -4
  120. package/src/cmd/project/auth/shared.ts +1 -1
  121. package/src/cmd/project/template-flow.ts +15 -1
  122. package/src/cmd/setup/index.ts +41 -3
  123. package/src/config.ts +11 -5
  124. package/src/steps.ts +4 -2
  125. package/src/tui.ts +61 -12
  126. package/src/utils/apt-validator.ts +3 -3
@@ -0,0 +1,136 @@
1
+ import { z } from 'zod';
2
+ import { createCommand } from '../../../../types';
3
+ import { getCommand } from '../../../../command-prefix';
4
+
5
+ const TEMPLATE_YAML = `# yaml-language-server: $schema=https://agentuity.dev/schema/cli/v1/agentuity-snapshot.json
6
+ #
7
+ # Agentuity Snapshot Build File
8
+ # =============================
9
+ # This file defines a reproducible sandbox environment.
10
+ # Build with: agentuity cloud sandbox snapshot build <directory>
11
+ #
12
+ # For documentation, see: https://agentuity.dev/Services/Sandbox
13
+
14
+ # Required: Schema version (must be 1)
15
+ version: 1
16
+
17
+ # Required: Runtime environment
18
+ # Format: name:tag (e.g., bun:1, node:20, python:3.12)
19
+ # Run 'agentuity cloud sandbox runtime list' to see available runtimes
20
+ runtime: bun:1
21
+
22
+ # Optional: Snapshot name (alphanumeric, underscores, dashes only)
23
+ # If not provided, a unique name will be auto-generated
24
+ # name: my-snapshot
25
+
26
+ # Optional: Human-readable description
27
+ # Can be overridden with --description flag
28
+ description: My sandbox snapshot
29
+
30
+ # Optional: Apt packages to install
31
+ # Packages are validated against Debian stable repositories
32
+ # Supports version pinning: package=version or package=version* (prefix match)
33
+ # dependencies:
34
+ # - curl
35
+ # - ffmpeg
36
+ # - imagemagick=8:6.9*
37
+
38
+ # Optional: Files to include from the build context directory
39
+ # Supports glob patterns and negative patterns (prefix with !)
40
+ # Files are placed in /home/agentuity/ in the sandbox
41
+ # files:
42
+ # - "*.js" # All JS files in root
43
+ # - src/** # All files in src/ recursively
44
+ # - config/*.json # All JSON files in config/
45
+ # - "!**/*.test.js" # Exclude test files
46
+ # - "!node_modules/**" # Exclude node_modules
47
+
48
+ # Optional: Environment variables
49
+ # Use \${VAR} syntax for build-time substitution via --env flag
50
+ # env:
51
+ # NODE_ENV: production
52
+ # API_URL: https://api.example.com
53
+ # SECRET_KEY: \${SECRET_KEY} # Substituted from: --env SECRET_KEY=value
54
+
55
+ # Optional: Custom metadata
56
+ # Use \${VAR} syntax for build-time substitution via --metadata flag
57
+ # Stored with the snapshot for reference
58
+ # metadata:
59
+ # version: \${VERSION} # Substituted from: --metadata VERSION=1.0.0
60
+ # author: team-name
61
+ # build_date: \${BUILD_DATE}
62
+ `;
63
+
64
+ const TEMPLATE_JSON = {
65
+ $schema: 'https://agentuity.dev/schema/cli/v1/agentuity-snapshot.json',
66
+ version: 1,
67
+ runtime: 'bun:1',
68
+ name: 'my-snapshot',
69
+ description: 'My sandbox snapshot',
70
+ dependencies: ['curl'],
71
+ files: ['src/**', '*.js', '!**/*.test.js'],
72
+ env: {
73
+ NODE_ENV: 'production',
74
+ },
75
+ metadata: {
76
+ version: '1.0.0',
77
+ },
78
+ };
79
+
80
+ const GenerateResponseSchema = z.object({
81
+ format: z.enum(['yaml', 'json']).describe('Output format'),
82
+ content: z.string().describe('Generated template content'),
83
+ });
84
+
85
+ export const generateSubcommand = createCommand({
86
+ name: 'generate',
87
+ aliases: ['gen', 'init'],
88
+ description: 'Generate a template snapshot build file',
89
+ tags: [],
90
+ requires: {},
91
+ skipUpgradeCheck: true,
92
+ skipSkill: true,
93
+ examples: [
94
+ {
95
+ command: getCommand('cloud sandbox snapshot generate'),
96
+ description: 'Generate a YAML template (default)',
97
+ },
98
+ {
99
+ command: getCommand('cloud sandbox snapshot generate --format json'),
100
+ description: 'Generate a JSON template',
101
+ },
102
+ {
103
+ command: getCommand('cloud sandbox snapshot generate > agentuity-snapshot.yaml'),
104
+ description: 'Save template to a file',
105
+ },
106
+ ],
107
+ schema: {
108
+ options: z.object({
109
+ format: z.enum(['yaml', 'json']).default('yaml').describe('Output format (yaml or json)'),
110
+ }),
111
+ response: GenerateResponseSchema,
112
+ },
113
+
114
+ async handler(ctx) {
115
+ const { opts, options } = ctx;
116
+ const format = opts.format;
117
+
118
+ let content: string;
119
+ if (format === 'json') {
120
+ content = JSON.stringify(TEMPLATE_JSON, null, 2);
121
+ } else {
122
+ content = TEMPLATE_YAML;
123
+ }
124
+
125
+ if (!options.json) {
126
+ console.log(content);
127
+ }
128
+
129
+ return {
130
+ format,
131
+ content,
132
+ };
133
+ },
134
+ });
135
+
136
+ export default generateSubcommand;
@@ -21,6 +21,7 @@ const SandboxInfoSchema = z.object({
21
21
 
22
22
  const SnapshotGetResponseSchema = z.object({
23
23
  snapshotId: z.string().describe('Snapshot ID'),
24
+ name: z.string().describe('Snapshot name'),
24
25
  tag: z.string().nullable().optional().describe('Snapshot tag'),
25
26
  sizeBytes: z.number().describe('Snapshot size in bytes'),
26
27
  fileCount: z.number().describe('Number of files'),
@@ -28,6 +29,11 @@ const SnapshotGetResponseSchema = z.object({
28
29
  createdAt: z.string().describe('Creation timestamp'),
29
30
  downloadUrl: z.string().optional().describe('Presigned download URL'),
30
31
  files: z.array(SnapshotFileSchema).nullable().optional().describe('Files in snapshot'),
32
+ userMetadata: z
33
+ .record(z.string(), z.string())
34
+ .nullable()
35
+ .optional()
36
+ .describe('User-defined metadata'),
31
37
  sandboxes: z
32
38
  .array(SandboxInfoSchema)
33
39
  .optional()
@@ -72,15 +78,32 @@ export const getSubcommand = createCommand({
72
78
  );
73
79
 
74
80
  if (!options.json) {
75
- tui.info(`Snapshot: ${tui.bold(snapshot.snapshotId)}`);
81
+ const tableData: Record<string, string | number> = {
82
+ Snapshot: tui.bold(snapshot.snapshotId),
83
+ Name: snapshot.name,
84
+ };
76
85
  if (snapshot.tag) {
77
- console.log(` ${tui.muted('Tag:')} ${snapshot.tag}`);
86
+ tableData['Tag'] = snapshot.tag;
78
87
  }
79
- console.log(` ${tui.muted('Size:')} ${tui.formatBytes(snapshot.sizeBytes)}`);
80
- console.log(` ${tui.muted('Files:')} ${snapshot.fileCount}`);
81
- console.log(` ${tui.muted('Created:')} ${snapshot.createdAt}`);
88
+ tableData['Size'] = tui.formatBytes(snapshot.sizeBytes);
89
+ tableData['Files'] = snapshot.fileCount;
90
+ tableData['Created'] = snapshot.createdAt;
82
91
  if (snapshot.parentSnapshotId) {
83
- console.log(` ${tui.muted('Parent:')} ${snapshot.parentSnapshotId}`);
92
+ tableData['Parent'] = snapshot.parentSnapshotId;
93
+ }
94
+
95
+ tui.table([tableData], Object.keys(tableData), { layout: 'vertical', padStart: ' ' });
96
+
97
+ if (
98
+ snapshot.userMetadata &&
99
+ typeof snapshot.userMetadata === 'object' &&
100
+ Object.keys(snapshot.userMetadata).length > 0
101
+ ) {
102
+ console.log('');
103
+ tui.info('Metadata:');
104
+ for (const [key, value] of Object.entries(snapshot.userMetadata)) {
105
+ console.log(` ${tui.muted('•')} ${key}=${value}`);
106
+ }
84
107
  }
85
108
 
86
109
  if (snapshot.files && snapshot.files.length > 0) {
@@ -4,6 +4,8 @@ import { listSubcommand } from './list';
4
4
  import { getSubcommand } from './get';
5
5
  import { deleteSubcommand } from './delete';
6
6
  import { tagSubcommand } from './tag';
7
+ import { buildSubcommand } from './build';
8
+ import { generateSubcommand } from './generate';
7
9
  import { getCommand } from '../../../../command-prefix';
8
10
 
9
11
  export const snapshotCommand = createCommand({
@@ -20,8 +22,24 @@ export const snapshotCommand = createCommand({
20
22
  command: getCommand('cloud sandbox snapshot list'),
21
23
  description: 'List all snapshots',
22
24
  },
25
+ {
26
+ command: getCommand('cloud sandbox snapshot build .'),
27
+ description: 'Build a snapshot from a declarative file',
28
+ },
29
+ {
30
+ command: getCommand('cloud sandbox snapshot generate > agentuity-snapshot.yaml'),
31
+ description: 'Generate a template build file',
32
+ },
33
+ ],
34
+ subcommands: [
35
+ createSubcommand,
36
+ listSubcommand,
37
+ getSubcommand,
38
+ deleteSubcommand,
39
+ tagSubcommand,
40
+ buildSubcommand,
41
+ generateSubcommand,
23
42
  ],
24
- subcommands: [createSubcommand, listSubcommand, getSubcommand, deleteSubcommand, tagSubcommand],
25
43
  requires: { auth: true, org: true },
26
44
  });
27
45
 
@@ -67,7 +67,7 @@ const SessionGetResponseSchema = z.object({
67
67
  pending: z.boolean(),
68
68
  success: z.boolean(),
69
69
  error: z.string().nullable(),
70
- result: z.string().nullable(),
70
+ result: z.record(z.string(), z.unknown()).nullable(),
71
71
  })
72
72
  )
73
73
  .describe('Eval runs'),
@@ -171,48 +171,44 @@ export const getSubcommand = createSubcommand({
171
171
  return result;
172
172
  }
173
173
 
174
- console.log(tui.bold('ID: ') + session.id);
175
- console.log(tui.bold('Project: ') + session.project_id);
176
- console.log(tui.bold('Deployment: ') + (session.deployment_id || '-'));
177
- console.log(tui.bold('Start: ') + new Date(session.start_time).toLocaleString());
174
+ const tableData: Record<string, string> = {
175
+ ID: session.id,
176
+ Project: session.project_id,
177
+ Deployment: session.deployment_id || '-',
178
+ Start: new Date(session.start_time).toLocaleString(),
179
+ };
178
180
  if (session.end_time) {
179
- console.log(tui.bold('End: ') + new Date(session.end_time).toLocaleString());
181
+ tableData['End'] = new Date(session.end_time).toLocaleString();
180
182
  }
181
- if (session.duration && session.end_time) {
182
- console.log(
183
- tui.bold('Duration: ') + `${(session.duration / 1_000_000).toFixed(0)}ms`
184
- );
183
+ if (session.duration != null && session.end_time != null) {
184
+ tableData['Duration'] = `${(session.duration / 1_000_000).toFixed(0)}ms`;
185
185
  }
186
- console.log(tui.bold('Method: ') + session.method);
187
- console.log(tui.bold('URL: ') + tui.link(session.url, session.url));
188
- console.log(tui.bold('Trigger: ') + session.trigger);
186
+ tableData['Method'] = session.method;
187
+ tableData['URL'] = tui.link(session.url, session.url);
188
+ tableData['Trigger'] = session.trigger;
189
189
  if (session.env !== 'production') {
190
- console.log(tui.bold('Environment: ') + session.env);
190
+ tableData['Environment'] = session.env;
191
191
  }
192
- console.log(tui.bold('Dev Mode: ') + (session.devmode ? 'Yes' : 'No'));
193
- console.log(
194
- tui.bold('Success: ') +
195
- (session.success ? tui.colorSuccess('✓') : tui.colorError('✗'))
196
- );
197
- console.log(tui.bold('Pending: ') + (session.pending ? 'Yes' : 'No'));
192
+ tableData['Dev Mode'] = session.devmode ? 'Yes' : 'No';
193
+ tableData['Success'] = session.success ? tui.colorSuccess('✓') : tui.colorError('✗');
194
+ tableData['Pending'] = session.pending ? 'Yes' : 'No';
198
195
  if (session.error) {
199
- console.log(tui.bold('Error: ') + tui.error(session.error));
196
+ tableData['Error'] = tui.colorError(session.error);
200
197
  }
201
198
  if (enriched.agents.length > 0) {
202
- const agentDisplay = enriched.agents
199
+ tableData['Agents'] = enriched.agents
203
200
  .map((agent: AgentInfo) => `${agent.name} ${tui.muted(`(${agent.identifier})`)}`)
204
201
  .join(', ');
205
- console.log(tui.bold('Agents: ') + agentDisplay);
206
202
  }
207
203
  if (enriched.route) {
208
- console.log(
209
- tui.bold('Route: ') +
210
- `${enriched.route.method.toUpperCase()} ${enriched.route.path} ${tui.muted(`(${enriched.route.id})`)}`
211
- );
204
+ tableData['Route'] =
205
+ `${enriched.route.method.toUpperCase()} ${enriched.route.path} ${tui.muted(`(${enriched.route.id})`)}`;
212
206
  } else {
213
- console.log(tui.bold('Route ID: ') + session.route_id);
207
+ tableData['Route ID'] = session.route_id;
214
208
  }
215
- console.log(tui.bold('Thread ID: ') + session.thread_id);
209
+ tableData['Thread ID'] = session.thread_id;
210
+
211
+ tui.table([tableData], Object.keys(tableData), { layout: 'vertical', padStart: ' ' });
216
212
 
217
213
  if (enriched.evalRuns.length > 0) {
218
214
  console.log('');
@@ -53,6 +53,10 @@ export const listSubcommand = createSubcommand({
53
53
  command: getCommand('cloud session list --env=production'),
54
54
  description: 'Only production environment',
55
55
  },
56
+ {
57
+ command: getCommand('cloud session list --all'),
58
+ description: 'List all sessions regardless of project context',
59
+ },
56
60
  ],
57
61
  aliases: ['ls'],
58
62
  requires: { auth: true },
@@ -76,6 +80,7 @@ export const listSubcommand = createSubcommand({
76
80
  .default(10)
77
81
  .describe('Number of sessions to list (1–100)'),
78
82
  projectId: z.string().optional().describe('Filter by project ID'),
83
+ all: z.boolean().optional().describe('List all sessions regardless of project context'),
79
84
  deploymentId: z.string().optional().describe('Filter by deployment ID'),
80
85
  trigger: z.string().optional().describe('Filter by trigger type (api, cron, webhook)'),
81
86
  env: z.string().optional().describe('Filter by environment'),
@@ -89,14 +94,14 @@ export const listSubcommand = createSubcommand({
89
94
  response: SessionListResponseSchema,
90
95
  },
91
96
  webUrl: (ctx) => {
92
- const projectId = ctx.opts?.projectId || ctx.project?.projectId;
97
+ const projectId = ctx.opts?.all ? undefined : ctx.opts?.projectId || ctx.project?.projectId;
93
98
  return projectId ? `/projects/${encodeURIComponent(projectId)}/sessions` : undefined;
94
99
  },
95
100
  async handler(ctx) {
96
101
  const { logger, auth, project, opts, options, config } = ctx;
97
102
  const catalystClient = await getGlobalCatalystAPIClient(logger, auth, config?.name);
98
103
 
99
- const projectId = opts.projectId || project?.projectId;
104
+ const projectId = opts.all ? undefined : opts.projectId || project?.projectId;
100
105
 
101
106
  try {
102
107
  const sessions = await sessionList(catalystClient, {
@@ -59,21 +59,25 @@ export const getSubcommand = createSubcommand({
59
59
  return result;
60
60
  }
61
61
 
62
- console.log(tui.bold('ID: ') + thread.id);
63
- console.log(tui.bold('Project: ') + thread.project_id);
64
- console.log(tui.bold('Created: ') + new Date(thread.created_at).toLocaleString());
65
- console.log(tui.bold('Updated: ') + new Date(thread.updated_at).toLocaleString());
66
- console.log(tui.bold('Deleted: ') + (thread.deleted ? 'Yes' : 'No'));
62
+ const tableData: Record<string, string> = {
63
+ ID: thread.id,
64
+ Project: thread.project_id,
65
+ Created: new Date(thread.created_at).toLocaleString(),
66
+ Updated: new Date(thread.updated_at).toLocaleString(),
67
+ Deleted: thread.deleted ? 'Yes' : 'No',
68
+ };
67
69
  if (thread.deleted_at) {
68
- console.log(tui.bold('Deleted At: ') + new Date(thread.deleted_at).toLocaleString());
70
+ tableData['Deleted At'] = new Date(thread.deleted_at).toLocaleString();
69
71
  }
70
72
  if (thread.deleted_by) {
71
- console.log(tui.bold('Deleted By: ') + thread.deleted_by);
73
+ tableData['Deleted By'] = thread.deleted_by;
72
74
  }
73
75
  if (thread.user_data) {
74
- console.log(tui.bold('User Data: ') + thread.user_data);
76
+ tableData['User Data'] = thread.user_data;
75
77
  }
76
78
 
79
+ tui.table([tableData], Object.keys(tableData), { layout: 'vertical', padStart: ' ' });
80
+
77
81
  return result;
78
82
  } catch (ex) {
79
83
  if (ex instanceof APIError && ex.status === 404) {
@@ -34,6 +34,10 @@ export const listSubcommand = createSubcommand({
34
34
  command: getCommand('cloud thread list --org-id=org_*'),
35
35
  description: 'Filter by organization',
36
36
  },
37
+ {
38
+ command: getCommand('cloud thread list --all'),
39
+ description: 'List all threads regardless of project context',
40
+ },
37
41
  ],
38
42
  aliases: ['ls'],
39
43
  requires: { auth: true },
@@ -58,6 +62,7 @@ export const listSubcommand = createSubcommand({
58
62
  .describe('Number of threads to list (1–100)'),
59
63
  orgId: z.string().optional().describe('Filter by organization ID'),
60
64
  projectId: z.string().optional().describe('Filter by project ID'),
65
+ all: z.boolean().optional().describe('List all threads regardless of project context'),
61
66
  }),
62
67
  response: ThreadListResponseSchema,
63
68
  },
@@ -65,7 +70,7 @@ export const listSubcommand = createSubcommand({
65
70
  const { logger, auth, project, opts, options, config } = ctx;
66
71
  const catalystClient = await getGlobalCatalystAPIClient(logger, auth, config?.name);
67
72
 
68
- const projectId = opts.projectId || project?.projectId;
73
+ const projectId = opts.all ? undefined : opts.projectId || project?.projectId;
69
74
  const orgId = opts.orgId;
70
75
 
71
76
  try {
@@ -55,6 +55,7 @@ export function createFileWatcher(options: FileWatcherOptions): FileWatcherManag
55
55
  '.idea',
56
56
  '.DS_Store',
57
57
  '.playwright',
58
+ '.bun',
58
59
  'src/generated',
59
60
  ]);
60
61
 
@@ -76,6 +77,7 @@ export function createFileWatcher(options: FileWatcherOptions): FileWatcherManag
76
77
  '.dist',
77
78
  '.vscode',
78
79
  '.idea',
80
+ '.bun',
79
81
  '.DS_Store',
80
82
  '.playwright',
81
83
  'src/web', // Vite handles frontend with HMR - no backend restart needed
@@ -12,9 +12,10 @@ import { APIClient, getAPIBaseURL, getAppBaseURL, getGravityDevModeURL } from '.
12
12
  import { download } from './download';
13
13
  import { createDevmodeSyncService } from './sync';
14
14
  import { getDevmodeDeploymentId } from '../build/ast';
15
- import { getDefaultConfigDir, saveConfig, loadProjectSDKKey } from '../../config';
15
+ import { getDefaultConfigDir, saveConfig, loadProjectSDKKey, getAuth } from '../../config';
16
16
  import type { Config } from '../../types';
17
17
  import { typecheck } from '../build/typecheck';
18
+ import { isTTY, hasLoggedInBefore } from '../../auth';
18
19
  import { createFileWatcher } from './file-watcher';
19
20
  import { regenerateSkillsAsync } from './skills';
20
21
  import { prepareDevLock, releaseLockSync } from './dev-lock';
@@ -171,12 +172,15 @@ export const command = createCommand({
171
172
  .describe('The TCP port to start the dev server (also reads from PORT env)'),
172
173
  }),
173
174
  },
174
- optional: { auth: 'Continue without an account (local only)', project: true },
175
+ optional: { project: true },
175
176
 
176
177
  async handler(ctx) {
177
- const { opts, logger, project, projectDir, auth } = ctx;
178
+ const { opts, logger, project, projectDir } = ctx;
178
179
  let { config } = ctx;
179
180
 
181
+ // Get auth state - we handle auth ourselves based on project state
182
+ let auth = await getAuth();
183
+
180
184
  const rootDir = resolve(projectDir);
181
185
  const appTs = join(rootDir, 'app.ts');
182
186
  const srcDir = join(rootDir, 'src');
@@ -208,6 +212,71 @@ export const command = createCommand({
208
212
  originalExit(1);
209
213
  }
210
214
 
215
+ // Handle authentication state based on project registration
216
+ if (project) {
217
+ // Registered project (has agentuity.json) - check if user needs to login
218
+ const isValidAuth = auth && auth.expires > new Date();
219
+ if (!isValidAuth) {
220
+ if (isTTY()) {
221
+ const hasProfile = await hasLoggedInBefore();
222
+ const message = hasProfile
223
+ ? 'Your session has expired or you are not logged in.'
224
+ : 'This project is registered with Agentuity Cloud but you are not logged in.';
225
+
226
+ tui.warning(message);
227
+ tui.newline();
228
+
229
+ const shouldLogin = await tui.confirm(
230
+ hasProfile ? 'Would you like to login now?' : 'Would you like to login or create an account?',
231
+ true
232
+ );
233
+
234
+ if (shouldLogin) {
235
+ tui.newline();
236
+
237
+ // Run login flow inline
238
+ const { loginCommand } = await import('../auth/login');
239
+
240
+ // Ensure apiClient is available for login handler
241
+ const loginCtx = ctx as unknown as Record<string, unknown>;
242
+ if (!loginCtx.apiClient) {
243
+ loginCtx.apiClient = new APIClient(getAPIBaseURL(config), logger, config);
244
+ }
245
+
246
+ if (loginCommand.handler) {
247
+ await loginCommand.handler(
248
+ loginCtx as Parameters<NonNullable<typeof loginCommand.handler>>[0]
249
+ );
250
+ }
251
+
252
+ // Refresh auth state after login
253
+ const freshAuth = await getAuth();
254
+ if (!freshAuth || freshAuth.expires <= new Date()) {
255
+ tui.fatal('Login was not completed successfully.', ErrorCode.AUTH_FAILED);
256
+ }
257
+ auth = freshAuth;
258
+ tui.newline();
259
+ tui.success('Login successful! Continuing with dev server...');
260
+ tui.newline();
261
+ } else {
262
+ // User chose not to login - show warning about disabled features
263
+ tui.newline();
264
+ tui.showLoggedOutMessage(getAppBaseURL(config), hasProfile);
265
+ }
266
+ } else {
267
+ // Non-TTY: fatal error with instruction
268
+ logger.fatal(
269
+ `Authentication required for this project.\n` +
270
+ `Run "${getCommand('auth login')}" to login to Agentuity`,
271
+ ErrorCode.AUTH_REQUIRED
272
+ );
273
+ }
274
+ }
275
+ } else {
276
+ // No agentuity.json - local-only mode, ignore auth state
277
+ tui.showLocalOnlyWarning();
278
+ }
279
+
211
280
  // Prepare dev lock: cleans up stale processes from previous sessions
212
281
  // and creates a new lockfile for this session
213
282
  const devLock = await prepareDevLock(rootDir, opts.port, logger);
@@ -229,7 +298,10 @@ export const command = createCommand({
229
298
  try {
230
299
  // Setup devmode and gravity (if using public URL)
231
300
  const useMockService = process.env.DEVMODE_SYNC_SERVICE_MOCK === 'true';
232
- const apiClient = auth ? new APIClient(getAPIBaseURL(config), logger, config) : null;
301
+ // Create apiClient with fresh auth API key (important after inline login)
302
+ const apiClient = auth
303
+ ? new APIClient(getAPIBaseURL(config), logger, auth.apiKey, config)
304
+ : null;
233
305
  const syncService = apiClient
234
306
  ? createDevmodeSyncService({
235
307
  logger,
@@ -262,7 +262,7 @@ export async function detectOrmSetup(projectDir: string): Promise<OrmSetup> {
262
262
  * @returns SQL DDL statements for auth tables
263
263
  */
264
264
  export async function generateAuthSchemaSql(logger: Logger, projectDir: string): Promise<string> {
265
- const schemaPath = path.join(projectDir, 'node_modules/@agentuity/auth/src/schema.ts');
265
+ const schemaPath = path.join(projectDir, 'node_modules/@agentuity/auth/dist/schema.js');
266
266
 
267
267
  if (!(await Bun.file(schemaPath).exists())) {
268
268
  throw new Error(
@@ -342,11 +342,25 @@ export async function runCreateFlow(options: CreateFlowOptions): Promise<void> {
342
342
  }
343
343
  switch (choices.db_action) {
344
344
  case 'Create New': {
345
+ const dbName = await prompt.text({
346
+ message: 'Database name',
347
+ hint: 'Optional - press Enter to auto-generate',
348
+ });
349
+ const dbDescription = await prompt.text({
350
+ message: 'Database description',
351
+ hint: 'Optional - press Enter to skip',
352
+ });
345
353
  const created = await tui.spinner({
346
354
  message: 'Provisioning New SQL Database',
347
355
  clearOnSuccess: true,
348
356
  callback: async () => {
349
- return createResources(catalystClient, orgId, region!, [{ type: 'db' }]);
357
+ return createResources(catalystClient, orgId, region!, [
358
+ {
359
+ type: 'db',
360
+ name: dbName || undefined,
361
+ description: dbDescription || undefined,
362
+ },
363
+ ]);
350
364
  },
351
365
  });
352
366
  // Collect env vars from newly created resource
@@ -1,9 +1,12 @@
1
1
  import { z } from 'zod';
2
2
  import { createCommand } from '../../types';
3
+ import { hasLoggedInBefore } from '../../auth';
3
4
  import { showBanner } from '../../banner';
4
5
  import * as tui from '../../tui';
5
6
  import { getCommand } from '../../command-prefix';
6
7
 
8
+ const validateToken = /[\d]{7,}\.[\w-_.]{22}/;
9
+
7
10
  export const command = createCommand({
8
11
  name: 'setup',
9
12
  description: 'Display first-run setup information (internal use)',
@@ -11,22 +14,57 @@ export const command = createCommand({
11
14
  skipUpgradeCheck: true,
12
15
  skipSkill: true,
13
16
  tags: ['read-only', 'fast'],
14
- optional: { auth: true },
15
17
  schema: {
16
18
  options: z.object({
17
19
  nonInteractive: z.boolean().optional().describe('Run in non-interactive mode'),
20
+ setupToken: z.string().optional().describe('Use a one-time use setup token'),
18
21
  }),
19
22
  },
20
23
 
21
24
  async handler(ctx) {
22
- const { opts, auth } = ctx;
25
+ const { opts } = ctx;
23
26
  const _nonInteractive = opts.nonInteractive ?? false;
24
27
 
28
+ // validate the one time setup token if provided
29
+ if (opts?.setupToken && opts.setupToken !== '-' && validateToken.test(opts.setupToken)) {
30
+ const [hours] = opts.setupToken.split('.');
31
+ const tokenInterval = Number(hours);
32
+ if (!Number.isNaN(tokenInterval)) {
33
+ const now = Math.round(Date.now() / (60_000 * 5));
34
+ if (tokenInterval === now || tokenInterval === now - 1) {
35
+ const ok = await tui.spinner({
36
+ message: 'Validating your identity',
37
+ clearOnSuccess: true,
38
+ callback: async () => {
39
+ const newargs = process.argv.map((x) => (x === 'setup' ? 'login' : x));
40
+ const r = Bun.spawn({
41
+ cmd: newargs.concat('--json'),
42
+ stdout: 'pipe',
43
+ stderr: 'inherit',
44
+ });
45
+ await r.exited;
46
+ try {
47
+ const res = JSON.parse(await r.stdout.text()) as { success: boolean };
48
+ return res.success;
49
+ } catch {
50
+ /* fall through */
51
+ }
52
+ return false;
53
+ },
54
+ });
55
+ if (ok) {
56
+ /* TODO */
57
+ return;
58
+ }
59
+ }
60
+ }
61
+ }
62
+
25
63
  tui.newline();
26
64
  showBanner();
27
65
  tui.newline();
28
66
 
29
- if (!auth?.expires) {
67
+ if (!(await hasLoggedInBefore())) {
30
68
  tui.output(`${tui.muted('To get started, run:')}`);
31
69
  tui.newline();
32
70
  tui.output(