@customclaw/composio 0.0.5 → 0.0.7
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 +22 -5
- package/dist/cli.js +53 -3
- package/dist/client.d.ts +34 -2
- package/dist/client.js +489 -46
- package/dist/client.test.js +322 -0
- package/dist/config.js +15 -2
- package/dist/index.js +0 -16
- package/dist/tools/connections.d.ts +20 -2
- package/dist/tools/connections.js +42 -5
- package/dist/tools/execute.d.ts +2 -0
- package/dist/tools/execute.js +8 -3
- package/dist/tools/search.js +3 -2
- package/dist/types.d.ts +10 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ openclaw plugins install @customclaw/composio
|
|
|
22
22
|
"enabled": true,
|
|
23
23
|
"config": {
|
|
24
24
|
"apiKey": "your-api-key",
|
|
25
|
+
"defaultUserId": "my-app-user-123",
|
|
25
26
|
"allowedToolkits": ["gmail", "sentry"]
|
|
26
27
|
}
|
|
27
28
|
}
|
|
@@ -41,16 +42,19 @@ The plugin gives your agent two tools:
|
|
|
41
42
|
- `composio_execute_tool` — runs a Composio action (e.g. `GMAIL_FETCH_EMAILS`, `SENTRY_LIST_ISSUES`)
|
|
42
43
|
- `composio_manage_connections` — checks connection status and generates OAuth links when a toolkit isn't connected yet
|
|
43
44
|
|
|
45
|
+
`composio_execute_tool` also accepts optional `user_id` and `connected_account_id` fields for deterministic account selection when multiple accounts exist.
|
|
46
|
+
|
|
44
47
|
The agent handles the rest. Ask it to "check my latest emails" and it will call the right tool, prompt you to connect Gmail if needed, and fetch the results.
|
|
45
48
|
|
|
46
49
|
## CLI
|
|
47
50
|
|
|
48
51
|
```bash
|
|
49
|
-
openclaw composio list
|
|
50
|
-
openclaw composio status
|
|
51
|
-
openclaw composio
|
|
52
|
-
openclaw composio
|
|
53
|
-
openclaw composio
|
|
52
|
+
openclaw composio list --user-id user-123 # list available toolkits for a user scope
|
|
53
|
+
openclaw composio status [toolkit] --user-id user-123 # check connection status in a user scope
|
|
54
|
+
openclaw composio accounts [toolkit] # inspect connected accounts (id/user_id/status)
|
|
55
|
+
openclaw composio connect gmail --user-id user-123 # open OAuth link for a specific user scope
|
|
56
|
+
openclaw composio disconnect gmail --user-id user-123 # remove a connection in that user scope
|
|
57
|
+
openclaw composio search "send email" --user-id user-123
|
|
54
58
|
```
|
|
55
59
|
|
|
56
60
|
## Config options
|
|
@@ -58,9 +62,22 @@ openclaw composio search "send email" # find tool slugs
|
|
|
58
62
|
| Key | Description |
|
|
59
63
|
|-----|-------------|
|
|
60
64
|
| `apiKey` | Composio API key (required) |
|
|
65
|
+
| `defaultUserId` | Default Composio `user_id` scope when `--user-id` is not provided |
|
|
61
66
|
| `allowedToolkits` | Only allow these toolkits (e.g. `["gmail", "sentry"]`) |
|
|
62
67
|
| `blockedToolkits` | Block specific toolkits |
|
|
63
68
|
|
|
69
|
+
## User ID Scope (Important)
|
|
70
|
+
|
|
71
|
+
Composio connections are scoped by `user_id`. If a toolkit is connected in the dashboard
|
|
72
|
+
under one user ID but OpenClaw checks another (for example `default`), status and execution
|
|
73
|
+
may look disconnected.
|
|
74
|
+
|
|
75
|
+
Tips:
|
|
76
|
+
|
|
77
|
+
- Set `defaultUserId` in plugin config for your app's primary identity.
|
|
78
|
+
- Use `--user-id` explicitly when checking status/connect/disconnect.
|
|
79
|
+
- Use `openclaw composio accounts <toolkit>` to discover which `user_id` owns active connections.
|
|
80
|
+
|
|
64
81
|
## Updating
|
|
65
82
|
|
|
66
83
|
```bash
|
package/dist/cli.js
CHANGED
|
@@ -7,13 +7,14 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
7
7
|
composio
|
|
8
8
|
.command("list")
|
|
9
9
|
.description("List available Composio toolkits")
|
|
10
|
-
.
|
|
10
|
+
.option("-u, --user-id <userId>", "User ID for session scoping")
|
|
11
|
+
.action(async (options) => {
|
|
11
12
|
if (!config.enabled) {
|
|
12
13
|
logger.error("Composio plugin is disabled");
|
|
13
14
|
return;
|
|
14
15
|
}
|
|
15
16
|
try {
|
|
16
|
-
const toolkits = await client.listToolkits();
|
|
17
|
+
const toolkits = await client.listToolkits(options.userId);
|
|
17
18
|
console.log("\nAvailable Composio Toolkits:");
|
|
18
19
|
console.log("─".repeat(40));
|
|
19
20
|
for (const toolkit of toolkits.sort()) {
|
|
@@ -50,12 +51,59 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
50
51
|
console.log(` ${icon} ${status.toolkit}: ${state}`);
|
|
51
52
|
}
|
|
52
53
|
}
|
|
54
|
+
if (toolkit && statuses.length === 1 && !statuses[0]?.connected) {
|
|
55
|
+
const currentUserId = options.userId || config.defaultUserId || "default";
|
|
56
|
+
const activeUserIds = await client.findActiveUserIdsForToolkit(toolkit);
|
|
57
|
+
const otherUserIds = activeUserIds.filter((uid) => uid !== currentUserId);
|
|
58
|
+
if (otherUserIds.length > 0) {
|
|
59
|
+
console.log(`\n Hint: '${toolkit}' is connected under other user_id(s): ${otherUserIds.join(", ")}`);
|
|
60
|
+
console.log(` Try: openclaw composio status ${toolkit} --user-id <user-id>`);
|
|
61
|
+
console.log(` Discover accounts: openclaw composio accounts ${toolkit}`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
53
64
|
console.log();
|
|
54
65
|
}
|
|
55
66
|
catch (err) {
|
|
56
67
|
logger.error(`Failed to get status: ${err instanceof Error ? err.message : String(err)}`);
|
|
57
68
|
}
|
|
58
69
|
});
|
|
70
|
+
// openclaw composio accounts [toolkit]
|
|
71
|
+
composio
|
|
72
|
+
.command("accounts [toolkit]")
|
|
73
|
+
.description("List connected accounts (account IDs, user IDs, and statuses)")
|
|
74
|
+
.option("-u, --user-id <userId>", "Filter by user ID")
|
|
75
|
+
.option("-s, --statuses <statuses>", "Comma-separated statuses (default: ACTIVE)", "ACTIVE")
|
|
76
|
+
.action(async (toolkit, options) => {
|
|
77
|
+
if (!config.enabled) {
|
|
78
|
+
logger.error("Composio plugin is disabled");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const statuses = String(options.statuses || "")
|
|
83
|
+
.split(",")
|
|
84
|
+
.map(s => s.trim())
|
|
85
|
+
.filter(Boolean);
|
|
86
|
+
const accounts = await client.listConnectedAccounts({
|
|
87
|
+
toolkits: toolkit ? [toolkit] : undefined,
|
|
88
|
+
userIds: options.userId ? [options.userId] : undefined,
|
|
89
|
+
statuses: statuses.length > 0 ? statuses : ["ACTIVE"],
|
|
90
|
+
});
|
|
91
|
+
console.log("\nComposio Connected Accounts:");
|
|
92
|
+
console.log("─".repeat(80));
|
|
93
|
+
if (accounts.length === 0) {
|
|
94
|
+
console.log(" No connected accounts found");
|
|
95
|
+
console.log();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (const account of accounts) {
|
|
99
|
+
console.log(` ${account.id} | toolkit=${account.toolkit} | status=${account.status || "unknown"} | user_id=${account.userId || "hidden"}`);
|
|
100
|
+
}
|
|
101
|
+
console.log(`\nTotal: ${accounts.length} connected accounts\n`);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
logger.error(`Failed to list connected accounts: ${err instanceof Error ? err.message : String(err)}`);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
59
107
|
// openclaw composio connect <toolkit>
|
|
60
108
|
composio
|
|
61
109
|
.command("connect <toolkit>")
|
|
@@ -67,7 +115,9 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
67
115
|
return;
|
|
68
116
|
}
|
|
69
117
|
try {
|
|
118
|
+
const currentUserId = options.userId || config.defaultUserId || "default";
|
|
70
119
|
console.log(`\nInitiating connection to ${toolkit}...`);
|
|
120
|
+
console.log(`Using user_id: ${currentUserId}`);
|
|
71
121
|
const result = await client.createConnection(toolkit, options.userId);
|
|
72
122
|
if ("error" in result) {
|
|
73
123
|
logger.error(`Failed to create connection: ${result.error}`);
|
|
@@ -77,7 +127,7 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
77
127
|
console.log("─".repeat(40));
|
|
78
128
|
console.log(result.authUrl);
|
|
79
129
|
console.log("\nOpen this URL in your browser to authenticate.");
|
|
80
|
-
console.log(
|
|
130
|
+
console.log(`After authentication, run 'openclaw composio status ${toolkit} --user-id ${currentUserId}' to verify.\n`);
|
|
81
131
|
// Try to open URL in browser
|
|
82
132
|
try {
|
|
83
133
|
const { exec } = await import("node:child_process");
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ComposioConfig, ToolSearchResult, ToolExecutionResult, ConnectionStatus } from "./types.js";
|
|
1
|
+
import type { ComposioConfig, ToolSearchResult, ToolExecutionResult, ConnectionStatus, ConnectedAccountSummary } from "./types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Composio client wrapper using Tool Router pattern
|
|
4
4
|
*/
|
|
@@ -14,7 +14,9 @@ export declare class ComposioClient {
|
|
|
14
14
|
/**
|
|
15
15
|
* Get or create a Tool Router session for a user
|
|
16
16
|
*/
|
|
17
|
+
private makeSessionCacheKey;
|
|
17
18
|
private getSession;
|
|
19
|
+
private clearUserSessionCache;
|
|
18
20
|
/**
|
|
19
21
|
* Check if a toolkit is allowed based on config
|
|
20
22
|
*/
|
|
@@ -34,11 +36,41 @@ export declare class ComposioClient {
|
|
|
34
36
|
/**
|
|
35
37
|
* Execute a single tool using COMPOSIO_MULTI_EXECUTE_TOOL
|
|
36
38
|
*/
|
|
37
|
-
executeTool(toolSlug: string, args: Record<string, unknown>, userId?: string): Promise<ToolExecutionResult>;
|
|
39
|
+
executeTool(toolSlug: string, args: Record<string, unknown>, userId?: string, connectedAccountId?: string): Promise<ToolExecutionResult>;
|
|
40
|
+
private tryExecutionRecovery;
|
|
41
|
+
private tryDirectExecutionFallback;
|
|
42
|
+
private executeDirectTool;
|
|
43
|
+
private tryHintedIdentifierRetry;
|
|
44
|
+
private shouldFallbackToDirectExecution;
|
|
45
|
+
private shouldRetryFromServerHint;
|
|
46
|
+
private extractServerHintLiteral;
|
|
47
|
+
private buildRetryArgsFromHint;
|
|
48
|
+
private extractSingleMissingField;
|
|
49
|
+
private buildCombinedErrorText;
|
|
50
|
+
private extractNestedMetaError;
|
|
51
|
+
private resolveConnectedAccountForExecution;
|
|
38
52
|
/**
|
|
39
53
|
* Get connection status for toolkits using session.toolkits()
|
|
40
54
|
*/
|
|
41
55
|
getConnectionStatus(toolkits?: string[], userId?: string): Promise<ConnectionStatus[]>;
|
|
56
|
+
private getToolkitStateMap;
|
|
57
|
+
private getActiveConnectedAccountToolkits;
|
|
58
|
+
private normalizeStatuses;
|
|
59
|
+
/**
|
|
60
|
+
* List connected accounts with optional filters.
|
|
61
|
+
* Uses raw API first to preserve user_id in responses, then falls back to SDK-normalized output.
|
|
62
|
+
*/
|
|
63
|
+
listConnectedAccounts(options?: {
|
|
64
|
+
toolkits?: string[];
|
|
65
|
+
userIds?: string[];
|
|
66
|
+
statuses?: string[];
|
|
67
|
+
}): Promise<ConnectedAccountSummary[]>;
|
|
68
|
+
/**
|
|
69
|
+
* Find user IDs that have an active connected account for a toolkit.
|
|
70
|
+
*/
|
|
71
|
+
findActiveUserIdsForToolkit(toolkit: string): Promise<string[]>;
|
|
72
|
+
private listConnectedAccountsRaw;
|
|
73
|
+
private listConnectedAccountsFallback;
|
|
42
74
|
/**
|
|
43
75
|
* Create an auth connection for a toolkit using session.authorize()
|
|
44
76
|
*/
|