Skill: felipematos/openclaw-plugin-ain (version 0.11.6)

# AIN — AI Node Plugin for OpenClaw Bridges the [AIN](https://github.com/felipematos/ain) provider registry, intelligent routing engine, and execution layer into the OpenClaw ecosystem. ## What it does - **Provider bridging** — All AIN-configured providers (LM Studio, Ollama, OpenAI, vLLM, etc.) are automatically exposed to OpenClaw as `ain:<name>` providers - **LLM tools** — Two agent tools: `ain_run` (prompt execution with routing, structured output, fallback chains) and `ain_classify` (task type and complexity classification) - **Routing hook** — `before_model_resolve` hook uses AIN's intelligent routing engine to automatically select the best model for each task based on policies and task classification ## Installation ```bash npm install openclaw-plugin-ain ``` Requires [@felipematos/ain-cli](https://www.npmjs.com/package/@felipematos/ain-cli) (installed as a dependency). ## Configuration In your OpenClaw config: ```json { "plugins": { "ain": { "enableRouting": true, "routingPolicy": "local-first", "exposeTools": true } } } ``` ### Options | Option | Type | Default | Description | |--------|------|---------|-------------| | `configPath` | string | `~/.ain/config.yaml` | Path to AIN config file | | `enableRouting` | boolean | `true` | Enable intelligent model routing | | `routingPolicy` | string | — | Named routing policy from AIN policies.yaml | | `exposeTools` | boolean | `true` | Expose ain_run and ain_classify tools to agents | ## Tools ### `ain_run` Execute an LLM prompt through AIN's execution engine with full support for routing, structured output, and fallback chains. **Parameters:** - `prompt` (string, required) — The prompt to execute - `provider` (string) — Provider name - `model` (string) — Model ID or alias - `jsonMode` (boolean) — Request JSON output - `schema` (object) — JSON Schema for output validation - `system` (string) — System prompt - `temperature` (number) — Sampling temperature **Returns:** `{ output, provider, model, usage, parsedOutput }` ### `ain_classify` Classify a prompt's task type and estimate its complexity. **Parameters:** - `prompt` (string, required) — The prompt to classify **Returns:** `{ taskType, complexity }` Task types: `classification`, `extraction`, `generation`, `reasoning`, `unknown` Complexity: `low`, `medium`, `high` ## Routing When `enableRouting` is true, the plugin registers a `before_model_resolve` hook that analyzes incoming prompts and selects the optimal model based on: - Task classification (classification/extraction → fast tier, generation → general tier, reasoning → reasoning tier) - Routing policies defined in `~/.ain/policies.yaml` - Model tags and tier configuration ## Requirements - Node.js >= 18 - AIN configured with at least one provider (`ain config init && ain providers add ...`) - OpenClaw >= 1.0.0 ## License MIT

path: README.md
content: # openclaw-plugin-ain

