@customclaw/composio 0.0.7 → 0.0.8
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 +42 -3
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +268 -37
- package/dist/client.d.ts +6 -0
- package/dist/client.js +224 -46
- package/dist/config.d.ts +49 -0
- package/dist/config.js +44 -20
- package/dist/index.d.ts +20 -0
- package/dist/index.js +21 -14
- package/dist/types.d.ts +5 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +76 -0
- package/openclaw.plugin.json +39 -0
- package/package.json +3 -2
- package/dist/client.test.d.ts +0 -1
- package/dist/client.test.js +0 -506
package/README.md
CHANGED
|
@@ -12,7 +12,13 @@ openclaw plugins install @customclaw/composio
|
|
|
12
12
|
|
|
13
13
|
1. Get an API key from [platform.composio.dev/settings](https://platform.composio.dev/settings)
|
|
14
14
|
|
|
15
|
-
2.
|
|
15
|
+
2. Run the guided setup:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
openclaw composio setup
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or add manually to `~/.openclaw/openclaw.json`:
|
|
16
22
|
|
|
17
23
|
```json
|
|
18
24
|
{
|
|
@@ -31,14 +37,24 @@ openclaw plugins install @customclaw/composio
|
|
|
31
37
|
}
|
|
32
38
|
```
|
|
33
39
|
|
|
34
|
-
|
|
40
|
+
You can also set `COMPOSIO_API_KEY` as an environment variable.
|
|
35
41
|
|
|
36
42
|
3. Restart the gateway.
|
|
37
43
|
|
|
44
|
+
If you upgraded from an older config shape where keys like `defaultUserId` or `allowedToolkits`
|
|
45
|
+
were placed directly under `plugins.entries.composio`, rerun:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
openclaw composio setup
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Legacy mixed entry-level config keys are now rejected at runtime.
|
|
52
|
+
|
|
38
53
|
## What it does
|
|
39
54
|
|
|
40
|
-
The plugin gives your agent
|
|
55
|
+
The plugin gives your agent three tools:
|
|
41
56
|
|
|
57
|
+
- `composio_search_tools` — discovers relevant Composio actions from natural-language intent
|
|
42
58
|
- `composio_execute_tool` — runs a Composio action (e.g. `GMAIL_FETCH_EMAILS`, `SENTRY_LIST_ISSUES`)
|
|
43
59
|
- `composio_manage_connections` — checks connection status and generates OAuth links when a toolkit isn't connected yet
|
|
44
60
|
|
|
@@ -49,6 +65,7 @@ The agent handles the rest. Ask it to "check my latest emails" and it will call
|
|
|
49
65
|
## CLI
|
|
50
66
|
|
|
51
67
|
```bash
|
|
68
|
+
openclaw composio setup # interactive setup for ~/.openclaw/openclaw.json
|
|
52
69
|
openclaw composio list --user-id user-123 # list available toolkits for a user scope
|
|
53
70
|
openclaw composio status [toolkit] --user-id user-123 # check connection status in a user scope
|
|
54
71
|
openclaw composio accounts [toolkit] # inspect connected accounts (id/user_id/status)
|
|
@@ -65,6 +82,10 @@ openclaw composio search "send email" --user-id user-123
|
|
|
65
82
|
| `defaultUserId` | Default Composio `user_id` scope when `--user-id` is not provided |
|
|
66
83
|
| `allowedToolkits` | Only allow these toolkits (e.g. `["gmail", "sentry"]`) |
|
|
67
84
|
| `blockedToolkits` | Block specific toolkits |
|
|
85
|
+
| `readOnlyMode` | Blocks likely-destructive actions by token matching (delete/update/create/send/etc.); use `allowedToolSlugs` to override safe exceptions |
|
|
86
|
+
| `sessionTags` | Optional Tool Router tags (`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`) |
|
|
87
|
+
| `allowedToolSlugs` | Optional explicit allowlist of UPPERCASE tool slugs |
|
|
88
|
+
| `blockedToolSlugs` | Explicit denylist of UPPERCASE tool slugs |
|
|
68
89
|
|
|
69
90
|
## User ID Scope (Important)
|
|
70
91
|
|
|
@@ -95,6 +116,24 @@ npm run build
|
|
|
95
116
|
npm test
|
|
96
117
|
```
|
|
97
118
|
|
|
119
|
+
### Live Integration Tests (Optional)
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
COMPOSIO_LIVE_TEST=1 \
|
|
123
|
+
COMPOSIO_API_KEY=your_key \
|
|
124
|
+
npm run test:live
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Optional env vars for execution/disconnect paths:
|
|
128
|
+
|
|
129
|
+
- `COMPOSIO_LIVE_USER_ID`
|
|
130
|
+
- `COMPOSIO_LIVE_TOOLKIT` (default: `gmail`)
|
|
131
|
+
- `COMPOSIO_LIVE_TOOL_SLUG`
|
|
132
|
+
- `COMPOSIO_LIVE_TOOL_ARGS` (JSON)
|
|
133
|
+
- `COMPOSIO_LIVE_CONNECTED_ACCOUNT_ID`
|
|
134
|
+
- `COMPOSIO_LIVE_EXPECT_EXECUTE_SUCCESS=1`
|
|
135
|
+
- `COMPOSIO_LIVE_ALLOW_DISCONNECT=1` (destructive, opt-in)
|
|
136
|
+
|
|
98
137
|
## Acknowledgments
|
|
99
138
|
|
|
100
139
|
Based on the Composio plugin from [openclaw-composio](https://github.com/ComposioHQ/openclaw-composio) by ComposioHQ. See [THIRD-PARTY-NOTICES](./THIRD-PARTY-NOTICES).
|
package/dist/cli.d.ts
CHANGED
|
@@ -7,12 +7,12 @@ interface PluginLogger {
|
|
|
7
7
|
}
|
|
8
8
|
interface RegisterCliOptions {
|
|
9
9
|
program: any;
|
|
10
|
-
|
|
10
|
+
getClient?: () => ComposioClient;
|
|
11
11
|
config: ComposioConfig;
|
|
12
12
|
logger: PluginLogger;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Register Composio CLI commands
|
|
16
16
|
*/
|
|
17
|
-
export declare function registerComposioCli({ program,
|
|
17
|
+
export declare function registerComposioCli({ program, getClient, config, logger }: RegisterCliOptions): void;
|
|
18
18
|
export {};
|
package/dist/cli.js
CHANGED
|
@@ -1,20 +1,253 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import process from "node:process";
|
|
4
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
5
|
+
import { createInterface } from "node:readline/promises";
|
|
6
|
+
import { isRecord, normalizeToolkitList, normalizeToolkitSlug, stripLegacyFlatConfigKeys, } from "./utils.js";
|
|
7
|
+
const DEFAULT_OPENCLAW_CONFIG_PATH = path.join(os.homedir(), ".openclaw", "openclaw.json");
|
|
8
|
+
const COMPOSIO_PLUGIN_ID = "composio";
|
|
9
|
+
function parseCsvToolkits(value) {
|
|
10
|
+
if (!value)
|
|
11
|
+
return undefined;
|
|
12
|
+
return normalizeToolkitList(value.split(","));
|
|
13
|
+
}
|
|
14
|
+
function parseBooleanLike(value) {
|
|
15
|
+
const raw = value.trim().toLowerCase();
|
|
16
|
+
if (!raw)
|
|
17
|
+
return undefined;
|
|
18
|
+
if (["1", "true", "yes", "y"].includes(raw))
|
|
19
|
+
return true;
|
|
20
|
+
if (["0", "false", "no", "n"].includes(raw))
|
|
21
|
+
return false;
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
function normalizePluginIdList(value) {
|
|
25
|
+
if (!Array.isArray(value))
|
|
26
|
+
return undefined;
|
|
27
|
+
const normalized = value
|
|
28
|
+
.filter((item) => typeof item === "string")
|
|
29
|
+
.map((item) => item.trim())
|
|
30
|
+
.filter(Boolean);
|
|
31
|
+
return Array.from(new Set(normalized));
|
|
32
|
+
}
|
|
33
|
+
async function readOpenClawConfig(configPath) {
|
|
34
|
+
try {
|
|
35
|
+
const raw = await readFile(configPath, "utf8");
|
|
36
|
+
const parsed = JSON.parse(raw);
|
|
37
|
+
return isRecord(parsed) ? parsed : {};
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
const nodeErr = err;
|
|
41
|
+
if (nodeErr?.code === "ENOENT")
|
|
42
|
+
return {};
|
|
43
|
+
throw err;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
1
46
|
/**
|
|
2
47
|
* Register Composio CLI commands
|
|
3
48
|
*/
|
|
4
|
-
export function registerComposioCli({ program,
|
|
49
|
+
export function registerComposioCli({ program, getClient, config, logger }) {
|
|
5
50
|
const composio = program.command("composio").description("Manage Composio Tool Router connections");
|
|
51
|
+
const requireClient = () => {
|
|
52
|
+
if (!config.enabled) {
|
|
53
|
+
logger.error("Composio plugin is disabled");
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
if (!getClient) {
|
|
57
|
+
logger.error("Composio API key is not configured. Run 'openclaw composio setup' first.");
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
return getClient();
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
logger.error(`Failed to initialize Composio client: ${err instanceof Error ? err.message : String(err)}`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
// openclaw composio setup
|
|
69
|
+
composio
|
|
70
|
+
.command("setup")
|
|
71
|
+
.description("Create or update Composio config in ~/.openclaw/openclaw.json")
|
|
72
|
+
.option("-c, --config-path <path>", "OpenClaw config file path", DEFAULT_OPENCLAW_CONFIG_PATH)
|
|
73
|
+
.option("--api-key <apiKey>", "Composio API key")
|
|
74
|
+
.option("--default-user-id <userId>", "Default user ID for Composio user scoping")
|
|
75
|
+
.option("--allowed-toolkits <toolkits>", "Comma-separated allowed toolkit slugs")
|
|
76
|
+
.option("--blocked-toolkits <toolkits>", "Comma-separated blocked toolkit slugs")
|
|
77
|
+
.option("--read-only <enabled>", "Enable read-only mode (true/false)")
|
|
78
|
+
.option("-y, --yes", "Skip prompts and use defaults/provided values")
|
|
79
|
+
.action(async (options) => {
|
|
80
|
+
const configPath = path.resolve(options.configPath || DEFAULT_OPENCLAW_CONFIG_PATH);
|
|
81
|
+
try {
|
|
82
|
+
const openClawConfig = await readOpenClawConfig(configPath);
|
|
83
|
+
const plugins = isRecord(openClawConfig.plugins) ? { ...openClawConfig.plugins } : {};
|
|
84
|
+
let updatedPluginSystemEnabled = false;
|
|
85
|
+
let addedToAllowlist = false;
|
|
86
|
+
let removedFromDenylist = false;
|
|
87
|
+
if (plugins.enabled === false) {
|
|
88
|
+
plugins.enabled = true;
|
|
89
|
+
updatedPluginSystemEnabled = true;
|
|
90
|
+
}
|
|
91
|
+
const allow = normalizePluginIdList(plugins.allow);
|
|
92
|
+
if (allow && allow.length > 0) {
|
|
93
|
+
const hasExactComposio = allow.includes(COMPOSIO_PLUGIN_ID);
|
|
94
|
+
const normalizedAllow = allow.filter((id) => id.toLowerCase() !== COMPOSIO_PLUGIN_ID);
|
|
95
|
+
normalizedAllow.push(COMPOSIO_PLUGIN_ID);
|
|
96
|
+
plugins.allow = Array.from(new Set(normalizedAllow));
|
|
97
|
+
if (!hasExactComposio) {
|
|
98
|
+
addedToAllowlist = true;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const deny = normalizePluginIdList(plugins.deny);
|
|
102
|
+
if (deny && deny.length > 0) {
|
|
103
|
+
const filteredDeny = deny.filter((id) => id.toLowerCase() !== COMPOSIO_PLUGIN_ID);
|
|
104
|
+
if (filteredDeny.length !== deny.length) {
|
|
105
|
+
removedFromDenylist = true;
|
|
106
|
+
}
|
|
107
|
+
if (filteredDeny.length > 0) {
|
|
108
|
+
plugins.deny = filteredDeny;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
delete plugins.deny;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const entries = isRecord(plugins.entries) ? { ...plugins.entries } : {};
|
|
115
|
+
const existingComposioEntry = isRecord(entries.composio) ? { ...entries.composio } : {};
|
|
116
|
+
const existingComposioConfig = isRecord(existingComposioEntry.config)
|
|
117
|
+
? { ...existingComposioEntry.config }
|
|
118
|
+
: {};
|
|
119
|
+
let apiKey = String(options.apiKey || "").trim() ||
|
|
120
|
+
String(existingComposioConfig.apiKey || "").trim() ||
|
|
121
|
+
String(config.apiKey || "").trim() ||
|
|
122
|
+
String(process.env.COMPOSIO_API_KEY || "").trim();
|
|
123
|
+
let defaultUserId = String(options.defaultUserId || "").trim() ||
|
|
124
|
+
String(existingComposioConfig.defaultUserId || "").trim() ||
|
|
125
|
+
String(config.defaultUserId || "").trim();
|
|
126
|
+
let allowedToolkits = parseCsvToolkits(options.allowedToolkits) ||
|
|
127
|
+
(Array.isArray(existingComposioConfig.allowedToolkits)
|
|
128
|
+
? normalizeToolkitList(existingComposioConfig.allowedToolkits)
|
|
129
|
+
: normalizeToolkitList(config.allowedToolkits));
|
|
130
|
+
let blockedToolkits = parseCsvToolkits(options.blockedToolkits) ||
|
|
131
|
+
(Array.isArray(existingComposioConfig.blockedToolkits)
|
|
132
|
+
? normalizeToolkitList(existingComposioConfig.blockedToolkits)
|
|
133
|
+
: normalizeToolkitList(config.blockedToolkits));
|
|
134
|
+
let readOnlyMode = typeof existingComposioConfig.readOnlyMode === "boolean"
|
|
135
|
+
? existingComposioConfig.readOnlyMode
|
|
136
|
+
: Boolean(config.readOnlyMode);
|
|
137
|
+
if (options.readOnly !== undefined) {
|
|
138
|
+
const parsedReadOnly = parseBooleanLike(options.readOnly);
|
|
139
|
+
if (parsedReadOnly === undefined) {
|
|
140
|
+
logger.error("Invalid value for --read-only. Expected one of: true/false/yes/no/1/0.");
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
readOnlyMode = parsedReadOnly;
|
|
144
|
+
}
|
|
145
|
+
if (!options.yes) {
|
|
146
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
147
|
+
try {
|
|
148
|
+
const apiKeyPrompt = await rl.question(`Composio API key${apiKey ? " [configured]" : ""}: `);
|
|
149
|
+
if (apiKeyPrompt.trim())
|
|
150
|
+
apiKey = apiKeyPrompt.trim();
|
|
151
|
+
const defaultUserPrompt = await rl.question(`Default user ID${defaultUserId ? ` [${defaultUserId}]` : " (optional)"}: `);
|
|
152
|
+
if (defaultUserPrompt.trim())
|
|
153
|
+
defaultUserId = defaultUserPrompt.trim();
|
|
154
|
+
const allowedDefault = allowedToolkits && allowedToolkits.length > 0
|
|
155
|
+
? ` [${allowedToolkits.join(",")}]`
|
|
156
|
+
: " (optional)";
|
|
157
|
+
const allowedPrompt = await rl.question(`Allowed toolkits${allowedDefault}: `);
|
|
158
|
+
if (allowedPrompt.trim()) {
|
|
159
|
+
allowedToolkits = parseCsvToolkits(allowedPrompt) || [];
|
|
160
|
+
}
|
|
161
|
+
const blockedDefault = blockedToolkits && blockedToolkits.length > 0
|
|
162
|
+
? ` [${blockedToolkits.join(",")}]`
|
|
163
|
+
: " (optional)";
|
|
164
|
+
const blockedPrompt = await rl.question(`Blocked toolkits${blockedDefault}: `);
|
|
165
|
+
if (blockedPrompt.trim()) {
|
|
166
|
+
blockedToolkits = parseCsvToolkits(blockedPrompt) || [];
|
|
167
|
+
}
|
|
168
|
+
const readOnlyPrompt = await rl.question(`Enable read-only safety mode? (y/N) [${readOnlyMode ? "Y" : "N"}]: `);
|
|
169
|
+
const parsedReadOnlyPrompt = parseBooleanLike(readOnlyPrompt);
|
|
170
|
+
if (parsedReadOnlyPrompt !== undefined) {
|
|
171
|
+
readOnlyMode = parsedReadOnlyPrompt;
|
|
172
|
+
}
|
|
173
|
+
else if (readOnlyPrompt.trim()) {
|
|
174
|
+
logger.warn("Invalid read-only input. Keeping existing readOnlyMode value.");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
rl.close();
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (!apiKey) {
|
|
182
|
+
logger.error("Composio API key is required. Provide --api-key or set COMPOSIO_API_KEY.");
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const mergedComposioConfig = {
|
|
186
|
+
...existingComposioConfig,
|
|
187
|
+
apiKey,
|
|
188
|
+
readOnlyMode,
|
|
189
|
+
};
|
|
190
|
+
if (defaultUserId) {
|
|
191
|
+
mergedComposioConfig.defaultUserId = defaultUserId;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
delete mergedComposioConfig.defaultUserId;
|
|
195
|
+
}
|
|
196
|
+
if (allowedToolkits && allowedToolkits.length > 0) {
|
|
197
|
+
mergedComposioConfig.allowedToolkits = allowedToolkits;
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
delete mergedComposioConfig.allowedToolkits;
|
|
201
|
+
}
|
|
202
|
+
if (blockedToolkits && blockedToolkits.length > 0) {
|
|
203
|
+
mergedComposioConfig.blockedToolkits = blockedToolkits;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
delete mergedComposioConfig.blockedToolkits;
|
|
207
|
+
}
|
|
208
|
+
entries.composio = {
|
|
209
|
+
...stripLegacyFlatConfigKeys(existingComposioEntry),
|
|
210
|
+
enabled: true,
|
|
211
|
+
config: mergedComposioConfig,
|
|
212
|
+
};
|
|
213
|
+
plugins.entries = entries;
|
|
214
|
+
openClawConfig.plugins = plugins;
|
|
215
|
+
await mkdir(path.dirname(configPath), { recursive: true });
|
|
216
|
+
await writeFile(configPath, `${JSON.stringify(openClawConfig, null, 2)}\n`, "utf8");
|
|
217
|
+
console.log("\nComposio setup saved.");
|
|
218
|
+
console.log("─".repeat(40));
|
|
219
|
+
console.log(`Config: ${configPath}`);
|
|
220
|
+
console.log(`defaultUserId: ${defaultUserId || "default"}`);
|
|
221
|
+
console.log(`readOnlyMode: ${readOnlyMode ? "enabled" : "disabled"}`);
|
|
222
|
+
if (updatedPluginSystemEnabled) {
|
|
223
|
+
console.log("plugins.enabled: set to true");
|
|
224
|
+
}
|
|
225
|
+
if (addedToAllowlist) {
|
|
226
|
+
console.log("plugins.allow: added 'composio'");
|
|
227
|
+
}
|
|
228
|
+
if (removedFromDenylist) {
|
|
229
|
+
console.log("plugins.deny: removed 'composio'");
|
|
230
|
+
}
|
|
231
|
+
console.log("\nNext steps:");
|
|
232
|
+
console.log(" 1) openclaw gateway restart");
|
|
233
|
+
console.log(" 2) openclaw composio status --user-id <user-id>");
|
|
234
|
+
console.log();
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
logger.error(`Failed to run setup: ${err instanceof Error ? err.message : String(err)}`);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
6
240
|
// openclaw composio list
|
|
7
241
|
composio
|
|
8
242
|
.command("list")
|
|
9
243
|
.description("List available Composio toolkits")
|
|
10
244
|
.option("-u, --user-id <userId>", "User ID for session scoping")
|
|
11
245
|
.action(async (options) => {
|
|
12
|
-
|
|
13
|
-
|
|
246
|
+
const composioClient = requireClient();
|
|
247
|
+
if (!composioClient)
|
|
14
248
|
return;
|
|
15
|
-
}
|
|
16
249
|
try {
|
|
17
|
-
const toolkits = await
|
|
250
|
+
const toolkits = await composioClient.listToolkits(options.userId);
|
|
18
251
|
console.log("\nAvailable Composio Toolkits:");
|
|
19
252
|
console.log("─".repeat(40));
|
|
20
253
|
for (const toolkit of toolkits.sort()) {
|
|
@@ -32,13 +265,13 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
32
265
|
.description("Check connection status for toolkits")
|
|
33
266
|
.option("-u, --user-id <userId>", "User ID for session scoping")
|
|
34
267
|
.action(async (toolkit, options) => {
|
|
35
|
-
|
|
36
|
-
|
|
268
|
+
const composioClient = requireClient();
|
|
269
|
+
if (!composioClient)
|
|
37
270
|
return;
|
|
38
|
-
}
|
|
39
271
|
try {
|
|
40
|
-
const
|
|
41
|
-
const
|
|
272
|
+
const toolkitSlug = toolkit ? normalizeToolkitSlug(toolkit) : undefined;
|
|
273
|
+
const toolkits = toolkitSlug ? [toolkitSlug] : undefined;
|
|
274
|
+
const statuses = await composioClient.getConnectionStatus(toolkits, options.userId);
|
|
42
275
|
console.log("\nComposio Connection Status:");
|
|
43
276
|
console.log("─".repeat(40));
|
|
44
277
|
if (statuses.length === 0) {
|
|
@@ -51,14 +284,14 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
51
284
|
console.log(` ${icon} ${status.toolkit}: ${state}`);
|
|
52
285
|
}
|
|
53
286
|
}
|
|
54
|
-
if (
|
|
287
|
+
if (toolkitSlug && statuses.length === 1 && !statuses[0]?.connected) {
|
|
55
288
|
const currentUserId = options.userId || config.defaultUserId || "default";
|
|
56
|
-
const activeUserIds = await
|
|
289
|
+
const activeUserIds = await composioClient.findActiveUserIdsForToolkit(toolkitSlug);
|
|
57
290
|
const otherUserIds = activeUserIds.filter((uid) => uid !== currentUserId);
|
|
58
291
|
if (otherUserIds.length > 0) {
|
|
59
|
-
console.log(`\n Hint: '${
|
|
60
|
-
console.log(` Try: openclaw composio status ${
|
|
61
|
-
console.log(` Discover accounts: openclaw composio accounts ${
|
|
292
|
+
console.log(`\n Hint: '${toolkitSlug}' is connected under other user_id(s): ${otherUserIds.join(", ")}`);
|
|
293
|
+
console.log(` Try: openclaw composio status ${toolkitSlug} --user-id <user-id>`);
|
|
294
|
+
console.log(` Discover accounts: openclaw composio accounts ${toolkitSlug}`);
|
|
62
295
|
}
|
|
63
296
|
}
|
|
64
297
|
console.log();
|
|
@@ -74,17 +307,16 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
74
307
|
.option("-u, --user-id <userId>", "Filter by user ID")
|
|
75
308
|
.option("-s, --statuses <statuses>", "Comma-separated statuses (default: ACTIVE)", "ACTIVE")
|
|
76
309
|
.action(async (toolkit, options) => {
|
|
77
|
-
|
|
78
|
-
|
|
310
|
+
const composioClient = requireClient();
|
|
311
|
+
if (!composioClient)
|
|
79
312
|
return;
|
|
80
|
-
}
|
|
81
313
|
try {
|
|
82
314
|
const statuses = String(options.statuses || "")
|
|
83
315
|
.split(",")
|
|
84
316
|
.map(s => s.trim())
|
|
85
317
|
.filter(Boolean);
|
|
86
|
-
const accounts = await
|
|
87
|
-
toolkits: toolkit ? [toolkit] : undefined,
|
|
318
|
+
const accounts = await composioClient.listConnectedAccounts({
|
|
319
|
+
toolkits: toolkit ? [normalizeToolkitSlug(toolkit)] : undefined,
|
|
88
320
|
userIds: options.userId ? [options.userId] : undefined,
|
|
89
321
|
statuses: statuses.length > 0 ? statuses : ["ACTIVE"],
|
|
90
322
|
});
|
|
@@ -110,15 +342,15 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
110
342
|
.description("Connect to a Composio toolkit (opens auth URL)")
|
|
111
343
|
.option("-u, --user-id <userId>", "User ID for session scoping")
|
|
112
344
|
.action(async (toolkit, options) => {
|
|
113
|
-
|
|
114
|
-
|
|
345
|
+
const composioClient = requireClient();
|
|
346
|
+
if (!composioClient)
|
|
115
347
|
return;
|
|
116
|
-
}
|
|
117
348
|
try {
|
|
349
|
+
const toolkitSlug = normalizeToolkitSlug(toolkit);
|
|
118
350
|
const currentUserId = options.userId || config.defaultUserId || "default";
|
|
119
|
-
console.log(`\nInitiating connection to ${
|
|
351
|
+
console.log(`\nInitiating connection to ${toolkitSlug}...`);
|
|
120
352
|
console.log(`Using user_id: ${currentUserId}`);
|
|
121
|
-
const result = await
|
|
353
|
+
const result = await composioClient.createConnection(toolkitSlug, options.userId);
|
|
122
354
|
if ("error" in result) {
|
|
123
355
|
logger.error(`Failed to create connection: ${result.error}`);
|
|
124
356
|
return;
|
|
@@ -127,7 +359,7 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
127
359
|
console.log("─".repeat(40));
|
|
128
360
|
console.log(result.authUrl);
|
|
129
361
|
console.log("\nOpen this URL in your browser to authenticate.");
|
|
130
|
-
console.log(`After authentication, run 'openclaw composio status ${
|
|
362
|
+
console.log(`After authentication, run 'openclaw composio status ${toolkitSlug} --user-id ${currentUserId}' to verify.\n`);
|
|
131
363
|
// Try to open URL in browser
|
|
132
364
|
try {
|
|
133
365
|
const { exec } = await import("node:child_process");
|
|
@@ -153,15 +385,15 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
153
385
|
.description("Disconnect from a Composio toolkit")
|
|
154
386
|
.option("-u, --user-id <userId>", "User ID for session scoping")
|
|
155
387
|
.action(async (toolkit, options) => {
|
|
156
|
-
|
|
157
|
-
|
|
388
|
+
const composioClient = requireClient();
|
|
389
|
+
if (!composioClient)
|
|
158
390
|
return;
|
|
159
|
-
}
|
|
160
391
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
392
|
+
const toolkitSlug = normalizeToolkitSlug(toolkit);
|
|
393
|
+
console.log(`\nDisconnecting from ${toolkitSlug}...`);
|
|
394
|
+
const result = await composioClient.disconnectToolkit(toolkitSlug, options.userId);
|
|
163
395
|
if (result.success) {
|
|
164
|
-
console.log(`Successfully disconnected from ${
|
|
396
|
+
console.log(`Successfully disconnected from ${toolkitSlug}\n`);
|
|
165
397
|
}
|
|
166
398
|
else {
|
|
167
399
|
logger.error(`Failed to disconnect: ${result.error}`);
|
|
@@ -179,14 +411,13 @@ export function registerComposioCli({ program, client, config, logger }) {
|
|
|
179
411
|
.option("-l, --limit <limit>", "Maximum results", "10")
|
|
180
412
|
.option("-u, --user-id <userId>", "User ID for session scoping")
|
|
181
413
|
.action(async (query, options) => {
|
|
182
|
-
|
|
183
|
-
|
|
414
|
+
const composioClient = requireClient();
|
|
415
|
+
if (!composioClient)
|
|
184
416
|
return;
|
|
185
|
-
}
|
|
186
417
|
try {
|
|
187
418
|
const limit = parseInt(options.limit, 10) || 10;
|
|
188
|
-
const toolkits = options.toolkit ? [options.toolkit] : undefined;
|
|
189
|
-
const results = await
|
|
419
|
+
const toolkits = options.toolkit ? [normalizeToolkitSlug(options.toolkit)] : undefined;
|
|
420
|
+
const results = await composioClient.searchTools(query, {
|
|
190
421
|
toolkits,
|
|
191
422
|
limit,
|
|
192
423
|
userId: options.userId,
|
package/dist/client.d.ts
CHANGED
|
@@ -15,12 +15,18 @@ export declare class ComposioClient {
|
|
|
15
15
|
* Get or create a Tool Router session for a user
|
|
16
16
|
*/
|
|
17
17
|
private makeSessionCacheKey;
|
|
18
|
+
private normalizeConnectedAccountsOverride;
|
|
19
|
+
private buildToolRouterBlockedToolsConfig;
|
|
20
|
+
private buildSessionConfig;
|
|
18
21
|
private getSession;
|
|
22
|
+
private shouldRetrySessionWithoutToolkitFilters;
|
|
19
23
|
private clearUserSessionCache;
|
|
20
24
|
/**
|
|
21
25
|
* Check if a toolkit is allowed based on config
|
|
22
26
|
*/
|
|
23
27
|
private isToolkitAllowed;
|
|
28
|
+
private isLikelyDestructiveToolSlug;
|
|
29
|
+
private getToolSlugRestrictionError;
|
|
24
30
|
/**
|
|
25
31
|
* Execute a Tool Router meta-tool
|
|
26
32
|
*/
|