@agentuity/cli 0.1.11 → 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 (114) 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.map +1 -1
  44. package/dist/cmd/cloud/sandbox/snapshot/build.js +39 -23
  45. package/dist/cmd/cloud/sandbox/snapshot/build.js.map +1 -1
  46. package/dist/cmd/cloud/sandbox/snapshot/generate.d.ts.map +1 -1
  47. package/dist/cmd/cloud/sandbox/snapshot/generate.js +2 -0
  48. package/dist/cmd/cloud/sandbox/snapshot/generate.js.map +1 -1
  49. package/dist/cmd/cloud/sandbox/snapshot/get.d.ts.map +1 -1
  50. package/dist/cmd/cloud/sandbox/snapshot/get.js +10 -7
  51. package/dist/cmd/cloud/sandbox/snapshot/get.js.map +1 -1
  52. package/dist/cmd/cloud/session/get.d.ts.map +1 -1
  53. package/dist/cmd/cloud/session/get.js +24 -23
  54. package/dist/cmd/cloud/session/get.js.map +1 -1
  55. package/dist/cmd/cloud/session/list.d.ts.map +1 -1
  56. package/dist/cmd/cloud/session/list.js +7 -2
  57. package/dist/cmd/cloud/session/list.js.map +1 -1
  58. package/dist/cmd/cloud/thread/get.d.ts.map +1 -1
  59. package/dist/cmd/cloud/thread/get.js +11 -8
  60. package/dist/cmd/cloud/thread/get.js.map +1 -1
  61. package/dist/cmd/cloud/thread/list.d.ts.map +1 -1
  62. package/dist/cmd/cloud/thread/list.js +6 -1
  63. package/dist/cmd/cloud/thread/list.js.map +1 -1
  64. package/dist/cmd/dev/file-watcher.d.ts.map +1 -1
  65. package/dist/cmd/dev/file-watcher.js +2 -0
  66. package/dist/cmd/dev/file-watcher.js.map +1 -1
  67. package/dist/cmd/dev/index.d.ts.map +1 -1
  68. package/dist/cmd/dev/index.js +62 -4
  69. package/dist/cmd/dev/index.js.map +1 -1
  70. package/dist/cmd/project/template-flow.d.ts.map +1 -1
  71. package/dist/cmd/project/template-flow.js +15 -1
  72. package/dist/cmd/project/template-flow.js.map +1 -1
  73. package/dist/cmd/setup/index.d.ts.map +1 -1
  74. package/dist/cmd/setup/index.js +40 -3
  75. package/dist/cmd/setup/index.js.map +1 -1
  76. package/dist/config.d.ts.map +1 -1
  77. package/dist/config.js +3 -2
  78. package/dist/config.js.map +1 -1
  79. package/dist/steps.d.ts.map +1 -1
  80. package/dist/steps.js +4 -2
  81. package/dist/steps.js.map +1 -1
  82. package/dist/tui.d.ts +11 -1
  83. package/dist/tui.d.ts.map +1 -1
  84. package/dist/tui.js +47 -12
  85. package/dist/tui.js.map +1 -1
  86. package/package.json +6 -6
  87. package/src/auth.ts +12 -11
  88. package/src/cli.ts +121 -43
  89. package/src/cmd/auth/login.ts +57 -5
  90. package/src/cmd/build/patch/_util.ts +6 -2
  91. package/src/cmd/cloud/agent/get.ts +14 -6
  92. package/src/cmd/cloud/db/create.ts +15 -3
  93. package/src/cmd/cloud/db/get.ts +14 -3
  94. package/src/cmd/cloud/db/list.ts +16 -26
  95. package/src/cmd/cloud/deployment/show.ts +53 -47
  96. package/src/cmd/cloud/sandbox/create.ts +20 -2
  97. package/src/cmd/cloud/sandbox/execution/get.ts +16 -13
  98. package/src/cmd/cloud/sandbox/get.ts +48 -38
  99. package/src/cmd/cloud/sandbox/list.ts +6 -1
  100. package/src/cmd/cloud/sandbox/run.ts +5 -1
  101. package/src/cmd/cloud/sandbox/snapshot/build.ts +51 -24
  102. package/src/cmd/cloud/sandbox/snapshot/generate.ts +2 -0
  103. package/src/cmd/cloud/sandbox/snapshot/get.ts +11 -7
  104. package/src/cmd/cloud/session/get.ts +25 -29
  105. package/src/cmd/cloud/session/list.ts +7 -2
  106. package/src/cmd/cloud/thread/get.ts +12 -8
  107. package/src/cmd/cloud/thread/list.ts +6 -1
  108. package/src/cmd/dev/file-watcher.ts +2 -0
  109. package/src/cmd/dev/index.ts +76 -4
  110. package/src/cmd/project/template-flow.ts +15 -1
  111. package/src/cmd/setup/index.ts +41 -3
  112. package/src/config.ts +3 -2
  113. package/src/steps.ts +4 -2
  114. package/src/tui.ts +61 -12
