nex-framework-cli 1.0.7 → 1.0.10

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.
package/cli/nex-cli.js CHANGED
@@ -28,10 +28,10 @@ program.configureHelp({
28
28
  program
29
29
  .name('nex')
30
30
  .description('NEX Framework - Framework completo de agentes AI')
31
- .version('1.0.6', '-v, --version', 'Mostra a versão')
31
+ .version('1.0.9', '-v, --version', 'Mostra a versão')
32
32
  .addHelpText('before', chalk.bold.cyan(`
33
33
  ╔════════════════════════════════════════════════════════════════╗
34
- ║ NEX Framework - CLI v1.0.6
34
+ ║ NEX Framework - CLI v1.0.9
35
35
  ║ Framework completo de agentes AI ║
36
36
  ╚════════════════════════════════════════════════════════════════╝
37
37
  `))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nex-framework-cli",
3
- "version": "1.0.7",
3
+ "version": "1.0.10",
4
4
  "description": "NEX CLI - Command-line interface for NEX Framework and Agent Marketplace",
5
5
  "type": "module",
6
6
  "main": "cli/nex-cli.js",
@@ -102,28 +102,46 @@ export default class NEXMarketplace {
102
102
 
103
103
  try {
104
104
  let results = []
105
+ let apiError = null
105
106
 
106
- // Search in Supabase if available
107
- if (this.supabase) {
108
- results = await this.searchRemote(query, options)
107
+ // Try remote search first
108
+ if (this.apiUrl || this.supabase) {
109
+ try {
110
+ results = await this.searchRemote(query, options)
111
+ } catch (error) {
112
+ apiError = error
113
+ // Continue with local search only
114
+ console.warn(chalk.yellow(`\n⚠️ Could not connect to marketplace: ${error.message}`))
115
+ console.log(chalk.gray(' Searching local registry only...\n'))
116
+ }
109
117
  }
110
118
 
111
- // Also search local registry
119
+ // Always search local registry
112
120
  const localResults = await this.searchLocal(query, options)
113
121
 
114
122
  // Merge results (deduplicate by agent_id)
115
123
  const merged = this.mergeResults(results, localResults)
116
124
 
117
- spinner.succeed(`Found ${merged.length} agents`)
125
+ if (merged.length > 0) {
126
+ spinner.succeed(`Found ${merged.length} agents`)
127
+ } else {
128
+ spinner.warn('No agents found')
129
+ }
118
130
 
119
131
  // Display results
120
132
  this.displaySearchResults(merged)
121
133
 
134
+ // Show warning if API failed
135
+ if (apiError && localResults.length > 0) {
136
+ console.log(chalk.yellow('\n💡 Tip: Some results may be missing. Check your connection.\n'))
137
+ }
138
+
122
139
  return merged
123
140
 
124
141
  } catch (error) {
125
142
  spinner.fail('Search failed')
126
- console.error(chalk.red(`Error: ${error.message}`))
143
+ console.error(chalk.red(`\nError: ${error.message}`))
144
+ console.log(chalk.gray('\n💡 Try searching locally or check your connection.\n'))
127
145
  throw error
128
146
  }
129
147
  }
@@ -149,20 +167,38 @@ export default class NEXMarketplace {
149
167
  * Search via Edge Function API (SEGURO - sem anon key)
150
168
  */
151
169
  async searchViaAPI(query, options = {}) {
152
- const params = new URLSearchParams()
153
- if (query) params.append('q', query)
154
- if (options.category) params.append('category', options.category)
155
- if (options.official) params.append('official', 'true')
156
- if (options.limit) params.append('limit', options.limit.toString())
157
-
158
- const response = await fetch(`${this.apiUrl}/search?${params}`)
159
-
160
- if (!response.ok) {
161
- throw new Error(`API error: ${response.statusText}`)
170
+ try {
171
+ const params = new URLSearchParams()
172
+ if (query) params.append('q', query)
173
+ if (options.category) params.append('category', options.category)
174
+ if (options.official) params.append('official', 'true')
175
+ if (options.limit) params.append('limit', options.limit.toString())
176
+
177
+ // Timeout de 10 segundos
178
+ const controller = new AbortController()
179
+ const timeoutId = setTimeout(() => controller.abort(), 10000)
180
+
181
+ const response = await fetch(`${this.apiUrl}/search?${params}`, {
182
+ signal: controller.signal
183
+ })
184
+
185
+ clearTimeout(timeoutId)
186
+
187
+ if (!response.ok) {
188
+ throw new Error(`API error: ${response.statusText}`)
189
+ }
190
+
191
+ const { agents } = await response.json()
192
+ return agents || []
193
+ } catch (error) {
194
+ if (error.name === 'AbortError') {
195
+ throw new Error('Connection timeout. The marketplace API is not responding.')
196
+ }
197
+ if (error.code === 'UND_ERR_CONNECT_TIMEOUT' || error.message.includes('timeout')) {
198
+ throw new Error('Connection timeout. Please check your internet connection or try again later.')
199
+ }
200
+ throw error
162
201
  }
163
-
164
- const { agents } = await response.json()
165
- return agents || []
166
202
  }
167
203
 
168
204
  /**
@@ -173,30 +209,77 @@ export default class NEXMarketplace {
173
209
 
174
210
  try {
175
211
  let results = []
212
+ let apiError = null
176
213
 
177
- // List from API/Remote if available
214
+ // Try API/Remote first
178
215
  if (this.apiUrl) {
179
- results = await this.listAllViaAPI(options)
216
+ try {
217
+ results = await this.listAllViaAPI(options)
218
+ } catch (error) {
219
+ apiError = error
220
+ console.warn(chalk.yellow(`\n⚠️ Could not connect to marketplace API: ${error.message}`))
221
+ console.log(chalk.gray(' Falling back to local registry...\n'))
222
+
223
+ // Try Supabase client as fallback
224
+ if (this.supabase) {
225
+ try {
226
+ results = await this.listAllViaClient(options)
227
+ } catch (clientError) {
228
+ // If both fail, continue with local only
229
+ results = []
230
+ }
231
+ }
232
+ }
180
233
  } else if (this.supabase) {
181
- results = await this.listAllViaClient(options)
234
+ try {
235
+ results = await this.listAllViaClient(options)
236
+ } catch (error) {
237
+ apiError = error
238
+ results = []
239
+ }
182
240
  }
183
241
 
184
- // Also include local registry
242
+ // Always include local registry
185
243
  const localResults = await this.searchLocal('', options)
186
244
 
187
245
  // Merge results
188
246
  const merged = this.mergeResults(results, localResults)
189
247
 
190
- spinner.succeed(`Found ${merged.length} available agents`)
248
+ if (merged.length > 0) {
249
+ spinner.succeed(`Found ${merged.length} available agents`)
250
+ } else if (apiError && localResults.length === 0) {
251
+ spinner.warn('No agents found (API unavailable and no local registry)')
252
+ } else {
253
+ spinner.warn('No agents found')
254
+ }
191
255
 
192
256
  // Display results
193
257
  this.displaySearchResults(merged)
194
258
 
259
+ // Show helpful messages
260
+ if (apiError) {
261
+ if (localResults.length > 0) {
262
+ console.log(chalk.yellow('\n💡 Tip: Some agents may be missing. Check your connection or configure NEX_MARKETPLACE_API_URL\n'))
263
+ } else {
264
+ console.log(chalk.yellow('\n💡 Troubleshooting:'))
265
+ console.log(chalk.gray(' • The marketplace API is not responding'))
266
+ console.log(chalk.gray(' • No local registry found in this project'))
267
+ console.log(chalk.gray(' • Try: nex agent list (shows only installed agents)'))
268
+ console.log(chalk.gray(' • Or configure: export NEX_MARKETPLACE_API_URL=<your-url>'))
269
+ console.log(chalk.gray(' • Check your internet connection and try again later\n'))
270
+ }
271
+ }
272
+
195
273
  return merged
196
274
 
197
275
  } catch (error) {
198
276
  spinner.fail('Failed to load agents')
199
- console.error(chalk.red(`Error: ${error.message}`))
277
+ console.error(chalk.red(`\nError: ${error.message}`))
278
+ console.log(chalk.gray('\n💡 Troubleshooting:'))
279
+ console.log(chalk.gray(' 1. Check your internet connection'))
280
+ console.log(chalk.gray(' 2. Verify the API URL: ' + (this.apiUrl || 'not configured')))
281
+ console.log(chalk.gray(' 3. Try: nex agent list (shows only installed agents)'))
282
+ console.log(chalk.gray(' 4. Configure custom API: export NEX_MARKETPLACE_API_URL=<your-url>\n'))
200
283
  throw error
201
284
  }
202
285
  }
@@ -205,14 +288,32 @@ export default class NEXMarketplace {
205
288
  * List all agents via Edge Function API
206
289
  */
207
290
  async listAllViaAPI(options = {}) {
208
- const response = await fetch(`${this.apiUrl}/list`)
209
-
210
- if (!response.ok) {
211
- throw new Error(`API error: ${response.statusText}`)
291
+ try {
292
+ // Timeout de 10 segundos
293
+ const controller = new AbortController()
294
+ const timeoutId = setTimeout(() => controller.abort(), 10000)
295
+
296
+ const response = await fetch(`${this.apiUrl}/list`, {
297
+ signal: controller.signal
298
+ })
299
+
300
+ clearTimeout(timeoutId)
301
+
302
+ if (!response.ok) {
303
+ throw new Error(`API error: ${response.statusText}`)
304
+ }
305
+
306
+ const { agents } = await response.json()
307
+ return agents || []
308
+ } catch (error) {
309
+ if (error.name === 'AbortError') {
310
+ throw new Error('Connection timeout. The marketplace API is not responding.')
311
+ }
312
+ if (error.code === 'UND_ERR_CONNECT_TIMEOUT' || error.message.includes('timeout')) {
313
+ throw new Error('Connection timeout. Please check your internet connection or try again later.')
314
+ }
315
+ throw error
212
316
  }
213
-
214
- const { agents } = await response.json()
215
- return agents || []
216
317
  }
217
318
 
218
319
  /**
@@ -287,6 +388,11 @@ export default class NEXMarketplace {
287
388
  async searchLocal(query, options = {}) {
288
389
  const results = []
289
390
 
391
+ // Check if registry path exists
392
+ if (!await fs.pathExists(this.registryPath)) {
393
+ return results
394
+ }
395
+
290
396
  const categories = ['planning', 'execution', 'community']
291
397
 
292
398
  for (const category of categories) {
@@ -294,36 +400,49 @@ export default class NEXMarketplace {
294
400
 
295
401
  if (!await fs.pathExists(categoryPath)) continue
296
402
 
297
- const agents = await fs.readdir(categoryPath)
298
-
299
- for (const agentId of agents) {
300
- const manifestPath = path.join(categoryPath, agentId, 'manifest.yaml')
301
-
302
- if (!await fs.pathExists(manifestPath)) continue
403
+ try {
404
+ const agents = await fs.readdir(categoryPath)
303
405
 
304
- const manifestFile = await fs.readFile(manifestPath, 'utf8')
305
- const manifest = yaml.parse(manifestFile)
306
-
307
- // Filter by query
308
- if (query) {
309
- const searchable = [
310
- manifest.name,
311
- manifest.description,
312
- manifest.tagline,
313
- ...(manifest.tags || [])
314
- ].join(' ').toLowerCase()
406
+ for (const agentId of agents) {
407
+ const manifestPath = path.join(categoryPath, agentId, 'manifest.yaml')
408
+
409
+ if (!await fs.pathExists(manifestPath)) continue
315
410
 
316
- if (!searchable.includes(query.toLowerCase())) {
411
+ try {
412
+ const manifestFile = await fs.readFile(manifestPath, 'utf8')
413
+ const manifest = yaml.parse(manifestFile)
414
+
415
+ // Filter by query
416
+ if (query) {
417
+ const searchable = [
418
+ manifest.name,
419
+ manifest.description,
420
+ manifest.tagline,
421
+ ...(manifest.tags || [])
422
+ ].join(' ').toLowerCase()
423
+
424
+ if (!searchable.includes(query.toLowerCase())) {
425
+ continue
426
+ }
427
+ }
428
+
429
+ // Filter by category
430
+ if (options.category && manifest.category !== options.category) {
431
+ continue
432
+ }
433
+
434
+ results.push({
435
+ agent_id: agentId,
436
+ ...manifest
437
+ })
438
+ } catch (error) {
439
+ // Skip invalid manifests
317
440
  continue
318
441
  }
319
442
  }
320
-
321
- // Filter by category
322
- if (options.category && manifest.category !== options.category) {
323
- continue
324
- }
325
-
326
- results.push(manifest)
443
+ } catch (error) {
444
+ // Skip categories that can't be read
445
+ continue
327
446
  }
328
447
  }
329
448