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.
- realtimex_sdk-1.0.0/.github/workflows/npm-publish.yml +37 -0
- realtimex_sdk-1.0.0/.github/workflows/pypi-publish.yml +32 -0
- realtimex_sdk-1.0.0/PKG-INFO +102 -0
- realtimex_sdk-1.0.0/README.md +41 -0
- realtimex_sdk-1.0.0/pyproject.toml +43 -0
- realtimex_sdk-1.0.0/python/README.md +76 -0
- realtimex_sdk-1.0.0/python/realtimex_sdk/__init__.py +13 -0
- realtimex_sdk-1.0.0/python/realtimex_sdk/activities.py +67 -0
- realtimex_sdk-1.0.0/python/realtimex_sdk/api.py +64 -0
- realtimex_sdk-1.0.0/python/realtimex_sdk/client.py +58 -0
- realtimex_sdk-1.0.0/python/realtimex_sdk/webhook.py +80 -0
- realtimex_sdk-1.0.0/typescript/README.md +142 -0
- realtimex_sdk-1.0.0/typescript/package.json +43 -0
- realtimex_sdk-1.0.0/typescript/src/index.ts +61 -0
- realtimex_sdk-1.0.0/typescript/src/modules/activities.ts +103 -0
- realtimex_sdk-1.0.0/typescript/src/modules/api.ts +41 -0
- realtimex_sdk-1.0.0/typescript/src/modules/webhook.ts +59 -0
- realtimex_sdk-1.0.0/typescript/src/types.ts +86 -0
- realtimex_sdk-1.0.0/typescript/tsconfig.json +31 -0
|
@@ -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
|
+
}
|