@@ -29,6 +29,7 @@ const SandboxGetResponseSchema = z.object({
29
29
  dependencies: z.array(z.string()).optional().describe('Apt packages installed'),
30
30
  metadata: z.record(z.string(), z.unknown()).optional().describe('User-defined metadata'),
31
31
  resources: SandboxResourcesSchema.optional().describe('Resource limits'),
32
+ url: z.string().optional().describe('Public URL for the sandbox (if network port configured)'),
32
33
  });
33
34
 
34
35
  export const getSubcommand = createCommand({
@@ -72,57 +73,65 @@ export const getSubcommand = createCommand({
72
73
  ? tui.colorError
73
74
  : tui.colorMuted;
74
75
 
75
- console.log(`${tui.muted('Sandbox:')} ${tui.bold(result.sandboxId)}`);
76
- if (result.name) {
77
- console.log(`${tui.muted('Name:')} ${result.name}`);
78
- }
79
- if (result.description) {
80
- console.log(`${tui.muted('Description:')} ${result.description}`);
81
- }
82
- console.log(`${tui.muted('Status:')} ${statusColor(result.status)}`);
83
- console.log(`${tui.muted('Created:')} ${result.createdAt}`);
84
- if (result.runtimeName) {
85
- console.log(`${tui.muted('Runtime:')} ${result.runtimeName}`);
86
- }
87
- if (result.region) {
88
- console.log(`${tui.muted('Region:')} ${result.region}`);
89
- }
90
- if (result.snapshotId || result.snapshotTag) {
91
- const snapshotDisplay = result.snapshotTag
92
- ? `${result.snapshotTag} ${tui.muted('(' + result.snapshotId + ')')}`
93
- : result.snapshotId;
94
- console.log(`${tui.muted('Snapshot:')} ${snapshotDisplay}`);
95
- }
96
- console.log(`${tui.muted('Executions:')} ${result.executions}`);
76
+ const snapshotDisplay =
77
+ result.snapshotId || result.snapshotTag
78
+ ? result.snapshotTag
79
+ ? result.snapshotId
80
+ ? `${result.snapshotTag} ${tui.muted('(' + result.snapshotId + ')')}`
81
+ : result.snapshotTag
82
+ : result.snapshotId
83
+ : undefined;
84
+
85
+ let streamDisplay: string | undefined;
97
86
  if (
98
87
  result.stdoutStreamUrl &&
99
88
  result.stderrStreamUrl &&
100
89
  result.stdoutStreamUrl === result.stderrStreamUrl
101
90
  ) {
102
- console.log(`${tui.muted('Stream:')} ${tui.link(result.stdoutStreamUrl)}`);
103
- } else {
104
- if (result.stdoutStreamUrl) {
105
- console.log(`${tui.muted('Stream (stdout):')} ${tui.link(result.stdoutStreamUrl)}`);
106
- }
107
- if (result.stderrStreamUrl) {
108
- console.log(`${tui.muted('Stream (stderr):')} ${tui.link(result.stderrStreamUrl)}`);
109
- }
110
- }
111
- if (result.dependencies && result.dependencies.length > 0) {
112
- console.log(`${tui.muted('Dependencies:')} ${result.dependencies.join(', ')}`);
91
+ streamDisplay = tui.link(result.stdoutStreamUrl);
113
92
  }
93
+
94
+ const resourceParts: string[] = [];
114
95
  if (result.resources) {
115
- const resourceParts: string[] = [];
116
96
  if (result.resources.memory) resourceParts.push(`memory=${result.resources.memory}`);
117
97
  if (result.resources.cpu) resourceParts.push(`cpu=${result.resources.cpu}`);
118
98
  if (result.resources.disk) resourceParts.push(`disk=${result.resources.disk}`);
119
- if (resourceParts.length > 0) {
120
- console.log(`${tui.muted('Resources:')} ${resourceParts.join(', ')}`);
121
- }
99
+ }
100
+
101
+ const tableData: Record<string, string | number> = {
102
+ Sandbox: tui.bold(result.sandboxId),
103
+ };
104
+
105
+ if (result.name) tableData['Name'] = result.name;
106
+ if (result.description) tableData['Description'] = result.description;
107
+ tableData['Status'] = statusColor(result.status);
108
+ tableData['Created'] = result.createdAt;
109
+ if (result.runtimeName) tableData['Runtime'] = result.runtimeName;
110
+ if (result.region) tableData['Region'] = result.region;
111
+ if (snapshotDisplay) tableData['Snapshot'] = snapshotDisplay;
112
+ tableData['Executions'] = result.executions;
113
+ if (streamDisplay) {
114
+ tableData['Stream'] = streamDisplay;
115
+ } else {
116
+ if (result.stdoutStreamUrl)
117
+ tableData['Stream (stdout)'] = tui.link(result.stdoutStreamUrl);
118
+ if (result.stderrStreamUrl)
119
+ tableData['Stream (stderr)'] = tui.link(result.stderrStreamUrl);
120
+ }
121
+ if (result.dependencies && result.dependencies.length > 0) {
122
+ tableData['Dependencies'] = result.dependencies.join(', ');
123
+ }
124
+ if (resourceParts.length > 0) {
125
+ tableData['Resources'] = resourceParts.join(', ');
122
126
  }
123
127
  if (result.metadata && Object.keys(result.metadata).length > 0) {
124
- console.log(`${tui.muted('Metadata:')} ${JSON.stringify(result.metadata)}`);
128
+ tableData['Metadata'] = JSON.stringify(result.metadata);
125
129
  }
130
+ if (result.url) {
131
+ tableData['URL'] = tui.link(result.url);
132
+ }
133
+
134
+ tui.table([tableData], Object.keys(tableData), { layout: 'vertical', padStart: ' ' });
126
135
  }
127
136
 
128
137
  return {
@@ -142,6 +151,7 @@ export const getSubcommand = createCommand({
142
151
  dependencies: result.dependencies,
143
152
  metadata: result.metadata,
144
153
  resources: result.resources,
154
+ url: result.url,
145
155
  };
146
156
  },
147
157
  });
@@ -59,6 +59,10 @@ export const listSubcommand = createCommand({
59
59
  command: getCommand('cloud sandbox list --limit 10 --offset 20'),
60
60
  description: 'List with pagination',
61
61
  },
62
+ {
63
+ command: getCommand('cloud sandbox list --all'),
64
+ description: 'List all sandboxes regardless of project context',
65
+ },
62
66
  ],
63
67
  schema: {
64
68
  options: z.object({
@@ -67,6 +71,7 @@ export const listSubcommand = createCommand({
67
71
  .optional()
68
72
  .describe('Filter by status'),
69
73
  projectId: z.string().optional().describe('Filter by project ID'),
74
+ all: z.boolean().optional().describe('List all sandboxes regardless of project context'),
70
75
  limit: z.number().optional().describe('Maximum number of results (default: 50, max: 100)'),
71
76
  offset: z.number().optional().describe('Pagination offset'),
72
77
  }),
@@ -77,7 +82,7 @@ export const listSubcommand = createCommand({
77
82
  const { opts, options, auth, project, logger, orgId, config } = ctx;
78
83
  const client = await getGlobalCatalystAPIClient(logger, auth, config?.name);
79
84
 
80
- const projectId = opts.projectId || project?.projectId;
85
+ const projectId = opts.all ? undefined : opts.projectId || project?.projectId;
81
86
 
82
87
  const result = await sandboxList(client, {
83
88
  orgId,
@@ -20,6 +20,7 @@ export const runSubcommand = createCommand({
20
20
  description: 'Run a one-shot command in a sandbox (creates, executes, destroys)',
21
21
  tags: ['slow', 'requires-auth'],
22
22
  requires: { auth: true, region: true, org: true },
23
+ optional: { project: true },
23
24
  examples: [
24
25
  {
25
26
  command: getCommand('cloud sandbox run -- echo "hello world"'),
@@ -63,12 +64,14 @@ export const runSubcommand = createCommand({
63
64
  .array(z.string())
64
65
  .optional()
65
66
  .describe('Apt packages to install (can be specified multiple times)'),
67
+ projectId: z.string().optional().describe('Project ID to associate this sandbox with'),
66
68
  }),
67
69
  response: SandboxRunResponseSchema,
68
70
  },
69
71
 
70
72
  async handler(ctx) {
71
- const { args, opts, options, auth, region, config, logger, orgId } = ctx;
73
+ const { args, opts, options, auth, region, config, logger, orgId, project } = ctx;
74
+ const projectId = opts.projectId || project?.projectId;
72
75
  const client = createSandboxClient(logger, auth, region);
73
76
  const started = Date.now();
74
77
 
@@ -151,6 +154,7 @@ export const runSubcommand = createCommand({
151
154
  try {
152
155
  const result = await sandboxRun(client, {
153
156
  options: {
157
+ projectId,
154
158
  runtime: opts.runtime,
155
159
  runtimeId: opts.runtimeId,
156
160
  name: opts.name,
@@ -190,7 +190,18 @@ async function resolveFileGlobs(
190
190
  }
191
191
  }
192
192
 
193
- for (const pattern of exclusions) {
193
+ for (let pattern of exclusions) {
194
+ // If the pattern refers to a directory, auto-append /** to exclude all contents
195
+ const patternPath = join(directory, pattern);
196
+ try {
197
+ const stat = statSync(patternPath);
198
+ if (stat.isDirectory()) {
199
+ pattern = pattern.endsWith('/') ? `${pattern}**` : `${pattern}/**`;
200
+ }
201
+ } catch {
202
+ // Path doesn't exist or can't be stat'd, use pattern as-is
203
+ }
204
+
194
205
  const glob = new Bun.Glob(pattern);
195
206
  for await (const file of glob.scan({ cwd: directory, dot: true })) {
196
207
  files.delete(file);
@@ -473,16 +484,20 @@ export const buildSubcommand = createCommand({
473
484
  if (!options.json) {
474
485
  tui.info(`${tui.bold('Dry Run')} - No upload will be performed`);
475
486
  console.log('');
476
- if (finalName) {
477
- console.log(` ${tui.muted('Name:'.padEnd(13))} ${finalName}`);
478
- }
479
- console.log(` ${tui.muted('Runtime:'.padEnd(13))} ${buildConfig.runtime}`);
480
- console.log(` ${tui.muted('Tag:'.padEnd(13))} ${opts.tag ?? 'latest'}`);
481
- if (finalDescription) {
482
- console.log(` ${tui.muted('Description:'.padEnd(13))} ${finalDescription}`);
483
- }
484
- console.log(` ${tui.muted('Size:'.padEnd(13))} ${tui.formatBytes(totalSize)}`);
485
- console.log(` ${tui.muted('Files:'.padEnd(13))} ${fileList.length}`);
487
+ tui.table(
488
+ [
489
+ {
490
+ Name: finalName,
491
+ Description: finalDescription ?? '-',
492
+ Runtime: buildConfig.runtime,
493
+ Tag: opts.tag ?? 'latest',
494
+ Size: tui.formatBytes(totalSize),
495
+ Files: fileList.length.toFixed(),
496
+ },
497
+ ],
498
+ ['Name', 'Description', 'Runtime', 'Tag', 'Size', 'Files'],
499
+ { layout: 'vertical', padStart: ' ' }
500
+ );
486
501
 
487
502
  if (buildConfig.dependencies && buildConfig.dependencies.length > 0) {
488
503
  console.log('');
@@ -569,9 +584,16 @@ export const buildSubcommand = createCommand({
569
584
  if (!options.json) {
570
585
  tui.success(`Snapshot unchanged ${tui.bold(initResult.existingId!)}`);
571
586
  console.log('');
572
- console.log(` ${tui.muted('Name:'.padEnd(13))} ${initResult.existingName}`);
573
- console.log(` ${tui.muted('Tag:'.padEnd(13))} ${initResult.existingTag ?? 'latest'}`);
574
- console.log(` ${tui.muted('Runtime:'.padEnd(13))} ${buildConfig.runtime}`);
587
+ tui.table(
588
+ [
589
+ {
590
+ Name: finalName,
591
+ Tag: opts.tag ?? 'latest',
592
+ },
593
+ ],
594
+ ['Name', 'Tag'],
595
+ { layout: 'vertical', padStart: ' ' }
596
+ );
575
597
  }
576
598
 
577
599
  return {
@@ -630,17 +652,22 @@ export const buildSubcommand = createCommand({
630
652
  if (!options.json) {
631
653
  tui.success(`Created snapshot ${tui.bold(snapshot.snapshotId)}`);
632
654
  console.log('');
633
- console.log(` ${tui.muted('Name:'.padEnd(13))} ${snapshot.name}`);
634
- console.log(` ${tui.muted('Tag:'.padEnd(13))} ${snapshot.tag ?? 'latest'}`);
635
- console.log(` ${tui.muted('Runtime:'.padEnd(13))} ${buildConfig.runtime}`);
636
- if (snapshot.description) {
637
- console.log(` ${tui.muted('Description:'.padEnd(13))} ${snapshot.description}`);
638
- }
639
- console.log(
640
- ` ${tui.muted('Size:'.padEnd(13))} ${tui.formatBytes(snapshot.sizeBytes)}`
655
+
656
+ tui.table(
657
+ [
658
+ {
659
+ Name: snapshot.name,
660
+ Description: snapshot.description ?? '-',
661
+ Runtime: buildConfig.runtime,
662
+ Tag: snapshot.tag ?? 'latest',
663
+ Size: tui.formatBytes(snapshot.sizeBytes),
664
+ Files: snapshot.fileCount.toFixed(),
665
+ Created: snapshot.createdAt,
666
+ },
667
+ ],
668
+ ['Name', 'Description', 'Runtime', 'Tag', 'Size', 'Files', 'Created'],
669
+ { layout: 'vertical', padStart: ' ' }
641
670
  );
642
- console.log(` ${tui.muted('Files:'.padEnd(13))} ${snapshot.fileCount}`);
643
- console.log(` ${tui.muted('Created:'.padEnd(13))} ${snapshot.createdAt}`);
644
671
 
645
672
  if (buildConfig.dependencies && buildConfig.dependencies.length > 0) {
646
673
  console.log('');
@@ -88,6 +88,8 @@ export const generateSubcommand = createCommand({
88
88
  description: 'Generate a template snapshot build file',
89
89
  tags: [],
90
90
  requires: {},
91
+ skipUpgradeCheck: true,
92
+ skipSkill: true,
91
93
  examples: [
92
94
  {
93
95
  command: getCommand('cloud sandbox snapshot generate'),
@@ -78,18 +78,22 @@ export const getSubcommand = createCommand({
78
78
  );
79
79
 
80
80
  if (!options.json) {
81
- tui.info(`Snapshot: ${tui.bold(snapshot.snapshotId)}`);
82
- console.log(` ${tui.muted('Name:')} ${snapshot.name}`);
81
+ const tableData: Record<string, string | number> = {
82
+ Snapshot: tui.bold(snapshot.snapshotId),
83
+ Name: snapshot.name,
84
+ };
83
85
  if (snapshot.tag) {
84
- console.log(` ${tui.muted('Tag:')} ${snapshot.tag}`);
86
+ tableData['Tag'] = snapshot.tag;
85
87
  }
86
- console.log(` ${tui.muted('Size:')} ${tui.formatBytes(snapshot.sizeBytes)}`);
87
- console.log(` ${tui.muted('Files:')} ${snapshot.fileCount}`);
88
- console.log(` ${tui.muted('Created:')} ${snapshot.createdAt}`);
88
+ tableData['Size'] = tui.formatBytes(snapshot.sizeBytes);
89
+ tableData['Files'] = snapshot.fileCount;
90
+ tableData['Created'] = snapshot.createdAt;
89
91
  if (snapshot.parentSnapshotId) {
90
- console.log(` ${tui.muted('Parent:')} ${snapshot.parentSnapshotId}`);
92
+ tableData['Parent'] = snapshot.parentSnapshotId;
91
93
  }
92
94
 
95
+ tui.table([tableData], Object.keys(tableData), { layout: 'vertical', padStart: ' ' });
96
+
93
97
  if (
94
98
  snapshot.userMetadata &&
95
99
  typeof snapshot.userMetadata === 'object' &&
@@ -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,