Skip to content

derfsss/ClaudeLibrary

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

claude.library

A native AmigaOS 4 shared library for the Anthropic Claude API.

Status: ALPHA. The library builds, loads cleanly, and passes its 35-test harness against a live api.anthropic.com endpoint - but the API surface is not yet frozen, on-target time is still measured in days rather than months, and several features (tool- use round-trip, chunked-encoding decode, OAuth, AmiSSL fallback) are pending. Expect bugs and breaking changes between releases. Not yet recommended for production use.

What is this?

claude.library is an Exec-style .library that lets any AmigaOS 4 program talk to Anthropic's Claude language models. It speaks HTTPS directly to api.anthropic.com over bsdsocket.library and AmiSSL, parses the JSON replies, and exposes the result through a familiar OS4 tag-driven interface (IClaude->Ask(...), IClaude->StreamAsk(...), etc.). It also publishes a public CLAUDE ARexx host port so any ARexx-capable application (YAM, CubicIDE, AmIRC, DOpus5, Hollywood, ...) can use Claude with a one-line ADDRESS CLAUDE 'ASK "..."'.

Who is it for?

  • AmigaOS 4 developers who want to add Claude-powered features (chat, summarisation, translation, code assistance, etc.) to a C/C++ or Hollywood application without writing HTTPS, JSON or retry logic by hand.
  • ARexx scripters who want to call Claude from any AmigaOS app that exposes an ARexx interface, with no programming required beyond ADDRESS CLAUDE.
  • Tool authors who want a single shared dependency that handles the API plumbing across multiple programs - the library is ~90 KB on disk and loads once, regardless of how many consumers open it.

Because everything lives in one shared library, applications stay small: a few IClaude-> calls plus whatever UI they need. No application re-implements TLS, transport, JSON, conversation history, streaming or response parsing.

Status Alpha - API not frozen, expect changes
Version 53.1-alpha (initial release)
Target AmigaOS 4.1 Final Edition, PowerPC
Size ~90 KB on disk, statically linked
Runtime deps bsdsocket.library v4+, amisslmaster.library v5+, newlib.library v53
License zlib

What it does

  • Opens HTTPS connections to api.anthropic.com (or any compatible endpoint) using bsdsocket.library + AmiSSL directly. No libcurl embed, no .so dependencies.
  • Builds Anthropic /v1/messages requests, parses responses, manages multi-turn conversation history.
  • Streams replies via Server-Sent Events into a caller-supplied struct Hook, with a dedicated worker Process so your Intuition loop stays responsive.
  • Renders Markdown replies to AmigaGuide (with working hyperlinks via amigaguide.library), plain text, or console-coloured output.
  • Publishes a public ARexx host port named CLAUDE that any ARexx-capable application (YAM, CubicIDE, AmIRC, DOpus5, Hollywood, …) can hit with ADDRESS CLAUDE 'ASK "..."' and read the reply from the RESULT variable.
  • Persists conversations as portable JSON via SaveConversation / LoadConversation.
  • Reads the API key from ENV:CLAUDE_API_KEY if not configured explicitly.

The library is designed to fail gracefully: every public method checks its inputs, every transport call validates that bsdsocket.library and AmiSSL are usable before dereferencing interfaces, and any failure surfaces a structured error code plus a human-readable string through IClaude->LastError. The library never crashes its caller.

Requirements

Runtime (on the AmigaOS 4 machine):

  • AmigaOS 4.1 Final Edition (or compatible).
  • bsdsocket.library v4 or newer. Provided by Roadshow on a stock AmigaOS 4.1 install.
  • amisslmaster.library v5 or newer, plus a working AmiSSL_v3xx.library.
  • newlib.library v53 (ships with AmigaOS 4.1 FE).
  • dos.library, utility.library, rexxsyslib.library, amigaguide.library (all stock).

Build host:

  • Docker Desktop (any recent version).
  • The cross-compiler image walkero/amigagccondocker:os4-gcc11 (the build script pulls it on first use).
  • The AmigaOS 4 SDK headers under $CLAUDELIB_SDK_DIR (defaults to ~/sdk).

No host-side AmigaOS toolchain installation required — everything runs inside the container.