OpenClaw plugin that bridges [AIN](https://github.com/felipematos/ain) provider registry, intelligent routing, and execution into the OpenClaw ecosystem.

## Features

- **Provider bridging** — All AIN-configured providers are exposed to OpenClaw as `ain:<name>`
- **LLM tools** — `ain_run` (prompt execution) and `ain_classify` (task classification) available to agents
- **Routing hook** — `before_model_resolve` hook uses AIN's intelligent routing engine

## Installation

```bash
npm install openclaw-plugin-ain
```

## Configuration

In your OpenClaw config:

```json
{
  "plugins": {
    "ain": {
      "enableRouting": true,
      "routingPolicy": "default",
      "exposeTools": true
    }
  }
}
```

### Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `configPath` | string | `~/.ain/config.yaml` | Path to AIN config file |
| `enableRouting` | boolean | `true` | Enable the routing hook |
| `routingPolicy` | string | — | Named routing policy from AIN policies |
| `exposeTools` | boolean | `true` | Expose ain_run and ain_classify tools |

## Tools

### `ain_run`

Execute an LLM prompt through AIN.

**Parameters:** `prompt` (required), `provider`, `model`, `jsonMode`, `schema`, `system`, `temperature`

### `ain_classify`

Classify a prompt's task type and complexity.

**Parameters:** `prompt` (required)

**Returns:** `{ taskType, complexity }`

## License

MIT


path: dist/index.d.ts
content: import type { OpenClawPluginDefinition } from './types.js';
declare const plugin: OpenClawPluginDefinition;
export default plugin;
//# sourceMappingURL=index.d.ts.map

path: dist/index.js
content: import { loadConfig } from '@felipematos/ain-cli';
import { registerAinProviders } from './providers.js';
import { registerAinTools } from './tools.js';
import { registerRoutingHook } from './routing-hook.js';
const plugin = {
    id: 'openclaw-plugin-ain',
    name: 'AIN - AI Node',
    description: 'AIN provider registry, routing, and execution for OpenClaw',
    version: '0.2.2',
    async register(api) {
        const pluginConfig = api.pluginConfig;
        const config = loadConfig();
        registerAinProviders(api, config);
        if (pluginConfig.exposeTools !== false) {
            registerAinTools(api);
        }
        if (pluginConfig.enableRouting !== false) {
            registerRoutingHook(api, pluginConfig.routingPolicy);
        }
        api.logger.info('AIN plugin registered successfully');
    },
};
export default plugin;
//# sourceMappingURL=index.js.map

path: dist/providers.d.ts
content: import type { AinConfig } from '@felipematos/ain-cli';
import type { OpenClawPluginApi } from './types.js';
export declare function registerAinProviders(api: OpenClawPluginApi, config: AinConfig): void;
//# sourceMappingURL=providers.d.ts.map

path: dist/providers.js
content: import { createAdapter } from '@felipematos/ain-cli';
export function registerAinProviders(api, config) {
    for (const [name, providerConfig] of Object.entries(config.providers)) {
        const adapter = createAdapter(providerConfig);
        const models = (providerConfig.models ?? []).map((m) => m.id);
        api.registerProvider({
            id: `ain:${name}`,
            name: `AIN/${name}`,
            models,
            async chat(request) {
                const response = await adapter.chat({
                    model: request.model,
                    messages: request.messages.map((m) => ({
                        role: m.role,
                        content: m.content,
                    })),
                    temperature: request.temperature,
                    max_tokens: request.max_tokens,
                    ...(request.response_format ? { response_format: request.response_format } : {}),
                });
                const content = response.choices[0]?.message?.content ?? '';
                return {
                    content,
                    model: response.model,
                    usage: response.usage,
                };
            },
            async *chatStream(request) {
                yield* adapter.chatStream({
                    model: request.model,
                    messages: request.messages.map((m) => ({
                        role: m.role,
                        content: m.content,
                    })),
                    temperature: request.temperature,
                    max_tokens: request.max_tokens,
                });
            },
        });
        api.logger.info(`Registered provider ain:${name} with ${models.length} model(s)`);
    }
}
//# sourceMappingURL=providers.js.map

path: dist/routing-hook.d.ts
content: import type { OpenClawPluginApi } from './types.js';
export declare function registerRoutingHook(api: OpenClawPluginApi, policyName?: string): void;
//# sourceMappingURL=routing-hook.d.ts.map

path: dist/routing-hook.js
content: import { route } from '@felipematos/ain-cli';
export function registerRoutingHook(api, policyName) {
    api.registerHook({
        event: 'before_model_resolve',
        async handler(context) {
            const prompt = context.prompt;
            if (!prompt)
                return;
            try {
                const decision = await route({
                    prompt,
                    policyName,
                });
                return {
                    provider: `ain:${decision.provider}`,
                    model: decision.model,
                    metadata: {
                        tier: decision.tier,
                        rationale: decision.rationale,
                        source: 'ain-routing',
                    },
                };
            }
            catch (err) {
                api.logger.warn(`AIN routing failed: ${err instanceof Error ? err.message : String(err)}`);
                return;
            }
        },
    });
}
//# sourceMappingURL=routing-hook.js.map

path: dist/tools.d.ts
content: import type { OpenClawPluginApi } from './types.js';
export declare function registerAinTools(api: OpenClawPluginApi): void;
//# sourceMappingURL=tools.d.ts.map

path: dist/tools.js
content: import { run, classifyTask, estimateComplexity } from '@felipematos/ain-cli';
export function registerAinTools(api) {
    api.registerTool({
        name: 'ain_run',
        description: 'Execute an LLM prompt through AIN with provider routing and structured output support',
        parameters: {
            type: 'object',
            properties: {
                prompt: { type: 'string', description: 'The prompt to send to the LLM' },
                provider: { type: 'string', description: 'Provider name (optional)' },
                model: { type: 'string', description: 'Model ID or alias (optional)' },
                jsonMode: { type: 'boolean', description: 'Request JSON output' },
                schema: { type: 'object', description: 'JSON schema for structured output' },
                system: { type: 'string', description: 'System prompt' },
                temperature: { type: 'number', description: 'Temperature (0-2)' },
            },
            required: ['prompt'],
        },
        async execute(params) {
            const result = await run({
                prompt: params.prompt,
                provider: params.provider,
                model: params.model,
                jsonMode: params.jsonMode,
                schema: params.schema,
                system: params.system,
                temperature: params.temperature,
            });
            return {
                output: result.output,
                provider: result.provider,
                model: result.model,
                usage: result.usage,
                parsedOutput: result.parsedOutput,
            };
        },
    });
    api.registerTool({
        name: 'ain_classify',
        description: 'Classify a prompt by task type and estimate its complexity',
        parameters: {
            type: 'object',
            properties: {
                prompt: { type: 'string', description: 'The prompt to classify' },
            },
            required: ['prompt'],
        },
        async execute(params) {
            const prompt = params.prompt;
            return {
                taskType: classifyTask(prompt),
                complexity: estimateComplexity(prompt),
            };
        },
    });
}
//# sourceMappingURL=tools.js.map

path: dist/types.d.ts
content: /**
 * Minimal OpenClaw plugin API types.
 * Defined here to avoid a hard dependency on openclaw — it's a peer dep.
 */
export interface OpenClawToolDefinition {
    name: string;
    description: string;
    parameters: Record<string, unknown>;
    execute: (params: Record<string, unknown>) => Promise<unknown>;
}
export interface OpenClawProviderDefinition {
    id: string;
    name: string;
    models: string[];
    chat: (request: OpenClawChatRequest) => Promise<OpenClawChatResponse>;
    chatStream?: (request: OpenClawChatRequest) => AsyncGenerator<string>;
}
export interface OpenClawChatRequest {
    model: string;
    messages: Array<{
        role: string;
        content: string;
    }>;
    temperature?: number;
    max_tokens?: number;
    response_format?: unknown;
}
export interface OpenClawChatResponse {
    content: string;
    model: string;
    usage?: {
        prompt_tokens: number;
        completion_tokens: number;
        total_tokens: number;
    };
}
export interface OpenClawHookDefinition {
    event: string;
    handler: (context: Record<string, unknown>) => Promise<Record<string, unknown> | void>;
}
export interface OpenClawLogger {
    info: (message: string) => void;
    warn: (message: string) => void;
    error: (message: string) => void;
    debug: (message: string) => void;
}
export interface OpenClawPluginApi {
    registerTool: (tool: OpenClawToolDefinition) => void;
    registerProvider: (provider: OpenClawProviderDefinition) => void;
    registerHook: (hook: OpenClawHookDefinition) => void;
    pluginConfig: Record<string, unknown>;
    logger: OpenClawLogger;
}
export interface OpenClawPluginDefinition {
    id: string;
    name: string;
    description: string;
    version: string;
    register: (api: OpenClawPluginApi) => Promise<void>;
}
export interface AinPluginConfig {
    configPath?: string;
    enableRouting?: boolean;
    routingPolicy?: string;
    exposeTools?: boolean;
}
//# sourceMappingURL=types.d.ts.map

path: dist/types.js
content: /**
 * Minimal OpenClaw plugin API types.
 * Defined here to avoid a hard dependency on openclaw — it's a peer dep.
 */
export {};
//# sourceMappingURL=types.js.map

path: openclaw.plugin.json
content: {
  "id": "openclaw-plugin-ain",
  "configSchema": {
    "type": "object",
    "properties": {
      "configPath": { "type": "string" },
      "enableRouting": { "type": "boolean", "default": true },
      "routingPolicy": { "type": "string" },
      "exposeTools": { "type": "boolean", "default": true }
    }
  }
}


path: package-lock.json
content: {
  "name": "openclaw-plugin-ain",
  "version": "0.2.0",
  "lockfileVersion": 3,
  "requires": true,
  "packages": {
    "": {
      "name": "openclaw-plugin-ain",
      "version": "0.2.0",
      "license": "MIT",
      "dependencies": {
        "@felipematos/ain-cli": "^0.11.0"
      },
      "devDependencies": {
        "typescript": "^5.5.0",
        "vitest": "^2.0.0"
      },
      "peerDependencies": {
        "openclaw": ">=1.0.0"
      },
      "peerDependenciesMeta": {
        "openclaw": {
          "optional": true
        }
      }
    },
    "../..": {
      "name": "@felipematos/ain-cli",
      "version": "0.11.0",
      "license": "MIT",
      "dependencies": {
        "commander": "^12.1.0",
        "json5": "^2.2.3",
        "yaml": "^2.6.1",
        "zod": "^3.23.8"
      },
      "bin": {
        "ain": "dist/cli/index.js"
      },
      "devDependencies": {
        "@types/node": "^22.0.0",
        "tsx": "^4.16.0",
        "typescript": "^5.5.0",
        "vitest": "^2.0.0"
      },
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/@esbuild/aix-ppc64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "aix"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/android-arm": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "android"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/android-arm64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "android"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/android-x64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "android"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/darwin-arm64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/darwin-x64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/freebsd-arm64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "freebsd"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/freebsd-x64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "freebsd"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/linux-arm": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/linux-arm64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/linux-ia32": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ia32"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/linux-loong64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "loong64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/linux-mips64el": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "mips64el"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/linux-ppc64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/linux-riscv64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "riscv64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/linux-s390x": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "s390x"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/linux-x64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/netbsd-x64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "netbsd"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/openbsd-x64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "openbsd"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/sunos-x64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "sunos"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/win32-arm64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/win32-ia32": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ia32"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@esbuild/win32-x64": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ],
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/@felipematos/ain-cli": {
      "resolved": "../..",
      "link": true
    },
    "node_modules/@jridgewell/sourcemap-codec": {
      "version": "1.5.5",
      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/@rollup/rollup-android-arm-eabi": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "android"
      ]
    },
    "node_modules/@rollup/rollup-android-arm64": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "android"
      ]
    },
    "node_modules/@rollup/rollup-darwin-arm64": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "darwin"
      ]
    },
    "node_modules/@rollup/rollup-darwin-x64": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "darwin"
      ]
    },
    "node_modules/@rollup/rollup-freebsd-arm64": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "freebsd"
      ]
    },
    "node_modules/@rollup/rollup-freebsd-x64": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "freebsd"
      ]
    },
    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-arm64-gnu": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-arm64-musl": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-loong64-gnu": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "loong64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-loong64-musl": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "loong64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-ppc64-musl": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ppc64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "riscv64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-riscv64-musl": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "riscv64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-s390x-gnu": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "s390x"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-x64-gnu": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-linux-x64-musl": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "linux"
      ]
    },
    "node_modules/@rollup/rollup-openbsd-x64": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "openbsd"
      ]
    },
    "node_modules/@rollup/rollup-openharmony-arm64": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "openharmony"
      ]
    },
    "node_modules/@rollup/rollup-win32-arm64-msvc": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "arm64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ]
    },
    "node_modules/@rollup/rollup-win32-ia32-msvc": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "ia32"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ]
    },
    "node_modules/@rollup/rollup-win32-x64-gnu": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ]
    },
    "node_modules/@rollup/rollup-win32-x64-msvc": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "cpu": [
        "x64"
      ],
      "dev": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "win32"
      ]
    },
    "node_modules/@types/estree": {
      "version": "1.0.8",
      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/@vitest/expect": {
      "version": "2.1.9",
      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@vitest/spy": "2.1.9",
        "@vitest/utils": "2.1.9",
        "chai": "^5.1.2",
        "tinyrainbow": "^1.2.0"
      },
      "funding": {
        "url": "https://opencollective.com/vitest"
      }
    },
    "node_modules/@vitest/mocker": {
      "version": "2.1.9",
      "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@vitest/spy": "2.1.9",
        "estree-walker": "^3.0.3",
        "magic-string": "^0.30.12"
      },
      "funding": {
        "url": "https://opencollective.com/vitest"
      },
      "peerDependencies": {
        "msw": "^2.4.9",
        "vite": "^5.0.0"
      },
      "peerDependenciesMeta": {
        "msw": {
          "optional": true
        },
        "vite": {
          "optional": true
        }
      }
    },
    "node_modules/@vitest/pretty-format": {
      "version": "2.1.9",
      "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "tinyrainbow": "^1.2.0"
      },
      "funding": {
        "url": "https://opencollective.com/vitest"
      }
    },
    "node_modules/@vitest/runner": {
      "version": "2.1.9",
      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@vitest/utils": "2.1.9",
        "pathe": "^1.1.2"
      },
      "funding": {
        "url": "https://opencollective.com/vitest"
      }
    },
    "node_modules/@vitest/snapshot": {
      "version": "2.1.9",
      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@vitest/pretty-format": "2.1.9",
        "magic-string": "^0.30.12",
        "pathe": "^1.1.2"
      },
      "funding": {
        "url": "https://opencollective.com/vitest"
      }
    },
    "node_modules/@vitest/spy": {
      "version": "2.1.9",
      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "tinyspy": "^3.0.2"
      },
      "funding": {
        "url": "https://opencollective.com/vitest"
      }
    },
    "node_modules/@vitest/utils": {
      "version": "2.1.9",
      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@vitest/pretty-format": "2.1.9",
        "loupe": "^3.1.2",
        "tinyrainbow": "^1.2.0"
      },
      "funding": {
        "url": "https://opencollective.com/vitest"
      }
    },
    "node_modules/assertion-error": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">=12"
      }
    },
    "node_modules/cac": {
      "version": "6.7.14",
      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">=8"
      }
    },
    "node_modules/chai": {
      "version": "5.3.3",
      "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "assertion-error": "^2.0.1",
        "check-error": "^2.1.1",
        "deep-eql": "^5.0.1",
        "loupe": "^3.1.0",
        "pathval": "^2.0.0"
      },
      "engines": {
        "node": ">=18"
      }
    },
    "node_modules/check-error": {
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">= 16"
      }
    },
    "node_modules/debug": {
      "version": "4.4.3",
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "ms": "^2.1.3"
      },
      "engines": {
        "node": ">=6.0"
      },
      "peerDependenciesMeta": {
        "supports-color": {
          "optional": true
        }
      }
    },
    "node_modules/deep-eql": {
      "version": "5.0.2",
      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">=6"
      }
    },
    "node_modules/es-module-lexer": {
      "version": "1.7.0",
      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/esbuild": {
      "version": "0.21.5",
      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "hasInstallScript": true,
      "license": "MIT",
      "bin": {
        "esbuild": "bin/esbuild"
      },
      "engines": {
        "node": ">=12"
      },
      "optionalDependencies": {
        "@esbuild/aix-ppc64": "0.21.5",
        "@esbuild/android-arm": "0.21.5",
        "@esbuild/android-arm64": "0.21.5",
        "@esbuild/android-x64": "0.21.5",
        "@esbuild/darwin-arm64": "0.21.5",
        "@esbuild/darwin-x64": "0.21.5",
        "@esbuild/freebsd-arm64": "0.21.5",
        "@esbuild/freebsd-x64": "0.21.5",
        "@esbuild/linux-arm": "0.21.5",
        "@esbuild/linux-arm64": "0.21.5",
        "@esbuild/linux-ia32": "0.21.5",
        "@esbuild/linux-loong64": "0.21.5",
        "@esbuild/linux-mips64el": "0.21.5",
        "@esbuild/linux-ppc64": "0.21.5",
        "@esbuild/linux-riscv64": "0.21.5",
        "@esbuild/linux-s390x": "0.21.5",
        "@esbuild/linux-x64": "0.21.5",
        "@esbuild/netbsd-x64": "0.21.5",
        "@esbuild/openbsd-x64": "0.21.5",
        "@esbuild/sunos-x64": "0.21.5",
        "@esbuild/win32-arm64": "0.21.5",
        "@esbuild/win32-ia32": "0.21.5",
        "@esbuild/win32-x64": "0.21.5"
      }
    },
    "node_modules/estree-walker": {
      "version": "3.0.3",
      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/estree": "^1.0.0"
      }
    },
    "node_modules/expect-type": {
      "version": "1.3.0",
      "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "Apache-2.0",
      "engines": {
        "node": ">=12.0.0"
      }
    },
    "node_modules/fsevents": {
      "version": "2.3.3",
      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "hasInstallScript": true,
      "license": "MIT",
      "optional": true,
      "os": [
        "darwin"
      ],
      "engines": {
        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
      }
    },
    "node_modules/loupe": {
      "version": "3.2.1",
      "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/magic-string": {
      "version": "0.30.21",
      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@jridgewell/sourcemap-codec": "^1.5.5"
      }
    },
    "node_modules/ms": {
      "version": "2.1.3",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/nanoid": {
      "version": "3.3.11",
      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "funding": [
        {
          "type": "github",
          "url": "https://github.com/sponsors/ai"
        }
      ],
      "license": "MIT",
      "bin": {
        "nanoid": "bin/nanoid.cjs"
      },
      "engines": {
        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
      }
    },
    "node_modules/pathe": {
      "version": "1.1.2",
      "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/pathval": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">= 14.16"
      }
    },
    "node_modules/picocolors": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "ISC"
    },
    "node_modules/postcss": {
      "version": "8.5.8",
      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "funding": [
        {
          "type": "opencollective",
          "url": "https://opencollective.com/postcss/"
        },
        {
          "type": "tidelift",
          "url": "https://tidelift.com/funding/github/npm/postcss"
        },
        {
          "type": "github",
          "url": "https://github.com/sponsors/ai"
        }
      ],
      "license": "MIT",
      "dependencies": {
        "nanoid": "^3.3.11",
        "picocolors": "^1.1.1",
        "source-map-js": "^1.2.1"
      },
      "engines": {
        "node": "^10 || ^12 || >=14"
      }
    },
    "node_modules/rollup": {
      "version": "4.59.0",
      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@types/estree": "1.0.8"
      },
      "bin": {
        "rollup": "dist/bin/rollup"
      },
      "engines": {
        "node": ">=18.0.0",
        "npm": ">=8.0.0"
      },
      "optionalDependencies": {
        "@rollup/rollup-android-arm-eabi": "4.59.0",
        "@rollup/rollup-android-arm64": "4.59.0",
        "@rollup/rollup-darwin-arm64": "4.59.0",
        "@rollup/rollup-darwin-x64": "4.59.0",
        "@rollup/rollup-freebsd-arm64": "4.59.0",
        "@rollup/rollup-freebsd-x64": "4.59.0",
        "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
        "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
        "@rollup/rollup-linux-arm64-gnu": "4.59.0",
        "@rollup/rollup-linux-arm64-musl": "4.59.0",
        "@rollup/rollup-linux-loong64-gnu": "4.59.0",
        "@rollup/rollup-linux-loong64-musl": "4.59.0",
        "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
        "@rollup/rollup-linux-ppc64-musl": "4.59.0",
        "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
        "@rollup/rollup-linux-riscv64-musl": "4.59.0",
        "@rollup/rollup-linux-s390x-gnu": "4.59.0",
        "@rollup/rollup-linux-x64-gnu": "4.59.0",
        "@rollup/rollup-linux-x64-musl": "4.59.0",
        "@rollup/rollup-openbsd-x64": "4.59.0",
        "@rollup/rollup-openharmony-arm64": "4.59.0",
        "@rollup/rollup-win32-arm64-msvc": "4.59.0",
        "@rollup/rollup-win32-ia32-msvc": "4.59.0",
        "@rollup/rollup-win32-x64-gnu": "4.59.0",
        "@rollup/rollup-win32-x64-msvc": "4.59.0",
        "fsevents": "~2.3.2"
      }
    },
    "node_modules/siginfo": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "ISC"
    },
    "node_modules/source-map-js": {
      "version": "1.2.1",
      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "BSD-3-Clause",
      "engines": {
        "node": ">=0.10.0"
      }
    },
    "node_modules/stackback": {
      "version": "0.0.2",
      "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/std-env": {
      "version": "3.10.0",
      "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/tinybench": {
      "version": "2.9.0",
      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/tinyexec": {
      "version": "0.3.2",
      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT"
    },
    "node_modules/tinypool": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": "^18.0.0 || >=20.0.0"
      }
    },
    "node_modules/tinyrainbow": {
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">=14.0.0"
      }
    },
    "node_modules/tinyspy": {
      "version": "3.0.2",
      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "engines": {
        "node": ">=14.0.0"
      }
    },
    "node_modules/typescript": {
      "version": "5.9.3",
      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "Apache-2.0",
      "bin": {
        "tsc": "bin/tsc",
        "tsserver": "bin/tsserver"
      },
      "engines": {
        "node": ">=14.17"
      }
    },
    "node_modules/vite": {
      "version": "5.4.21",
      "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "esbuild": "^0.21.3",
        "postcss": "^8.4.43",
        "rollup": "^4.20.0"
      },
      "bin": {
        "vite": "bin/vite.js"
      },
      "engines": {
        "node": "^18.0.0 || >=20.0.0"
      },
      "funding": {
        "url": "https://github.com/vitejs/vite?sponsor=1"
      },
      "optionalDependencies": {
        "fsevents": "~2.3.3"
      },
      "peerDependencies": {
        "@types/node": "^18.0.0 || >=20.0.0",
        "less": "*",
        "lightningcss": "^1.21.0",
        "sass": "*",
        "sass-embedded": "*",
        "stylus": "*",
        "sugarss": "*",
        "terser": "^5.4.0"
      },
      "peerDependenciesMeta": {
        "@types/node": {
          "optional": true
        },
        "less": {
          "optional": true
        },
        "lightningcss": {
          "optional": true
        },
        "sass": {
          "optional": true
        },
        "sass-embedded": {
          "optional": true
        },
        "stylus": {
          "optional": true
        },
        "sugarss": {
          "optional": true
        },
        "terser": {
          "optional": true
        }
      }
    },
    "node_modules/vite-node": {
      "version": "2.1.9",
      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "cac": "^6.7.14",
        "debug": "^4.3.7",
        "es-module-lexer": "^1.5.4",
        "pathe": "^1.1.2",
        "vite": "^5.0.0"
      },
      "bin": {
        "vite-node": "vite-node.mjs"
      },
      "engines": {
        "node": "^18.0.0 || >=20.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/vitest"
      }
    },
    "node_modules/vitest": {
      "version": "2.1.9",
      "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "@vitest/expect": "2.1.9",
        "@vitest/mocker": "2.1.9",
        "@vitest/pretty-format": "^2.1.9",
        "@vitest/runner": "2.1.9",
        "@vitest/snapshot": "2.1.9",
        "@vitest/spy": "2.1.9",
        "@vitest/utils": "2.1.9",
        "chai": "^5.1.2",
        "debug": "^4.3.7",
        "expect-type": "^1.1.0",
        "magic-string": "^0.30.12",
        "pathe": "^1.1.2",
        "std-env": "^3.8.0",
        "tinybench": "^2.9.0",
        "tinyexec": "^0.3.1",
        "tinypool": "^1.0.1",
        "tinyrainbow": "^1.2.0",
        "vite": "^5.0.0",
        "vite-node": "2.1.9",
        "why-is-node-running": "^2.3.0"
      },
      "bin": {
        "vitest": "vitest.mjs"
      },
      "engines": {
        "node": "^18.0.0 || >=20.0.0"
      },
      "funding": {
        "url": "https://opencollective.com/vitest"
      },
      "peerDependencies": {
        "@edge-runtime/vm": "*",
        "@types/node": "^18.0.0 || >=20.0.0",
        "@vitest/browser": "2.1.9",
        "@vitest/ui": "2.1.9",
        "happy-dom": "*",
        "jsdom": "*"
      },
      "peerDependenciesMeta": {
        "@edge-runtime/vm": {
          "optional": true
        },
        "@types/node": {
          "optional": true
        },
        "@vitest/browser": {
          "optional": true
        },
        "@vitest/ui": {
          "optional": true
        },
        "happy-dom": {
          "optional": true
        },
        "jsdom": {
          "optional": true
        }
      }
    },
    "node_modules/why-is-node-running": {
      "version": "2.3.0",
      "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
      "integrity": [REDACTED_SECRET],
      "dev": true,
      "license": "MIT",
      "dependencies": {
        "siginfo": "^2.0.0",
        "stackback": "0.0.2"
      },
      "bin": {
        "why-is-node-running": "cli.js"
      },
      "engines": {
        "node": ">=8"
      }
    }
  }
}


