@dench.com/cli 0.4.9 → 2.0.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/README.md +1 -1
- package/crm.ts +60 -26
- package/dench.ts +870 -229
- package/host.ts +3 -3
- package/image.ts +49 -40
- package/lib/enrichment-gateway.ts +30 -13
- package/package.json +2 -2
- package/search.ts +56 -48
- package/tools.ts +59 -34
package/tools.ts
CHANGED
|
@@ -3,13 +3,9 @@
|
|
|
3
3
|
* Composio's get-apps / get-tools / execute-tool surface.
|
|
4
4
|
*
|
|
5
5
|
* Mirrors `denchclaw`'s pattern: this CLI is a thin wrapper over five
|
|
6
|
-
* gateway endpoints, authenticated with
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* environment that has the API key (sandboxes, CI, local dev with the
|
|
10
|
-
* key exported manually). The previous implementation forced
|
|
11
|
-
* `requireSessionRuntime` and routed through a Convex action, which
|
|
12
|
-
* broke `apiKey` runtime mode entirely.
|
|
6
|
+
* gateway endpoints, authenticated with either `DENCH_API_KEY` or a
|
|
7
|
+
* paid-workspace gateway key resolved from the active agent session by
|
|
8
|
+
* the top-level dispatcher.
|
|
13
9
|
*
|
|
14
10
|
* Subcommands:
|
|
15
11
|
*
|
|
@@ -20,7 +16,7 @@
|
|
|
20
16
|
* dench tool connect <toolkit> [--callback-url <url>] [--json] — print OAuth redirect URL
|
|
21
17
|
* dench tool disconnect <connectionId> [--json]
|
|
22
18
|
*
|
|
23
|
-
* Endpoints used (all `Authorization: Bearer
|
|
19
|
+
* Endpoints used (all `Authorization: Bearer <gateway credential>`):
|
|
24
20
|
*
|
|
25
21
|
* GET /v1/composio/connections
|
|
26
22
|
* GET /v1/composio/toolkits?search=<slug>
|
|
@@ -36,10 +32,7 @@
|
|
|
36
32
|
* working.
|
|
37
33
|
*/
|
|
38
34
|
|
|
39
|
-
import {
|
|
40
|
-
PRODUCTION_API_URL,
|
|
41
|
-
PRODUCTION_GATEWAY_URL,
|
|
42
|
-
} from "./host";
|
|
35
|
+
import { PRODUCTION_API_URL, PRODUCTION_GATEWAY_URL } from "./host";
|
|
43
36
|
import {
|
|
44
37
|
CliArgError,
|
|
45
38
|
getFlag,
|
|
@@ -55,8 +48,15 @@ import {
|
|
|
55
48
|
export type ToolCliContext = {
|
|
56
49
|
args: string[];
|
|
57
50
|
jsonOutput: boolean;
|
|
51
|
+
bearerToken?: string;
|
|
52
|
+
gatewayBaseUrl?: string;
|
|
58
53
|
};
|
|
59
54
|
|
|
55
|
+
type GatewayAuthContext = Pick<
|
|
56
|
+
ToolCliContext,
|
|
57
|
+
"bearerToken" | "gatewayBaseUrl"
|
|
58
|
+
>;
|
|
59
|
+
|
|
60
60
|
export async function runToolCommand(ctx: ToolCliContext): Promise<void> {
|
|
61
61
|
const sub = ctx.args[0];
|
|
62
62
|
if (!sub || sub === "help" || sub === "--help" || sub === "-h") {
|
|
@@ -91,11 +91,14 @@ export async function runToolCommand(ctx: ToolCliContext): Promise<void> {
|
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* Summarise the user's connected apps for `dench context` and friends.
|
|
94
|
-
*
|
|
94
|
+
* Speaks straight to the gateway with either DENCH_API_KEY or a gateway
|
|
95
|
+
* key resolved from the active agent session.
|
|
95
96
|
* Returns the same legacy shape the previous Convex-routed path did so
|
|
96
97
|
* existing parsers don't need to change.
|
|
97
98
|
*/
|
|
98
|
-
export async function fetchConnectedAppsSummary(
|
|
99
|
+
export async function fetchConnectedAppsSummary(
|
|
100
|
+
ctx: GatewayAuthContext = {},
|
|
101
|
+
): Promise<{
|
|
99
102
|
available: boolean;
|
|
100
103
|
count: number;
|
|
101
104
|
connections: Array<{
|
|
@@ -109,7 +112,7 @@ export async function fetchConnectedAppsSummary(): Promise<{
|
|
|
109
112
|
}> {
|
|
110
113
|
let payload: unknown;
|
|
111
114
|
try {
|
|
112
|
-
payload = await callGateway("GET", "/v1/composio/connections");
|
|
115
|
+
payload = await callGateway(ctx, "GET", "/v1/composio/connections");
|
|
113
116
|
} catch (error) {
|
|
114
117
|
if (error instanceof MissingApiKeyError) {
|
|
115
118
|
return {
|
|
@@ -139,6 +142,7 @@ async function runStatusSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
|
139
142
|
if (filterToolkit) ctx.args.shift();
|
|
140
143
|
|
|
141
144
|
const connectionsPayload = await callGateway(
|
|
145
|
+
ctx,
|
|
142
146
|
"GET",
|
|
143
147
|
"/v1/composio/connections",
|
|
144
148
|
);
|
|
@@ -146,6 +150,7 @@ async function runStatusSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
|
146
150
|
if (filterToolkit) {
|
|
147
151
|
const search = encodeURIComponent(filterToolkit);
|
|
148
152
|
toolkitsPayload = await callGateway(
|
|
153
|
+
ctx,
|
|
149
154
|
"GET",
|
|
150
155
|
`/v1/composio/toolkits?search=${search}`,
|
|
151
156
|
);
|
|
@@ -194,7 +199,12 @@ async function runSearchSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
|
194
199
|
if (toolkit) body.toolkit_slug = toolkit;
|
|
195
200
|
if (limit) body.limit = limit;
|
|
196
201
|
|
|
197
|
-
const payload = await callGateway(
|
|
202
|
+
const payload = await callGateway(
|
|
203
|
+
ctx,
|
|
204
|
+
"POST",
|
|
205
|
+
"/v1/composio/tools/search",
|
|
206
|
+
body,
|
|
207
|
+
);
|
|
198
208
|
if (ctx.jsonOutput) {
|
|
199
209
|
console.log(JSON.stringify(payload, null, 2));
|
|
200
210
|
return;
|
|
@@ -231,7 +241,12 @@ async function runRunSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
|
231
241
|
|
|
232
242
|
let payload: unknown;
|
|
233
243
|
try {
|
|
234
|
-
payload = await callGateway(
|
|
244
|
+
payload = await callGateway(
|
|
245
|
+
ctx,
|
|
246
|
+
"POST",
|
|
247
|
+
"/v1/composio/tools/execute",
|
|
248
|
+
body,
|
|
249
|
+
);
|
|
235
250
|
} catch (error) {
|
|
236
251
|
if (error instanceof GatewayHttpError && isLikelyNoConnection(error)) {
|
|
237
252
|
const noConnPayload = noConnectionPayload(toolSlug, error);
|
|
@@ -266,7 +281,7 @@ async function runConnectSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
|
266
281
|
process.env.DENCH_OAUTH_CALLBACK_URL?.trim() ??
|
|
267
282
|
`${resolveAppPublicOrigin()}/api/composio/callback`;
|
|
268
283
|
|
|
269
|
-
const payload = await callGateway("POST", "/v1/composio/connect", {
|
|
284
|
+
const payload = await callGateway(ctx, "POST", "/v1/composio/connect", {
|
|
270
285
|
toolkit,
|
|
271
286
|
callback_url: callbackUrl,
|
|
272
287
|
});
|
|
@@ -313,7 +328,7 @@ async function runDisconnectSubcommand(ctx: ToolCliContext): Promise<void> {
|
|
|
313
328
|
const path = `/v1/composio/connections/${encodeURIComponent(connectionId)}`;
|
|
314
329
|
let payload: unknown = { ok: true, deleted: true };
|
|
315
330
|
try {
|
|
316
|
-
payload = await callGateway("DELETE", path);
|
|
331
|
+
payload = await callGateway(ctx, "DELETE", path);
|
|
317
332
|
} catch (error) {
|
|
318
333
|
// Composio returns 404 for connections that are already gone — surface
|
|
319
334
|
// that as a soft success so callers can keep their local state in sync.
|
|
@@ -350,9 +365,8 @@ class ToolCliError extends Error {
|
|
|
350
365
|
class MissingApiKeyError extends ToolCliError {
|
|
351
366
|
constructor() {
|
|
352
367
|
super(
|
|
353
|
-
"
|
|
354
|
-
"
|
|
355
|
-
"`dench login` and copy the api-key from the output.",
|
|
368
|
+
"No Dench gateway credential found. Set DENCH_API_KEY or run " +
|
|
369
|
+
"`dench signin` for a paid workspace.",
|
|
356
370
|
);
|
|
357
371
|
this.name = "MissingApiKeyError";
|
|
358
372
|
}
|
|
@@ -376,10 +390,19 @@ class GatewayHttpError extends ToolCliError {
|
|
|
376
390
|
}
|
|
377
391
|
}
|
|
378
392
|
|
|
379
|
-
function
|
|
393
|
+
function normalizeGatewayBaseUrl(value: string): string {
|
|
394
|
+
return value.replace(/\/+$/, "").replace(/\/v1$/, "");
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function resolveGatewayBaseUrl(
|
|
398
|
+
ctx?: Pick<ToolCliContext, "gatewayBaseUrl">,
|
|
399
|
+
): string {
|
|
400
|
+
if (ctx?.gatewayBaseUrl?.trim()) {
|
|
401
|
+
return normalizeGatewayBaseUrl(ctx.gatewayBaseUrl.trim());
|
|
402
|
+
}
|
|
380
403
|
const explicit =
|
|
381
404
|
process.env.DENCH_GATEWAY_URL?.trim() || process.env.GATEWAY_URL?.trim();
|
|
382
|
-
if (explicit) return explicit
|
|
405
|
+
if (explicit) return normalizeGatewayBaseUrl(explicit);
|
|
383
406
|
const host = process.env.DENCH_HOST?.trim();
|
|
384
407
|
if (host && /localhost|127\.0\.0\.1/.test(host)) {
|
|
385
408
|
return "http://localhost:8787";
|
|
@@ -410,24 +433,26 @@ function resolveAppPublicOrigin(): string {
|
|
|
410
433
|
return PRODUCTION_API_URL;
|
|
411
434
|
}
|
|
412
435
|
|
|
413
|
-
function
|
|
414
|
-
const
|
|
415
|
-
|
|
416
|
-
|
|
436
|
+
function requireBearerToken(ctx?: Pick<ToolCliContext, "bearerToken">): string {
|
|
437
|
+
const bearerToken =
|
|
438
|
+
ctx?.bearerToken?.trim() || process.env.DENCH_API_KEY?.trim();
|
|
439
|
+
if (!bearerToken) throw new MissingApiKeyError();
|
|
440
|
+
return bearerToken;
|
|
417
441
|
}
|
|
418
442
|
|
|
419
443
|
async function callGateway(
|
|
444
|
+
ctx: GatewayAuthContext,
|
|
420
445
|
method: "GET" | "POST" | "DELETE",
|
|
421
446
|
path: string,
|
|
422
447
|
body?: Record<string, unknown>,
|
|
423
448
|
): Promise<unknown> {
|
|
424
|
-
const
|
|
425
|
-
const baseUrl = resolveGatewayBaseUrl();
|
|
449
|
+
const bearerToken = requireBearerToken(ctx);
|
|
450
|
+
const baseUrl = resolveGatewayBaseUrl(ctx);
|
|
426
451
|
const init: RequestInit = {
|
|
427
452
|
method,
|
|
428
453
|
headers: {
|
|
429
454
|
"content-type": "application/json",
|
|
430
|
-
authorization: `Bearer ${
|
|
455
|
+
authorization: `Bearer ${bearerToken}`,
|
|
431
456
|
},
|
|
432
457
|
};
|
|
433
458
|
if (body !== undefined) init.body = JSON.stringify(body);
|
|
@@ -730,8 +755,8 @@ Usage:
|
|
|
730
755
|
dench tool disconnect <connectionId> [--json]
|
|
731
756
|
|
|
732
757
|
Notes:
|
|
733
|
-
All commands authenticate with DENCH_API_KEY
|
|
734
|
-
|
|
758
|
+
All commands authenticate with DENCH_API_KEY or the active \`dench signin\`
|
|
759
|
+
agent session. Agent sessions require a paid workspace.
|
|
735
760
|
dench tool connect prints the OAuth redirect URL for the human to open.
|
|
736
761
|
dench tool run output is redacted for display; pass --json for raw output.
|
|
737
762
|
Override the gateway base with DENCH_GATEWAY_URL or GATEWAY_URL.
|
|
@@ -804,7 +829,7 @@ function parsePositiveInt(name: string, raw: string): number {
|
|
|
804
829
|
// them as unused when callers tree-shake.
|
|
805
830
|
export { ToolCliError, GatewayHttpError, MissingApiKeyError };
|
|
806
831
|
// Kept reachable so a future caller can build their own dispatcher.
|
|
807
|
-
export { resolveGatewayBaseUrl,
|
|
832
|
+
export { resolveGatewayBaseUrl, requireBearerToken, callGateway };
|
|
808
833
|
// shiftRaw / hasFlag from cli-args are not used in this module yet but
|
|
809
834
|
// keep the import group consistent with cli/search.ts conventions.
|
|
810
835
|
void shiftRaw;
|