Metadata-Version: 2.4
Name: sharkmagic
Version: 0.1.0
Summary: Python SDK for Shark: persistent Spaces for AI agents, every command in a fresh microVM
Author: Shark Magic
License-Expression: Apache-2.0
Project-URL: Homepage, https://sharkmagic.si
Keywords: sandbox,microvm,ai-agents,code-execution,shark
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.3; extra == "langchain"
Provides-Extra: langgraph
Requires-Dist: langchain-core>=0.3; extra == "langgraph"
Requires-Dist: langgraph>=0.3; extra == "langgraph"
Provides-Extra: smolagents
Requires-Dist: smolagents>=1.20; extra == "smolagents"
Provides-Extra: prefect
Requires-Dist: prefect>=3; extra == "prefect"
Dynamic: license-file

# Shark SDK for Python

Persistent Spaces for AI agents: every command runs in a fresh microVM, the files stay. Python 3.9+, standard
library only (no dependencies).

```sh
pip install sharkmagic
```

## Use

```python
from sharkmagic import Shark

shark = Shark()                                    # reads SHARK_API_KEY
space = shark.spaces.create(name="quickstart")

r = space.run("echo hello > hello.txt && cat hello.txt")
print(r.exit, r.stdout.strip())                    # 0 hello

print(space.files.read_text("hello.txt"))          # served from the committed tree, no VM

for fork in space.fork(count=2):                   # copies of the Space at the same commit, nothing copied
    print(fork.id, fork.run("cat hello.txt").stdout.strip())

print(space.run("exit 7").exit)                    # a failing command is a result, not an exception
```

Also: `space.run(cmd, cwd=..., env=..., timeout=..., check=True)`, `space.stream(cmd)` (live events),
`space.start(cmd)` (an `Op` at once), `space.files.write(...)`, `space.files.upload_dir(...)`, `space.hibernate()`,
`space.delete()`. Async: `AsyncShark`.

## SDK Spaces: nothing of your account unless asked

Spaces made by the SDK are `sdk` Spaces (`space.profile`): your account's secrets, GitHub connection, background jobs
and the in-Space CLI credential do **not** reach them unless the Space asks
(docs: [api.md#sdk-spaces-and-devbox-spaces-profile](../../site-docs/api.md#sdk-spaces-and-devbox-spaces-profile)).

```python
sp = shark.spaces.create(labels={"customer": "acme"}, egress="coding",      # "deny" | "coding" | "open" | a policy
                         account_secrets=["OPENAI_API_KEY"],                 # "all" | "none" (default) | names
                         github=False, git={"name": "Acme Bot", "email": "bot@acme.test"},
                         keep_background=False, keep_awake=False, space_cli=False)   # space_cli=["spaces:read"]: scopes
sp.update(account_secrets=None)                       # None: back to the profile's default
sp.run("python srv.py &", background=True)            # this command's leftovers stay as a job
forks = sp.fork(count=3, account_secrets="none")      # forks keep the source's choices unless overridden
[s.id for s in shark.spaces.list(label="customer:acme")]

sp.secrets.set("STRIPE_KEY", key, hosts=["api.stripe.com"], in_=["bearer"])   # inject (the value never enters the VM)
sp.secrets.set("GITHUB_TOKEN", tok, preset="github", allow_egress=True)         # bring your own GitHub token
sp.secrets.set("DB_URL", url, exec=True); sp.run("…", secrets=["DB_URL"])      # exec: in that command's env
sp.secrets.rotate("STRIPE_KEY", new_key); sp.secrets.edit("STRIPE_KEY", hosts=[…]); sp.secrets.remove("DB_URL")
sp.secrets.reference("OPENAI_API_KEY")                # use one account secret here
shark.secrets.set("OPENAI_API_KEY", key, preset="openai", apply="ref")   # account secrets; shark.secrets.presets()
shark.space_defaults.set(account_secrets="none")      # the account's default for unset fields
```

`profile="devbox"` makes a Space as `shark create` / `shark code` do (account secrets, GitHub, background jobs).

Your own GitHub App (control-plane mode): `shark.github_app.register(app_id, pem)` once, then
`shark.github_app.link(space, installation_id, repos=["site"])` stores that installation's token as the Space's
`GITHUB_TOKEN` (its node keeps it fresh); `status()`, `remove()`.

## Credentials

- `SHARK_API_KEY`: an API key from the Shark console (`shk_live_…`). Keep it in the environment, never in code.
- The SDK exchanges the key at the control plane (`https://api.sharkmagic.si`, or `SHARK_CP`) for short-lived node
  credentials and sends each call to the node that holds the Space. The key itself never reaches a node.
- Self-hosted node: `Shark(base_url=..., api_key=...)` or `SHARK_API` (the node's own key).

## Errors

Every API error is a `sharkmagic.SharkError` subclass with a stable `code` (`credits_exhausted`, `not_found`,
`quota_exceeded`, …). Retries keep the same `op_id`, so a command never runs twice.

## Framework integrations

Optional extras: `sharkmagic[langchain]`, `[langgraph]`, `[smolagents]`, `[prefect]` (`sharkmagic.integrations.*`).

## Docs

https://sharkmagic.si — quickstart, API reference, error codes and limits are linked from there.
