{"_id":"aiplug","_rev":"4-9b11970d423e9ee303912040b7a92fbc","name":"aiplug","dist-tags":{"latest":"0.1.2"},"versions":{"0.0.1":{"name":"aiplug","version":"0.0.1","_id":"aiplug@0.0.1","maintainers":[{"name":"mgks","email":"hello@mgks.dev"}],"dist":{"shasum":"09b2bbff2700ad06ff8c8d800358d7fdf6c95ac9","tarball":"https://registry.npmjs.org/aiplug/-/aiplug-0.0.1.tgz","fileCount":1,"integrity":"sha512-EL/oL4wg/Ec6gIqVNrUxk54t3iRDqq6SU1ysz4Ku+48uRn/L3MFUa/UwB70qMJCozvWp/UjlnIH49sqaEMQy9w==","signatures":[{"sig":"MEUCIEa8Uwh4oag8ymF77C3+kWLCYIfRJ8TohTwxdT4WDz8kAiEA029gHXBIeeLS6S/8DK+HJg4SrdEBsgvZUKhr0vwCKpg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":115},"_npmUser":{"name":"mgks","email":"hello@mgks.dev"},"_npmVersion":"11.16.0","description":"AI Plugin for managing and connecting to AI models","directories":{},"_nodeVersion":"25.2.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/aiplug_0.0.1_1782820887010_0.9858633551243381","host":"s3://npm-registry-packages-npm-production"}},"0.1.0":{"name":"aiplug","version":"0.1.0","keywords":["memoryblock","mblk","ai-agent","assistants","agents","automation","multi-agent","agentic-framework","core","cli","framework","interface","channels","adapters","tools","skills","local-ai","local-llm","openai","anthropic","gemini","groq","chatgpt","deepseek","ollama"],"author":{"url":"https://mgks.dev","name":"Ghazi"},"license":"MIT","_id":"aiplug@0.1.0","maintainers":[{"name":"mgks","email":"hello@mgks.dev"}],"homepage":"https://mgks.dev","bugs":{"url":"https://github.com/mgks/aiplug/issues"},"bin":{"aiplug":"dist/cli/index.js"},"dist":{"shasum":"ea246762e3ed2fa1caa93d13b0f41f2133677bd1","tarball":"https://registry.npmjs.org/aiplug/-/aiplug-0.1.0.tgz","fileCount":673,"integrity":"sha512-rPC+ZIXEW7OHHhAn4kI65ht2KEvztm7iaiEZkqcVVN9EZqvANcmxGmyMB/6FOi3HZ666b1Ft0ULz90Y4ZweiCA==","signatures":[{"sig":"MEUCIEw4Ttx0L2b9lUu53yXd8GuYMVDg0xrw8HHEC27LILmYAiEAt3D99DfGx/wrnbuHpK2lLOliWPp6Hr5ODfVgfmopQXs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/aiplug@0.1.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1292627},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.17.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json","./data/registry.json":"./data/registry.json"},"funding":"https://github.com/sponsors/mgks","gitHead":"d0b44289d53b050cf32ddf5085737cb5f1df256d","scripts":{"dev":"npm run build:registry && tsc -p tsconfig.json --watch","lint":"tsc --noEmit","test":"tsx --test src/__tests__/**/*.test.ts","build":"tsc -p tsconfig.json","smoke":"tsx scripts/smoke.ts","smoke:e2e":"tsx scripts/smoke-e2e.ts","typecheck":"tsc --noEmit","build:registry":"tsx scripts/build-registry.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c1dec0fc-17fa-4b4e-890f-28847232a9cf"}},"repository":{"url":"git+https://github.com/mgks/aiplug.git","type":"git"},"_npmVersion":"11.12.1","description":"A lightweight runtime that unifies 110+ AI providers behind a single, OpenAI-compatible API.","directories":{},"_nodeVersion":"20.20.2","dependencies":{"yaml":"^2.9.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.5","typescript":"^5.9.3","@types/node":"^22.20.0"},"_npmOperationalInternal":{"tmp":"tmp/aiplug_0.1.0_1783083874045_0.5673803252173313","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"aiplug","version":"0.1.1","keywords":["memoryblock","mblk","ai-agent","assistants","agents","automation","multi-agent","agentic-framework","core","cli","framework","interface","channels","adapters","tools","skills","local-ai","local-llm","openai","anthropic","gemini","groq","chatgpt","deepseek","ollama"],"author":{"url":"https://mgks.dev","name":"Ghazi"},"license":"MIT","_id":"aiplug@0.1.1","maintainers":[{"name":"mgks","email":"hello@mgks.dev"}],"homepage":"https://mgks.dev","bugs":{"url":"https://github.com/mgks/aiplug/issues"},"bin":{"aiplug":"dist/cli/index.js"},"dist":{"shasum":"ca0459d402b2c10a1bd61089f1d58102fc66e850","tarball":"https://registry.npmjs.org/aiplug/-/aiplug-0.1.1.tgz","fileCount":673,"integrity":"sha512-Irz4jbbNJdII8QdXjEBdKoajHe3X9RFvR2LGIgQu7tRJL97ujCgrXwOoLlRzxiHSM43rpnC4kGq4EM6D//9wMg==","signatures":[{"sig":"MEYCIQD83UjYeruwU2YM0+UhcrlmuqVguGqfVotdKeZb1En5PgIhALifSXV3X+IDQBb5PanNQ9pMVMLWpV74SbBhmknpv7eR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/aiplug@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1294858},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=18.17.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./package.json":"./package.json","./data/registry.json":"./data/registry.json"},"funding":"https://github.com/sponsors/mgks","gitHead":"287ed814ec306e9fa8c7eb048e8ab3c276a1290e","scripts":{"dev":"npm run build:registry && tsc -p tsconfig.json --watch","lint":"tsc --noEmit","test":"tsx --test src/__tests__/**/*.test.ts","build":"tsc -p tsconfig.json","smoke":"tsx scripts/smoke.ts","smoke:e2e":"tsx scripts/smoke-e2e.ts","typecheck":"tsc --noEmit","build:registry":"tsx scripts/build-registry.ts"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c1dec0fc-17fa-4b4e-890f-28847232a9cf"}},"repository":{"url":"git+https://github.com/mgks/aiplug.git","type":"git"},"_npmVersion":"11.12.1","description":"A lightweight runtime that unifies 110+ AI providers behind a single, OpenAI-compatible API.","directories":{},"_nodeVersion":"20.20.2","allowScripts":{"esbuild":true,"fsevents":true},"dependencies":{"yaml":"^2.9.0"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.22.5","typescript":"^5.9.3","@types/node":"^22.20.0"},"_npmOperationalInternal":{"tmp":"tmp/aiplug_0.1.1_1785108246165_0.3383658817640729","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"aiplug","version":"0.1.2","description":"A lightweight runtime that unifies 110+ AI providers behind a single, OpenAI-compatible API.","type":"module","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.js"},"./package.json":"./package.json","./data/registry.json":"./data/registry.json"},"bin":{"aiplug":"dist/cli/index.js"},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit","lint":"tsc --noEmit","test":"tsx --test src/__tests__/**/*.test.ts","build:registry":"tsx scripts/build-registry.ts","dev":"npm run build:registry && tsc -p tsconfig.json --watch","smoke":"tsx scripts/smoke.ts","smoke:e2e":"tsx scripts/smoke-e2e.ts"},"engines":{"node":">=18.17.0"},"dependencies":{"yaml":"^2.9.0"},"devDependencies":{"@types/node":"^22.20.0","tsx":"^4.22.5","typescript":"^5.9.3"},"allowScripts":{"esbuild":true,"fsevents":true},"keywords":["memoryblock","mblk","ai-agent","assistants","agents","automation","multi-agent","agentic-framework","core","cli","framework","interface","channels","adapters","tools","skills","local-ai","local-llm","openai","anthropic","gemini","groq","chatgpt","deepseek","ollama"],"author":{"name":"Ghazi","url":"https://mgks.dev"},"repository":{"type":"git","url":"git+https://github.com/mgks/aiplug.git"},"bugs":{"url":"https://github.com/mgks/aiplug/issues"},"homepage":"https://mgks.dev","funding":"https://github.com/sponsors/mgks","license":"MIT","gitHead":"bf1249771140f46ad3d4512aaccb54537bca0c05","_id":"aiplug@0.1.2","_nodeVersion":"20.20.2","_npmVersion":"11.12.1","dist":{"integrity":"sha512-NYYRuCewtIACs2Vbxw8l0EOBZMsyV/uWGq5zh+3f9rj1DBMheycCj031eudZmRJIwPUgsx/LzXY4/JkyP7owhQ==","shasum":"3156d5d9a9b4ebd87db94e46e7aa8e9c5bfb26e6","tarball":"https://registry.npmjs.org/aiplug/-/aiplug-0.1.2.tgz","fileCount":673,"unpackedSize":1302518,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/aiplug@0.1.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHlRvnglAS901uZ0F/XE1YDXg2zl/z0XpFfpyUupd/fUAiEA25L2L3BFhhaXzZg4hcQwO0pZkbgU6FfDR1+e7X5s0E8="}]},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c1dec0fc-17fa-4b4e-890f-28847232a9cf"}},"directories":{},"maintainers":[{"name":"mgks","email":"hello@mgks.dev"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/aiplug_0.1.2_1785543223162_0.06379919387192845"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-30T12:01:26.843Z","modified":"2026-08-01T00:13:43.706Z","0.0.1":"2026-06-30T12:01:27.131Z","0.1.0":"2026-07-03T13:04:34.252Z","0.1.1":"2026-07-26T23:24:06.313Z","0.1.2":"2026-08-01T00:13:43.364Z"},"bugs":{"url":"https://github.com/mgks/aiplug/issues"},"author":{"name":"Ghazi","url":"https://mgks.dev"},"license":"MIT","homepage":"https://mgks.dev","keywords":["memoryblock","mblk","ai-agent","assistants","agents","automation","multi-agent","agentic-framework","core","cli","framework","interface","channels","adapters","tools","skills","local-ai","local-llm","openai","anthropic","gemini","groq","chatgpt","deepseek","ollama"],"repository":{"type":"git","url":"git+https://github.com/mgks/aiplug.git"},"description":"A lightweight runtime that unifies 110+ AI providers behind a single, OpenAI-compatible API.","maintainers":[{"name":"mgks","email":"hello@mgks.dev"}],"readme":"# AIPlug\n\nA lightweight, dependency-free TypeScript runtime that gives every AI backend one identical face. AIPlug is a **universal transport layer** — point any OpenAI-compatible SDK at AIPlug and switch the underlying provider with one config line.\n\n```ts\nimport OpenAI from 'openai';\n\nconst client = new OpenAI({\n  baseURL: 'http://localhost:3711/v1',\n  apiKey: 'whatever',        // AIPlug handles real auth\n});\n\n// Now this hits Anthropic, or Ollama, or Groq, or any of 100+ providers\n// depending on what you set with `aiplug transport use anthropic`.\n```\n\nBuilt on three principles:\n\n- **Zero runtime dependencies** beyond `yaml` (≈ 80 kB).\n- **No hidden retries, no hidden routing, no automatic model selection.** You say what you want, AIPlug sends it.\n- **One folder per provider.** Adding a new provider is a five-minute copy-paste.\n\n## Quick start\n\n```bash\nnpm install\nnpm run build\n\n# Configure providers\n./dist/cli/index.js init\n./dist/cli/index.js transport add anthropic              # interactive\n./dist/cli/index.js transport add ollama  --base-url=http://localhost:11434 --model=llama3.2 --force --yes\n./dist/cli/index.js transport add openai   --api-key=$OPENAI_API_KEY --model=gpt-4o --force --yes\n\n# Use one\n./dist/cli/index.js transport use anthropic\n\n# Boot the OpenAI-compatible HTTP server\n./dist/cli/index.js serve\n```\n\nThen point any OpenAI SDK at `http://localhost:3711/v1`. **The application code doesn't change** when you switch providers — only the `aiplug transport use` line.\n\n## CLI\n\n| Command | What it does |\n|---------|--------------|\n| `aiplug init` | Create `~/.config/aiplug/` and seed empty config |\n| `aiplug transport add <slug>` | Interactively add a provider. `--api-key`, `--base-url`, `--model`, `--force`, `--yes` available |\n| `aiplug transport remove <slug>` | Remove a configured provider (`--force`) |\n| `aiplug transport list` | Show configured providers + which is active |\n| `aiplug transport test <slug>` | Live health-check against the provider's endpoint |\n| `aiplug transport use <slug>` | Mark which provider serves the HTTP server |\n| `aiplug models` | List models from the active provider's `/models` endpoint |\n| `aiplug config` | Print the resolved effective config (CLI > env > file > defaults) |\n| `aiplug status [--live]` | Table of providers, optionally with live health probes |\n| `aiplug serve [--port=3711] [--host=127.0.0.1]` | Start the OpenAI-compatible HTTP server |\n| `aiplug health` | Health-check the active provider |\n| `aiplug chat [model]` | Minimal streaming REPL against the active transport |\n| `aiplug --json` | Machine-readable output (works on every command) |\n| `aiplug --help` | Built-in help |\n\n## Chat REPL\n\n`aiplug chat` opens a minimal streaming REPL against whichever provider you made active with `aiplug transport use <name>`. No banners, no onboarding, no colour noise. Direct prompt → streamed reply → next prompt.\n\n```bash\n$ aiplug transport use anthropic\n$ aiplug chat claude-3-5-sonnet-latest\naiplug chat — anthropic / claude-3-5-sonnet-latest\nType /help for commands, Ctrl+D to exit.\n\nyou> What's the capital of France?\nParis.\n\nyou> And its population?\nAbout 2.1 million in the city proper (roughly 12 million in the metro area).\n\nyou> /model claude-3-opus-latest\n(model: claude-3-opus-latest)\n\nyou> /exit\n```\n\n### In-session commands\n\n| Command | Effect |\n|---------|--------|\n| `/help` | Show available commands |\n| `/model <name>` | Switch model mid-session |\n| `/provider` | Show active transport + model |\n| `/clear` | Clear conversation history |\n| `/exit`, `/quit`, `/q` | End the session |\n\n### Signals\n\n| Key | Effect |\n|-----|--------|\n| `Ctrl+C` during a stream | Aborts the current request, stays in REPL |\n| `Ctrl+C` idle | Exits |\n| `Ctrl+D` | Exits |\n\n## Programmatic API\n\n```ts\nimport { AIPlug, loadConfig } from 'aiplug';\n\n// Pick a provider two ways:\n//   1. Explicit config\nconst ai = new AIPlug({\n  transport: 'anthropic',\n  apiKey:    process.env.ANTHROPIC_API_KEY!,\n  model:     'claude-3-5-sonnet-latest',\n});\n\n//   2. From a profile in aiplug.config.json (CLI > env > file > defaults)\nconst { config } = loadConfig({}, 'work');\nconst ai = new AIPlug(config);\n\n// All methods accept an AbortSignal\nconst ctrl = new AbortController();\nsetTimeout(() => ctrl.abort(), 5000);\n\nconst reply = await ai.chat(\n  {\n    model: 'claude-3-5-sonnet-latest',\n    messages: [{ role: 'user', content: 'Hello!' }],\n  },\n  { signal: ctrl.signal },\n);\nconsole.log(reply.message.content);\n\n// Streaming\nfor await (const chunk of ai.stream({\n  model: 'claude-3-5-sonnet-latest',\n  messages: [{ role: 'user', content: 'Tell me a story.' }],\n})) {\n  if (chunk.type === 'text-delta') process.stdout.write(chunk.delta);\n  if (chunk.type === 'finish') console.log('\\n[done]', chunk.reason);\n}\n\n// Other capabilities — same shape across providers\nawait ai.embeddings({ model: 'text-embedding-3-small', input: 'hello world' });\nawait ai.images({ model: 'dall-e-3', prompt: 'a robot cat' });\nawait ai.audio({ model: 'tts-1', input: 'hello world', voice: 'alloy' });\nawait ai.transcription({ model: 'whisper-1', audio: audioBytes });\nawait ai.models();           // → ModelInfo[]\nawait ai.health();           // → { ok, latencyMs? }\nai.capabilities();           // → TransportMetadata (sync)\n```\n\n## HTTP server\n\n`aiplug serve` exposes an OpenAI-compatible API on `127.0.0.1:3711` by default:\n\n```\nPOST /v1/chat/completions      # OpenAI Chat Completions; SSE when stream=true\nPOST /v1/responses             # alias for /v1/chat/completions\nPOST /v1/embeddings\nPOST /v1/images/generations\nPOST /v1/audio/speech\nPOST /v1/audio/transcriptions  # multipart\nGET  /v1/models\nGET  /healthz\n```\n\n**Works with every OpenAI client SDK on the planet** (Python `openai`, JS `openai`, Go `openai-go`, etc.) by setting `baseURL: http://localhost:3711/v1`.\n\nUse `--port=0` for ephemeral ports (useful in tests).\n\n## Embedding AIPlug in another project\n\n`AIPlug` and the typed public surface (`AIPlug`, `Transport`, `ChatMessage`, `ToolCall`, …) are the canonical types an embedding project should consume. The package has zero third-party runtime dependencies, ships as ESM, and exposes its full shape via `import { … } from 'aiplug'`. Memoryblock and any other host should treat aiplug as the source of truth for adapter implementations.\n\n### `LLMAdapter` shape (memoryblock-compatible)\n\nFor host projects that follow the `LLMAdapter` shape from `@memoryblock/types`, aiplug ships an exact-match façade:\n\n````typescript\nimport { createLLMAdapter, type LLMMessage } from 'aiplug';\n\nconst adapter = createLLMAdapter({\n  provider: 'openai',\n  model: 'gpt-4o-mini',\n  apiKey: process.env.OPENAI_API_KEY,\n});\nconst reply = await adapter.converse([\n  { role: 'user', content: 'hi' } satisfies LLMMessage,\n]);\nconsole.log(reply.message.content, reply.stopReason, reply.usage);\n````\n\n`createLLMAdapter` returns an `LLMAdapter` whose `converse` and `converseStream` methods match the canonical memoryblock contract (`LLMMessage`, `TokenUsage`, `StopReason`). The re-exported types `LLMMessage`, `LLMResponse`, `LLMAdapterToolDefinition`, `TokenUsage`, `StopReason` are pure aliases of the same names so user code compiles unchanged.\n\n## Stream protocol\n\n`AIPlug.stream()` yields a discriminated union of `StreamChunk` variants. Adapters downstream (memoryblock, custom agents, scripts) consume this shape regardless of the underlying provider. The wire format is provider-specific; the chunk shape is uniform.\n\n### Chunk variants\n\n| Variant | When it fires | Provider examples |\n|---------|--------------|-------------------|\n| `text-delta` | Plain response text streams | All |\n| `reasoning-delta` | Model emits thinking that should not be shown to the user verbatim | MiniMax-M3 (`reasoning_split: true`), Anthropic Claude, DeepSeek-V4 (reasoning mode) |\n| `tool-call-delta` | Tool-call arguments stream in incrementally (partial JSON) | OpenAI, Bedrock ConverseStream, Anthropic |\n| `tool-call` | The final assembled tool call, ready for execution | All |\n| `cache-read` | The provider reports cached prompt tokens were hit | Anthropic, Bedrock, MiniMax |\n| `cache-write` | The provider reports new prompt tokens were cached | Anthropic, Bedrock |\n| `usage` | Token accounting chunk (prompt, completion, total, cache deltas) | All |\n| `finish` | Stream completed; carries the stop reason | All |\n| `error` | Mid-stream failure that the transport decided to surface as a chunk | All |\n\n`Usage` carries the cache deltas:\n\n```typescript\nconst usage = chunk.usage;\n// {\n//   promptTokens: 100,\n//   completionTokens: 50,\n//   totalTokens: 150,\n//   cacheReadTokens: 80,    // optional\n//   cacheWriteTokens: 20,   // optional\n//   reasoningTokens: 10,   // optional\n// }\n```\n\nProvider-specific fields land through the index signature (e.g. Anthropic's `cache_creation_input_tokens`).\n\n### Multimodal content\n\n`ChatMessage.content` is `string | ContentPart[]`. Each `ContentPart` carries an optional `cacheControl` marker that maps to the provider-native equivalent (`cache_control: { type: 'ephemeral' }` on Anthropic/Bedrock, server-side prefix cache on OpenAI).\n\n```typescript\nawait ai.chat({\n  model: 'MiniMax-M3',\n  messages: [{\n    role: 'user',\n    content: [\n      { type: 'text', text: 'What is in this image?' },\n      { type: 'image_url', imageUrl: { url: 'https://…/photo.jpg', detail: 'high' } },\n    ],\n  }],\n});\n```\n\nThe transport serialises content parts into the provider's wire format. Providers that do not support a given part type silently drop it from the text view via the `extractText` helper; if you need to gate multimodal inputs at the application boundary, use `transport.capabilities()` to check before sending.\n\n### Provider-specific body overrides\n\n`request.providerOptions` is forwarded into the body verbatim after the standard OpenAI-shaped fields, so provider-native toggles pass through without losing the rest of the request:\n\n```typescript\nawait ai.stream({\n  model: 'MiniMax-M3',\n  messages: [{ role: 'user', content: 'ping' }],\n  providerOptions: {\n    thinking: { type: 'disabled' },   // MiniMax native toggle\n    reasoning_split: false,           // keep reasoning inline\n  },\n});\n```\n\nFor convenience, the MiniMax transport injects `thinking: { type: 'adaptive' }` + `reasoning_split: true` by default for reasoning-capable model IDs, so callers do not need to remember the wire format.\n\n## Embedding aiplug in memoryblock\n\n`aiplug` stays an independent package — memoryblock's `@memoryblock/adapters` package wraps it so the rest of memoryblock stays provider-agnostic.\n\n### Pass-through pattern\n\nReplace the per-provider classes in `packages/adapters/src/{openai,anthropic,gemini,bedrock}/index.ts` with a thin pass-through:\n\n```typescript\n// before — packages/adapters/src/openai/index.ts\nexport class OpenAIAdapter implements LLMAdapter {\n  constructor(config) { /* ~50 lines of field mapping */ }\n  async converse(messages, tools) { /* hand-written HTTP + JSON */ }\n  async converseStream(messages, tools, onChunk) { /* SSE parsing */ }\n}\n\n// after\nimport { createLLMAdapter } from 'aiplug';\n\nexport class OpenAIAdapter {\n  private inner: LLMAdapter;\n  constructor(config) {\n    this.inner = createLLMAdapter({\n      provider: 'openai',\n      model: config.model,\n      apiKey: config.apiKey ?? process.env.OPENAI_API_KEY,\n      baseURL: config.baseURL,\n    });\n  }\n  get provider() { return this.inner.provider; }\n  get model() { return this.inner.model; }\n  converse = this.inner.converse.bind(this.inner);\n  converseStream = this.inner.converseStream?.bind(this.inner);\n}\n```\n\nThe class names stay so existing imports in `packages/memoryblock` and the `init` / `start` commands keep working. The hand-written HTTP and JSON parsing go away.\n\n### Provider name mapping\n\nMemoryblock's `block.config.json` continues to declare `adapter.provider`. Map it to aiplug's transport slug:\n\n| memoryblock `provider` | aiplug transport slug |\n|------------------------|-----------------------|\n| `bedrock` | `bedrock-aws` (SigV4 Converse) |\n| `openai` | `openai` |\n| `anthropic` | `anthropic` |\n| `gemini` | `google-ai-studio` (native adapter) |\n| `ollama` | `ollama` |\n\nThe capability matrix exposed by `transport.capabilities()` is the signal memoryblock should consult when deciding whether to gate vision / tool / streaming support per-block, rather than the provider-name string match alone.\n\n### Streaming integration\n\n`converseStream` keeps the existing `onChunk(text)` callback contract, so the `Monitor` engine does not need to change. When aiplug emits a `reasoning-delta`, the wrapper can either drop it (current behaviour — reasoning is invisible) or forward it as a separate notification so memoryblock can log it. Recommendation: log reasoning to `logs/<date>.log` keyed by `blockName + turnId`, keep the user-visible stream text-only. Reasoning never reaches the chat channel.\n\n### Adding a new provider\n\nWhen memoryblock needs a provider aiplug does not yet ship:\n\n1. Add the provider to `data/providers.json`, or write a custom adapter under `aiplug/src/providers/<slug>/` with `@aiplug:keep`.\n2. Run `npm run build:registry` in the aiplug package.\n3. Map the provider name in `packages/adapters/src/index.ts` to the new aiplug transport slug.\n4. Update memoryblock's `init.ts` provider list.\n\nNo memoryblock core changes required.\n\n## Supported providers (100+ entries)\n\nThe full registry lives in [`data/registry.json`](data/registry.json) and is generated from [`data/providers.json`](data/providers.json) (synced from [foisalislambd/all-llm-provider-list](https://github.com/foisalislambd/all-llm-provider-list)).\n\n### Frontier (18)\n\n| Slug | Name | Base URL | Env var | OpenAI-shaped |\n|------|------|----------|---------|---------------|\n| `openai` | OpenAI | `https://api.openai.com/v1` | `OPENAI_API_KEY` | ✓ |\n| `anthropic` | Anthropic | `https://api.anthropic.com` | `ANTHROPIC_API_KEY` | ✗ |\n| `google-ai-studio` | Google AI Studio | `https://generativelanguage.googleapis.com` | `GEMINI_API_KEY` | ✓ |\n| `gemini` | Gemini (native adapter) | `https://generativelanguage.googleapis.com` | `GEMINI_API_KEY` | ✗ |\n| `xai` | xAI (Grok) | `https://api.x.ai/v1` | `XAI_API_KEY` | ✓ |\n| `deepseek` | DeepSeek | `https://api.deepseek.com/v1` | `DEEPSEEK_API_KEY` | ✓ |\n| `mistral` | Mistral AI | `https://api.mistral.ai/v1` | `MISTRAL_API_KEY` | ✓ |\n| `cohere` | Cohere | `https://api.cohere.com/v2` | `COHERE_API_KEY` | ✗ |\n| `perplexity` | Perplexity | `https://api.perplexity.ai` | `PERPLEXITY_API_KEY` | ✓ |\n| `ai21` | AI21 Labs | `https://api.ai21.com/studio/v1` | `AI21_API_KEY` | ✓ |\n| `minimax` | MiniMax | `https://api.minimax.io/v1` | `MINIMAX_API_KEY` | ✓ |\n| `reka` | Reka AI | `https://api.reka.ai/v1` | `REKA_API_KEY` | ✓ |\n| `baidu-qianfan` | Baidu Qianfan | `https://api.baiduqianfan.ai/v1` | `QIANFAN_API_KEY` | ✓ |\n| `dashscope` | Alibaba DashScope | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` | `DASHSCOPE_API_KEY` | ✓ |\n| `stepfun` | StepFun | `https://api.stepfun.com/v1` | `STEPFUN_API_KEY` | ✓ |\n| `zhipu` | Z.ai (Zhipu AI) | `https://open.bigmodel.cn/api/paas/v4/` | `ZHIPU_API_KEY` | ✓ |\n| `upstage` | Upstage | `https://api.upstage.ai/v1/solar` | `UPSTAGE_API_KEY` | ✓ |\n| `xiaomi` | Xiaomi | Custom endpoint | — | ✗ |\n| `inflection` | Inflection | Custom webhooks | — | ✗ |\n\n### Aggregator (6)\n\n| Slug | Name | Base URL | Env var | OpenAI-shaped |\n|------|------|----------|---------|---------------|\n| `openrouter` | OpenRouter | `https://openrouter.ai/api/v1` | `OPENROUTER_API_KEY` | ✓ |\n| `litellm` | LiteLLM | `http://localhost:4000/v1` | `LITELLM_MASTER_KEY` | ✓ |\n| `portkey` | Portkey | `https://api.portkey.ai/v1` | `PORTKEY_API_KEY` | ✓ |\n| `302-ai` | 302.AI | `https://api.302.ai/v1` | `302AI_API_KEY` | ✓ |\n| `aimlapi` | AIMLAPI | `https://api.aimlapi.com/v1` | `AIMLAPI_API_KEY` | ✓ |\n| `coze` | Coze (ByteDance) | `https://api.coze.com/v1` | `COZE_API_KEY` | ✓ |\n| `frogbot` | FrogBot | `https://app.frogbot.ai/api` | `FROGBOT_API_KEY` | ✓ |\n| `lemondata` | LemonData | `https://api.lemondata.ai/v1` | `LEMONDATA_API_KEY` | ✓ |\n| `eden-ai` | Eden AI | `https://api.edenai.co/v2` | `EDENAI_API_KEY` | ✗ |\n\n### IaaS / GPU clouds (27)\n\n| Slug | Name | Base URL |\n|------|------|----------|\n| `groq` | Groq | `https://api.groq.com/openai/v1` |\n| `cerebras` | Cerebras | `https://api.cerebras.ai/v1` |\n| `sambanova` | SambaNova | `https://api.sambanova.ai/v1` |\n| `fireworks` | Fireworks AI | `https://api.fireworks.ai/inference/v1` |\n| `together` | Together AI | `https://api.together.xyz/v1` |\n| `deepinfra` | DeepInfra | `https://api.deepinfra.com/v1/openai` |\n| `huggingface` | HuggingFace Inference | `https://router.huggingface.co/v1` |\n| `nvidia-nim` | NVIDIA NIM | `https://integrate.api.nvidia.com/v1` |\n| `nebius` | Nebius AI Studio | `https://api.studio.nebius.ai/v1` |\n| `novita` | Novita | `https://api.novita.ai/openai/v1` |\n| `anyscale` | Anyscale Endpoints | `https://api.endpoints.anyscale.com/v1` |\n| `arcee` | Arcee AI | `https://conductor.arcee.ai/v1` |\n| `friendli` | Friendli | `https://api.friendli.ai/serverless/v1` |\n| `glhf` | Glhf.chat | `https://glhf.chat/api/openai/v1` |\n| `hyperbolic` | Hyperbolic | `https://api.hyperbolic.xyz/v1` |\n| `inception` | Inception | `https://api.inceptionlabs.ai/v1` |\n| `inceptron` | Inceptron | Custom endpoint |\n| `inference-net` | Inference.net | `https://api.inference.net/v1` |\n| `infermatic` | Infermatic | `https://api.totalgpt.ai` |\n| `kluster` | Kluster.ai | `https://api.kluster.ai/v1` |\n| `lepton` | Lepton AI | `https://api.lepton.ai/v1` |\n| `liquid` | Liquid AI | Custom cluster endpoints |\n| `mancer` | Mancer | `https://mancer.tech/oai/v1` |\n| `morph` | Morph | `https://api.morphllm.com/v1` |\n| `siliconflow` | SiliconFlow | `https://api.siliconflow.cn/v1` |\n| `replicate` | Replicate | `https://api.replicate.com/v1` |\n| `ollama-cloud` | Ollama Cloud | `https://ollama.com/api` |\n\n### Sovereign / Cloud (29)\n\n| Slug | Name | Base URL |\n|------|------|----------|\n| `bedrock` | Amazon Bedrock | `https://bedrock-runtime.<region>.amazonaws.com` |\n| `azure-openai` | Azure OpenAI | `https://<resource>.openai.azure.com/openai/v1` |\n| `azure-cognitive-services` | Azure Cognitive Services | `https://<resource>.cognitiveservices.azure.com/openai/v1` |\n| `vertex-ai` | Google Vertex AI | Region-dependent |\n| `cloudflare-workers-ai` | Cloudflare Workers AI | `https://api.cloudflare.com/client/v4/accounts/{id}/ai/v1` |\n| `github-models` | GitHub Models | `https://models.inference.ai.azure.com` |\n| `github-copilot` | GitHub Copilot | OAuth device flow |\n| `gitlab-duo` | GitLab Duo | `https://gitlab.com/api/v4/ai` |\n| `digitalocean` | DigitalOcean | `https://inference.do-ai.run/v1/` |\n| `scaleway` | Scaleway | `https://api.scaleway.ai/v1` |\n| `ovhcloud` | OVHcloud AI | `https://oai.endpoints.kepler.ai.cloud.ovh.net/v1` |\n| `stackit` | STACKIT AI Model Serving | `https://api.openai-compat.model-serving.eu01.onstackit.cloud/v1` |\n| `akashml` | AkashML | `https://api.akashml.com/v1` |\n| `atlascloud` | AtlasCloud | `https://api.atlascloud.ai/v1` |\n| `baseten` | Baseten | `https://model-{id}.api.baseten.co/v1` |\n| `chutes` | Chutes | `https://llm.chutes.ai/v1` |\n| `clarifai` | Clarifai | Custom endpoints |\n| `gmicloud` | GMICloud | `https://api.gmi-serving.com/v1` |\n| `modal` | Modal | `https://<app>.modal.run/v1` |\n| `nextbit` | NextBit | `https://api.nextbit256.com/v1` |\n| `parasail` | Parasail | `https://api.saas.parasail.io/v1` |\n| `phala` | Phala | `POST /v1/chat/completions` |\n| `poolside` | Poolside | `https://divers.poolsi.de/openai/v1/` |\n| `sap-ai-core` | SAP AI Core | Region-dependent |\n| `snowflake-cortex` | Snowflake Cortex | `https://<account>.snowflakecomputing.com/api/v2/cortex/v1` |\n| `venice` | Venice | `https://api.venice.ai/api/v1` |\n| `wafer` | Wafer | `https://pass.wafer.ai/v1` |\n| `io-net` | io.net | `https://api.intelligence.io.solutions/api/v1` |\n\n### Gateway (24)\n\n| Slug | Name | Base URL |\n|------|------|----------|\n| `vercel-ai-gateway` | Vercel AI Gateway | `https://ai-gateway.vercel.sh/v1` |\n| `helicone` | Helicone | `https://ai-gateway.helicone.ai/v1` |\n| `cloudflare-ai-gateway` | Cloudflare AI Gateway | `https://gateway.ai.cloudflare.com/v1` |\n| `llm-gateway` | LLM Gateway | `https://api.llmgateway.io/v1` |\n| `axiom` | Axiom | `https://cloud.axiomstudio.ai/rest/v1/llm-gateway/v1/` |\n| `cortecs` | Cortecs | `https://api.cortecs.ai/v1` |\n| `kong-ai-gateway` | Kong AI Gateway | Self-hosted / enterprise |\n| `moonshot` | Moonshot AI | `https://api.moonshot.ai/v1` |\n| `opencode-go` | OpenCode Go | `https://opencode.ai/zen/go/v1` |\n| `opencode-zen` | OpenCode Zen | `https://opencode.ai/zen/v1` |\n| `opper` | Opper | `https://api.opper.ai/v3/compat` |\n| `perceptron` | Perceptron | Custom gateway |\n| `prism-api` | Prism API | `https://sub2api.558686.xyz/v1` |\n| `relace` | Relace | `https://api.relace.ai/v1` |\n| `requesty` | Requesty | `https://router.requesty.ai/v1` |\n| `sakana-fugu` | Sakana AI (Fugu) | `https://api.sakana.ai/v1` |\n| `switchpoint` | Switchpoint | `https://api.ppq.ai` |\n| `unify` | Unify.ai | `https://api.unify.ai/v0` |\n| `wandb` | Weights & Biases | Evaluation registry |\n| `zenmux` | ZenMux | `https://zenmux.ai/api/v1` |\n| `openinference` | OpenInference | Tracing / observability |\n\n### Local runtimes (7)\n\n| Slug | Name | Base URL |\n|------|------|----------|\n| `ollama` | Ollama | `http://localhost:11434` |\n| `llama-cpp` | llama.cpp | `http://localhost:8080/v1` |\n| `lm-studio` | LM Studio | `http://localhost:1234/v1` |\n| `vllm` | vLLM | `http://localhost:8000/v1` |\n| `localai` | LocalAI | `http://localhost:8080/v1` |\n| `jan` | Jan.ai | `http://localhost:1337/v1` |\n| `atomic-chat` | Atomic Chat | `http://127.0.0.1:1337/v1` |\n\n### Specialized (2)\n\n| Slug | Name | Base URL |\n|------|------|----------|\n| `nlpcloud` | NLP Cloud | `https://api.nlpcloud.io/v1` |\n| `puter` | Puter.js | `https://api.puter.com/ai/chat` |\n\n### Embeddings (1)\n\n| Slug | Name | Base URL |\n|------|------|----------|\n| `voyage` | Voyage AI | `https://api.voyageai.com/v1` |\n\n## How it works\n\n### Custom adapters (native wire format)\n\n- **`anthropic`** — Anthropic Messages API + SSE streaming, `x-api-key` header, system message hoisting, `max_tokens` required, `tool_use` blocks mapped to `ToolCall`.\n- **`gemini`** — Google AI Studio native API + SSE, `contents[].parts[]` blocks, `systemInstruction` field, function calling via `tools[].functionDeclarations`, embeddings via `models/embedContent` (not yet wired).\n- **`ollama`** — Ollama native `/api/chat` (NDJSON streaming), `/api/embeddings`, `/api/tags`. No auth header.\n\n### OpenAI-compatible adapter (98+ providers)\n\nEvery provider marked ✓ in the tables above uses the `openai-compatible` adapter, which speaks the OpenAI Chat Completions wire format (`/v1/chat/completions`, `/v1/embeddings`, `/v1/images/generations`, `/v1/audio/speech`, `/v1/audio/transcriptions`, `/v1/models`). Adding a new one is just an entry in `data/registry.json`.\n\n### Lazy loading\n\nEvery transport is dynamically `import()`-ed on first use. Nothing is bundled into the core. Add a new entry to `data/registry.json`, drop a folder at `src/providers/<slug>/`, and it works.\n\n## Design principles\n\n`aiplug` is a thin pass-through. The runtime adds **no measurable overhead** to a model request beyond the underlying HTTP call and JSON parsing — no token counting, no retries, no per-request logging, no redaction on the success path, no per-request config re-resolution.\n\nThe codebase will not introduce any of the following without a major version bump:\n\n- Per-request token counting or rate limiting\n- Per-request retries (deliberate — wrap the client if you need them)\n- Per-request capability re-detection\n- Per-request logging or telemetry hooks\n- Per-request redaction or sanitisation of the request body\n- Per-request config re-resolution\n\nIf you need any of those, wrap the client with a higher-level abstraction. They live \"one layer up\" by design.\n\n<details>\n<summary><strong>What runs on the request path</strong> (7 cheap operations, no I/O)</summary>\n\nFor a single `ai.chat({ ... })` call:\n\n| Step | Operation | Cost |\n|------|-----------|------|\n| 1 | `AIPlug.ready()` returns the cached transport instance | 1 truthy check + 1 map lookup |\n| 2 | Transport `chat(req, signal)` | one method call |\n| 3 | `requireModel(config)` (sync, throws if missing) | 1 string truthy check |\n| 4 | `buildBody(req)` builds the request JSON | object literal + `JSON.stringify` |\n| 5 | `fetch(url, init)` | the network call (unavoidable) |\n| 6 | `await res.json()` | parses the upstream response (unavoidable) |\n| 7 | Map response to `ChatResponse` | small object construction |\n\nFor streaming, each chunk is decoded once in the transport, then yielded. The `AIPlug.stream` wrapper does a single string comparison per chunk (`chunk.type === 'finish' \\|\\| chunk.type === 'error'`) to short-circuit on terminal chunks.\n\n**What does NOT run on the hot path** (these exist but never execute on success):\n\n- **Redaction** (`redactString`, `redactSecrets`, `redactHeaders`): only invoked from `makeError` → `AIPlugError` constructor → `buildError`. Triggers only on errors.\n- **Capability detection** (`detect()`, `probeCapabilities()`): runs once per transport+baseURL on first call, then cached in-memory.\n- **Config loading** (`load()` in `src/config.ts`): runs once at process start. The merged `AiplugConfig` is frozen in `freezeConfig()` and held by the `AIPlug` instance for its lifetime.\n- **Registry parsing** (`getRegistry()`, `validateRegistry()`): reads and parses `data/registry.json` once. Cached at module level.\n- **Dynamic `import()` of transport modules**: Node's loader caches the resolved module. After the first import for a given URL, it's a single map lookup.\n\n</details>\n\n<details>\n<summary><strong>Adding new code that touches the hot path</strong> (contributor checklist)</summary>\n\nIf you need to add logic to `Transport.chat()`, `Transport.stream()`, or any provider's request builder:\n\n1. State the cost in the PR description (e.g. \"adds ~50 ns of regex matching per request\").\n2. Avoid regex that compiles on every call. Hoist patterns to module scope.\n3. Avoid logging on the success path. Errors get full logging; success is silent.\n4. Avoid synchronous I/O. The hot path must not touch the filesystem, network (other than the upstream call), or env vars.\n5. Keep allocations small. A single object literal per request is fine; allocating per chunk in a stream is not.\n\nA test that asserts request shape (`vi.stubGlobal('fetch', stub)`) is required for any new transport method.\n\n</details>\n\n## Configuration\n\nPrecedence (highest wins):\n\n1. **CLI flags** — `--transport=openai --model=gpt-4o --api-key=xyz`\n2. **Env vars** — `AIPLUG_TRANSPORT`, `AIPLUG_API_KEY`, `AIPLUG_MODEL`, `AIPLUG_BASE_URL`, `AIPLUG_PROFILE`, `AIPLUG_CAPABILITIES`, `AIPLUG_TIMEOUT_MS`\n3. **Project file** — `./aiplug.config.json` (or `.yaml`)\n4. **Global file** — `~/.config/aiplug/config.json` (or `.yaml`)\n5. **Hardcoded defaults**\n\nExample config file:\n\n```jsonc\n{\n  \"active\": \"anthropic\",\n  \"transports\": {\n    \"anthropic\": { \"apiKey\": \"${ANTHROPIC_API_KEY}\", \"model\": \"claude-3-5-sonnet-latest\" },\n    \"openai\":    { \"apiKey\": \"${OPENAI_API_KEY}\",    \"model\": \"gpt-4o\" },\n    \"ollama\":    { \"baseURL\": \"http://localhost:11434\", \"model\": \"llama3.2\" }\n  },\n  \"profiles\": {\n    \"fast\":    { \"transport\": \"openai\",    \"model\": \"gpt-4o-mini\" },\n    \"private\": { \"transport\": \"ollama\",    \"model\": \"llama3.2\" }\n  }\n}\n```\n\n`${ENV_VAR}` substitution happens at load time. Secrets never appear in error messages or logs.\n\n## Error model\n\n```ts\nclass AIPlugError extends Error {\n  code: 'AUTH_INVALID' | 'AUTH_MISSING' | 'MODEL_NOT_FOUND' | 'RATE_LIMITED'\n       | 'NETWORK_TIMEOUT' | 'REQUEST_ABORTED' | 'INVALID_CONFIGURATION'\n       | 'TRANSPORT_UNAVAILABLE' | 'UNSUPPORTED_CAPABILITY' | 'INVALID_RESPONSE'\n       | 'STREAM_ERROR';\n  transport: string;\n  status?: number;\n  retryable: boolean;\n  details?: unknown;\n  cause?: unknown;\n}\n```\n\n`makeError({...})` maps HTTP status to code when no explicit `code` is given. Every API key, bearer token, and cookie is stripped from `message` and `details` before the error is constructed.\n\n## Adding a new provider\n\n```bash\ncp -r src/providers/_template src/providers/myprovider\n```\n\nThen edit:\n- `src/providers/myprovider/capabilities.ts` — capability list + auth scheme\n- `src/providers/myprovider/index.ts` — implement the 9 Transport methods\n- `src/providers/myprovider/README.md` — auth + sync notes\n\nAdd an entry to `data/registry.json` (or run `python3 scripts/build-registry.py` after editing `data/providers.json`):\n\n```json\n\"myprovider\": {\n  \"module\": \"./myprovider/index.js\",\n  \"class\": \"MyProviderTransport\",\n  \"defaultBaseURL\": \"https://api.myprovider.com/v1\",\n  \"auth\": \"bearer\",\n  \"authHeader\": \"Authorization\",\n  \"displayName\": \"My Provider\"\n}\n```\n\nRun `npm test` and you're done. The next `aiplug transport add myprovider` works.\n\n## Repository layout\n\n```\nsrc/\n  types.ts              # every public type\n  errors.ts             # AIPlugError + factory + secret redaction\n  transport.ts          # abstract Transport + helpers\n  client.ts             # public AIPlug client\n  config.ts             # precedence loader + profile resolution\n  streaming.ts          # SSE + NDJSON normalisers\n  capabilities.ts       # capability detector with caching\n  registry.ts           # lazy transport loader\n  index.ts              # public barrel\n  providers/            # one folder per provider\n    _template/          # boilerplate new providers copy from\n    openai/             # OpenAI Chat Completions + embeddings + images + audio\n    openai-compatible/  # any server speaking OpenAI wire format\n    anthropic/          # Anthropic Messages API + SSE\n    gemini/             # Google AI Studio native API + SSE\n    ollama/             # local-first HTTP + NDJSON streaming\n  cli/                  # CLI entrypoint + per-command files\n  server/               # OpenAI-compatible HTTP server\n\ndata/\n  registry.json         # generated, versioned transport metadata (266 entries)\n  providers.json        # synced from foisalislambd/all-llm-provider-list\n\ntests/                  # Node test runner regression tests\nscripts/                # smoke + e2e + build-registry scripts\n```\n\n## Testing\n\n```bash\nnpm run typecheck   # tsc --noEmit, strict + exactOptionalPropertyTypes\nnpm run smoke       # import smoke\nnpm run smoke:e2e   # boot the server, hit every endpoint, verify shapes\n```\n\n## Runtime requirements\n\n- Node.js ≥ 18.17 (native `fetch`, native `Web Streams`).\n- TypeScript ≥ 5.7 with `strict`, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`.\n- One runtime dependency: `yaml` (≈ 80 kB).","readmeFilename":"README.md"}