Metadata-Version: 2.4
Name: linkworld-sdk
Version: 0.1.0
Summary: Build apps for the Linkworld Open App Platform
Project-URL: Homepage, https://linkworld.ai
Project-URL: Documentation, https://linkworld.ai/docs
Project-URL: Source, https://github.com/linkworld/linkworld
Project-URL: Issues, https://github.com/linkworld/linkworld/issues
Author-email: Linkworld <hello@linkworld.ai>
License: MIT
License-File: LICENSE
Keywords: agents,ai,linkworld,mcp,platform
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Requires-Python: >=3.10
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.6
Requires-Dist: pyyaml>=6.0
Requires-Dist: structlog>=24.1
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# linkworld-sdk

Python SDK for building apps on the
[Linkworld](https://linkworld.ai) Open App Platform.

> **Status: 0.x alpha.** Phase 2 of the open-platform rollout. Decorator
> API is stable; production runtime (MCP-based dispatch) lands with
> platform Milestone 5. Until then, run apps in local mode to develop
> against the SDK, then deploy once the platform side is online.

## Install

```bash
pip install linkworld-sdk
```

## 60-second tour

```python
from linkworld_sdk import App

app = App.from_manifest("linkworld.app.yaml")

@app.on_inbound
async def handle(ctx, env):
    await ctx.tools.call(
        "email_send",
        to="ops@example.com",
        subject="Inbound!",
        body=f"From {env.user_id}: {env.message_text}",
    )

if __name__ == "__main__":
    app.run()
```

The matching manifest:

```yaml
apiVersion: linkworld.ai/v2
app_id: my-app
version: 0.1.0
name: My App
required_scopes: [mail.send]
runtime:
  image: ghcr.io/your-org/my-app:0.1.0
lifecycle:
  on_inbound: true
```

## Local development

```bash
LINKWORLD_LOCAL=1 python main.py
```

This starts a trigger HTTP server you can `curl` to simulate any
platform event. Tools are mocked — `ctx.tools.call(...)` records
the call and returns whatever you wired with `MockTools`.

See [`examples/hello-world/`](./examples/hello-world/) for a runnable
walkthrough.

## Testing your app

```python
import pytest
from linkworld_sdk.testing import TestClient
from my_app import app

@pytest.fixture
def client():
    return TestClient(app)

async def test_echo(client):
    client.tools.set_response("email_send", {"sent": True})
    await client.simulate_inbound(
        tenant_id="t1", user_id="u1", message_text="hello"
    )
    assert client.tools.calls[0][0] == "email_send"
    assert client.tools.calls[0][1]["body"] == "From u1: hello"
```

## Decorator API

| Decorator | Receives | When |
|---|---|---|
| `@app.on_inbound` | `(ctx, env)` | Tenant got an inbound message and opted into fan-out |
| `@app.on_install` | `(ctx)` | Tenant activated the app |
| `@app.on_uninstall` | `(ctx)` | Tenant deactivated the app |
| `@app.on_user_added` | `(ctx, user)` | New user joined the tenant |
| `@app.on_schedule(name)` | `(ctx)` | Cron entry from `lifecycle.schedules[name]` fires |
| `@app.tool(name, ...)` | `(ctx, **args)` | Custom tool exposed to tenant agents |

Every handler receives a `Context`:

```python
ctx.tenant_id        # UUID of the tenant
ctx.user_id          # UUID of the originating user, or None
ctx.app_id           # this app's slug
ctx.event_type       # 'inbound', 'schedule', 'install', etc.
ctx.tools            # ctx.tools.call("tool_name", **args)
ctx.secrets          # await ctx.secrets.get("KEY")
ctx.logger           # structlog-compatible logger
```

## Manifest schema

The full schema lives at
[`packages/sdk-spec/manifest-v2.schema.yaml`](https://github.com/linkworld/linkworld/blob/main/packages/sdk-spec/manifest-v2.schema.yaml).
Validate locally with `linkworld_sdk.load_manifest("linkworld.app.yaml")`.

## License

MIT — see [LICENSE](./LICENSE).
