clawdi 0.13.1 → 0.13.3

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.
@@ -79,12 +79,38 @@ When the user's request is **conceptual** ("how do I usually do X"), prefer `mem
79
79
 
80
80
  ## Connectors
81
81
 
82
- Connected service tools (Gmail, GitHub, Notion, etc.) are dynamically registered from the user's Clawdi Cloud dashboard. They appear as individual tools like `gmail_fetch_emails`, `github_list_issues`, etc.
83
-
84
- - These tools are already authenticated — no OAuth needed at runtime
85
- - If a tool call fails with "No connected account", tell the user to connect the service in the Clawdi Cloud dashboard
86
- - File downloads from connectors return signed URLs download them with `curl` or `fetch` before processing
87
- - Confirm with the user before side-effecting operations (sending email, creating issues, etc.)
82
+ Use the Composio Tool Router meta-tools returned by `tools/list` on the `clawdi` MCP server.
83
+ Treat their live names and schemas as authoritative; never assume a fixed meta-tool set.
84
+
85
+ 1. Start each external-app workflow with `COMPOSIO_SEARCH_TOOLS`. Follow its exposed
86
+ `queries` and `session` schema, reuse the returned session ID throughout that workflow,
87
+ and use only the exact toolkit and tool slugs it returns. If a required schema is absent
88
+ or incomplete, call `COMPOSIO_GET_TOOL_SCHEMAS`; never invent fields or inputs.
89
+ 2. Before a side effect, require a complete target identity and all schema-required inputs.
90
+ Explicit intent authorizes the exact requested action and target, but never authorizes
91
+ guessing a missing recipient, account, resource, or other target. Ask only for what is
92
+ missing, and do not request redundant confirmation once the exact action is authorized.
93
+ 3. When search reports no active connection, call `COMPOSIO_MANAGE_CONNECTIONS` with its
94
+ exposed schema and interpret only the fields it returns. Continue on `active`. On
95
+ `initiated`, present its non-empty `redirect_url` as a clickable authentication link with
96
+ a concise explanation that authorization is pending; the link URL must be exactly that
97
+ value. If `initiated` has no non-empty `redirect_url`, report that authorization cannot
98
+ continue and stop. On `failed`, report the returned error and stop. Never construct a
99
+ substitute link, ask for OAuth credentials, API keys, or tokens, or suggest an
100
+ out-of-band fallback.
101
+ 4. Use a wait or status operation only when `tools/list` exposes one. Follow its actual schema
102
+ and status values without inventing polling arguments. Continue only when it reports an
103
+ active connection; keep waiting only for a non-terminal status its schema defines, and
104
+ report any terminal failure. If none is exposed, stop until the user reports completing
105
+ authorization, then re-run search to verify the active connection before continuing.
106
+ 5. Execute exact returned slugs through `COMPOSIO_MULTI_EXECUTE_TOOL` with schema-compliant
107
+ arguments. Batch only independent calls. Keep ordinary results inline. Set
108
+ `sync_response_to_workbench` only when a result may be large or needs later remote
109
+ processing; use `COMPOSIO_REMOTE_WORKBENCH` / `COMPOSIO_REMOTE_BASH_TOOL` only for large
110
+ responses saved remotely or remote artifacts.
111
+ 6. Preserve dependencies and returned semantics. Follow signed-file metadata, pagination
112
+ fields, and termination signals exactly as exposed. Select an account only when the schema
113
+ supports it, and use additional or future meta-tools only according to their live schemas.
88
114
 
89
115
  ## Vault CLI
90
116
 
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: clawdi
3
+ description: "Find and read the user's past agent sessions across Claude Code, Codex, OpenClaw, and Hermes; use connected-service tools such as Gmail, GitHub, Notion, Drive, and Calendar; and read Clawdi share URLs (https://cloud.clawdi.ai/s/...) the user pastes."
4
+ ---
5
+
6
+ # Clawdi Cloud
7
+
8
+ Use Clawdi Cloud tools through the `clawdi` MCP server.
9
+
10
+ ## Sessions
11
+
12
+ - Use `session_search` to find past agent conversations by keyword and obtain session UUIDs.
13
+ - Use `session_read` to read a session by UUID or Clawdi share URL.
14
+
15
+ Call `session_read` when the user provides a Clawdi share URL. When the user refers to a
16
+ past conversation without a UUID, call `session_search` first and then read the matching
17
+ session. Do not use a generic web fetcher for Clawdi share URLs.
18
+
19
+ ## Connectors
20
+
21
+ Use the Composio Tool Router meta-tools returned by `tools/list` on the `clawdi` MCP server.
22
+ Treat their live names and schemas as authoritative; never assume a fixed meta-tool set.
23
+
24
+ 1. Start each external-app workflow with `COMPOSIO_SEARCH_TOOLS`. Follow its exposed
25
+ `queries` and `session` schema, reuse the returned session ID throughout that workflow,
26
+ and use only the exact toolkit and tool slugs it returns. If a required schema is absent
27
+ or incomplete, call `COMPOSIO_GET_TOOL_SCHEMAS`; never invent fields or inputs.
28
+ 2. Before a side effect, require a complete target identity and all schema-required inputs.
29
+ Explicit intent authorizes the exact requested action and target, but never authorizes
30
+ guessing a missing recipient, account, resource, or other target. Ask only for what is
31
+ missing, and do not request redundant confirmation once the exact action is authorized.
32
+ 3. When search reports no active connection, call `COMPOSIO_MANAGE_CONNECTIONS` with its
33
+ exposed schema and interpret only the fields it returns. Continue on `active`. On
34
+ `initiated`, present its non-empty `redirect_url` as a clickable authentication link with
35
+ a concise explanation that authorization is pending; the link URL must be exactly that
36
+ value. If `initiated` has no non-empty `redirect_url`, report that authorization cannot
37
+ continue and stop. On `failed`, report the returned error and stop. Never construct a
38
+ substitute link, ask for OAuth credentials, API keys, or tokens, or suggest an
39
+ out-of-band fallback.
40
+ 4. Use a wait or status operation only when `tools/list` exposes one. Follow its actual schema
41
+ and status values without inventing polling arguments. Continue only when it reports an
42
+ active connection; keep waiting only for a non-terminal status its schema defines, and
43
+ report any terminal failure. If none is exposed, stop until the user reports completing
44
+ authorization, then re-run search to verify the active connection before continuing.
45
+ 5. Execute exact returned slugs through `COMPOSIO_MULTI_EXECUTE_TOOL` with schema-compliant
46
+ arguments. Batch only independent calls. Keep ordinary results inline. Set
47
+ `sync_response_to_workbench` only when a result may be large or needs later remote
48
+ processing; use `COMPOSIO_REMOTE_WORKBENCH` / `COMPOSIO_REMOTE_BASH_TOOL` only for large
49
+ responses saved remotely or remote artifacts.
50
+ 6. Preserve dependencies and returned semantics. Follow signed-file metadata, pagination
51
+ fields, and termination signals exactly as exposed. Select an account only when the schema
52
+ supports it, and use additional or future meta-tools only according to their live schemas.
@@ -12,7 +12,7 @@ import os
12
12
  import re
13
13
  from pathlib import Path
14
14
  from typing import Any
15
- from urllib.parse import parse_qs, quote, unquote, urlsplit
15
+ from urllib.parse import parse_qs, urlsplit
16
16
 
17
17
  try: # pragma: no cover - exercised only under mitmdump.
18
18
  from mitmproxy import ctx, http
@@ -114,6 +114,7 @@ class ClawdiEgressAddon:
114
114
  self.secret_file_path = Path(secret_file) if secret_file else None
115
115
  self.profiles = load_profiles(self.profile_bundle_path)
116
116
  self.secrets = load_secrets(self.secret_file_path)
117
+ validate_profile_secrets(self.profiles, self.secrets)
117
118
  self.profile_hosts = profile_host_set(self.profiles)
118
119
 
119
120
  def should_intercept_sni(self, server_name: str) -> bool:
@@ -205,6 +206,30 @@ def load_secrets(path: Path | None) -> dict[str, str]:
205
206
  return {str(key): str(value) for key, value in raw.items() if isinstance(value, str)}
206
207
 
207
208
 
209
+ def validate_profile_secrets(
210
+ profiles: list[dict[str, Any]], secrets: dict[str, str]
211
+ ) -> None:
212
+ required: set[str] = set()
213
+ for profile in profiles:
214
+ collect_secret_refs(profile, required)
215
+ missing = sorted(ref for ref in required if not secrets.get(ref))
216
+ if missing:
217
+ raise ConfigError(f"egress profile secrets are missing: {', '.join(missing)}")
218
+
219
+
220
+ def collect_secret_refs(value: Any, refs: set[str]) -> None:
221
+ if isinstance(value, list):
222
+ for item in value:
223
+ collect_secret_refs(item, refs)
224
+ return
225
+ if not isinstance(value, dict):
226
+ return
227
+ for key, entry in value.items():
228
+ if isinstance(entry, str) and (key == "secretRef" or key.endswith("SecretRef")):
229
+ refs.add(entry)
230
+ collect_secret_refs(entry, refs)
231
+
232
+
208
233
  def profile_host_set(profiles: list[dict[str, Any]]) -> set[str]:
209
234
  hosts: set[str] = set()
210
235
  for profile in profiles:
@@ -297,9 +322,13 @@ def host_matches(flow: Any, profile: dict[str, Any]) -> bool:
297
322
  match = profile.get("match")
298
323
  if not isinstance(match, dict):
299
324
  return False
300
- expected = normalize_host(str(match.get("host", "")))
325
+ expected_authority = str(match.get("host", "")).strip().lower()
326
+ expected_host, expected_port = split_authority(expected_authority)
327
+ expected = normalize_host(expected_host)
301
328
  if not expected:
302
329
  return False
330
+ if expected_port is not None and getattr(flow.request, "port", None) != expected_port:
331
+ return False
303
332
  return expected in request_host_candidates(flow)
304
333
 
305
334
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawdi",
3
- "version": "0.13.1",
3
+ "version": "0.13.3",
4
4
  "description": "iCloud for AI Agents — cross-agent sessions, skills, memory, and vault.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "scripts": {
25
25
  "dev": "bun run src/index.ts",
26
- "build": "rm -rf dist && bun build src/index.ts --outdir dist --target node --minify --define 'process.env.CLAWDI_DEFAULT_API_URL=\"https://cloud-api.clawdi.ai\"' && cp -r skills dist/skills",
26
+ "build": "rm -rf dist && bun build src/index.ts --outdir dist --target node --minify --define 'process.env.CLAWDI_DEFAULT_API_URL=\"https://cloud-api.clawdi.ai\"' --define 'process.env.CLAWDI_DEFAULT_DEPLOY_API_URL=\"https://api.clawdi.ai\"' && cp -r skills dist/skills",
27
27
  "build:binary": "bun run scripts/build-binary.mjs",
28
28
  "package:binary-release": "bun run scripts/package-binary-release.mjs",
29
29
  "build:dev": "rm -rf dist && bun build src/index.ts --outdir dist --target node && cp -r skills dist/skills",