Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The package is not on PyPI yet.
| Reliability | [docs/RELIABILITY.md](docs/RELIABILITY.md) |
| Full catalog | [docs/INDEX.md](docs/INDEX.md) |
| End-user agent setup | [AGENT_SETUP.md](AGENT_SETUP.md) |
| End-user agent usage | [AGENT_USAGE.md](AGENT_USAGE.md) |

## Layout

Expand Down
18 changes: 16 additions & 2 deletions AGENT_SETUP.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Bild Python — agent setup playbook

You are an agent setting up the **Bild Python SDK** for a human. This file is
the only document you need. Follow it in order. Do not declare success until
the handshake at the end returns a value.
the only document you need for **install and verify**. After a successful
handshake, open [AGENT_USAGE.md](AGENT_USAGE.md) for day-to-day calls.
Follow this file in order. Do not declare success until the handshake at
the end returns a value.

Bild is a PDM/PLM product. This library talks to `https://api.getbild.com`
using a JWT personal access token. The package is used from source (not on
Expand Down Expand Up @@ -152,6 +154,18 @@ Do not declare setup complete without a successful `verify()` return value.
## After setup

You may keep using the same `BildClient()` for the human's next request.
Open [AGENT_USAGE.md](AGENT_USAGE.md) before guessing IDs or routes.

**Default branch:** do not look for a branch named `main` or `master`.
Bild names are often the project name. Call:

```python
branch_id = client.resolve_branch_id(project_id)
```

Or pass `branch_id=None` on branch-scoped methods. The helper lists
`GET /projects/{id}/branches` and picks the flagged default, else the
first branch.

Documented groups: `users`, `projects`, `project_users`, `branches`,
`commits`, `files`, `uploads`, `checkouts`, `shared_links`, `metadata`,
Expand Down
134 changes: 134 additions & 0 deletions AGENT_USAGE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Bild Python — agent usage

You are an agent using the **Bild Python SDK** after setup. Host is always
`https://api.getbild.com`. Auth is always a Bearer JWT (`BILD_API_KEY` /
`token=`). Do not invent a host, auth scheme, or endpoint.

If setup is not done, follow [AGENT_SETUP.md](AGENT_SETUP.md) first. Call
`BildClient.verify()` before other work.

## Start every task this way

```python
from bild import BildClient

client = BildClient() # reads BILD_API_KEY from .env
projects = client.api.projects.list()
```

Pick IDs from API responses. They are UUID v4 values. Do not invent IDs.
List payloads are often `{ "data": [ ... ], "message": "success" }`. Read
`id` (or `projectID` / `fileID` / `branchID` when that is what the item uses).

## Default branch — do not guess `main` or `master`

Bild branches are often named after the project. Names like `main` and
`master` frequently do not exist. Never ask the human for a branch id
until `resolve_branch_id` has failed.

```python
branch_id = client.resolve_branch_id(project_id)
```

That lists `GET /projects/{project_id}/branches` and picks, in order:

1. A branch flagged `isMain`, `isDefault`, `isDefaultBranch`, or `default`
2. A branch named `main` or `master` (case-insensitive)
3. The first branch in the list

You can also pass `branch_id=None` into any branch-scoped method. The SDK
resolves it the same way.

```python
files = client.api.files.list_versions(project_id, None, file_id)
boms = client.api.boms.list(project_id) # None is the default
meta = client.api.metadata.get(project_id, None, file_id)
```

These list calls are different: omitting `branch_id` hits a **project-level**
route (the API's own default-branch / account-wide list), not a resolved
branch path:

- `client.api.files.list(project_id)` → `GET /projects/{id}/files`
- `client.api.commits.list(project_id)` → `GET /projects/{id}/commits`
- `client.api.shared_links.list(project_id)` → `GET /projects/{id}/sharedLinks`
- `client.api.revisions.list(project_id)` → `GET /projects/{id}/revisions`
- `client.api.feedback.list(project_id)` → `GET /projects/{id}/feedbackItems`

To force a specific branch on those, pass the id from `resolve_branch_id`
or `client.api.branches.list(project_id)`.

## Latest file version

```python
version_id = client.resolve_file_version(project_id, branch_id, file_id)
# or
latest = client.api.files.get_latest(project_id, None, file_id)
```

`files.export_universal(..., branch_id=None, file_version=None)` resolves
both the default branch and the latest version.

## Resource groups

Use `client.api.<group>`. Do not invent methods.

| Group | Typical calls |
| --- | --- |
| `users` | `list` |
| `projects` | `list` |
| `project_users` | `list` |
| `branches` | `list` |
| `commits` | `list`, `get` |
| `files` | `list`, `list_versions`, `get_latest`, `get_version`, `export_universal` |
| `shared_links` | `list` |
| `metadata` | `list_fields`, `get` |
| `feedback` | `list`, `get` |
| `packages` | `list`, `get` |
| `revisions` | `list`, `get`, `get_closure` |
| `approvals` | `list`, `get` |
| `boms` | `list`, `get` |
| `search` | `files("query")` — this is `PUT /search` |
| `webhooks` | `list`, `get` |

Writes (invite, upload, checkout, move, delete, release, create shared
link, create webhook) only if the human explicitly asked. Prefer
list/get/search.

## Errors

| Exception | When |
| --- | --- |
| `ValueError` | Missing token, or no branch/version could be resolved |
| `BildAuthError` | HTTP 401/403 — ask for a new JWT; do not retry blindly |
| `BildAPIError` | Other HTTP failures — show `status_code` and `payload` |

```python
from bild import BildAPIError, BildAuthError
```

## Response envelopes

- Success items usually live under `data` (sometimes `items`).
- Large lists may return `{ "s3Url": "..." }` instead of an inline array.
GET that URL yourself. Do **not** send the Bild `Authorization` header
to S3.
- Some writes are async. A 200 means accepted; the change may finish later.

## Escape hatch

Unwrapped path only. Do not set `Content-Type` on a shared session if you
drop to raw `requests` — Bild treats that header as "this request has a
JSON body" and GET/DELETE then 500.

```python
raw = client.get("projects")
```

## Do not

- Guess branch names (`main`, `master`, …).
- Invent hosts, tokens, or endpoints.
- Commit `.env` or print the JWT.
- Write or delete unless the human asked.
- Follow `s3Url` with the Bild bearer token.
27 changes: 24 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ library, call `BildClient.verify()`, and show you the return value.
https://github.com/AJFrio/Bild-Python/blob/main/AGENT_SETUP.md
```

Or hand it the file in this repo: [AGENT_SETUP.md](AGENT_SETUP.md).
After setup, hand it [AGENT_USAGE.md](AGENT_USAGE.md) (or
`https://github.com/AJFrio/Bild-Python/blob/main/AGENT_USAGE.md`) so it
resolves the default branch instead of guessing `main` / `master`.

Or hand it the files in this repo: [AGENT_SETUP.md](AGENT_SETUP.md) and
[AGENT_USAGE.md](AGENT_USAGE.md).

## 1) Clone and set up (manual)

Expand Down Expand Up @@ -100,9 +105,25 @@ client.api.users.invite(
)
```

### Default branch

Bild branches are often **not** named `main` or `master`. Do not guess
those names. Resolve the id, or pass `branch_id=None` on branch-scoped
methods:

```python
branch_id = client.resolve_branch_id("project-id")
branches = client.api.branches.list("project-id")
```

`resolve_branch_id` lists the project's branches and prefers a flagged
default (`isMain` / `isDefault` / `isDefaultBranch`), then a main/master
name, then the first branch.

### List files in a project

```python
# Official default-branch file list (no branch id needed)
files = client.api.files.list("project-id")
print(files)
```
Expand All @@ -112,7 +133,7 @@ print(files)
```python
result = client.api.files.export_universal(
project_id="project-id",
branch_id=None, # auto-resolves main/default branch
branch_id=None, # resolve_branch_id — not a guessed "main" name
file_id="file-id",
output_format="stl",
)
Expand All @@ -127,7 +148,7 @@ print(links)

new_link = client.api.shared_links.create_live(
"project-id",
"branch-id",
None, # default branch
name="Review Link",
file_ids=["file-id"],
)
Expand Down
Loading
Loading