@dench.com/cli 2.7.4 → 2.7.5
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/dench.ts +1 -1
- package/package.json +1 -1
- package/tools.ts +108 -8
package/dench.ts
CHANGED
|
@@ -677,7 +677,7 @@ Usage:
|
|
|
677
677
|
dench integrations [--json]
|
|
678
678
|
dench tool status [toolkit] [--json]
|
|
679
679
|
dench tool connect <toolkit> [--callback-url <url>] [--json]
|
|
680
|
-
dench tool search "create github issue" [--toolkit github] [--limit 20] [--json]
|
|
680
|
+
dench tool search "create github issue" [--toolkit github] [--limit 20] [--compact] [--json]
|
|
681
681
|
dench tool run <composio_tool_slug> --args '{"key":"value"}' [--account <connectedAccountId>] [--json]
|
|
682
682
|
dench tool disconnect <connectionId> [--json]
|
|
683
683
|
|
package/package.json
CHANGED
package/tools.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* dench apps [--json] — list connected apps
|
|
13
13
|
* dench tool status [toolkit?] [--json] — list connections (optionally narrowed)
|
|
14
|
-
* dench tool search "<query>" [--toolkit <slug>] [--limit N] [--json]
|
|
14
|
+
* dench tool search "<query>" [--toolkit <slug>] [--limit N] [--compact] [--json]
|
|
15
15
|
* dench tool run <slug> [--args <json>] [--account <id>] [--json]
|
|
16
16
|
* dench tool connect <toolkit> [--callback-url <url>] [--json] — print OAuth redirect URL
|
|
17
17
|
* dench tool disconnect <connectionId> [--json]
|
|
@@ -187,12 +187,22 @@ async function runSearchSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
|
187
187
|
const toolkit = getFlag(ctx.args, "--toolkit");
|
|
188
188
|
const limitRaw = getFlag(ctx.args, "--limit");
|
|
189
189
|
const limit = limitRaw ? parsePositiveInt("--limit", limitRaw) : undefined;
|
|
190
|
+
const compact = hasFlag(ctx.args, "--compact");
|
|
190
191
|
const queryParts = ctx.args.slice();
|
|
191
192
|
ctx.args.length = 0;
|
|
193
|
+
// Everything left is the query, so an unconsumed flag would be searched for
|
|
194
|
+
// as a word and quietly match nothing. Refuse it instead: a typo that reads
|
|
195
|
+
// as "no results" is indistinguishable from an empty catalog.
|
|
196
|
+
const unknown = queryParts.find((part) => part.startsWith("--"));
|
|
197
|
+
if (unknown) {
|
|
198
|
+
throw new ToolCliError(
|
|
199
|
+
`Unknown flag for dench tool search: ${unknown}. Supported: --toolkit <slug>, --limit N, --compact, --json.`,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
192
202
|
const query = queryParts.join(" ").trim();
|
|
193
203
|
if (!query) {
|
|
194
204
|
throw new ToolCliError(
|
|
195
|
-
'Usage: dench tool search "<query>" [--toolkit <slug>] [--limit N] [--json]',
|
|
205
|
+
'Usage: dench tool search "<query>" [--toolkit <slug>] [--limit N] [--compact] [--json]',
|
|
196
206
|
);
|
|
197
207
|
}
|
|
198
208
|
|
|
@@ -210,7 +220,7 @@ async function runSearchSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
|
210
220
|
console.log(JSON.stringify(payload, null, 2));
|
|
211
221
|
return;
|
|
212
222
|
}
|
|
213
|
-
console.log(formatSearchResult(payload));
|
|
223
|
+
console.log(formatSearchResult(payload, compact));
|
|
214
224
|
}
|
|
215
225
|
|
|
216
226
|
async function runRunSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
@@ -249,7 +259,22 @@ async function runRunSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
|
249
259
|
body,
|
|
250
260
|
);
|
|
251
261
|
} catch (error) {
|
|
252
|
-
if (error instanceof GatewayHttpError
|
|
262
|
+
if (!(error instanceof GatewayHttpError)) throw error;
|
|
263
|
+
// Ambiguity is the opposite of a missing connection, and it has to be
|
|
264
|
+
// checked first: the gateway reports it with a 400 whose body also trips
|
|
265
|
+
// the no-connection hints, which would send the caller off to add a third
|
|
266
|
+
// account when the fix is to name one of the two they have.
|
|
267
|
+
if (isAccountSelectionRequired(error)) {
|
|
268
|
+
const selection = accountSelectionPayload(toolSlug, error);
|
|
269
|
+
console.log(
|
|
270
|
+
ctx.jsonOutput
|
|
271
|
+
? JSON.stringify(selection, null, 2)
|
|
272
|
+
: formatAccountSelection(selection),
|
|
273
|
+
);
|
|
274
|
+
process.exitCode = 1;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (isLikelyNoConnection(error)) {
|
|
253
278
|
const noConnPayload = noConnectionPayload(toolSlug, error);
|
|
254
279
|
if (ctx.jsonOutput) {
|
|
255
280
|
console.log(JSON.stringify(noConnPayload, null, 2));
|
|
@@ -475,8 +500,9 @@ async function callGateway(
|
|
|
475
500
|
// Result extraction & formatting
|
|
476
501
|
// ---------------------------------------------------------------------------
|
|
477
502
|
|
|
503
|
+
const ACCOUNT_SELECTION_CODE = "composio_account_selection_required";
|
|
504
|
+
|
|
478
505
|
const NO_CONNECTION_HINTS = [
|
|
479
|
-
"composio_account_selection_required",
|
|
480
506
|
"no active connection",
|
|
481
507
|
"no connection found",
|
|
482
508
|
"connected_account_id is required",
|
|
@@ -493,6 +519,78 @@ function isLikelyNoConnection(error: GatewayHttpError): boolean {
|
|
|
493
519
|
return NO_CONNECTION_HINTS.some((hint) => lower.includes(hint));
|
|
494
520
|
}
|
|
495
521
|
|
|
522
|
+
function isAccountSelectionRequired(error: GatewayHttpError): boolean {
|
|
523
|
+
return error.body.toLowerCase().includes(ACCOUNT_SELECTION_CODE);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function accountSelectionPayload(toolSlug: string, error: GatewayHttpError) {
|
|
527
|
+
const accounts = accountChoices(error.body);
|
|
528
|
+
return {
|
|
529
|
+
ok: false,
|
|
530
|
+
account_selection_required: true,
|
|
531
|
+
tool_slug: toolSlug,
|
|
532
|
+
toolkit: toolkitFromSlug(toolSlug),
|
|
533
|
+
accounts,
|
|
534
|
+
error: error.message,
|
|
535
|
+
next_actions: accounts.length
|
|
536
|
+
? accounts.map(
|
|
537
|
+
(account) =>
|
|
538
|
+
`Re-run with --account ${account.connected_account_id}${account.label ? ` (${account.label})` : ""}`,
|
|
539
|
+
)
|
|
540
|
+
: [
|
|
541
|
+
"Re-run with --account <connected_account_id>. Run `dench tool status <toolkit> --json` to list them.",
|
|
542
|
+
],
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* The candidate accounts named in an account-selection error. The gateway
|
|
548
|
+
* already lists them, so parse them back out rather than spending a round trip
|
|
549
|
+
* re-listing connections the caller was just handed.
|
|
550
|
+
*
|
|
551
|
+
* They arrive as a JSON array inside the JSON-encoded error message, so decode
|
|
552
|
+
* the body first: that hands the array back unescaped instead of leaving us to
|
|
553
|
+
* undo the escaping by hand.
|
|
554
|
+
*/
|
|
555
|
+
function accountChoices(body: string) {
|
|
556
|
+
const error = asRecord(asRecord(parseJsonOrUndefined(body))?.error);
|
|
557
|
+
const message = stringField(error, "message") ?? "";
|
|
558
|
+
const start = message.indexOf("[");
|
|
559
|
+
const end = message.lastIndexOf("]");
|
|
560
|
+
if (start === -1 || end <= start) return [];
|
|
561
|
+
const parsed = parseJsonOrUndefined(message.slice(start, end + 1));
|
|
562
|
+
return asRecordArray(parsed).flatMap((entry) => {
|
|
563
|
+
const id = stringField(entry, "connected_account_id");
|
|
564
|
+
if (!id) return [];
|
|
565
|
+
return [
|
|
566
|
+
{ connected_account_id: id, label: stringField(entry, "label", "email") },
|
|
567
|
+
];
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function parseJsonOrUndefined(value: string): unknown {
|
|
572
|
+
try {
|
|
573
|
+
return JSON.parse(value);
|
|
574
|
+
} catch {
|
|
575
|
+
return undefined;
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function formatAccountSelection(
|
|
580
|
+
payload: ReturnType<typeof accountSelectionPayload>,
|
|
581
|
+
) {
|
|
582
|
+
return [
|
|
583
|
+
"Multiple active accounts for this tool — name the one to use.",
|
|
584
|
+
`Tool: ${payload.tool_slug}`,
|
|
585
|
+
payload.toolkit ? `Toolkit: ${payload.toolkit}` : null,
|
|
586
|
+
"",
|
|
587
|
+
"Next actions:",
|
|
588
|
+
...payload.next_actions.map((line) => ` - ${line}`),
|
|
589
|
+
]
|
|
590
|
+
.filter((line) => line !== null)
|
|
591
|
+
.join("\n");
|
|
592
|
+
}
|
|
593
|
+
|
|
496
594
|
function noConnectionPayload(toolSlug: string, error: GatewayHttpError) {
|
|
497
595
|
const toolkit = toolkitFromSlug(toolSlug);
|
|
498
596
|
return {
|
|
@@ -623,7 +721,7 @@ function toolApprovalHint(toolSlug: string | undefined): string {
|
|
|
623
721
|
return readOnly ? `read-only (${readOnly})` : "approval likely";
|
|
624
722
|
}
|
|
625
723
|
|
|
626
|
-
function formatSearchResult(searchResult: unknown): string {
|
|
724
|
+
function formatSearchResult(searchResult: unknown, compact = false): string {
|
|
627
725
|
const root = asRecord(searchResult);
|
|
628
726
|
const data = asRecord(root?.data) ?? root;
|
|
629
727
|
const items = extractSearchItems(data);
|
|
@@ -658,7 +756,9 @@ function formatSearchResult(searchResult: unknown): string {
|
|
|
658
756
|
const isConnected =
|
|
659
757
|
booleanField(asRecord(tool.connection_status), "is_connected") ??
|
|
660
758
|
connectedSet.has(toolkit.toLowerCase());
|
|
661
|
-
const description =
|
|
759
|
+
const description = compact
|
|
760
|
+
? null
|
|
761
|
+
: (compactLine(tool.description) ?? null);
|
|
662
762
|
lines.push(
|
|
663
763
|
`${index + 1}. ${slug} - ${name}`,
|
|
664
764
|
` toolkit: ${toolkit} | ${toolApprovalHint(slug)} | ${
|
|
@@ -752,7 +852,7 @@ Usage:
|
|
|
752
852
|
dench apps [--json]
|
|
753
853
|
dench tool status [toolkit] [--json]
|
|
754
854
|
dench tool connect <toolkit> [--callback-url <url>] [--json]
|
|
755
|
-
dench tool search "create github issue" [--toolkit github] [--limit 20] [--json]
|
|
855
|
+
dench tool search "create github issue" [--toolkit github] [--limit 20] [--compact] [--json]
|
|
756
856
|
dench tool run <composio_tool_slug> --args '{"key":"value"}' [--account <connectedAccountId>] [--json]
|
|
757
857
|
dench tool disconnect <connectionId> [--json]
|
|
758
858
|
|