@dench.com/cli 0.4.0 → 0.4.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.
Files changed (3) hide show
  1. package/dench.ts +60 -476
  2. package/package.json +2 -1
  3. package/tools.ts +808 -0
package/dench.ts CHANGED
@@ -38,6 +38,7 @@ import {
38
38
  withCurrentSessionSelection,
39
39
  withSavedSession,
40
40
  } from "./session";
41
+ import { fetchConnectedAppsSummary, runToolCommand } from "./tools";
41
42
 
42
43
  type JsonRecord = Record<string, unknown>;
43
44
 
@@ -172,20 +173,6 @@ const api = {
172
173
  "functions/agentWorkspace:whatCanIDoHere",
173
174
  ),
174
175
  },
175
- agentToolsNode: {
176
- agentToolConnect: makeFunctionReference<"action">(
177
- "functions/agentToolsNode:agentToolConnect",
178
- ),
179
- agentToolRun: makeFunctionReference<"action">(
180
- "functions/agentToolsNode:agentToolRun",
181
- ),
182
- agentToolSearch: makeFunctionReference<"action">(
183
- "functions/agentToolsNode:agentToolSearch",
184
- ),
185
- agentToolStatus: makeFunctionReference<"action">(
186
- "functions/agentToolsNode:agentToolStatus",
187
- ),
188
- },
189
176
  files: {
190
177
  listTree: makeFunctionReference<"query">("functions/files:listTree"),
191
178
  deleteFile: makeFunctionReference<"mutation">(
@@ -362,7 +349,7 @@ function normalizeCliError(error: unknown): CliError {
362
349
  return new CliError(message);
363
350
  }
364
351
 
365
- function parseJsonObjectOption(name: string) {
352
+ function _parseJsonObjectOption(name: string) {
366
353
  const raw = option(name);
367
354
  if (!raw) return {};
368
355
  let parsed: unknown;
@@ -594,16 +581,22 @@ function toolHelp() {
594
581
  Usage:
595
582
  dench apps [--json]
596
583
  dench tool status [toolkit] [--json]
597
- dench tool connect <toolkit> [--json]
598
- dench tool search "create github issue" [--toolkit github] [--limit 20] [--compact] [--json]
599
- dench tool run <composio_tool_slug> --args '{"key":"value"}' [--account <connected_account_id>] [--approval <approvalId>] [--json]
584
+ dench tool connect <toolkit> [--callback-url <url>] [--json]
585
+ dench tool search "create github issue" [--toolkit github] [--limit 20] [--json]
586
+ dench tool run <composio_tool_slug> --args '{"key":"value"}' [--account <connectedAccountId>] [--json]
587
+ dench tool disconnect <connectionId> [--json]
588
+
589
+ Auth:
590
+ All commands authenticate with DENCH_API_KEY (Bearer) directly to the
591
+ Dench Cloud Gateway. No \`dench login\` agent session is required —
592
+ inside a Dench sandbox the key is baked into the env automatically;
593
+ outside, export DENCH_API_KEY=<key> first.
600
594
 
601
595
  Notes:
602
- dench apps lists all connected apps. It returns the same shape as dench tool status --json.
603
- dench tool search prints compact ranked results by default. Add --json only when you need full schemas.
604
- Run clear read-only FETCH, GET, LIST, SEARCH, READ, and FIND tools through Dench without manual approval.
605
- Non-JSON tool run output redacts likely OTP codes, secrets, and tokens for display.
606
- Dench enforces the final policy and returns requiresApproval when approval is needed.
596
+ dench apps is an alias for dench tool status.
597
+ dench tool connect prints the OAuth redirect URL for the human to open.
598
+ dench tool run output is redacted for display; pass --json for raw JSON.
599
+ Override the gateway base with DENCH_GATEWAY_URL or GATEWAY_URL.
607
600
  `);
608
601
  }
609
602
 
@@ -745,8 +738,10 @@ function appsHelp() {
745
738
  Usage:
746
739
  dench apps [--json]
747
740
 
748
- Lists all connected external apps for the current Dench session.
749
- This is an alias for dench tool status [--json].
741
+ Lists all external apps connected to this organization (Slack, Gmail,
742
+ GitHub, etc.). Authenticates with DENCH_API_KEY (Bearer) directly to the
743
+ Dench Cloud Gateway — no \`dench login\` agent session required. Alias
744
+ for \`dench tool status\`.
750
745
  `);
751
746
  }
752
747
 
@@ -1899,10 +1894,7 @@ function requireSessionRuntime(
1899
1894
  * (`dench_*` / `gw_sk_*` / etc.) baked into sandbox envVars. The
1900
1895
  * server-side `requireCrmAccess` helper dispatches on the prefix.
1901
1896
  */
1902
- type AuthenticatedRuntime = Extract<
1903
- Runtime,
1904
- { mode: "session" | "apiKey" }
1905
- >;
1897
+ type AuthenticatedRuntime = Extract<Runtime, { mode: "session" | "apiKey" }>;
1906
1898
 
1907
1899
  function requireAuthenticatedRuntime(
1908
1900
  runtime: Runtime,
@@ -1950,25 +1942,6 @@ function rejectApiKeyForAgentCommand(
1950
1942
  }
1951
1943
  }
1952
1944
 
1953
- async function printToolStatus(runtime: Runtime, toolkit?: string) {
1954
- const sessionRuntime = requireSessionRuntime(runtime, "dench tool status");
1955
- try {
1956
- print(
1957
- await sessionRuntime.client.action(
1958
- api.functions.agentToolsNode.agentToolStatus,
1959
- {
1960
- sessionToken: sessionRuntime.sessionToken,
1961
- toolkit,
1962
- },
1963
- ),
1964
- );
1965
- } catch (error) {
1966
- const unavailable = toolGatewayUnavailable(error, { toolkit });
1967
- if (!unavailable) throw error;
1968
- print(json ? unavailable : formatToolUnavailable(unavailable));
1969
- }
1970
- }
1971
-
1972
1945
  function asRecord(value: unknown): JsonRecord | undefined {
1973
1946
  if (!value || typeof value !== "object" || Array.isArray(value)) {
1974
1947
  return undefined;
@@ -2004,7 +1977,7 @@ function numberField(record: JsonRecord | undefined, ...names: string[]) {
2004
1977
  return undefined;
2005
1978
  }
2006
1979
 
2007
- function booleanField(record: JsonRecord | undefined, ...names: string[]) {
1980
+ function _booleanField(record: JsonRecord | undefined, ...names: string[]) {
2008
1981
  if (!record) return undefined;
2009
1982
  for (const name of names) {
2010
1983
  const value = record[name];
@@ -2027,94 +2000,6 @@ function isMissingConvexFunction(error: unknown) {
2027
2000
  return /Could not find public function/i.test(errorMessage(error));
2028
2001
  }
2029
2002
 
2030
- function toolGatewayUnavailable(error: unknown, context: JsonRecord = {}) {
2031
- const message = errorMessage(error);
2032
- if (!/gateway API key|callGateway|Composio/i.test(message)) {
2033
- return null;
2034
- }
2035
- const invalidKey = /Invalid gateway API key/i.test(message);
2036
- const query = stringField(context, "query");
2037
- const toolkit = stringField(context, "toolkit");
2038
- const retryCommand = query
2039
- ? `Retry dench tool search ${JSON.stringify(query)}${toolkit ? ` --toolkit ${toolkit}` : ""} after the gateway key is fixed.`
2040
- : "Retry dench apps --json after the gateway key is fixed.";
2041
- return {
2042
- ok: false,
2043
- available: false,
2044
- code: invalidKey ? "invalid_gateway_api_key" : "tool_gateway_unavailable",
2045
- reason: invalidKey
2046
- ? "The workspace gateway key is invalid or out of sync."
2047
- : "The connected-app gateway is unavailable.",
2048
- ...context,
2049
- nextActions: [
2050
- "Ask a workspace admin to repair or rotate the Dench gateway key.",
2051
- retryCommand,
2052
- ],
2053
- };
2054
- }
2055
-
2056
- function formatToolUnavailable(payload: JsonRecord) {
2057
- return [
2058
- "Connected apps unavailable.",
2059
- `Reason: ${stringField(payload, "reason") ?? "tool gateway unavailable"}`,
2060
- "",
2061
- "Next actions:",
2062
- ...(
2063
- (Array.isArray(payload.nextActions)
2064
- ? payload.nextActions
2065
- : []) as unknown[]
2066
- )
2067
- .filter((item): item is string => typeof item === "string")
2068
- .map((item) => ` - ${item}`),
2069
- ].join("\n");
2070
- }
2071
-
2072
- const READ_ONLY_TOOL_ACTIONS = new Set([
2073
- "FETCH",
2074
- "GET",
2075
- "LIST",
2076
- "SEARCH",
2077
- "READ",
2078
- "FIND",
2079
- ]);
2080
-
2081
- function toolSlugTokens(toolSlug: string) {
2082
- return toolSlug
2083
- .toUpperCase()
2084
- .split(/[^A-Z0-9]+/)
2085
- .filter(Boolean);
2086
- }
2087
-
2088
- function toolApprovalHint(toolSlug: string | undefined) {
2089
- if (!toolSlug) return "approval unknown";
2090
- const readOnly = toolSlugTokens(toolSlug).find((token) =>
2091
- READ_ONLY_TOOL_ACTIONS.has(token),
2092
- );
2093
- return readOnly ? `read-only (${readOnly})` : "approval likely";
2094
- }
2095
-
2096
- function compactConnection(connection: JsonRecord) {
2097
- return {
2098
- toolkit: stringField(connection, "toolkit") ?? "unknown",
2099
- status: stringField(connection, "status") ?? "unknown",
2100
- connectedAccountId:
2101
- stringField(connection, "connectedAccountId", "connected_account_id") ??
2102
- stringField(connection, "id"),
2103
- accountLabel:
2104
- stringField(connection, "accountLabel", "account_label", "label") ?? null,
2105
- };
2106
- }
2107
-
2108
- function summarizeConnectedApps(value: unknown) {
2109
- const record = asRecord(value);
2110
- const connections = asRecordArray(record?.connections).map(compactConnection);
2111
- return {
2112
- available: true,
2113
- count: connections.length,
2114
- connections,
2115
- };
2116
- }
2117
-
2118
2003
  function workspaceSlugFromContext(context: JsonRecord) {
2119
2004
  const workspace = asRecord(context.workspace);
2120
2005
  return (
@@ -2141,41 +2026,12 @@ function nextContextCommands() {
2141
2026
  ];
2142
2027
  }
2143
2028
 
2144
- async function connectedAppsContext(runtime: Runtime) {
2145
- if (runtime.mode !== "session") {
2146
- return {
2147
- available: false,
2148
- reason: "Connected apps require a logged-in Dench agent session.",
2149
- count: 0,
2150
- connections: [],
2151
- };
2152
- }
2153
-
2154
- try {
2155
- return summarizeConnectedApps(
2156
- await runtime.client.action(
2157
- api.functions.agentToolsNode.agentToolStatus,
2158
- {
2159
- sessionToken: runtime.sessionToken,
2160
- },
2161
- ),
2162
- );
2163
- } catch (error) {
2164
- const unavailable = toolGatewayUnavailable(error);
2165
- if (unavailable) {
2166
- return {
2167
- ...unavailable,
2168
- count: 0,
2169
- connections: [],
2170
- };
2171
- }
2172
- return {
2173
- available: false,
2174
- error: errorMessage(error),
2175
- count: 0,
2176
- connections: [],
2177
- };
2178
- }
2029
+ async function connectedAppsContext(_runtime: Runtime) {
2030
+ // Reach the Dench gateway directly with DENCH_API_KEY (mirrors the
2031
+ // denchclaw pattern). This works for every Runtime mode — session,
2032
+ // apiKey, dev — because the gateway only cares about the bearer key,
2033
+ // not which Convex session is calling.
2034
+ return fetchConnectedAppsSummary();
2179
2035
  }
2180
2036
 
2181
2037
  async function buildContext(runtime: Runtime) {
@@ -2414,223 +2270,6 @@ function formatContext(context: JsonRecord) {
2414
2270
  return lines.join("\n");
2415
2271
  }
2416
2272
 
2417
- function toolkitSlugFromTool(tool: JsonRecord) {
2418
- const toolkit = tool.toolkit;
2419
- if (typeof toolkit === "string") return toolkit;
2420
- const toolkitRecord = asRecord(toolkit);
2421
- return (
2422
- stringField(toolkitRecord, "slug", "name") ??
2423
- stringField(tool, "toolkit_slug")
2424
- );
2425
- }
2426
-
2427
- function connectedToolkitSet(data: JsonRecord | undefined) {
2428
- const connected = new Set<string>();
2429
- const connectedToolkits = Array.isArray(data?.connected_toolkits)
2430
- ? data?.connected_toolkits
2431
- : [];
2432
- for (const toolkit of connectedToolkits) {
2433
- const value = stringValue(toolkit);
2434
- if (value) connected.add(value.toLowerCase());
2435
- }
2436
- for (const status of asRecordArray(data?.toolkit_connection_statuses)) {
2437
- const toolkit = stringField(status, "toolkit");
2438
- const isConnected = booleanField(status, "has_active_connection");
2439
- if (toolkit && isConnected) connected.add(toolkit.toLowerCase());
2440
- }
2441
- return connected;
2442
- }
2443
-
2444
- function searchToolItems(searchResult: unknown) {
2445
- const root = asRecord(searchResult);
2446
- const data = asRecord(root?.data) ?? root;
2447
- const directItems = asRecordArray(data?.items);
2448
- if (directItems.length > 0) {
2449
- return { data, items: directItems };
2450
- }
2451
-
2452
- const schemas = asRecord(data?.tool_schemas);
2453
- if (!schemas) return { data, items: [] };
2454
- return {
2455
- data,
2456
- items: Object.entries(schemas).flatMap<JsonRecord>(([slug, value]) => {
2457
- const tool = asRecord(value);
2458
- return tool
2459
- ? [{ ...tool, tool_slug: stringField(tool, "tool_slug") ?? slug }]
2460
- : [];
2461
- }),
2462
- };
2463
- }
2464
-
2465
- function compactToolSearchResult(searchResult: unknown) {
2466
- const root = asRecord(searchResult);
2467
- const { data, items } = searchToolItems(searchResult);
2468
- const connected = connectedToolkitSet(data);
2469
- const tools = items.map((tool) => {
2470
- const toolkit = toolkitSlugFromTool(tool);
2471
- const connectionStatus = asRecord(tool.connection_status);
2472
- const isConnected =
2473
- booleanField(connectionStatus, "is_connected") ??
2474
- (toolkit ? connected.has(toolkit.toLowerCase()) : undefined);
2475
- const slug = stringField(tool, "slug", "tool_slug") ?? "unknown_tool";
2476
- return {
2477
- slug,
2478
- name: stringField(tool, "display_name", "name") ?? slug,
2479
- toolkit: toolkit ?? "unknown",
2480
- approval: toolApprovalHint(slug),
2481
- connected:
2482
- isConnected === undefined
2483
- ? "connection unknown"
2484
- : isConnected
2485
- ? "connected"
2486
- : "not connected",
2487
- description: compactLine(tool.description) ?? null,
2488
- };
2489
- });
2490
-
2491
- return {
2492
- ok: root?.ok ?? true,
2493
- query: stringField(root, "query") ?? stringField(data, "query") ?? null,
2494
- returned: tools.length,
2495
- total:
2496
- numberField(data, "total_items", "total", "count") ??
2497
- numberField(root, "total_items", "total", "count") ??
2498
- tools.length,
2499
- tools,
2500
- };
2501
- }
2502
-
2503
- function formatToolSearchResult(searchResult: unknown) {
2504
- const summary = compactToolSearchResult(searchResult);
2505
- const query = summary.query ? ` for "${summary.query}"` : "";
2506
- if (summary.tools.length === 0) {
2507
- return `No matching tools found${query}.\nUse --json if you need the raw provider response.`;
2508
- }
2509
-
2510
- const lines = [`Top tools${query}:`];
2511
- for (const [index, tool] of summary.tools.entries()) {
2512
- lines.push(
2513
- `${index + 1}. ${tool.slug} - ${tool.name}`,
2514
- ` toolkit: ${tool.toolkit} | ${tool.approval} | ${tool.connected}`,
2515
- );
2516
- if (tool.description) {
2517
- lines.push(` ${tool.description}`);
2518
- }
2519
- }
2520
- lines.push(
2521
- "",
2522
- "Use dench tool run <slug> --args '{...}' when ready.",
2523
- "Use --json only when you need full schemas or raw arguments.",
2524
- );
2525
- return lines.join("\n");
2526
- }
2527
-
2528
- function printToolSearchResult(searchResult: unknown) {
2529
- print(json ? searchResult : formatToolSearchResult(searchResult));
2530
- }
2531
-
2532
- const SENSITIVE_KEY_RE =
2533
- /(?:otp|oneTime|verification|security|passcode|password|secret|api[_-]?key|access[_-]?token|refresh[_-]?token|authorization)/i;
2534
-
2535
- function redactSensitiveString(value: string) {
2536
- return value
2537
- .replace(
2538
- /\b((?:one[-\s]?time|verification|security|login|sign[-\s]?in|auth(?:entication)?|2fa|mfa|otp|passcode|code)[^.\n\r]{0,80}?)(\d[\d\s-]{2,10}\d)\b/gi,
2539
- "$1[redacted-code]",
2540
- )
2541
- .replace(
2542
- /\b((?:api[_\s-]?key|secret|token|password)[^.\n\r]{0,60}?)([A-Za-z0-9_=-]{12,})\b/gi,
2543
- "$1[redacted-secret]",
2544
- );
2545
- }
2546
-
2547
- function redactSensitiveDisplay(value: unknown, key?: string): unknown {
2548
- if (typeof value === "string") {
2549
- return key && SENSITIVE_KEY_RE.test(key)
2550
- ? "[redacted]"
2551
- : redactSensitiveString(value);
2552
- }
2553
- if (typeof value === "number" && key && SENSITIVE_KEY_RE.test(key)) {
2554
- return "[redacted]";
2555
- }
2556
- if (Array.isArray(value)) {
2557
- return value.map((item) => redactSensitiveDisplay(item));
2558
- }
2559
- const record = asRecord(value);
2560
- if (!record) return value;
2561
-
2562
- return Object.fromEntries(
2563
- Object.entries(record).map(([entryKey, entryValue]) => [
2564
- entryKey,
2565
- redactSensitiveDisplay(entryValue, entryKey),
2566
- ]),
2567
- );
2568
- }
2569
-
2570
- function connectionUrlFromToolConnect(result: unknown) {
2571
- const record = asRecord(result);
2572
- const data = asRecord(record?.data);
2573
- return (
2574
- stringField(record, "connectUrl", "redirect_url", "redirectUrl", "url") ??
2575
- stringField(data, "redirect_url", "redirectUrl", "url")
2576
- );
2577
- }
2578
-
2579
- function withToolConnectSummary(result: unknown) {
2580
- const record = asRecord(result);
2581
- if (!record) return result;
2582
- const toolkit = stringField(record, "toolkit") ?? "app";
2583
- const connectUrl = connectionUrlFromToolConnect(record);
2584
- if (!connectUrl) return record;
2585
- return {
2586
- ...record,
2587
- connectUrl,
2588
- linkText: `Connect ${toolkit} here`,
2589
- message: `Connect ${toolkit} here: ${connectUrl}`,
2590
- };
2591
- }
2592
-
2593
- function formatToolConnectResult(result: unknown) {
2594
- const record = asRecord(withToolConnectSummary(result));
2595
- if (!record) return String(result);
2596
- const toolkit = stringField(record, "toolkit") ?? "app";
2597
- const connectUrl = stringField(record, "connectUrl");
2598
- if (connectUrl) {
2599
- return `Connect ${toolkit} here: ${connectUrl}`;
2600
- }
2601
- return JSON.stringify(record, null, 2);
2602
- }
2603
-
2604
- function withToolRunGuidance(runtime: Runtime, result: unknown) {
2605
- const record = asRecord(result);
2606
- if (!record || record.requiresApproval !== true) return result;
2607
- const approvalId = stringField(record, "approvalId");
2608
- const statusContext =
2609
- runtime.mode === "session" || runtime.mode === "apiKey"
2610
- ? workspaceUrls(runtime.host, runtime.organization?.slug)
2611
- : {};
2612
- const approvalCommand = approvalId
2613
- ? `dench approval approve ${approvalId} --evidence "User said yes in chat" --json`
2614
- : undefined;
2615
- return {
2616
- ...record,
2617
- ...statusContext,
2618
- ...(approvalCommand ? { approvalCommand } : {}),
2619
- nextActions: [
2620
- "Ask the human for an explicit yes/no in chat.",
2621
- ...(approvalCommand ? [`If yes, run: ${approvalCommand}`] : []),
2622
- approvalId
2623
- ? `Rerun the exact same dench tool run command with --approval ${approvalId}.`
2624
- : "Rerun the exact same dench tool run command after approval.",
2625
- ],
2626
- };
2627
- }
2628
-
2629
- function printToolRunResult(runtime: Runtime, result: unknown) {
2630
- const guided = withToolRunGuidance(runtime, result);
2631
- print(json ? guided : redactSensitiveDisplay(guided));
2632
- }
2633
-
2634
2273
  async function devOverview(runtime: Awaited<ReturnType<typeof getRuntime>>) {
2635
2274
  if (runtime.mode !== "dev") {
2636
2275
  throw new Error("Not in dev mode");
@@ -2823,10 +2462,12 @@ async function runFsCommand() {
2823
2462
  function normalizeFilesPath(input: string): string {
2824
2463
  if (!input || input === "/") return "/";
2825
2464
  let cleaned = input.replace(/\\/g, "/").replace(/\/+/g, "/");
2826
- if (cleaned.startsWith("/workspace/")) cleaned = cleaned.slice("/workspace".length);
2465
+ if (cleaned.startsWith("/workspace/"))
2466
+ cleaned = cleaned.slice("/workspace".length);
2827
2467
  else if (cleaned === "/workspace") cleaned = "/";
2828
2468
  if (!cleaned.startsWith("/")) cleaned = `/${cleaned}`;
2829
- if (cleaned.length > 1 && cleaned.endsWith("/")) cleaned = cleaned.slice(0, -1);
2469
+ if (cleaned.length > 1 && cleaned.endsWith("/"))
2470
+ cleaned = cleaned.slice(0, -1);
2830
2471
  return cleaned;
2831
2472
  }
2832
2473
 
@@ -3004,9 +2645,9 @@ async function runFilesMv() {
3004
2645
  }
3005
2646
 
3006
2647
  const sourceRows = await listConvexTreeRecursive(runtime, apiKey, from);
3007
- const sourceSelf = (await listConvexTreeAt(runtime, apiKey, parentOfPath(from))).find(
3008
- (row) => row.path === from,
3009
- );
2648
+ const sourceSelf = (
2649
+ await listConvexTreeAt(runtime, apiKey, parentOfPath(from))
2650
+ ).find((row) => row.path === from);
3010
2651
  if (!sourceSelf && sourceRows.length === 0) {
3011
2652
  throw new CliError(`Source not found in Convex: ${from}`, {
3012
2653
  code: "files_mv_not_found",
@@ -3068,7 +2709,11 @@ async function runFilesRm() {
3068
2709
  const target = normalizeFilesPath(targetRaw);
3069
2710
  const recursive = hasFlag("--recursive") || hasFlag("-r");
3070
2711
 
3071
- const parentRows = await listConvexTreeAt(runtime, apiKey, parentOfPath(target));
2712
+ const parentRows = await listConvexTreeAt(
2713
+ runtime,
2714
+ apiKey,
2715
+ parentOfPath(target),
2716
+ );
3072
2717
  const self = parentRows.find((row) => row.path === target);
3073
2718
  if (!self) {
3074
2719
  if (hasFlag("--json")) {
@@ -3615,6 +3260,25 @@ async function main() {
3615
3260
  return;
3616
3261
  }
3617
3262
 
3263
+ if (command === "apps") {
3264
+ // `dench apps` is an alias for `dench tool status`. Both speak
3265
+ // straight HTTP to the Dench Cloud Gateway with DENCH_API_KEY —
3266
+ // mirrors the denchclaw pattern. No Convex session involved.
3267
+ await runToolCommand({ args: ["status"], jsonOutput: json });
3268
+ return;
3269
+ }
3270
+
3271
+ if (command === "tool") {
3272
+ // `dench tool {status,connect,search,run,disconnect}` is the model-facing
3273
+ // composio surface and only needs DENCH_API_KEY (Bearer) on the
3274
+ // gateway. Bypasses requireSessionRuntime entirely so it works in
3275
+ // every sandbox / API-key context.
3276
+ const subArgs = stripRuntimeFlags(args.slice(args.indexOf("tool") + 1));
3277
+ const filteredSubArgs = subArgs.filter((a) => a !== "--json");
3278
+ await runToolCommand({ args: filteredSubArgs, jsonOutput: json });
3279
+ return;
3280
+ }
3281
+
3618
3282
  if (command === "chat") {
3619
3283
  const subArgs = args.slice(args.indexOf("chat") + 1);
3620
3284
  const sub = subArgs[0];
@@ -3865,11 +3529,6 @@ async function main() {
3865
3529
  return;
3866
3530
  }
3867
3531
 
3868
- if (command === "apps") {
3869
- await printToolStatus(runtime);
3870
- return;
3871
- }
3872
-
3873
3532
  if (command === "context") {
3874
3533
  const context = await buildContext(runtime);
3875
3534
  print(json ? context : formatContext(context));
@@ -4022,9 +3681,7 @@ async function main() {
4022
3681
  return;
4023
3682
  }
4024
3683
  if (runtime.mode === "apiKey") {
4025
- throw loginRequiredError(
4026
- "dench tasks (agent-scoped task list)",
4027
- );
3684
+ throw loginRequiredError("dench tasks (agent-scoped task list)");
4028
3685
  }
4029
3686
  const data = await devOverview(runtime);
4030
3687
  print(data.tasks);
@@ -4314,79 +3971,6 @@ async function main() {
4314
3971
  return;
4315
3972
  }
4316
3973
 
4317
- if (command === "tool") {
4318
- const sessionRuntime = requireSessionRuntime(runtime, "dench tool");
4319
-
4320
- if (subcommand === "status") {
4321
- await printToolStatus(sessionRuntime, positional(2));
4322
- return;
4323
- }
4324
-
4325
- if (subcommand === "connect") {
4326
- const toolkit = positional(2);
4327
- if (!toolkit) throw new Error("Missing toolkit");
4328
- const result = await sessionRuntime.client.action(
4329
- api.functions.agentToolsNode.agentToolConnect,
4330
- {
4331
- sessionToken: sessionRuntime.sessionToken,
4332
- toolkit,
4333
- callbackUrl: option("--callback-url"),
4334
- },
4335
- );
4336
- print(
4337
- json ? withToolConnectSummary(result) : formatToolConnectResult(result),
4338
- );
4339
- return;
4340
- }
4341
-
4342
- if (subcommand === "search") {
4343
- const query = positional(2);
4344
- if (!query) throw new Error("Missing search query");
4345
- try {
4346
- printToolSearchResult(
4347
- await sessionRuntime.client.action(
4348
- api.functions.agentToolsNode.agentToolSearch,
4349
- {
4350
- sessionToken: sessionRuntime.sessionToken,
4351
- query,
4352
- toolkit: option("--toolkit"),
4353
- limit: parseNumberOption("--limit"),
4354
- },
4355
- ),
4356
- );
4357
- } catch (error) {
4358
- const unavailable = toolGatewayUnavailable(error, {
4359
- query,
4360
- toolkit: option("--toolkit"),
4361
- });
4362
- if (!unavailable) throw error;
4363
- print(json ? unavailable : formatToolUnavailable(unavailable));
4364
- }
4365
- return;
4366
- }
4367
-
4368
- if (subcommand === "run") {
4369
- const toolSlug = positional(2);
4370
- if (!toolSlug) throw new Error("Missing Composio tool slug");
4371
- printToolRunResult(
4372
- sessionRuntime,
4373
- await sessionRuntime.client.action(
4374
- api.functions.agentToolsNode.agentToolRun,
4375
- {
4376
- sessionToken: sessionRuntime.sessionToken,
4377
- toolSlug,
4378
- arguments: parseJsonObjectOption("--args"),
4379
- connectedAccountId: option("--account"),
4380
- approvalId: option("--approval") as never,
4381
- },
4382
- ),
4383
- );
4384
- return;
4385
- }
4386
-
4387
- throw new Error(`Unknown tool command: ${filteredArgs.join(" ")}`);
4388
- }
4389
-
4390
3974
  if (
4391
3975
  command === "approval" &&
4392
3976
  (subcommand === "approve" || subcommand === "reject")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dench.com/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Dench agent workspace CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -21,6 +21,7 @@
21
21
  "cron.ts",
22
22
  "search.ts",
23
23
  "image.ts",
24
+ "tools.ts",
24
25
  "agentKind.ts",
25
26
  "host.ts",
26
27
  "openUrl.ts",