path: package.json
content: {
  "name": "openclaw-plugin-ain",
  "version": "0.2.2",
  "description": "OpenClaw plugin for AIN — provider registry, routing, and execution",
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/felipematos/ain.git",
    "directory": "packages/openclaw-plugin-ain"
  },
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "files": [
    "dist",
    "openclaw.plugin.json",
    "README.md"
  ],
  "scripts": {
    "build": "tsc",
    "test": "vitest run",
    "prepublishOnly": "npm run build && npm run test"
  },
  "dependencies": {
    "@felipematos/ain-cli": "^0.11.0"
  },
  "peerDependencies": {
    "openclaw": ">=1.0.0"
  },
  "peerDependenciesMeta": {
    "openclaw": {
      "optional": true
    }
  },
  "devDependencies": {
    "typescript": "^5.5.0",
    "vitest": "^2.0.0"
  }
}


path: src/index.ts
content: import { loadConfig } from '@felipematos/ain-cli';
import { registerAinProviders } from './providers.js';
import { registerAinTools } from './tools.js';
import { registerRoutingHook } from './routing-hook.js';
import type { OpenClawPluginDefinition, OpenClawPluginApi, AinPluginConfig } from './types.js';

const plugin: OpenClawPluginDefinition = {
  id: 'openclaw-plugin-ain',
  name: 'AIN - AI Node',
  description: 'AIN provider registry, routing, and execution for OpenClaw',
  version: '0.2.2',

  async register(api: OpenClawPluginApi) {
    const pluginConfig = api.pluginConfig as AinPluginConfig;
    const config = loadConfig();

    registerAinProviders(api, config);

    if (pluginConfig.exposeTools !== false) {
      registerAinTools(api);
    }

    if (pluginConfig.enableRouting !== false) {
      registerRoutingHook(api, pluginConfig.routingPolicy);
    }

    api.logger.info('AIN plugin registered successfully');
  },
};

