Skip to content
Merged
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

# inkbox is mocked in the tests, so install only what they import.
- name: Install test deps
run: pip install pytest aiohttp segno claude-agent-sdk

- name: Test
run: pytest -q
2 changes: 1 addition & 1 deletion inkbox_claude/doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def run_doctor() -> List[Tuple[str, bool, str]]:
import inkbox # noqa: F401
checks.append(("inkbox SDK", True, "installed"))
except ImportError:
checks.append(("inkbox SDK", False, "pip install 'inkbox>=0.4.7'"))
checks.append(("inkbox SDK", False, "pip install 'inkbox>=0.4.10'"))

try:
import claude_agent_sdk # noqa: F401
Expand Down
2 changes: 1 addition & 1 deletion inkbox_claude/gateway.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ async def run(self) -> None:
if not AIOHTTP_AVAILABLE:
raise RuntimeError("aiohttp is not installed; run: pip install aiohttp")
if not INKBOX_AVAILABLE:
raise RuntimeError("inkbox SDK is not installed; run: pip install 'inkbox>=0.4.7'")
raise RuntimeError("inkbox SDK is not installed; run: pip install 'inkbox>=0.4.10'")
if not self.cfg.api_key or not self.cfg.identity:
raise RuntimeError("INKBOX_API_KEY and INKBOX_IDENTITY must be set (see README)")

Expand Down
33 changes: 30 additions & 3 deletions inkbox_claude/setup_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@

# Packages the wizard itself needs to talk to Inkbox during setup. The
# gateway's other dependency (claude-agent-sdk) is checked by doctor.
INKBOX_REQUIREMENTS = ("inkbox>=0.4.7", "aiohttp>=3.9")
INKBOX_MIN_VERSION = (0, 4, 10)
INKBOX_REQUIREMENTS = ("inkbox>=0.4.10", "aiohttp>=3.9")
_BRACKETED_PASTE_PATTERN = re.compile(r"\x1b\[\s*200~|\x1b\[\s*201~")

# Bundled avatar attached to the agent's Inkbox contact card during setup.
Expand Down Expand Up @@ -366,18 +367,43 @@ def _load_inkbox_symbols() -> dict[str, Any]:
}


def _inkbox_version_ok() -> bool:
try:
from importlib.metadata import version

raw = version("inkbox")
except Exception:
return True # can't tell — let the import/runtime path surface it
try:
from packaging.version import Version

return Version(raw) >= Version(".".join(str(p) for p in INKBOX_MIN_VERSION))
except Exception:
parts = []
for chunk in raw.split(".")[:3]:
digits = "".join(c for c in chunk if c.isdigit())
parts.append(int(digits) if digits else 0)
return tuple(parts) >= INKBOX_MIN_VERSION


def _ensure_inkbox_sdk() -> dict[str, Any] | None:
"""Import the Inkbox SDK, offering to install it into this env if missing.

Returns:
dict[str, Any] | None: SDK symbols on success, None if unavailable.
"""
try:
return _load_inkbox_symbols()
symbols = _load_inkbox_symbols()
if _inkbox_version_ok():
return symbols
first_error = (
"the installed inkbox SDK is older than "
f"{'.'.join(str(p) for p in INKBOX_MIN_VERSION)}"
)
except Exception as exc:
first_error = exc

print_warning("The Python Inkbox SDK is not available in this environment.")
print_warning("The Python Inkbox SDK is missing or too old in this environment.")
print_info("The setup command is running under:")
print_info(f" {sys.executable}")
print_info("Install or upgrade the SDK in that exact environment with:")
Expand Down Expand Up @@ -1109,6 +1135,7 @@ def _self_signup_flow(base_url: str, Inkbox: Any, InkboxAPIError: Any) -> tuple[
note_to_human=note,
agent_handle=handle,
base_url=base_url,
harness="claude-code",
)
break
except InkboxAPIError as exc:
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description = "Inkbox bridge for Claude Code — talk to your coding agent over
requires-python = ">=3.10"
dependencies = [
"aiohttp>=3.9",
"inkbox>=0.4.7",
"inkbox>=0.4.10",
"claude-agent-sdk>=0.1.0",
"segno>=1.5", # terminal QR codes for the iMessage connect step
]
Expand Down
8 changes: 4 additions & 4 deletions tests/test_setup_wizard.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def test_install_command_prefers_uv_when_available(monkeypatch):
"install",
"--python",
"/tmp/venv/bin/python",
"inkbox>=0.4.7",
"inkbox>=0.4.10",
"aiohttp>=3.9",
]]

Expand All @@ -93,10 +93,10 @@ def test_install_command_falls_back_to_pip_and_ensurepip(monkeypatch):
monkeypatch.setattr(setup_wizard.shutil, "which", lambda _name: None)

assert setup_wizard._install_commands() == [
[["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.7", "aiohttp>=3.9"]],
[["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.10", "aiohttp>=3.9"]],
[
["/tmp/venv/bin/python", "-m", "ensurepip", "--upgrade"],
["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.7", "aiohttp>=3.9"],
["/tmp/venv/bin/python", "-m", "pip", "install", "inkbox>=0.4.10", "aiohttp>=3.9"],
],
]

Expand All @@ -115,7 +115,7 @@ def fail_import():
out = capsys.readouterr().out
assert "/tmp/venv/bin/python" in out
assert "uv pip install --python" in out
assert "inkbox>=0.4.7" in out
assert "inkbox>=0.4.10" in out


# ----------------------------------------------------------------------
Expand Down
Loading