@customclaw/composio 0.0.6 → 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 +64 -8
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +309 -28
- package/dist/client.d.ts +40 -2
- package/dist/client.js +687 -66
- package/dist/config.d.ts +49 -0
- package/dist/config.js +45 -8
- package/dist/index.d.ts +20 -0
- package/dist/index.js +21 -14
- package/dist/tools/connections.d.ts +20 -2
- package/dist/tools/connections.js +39 -28
- package/dist/tools/execute.d.ts +2 -0
- package/dist/tools/execute.js +5 -1
- package/dist/types.d.ts +15 -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 -226
package/dist/client.test.js
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi } from "vitest";
|
|
2
|
-
import { ComposioClient } from "./client.js";
|
|
3
|
-
import { parseComposioConfig } from "./config.js";
|
|
4
|
-
import { createComposioExecuteTool } from "./tools/execute.js";
|
|
5
|
-
import { createComposioConnectionsTool } from "./tools/connections.js";
|
|
6
|
-
// Mock the Composio SDK
|
|
7
|
-
vi.mock("@composio/core", () => ({
|
|
8
|
-
Composio: vi.fn().mockImplementation(() => ({
|
|
9
|
-
toolRouter: {
|
|
10
|
-
create: vi.fn().mockResolvedValue({
|
|
11
|
-
sessionId: "test-session-123",
|
|
12
|
-
tools: vi.fn().mockResolvedValue([]),
|
|
13
|
-
authorize: vi.fn().mockResolvedValue({ url: "https://connect.composio.dev/test" }),
|
|
14
|
-
toolkits: vi.fn().mockResolvedValue({
|
|
15
|
-
items: [
|
|
16
|
-
{ slug: "gmail", name: "Gmail", connection: { isActive: true } },
|
|
17
|
-
{ slug: "sentry", name: "Sentry", connection: { isActive: false } },
|
|
18
|
-
{ slug: "github", name: "GitHub", connection: { isActive: true } },
|
|
19
|
-
{ slug: "affinity", name: "Affinity", connection: { isActive: false } },
|
|
20
|
-
],
|
|
21
|
-
}),
|
|
22
|
-
experimental: { assistivePrompt: "" },
|
|
23
|
-
}),
|
|
24
|
-
},
|
|
25
|
-
client: {
|
|
26
|
-
tools: {
|
|
27
|
-
execute: vi.fn().mockResolvedValue({
|
|
28
|
-
successful: true,
|
|
29
|
-
data: { results: [{ tool_slug: "GMAIL_FETCH_EMAILS", index: 0, response: { successful: true, data: { messages: [] } } }] },
|
|
30
|
-
}),
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
connectedAccounts: {
|
|
34
|
-
list: vi.fn().mockResolvedValue({ items: [] }),
|
|
35
|
-
delete: vi.fn().mockResolvedValue({}),
|
|
36
|
-
},
|
|
37
|
-
})),
|
|
38
|
-
}));
|
|
39
|
-
function makeClient(overrides) {
|
|
40
|
-
return new ComposioClient({
|
|
41
|
-
enabled: true,
|
|
42
|
-
apiKey: "test-key",
|
|
43
|
-
...overrides,
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
describe("config parsing", () => {
|
|
47
|
-
it("reads apiKey from config object", () => {
|
|
48
|
-
const config = parseComposioConfig({ config: { apiKey: "from-config" } });
|
|
49
|
-
expect(config.apiKey).toBe("from-config");
|
|
50
|
-
});
|
|
51
|
-
it("reads apiKey from top-level", () => {
|
|
52
|
-
const config = parseComposioConfig({ apiKey: "from-top" });
|
|
53
|
-
expect(config.apiKey).toBe("from-top");
|
|
54
|
-
});
|
|
55
|
-
it("falls back to env var", () => {
|
|
56
|
-
process.env.COMPOSIO_API_KEY = "from-env";
|
|
57
|
-
const config = parseComposioConfig({});
|
|
58
|
-
expect(config.apiKey).toBe("from-env");
|
|
59
|
-
delete process.env.COMPOSIO_API_KEY;
|
|
60
|
-
});
|
|
61
|
-
it("defaults enabled to true", () => {
|
|
62
|
-
const config = parseComposioConfig({});
|
|
63
|
-
expect(config.enabled).toBe(true);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
describe("toolkit filtering", () => {
|
|
67
|
-
it("allows all toolkits when no filter set", async () => {
|
|
68
|
-
const client = makeClient();
|
|
69
|
-
const statuses = await client.getConnectionStatus(["gmail", "sentry", "github"]);
|
|
70
|
-
expect(statuses).toHaveLength(3);
|
|
71
|
-
});
|
|
72
|
-
it("filters by allowedToolkits", async () => {
|
|
73
|
-
const client = makeClient({ allowedToolkits: ["gmail", "sentry"] });
|
|
74
|
-
const statuses = await client.getConnectionStatus(["gmail", "sentry", "github"]);
|
|
75
|
-
expect(statuses).toHaveLength(2);
|
|
76
|
-
expect(statuses.map(s => s.toolkit)).toEqual(["gmail", "sentry"]);
|
|
77
|
-
});
|
|
78
|
-
it("filters by blockedToolkits", async () => {
|
|
79
|
-
const client = makeClient({ blockedToolkits: ["github"] });
|
|
80
|
-
const statuses = await client.getConnectionStatus(["gmail", "sentry", "github"]);
|
|
81
|
-
expect(statuses).toHaveLength(2);
|
|
82
|
-
expect(statuses.find(s => s.toolkit === "github")).toBeUndefined();
|
|
83
|
-
});
|
|
84
|
-
it("blocked takes priority over allowed", async () => {
|
|
85
|
-
const client = makeClient({ allowedToolkits: ["gmail", "github"], blockedToolkits: ["github"] });
|
|
86
|
-
const statuses = await client.getConnectionStatus(["gmail", "github"]);
|
|
87
|
-
expect(statuses).toHaveLength(1);
|
|
88
|
-
expect(statuses[0].toolkit).toBe("gmail");
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
describe("connection status", () => {
|
|
92
|
-
it("reports gmail as connected", async () => {
|
|
93
|
-
const client = makeClient();
|
|
94
|
-
const statuses = await client.getConnectionStatus(["gmail"]);
|
|
95
|
-
expect(statuses[0].connected).toBe(true);
|
|
96
|
-
});
|
|
97
|
-
it("reports sentry as not connected", async () => {
|
|
98
|
-
const client = makeClient();
|
|
99
|
-
const statuses = await client.getConnectionStatus(["sentry"]);
|
|
100
|
-
expect(statuses[0].connected).toBe(false);
|
|
101
|
-
});
|
|
102
|
-
it("returns only connected toolkits when no filter", async () => {
|
|
103
|
-
const client = makeClient();
|
|
104
|
-
const statuses = await client.getConnectionStatus();
|
|
105
|
-
expect(statuses.every(s => s.connected)).toBe(true);
|
|
106
|
-
expect(statuses.map(s => s.toolkit)).toEqual(["gmail", "github"]);
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
describe("execute tool", () => {
|
|
110
|
-
it("executes and returns result", async () => {
|
|
111
|
-
const client = makeClient();
|
|
112
|
-
const result = await client.executeTool("GMAIL_FETCH_EMAILS", {});
|
|
113
|
-
expect(result.success).toBe(true);
|
|
114
|
-
expect(result.data).toEqual({ messages: [] });
|
|
115
|
-
});
|
|
116
|
-
it("rejects blocked toolkit", async () => {
|
|
117
|
-
const client = makeClient({ allowedToolkits: ["sentry"] });
|
|
118
|
-
const result = await client.executeTool("GMAIL_FETCH_EMAILS", {});
|
|
119
|
-
expect(result.success).toBe(false);
|
|
120
|
-
expect(result.error).toContain("not allowed");
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
describe("create connection", () => {
|
|
124
|
-
it("returns auth URL", async () => {
|
|
125
|
-
const client = makeClient();
|
|
126
|
-
const result = await client.createConnection("gmail");
|
|
127
|
-
expect("authUrl" in result).toBe(true);
|
|
128
|
-
if ("authUrl" in result) {
|
|
129
|
-
expect(result.authUrl).toContain("connect.composio.dev");
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
it("rejects blocked toolkit", async () => {
|
|
133
|
-
const client = makeClient({ blockedToolkits: ["gmail"] });
|
|
134
|
-
const result = await client.createConnection("gmail");
|
|
135
|
-
expect("error" in result).toBe(true);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
describe("session caching", () => {
|
|
139
|
-
it("reuses session for same user", async () => {
|
|
140
|
-
const client = makeClient();
|
|
141
|
-
await client.getConnectionStatus(["gmail"]);
|
|
142
|
-
await client.getConnectionStatus(["gmail"]);
|
|
143
|
-
// toolRouter.create should only be called once
|
|
144
|
-
const { Composio } = await import("@composio/core");
|
|
145
|
-
const instance = Composio.mock.results[0].value;
|
|
146
|
-
expect(instance.toolRouter.create).toHaveBeenCalledTimes(1);
|
|
147
|
-
});
|
|
148
|
-
});
|
|
149
|
-
describe("execute tool string arguments (GLM-5 workaround)", () => {
|
|
150
|
-
function makeTool() {
|
|
151
|
-
const client = makeClient();
|
|
152
|
-
const config = parseComposioConfig({ config: { apiKey: "test-key" } });
|
|
153
|
-
return createComposioExecuteTool(client, config);
|
|
154
|
-
}
|
|
155
|
-
it("parses string arguments as JSON", async () => {
|
|
156
|
-
const tool = makeTool();
|
|
157
|
-
const result = await tool.execute("test", {
|
|
158
|
-
tool_slug: "GMAIL_FETCH_EMAILS",
|
|
159
|
-
arguments: '{"user_id": "me", "max_results": 5}',
|
|
160
|
-
});
|
|
161
|
-
expect(result.details).toHaveProperty("success", true);
|
|
162
|
-
});
|
|
163
|
-
it("handles object arguments normally", async () => {
|
|
164
|
-
const tool = makeTool();
|
|
165
|
-
const result = await tool.execute("test", {
|
|
166
|
-
tool_slug: "GMAIL_FETCH_EMAILS",
|
|
167
|
-
arguments: { user_id: "me", max_results: 5 },
|
|
168
|
-
});
|
|
169
|
-
expect(result.details).toHaveProperty("success", true);
|
|
170
|
-
});
|
|
171
|
-
it("falls back to empty args on invalid JSON string", async () => {
|
|
172
|
-
const tool = makeTool();
|
|
173
|
-
const result = await tool.execute("test", {
|
|
174
|
-
tool_slug: "GMAIL_FETCH_EMAILS",
|
|
175
|
-
arguments: "not valid json",
|
|
176
|
-
});
|
|
177
|
-
expect(result.details).toHaveProperty("success", true);
|
|
178
|
-
});
|
|
179
|
-
it("falls back to empty args when arguments is missing", async () => {
|
|
180
|
-
const tool = makeTool();
|
|
181
|
-
const result = await tool.execute("test", {
|
|
182
|
-
tool_slug: "GMAIL_FETCH_EMAILS",
|
|
183
|
-
});
|
|
184
|
-
expect(result.details).toHaveProperty("success", true);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
describe("connections tool", () => {
|
|
188
|
-
function makeConnectionsTool() {
|
|
189
|
-
const client = makeClient();
|
|
190
|
-
const config = parseComposioConfig({ config: { apiKey: "test-key" } });
|
|
191
|
-
return createComposioConnectionsTool(client, config);
|
|
192
|
-
}
|
|
193
|
-
it("list action passes user_id to client", async () => {
|
|
194
|
-
const tool = makeConnectionsTool();
|
|
195
|
-
const result = await tool.execute("test", { action: "list", user_id: "custom-user" });
|
|
196
|
-
const details = result.details;
|
|
197
|
-
expect(details).toHaveProperty("action", "list");
|
|
198
|
-
expect(details.toolkits).toBeInstanceOf(Array);
|
|
199
|
-
});
|
|
200
|
-
it("status probes API-key toolkit and flips to connected on success", async () => {
|
|
201
|
-
const tool = makeConnectionsTool();
|
|
202
|
-
const result = await tool.execute("test", { action: "status", toolkit: "affinity" });
|
|
203
|
-
const details = result.details;
|
|
204
|
-
const conn = details.connections.find((c) => c.toolkit === "affinity");
|
|
205
|
-
expect(conn.connected).toBe(true);
|
|
206
|
-
});
|
|
207
|
-
it("status does not probe toolkits without a defined probe", async () => {
|
|
208
|
-
const tool = makeConnectionsTool();
|
|
209
|
-
const result = await tool.execute("test", { action: "status", toolkit: "sentry" });
|
|
210
|
-
const details = result.details;
|
|
211
|
-
const conn = details.connections.find((c) => c.toolkit === "sentry");
|
|
212
|
-
expect(conn.connected).toBe(false);
|
|
213
|
-
});
|
|
214
|
-
it("status keeps disconnected when probe fails", async () => {
|
|
215
|
-
const tool = makeConnectionsTool();
|
|
216
|
-
// Get the latest Composio instance (the one this tool's client is using)
|
|
217
|
-
const { Composio } = await import("@composio/core");
|
|
218
|
-
const mockResults = Composio.mock.results;
|
|
219
|
-
const instance = mockResults[mockResults.length - 1].value;
|
|
220
|
-
instance.client.tools.execute.mockRejectedValueOnce(new Error("probe failed"));
|
|
221
|
-
const result = await tool.execute("test", { action: "status", toolkit: "affinity" });
|
|
222
|
-
const details = result.details;
|
|
223
|
-
const conn = details.connections.find((c) => c.toolkit === "affinity");
|
|
224
|
-
expect(conn.connected).toBe(false);
|
|
225
|
-
});
|
|
226
|
-
});
|