@@ -7,35 +7,18 @@ import { loadLocalAgents } from '../agents/load-agents'
77import { websiteUrl } from '../config'
88import { getUserCredentials } from '../credentials'
99
10+ import type {
11+ PublishAgentsErrorResponse ,
12+ PublishAgentsResponse ,
13+ } from '@codebuff/common/types/api/agents/publish'
1014import type { DynamicAgentTemplate } from '@codebuff/common/types/dynamic-agent-template'
1115
12- interface PublishResponse {
13- success : boolean
14- agentId ?: string
15- version ?: string
16- message ?: string
17- error ?: string
18- details ?: string
19- statusCode ?: number
20- availablePublishers ?: Array < {
21- id : string
22- name : string
23- ownershipType : 'user' | 'organization'
24- organizationName ?: string
25- } >
26- validationErrors ?: Array < {
27- code : string
28- message : string
29- path : ( string | number ) [ ]
30- } >
31- }
32-
3316/**
3417 * Handle the publish command to upload agent templates to the backend
3518 * @param agentId The id of the agent to publish (required)
3619 * @param publisherId The id of the publisher to use (optional)
3720 */ export async function handlePublish (
38- agentId ? : string ,
21+ agentIds : string [ ] ,
3922 publisherId ?: string ,
4023) : Promise < void > {
4124 const user = getUserCredentials ( )
@@ -45,10 +28,9 @@ interface PublishResponse {
4528 return
4629 }
4730
48- if ( ! agentId ) {
49- console . log ( red ( 'Agent id is required. Usage: publish <agent-id>' ) )
31+ if ( agentIds ?. length === 0 ) {
5032 console . log (
51- yellow ( 'This prevents accidentally publishing all agents at once .') ,
33+ red ( 'Agent id is required. Usage: publish <agent-id> [agent-id2] .. .') ,
5234 )
5335
5436 // Show available agents
@@ -84,126 +66,122 @@ interface PublishResponse {
8466 return
8567 }
8668
87- // Find the specific agent
88- const matchingTemplate = Object . entries ( agentTemplates ) . find (
89- ( [ key , template ] ) =>
90- key === agentId ||
91- template . id === agentId ||
92- template . displayName === agentId ,
93- )
69+ const matchingTemplates : Record < string , DynamicAgentTemplate > = { }
70+ for ( const agentId of agentIds ) {
71+ // Find the specific agent
72+ const matchingTemplate = Object . entries ( agentTemplates ) . find (
73+ ( [ key , template ] ) =>
74+ key === agentId ||
75+ template . id === agentId ||
76+ template . displayName === agentId ,
77+ )
9478
95- if ( ! matchingTemplate ) {
96- console . log ( red ( `Agent "${ agentId } " not found. Available agents:` ) )
97- Object . values ( agentTemplates ) . forEach ( ( template ) => {
98- console . log ( ` - ${ template . displayName } (${ template . id } )` )
99- } )
100- return
79+ if ( ! matchingTemplate ) {
80+ console . log ( red ( `Agent "${ agentId } " not found. Available agents:` ) )
81+ Object . values ( agentTemplates ) . forEach ( ( template ) => {
82+ console . log ( ` - ${ template . displayName } (${ template . id } )` )
83+ } )
84+ return
85+ }
86+
87+ matchingTemplates [ matchingTemplate [ 0 ] ] = matchingTemplate [ 1 ]
88+ }
89+ console . log ( yellow ( `Publishing:` ) )
90+ for ( const [ key , template ] of Object . entries ( matchingTemplates ) ) {
91+ console . log ( ` - ${ template . displayName } (${ template . id } )` )
10192 }
102- const [ key , template ] = matchingTemplate
103- console . log (
104- yellow ( `Publishing ${ template . displayName } (${ template . id } )...` ) ,
105- )
10693
10794 try {
108- const result = await publishAgentTemplate (
109- template ,
95+ const result = await publishAgentTemplates (
96+ Object . values ( matchingTemplates ) ,
11097 user . authToken ! ,
11198 publisherId ,
11299 )
113100
114101 if ( result . success ) {
102+ console . log ( green ( `✅ Successfully published:` ) )
103+ for ( const agent of result . agents ) {
104+ console . log (
105+ cyan (
106+ ` - ${ agent . displayName } (${ result . publisherId } /${ agent . id } @${ agent . version } )` ,
107+ ) ,
108+ )
109+ }
110+ return
111+ }
112+
113+ console . log ( red ( `❌ Failed to publish agents: ${ result . error } ` ) )
114+ if ( result . statusCode !== 403 ) {
115+ return
116+ }
117+
118+ // Check if this is a "no publisher" error vs "multiple publishers" error
119+ if ( result . error ?. includes ( 'No publisher associated with user' ) ) {
120+ console . log ( )
115121 console . log (
116- green (
117- `✅ Successfully published ${ template . displayName } v${ result . version } ` ,
118- ) ,
122+ cyan ( 'Please visit the website to create your publisher profile:' ) ,
119123 )
120- console . log ( cyan ( ` Agent ID: ${ result . agentId } ` ) )
121- } else {
124+ console . log ( yellow ( `${ websiteUrl } /publishers` ) )
125+ console . log ( )
126+ console . log ( 'A publisher profile allows you to:' )
127+ console . log ( ' • Publish and manage your agents' )
128+ console . log ( ' • Build your reputation in the community' )
129+ console . log ( ' • Organize agents under your name or organization' )
130+ console . log ( )
131+ } else if (
132+ result . availablePublishers &&
133+ result . availablePublishers . length > 0
134+ ) {
135+ // Show available publishers
122136 console . log (
123- red ( `❌ Failed to publish ${ template . displayName } : ${ result . error } ` ) ,
137+ cyan (
138+ 'You have access to multiple publishers. Please specify which one to use:' ,
139+ ) ,
124140 )
125- // Check if the error is about missing publisher (403 status)
126- if ( result . statusCode === 403 ) {
127- console . log ( )
128-
129- // Check if this is a "no publisher" error vs "multiple publishers" error
130- if ( result . error ?. includes ( 'No publisher associated with user' ) ) {
131- console . log (
132- cyan (
133- 'Please visit the website to create your publisher profile:' ,
134- ) ,
135- )
136- console . log ( yellow ( `${ websiteUrl } /publishers` ) )
137- console . log ( )
138- console . log ( 'A publisher profile allows you to:' )
139- console . log ( ' • Publish and manage your agents' )
140- console . log ( ' • Build your reputation in the community' )
141- console . log ( ' • Organize agents under your name or organization' )
142- console . log ( )
143- } else if (
144- result . availablePublishers &&
145- result . availablePublishers . length > 0
146- ) {
147- // Show available publishers
148- console . log (
149- cyan (
150- 'You have access to multiple publishers. Please specify which one to use:' ,
151- ) ,
152- )
153- console . log ( )
154- console . log ( cyan ( 'Available publishers:' ) )
155- result . availablePublishers . forEach ( ( publisher ) => {
156- const orgInfo = publisher . organizationName
157- ? ` (${ publisher . organizationName } )`
158- : ''
159- const typeInfo =
160- publisher . ownershipType === 'organization'
161- ? ' [Organization]'
162- : ' [Personal]'
163- console . log (
164- ` • ${ yellow ( publisher . id ) } - ${ publisher . name } ${ orgInfo } ${ typeInfo } ` ,
165- )
166- } )
167- console . log ( )
168- console . log ( 'Run one of these commands:' )
169- result . availablePublishers . forEach ( ( publisher ) => {
170- console . log (
171- yellow (
172- ` codebuff publish ${ agentId } --publisher ${ publisher . id } ` ,
173- ) ,
174- )
175- } )
176- console . log ( )
177- console . log ( cyan ( 'Or visit the website to manage your publishers:' ) )
178- console . log ( yellow ( `${ websiteUrl } /publishers` ) )
179- console . log ( )
180- } else {
181- // Generic 403 error
182- console . log ( cyan ( 'You may need to specify which publisher to use.' ) )
183- console . log ( )
184- console . log ( 'Try running:' )
185- console . log (
186- yellow ( ` publish ${ agentId } --publisher <publisher-id>` ) ,
187- )
188- console . log ( )
189- console . log (
190- cyan ( 'Visit the website to see your available publishers:' ) ,
191- )
192- console . log ( yellow ( `${ websiteUrl } /publishers` ) )
193- console . log ( )
194- }
195- } else {
141+ console . log ( )
142+ console . log ( cyan ( 'Available publishers:' ) )
143+ result . availablePublishers . forEach ( ( publisher ) => {
144+ const orgInfo = publisher . organizationName
145+ ? ` (${ publisher . organizationName } )`
146+ : ''
147+ const typeInfo =
148+ publisher . ownershipType === 'organization'
149+ ? ' [Organization]'
150+ : ' [Personal]'
196151 console . log (
197- red (
198- `❌ Failed to publish ${ template . displayName } : ${ result . error } ` ,
152+ ` • ${ yellow ( publisher . id ) } - ${ publisher . name } ${ orgInfo } ${ typeInfo } ` ,
153+ )
154+ } )
155+ console . log ( )
156+ console . log ( 'Run one of these commands:' )
157+ result . availablePublishers . forEach ( ( publisher ) => {
158+ console . log (
159+ yellow (
160+ ` codebuff publish ${ agentIds . join ( ' ' ) } --publisher ${ publisher . id } ` ,
199161 ) ,
200162 )
201- }
163+ } )
164+ console . log ( )
165+ console . log ( cyan ( 'Or visit the website to manage your publishers:' ) )
166+ console . log ( yellow ( `${ websiteUrl } /publishers` ) )
167+ console . log ( )
168+ } else {
169+ // Generic 403 error
170+ console . log ( cyan ( 'You may need to specify which publisher to use.' ) )
171+ console . log ( )
172+ console . log ( 'Try running:' )
173+ console . log (
174+ yellow ( ` publish ${ agentIds . join ( ' ' ) } --publisher <publisher-id>` ) ,
175+ )
176+ console . log ( )
177+ console . log ( cyan ( 'Visit the website to see your available publishers:' ) )
178+ console . log ( yellow ( `${ websiteUrl } /publishers` ) )
179+ console . log ( )
202180 }
203181 } catch ( error ) {
204182 console . log (
205183 red (
206- `❌ Error publishing ${ template . displayName } : ${ error instanceof Error ? error . message : String ( error ) } ` ,
184+ `❌ Error publishing agents : ${ error instanceof Error ? error . message : String ( error ) } ` ,
207185 ) ,
208186 )
209187 // Avoid logger.error here as it can cause sonic boom errors that mask the real error
@@ -221,13 +199,13 @@ interface PublishResponse {
221199}
222200
223201/**
224- * Publish an agent template to the backend
202+ * Publish agent templates to the backend
225203 */
226- async function publishAgentTemplate (
227- data : DynamicAgentTemplate ,
204+ async function publishAgentTemplates (
205+ data : DynamicAgentTemplate [ ] ,
228206 authToken : string ,
229207 publisherId ?: string ,
230- ) : Promise < PublishResponse > {
208+ ) : Promise < PublishAgentsResponse & { statusCode ?: number } > {
231209 try {
232210 const response = await fetch ( `${ websiteUrl } /api/agents/publish` , {
233211 method : 'POST' ,
@@ -241,17 +219,19 @@ async function publishAgentTemplate(
241219 } ) ,
242220 } )
243221
244- let result : any
222+ let result : PublishAgentsResponse
245223 try {
246224 result = await response . json ( )
247225 } catch ( jsonError ) {
248226 return {
249227 success : false ,
250228 error : `Failed to parse server response: ${ response . status } ${ response . statusText } ` ,
229+ statusCode : response . status ,
251230 }
252231 }
253232
254233 if ( ! response . ok ) {
234+ result = result as PublishAgentsErrorResponse
255235 // Extract detailed error information from the response
256236 let errorMessage =
257237 result . error || `HTTP ${ response . status } : ${ response . statusText } `
@@ -284,10 +264,8 @@ async function publishAgentTemplate(
284264 }
285265
286266 return {
287- success : true ,
288- agentId : result . agent ?. id ,
289- version : result . agent ?. version ,
290- message : result . message ,
267+ ...result ,
268+ statusCode : response . status ,
291269 }
292270 } catch ( error ) {
293271 // Handle network errors, timeouts, etc.
0 commit comments