export default plugin;


path: src/providers.ts
content: import { createAdapter, loadConfig } from '@felipematos/ain-cli';
import type { AinConfig } from '@felipematos/ain-cli';
import type { OpenClawPluginApi, OpenClawChatRequest, OpenClawChatResponse } from './types.js';

export function registerAinProviders(api: OpenClawPluginApi, config: AinConfig): void {
  for (const [name, providerConfig] of Object.entries(config.providers)) {
    const adapter = createAdapter(providerConfig);
    const models = (providerConfig.models ?? []).map((m) => m.id);

    api.registerProvider({
      id: `ain:${name}`,
      name: `AIN/${name}`,
      models,
      async chat(request: OpenClawChatRequest): Promise<OpenClawChatResponse> {
        const response = await adapter.chat({
          model: request.model,
          messages: request.messages.map((m) => ({
            role: m.role as 'system' | 'user' | 'assistant',
            content: m.content,
          })),
          temperature: request.temperature,
          max_tokens: request.max_tokens,
          ...(request.response_format ? { response_format: request.response_format as { type: 'json_object' } } : {}),
        });

        const content = response.choices[0]?.message?.content ?? '';
        return {
          content,
          model: response.model,
          usage: response.usage,
        };
      },
      async *chatStream(request: OpenClawChatRequest): AsyncGenerator<string> {
        yield* adapter.chatStream({
          model: request.model,
          messages: request.messages.map((m) => ({
            role: m.role as 'system' | 'user' | 'assistant',
            content: m.content,
          })),
          temperature: request.temperature,
          max_tokens: request.max_tokens,
        });
      },
    });

    api.logger.info(`Registered provider ain:${name} with ${models.length} model(s)`);
  }
}


