@customclaw/composio 0.0.7 → 0.0.9
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 +253 -53
- 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/dist/config.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import { LEGACY_ENTRY_FLAT_CONFIG_KEYS, LEGACY_SHAPE_ERROR, SESSION_TAGS, isRecord, normalizeSessionTags, normalizeToolkitList, normalizeToolSlugList, } from "./utils.js";
|
|
2
3
|
/**
|
|
3
4
|
* Zod schema for Composio plugin configuration
|
|
4
5
|
*/
|
|
@@ -8,36 +9,39 @@ export const ComposioConfigSchema = z.object({
|
|
|
8
9
|
defaultUserId: z.string().optional(),
|
|
9
10
|
allowedToolkits: z.array(z.string()).optional(),
|
|
10
11
|
blockedToolkits: z.array(z.string()).optional(),
|
|
12
|
+
readOnlyMode: z.boolean().default(false),
|
|
13
|
+
sessionTags: z.array(z.enum(SESSION_TAGS)).optional(),
|
|
14
|
+
allowedToolSlugs: z.array(z.string()).optional(),
|
|
15
|
+
blockedToolSlugs: z.array(z.string()).optional(),
|
|
11
16
|
});
|
|
12
17
|
/**
|
|
13
18
|
* Parse and validate plugin config with environment fallbacks
|
|
14
19
|
*/
|
|
15
20
|
export function parseComposioConfig(value) {
|
|
16
|
-
const raw = value
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
const blockedToolkits = (Array.isArray(raw.blockedToolkits) ? raw.blockedToolkits : undefined) ??
|
|
29
|
-
(Array.isArray(configObj?.blockedToolkits) ? configObj.blockedToolkits : undefined);
|
|
30
|
-
// Allow API key from config.apiKey, top-level apiKey, or environment.
|
|
31
|
-
const apiKey = (typeof configObj?.apiKey === "string" && configObj.apiKey.trim()) ||
|
|
32
|
-
(typeof raw.apiKey === "string" && raw.apiKey.trim()) ||
|
|
21
|
+
const raw = isRecord(value) ? value : {};
|
|
22
|
+
const configObj = isRecord(raw.config) ? raw.config : undefined;
|
|
23
|
+
if (configObj) {
|
|
24
|
+
const hasLegacyFlatKeys = LEGACY_ENTRY_FLAT_CONFIG_KEYS.some((key) => key in raw);
|
|
25
|
+
if (hasLegacyFlatKeys) {
|
|
26
|
+
throw new Error(LEGACY_SHAPE_ERROR);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const source = configObj ?? raw;
|
|
30
|
+
const enabled = typeof raw.enabled === "boolean" ? raw.enabled : true;
|
|
31
|
+
const readOnlyMode = typeof source.readOnlyMode === "boolean" ? source.readOnlyMode : false;
|
|
32
|
+
const apiKey = (typeof source.apiKey === "string" && source.apiKey.trim()) ||
|
|
33
33
|
process.env.COMPOSIO_API_KEY ||
|
|
34
34
|
"";
|
|
35
35
|
return ComposioConfigSchema.parse({
|
|
36
36
|
enabled,
|
|
37
37
|
apiKey,
|
|
38
|
-
defaultUserId,
|
|
39
|
-
allowedToolkits,
|
|
40
|
-
blockedToolkits,
|
|
38
|
+
defaultUserId: typeof source.defaultUserId === "string" ? source.defaultUserId : undefined,
|
|
39
|
+
allowedToolkits: normalizeToolkitList(Array.isArray(source.allowedToolkits) ? source.allowedToolkits : undefined),
|
|
40
|
+
blockedToolkits: normalizeToolkitList(Array.isArray(source.blockedToolkits) ? source.blockedToolkits : undefined),
|
|
41
|
+
readOnlyMode,
|
|
42
|
+
sessionTags: normalizeSessionTags(Array.isArray(source.sessionTags) ? source.sessionTags : undefined),
|
|
43
|
+
allowedToolSlugs: normalizeToolSlugList(Array.isArray(source.allowedToolSlugs) ? source.allowedToolSlugs : undefined),
|
|
44
|
+
blockedToolSlugs: normalizeToolSlugList(Array.isArray(source.blockedToolSlugs) ? source.blockedToolSlugs : undefined),
|
|
41
45
|
});
|
|
42
46
|
}
|
|
43
47
|
/**
|
|
@@ -67,6 +71,26 @@ export const composioConfigUiHints = {
|
|
|
67
71
|
help: "Block specific toolkits from being used",
|
|
68
72
|
advanced: true,
|
|
69
73
|
},
|
|
74
|
+
readOnlyMode: {
|
|
75
|
+
label: "Read-Only Mode",
|
|
76
|
+
help: "Block likely-destructive tool actions by token matching; allow specific slugs with allowedToolSlugs if needed",
|
|
77
|
+
advanced: true,
|
|
78
|
+
},
|
|
79
|
+
sessionTags: {
|
|
80
|
+
label: "Session Tags",
|
|
81
|
+
help: "Composio Tool Router behavior tags (e.g., readOnlyHint, destructiveHint)",
|
|
82
|
+
advanced: true,
|
|
83
|
+
},
|
|
84
|
+
allowedToolSlugs: {
|
|
85
|
+
label: "Allowed Tool Slugs",
|
|
86
|
+
help: "Optional explicit allowlist for tool slugs (UPPERCASE)",
|
|
87
|
+
advanced: true,
|
|
88
|
+
},
|
|
89
|
+
blockedToolSlugs: {
|
|
90
|
+
label: "Blocked Tool Slugs",
|
|
91
|
+
help: "Explicit denylist for tool slugs (UPPERCASE)",
|
|
92
|
+
advanced: true,
|
|
93
|
+
},
|
|
70
94
|
};
|
|
71
95
|
/**
|
|
72
96
|
* Plugin config schema object for openclaw
|
package/dist/index.d.ts
CHANGED
|
@@ -49,6 +49,26 @@ declare const composioPlugin: {
|
|
|
49
49
|
help: string;
|
|
50
50
|
advanced: boolean;
|
|
51
51
|
};
|
|
52
|
+
readOnlyMode: {
|
|
53
|
+
label: string;
|
|
54
|
+
help: string;
|
|
55
|
+
advanced: boolean;
|
|
56
|
+
};
|
|
57
|
+
sessionTags: {
|
|
58
|
+
label: string;
|
|
59
|
+
help: string;
|
|
60
|
+
advanced: boolean;
|
|
61
|
+
};
|
|
62
|
+
allowedToolSlugs: {
|
|
63
|
+
label: string;
|
|
64
|
+
help: string;
|
|
65
|
+
advanced: boolean;
|
|
66
|
+
};
|
|
67
|
+
blockedToolSlugs: {
|
|
68
|
+
label: string;
|
|
69
|
+
help: string;
|
|
70
|
+
advanced: boolean;
|
|
71
|
+
};
|
|
52
72
|
};
|
|
53
73
|
};
|
|
54
74
|
register(api: any): void;
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { createComposioSearchTool } from "./tools/search.js";
|
|
|
4
4
|
import { createComposioExecuteTool } from "./tools/execute.js";
|
|
5
5
|
import { createComposioConnectionsTool } from "./tools/connections.js";
|
|
6
6
|
import { registerComposioCli } from "./cli.js";
|
|
7
|
+
import { LEGACY_SHAPE_ERROR, hasLegacyFlatEntryConfig } from "./utils.js";
|
|
7
8
|
/**
|
|
8
9
|
* Composio Tool Router Plugin for OpenClaw
|
|
9
10
|
*
|
|
@@ -31,7 +32,27 @@ const composioPlugin = {
|
|
|
31
32
|
"Search, authenticate, and execute tools for Gmail, Slack, GitHub, Notion, and more.",
|
|
32
33
|
configSchema: composioPluginConfigSchema,
|
|
33
34
|
register(api) {
|
|
35
|
+
if (hasLegacyFlatEntryConfig(api?.config)) {
|
|
36
|
+
throw new Error(LEGACY_SHAPE_ERROR);
|
|
37
|
+
}
|
|
34
38
|
const config = parseComposioConfig(api.pluginConfig);
|
|
39
|
+
let client = null;
|
|
40
|
+
const ensureClient = () => {
|
|
41
|
+
if (!config.apiKey) {
|
|
42
|
+
throw new Error("Composio API key required. Run 'openclaw composio setup' or set COMPOSIO_API_KEY.");
|
|
43
|
+
}
|
|
44
|
+
if (!client) {
|
|
45
|
+
client = createComposioClient(config);
|
|
46
|
+
}
|
|
47
|
+
return client;
|
|
48
|
+
};
|
|
49
|
+
// Register CLI commands even without API key so setup/status tooling remains available.
|
|
50
|
+
api.registerCli(({ program }) => registerComposioCli({
|
|
51
|
+
program,
|
|
52
|
+
getClient: config.apiKey ? ensureClient : undefined,
|
|
53
|
+
config,
|
|
54
|
+
logger: api.logger,
|
|
55
|
+
}), { commands: ["composio"] });
|
|
35
56
|
if (!config.enabled) {
|
|
36
57
|
api.logger.debug("[composio] Plugin disabled in config");
|
|
37
58
|
return;
|
|
@@ -40,13 +61,6 @@ const composioPlugin = {
|
|
|
40
61
|
api.logger.warn("[composio] No API key configured. Set COMPOSIO_API_KEY env var or plugins.composio.apiKey in config.");
|
|
41
62
|
return;
|
|
42
63
|
}
|
|
43
|
-
let client = null;
|
|
44
|
-
const ensureClient = () => {
|
|
45
|
-
if (!client) {
|
|
46
|
-
client = createComposioClient(config);
|
|
47
|
-
}
|
|
48
|
-
return client;
|
|
49
|
-
};
|
|
50
64
|
// Register tools (lazily create client on first use)
|
|
51
65
|
api.registerTool({
|
|
52
66
|
...createComposioSearchTool(ensureClient(), config),
|
|
@@ -66,13 +80,6 @@ const composioPlugin = {
|
|
|
66
80
|
return createComposioConnectionsTool(ensureClient(), config).execute(toolCallId, params);
|
|
67
81
|
},
|
|
68
82
|
});
|
|
69
|
-
// Register CLI commands
|
|
70
|
-
api.registerCli(({ program }) => registerComposioCli({
|
|
71
|
-
program,
|
|
72
|
-
client: ensureClient(),
|
|
73
|
-
config,
|
|
74
|
-
logger: api.logger,
|
|
75
|
-
}), { commands: ["composio"] });
|
|
76
83
|
api.logger.info("[composio] Plugin registered with 3 tools and CLI commands");
|
|
77
84
|
},
|
|
78
85
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Composio Tool Router types for OpenClaw integration
|
|
3
3
|
*/
|
|
4
|
+
export type ComposioSessionTag = "readOnlyHint" | "destructiveHint" | "idempotentHint" | "openWorldHint";
|
|
4
5
|
export interface ComposioConfig {
|
|
5
6
|
enabled: boolean;
|
|
6
7
|
apiKey?: string;
|
|
7
8
|
defaultUserId?: string;
|
|
8
9
|
allowedToolkits?: string[];
|
|
9
10
|
blockedToolkits?: string[];
|
|
11
|
+
readOnlyMode?: boolean;
|
|
12
|
+
sessionTags?: ComposioSessionTag[];
|
|
13
|
+
allowedToolSlugs?: string[];
|
|
14
|
+
blockedToolSlugs?: string[];
|
|
10
15
|
}
|
|
11
16
|
export interface ToolSearchResult {
|
|
12
17
|
name: string;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ComposioSessionTag } from "./types.js";
|
|
2
|
+
export declare const SESSION_TAGS: readonly ["readOnlyHint", "destructiveHint", "idempotentHint", "openWorldHint"];
|
|
3
|
+
export declare const LEGACY_ENTRY_FLAT_CONFIG_KEYS: readonly ["apiKey", "defaultUserId", "allowedToolkits", "blockedToolkits", "readOnlyMode", "sessionTags", "allowedToolSlugs", "blockedToolSlugs"];
|
|
4
|
+
export declare const LEGACY_SHAPE_ERROR = "Legacy Composio config shape detected. Run 'openclaw composio setup'.";
|
|
5
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
6
|
+
export declare function normalizeToolkitSlug(value: unknown): string;
|
|
7
|
+
export declare function normalizeToolSlug(value: unknown): string;
|
|
8
|
+
export declare function normalizeToolkitList(values?: readonly unknown[]): string[] | undefined;
|
|
9
|
+
export declare function normalizeToolSlugList(values?: readonly unknown[]): string[] | undefined;
|
|
10
|
+
export declare function normalizeSessionTags(values?: readonly unknown[]): ComposioSessionTag[] | undefined;
|
|
11
|
+
export declare function hasLegacyFlatEntryConfig(config: unknown): boolean;
|
|
12
|
+
export declare function stripLegacyFlatConfigKeys(entry: Record<string, unknown>): Record<string, unknown>;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const SESSION_TAGS = [
|
|
2
|
+
"readOnlyHint",
|
|
3
|
+
"destructiveHint",
|
|
4
|
+
"idempotentHint",
|
|
5
|
+
"openWorldHint",
|
|
6
|
+
];
|
|
7
|
+
export const LEGACY_ENTRY_FLAT_CONFIG_KEYS = [
|
|
8
|
+
"apiKey",
|
|
9
|
+
"defaultUserId",
|
|
10
|
+
"allowedToolkits",
|
|
11
|
+
"blockedToolkits",
|
|
12
|
+
"readOnlyMode",
|
|
13
|
+
"sessionTags",
|
|
14
|
+
"allowedToolSlugs",
|
|
15
|
+
"blockedToolSlugs",
|
|
16
|
+
];
|
|
17
|
+
export const LEGACY_SHAPE_ERROR = "Legacy Composio config shape detected. Run 'openclaw composio setup'.";
|
|
18
|
+
const SESSION_TAG_SET = new Set(SESSION_TAGS);
|
|
19
|
+
export function isRecord(value) {
|
|
20
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
21
|
+
}
|
|
22
|
+
export function normalizeToolkitSlug(value) {
|
|
23
|
+
return String(value ?? "").trim().toLowerCase();
|
|
24
|
+
}
|
|
25
|
+
export function normalizeToolSlug(value) {
|
|
26
|
+
return String(value ?? "").trim().toUpperCase();
|
|
27
|
+
}
|
|
28
|
+
export function normalizeToolkitList(values) {
|
|
29
|
+
if (!values || values.length === 0)
|
|
30
|
+
return undefined;
|
|
31
|
+
const normalized = values
|
|
32
|
+
.filter((item) => typeof item === "string")
|
|
33
|
+
.map((item) => normalizeToolkitSlug(item))
|
|
34
|
+
.filter(Boolean);
|
|
35
|
+
if (normalized.length === 0)
|
|
36
|
+
return undefined;
|
|
37
|
+
return Array.from(new Set(normalized));
|
|
38
|
+
}
|
|
39
|
+
export function normalizeToolSlugList(values) {
|
|
40
|
+
if (!values || values.length === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
const normalized = values
|
|
43
|
+
.filter((item) => typeof item === "string")
|
|
44
|
+
.map((item) => normalizeToolSlug(item))
|
|
45
|
+
.filter(Boolean);
|
|
46
|
+
if (normalized.length === 0)
|
|
47
|
+
return undefined;
|
|
48
|
+
return Array.from(new Set(normalized));
|
|
49
|
+
}
|
|
50
|
+
export function normalizeSessionTags(values) {
|
|
51
|
+
if (!values || values.length === 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
const normalized = values
|
|
54
|
+
.filter((item) => typeof item === "string")
|
|
55
|
+
.map((item) => item.trim())
|
|
56
|
+
.filter((item) => SESSION_TAG_SET.has(item));
|
|
57
|
+
if (normalized.length === 0)
|
|
58
|
+
return undefined;
|
|
59
|
+
return Array.from(new Set(normalized));
|
|
60
|
+
}
|
|
61
|
+
export function hasLegacyFlatEntryConfig(config) {
|
|
62
|
+
const root = isRecord(config) ? config : undefined;
|
|
63
|
+
const plugins = isRecord(root?.plugins) ? root.plugins : undefined;
|
|
64
|
+
const entries = isRecord(plugins?.entries) ? plugins.entries : undefined;
|
|
65
|
+
const composioEntry = isRecord(entries?.composio) ? entries.composio : undefined;
|
|
66
|
+
if (!composioEntry)
|
|
67
|
+
return false;
|
|
68
|
+
return LEGACY_ENTRY_FLAT_CONFIG_KEYS.some((key) => key in composioEntry);
|
|
69
|
+
}
|
|
70
|
+
export function stripLegacyFlatConfigKeys(entry) {
|
|
71
|
+
const sanitized = { ...entry };
|
|
72
|
+
for (const key of LEGACY_ENTRY_FLAT_CONFIG_KEYS) {
|
|
73
|
+
delete sanitized[key];
|
|
74
|
+
}
|
|
75
|
+
return sanitized;
|
|
76
|
+
}
|
package/openclaw.plugin.json
CHANGED
|
@@ -27,6 +27,25 @@
|
|
|
27
27
|
"type": "array",
|
|
28
28
|
"items": { "type": "string" },
|
|
29
29
|
"description": "Block specific toolkits"
|
|
30
|
+
},
|
|
31
|
+
"readOnlyMode": {
|
|
32
|
+
"type": "boolean",
|
|
33
|
+
"description": "Block likely destructive tool actions by default"
|
|
34
|
+
},
|
|
35
|
+
"sessionTags": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": { "type": "string", "enum": ["readOnlyHint", "destructiveHint", "idempotentHint", "openWorldHint"] },
|
|
38
|
+
"description": "Optional Tool Router behavior tags"
|
|
39
|
+
},
|
|
40
|
+
"allowedToolSlugs": {
|
|
41
|
+
"type": "array",
|
|
42
|
+
"items": { "type": "string" },
|
|
43
|
+
"description": "Optional explicit allowlist for tool slugs (UPPERCASE)"
|
|
44
|
+
},
|
|
45
|
+
"blockedToolSlugs": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"items": { "type": "string" },
|
|
48
|
+
"description": "Explicit denylist for tool slugs (UPPERCASE)"
|
|
30
49
|
}
|
|
31
50
|
}
|
|
32
51
|
},
|
|
@@ -53,6 +72,26 @@
|
|
|
53
72
|
"label": "Blocked Toolkits",
|
|
54
73
|
"help": "Block specific toolkits from being used",
|
|
55
74
|
"advanced": true
|
|
75
|
+
},
|
|
76
|
+
"readOnlyMode": {
|
|
77
|
+
"label": "Read-Only Mode",
|
|
78
|
+
"help": "Block likely-destructive tool actions by default",
|
|
79
|
+
"advanced": true
|
|
80
|
+
},
|
|
81
|
+
"sessionTags": {
|
|
82
|
+
"label": "Session Tags",
|
|
83
|
+
"help": "Tool Router behavior tags (e.g., readOnlyHint, destructiveHint)",
|
|
84
|
+
"advanced": true
|
|
85
|
+
},
|
|
86
|
+
"allowedToolSlugs": {
|
|
87
|
+
"label": "Allowed Tool Slugs",
|
|
88
|
+
"help": "Optional explicit allowlist for tool slugs (UPPERCASE)",
|
|
89
|
+
"advanced": true
|
|
90
|
+
},
|
|
91
|
+
"blockedToolSlugs": {
|
|
92
|
+
"label": "Blocked Tool Slugs",
|
|
93
|
+
"help": "Explicit denylist for tool slugs (UPPERCASE)",
|
|
94
|
+
"advanced": true
|
|
56
95
|
}
|
|
57
96
|
}
|
|
58
97
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@customclaw/composio",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Composio Tool Router plugin for OpenClaw — access 1000+ third-party integrations",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"build": "tsc",
|
|
14
|
+
"build": "rm -rf dist && tsc",
|
|
15
15
|
"test": "vitest run",
|
|
16
|
+
"test:live": "vitest run src/live.integration.test.ts",
|
|
16
17
|
"prepublishOnly": "npm run build"
|
|
17
18
|
},
|
|
18
19
|
"files": [
|
package/dist/client.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|