clementine-agent 1.12.0 → 1.12.1
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/dist/cli/dashboard.js
CHANGED
|
@@ -4406,8 +4406,16 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
|
|
|
4406
4406
|
// This replaces the hardcoded CURATED_TOOLKITS — slug typos are now
|
|
4407
4407
|
// impossible because we render directly from Composio's data, and
|
|
4408
4408
|
// newly-added services appear automatically.
|
|
4409
|
-
|
|
4410
|
-
|
|
4409
|
+
let catalog = [];
|
|
4410
|
+
let catalogError = null;
|
|
4411
|
+
try {
|
|
4412
|
+
catalog = await c.listAllToolkits();
|
|
4413
|
+
}
|
|
4414
|
+
catch (err) {
|
|
4415
|
+
catalogError = err?.message ?? String(err);
|
|
4416
|
+
console.error('[composio] catalog fetch failed:', catalogError);
|
|
4417
|
+
}
|
|
4418
|
+
const [connected, configured] = await Promise.all([
|
|
4411
4419
|
c.listConnectedToolkits(),
|
|
4412
4420
|
c.listToolkitSlugsWithAuthConfig(),
|
|
4413
4421
|
]);
|
|
@@ -4475,6 +4483,7 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
|
|
|
4475
4483
|
toolkits,
|
|
4476
4484
|
featured: featured.map(t => t.slug),
|
|
4477
4485
|
totalCount: toolkits.length,
|
|
4486
|
+
catalogError,
|
|
4478
4487
|
});
|
|
4479
4488
|
}
|
|
4480
4489
|
catch (err) {
|
|
@@ -25794,7 +25803,18 @@ async function refreshComposioConnections() {
|
|
|
25794
25803
|
}
|
|
25795
25804
|
var toolkits = d.toolkits || [];
|
|
25796
25805
|
if (toolkits.length === 0) {
|
|
25797
|
-
|
|
25806
|
+
// Distinguish "catalog fetch failed" from "user has nothing connected".
|
|
25807
|
+
// Surfacing d.catalogError tells us if Composio rejected the catalog
|
|
25808
|
+
// call (different permission than connectedAccounts.list, etc.).
|
|
25809
|
+
var msg = d.catalogError
|
|
25810
|
+
? 'Could not load toolkits from Composio: <code style="color:var(--red);font-size:11px">' + esc(d.catalogError) + '</code>'
|
|
25811
|
+
: 'Composio returned an empty toolkit catalog. This is unusual — try refreshing.';
|
|
25812
|
+
container.innerHTML =
|
|
25813
|
+
'<div style="padding:16px">' +
|
|
25814
|
+
'<div style="margin-bottom:12px;font-size:13px">' + msg + '</div>' +
|
|
25815
|
+
'<div style="font-size:11px;color:var(--text-muted);margin-bottom:12px">If your existing connections (e.g. Outlook) work via the agent, your API key is valid — the catalog endpoint may require a different permission tier or there\\'s a transient API issue.</div>' +
|
|
25816
|
+
'<button class="btn btn-sm btn-primary" onclick="refreshComposioConnections()">Retry</button>' +
|
|
25817
|
+
'</div>';
|
|
25798
25818
|
return;
|
|
25799
25819
|
}
|
|
25800
25820
|
// Stash full catalog in a closure so the search input can re-render
|
|
@@ -79,8 +79,8 @@ export interface CatalogToolkit {
|
|
|
79
79
|
* static CURATED_TOOLKITS array as the source of truth for the dashboard
|
|
80
80
|
* Connections panel — slug typos are now impossible because we render from
|
|
81
81
|
* Composio's own data, and new services appear automatically as they're
|
|
82
|
-
* added. Cached at module level for 1 hour;
|
|
83
|
-
*
|
|
82
|
+
* added. Cached at module level for 1 hour on success; failures don't
|
|
83
|
+
* cache (so the next request retries instead of returning stale empty).
|
|
84
84
|
*/
|
|
85
85
|
export declare function listAllToolkits(): Promise<CatalogToolkit[]>;
|
|
86
86
|
export declare function listToolkitMeta(): Promise<Map<string, ToolkitMeta>>;
|
|
@@ -342,57 +342,88 @@ const CATALOG_TTL_MS = 60 * 60 * 1000; // 1h — catalog drifts very slowly
|
|
|
342
342
|
* static CURATED_TOOLKITS array as the source of truth for the dashboard
|
|
343
343
|
* Connections panel — slug typos are now impossible because we render from
|
|
344
344
|
* Composio's own data, and new services appear automatically as they're
|
|
345
|
-
* added. Cached at module level for 1 hour;
|
|
346
|
-
*
|
|
345
|
+
* added. Cached at module level for 1 hour on success; failures don't
|
|
346
|
+
* cache (so the next request retries instead of returning stale empty).
|
|
347
347
|
*/
|
|
348
348
|
export async function listAllToolkits() {
|
|
349
349
|
const now = Date.now();
|
|
350
|
-
if (catalogCache && now - catalogCache.at < CATALOG_TTL_MS) {
|
|
350
|
+
if (catalogCache && catalogCache.data.length > 0 && now - catalogCache.at < CATALOG_TTL_MS) {
|
|
351
351
|
return catalogCache.data;
|
|
352
352
|
}
|
|
353
353
|
const composio = getComposio();
|
|
354
354
|
if (!composio)
|
|
355
355
|
return [];
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
//
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
356
|
+
// Try the raw client first — supports cursor pagination so we get the full
|
|
357
|
+
// 1000+ catalog. If that errors (some API keys / plans restrict it), fall
|
|
358
|
+
// back to the high-level wrapper which returns up to 500 in one shot.
|
|
359
|
+
let result = [];
|
|
360
|
+
let lastError = null;
|
|
361
|
+
try {
|
|
362
|
+
result = await fetchCatalogViaRawClient(composio);
|
|
363
|
+
}
|
|
364
|
+
catch (err) {
|
|
365
|
+
lastError = err;
|
|
366
|
+
logger.warn({ err }, 'Raw-client toolkit list failed — falling back to high-level wrapper');
|
|
367
|
+
}
|
|
368
|
+
if (result.length === 0) {
|
|
362
369
|
try {
|
|
363
|
-
|
|
364
|
-
const resp = await rawClient.toolkits.list({
|
|
365
|
-
limit: 500,
|
|
366
|
-
...(cursor ? { cursor } : {}),
|
|
367
|
-
});
|
|
368
|
-
const items = (resp?.items ?? []);
|
|
369
|
-
for (const it of items) {
|
|
370
|
-
const managed = (it.composioManagedAuthSchemes ?? it.composio_managed_auth_schemes ?? []);
|
|
371
|
-
const schemes = (it.authSchemes ?? it.auth_schemes ?? []);
|
|
372
|
-
const noAuth = it.noAuth ?? it.no_auth ?? false;
|
|
373
|
-
out.push({
|
|
374
|
-
slug: it.slug,
|
|
375
|
-
name: it.name,
|
|
376
|
-
logoUrl: it.meta?.logo,
|
|
377
|
-
description: it.meta?.description,
|
|
378
|
-
toolsCount: it.meta?.toolsCount ?? it.meta?.tools_count,
|
|
379
|
-
authMode: noAuth ? 'none' : (managed.length > 0 ? 'managed' : (schemes.length > 0 ? 'byo' : 'none')),
|
|
380
|
-
categories: it.meta?.categories ?? [],
|
|
381
|
-
});
|
|
382
|
-
}
|
|
383
|
-
cursor = resp?.next_cursor ?? resp?.nextCursor;
|
|
384
|
-
if (!cursor || items.length === 0)
|
|
385
|
-
break;
|
|
370
|
+
result = await fetchCatalogViaWrapper(composio);
|
|
386
371
|
}
|
|
387
372
|
catch (err) {
|
|
388
|
-
|
|
389
|
-
|
|
373
|
+
lastError = err;
|
|
374
|
+
logger.error({ err }, 'High-level toolkit list also failed');
|
|
390
375
|
}
|
|
391
376
|
}
|
|
392
|
-
|
|
393
|
-
|
|
377
|
+
if (result.length === 0) {
|
|
378
|
+
// Surface whichever error we hit so the dashboard can render something
|
|
379
|
+
// actionable instead of "no toolkits available".
|
|
380
|
+
const detail = lastError?.message ?? 'Unknown error';
|
|
381
|
+
throw new Error(`Composio catalog fetch failed: ${detail}`);
|
|
382
|
+
}
|
|
383
|
+
catalogCache = { at: now, data: result };
|
|
384
|
+
logger.info({ count: result.length }, 'Composio catalog fetched');
|
|
385
|
+
return result;
|
|
386
|
+
}
|
|
387
|
+
function normalizeCatalogItem(it) {
|
|
388
|
+
const managed = (it.composioManagedAuthSchemes ?? it.composio_managed_auth_schemes ?? []);
|
|
389
|
+
const schemes = (it.authSchemes ?? it.auth_schemes ?? []);
|
|
390
|
+
const noAuth = it.noAuth ?? it.no_auth ?? false;
|
|
391
|
+
return {
|
|
392
|
+
slug: it.slug,
|
|
393
|
+
name: it.name,
|
|
394
|
+
logoUrl: it.meta?.logo,
|
|
395
|
+
description: it.meta?.description,
|
|
396
|
+
toolsCount: it.meta?.toolsCount ?? it.meta?.tools_count,
|
|
397
|
+
authMode: noAuth ? 'none' : (managed.length > 0 ? 'managed' : (schemes.length > 0 ? 'byo' : 'none')),
|
|
398
|
+
categories: it.meta?.categories ?? [],
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
async function fetchCatalogViaRawClient(composio) {
|
|
402
|
+
const out = [];
|
|
403
|
+
let cursor;
|
|
404
|
+
// Bounded loop — 30 pages × 500 items = 15K ceiling.
|
|
405
|
+
for (let page = 0; page < 30; page++) {
|
|
406
|
+
const rawClient = composio.client;
|
|
407
|
+
const resp = await rawClient.toolkits.list({
|
|
408
|
+
limit: 500,
|
|
409
|
+
...(cursor ? { cursor } : {}),
|
|
410
|
+
});
|
|
411
|
+
const items = (resp?.items ?? []);
|
|
412
|
+
for (const it of items)
|
|
413
|
+
out.push(normalizeCatalogItem(it));
|
|
414
|
+
cursor = resp?.next_cursor ?? resp?.nextCursor;
|
|
415
|
+
if (!cursor || items.length === 0)
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
394
418
|
return out;
|
|
395
419
|
}
|
|
420
|
+
async function fetchCatalogViaWrapper(composio) {
|
|
421
|
+
// High-level wrapper returns an array (up to limit). No cursor support
|
|
422
|
+
// through this path, so we cap at the documented max. Better than zero.
|
|
423
|
+
const resp = await composio.toolkits.get({ limit: 500 });
|
|
424
|
+
const items = (Array.isArray(resp) ? resp : (resp?.items ?? []));
|
|
425
|
+
return items.map(normalizeCatalogItem);
|
|
426
|
+
}
|
|
396
427
|
async function fetchAllToolkitMeta() {
|
|
397
428
|
const composio = getComposio();
|
|
398
429
|
if (!composio)
|