path: src/routing-hook.ts
content: import { route } from '@felipematos/ain-cli';
import type { OpenClawPluginApi } from './types.js';

export function registerRoutingHook(api: OpenClawPluginApi, policyName?: string): void {
  api.registerHook({
    event: 'before_model_resolve',
    async handler(context) {
      const prompt = context.prompt as string | undefined;
      if (!prompt) return;

      try {
        const decision = await route({
          prompt,
          policyName,
        });

        return {
          provider: `ain:${decision.provider}`,
          model: decision.model,
          metadata: {
            tier: decision.tier,
            rationale: decision.rationale,
            source: 'ain-routing',
          },
        };
      } catch (err) {
        api.logger.warn(`AIN routing failed: ${err instanceof Error ? err.message : String(err)}`);
        return;
      }
    },
  });
}


path: src/tools.ts
content: import { run, classifyTask, estimateComplexity } from '@felipematos/ain-cli';
import type { OpenClawPluginApi } from './types.js';

export function registerAinTools(api: OpenClawPluginApi): void {
  api.registerTool({
    name: 'ain_run',
    description: 'Execute an LLM prompt through AIN with provider routing and structured output support',
    parameters: {
      type: 'object',
      properties: {
        prompt: { type: 'string', description: 'The prompt to send to the LLM' },
        provider: { type: 'string', description: 'Provider name (optional)' },
        model: { type: 'string', description: 'Model ID or alias (optional)' },
        jsonMode: { type: 'boolean', description: 'Request JSON output' },
        schema: { type: 'object', description: 'JSON schema for structured output' },
        system: { type: 'string', description: 'System prompt' },
        temperature: { type: 'number', description: 'Temperature (0-2)' },
      },
      required: ['prompt'],
    },
    async execute(params) {
      const result = await run({
        prompt: params.prompt as string,
        provider: params.provider as string | undefined,
        model: params.model as string | undefined,
        jsonMode: params.jsonMode as boolean | undefined,
        schema: params.schema as object | undefined,
        system: params.system as string | undefined,
        temperature: params.temperature as number | undefined,
      });
      return {
        output: result.output,
        provider: result.provider,
        model: result.model,
        usage: result.usage,
        parsedOutput: result.parsedOutput,
      };
    },
  });

  api.registerTool({
    name: 'ain_classify',
    description: 'Classify a prompt by task type and estimate its complexity',
    parameters: {
      type: 'object',
      properties: {
        prompt: { type: 'string', description: 'The prompt to classify' },
      },
      required: ['prompt'],
    },
    async execute(params) {
      const prompt = params.prompt as string;
      return {
        taskType: classifyTask(prompt),
        complexity: estimateComplexity(prompt),
      };
    },
  });
}