Quick start

git clone https://github.com/<you>/ClaudeLibrary
cd ClaudeLibrary
./build.sh

This produces three artefacts in bin/:

  • claude.library — copy to LIBS: on your AmigaOS machine.
  • claudesh — minimal CLI consumer (claudesh "your prompt").
  • test_claude_library — 35-test TAP-style smoke test.

Then on the AmigaOS side:

SetEnv CLAUDE_API_KEY "sk-ant-api03-..."
test_claude_library --live

Using it from C

#include <proto/claude.h>

struct Library     *ClaudeBase;
struct ClaudeIFace *IClaude;

int main(void) {
    struct ClaudeConv *conv;
    STRPTR reply;

    ClaudeBase = IExec->OpenLibrary("claude.library", 53);
    IClaude    = (struct ClaudeIFace *)
        IExec->GetInterface(ClaudeBase, "main", 1, NULL);

    IClaude->Configure(
        CLA_DefaultModel, (uint32)CLAUDE_MODEL_HAIKU_4_5,
        CLA_ConnectTimeout, 15,
        TAG_END);

    conv = IClaude->NewConversation(
        CLA_System, (uint32)"You are a concise assistant.",
        TAG_END);

    reply = IClaude->Ask(conv, (CONST_STRPTR)"What year did the Amiga 1000 ship?",
                         CLA_MaxTokens, 64,
                         TAG_END);
    if (reply) {
        IDOS->Printf("%s\n", reply);
        IExec->FreeVec(reply);
    } else {
        IDOS->Printf("error: %s\n", IClaude->LastError(conv));
    }

    IClaude->FreeConversation(conv);
    IExec->DropInterface((struct Interface *)IClaude);
    IExec->CloseLibrary(ClaudeBase);
    return 0;
}

Using it from ARexx

Any time claude.library is open in a process that called Configure(CLA_StartArexxPort, TRUE, TAG_END), the public port CLAUDE accepts the following verbs:

Verb Action
VERSION RESULT set to "claude.library 53.1"
MODEL <name> switch the default conversation's model
SYSTEM <text> set the default conversation's system prompt
RESET discard the default conversation, start fresh
ASK <prompt> send <prompt>; RESULT set to the reply text
LASTERROR RESULT set to the last error string (or empty)
/* askclaude.rexx */
ADDRESS CLAUDE
SYSTEM "You are a terse Amiga developer."
ASK "What is the V53 entry for FillPenA in graphics.library?"
SAY RESULT

Streaming

struct StreamCounters { int delta; char buf[2048]; size_t len; };

static uint32 my_stream_hook(struct Hook *h,
                             struct ClaudeConv *conv,
                             struct ClaudeStreamEvent *ev)
{
    struct StreamCounters *c = (struct StreamCounters *)h->h_Data;
    switch (ev->cse_Type) {
        case CSEV_Delta:
            if (c->len + ev->cse_Length < sizeof(c->buf)) {
                memcpy(c->buf + c->len, ev->cse_Text, ev->cse_Length);
                c->len += ev->cse_Length;
            }
            c->delta++;
            break;
        case CSEV_Stop:
            IDOS->Printf("\n[done after %d deltas]\n", c->delta);
            break;
        case CSEV_Error:
            IDOS->Printf("\n[error: %s]\n", IClaude->LastError(conv));
            break;
    }
    return 0;
}

struct Hook stream_hook = { .h_Entry = (HOOKFUNC)my_stream_hook,
                            .h_Data  = &counters };
IClaude->StreamAsk(conv, prompt,
                   CLA_StreamHook, (uint32)&stream_hook,
                   TAG_END);

The hook is called from the worker process on each event. To receive events on your own task, have the hook post a struct Message to a port you own.

Architecture

   +----------------------+
   |    your OS4 app      |
   +----------+-----------+
              | OpenLibrary("claude.library", 53)
              v
   +----------------------+
   |   claude.library     |  ROMTag-based AmigaOS shared library
   |  (IClaude interface) |  - tag-driven API
   +--+-----+-----+-------+
      |     |     |
      |     |     +-->  amigaguide.library  (markdown rendering)
      |     |
      |     +-------->  dos.library + utility.library + newlib.library
      |                  (interfaces opened in libInit and per worker task)
      |
      +-------------->  bsdsocket.library + amisslmaster.library
                         (HTTPS to api.anthropic.com)

