@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/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;
@@ -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
+ }
@@ -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.7",
3
+ "version": "0.0.8",
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": [
@@ -1 +0,0 @@
1
- export {};