path: src/types.ts
content: /**
 * Minimal OpenClaw plugin API types.
 * Defined here to avoid a hard dependency on openclaw — it's a peer dep.
 */

export interface OpenClawToolDefinition {
  name: string;
  description: string;
  parameters: Record<string, unknown>;
  execute: (params: Record<string, unknown>) => Promise<unknown>;
}

export interface OpenClawProviderDefinition {
  id: string;
  name: string;
  models: string[];
  chat: (request: OpenClawChatRequest) => Promise<OpenClawChatResponse>;
  chatStream?: (request: OpenClawChatRequest) => AsyncGenerator<string>;
}

export interface OpenClawChatRequest {
  model: string;
  messages: Array<{ role: string; content: string }>;
  temperature?: number;
  max_tokens?: number;
  response_format?: unknown;
}

export interface OpenClawChatResponse {
  content: string;
  model: string;
  usage?: {
    prompt_tokens: number;
    completion_tokens: number;
    total_tokens: number;
  };
}

export interface OpenClawHookDefinition {
  event: string;
  handler: (context: Record<string, unknown>) => Promise<Record<string, unknown> | void>;
}

export interface OpenClawLogger {
  info: (message: string) => void;
  warn: (message: string) => void;
  error: (message: string) => void;
  debug: (message: string) => void;
}