Two long-running worker Processes exist when their features are used:

  • claude.rexx-port — owns the public CLAUDE MsgPort, dispatches ARexx commands. Spawned by Configure(CLA_StartArexxPort, TRUE).
  • claude.stream — runs the HTTPS streaming transport so StreamAsk returns immediately. Spawned per StreamAsk call.

Both workers OpenLibrary("newlib.library") in their own task context so their per-task _REENT is initialised before any libc call. They also AllocSignal their own shutdown signal — cross-task signal allocation is invalid per exec.library/AllocSignal.

Building

./build.sh           # build everything (library + examples + tests)
./build.sh lib       # library only
./build.sh tests     # the test harness only
./build.sh clean

The script invokes ppc-amigaos-gcc inside Docker via docker-cc, which mounts the project tree at /work and the SDK at /sdk.

Override paths with environment variables:

CLAUDELIB_IMAGE=walkero/amigagccondocker:os4-gcc11 \
CLAUDELIB_PROJECT_DIR=/path/to/repo \
CLAUDELIB_SDK_DIR=/path/to/sdk \
    ./build.sh

A standard make works too:

make            # library + examples + tests
make lib
make examples
make tests
make clean

Build flags worth knowing:

  • -mcrt=newlib — uses newlib as the C runtime.
  • -nostartfiles — no CLI startup glue; the ROMTag is the entry point.
  • No -shared — the library is a regular ELF EXEC binary with a Resident structure scanned by exec.library at OpenLibrary time. Building with -shared produces a DYN object with libc.so / libgcc.so dependencies that may load the wrong runtime under multi-libc setups.

Testing

The bundled TAP-style harness exercises every IClaude method end-to-end:

# On the AmigaOS machine:
test_claude_library              # 35 tests, dry-run + ARexx port
test_claude_library --skip-rexx  # skip the host-port dispatch test
test_claude_library --live       # also hit api.anthropic.com (needs key)
test_claude_library --hold 30    # keep the CLAUDE port alive for 30s
                                 # so external rx scripts can use it

For QEMU-based testing the repo also includes host-side helpers under tests/:

  • drive_test.py — drives the on-target test via SerialShell.
  • mcpd_client.py — generic MCPd JSON-RPC client (useful if your AmigaOS QEMU runs the MCPd daemon).

Authentication

claude.library reads ENV:CLAUDE_API_KEY (global env) on first use unless an explicit key is supplied via:

IClaude->Configure(CLA_ApiKey, (uint32)"sk-ant-api03-...", TAG_END);

To persist the key across reboots on the guest:

SetEnv CLAUDE_API_KEY "sk-ant-api03-..."
Copy ENV:CLAUDE_API_KEY ENVARC:

Treat the key as a secret. The library does not log it; the request sends it only in the x-api-key HTTPS header.

OAuth-against-subscription is not currently supported because Anthropic routes third-party OAuth tokens to a separate empty billing pool. An issue tracking eventual re-enablement is anthropics/claude-code#37205.

Public API at a glance

All methods take a struct ClaudeIFace *Self (implicit when called through IClaude->...). Tag identifiers live under (TAG_USER | 0x00CD0000) to avoid clashing with other libraries.

Method Purpose
Configure(... TAG_END) Set API key, endpoint, default model, timeouts, debug flags, start ARexx port.
NewConversation(... TAG_END)ClaudeConv * Begin a conversation. Tags: CLA_System, CLA_Model, CLA_UseCaching.
FreeConversation(conv) Release conversation memory; aborts any active stream first.
SaveConversation(conv, "T:foo.json") JSON serialise.
LoadConversation("T:foo.json")ClaudeConv * JSON deserialise.
Ask(conv, prompt, ... TAG_END)STRPTR Synchronous request. Caller frees the reply via IExec->FreeVec. Tags: CLA_MaxTokens, CLA_Temperature, CLA_Model, CLA_TimeoutSecs, CLA_Image.
StreamAsk(conv, prompt, CLA_StreamHook, &h, ... TAG_END)LONG Async streaming via SSE; events delivered through a struct Hook.
AbortStream(conv) Cancel an in-flight stream.
AddTool(conv, name, desc, schema_json, hook) Register a tool the model can call.
RenderMarkdown(md, format, dest, ... TAG_END)LONG format is CLAR_AmigaGuide, CLAR_PlainText or CLAR_Console; dest is a filename.
LastError(conv)CONST_STRPTR Human-readable error message for the last failed call.
LastErrorCode(conv)LONG CLERR_* value.

