@hasna/switcher 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env bun
2
+ export declare function main(args?: string[]): Promise<void>;
@@ -0,0 +1,2 @@
1
+ import { Store } from "./store";
2
+ export declare function createHandler(store: Store, apiKey: string, providerEnv?: Record<string, string | undefined>): (request: Request) => Promise<Response>;
@@ -0,0 +1,48 @@
1
+ import { SQL } from "bun";
2
+ type Connection = Pick<SQL, "unsafe">;
3
+ export type Resource = "providers" | "profiles" | "runs" | "catalogs";
4
+ export type RecordValue = {
5
+ id: string;
6
+ version: number;
7
+ updatedAt: string;
8
+ [key: string]: unknown;
9
+ };
10
+ export declare class Store {
11
+ private tail;
12
+ private exclusive;
13
+ readonly sql: SQL;
14
+ readonly engine: "sqlite" | "postgresql";
15
+ private constructor();
16
+ static open(config: {
17
+ databaseUrl?: string;
18
+ sqlitePath?: string;
19
+ }): Promise<Store>;
20
+ private migrate;
21
+ ready(): Promise<void>;
22
+ close(): Promise<void>;
23
+ get<T>(kind: Resource, id: string, db?: Connection): Promise<T>;
24
+ private read;
25
+ list<T>(kind: Resource, { limit, offset, search }?: {
26
+ limit?: number | undefined;
27
+ offset?: number | undefined;
28
+ search?: string | undefined;
29
+ }): Promise<{
30
+ data: T[];
31
+ total: number;
32
+ limit: number;
33
+ offset: number;
34
+ }>;
35
+ put(kind: Resource, input: Record<string, any>, expectedVersion: number | undefined, db: Connection): Promise<any>;
36
+ remove(kind: "providers" | "profiles", id: string, version: number, db: Connection): Promise<{
37
+ deleted: string;
38
+ }>;
39
+ mutate<T>(key: string, fingerprint: string, action: (db: Connection) => Promise<T>): Promise<T>;
40
+ replay(key: string, fingerprint: string): Promise<{
41
+ found: false;
42
+ } | {
43
+ found: true;
44
+ value: unknown;
45
+ }>;
46
+ private transaction;
47
+ }
48
+ export {};
@@ -0,0 +1,41 @@
1
+ services:
2
+ switcher-sqlite:
3
+ profiles: [sqlite]
4
+ build: .
5
+ command: ["bun", "dist/serve/index.js", "--host", "0.0.0.0", "--port", "8080", "--data-dir", "/data"]
6
+ environment:
7
+ HASNA_SWITCHER_API_KEY: ${HASNA_SWITCHER_API_KEY:?Inject the operator API token}
8
+ SWITCHER_PROVIDER_OPENROUTER: ${SWITCHER_PROVIDER_OPENROUTER:-}
9
+ volumes:
10
+ - switcher-data:/data
11
+ ports:
12
+ - "127.0.0.1:8080:8080"
13
+ switcher-postgres:
14
+ profiles: [postgres]
15
+ build: .
16
+ environment:
17
+ HASNA_SWITCHER_API_KEY: ${HASNA_SWITCHER_API_KEY:?Inject the operator API token}
18
+ HASNA_SWITCHER_DATABASE_URL: ${HASNA_SWITCHER_DATABASE_URL:-}
19
+ SWITCHER_PROVIDER_OPENROUTER: ${SWITCHER_PROVIDER_OPENROUTER:-}
20
+ ports:
21
+ - "127.0.0.1:8080:8080"
22
+ depends_on:
23
+ postgres:
24
+ condition: service_healthy
25
+ postgres:
26
+ profiles: [postgres]
27
+ image: postgres:17
28
+ environment:
29
+ POSTGRES_DB: switcher
30
+ POSTGRES_USER: switcher
31
+ POSTGRES_PASSWORD: ${SWITCHER_POSTGRES_PASSWORD:-}
32
+ volumes:
33
+ - postgres-data:/var/lib/postgresql/data
34
+ healthcheck:
35
+ test: ["CMD-SHELL", "pg_isready -U switcher -d switcher"]
36
+ interval: 5s
37
+ timeout: 3s
38
+ retries: 10
39
+ volumes:
40
+ switcher-data:
41
+ postgres-data:
@@ -0,0 +1,94 @@
1
+ {
2
+ "$schema": "./node_modules/@hasna/contracts/src/hasna.contract.schema.json",
3
+ "schema": "hasna.service_contract.v1",
4
+ "name": "switcher",
5
+ "class": "cli-with-store",
6
+ "contractVersion": "v1",
7
+ "kitVersion": "1.0.1",
8
+ "description": "Universal provider and model switcher for coding harnesses with an API, SDK and self-hosted storage",
9
+ "bins": [
10
+ "switcher",
11
+ "switcher-mcp",
12
+ "switcher-serve"
13
+ ],
14
+ "hosting": [
15
+ "user-hosted"
16
+ ],
17
+ "serviceSurfaces": [
18
+ {
19
+ "name": "http-api",
20
+ "kind": "api",
21
+ "status": "supported",
22
+ "bin": "switcher-serve",
23
+ "authMode": "api-key",
24
+ "health": {
25
+ "method": "GET",
26
+ "path": "/health",
27
+ "public": true
28
+ },
29
+ "readiness": {
30
+ "method": "GET",
31
+ "path": "/ready",
32
+ "public": true
33
+ },
34
+ "version": {
35
+ "method": "GET",
36
+ "path": "/version",
37
+ "public": true
38
+ },
39
+ "apiBasePath": "/v1",
40
+ "openApiPath": "/v1/openapi.json"
41
+ },
42
+ {
43
+ "name": "package-sdk",
44
+ "kind": "sdk",
45
+ "status": "supported",
46
+ "authMode": "api-key",
47
+ "exportSubpath": "./sdk",
48
+ "generatedFrom": "/v1/openapi.json"
49
+ },
50
+ {
51
+ "name": "mcp",
52
+ "kind": "mcp",
53
+ "status": "supported",
54
+ "mcpBin": "switcher-mcp",
55
+ "authMode": "api-key"
56
+ },
57
+ {
58
+ "name": "cli",
59
+ "kind": "cli",
60
+ "status": "supported",
61
+ "bin": "switcher",
62
+ "authMode": "api-key"
63
+ }
64
+ ],
65
+ "storage": {
66
+ "backend": "postgresql",
67
+ "engines": [
68
+ "sqlite",
69
+ "postgresql"
70
+ ],
71
+ "envPrefix": "HASNA_SWITCHER_",
72
+ "aliasEnvPrefix": "SWITCHER_",
73
+ "sqlitePath": "~/.hasna/switcher/switcher.db",
74
+ "pgTestGate": {
75
+ "envVar": "SWITCHER_TEST_DATABASE_URL",
76
+ "command": "bun run test:postgres"
77
+ }
78
+ },
79
+ "metadata": {
80
+ "release": {
81
+ "artifactScan": {
82
+ "script": "scan:artifact"
83
+ }
84
+ },
85
+ "switcherStorageException": {
86
+ "authority": "Explicit user directive 2026-09-05, task 01a07181-ca8d-70c1-99a2-b276dc5770f3",
87
+ "serverBackends": [
88
+ "sqlite",
89
+ "postgresql"
90
+ ],
91
+ "scope": "Only switcher HTTP server; all CLI, SDK and MCP data access remains authenticated HTTP. PostgreSQL remains canonical backend."
92
+ }
93
+ }
94
+ }