export interface OpenClawPluginApi {
  registerTool: (tool: OpenClawToolDefinition) => void;
  registerProvider: (provider: OpenClawProviderDefinition) => void;
  registerHook: (hook: OpenClawHookDefinition) => void;
  pluginConfig: Record<string, unknown>;
  logger: OpenClawLogger;
}

export interface OpenClawPluginDefinition {
  id: string;
  name: string;
  description: string;
  version: string;
  register: (api: OpenClawPluginApi) => Promise<void>;
}

export interface AinPluginConfig {
  configPath?: string;
  enableRouting?: boolean;
  routingPolicy?: string;
  exposeTools?: boolean;
}


path: test/plugin.test.ts
content: import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { OpenClawPluginApi, OpenClawToolDefinition, OpenClawProviderDefinition, OpenClawHookDefinition } from '../src/types.js';

// Mock ain-cli
vi.mock('@felipematos/ain-cli', () => ({
  loadConfig: vi.fn(() => ({
    version: 1,
    providers: {
      local: {
        kind: 'openai-compatible',
        baseUrl: 'http://localhost:1234/v1',
        models: [{ id: 'llama-3' }, { id: 'mistral-7b' }],
        timeoutMs: 60000,
      },
      openai: {
        kind: 'openai-compatible',
        baseUrl: 'https://api.openai.com/v1',
        [REDACTED_SECRET]',
        models: [{ id: 'gpt-4o' }],
        timeoutMs: 60000,
      },
    },
    defaults: { provider: 'local' },
  })),
  createAdapter: vi.fn(() => ({
    chat: vi.fn(async () => ({
      id: 'test',
      object: 'chat.completion',
      created: 1,
      model: 'llama-3',
      choices: [{ index: 0, message: { role: 'assistant', content: 'Hello' }, finish_reason: 'stop' }],
      usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
    })),
    chatStream: vi.fn(async function* () {
      yield 'Hello';
      yield ' world';
    }),
  })),
  run: vi.fn(async () => ({
    ok: true,
    provider: 'local',
    model: 'llama-3',
    output: 'test output',
    usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
  })),
  route: vi.fn(() => ({
    provider: 'local',
    model: 'llama-3',
    tier: 'general',
    rationale: 'Heuristic routing',
  })),
  classifyTask: vi.fn(() => 'generation'),
  estimateComplexity: vi.fn(() => 'low'),
}));