Full reference: see include/libraries/claude.h (tags + struct layouts) and include/interfaces/claude.h (struct ClaudeIFace).

Project layout

ClaudeLibrary/
├── include/              public SDK headers
│   ├── libraries/claude.h     tags, structs, error codes
│   ├── interfaces/claude.h    struct ClaudeIFace definition
│   ├── proto/claude.h         global IClaude / ClaudeBase decls
│   └── clib/claude_protos.h   C convenience prototypes
├── src/                  library implementation
│   ├── library.c              ROMTag + manager interface
│   ├── iface_main.c           IClaude vector destinations
│   ├── config.c               IClaude->Configure
│   ├── conversation.c         NewConversation / Save / Load
│   ├── ask.c                  IClaude->Ask (sync)
│   ├── stream.c               IClaude->StreamAsk (worker process)
│   ├── transport.c            bsdsocket + AmiSSL HTTPS POST
│   ├── markdown.c             markdown -> AmigaGuide / plain / console
│   ├── rexx_port.c            public CLAUDE ARexx host port
│   ├── tools.c                tool-use registry
│   └── cjson_mini.c           vendored compact JSON
├── examples/
│   └── claudesh/main.c        minimal CLI consumer
├── tests/
│   └── test_claude_library/main.c    35-test TAP harness
├── docker-cc                  cross-compile wrapper
├── build.sh / Makefile        build entry points
└── LICENSE                    zlib

Current status and roadmap

Release stage: Alpha. The library is functional and well-tested on QEMU, but the public API may still shift between releases as real-world consumers shake out rough edges. Pin your build to a specific commit if you depend on it. The next planned milestone is Beta (API frozen at v53.x, tool-use round-trip wired, AmiSSL fallback, install script).

Working today (v53.1-alpha, initial release):

  • Synchronous Ask against api.anthropic.com over real TLS.
  • Streaming StreamAsk with SSE delta parsing.
  • Conversation save/load.
  • Markdown → AmigaGuide / plain / console.
  • CLAUDE ARexx host port with end-to-end message dispatch verified by the TAP harness.
  • All 35 unit tests pass on QEMU AmigaOne (claude-backup-base_a1).

Planned:

  • Tool-use multi-turn loop (AddTool registers, but the tool_use / tool_result round-trip is not yet wired into the request cycle).
  • Chunked transfer-encoding decoding (currently the request uses HTTP/1.0 to sidestep chunked).
  • OAuth Bearer auth (deferred — see Authentication above).
  • AmiSSL fallback if AmiSSL is unavailable.
  • A real install script (Installer-format).

Contributing

Contributions welcome — please open an issue first for non-trivial changes. The project deliberately avoids non-ASCII characters in source and runtime output (the AmigaOS console is Latin-1 and serial-capture pipelines mishandle multi-byte UTF-8); please respect that in PRs.

Run the full test harness before submitting:

./build.sh tests
# then on the AmigaOS target:
test_claude_library

License

zlib — see LICENSE. Deliberately liberal so any OS4 application can link against claude.library without licensing friction.

Acknowledgements

  • The Anthropic team for the Claude API and the public OS4 documentation efforts.
  • The walkero/amigagccondocker image which makes cross-compiling for OS4 painless.
  • The rolsen74/amy_skeletons reference library skeleton, which shows the modern AutoInit pattern used here.
  • The clib4 project for documentation of how a real .library works on OS4 PPC.

About

AmigaOS 4 shared library for the Anthropic Claude API (Alpha). HTTPS + JSON + Markdown + ARexx host port, native bsdsocket + AmiSSL.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors