realtimex-sdk 1.0.0__tar.gz

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.
@@ -0,0 +1,37 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+ inputs:
8
+ version:
9
+ description: 'Version to publish (e.g., 1.0.0)'
10
+ required: false
11
+
12
+ jobs:
13
+ build-and-publish:
14
+ runs-on: ubuntu-latest
15
+ defaults:
16
+ run:
17
+ working-directory: typescript
18
+
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Setup Node.js
23
+ uses: actions/setup-node@v4
24
+ with:
25
+ node-version: '20'
26
+ registry-url: 'https://registry.npmjs.org'
27
+
28
+ - name: Install dependencies
29
+ run: npm ci
30
+
31
+ - name: Build
32
+ run: npm run build
33
+
34
+ - name: Publish to npm
35
+ run: npm publish --access public
36
+ env:
37
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,32 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ build-and-publish:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Setup Python
16
+ uses: actions/setup-python@v5
17
+ with:
18
+ python-version: '3.11'
19
+
20
+ - name: Install build tools
21
+ run: |
22
+ python -m pip install --upgrade pip
23
+ pip install build twine
24
+
25
+ - name: Build package
26
+ run: python -m build
27
+
28
+ - name: Publish to PyPI
29
+ env:
30
+ TWINE_USERNAME: __token__
31
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
32
+ run: twine upload dist/*
@@ -0,0 +1,102 @@
1
+ Metadata-Version: 2.4
2
+ Name: realtimex-sdk
3
+ Version: 1.0.0
4
+ Summary: Python SDK for building Local Apps that integrate with RealtimeX
5
+ Project-URL: Homepage, https://github.com/realtimex/rtx-local-app-sdk
6
+ Project-URL: Documentation, https://docs.realtimex.ai/local-apps
7
+ Project-URL: Repository, https://github.com/realtimex/rtx-local-app-sdk
8
+ Author-email: RealtimeX Team <team@realtimex.ai>
9
+ License-Expression: MIT
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.8
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Python: >=3.8
20
+ Requires-Dist: httpx>=0.25.0
21
+ Requires-Dist: supabase>=2.0.0
22
+ Provides-Extra: dev
23
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
24
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # RealtimeX Local App SDK - Python
28
+
29
+ Python SDK for building Local Apps that integrate with RealtimeX.
30
+
31
+ ## Installation
32
+
33
+ ```bash
34
+ pip install realtimex-sdk
35
+ ```
36
+
37
+ ## Prerequisites
38
+
39
+ Before using this SDK, ensure your Supabase database is set up:
40
+
41
+ 1. Open **RealtimeX Main App** → **Local Apps** → Your App → **Configure**
42
+ 2. Enter your Supabase **URL** and **Anon Key**
43
+ 3. Select **Compatible Mode** and click **Login to Supabase**
44
+ 4. Click **Auto-Setup Schema** to create required tables and functions
45
+
46
+ > **Note:** Schema setup is handled entirely by the Main App.
47
+
48
+ ## Quick Start
49
+
50
+ ```python
51
+ import asyncio
52
+ from realtimex_sdk import RealtimeXSDK, SupabaseConfig, RealtimeXConfig
53
+
54
+ async def main():
55
+ # Initialize SDK
56
+ sdk = RealtimeXSDK(
57
+ supabase=SupabaseConfig(
58
+ url="https://your-project.supabase.co",
59
+ anon_key="your-anon-key"
60
+ ),
61
+ realtimex=RealtimeXConfig(
62
+ url="http://localhost:3001",
63
+ app_name="My Local App"
64
+ )
65
+ )
66
+
67
+ # Insert activity
68
+ activity = await sdk.activities.insert({
69
+ "type": "new_lead",
70
+ "email": "user@example.com"
71
+ })
72
+
73
+ # Trigger agent (auto-run)
74
+ result = await sdk.webhook.trigger_agent(
75
+ raw_data=activity,
76
+ auto_run=True,
77
+ agent_name="lead-processor",
78
+ workspace_slug="sales"
79
+ )
80
+ print(f"Task created: {result['task_uuid']}")
81
+
82
+ # Or create calendar event for manual review
83
+ result = await sdk.webhook.trigger_agent(
84
+ raw_data=activity,
85
+ auto_run=False
86
+ )
87
+
88
+ asyncio.run(main())
89
+ ```
90
+
91
+ ## Environment Variables
92
+
93
+ When your app is started by the Main App, these are auto-set:
94
+
95
+ | Variable | Description |
96
+ |----------|-------------|
97
+ | `RTX_APP_ID` | Your app's unique ID |
98
+ | `RTX_APP_NAME` | Your app's display name |
99
+
100
+ ## API Reference
101
+
102
+ See the main [TypeScript README](../typescript/README.md) for full API documentation.
@@ -0,0 +1,41 @@
1
+ # RealtimeX Local App SDK
2
+
3
+ Official SDK for building Local Apps that integrate with RealtimeX.
4
+
5
+ ## Overview
6
+
7
+ This SDK allows your applications to:
8
+ - Insert and manage activities in Supabase
9
+ - Trigger RealtimeX agents automatically or manually
10
+ - Access RealtimeX APIs for workspaces, agents, and threads
11
+
12
+ ## Available SDKs
13
+
14
+ | Language | Package | Documentation |
15
+ |----------|---------|---------------|
16
+ | TypeScript/JavaScript | `@realtimex/local-app-sdk` | [TypeScript README](./typescript/README.md) |
17
+ | Python | `realtimex-sdk` | [Python README](./python/README.md) |
18
+
19
+
20
+ ## Prerequisites
21
+
22
+ Before using the SDK, set up your Supabase database via the RealtimeX Main App:
23
+
24
+ 1. Open **RealtimeX** → **Local Apps** → Your App → **Configure**
25
+ 2. Enter your Supabase **URL** and **Anon Key**
26
+ 3. Select **Compatible Mode** → **Login to Supabase** → **Auto-Setup Schema**
27
+
28
+ > Schema setup is handled entirely by the Main App - no manual SQL required.
29
+
30
+ ## Architecture
31
+
32
+ ```
33
+ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
34
+ │ Your App │────▶│ RealtimeX Main │────▶│ Supabase │
35
+ │ (SDK) │ │ App (Proxy) │ │ Database │
36
+ └─────────────────┘ └──────────────────┘ └─────────────┘
37
+ ```
38
+
39
+ ## License
40
+
41
+ MIT
@@ -0,0 +1,43 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "realtimex-sdk"
7
+ version = "1.0.0"
8
+ description = "Python SDK for building Local Apps that integrate with RealtimeX"
9
+ readme = "python/README.md"
10
+ license = "MIT"
11
+ authors = [
12
+ { name = "RealtimeX Team", email = "team@realtimex.ai" }
13
+ ]
14
+ requires-python = ">=3.8"
15
+ classifiers = [
16
+ "Development Status :: 4 - Beta",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.8",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ ]
26
+ dependencies = [
27
+ "supabase>=2.0.0",
28
+ "httpx>=0.25.0",
29
+ ]
30
+
31
+ [project.optional-dependencies]
32
+ dev = [
33
+ "pytest>=7.0.0",
34
+ "pytest-asyncio>=0.21.0",
35
+ ]
36
+
37
+ [project.urls]
38
+ Homepage = "https://github.com/realtimex/rtx-local-app-sdk"
39
+ Documentation = "https://docs.realtimex.ai/local-apps"
40
+ Repository = "https://github.com/realtimex/rtx-local-app-sdk"
41
+
42
+ [tool.hatch.build.targets.wheel]
43
+ packages = ["python/realtimex_sdk"]
@@ -0,0 +1,76 @@
1
+ # RealtimeX Local App SDK - Python
2
+
3
+ Python SDK for building Local Apps that integrate with RealtimeX.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install realtimex-sdk
9
+ ```
10
+
11
+ ## Prerequisites
12
+
13
+ Before using this SDK, ensure your Supabase database is set up:
14
+
15
+ 1. Open **RealtimeX Main App** → **Local Apps** → Your App → **Configure**
16
+ 2. Enter your Supabase **URL** and **Anon Key**
17
+ 3. Select **Compatible Mode** and click **Login to Supabase**
18
+ 4. Click **Auto-Setup Schema** to create required tables and functions
19
+
20
+ > **Note:** Schema setup is handled entirely by the Main App.
21
+
22
+ ## Quick Start
23
+
24
+ ```python
25
+ import asyncio
26
+ from realtimex_sdk import RealtimeXSDK, SupabaseConfig, RealtimeXConfig
27
+
28
+ async def main():
29
+ # Initialize SDK
30
+ sdk = RealtimeXSDK(
31
+ supabase=SupabaseConfig(
32
+ url="https://your-project.supabase.co",
33
+ anon_key="your-anon-key"
34
+ ),
35
+ realtimex=RealtimeXConfig(
36
+ url="http://localhost:3001",
37
+ app_name="My Local App"
38
+ )
39
+ )
40
+
41
+ # Insert activity
42
+ activity = await sdk.activities.insert({
43
+ "type": "new_lead",
44
+ "email": "user@example.com"
45
+ })
46
+
47
+ # Trigger agent (auto-run)
48
+ result = await sdk.webhook.trigger_agent(
49
+ raw_data=activity,
50
+ auto_run=True,
51
+ agent_name="lead-processor",
52
+ workspace_slug="sales"
53
+ )
54
+ print(f"Task created: {result['task_uuid']}")
55
+
56
+ # Or create calendar event for manual review
57
+ result = await sdk.webhook.trigger_agent(
58
+ raw_data=activity,
59
+ auto_run=False
60
+ )
61
+
62
+ asyncio.run(main())
63
+ ```
64
+
65
+ ## Environment Variables
66
+
67
+ When your app is started by the Main App, these are auto-set:
68
+
69
+ | Variable | Description |
70
+ |----------|-------------|
71
+ | `RTX_APP_ID` | Your app's unique ID |
72
+ | `RTX_APP_NAME` | Your app's display name |
73
+
74
+ ## API Reference
75
+
76
+ See the main [TypeScript README](../typescript/README.md) for full API documentation.
@@ -0,0 +1,13 @@
1
+ """
2
+ RealtimeX Local App SDK - Python
3
+
4
+ SDK for building Local Apps that integrate with RealtimeX.
5
+ """
6
+
7
+ from .client import RealtimeXSDK
8
+ from .activities import ActivitiesModule
9
+ from .webhook import WebhookModule
10
+ from .api import ApiModule
11
+
12
+ __version__ = "1.0.0"
13
+ __all__ = ["RealtimeXSDK", "ActivitiesModule", "WebhookModule", "ApiModule"]
@@ -0,0 +1,67 @@
1
+ """
2
+ Activities Module - Supabase CRUD operations
3
+ """
4
+
5
+ from typing import Any, Dict, List, Optional
6
+ from supabase import create_client, Client
7
+
8
+
9
+ class ActivitiesModule:
10
+ """CRUD operations for activities table in Supabase."""
11
+
12
+ def __init__(self, supabase_url: str, supabase_key: str):
13
+ self.supabase: Client = create_client(supabase_url, supabase_key)
14
+ self.table_name = "activities"
15
+
16
+ async def insert(self, raw_data: Dict[str, Any]) -> Dict[str, Any]:
17
+ """Insert a new activity."""
18
+ response = self.supabase.table(self.table_name).insert({
19
+ "raw_data": raw_data,
20
+ "status": "pending"
21
+ }).execute()
22
+
23
+ if not response.data:
24
+ raise Exception("Failed to insert activity")
25
+
26
+ return response.data[0]
27
+
28
+ async def update(self, id: str, updates: Dict[str, Any]) -> Dict[str, Any]:
29
+ """Update an existing activity."""
30
+ response = self.supabase.table(self.table_name).update(
31
+ updates
32
+ ).eq("id", id).execute()
33
+
34
+ if not response.data:
35
+ raise Exception("Failed to update activity")
36
+
37
+ return response.data[0]
38
+
39
+ async def delete(self, id: str) -> None:
40
+ """Delete an activity."""
41
+ self.supabase.table(self.table_name).delete().eq("id", id).execute()
42
+
43
+ async def get(self, id: str) -> Optional[Dict[str, Any]]:
44
+ """Get an activity by ID."""
45
+ response = self.supabase.table(self.table_name).select(
46
+ "*"
47
+ ).eq("id", id).execute()
48
+
49
+ if not response.data:
50
+ return None
51
+
52
+ return response.data[0]
53
+
54
+ async def list(
55
+ self,
56
+ status: Optional[str] = None,
57
+ limit: int = 50
58
+ ) -> List[Dict[str, Any]]:
59
+ """List activities with optional filters."""
60
+ query = self.supabase.table(self.table_name).select("*")
61
+
62
+ if status:
63
+ query = query.eq("status", status)
64
+
65
+ response = query.order("created_at", desc=True).limit(limit).execute()
66
+
67
+ return response.data or []
@@ -0,0 +1,64 @@
1
+ """
2
+ API Module - Call RealtimeX public APIs
3
+ """
4
+
5
+ from typing import Any, Dict, List
6
+ import httpx
7
+
8
+
9
+ class ApiModule:
10
+ """Call RealtimeX public API endpoints."""
11
+
12
+ def __init__(self, realtimex_url: str):
13
+ self.realtimex_url = realtimex_url.rstrip("/")
14
+
15
+ async def get_agents(self) -> List[Dict[str, Any]]:
16
+ """Get available agents."""
17
+ async with httpx.AsyncClient() as client:
18
+ response = await client.get(f"{self.realtimex_url}/api/agents")
19
+ data = response.json()
20
+
21
+ if not response.is_success:
22
+ raise Exception(data.get("error", "Failed to get agents"))
23
+
24
+ return data.get("agents", [])
25
+
26
+ async def get_workspaces(self) -> List[Dict[str, Any]]:
27
+ """Get workspaces."""
28
+ async with httpx.AsyncClient() as client:
29
+ response = await client.get(f"{self.realtimex_url}/api/workspaces")
30
+ data = response.json()
31
+
32
+ if not response.is_success:
33
+ raise Exception(data.get("error", "Failed to get workspaces"))
34
+
35
+ return data.get("workspaces", [])
36
+
37
+ async def get_threads(self, workspace_slug: str) -> List[Dict[str, Any]]:
38
+ """Get threads in a workspace."""
39
+ async with httpx.AsyncClient() as client:
40
+ response = await client.get(
41
+ f"{self.realtimex_url}/api/workspaces/{workspace_slug}/threads"
42
+ )
43
+ data = response.json()
44
+
45
+ if not response.is_success:
46
+ raise Exception(data.get("error", "Failed to get threads"))
47
+
48
+ return data.get("threads", [])
49
+
50
+ async def get_task(self, task_uuid: str) -> Dict[str, Any]:
51
+ """Get task status."""
52
+ async with httpx.AsyncClient() as client:
53
+ response = await client.get(
54
+ f"{self.realtimex_url}/api/task/{task_uuid}"
55
+ )
56
+ data = response.json()
57
+
58
+ if not response.is_success:
59
+ raise Exception(data.get("error", "Failed to get task"))
60
+
61
+ return {
62
+ **data.get("task", {}),
63
+ "runs": data.get("runs", [])
64
+ }
@@ -0,0 +1,58 @@
1
+ """
2
+ RealtimeX SDK Client
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Optional
7
+
8
+ from .activities import ActivitiesModule
9
+ from .webhook import WebhookModule
10
+ from .api import ApiModule
11
+
12
+
13
+ @dataclass
14
+ class SupabaseConfig:
15
+ url: str
16
+ anon_key: str
17
+
18
+
19
+ @dataclass
20
+ class RealtimeXConfig:
21
+ url: str = "http://localhost:3001" # Default fallback
22
+ app_name: Optional[str] = None
23
+
24
+
25
+ class RealtimeXSDK:
26
+ """
27
+ Main SDK client for RealtimeX Local Apps.
28
+
29
+ Example:
30
+ sdk = RealtimeXSDK(
31
+ supabase=SupabaseConfig(
32
+ url="https://xxx.supabase.co",
33
+ anon_key="your-key"
34
+ ),
35
+ realtimex=RealtimeXConfig(
36
+ app_name="My App" # url defaults to localhost:3001
37
+ )
38
+ )
39
+ """
40
+
41
+ DEFAULT_REALTIMEX_URL = "http://localhost:3001"
42
+
43
+ def __init__(
44
+ self,
45
+ supabase: SupabaseConfig,
46
+ realtimex: Optional[RealtimeXConfig] = None
47
+ ):
48
+ if not supabase.url or not supabase.anon_key:
49
+ raise ValueError("Supabase URL and anon_key are required")
50
+
51
+ # Fallback realtimex config
52
+ realtimex_url = realtimex.url if realtimex else self.DEFAULT_REALTIMEX_URL
53
+ app_name = realtimex.app_name if realtimex else None
54
+
55
+ self.activities = ActivitiesModule(supabase.url, supabase.anon_key)
56
+ self.webhook = WebhookModule(realtimex_url, app_name)
57
+ self.api = ApiModule(realtimex_url)
58
+
@@ -0,0 +1,80 @@
1
+ """
2
+ Webhook Module - Call RealtimeX webhook
3
+ """
4
+
5
+ from typing import Any, Dict, Optional
6
+ import httpx
7
+
8
+
9
+ class WebhookModule:
10
+ """Call RealtimeX webhook endpoints."""
11
+
12
+ def __init__(self, realtimex_url: str, app_name: Optional[str] = None):
13
+ self.realtimex_url = realtimex_url.rstrip("/")
14
+ self.app_name = app_name
15
+
16
+ async def trigger_agent(
17
+ self,
18
+ raw_data: Dict[str, Any],
19
+ auto_run: bool = False,
20
+ agent_name: Optional[str] = None,
21
+ workspace_slug: Optional[str] = None,
22
+ thread_slug: Optional[str] = None
23
+ ) -> Dict[str, Any]:
24
+ """
25
+ Trigger agent via webhook.
26
+
27
+ Args:
28
+ raw_data: Data to send (required)
29
+ auto_run: If True, trigger agent immediately (default: False)
30
+ agent_name: Agent to trigger (required if auto_run)
31
+ workspace_slug: Workspace (required if auto_run)
32
+ thread_slug: Thread (optional)
33
+
34
+ Returns:
35
+ Response with task_uuid and status
36
+ """
37
+ if auto_run:
38
+ if not agent_name or not workspace_slug:
39
+ raise ValueError("auto_run requires agent_name and workspace_slug")
40
+
41
+ async with httpx.AsyncClient() as client:
42
+ response = await client.post(
43
+ f"{self.realtimex_url}/webhooks/realtimex",
44
+ json={
45
+ "app_name": self.app_name,
46
+ "event": "trigger-agent",
47
+ "payload": {
48
+ "raw_data": raw_data,
49
+ "auto_run": auto_run,
50
+ "agent_name": agent_name,
51
+ "workspace_slug": workspace_slug,
52
+ "thread_slug": thread_slug,
53
+ }
54
+ }
55
+ )
56
+
57
+ data = response.json()
58
+
59
+ if not response.is_success:
60
+ raise Exception(data.get("error", "Failed to trigger agent"))
61
+
62
+ return data
63
+
64
+ async def ping(self) -> Dict[str, Any]:
65
+ """Ping webhook to check connection."""
66
+ async with httpx.AsyncClient() as client:
67
+ response = await client.post(
68
+ f"{self.realtimex_url}/webhooks/realtimex",
69
+ json={
70
+ "app_name": self.app_name,
71
+ "event": "ping"
72
+ }
73
+ )
74
+
75
+ data = response.json()
76
+
77
+ if not response.is_success:
78
+ raise Exception(data.get("error", "Ping failed"))
79
+
80
+ return data
@@ -0,0 +1,142 @@
1
+ # RealtimeX Local App SDK
2
+
3
+ TypeScript/JavaScript SDK for building Local Apps that integrate with RealtimeX.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @realtimex/local-app-sdk
9
+ ```
10
+
11
+ ## Prerequisites
12
+
13
+ Before using this SDK, ensure your Supabase database is set up:
14
+
15
+ 1. Open **RealtimeX Main App** → **Local Apps** → Your App → **Configure**
16
+ 2. Enter your Supabase **URL** and **Anon Key**
17
+ 3. Select **Compatible Mode** and click **Login to Supabase**
18
+ 4. Click **Auto-Setup Schema** to create the required tables and functions
19
+
20
+ > **Note:** Schema setup is handled entirely by the Main App. You don't need to run any SQL manually.
21
+
22
+ ## Quick Start
23
+
24
+ ```typescript
25
+ import { RealtimeXSDK } from '@realtimex/local-app-sdk';
26
+
27
+ // No config needed - RTX_APP_ID is auto-detected from environment
28
+ const sdk = new RealtimeXSDK();
29
+
30
+ // Insert activity
31
+ const activity = await sdk.activities.insert({
32
+ type: 'new_lead',
33
+ email: 'user@example.com',
34
+ });
35
+
36
+ // Trigger agent (optional - for auto-processing)
37
+ await sdk.webhook.triggerAgent({
38
+ raw_data: activity,
39
+ auto_run: true,
40
+ agent_name: 'processor',
41
+ workspace_slug: 'sales',
42
+ });
43
+ ```
44
+
45
+ ## How It Works
46
+
47
+ When you start your Local App from the RealtimeX Main App:
48
+
49
+ 1. Environment variables `RTX_APP_ID` and `RTX_APP_NAME` are automatically set
50
+ 2. The SDK auto-detects these - no manual configuration needed
51
+ 3. All operations go through the Main App's proxy endpoints
52
+
53
+ ## Configuration (Optional)
54
+
55
+ ```typescript
56
+ const sdk = new RealtimeXSDK({
57
+ realtimex: {
58
+ url: 'http://custom-host:3001', // Default: localhost:3001
59
+ appId: 'custom-id', // Override auto-detected
60
+ appName: 'My App', // Override auto-detected
61
+ }
62
+ });
63
+ ```
64
+
65
+ ## API Reference
66
+
67
+ ### Activities CRUD
68
+
69
+ ```typescript
70
+ // Insert
71
+ const activity = await sdk.activities.insert({ type: 'order', amount: 100 });
72
+
73
+ // List
74
+ const pending = await sdk.activities.list({ status: 'pending', limit: 50 });
75
+
76
+ // Get
77
+ const item = await sdk.activities.get('activity-uuid');
78
+
79
+ // Update
80
+ await sdk.activities.update('activity-uuid', { status: 'processed' });
81
+
82
+ // Delete
83
+ await sdk.activities.delete('activity-uuid');
84
+ ```
85
+
86
+ ### Webhook - Trigger Agent
87
+
88
+ ```typescript
89
+ // Manual mode (creates calendar event only)
90
+ await sdk.webhook.triggerAgent({
91
+ raw_data: { email: 'customer@example.com' },
92
+ });
93
+
94
+ // Auto-run mode (creates event and triggers agent immediately)
95
+ await sdk.webhook.triggerAgent({
96
+ raw_data: activity,
97
+ auto_run: true,
98
+ agent_name: 'processor',
99
+ workspace_slug: 'sales',
100
+ thread_slug: 'optional-thread', // Optional: specific thread
101
+ });
102
+ ```
103
+
104
+ ### Public APIs
105
+
106
+ ```typescript
107
+ // Get available agents in a workspace
108
+ const agents = await sdk.api.getAgents();
109
+
110
+ // Get all workspaces
111
+ const workspaces = await sdk.api.getWorkspaces();
112
+
113
+ // Get threads in a workspace
114
+ const threads = await sdk.api.getThreads('sales');
115
+
116
+ // Get task status
117
+ const task = await sdk.api.getTask('task-uuid');
118
+ ```
119
+
120
+ ## Environment Variables
121
+
122
+ | Variable | Description |
123
+ |----------|-------------|
124
+ | `RTX_APP_ID` | Auto-set by Main App when starting your app |
125
+ | `RTX_APP_NAME` | Auto-set by Main App when starting your app |
126
+
127
+ ## Architecture
128
+
129
+ ```
130
+ ┌─────────────────┐ ┌──────────────────┐ ┌─────────────┐
131
+ │ Your App │────▶│ RealtimeX Main │────▶│ Supabase │
132
+ │ (SDK) │ │ App (Proxy) │ │ Database │
133
+ └─────────────────┘ └──────────────────┘ └─────────────┘
134
+ ```
135
+
136
+ - Your app uses the SDK to communicate with the Main App
137
+ - Main App proxies all database operations to Supabase
138
+ - Schema is managed by Main App (no direct database access needed)
139
+
140
+ ## License
141
+
142
+ MIT
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@realtimex/local-app-sdk",
3
+ "version": "1.0.0",
4
+ "description": "SDK for building Local Apps that integrate with RealtimeX",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
17
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
18
+ "test": "vitest run",
19
+ "prepublishOnly": "npm run build"
20
+ },
21
+ "keywords": [
22
+ "realtimex",
23
+ "local-apps",
24
+ "sdk",
25
+ "supabase"
26
+ ],
27
+ "author": "RealtimeX Team",
28
+ "license": "MIT",
29
+ "dependencies": {},
30
+ "devDependencies": {
31
+ "@types/node": "^20.10.0",
32
+ "tsup": "^8.0.0",
33
+ "typescript": "^5.3.0",
34
+ "vitest": "^1.0.0"
35
+ },
36
+ "files": [
37
+ "dist",
38
+ "README.md"
39
+ ],
40
+ "engines": {
41
+ "node": ">=18.0.0"
42
+ }
43
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * RealtimeX Local App SDK
3
+ *
4
+ * SDK for building Local Apps that integrate with RealtimeX
5
+ * All operations go through RealtimeX Main App proxy
6
+ */
7
+
8
+ import { SDKConfig } from './types';
9
+ import { ActivitiesModule } from './modules/activities';
10
+ import { WebhookModule } from './modules/webhook';
11
+ import { ApiModule } from './modules/api';
12
+
13
+ export class RealtimeXSDK {
14
+ public activities: ActivitiesModule;
15
+ public webhook: WebhookModule;
16
+ public api: ApiModule;
17
+ public readonly appId: string;
18
+ public readonly appName: string | undefined;
19
+
20
+ private static DEFAULT_REALTIMEX_URL = 'http://localhost:3001';
21
+
22
+ constructor(config: SDKConfig = {}) {
23
+ // Auto-detect app ID from environment (injected by Main App)
24
+ const envAppId = this.getEnvVar('RTX_APP_ID');
25
+ const envAppName = this.getEnvVar('RTX_APP_NAME');
26
+
27
+ this.appId = config.realtimex?.appId || envAppId || '';
28
+ this.appName = config.realtimex?.appName || envAppName;
29
+
30
+ // Default to localhost:3001
31
+ const realtimexUrl = config.realtimex?.url || RealtimeXSDK.DEFAULT_REALTIMEX_URL;
32
+
33
+ // Initialize modules
34
+ this.activities = new ActivitiesModule(realtimexUrl, this.appId);
35
+ this.webhook = new WebhookModule(realtimexUrl, this.appName, this.appId);
36
+ this.api = new ApiModule(realtimexUrl);
37
+ }
38
+
39
+ /**
40
+ * Get environment variable (works in Node.js and browser)
41
+ */
42
+ private getEnvVar(name: string): string | undefined {
43
+ // Node.js environment
44
+ if (typeof process !== 'undefined' && process.env) {
45
+ return process.env[name];
46
+ }
47
+ // Browser with injected globals
48
+ if (typeof window !== 'undefined') {
49
+ return (window as any)[name];
50
+ }
51
+ return undefined;
52
+ }
53
+ }
54
+
55
+ // Re-export types
56
+ export * from './types';
57
+
58
+ // Re-export modules for advanced usage
59
+ export { ActivitiesModule } from './modules/activities';
60
+ export { WebhookModule } from './modules/webhook';
61
+ export { ApiModule } from './modules/api';
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Activities Module - HTTP Proxy to RealtimeX Main App
3
+ * No direct Supabase access - all operations go through Main App
4
+ */
5
+
6
+ import { Activity } from '../types';
7
+
8
+ export class ActivitiesModule {
9
+ private baseUrl: string;
10
+ private appId: string;
11
+
12
+ constructor(realtimexUrl: string, appId: string) {
13
+ this.baseUrl = realtimexUrl.replace(/\/$/, '');
14
+ this.appId = appId;
15
+ }
16
+
17
+ private async request<T>(
18
+ path: string,
19
+ options: RequestInit = {}
20
+ ): Promise<T> {
21
+ const url = `${this.baseUrl}/api${path}`;
22
+ const headers: Record<string, string> = {
23
+ 'Content-Type': 'application/json',
24
+ };
25
+
26
+ // Add app ID header if available
27
+ if (this.appId) {
28
+ headers['X-App-Id'] = this.appId;
29
+ }
30
+
31
+ const response = await fetch(url, {
32
+ ...options,
33
+ headers: {
34
+ ...headers,
35
+ ...options.headers,
36
+ },
37
+ });
38
+
39
+ const data = await response.json();
40
+ if (!response.ok) {
41
+ throw new Error(data.error || `Request failed: ${response.status}`);
42
+ }
43
+ return data;
44
+ }
45
+
46
+ /**
47
+ * Insert a new activity
48
+ */
49
+ async insert(rawData: Record<string, unknown>): Promise<Activity> {
50
+ const result = await this.request<{ data: Activity }>('/activities', {
51
+ method: 'POST',
52
+ body: JSON.stringify({ raw_data: rawData }),
53
+ });
54
+ return result.data;
55
+ }
56
+
57
+ /**
58
+ * Update an existing activity
59
+ */
60
+ async update(id: string, updates: Partial<Activity>): Promise<Activity> {
61
+ const result = await this.request<{ data: Activity }>(`/activities/${id}`, {
62
+ method: 'PATCH',
63
+ body: JSON.stringify(updates),
64
+ });
65
+ return result.data;
66
+ }
67
+
68
+ /**
69
+ * Delete an activity
70
+ */
71
+ async delete(id: string): Promise<void> {
72
+ await this.request<{ success: boolean }>(`/activities/${id}`, {
73
+ method: 'DELETE',
74
+ });
75
+ }
76
+
77
+ /**
78
+ * Get a single activity by ID
79
+ */
80
+ async get(id: string): Promise<Activity | null> {
81
+ try {
82
+ const result = await this.request<{ data: Activity }>(`/activities/${id}`);
83
+ return result.data;
84
+ } catch (error: any) {
85
+ if (error.message?.includes('not found')) return null;
86
+ throw error;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * List activities with optional filters
92
+ */
93
+ async list(options?: { status?: string; limit?: number; offset?: number }): Promise<Activity[]> {
94
+ const params = new URLSearchParams();
95
+ if (options?.status) params.set('status', options.status);
96
+ if (options?.limit) params.set('limit', String(options.limit));
97
+ if (options?.offset) params.set('offset', String(options.offset));
98
+
99
+ const query = params.toString() ? `?${params}` : '';
100
+ const result = await this.request<{ data: Activity[] }>(`/activities${query}`);
101
+ return result.data;
102
+ }
103
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * API Module - Call RealtimeX public APIs
3
+ */
4
+
5
+ import { Agent, Workspace, Thread, Task } from '../types';
6
+
7
+ export class ApiModule {
8
+ private realtimexUrl: string;
9
+
10
+ constructor(realtimexUrl: string) {
11
+ this.realtimexUrl = realtimexUrl.replace(/\/$/, '');
12
+ }
13
+
14
+ async getAgents(): Promise<Agent[]> {
15
+ const response = await fetch(`${this.realtimexUrl}/api/agents`);
16
+ const data = await response.json();
17
+ if (!response.ok) throw new Error(data.error || 'Failed to get agents');
18
+ return data.agents;
19
+ }
20
+
21
+ async getWorkspaces(): Promise<Workspace[]> {
22
+ const response = await fetch(`${this.realtimexUrl}/api/workspaces`);
23
+ const data = await response.json();
24
+ if (!response.ok) throw new Error(data.error || 'Failed to get workspaces');
25
+ return data.workspaces;
26
+ }
27
+
28
+ async getThreads(workspaceSlug: string): Promise<Thread[]> {
29
+ const response = await fetch(`${this.realtimexUrl}/api/workspaces/${encodeURIComponent(workspaceSlug)}/threads`);
30
+ const data = await response.json();
31
+ if (!response.ok) throw new Error(data.error || 'Failed to get threads');
32
+ return data.threads;
33
+ }
34
+
35
+ async getTask(taskUuid: string): Promise<Task> {
36
+ const response = await fetch(`${this.realtimexUrl}/api/task/${encodeURIComponent(taskUuid)}`);
37
+ const data = await response.json();
38
+ if (!response.ok) throw new Error(data.error || 'Failed to get task');
39
+ return { ...data.task, runs: data.runs };
40
+ }
41
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Webhook Module - Call RealtimeX webhook
3
+ */
4
+
5
+ import { TriggerAgentPayload, TriggerAgentResponse } from '../types';
6
+
7
+ export class WebhookModule {
8
+ private realtimexUrl: string;
9
+ private appName?: string;
10
+ private appId?: string;
11
+
12
+ constructor(realtimexUrl: string, appName?: string, appId?: string) {
13
+ this.realtimexUrl = realtimexUrl.replace(/\/$/, '');
14
+ this.appName = appName;
15
+ this.appId = appId;
16
+ }
17
+
18
+ async triggerAgent(payload: TriggerAgentPayload): Promise<TriggerAgentResponse> {
19
+ if (payload.auto_run && (!payload.agent_name || !payload.workspace_slug)) {
20
+ throw new Error('auto_run requires agent_name and workspace_slug');
21
+ }
22
+
23
+ const response = await fetch(`${this.realtimexUrl}/webhooks/realtimex`, {
24
+ method: 'POST',
25
+ headers: { 'Content-Type': 'application/json' },
26
+ body: JSON.stringify({
27
+ app_name: this.appName,
28
+ app_id: this.appId,
29
+ event: 'trigger-agent',
30
+ payload: {
31
+ raw_data: payload.raw_data,
32
+ auto_run: payload.auto_run ?? false,
33
+ agent_name: payload.agent_name,
34
+ workspace_slug: payload.workspace_slug,
35
+ thread_slug: payload.thread_slug,
36
+ },
37
+ }),
38
+ });
39
+
40
+ const data = await response.json();
41
+ if (!response.ok) throw new Error(data.error || 'Failed to trigger agent');
42
+ return data as TriggerAgentResponse;
43
+ }
44
+
45
+ async ping(): Promise<{ success: boolean; app_name: string; message: string }> {
46
+ const response = await fetch(`${this.realtimexUrl}/webhooks/realtimex`, {
47
+ method: 'POST',
48
+ headers: { 'Content-Type': 'application/json' },
49
+ body: JSON.stringify({
50
+ app_name: this.appName,
51
+ app_id: this.appId,
52
+ event: 'ping'
53
+ }),
54
+ });
55
+ const data = await response.json();
56
+ if (!response.ok) throw new Error(data.error || 'Ping failed');
57
+ return data;
58
+ }
59
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * RealtimeX Local App SDK - Types
3
+ */
4
+
5
+ export interface SDKConfig {
6
+ realtimex?: {
7
+ url?: string; // Default: http://localhost:3001
8
+ appId?: string; // Auto-detected from RTX_APP_ID env
9
+ appName?: string; // Auto-detected from RTX_APP_NAME env
10
+ };
11
+ }
12
+
13
+ export interface Activity {
14
+ id: string;
15
+ raw_data: Record<string, unknown>;
16
+ old_data?: Record<string, unknown>;
17
+ status: 'pending' | 'claimed' | 'completed' | 'failed';
18
+ locked_by?: string;
19
+ locked_at?: string;
20
+ completed_at?: string;
21
+ error_message?: string;
22
+ result?: Record<string, unknown>;
23
+ created_at: string;
24
+ }
25
+
26
+ export interface TriggerAgentPayload {
27
+ raw_data: Record<string, unknown>;
28
+ auto_run?: boolean;
29
+ agent_name?: string;
30
+ workspace_slug?: string;
31
+ thread_slug?: string;
32
+ }
33
+
34
+ export interface TriggerAgentResponse {
35
+ success: boolean;
36
+ task_uuid?: string;
37
+ calendar_event_uuid?: string;
38
+ auto_run?: boolean;
39
+ message?: string;
40
+ error?: string;
41
+ }
42
+
43
+ export interface Agent {
44
+ slug: string;
45
+ name: string;
46
+ description?: string;
47
+ hub_id?: string;
48
+ }
49
+
50
+ export interface Workspace {
51
+ id: number;
52
+ slug: string;
53
+ name: string;
54
+ type: string;
55
+ created_at: string;
56
+ }
57
+
58
+ export interface Thread {
59
+ id: number;
60
+ slug: string;
61
+ name: string;
62
+ created_at: string;
63
+ }
64
+
65
+ export interface TaskRun {
66
+ id: number;
67
+ agent_name: string;
68
+ workspace_slug: string;
69
+ thread_slug?: string;
70
+ status: string;
71
+ started_at?: string;
72
+ completed_at?: string;
73
+ error?: string;
74
+ }
75
+
76
+ export interface Task {
77
+ uuid: string;
78
+ title: string;
79
+ status: string;
80
+ action_type: string;
81
+ source_app: string;
82
+ error?: string;
83
+ created_at: string;
84
+ updated_at: string;
85
+ runs: TaskRun[];
86
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "moduleResolution": "node",
6
+ "lib": [
7
+ "ES2020",
8
+ "DOM"
9
+ ],
10
+ "types": [
11
+ "node"
12
+ ],
13
+ "declaration": true,
14
+ "declarationMap": true,
15
+ "sourceMap": true,
16
+ "strict": true,
17
+ "esModuleInterop": true,
18
+ "skipLibCheck": true,
19
+ "forceConsistentCasingInFileNames": true,
20
+ "outDir": "./dist",
21
+ "rootDir": "./src",
22
+ "resolveJsonModule": true
23
+ },
24
+ "include": [
25
+ "src/**/*"
26
+ ],
27
+ "exclude": [
28
+ "node_modules",
29
+ "dist"
30
+ ]
31
+ }