function createMockApi(): OpenClawPluginApi & {
  tools: OpenClawToolDefinition[];
  providers: OpenClawProviderDefinition[];
  hooks: OpenClawHookDefinition[];
} {
  const tools: OpenClawToolDefinition[] = [];
  const providers: OpenClawProviderDefinition[] = [];
  const hooks: OpenClawHookDefinition[] = [];

  return {
    tools,
    providers,
    hooks,
    pluginConfig: {},
    logger: {
      info: vi.fn(),
      warn: vi.fn(),
      error: vi.fn(),
      debug: vi.fn(),
    },
    registerTool(tool: OpenClawToolDefinition) {
      tools.push(tool);
    },
    registerProvider(provider: OpenClawProviderDefinition) {
      providers.push(provider);
    },
    registerHook(hook: OpenClawHookDefinition) {
      hooks.push(hook);
    },
  };
}

describe('OpenClaw plugin registration', () => {
  let mockApi: ReturnType<typeof createMockApi>;

  beforeEach(() => {
    mockApi = createMockApi();
  });

  it('registers a provider for each AIN provider', async () => {
    const plugin = (await import('../src/index.js')).default;
    await plugin.register(mockApi);

    expect(mockApi.providers).toHaveLength(2);
    expect(mockApi.providers.map((p) => p.id)).toEqual(['ain:local', 'ain:openai']);
  });

  it('registers providers with correct model lists', async () => {
    const plugin = (await import('../src/index.js')).default;
    await plugin.register(mockApi);

    const local = mockApi.providers.find((p) => p.id === 'ain:local');
    expect(local?.models).toEqual(['llama-3', 'mistral-7b']);

    const openai = mockApi.providers.find((p) => p.id === 'ain:openai');
    expect(openai?.models).toEqual(['gpt-4o']);
  });

  it('registers ain_run and ain_classify tools', async () => {
    const plugin = (await import('../src/index.js')).default;
    await plugin.register(mockApi);

    const toolNames = mockApi.tools.map((t) => t.name);
    expect(toolNames).toContain('ain_run');
    expect(toolNames).toContain('ain_classify');
  });

  it('registers before_model_resolve hook', async () => {
    const plugin = (await import('../src/index.js')).default;
    await plugin.register(mockApi);

    expect(mockApi.hooks).toHaveLength(1);
    expect(mockApi.hooks[0]?.event).toBe('before_model_resolve');
  });

  it('skips tools when exposeTools=false', async () => {
    mockApi.pluginConfig = { exposeTools: false };
    const plugin = (await import('../src/index.js')).default;
    await plugin.register(mockApi);

    expect(mockApi.tools).toHaveLength(0);
  });

  it('skips routing hook when enableRouting=false', async () => {
    mockApi.pluginConfig = { enableRouting: false };
    const plugin = (await import('../src/index.js')).default;
    await plugin.register(mockApi);

    expect(mockApi.hooks).toHaveLength(0);
  });

  it('ain_run tool executes a prompt', async () => {
    const plugin = (await import('../src/index.js')).default;
    await plugin.register(mockApi);

    const ainRun = mockApi.tools.find((t) => t.name === 'ain_run');
    const result = await ainRun!.execute({ prompt: 'Hello world' });
    expect(result).toHaveProperty('output', 'test output');
    expect(result).toHaveProperty('provider', 'local');
  });

  it('ain_classify tool classifies a prompt', async () => {
    const plugin = (await import('../src/index.js')).default;
    await plugin.register(mockApi);

    const ainClassify = mockApi.tools.find((t) => t.name === 'ain_classify');
    const result = await ainClassify!.execute({ prompt: 'Write a poem' });
    expect(result).toEqual({ taskType: 'generation', complexity: 'low' });
  });

  it('routing hook returns provider and model', async () => {
    const plugin = (await import('../src/index.js')).default;
    await plugin.register(mockApi);

    const hook = mockApi.hooks[0]!;
    const result = await hook.handler({ prompt: 'Hello' });
    expect(result).toEqual({
      provider: 'ain:local',
      model: 'llama-3',
      metadata: {
        tier: 'general',
        rationale: 'Heuristic routing',
        source: 'ain-routing',
      },
    });
  });

  it('has correct plugin metadata', async () => {
    const plugin = (await import('../src/index.js')).default;
    expect(plugin.id).toBe('openclaw-plugin-ain');
    expect(plugin.name).toBe('AIN - AI Node');
    expect(plugin.version).toBe('0.2.2');
  });
});


path: tsconfig.json
content: {
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "test"]
}


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (HIGH)