@nexuss0781/nar 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 nexuss0781
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # @nexuss0781/nar
2
+
3
+ Minimal TypeScript client for **Nexuss AI Router (NAR)**. Zero dependencies.
4
+
5
+ Do not hand-write `curl` requests, and do not call a provider directly. NAR routes
6
+ every call to a healthy free model and falls back automatically when one is busy.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install @nexuss0781/nar
12
+ ```
13
+
14
+ Zero dependencies. Node 18+, Bun, Deno, Cloudflare Workers, Vercel Edge.
15
+
16
+ ```bash
17
+ export NAR_BASE_URL="https://omniouter-vercel.vercel.app"
18
+ export NAR_API_KEY="<your master key>"
19
+ ```
20
+
21
+
22
+ ## Stream a completion
23
+
24
+ ```ts
25
+ import { stream } from "@nexuss0781/nar";
26
+
27
+ for await (const delta of stream("Explain ownership in Rust in two sentences")) {
28
+ process.stdout.write(delta);
29
+ }
30
+ ```
31
+
32
+ ## Get the whole answer
33
+
34
+ ```ts
35
+ import { chat } from "@nexuss0781/nar";
36
+
37
+ const { text, route } = await chat("Write a haiku about routers");
38
+ console.log(text);
39
+ console.log(route.provider, route.model); // e.g. "puter puter/infron:qwen/qwen3.8-27b:free"
40
+ ```
41
+
42
+ ## Which model actually answered
43
+
44
+ `route.provider` and `route.model` come from NAR's response headers, not from
45
+ guessing. Log them — they are how you measure which free route is actually
46
+ carrying traffic, and how you tell a silent fallback from a healthy call.
47
+
48
+ ```ts
49
+ const { text, route } = await chat(prompt);
50
+ console.log(`[${route.provider}/${route.model}] ${text.length} chars`);
51
+ ```
52
+
53
+ ## Pin a specific model
54
+
55
+ Only when you need determinism. Omit `model` to let NAR choose.
56
+
57
+ ```ts
58
+ await chat("hi", { model: "puter/infron:qwen/qwen3.8-27b:free" });
59
+ ```
60
+
61
+ Call `models()` to list what is live right now:
62
+
63
+ ```ts
64
+ import { models } from "@nexuss0781/nar";
65
+ console.log(await models());
66
+ ```
67
+
68
+ ## Tool calling
69
+
70
+ Tool call deltas are accumulated for you and arrive keyed by index.
71
+
72
+ ```ts
73
+ import { streamEvents, complete } from "@nexuss0781/nar";
74
+
75
+ for await (const event of streamEvents("What is the weather in Oslo?", {
76
+ tools: [{
77
+ type: "function",
78
+ function: {
79
+ name: "get_weather",
80
+ description: "Current weather for a city",
81
+ parameters: {
82
+ type: "object",
83
+ properties: { city: { type: "string" } },
84
+ required: ["city"],
85
+ },
86
+ },
87
+ }],
88
+ })) {
89
+ for (const call of Object.values(event.toolCalls)) {
90
+ if (call.name && !event.delta) console.log(call.name, call.arguments);
91
+ }
92
+ }
93
+ ```
94
+
95
+ NAR keeps a tool-calling conversation pinned to the model that started it, so a
96
+ multi-turn tool loop does not silently change models mid-conversation.
97
+
98
+ ## Errors
99
+
100
+ ```ts
101
+ import { NarError } from "@nexuss0781/nar";
102
+
103
+ try {
104
+ await chat("hi");
105
+ } catch (error) {
106
+ if (error instanceof NarError) {
107
+ console.error(error.status, error.code, error.message);
108
+ console.error(error.route.attemptTrail); // providers that failed before this one
109
+ }
110
+ }
111
+ ```
112
+
113
+ `attemptTrail` is populated when a request failed everywhere. It is the fastest way
114
+ to tell "the model was bad" from "every free tier was rate-limited".
115
+
116
+ ## Health
117
+
118
+ ```ts
119
+ import { health } from "@nexuss0781/nar";
120
+ const report = await health();
121
+ console.log(report.checks.find((c) => c.name === "puter_auth"));
122
+ ```
123
+
124
+ `puter_auth` reports `ok`, `rejected` (token died — sign out/in again) or
125
+ `unreachable` (network trouble, token probably fine). Check this before debugging
126
+ a slow provider, because a silently failing provider still returns 200 traffic
127
+ routed elsewhere.
128
+
129
+ ## API
130
+
131
+ | Function | Returns |
132
+ | --- | --- |
133
+ | `stream(prompt, opts?)` | `AsyncGenerator<string>` of text deltas |
134
+ | `streamEvents(prompt, opts?)` | `AsyncGenerator<StreamEvent>` with tool calls + route |
135
+ | `chat(prompt, opts?)` | `Promise<ChatResult>` — text + route |
136
+ | `complete(prompt, opts?)` | `Promise<ChatResult>` — non-streaming, keeps usage |
137
+ | `models(opts?)` | `Promise<string[]>` of live model ids |
138
+ | `health(opts?)` | `Promise<HealthReport>` |
139
+
140
+ `ChatOptions`: `model`, `system`, `temperature`, `maxTokens`, `tools`, `signal`,
141
+ `baseUrl`, `apiKey`, `extra`.
142
+
143
+ ## Rules for agents
144
+
145
+ - Always default to `model: "auto"`. Pin a model only for tests that must be
146
+ reproducible.
147
+ - Never read a provider API key. One NAR master key is the whole credential.
148
+ - Never retry on 429 yourself; NAR already falls back. Retry only on `5xx`, and
149
+ at most twice.
150
+ - Never assume a model name is stable. Call `models()` if you need to verify one.
151
+ - Streaming responses usually omit `usage`. Do not assert on token counts.
@@ -0,0 +1,112 @@
1
+ /**
2
+ * nar — minimal client for Nexuss AI Router.
3
+ *
4
+ * Zero dependencies: uses the platform fetch, so it runs on Node 18+, Bun, Deno,
5
+ * Cloudflare Workers and Vercel Edge without a polyfill.
6
+ *
7
+ * The point of this wrapper is that a caller never assembles a URL, never picks a
8
+ * provider, and never handles a provider outage. Pass "auto" (the default) and NAR
9
+ * picks a healthy free model. Every call reports which model actually served it.
10
+ */
11
+ export type ChatOptions = {
12
+ /** Model id such as "auto", or a pinned id like "puter/infron:qwen/qwen3.8-27b:free". */
13
+ model?: string;
14
+ /** System prompt. Appended ahead of the user turn. */
15
+ system?: string;
16
+ temperature?: number;
17
+ maxTokens?: number;
18
+ /** OpenAI-style tool definitions, for tool-calling loops. */
19
+ tools?: unknown[];
20
+ /** Abort the request. */
21
+ signal?: AbortSignal;
22
+ /** Override the deployment for this call only. */
23
+ baseUrl?: string;
24
+ apiKey?: string;
25
+ /** Extra body fields merged last, for anything this wrapper does not model. */
26
+ extra?: Record<string, unknown>;
27
+ };
28
+ /** Which route actually served the request, read from NAR's response headers. */
29
+ export type Route = {
30
+ provider: string | null;
31
+ model: string | null;
32
+ /** Providers that failed and were fallen through, when the request ultimately failed. */
33
+ attemptTrail: string | null;
34
+ };
35
+ /** OpenAI-shaped token usage. Keys are the wire names, so it can be forwarded as-is. */
36
+ export type Usage = {
37
+ prompt_tokens?: number;
38
+ completion_tokens?: number;
39
+ total_tokens?: number;
40
+ };
41
+ export type ChatResult = {
42
+ text: string;
43
+ route: Route;
44
+ /** Token usage, when the upstream reported it. Streaming responses usually omit it. */
45
+ usage: Usage | null;
46
+ /** Raw response envelope, for callers that need more than text. */
47
+ raw: unknown;
48
+ };
49
+ export type StreamEvent = {
50
+ /** Incremental text. Empty on role-only or tool-call-only chunks. */
51
+ delta: string;
52
+ /** Accumulated tool calls so far, keyed by index. */
53
+ toolCalls: Record<number, {
54
+ id?: string;
55
+ name?: string;
56
+ arguments: string;
57
+ }>;
58
+ finishReason: string | null;
59
+ route: Route;
60
+ };
61
+ export declare class NarError extends Error {
62
+ readonly status: number;
63
+ readonly code: string;
64
+ readonly route: Route;
65
+ readonly body: unknown;
66
+ constructor(message: string, status: number, code: string, route: Route, body: unknown);
67
+ }
68
+ /**
69
+ * Stream a chat completion as text deltas.
70
+ *
71
+ * for await (const delta of nar.stream("explain pythons")) console.log(delta);
72
+ *
73
+ * Yields plain strings, so a caller that only wants the text never touches SSE.
74
+ * Use streamEvents when tool calls are needed.
75
+ */
76
+ export declare function stream(prompt: string, options?: ChatOptions): AsyncGenerator<string>;
77
+ /** Same as stream(), but yields structured events including accumulated tool calls. */
78
+ export declare function streamEvents(prompt: string, options?: ChatOptions): AsyncGenerator<StreamEvent>;
79
+ /** Collect a full streamed answer. Convenient when streaming is not needed downstream. */
80
+ export declare function chat(prompt: string, options?: ChatOptions): Promise<ChatResult>;
81
+ /** One non-streaming request, returning the parsed OpenAI-shaped envelope. */
82
+ export declare function complete(prompt: string, options?: ChatOptions): Promise<ChatResult>;
83
+ /** List live models. Pass this to a user or an agent to see what is actually available. */
84
+ export declare function models(options?: {
85
+ baseUrl?: string;
86
+ apiKey?: string;
87
+ signal?: AbortSignal;
88
+ }): Promise<string[]>;
89
+ export type HealthReport = {
90
+ status: string;
91
+ ready: boolean;
92
+ checks: Array<{
93
+ name: string;
94
+ status: string;
95
+ detail: string;
96
+ }>;
97
+ };
98
+ /** Router health, including per-provider auth state. Useful for uptime checks. */
99
+ export declare function health(options?: {
100
+ baseUrl?: string;
101
+ signal?: AbortSignal;
102
+ }): Promise<HealthReport>;
103
+ export declare const nar: {
104
+ stream: typeof stream;
105
+ streamEvents: typeof streamEvents;
106
+ chat: typeof chat;
107
+ complete: typeof complete;
108
+ models: typeof models;
109
+ health: typeof health;
110
+ };
111
+ export default nar;
112
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,225 @@
1
+ /**
2
+ * nar — minimal client for Nexuss AI Router.
3
+ *
4
+ * Zero dependencies: uses the platform fetch, so it runs on Node 18+, Bun, Deno,
5
+ * Cloudflare Workers and Vercel Edge without a polyfill.
6
+ *
7
+ * The point of this wrapper is that a caller never assembles a URL, never picks a
8
+ * provider, and never handles a provider outage. Pass "auto" (the default) and NAR
9
+ * picks a healthy free model. Every call reports which model actually served it.
10
+ */
11
+ export class NarError extends Error {
12
+ status;
13
+ code;
14
+ route;
15
+ body;
16
+ constructor(message, status, code, route, body) {
17
+ super(message);
18
+ this.name = "NarError";
19
+ this.status = status;
20
+ this.code = code;
21
+ this.route = route;
22
+ this.body = body;
23
+ }
24
+ }
25
+ const DEFAULT_MODEL = "auto";
26
+ function envBase() {
27
+ const raw = process.env.NAR_BASE_URL || process.env.OMNIROUTE_BASE_URL || "https://omniouter-vercel.vercel.app";
28
+ return raw.replace(/\/+$/, "");
29
+ }
30
+ function envKey() {
31
+ const key = process.env.NAR_API_KEY || process.env.OMNIROUTE_AI_API_KEY || "";
32
+ if (!key) {
33
+ throw new NarError("No master key. Set NAR_API_KEY (or OMNIROUTE_AI_API_KEY) to your NAR gateway key.", 401, "missing_api_key", { provider: null, model: null, attemptTrail: null }, null);
34
+ }
35
+ return key;
36
+ }
37
+ function apiBase(override) {
38
+ const base = (override || envBase()).replace(/\/+$/, "");
39
+ return base.endsWith("/api/v1") ? base : `${base}/api/v1`;
40
+ }
41
+ function routeFrom(headers) {
42
+ return {
43
+ provider: headers.get("x-omniroute-provider"),
44
+ model: headers.get("x-omniroute-model"),
45
+ attemptTrail: headers.get("x-omniroute-attempt-trail"),
46
+ };
47
+ }
48
+ function buildMessages(prompt, options) {
49
+ const messages = [];
50
+ if (options.system)
51
+ messages.push({ role: "system", content: options.system });
52
+ messages.push({ role: "user", content: prompt });
53
+ return messages;
54
+ }
55
+ function buildBody(prompt, options, stream) {
56
+ return {
57
+ model: options.model || DEFAULT_MODEL,
58
+ messages: buildMessages(prompt, options),
59
+ ...(options.temperature === undefined ? {} : { temperature: options.temperature }),
60
+ ...(options.maxTokens === undefined ? {} : { max_tokens: options.maxTokens }),
61
+ ...(options.tools ? { tools: options.tools } : {}),
62
+ ...(stream ? { stream: true } : {}),
63
+ ...(options.extra ?? {}),
64
+ };
65
+ }
66
+ async function readError(response, route) {
67
+ let body = null;
68
+ try {
69
+ body = await response.json();
70
+ }
71
+ catch {
72
+ body = null;
73
+ }
74
+ const record = (body ?? {});
75
+ const message = record.error?.message || `NAR request failed with status ${response.status}`;
76
+ const code = record.error?.code || record.error?.type || `http_${response.status}`;
77
+ return new NarError(message, response.status, code, route, body);
78
+ }
79
+ function mergeToolCalls(into, deltas) {
80
+ if (!Array.isArray(deltas))
81
+ return into;
82
+ for (const call of deltas) {
83
+ const index = typeof call.index === "number" ? call.index : 0;
84
+ const existing = (into[index] ??= { arguments: "" });
85
+ if (typeof call.id === "string" && call.id)
86
+ existing.id = call.id;
87
+ const fn = call.function;
88
+ if (fn && typeof fn.name === "string")
89
+ existing.name = fn.name;
90
+ if (fn && typeof fn.arguments === "string")
91
+ existing.arguments += fn.arguments;
92
+ }
93
+ return into;
94
+ }
95
+ function parseChunk(payload, route, toolCalls) {
96
+ try {
97
+ const parsed = JSON.parse(payload);
98
+ const choice = parsed?.choices?.[0] ?? {};
99
+ const delta = choice.delta ?? {};
100
+ mergeToolCalls(toolCalls, delta.tool_calls);
101
+ return {
102
+ delta: typeof delta.content === "string" ? delta.content : "",
103
+ toolCalls,
104
+ finishReason: typeof choice.finish_reason === "string" ? choice.finish_reason : null,
105
+ route,
106
+ };
107
+ }
108
+ catch {
109
+ return { delta: "", toolCalls, finishReason: null, route };
110
+ }
111
+ }
112
+ /**
113
+ * Stream a chat completion as text deltas.
114
+ *
115
+ * for await (const delta of nar.stream("explain pythons")) console.log(delta);
116
+ *
117
+ * Yields plain strings, so a caller that only wants the text never touches SSE.
118
+ * Use streamEvents when tool calls are needed.
119
+ */
120
+ export async function* stream(prompt, options = {}) {
121
+ for await (const event of streamEvents(prompt, options)) {
122
+ if (event.delta)
123
+ yield event.delta;
124
+ }
125
+ }
126
+ /** Same as stream(), but yields structured events including accumulated tool calls. */
127
+ export async function* streamEvents(prompt, options = {}) {
128
+ const response = await fetch(`${apiBase(options.baseUrl)}/chat/completions`, {
129
+ method: "POST",
130
+ headers: {
131
+ "content-type": "application/json",
132
+ authorization: `Bearer ${options.apiKey || envKey()}`,
133
+ },
134
+ body: JSON.stringify(buildBody(prompt, options, true)),
135
+ signal: options.signal,
136
+ });
137
+ const route = routeFrom(response.headers);
138
+ if (!response.ok)
139
+ throw await readError(response, route);
140
+ if (!response.body)
141
+ throw new NarError("NAR returned an empty stream", 502, "empty_stream", route, null);
142
+ // Network chunks do not align with SSE frame boundaries, so partial frames are held
143
+ // in `buffer` until the next chunk supplies the rest of the line.
144
+ const reader = response.body.getReader();
145
+ const decoder = new TextDecoder();
146
+ const toolCalls = {};
147
+ let buffer = "";
148
+ let done = false;
149
+ while (!done) {
150
+ const { value, done: finished } = await reader.read();
151
+ if (finished) {
152
+ buffer += decoder.decode();
153
+ done = true;
154
+ }
155
+ else {
156
+ buffer += decoder.decode(value, { stream: true });
157
+ }
158
+ // Frames are separated by a blank line; a trailing partial frame stays in buffer.
159
+ let boundary = buffer.indexOf("\n\n");
160
+ while (boundary !== -1) {
161
+ const frame = buffer.slice(0, boundary);
162
+ buffer = buffer.slice(boundary + 2);
163
+ const payload = frame
164
+ .split("\n")
165
+ .filter((line) => line.startsWith("data:"))
166
+ .map((line) => line.slice(5).trim())
167
+ .join("");
168
+ if (payload && payload !== "[DONE]")
169
+ yield parseChunk(payload, route, toolCalls);
170
+ boundary = buffer.indexOf("\n\n");
171
+ }
172
+ }
173
+ }
174
+ /** Collect a full streamed answer. Convenient when streaming is not needed downstream. */
175
+ export async function chat(prompt, options = {}) {
176
+ let text = "";
177
+ let route = { provider: null, model: null, attemptTrail: null };
178
+ for await (const event of streamEvents(prompt, options)) {
179
+ text += event.delta;
180
+ route = event.route;
181
+ }
182
+ return { text, route, usage: null, raw: null };
183
+ }
184
+ /** One non-streaming request, returning the parsed OpenAI-shaped envelope. */
185
+ export async function complete(prompt, options = {}) {
186
+ const response = await fetch(`${apiBase(options.baseUrl)}/chat/completions`, {
187
+ method: "POST",
188
+ headers: {
189
+ "content-type": "application/json",
190
+ authorization: `Bearer ${options.apiKey || envKey()}`,
191
+ },
192
+ body: JSON.stringify(buildBody(prompt, options, false)),
193
+ signal: options.signal,
194
+ });
195
+ const route = routeFrom(response.headers);
196
+ if (!response.ok)
197
+ throw await readError(response, route);
198
+ const body = (await response.json());
199
+ return {
200
+ text: body?.choices?.[0]?.message?.content ?? "",
201
+ route,
202
+ usage: body?.usage ?? null,
203
+ raw: body,
204
+ };
205
+ }
206
+ /** List live models. Pass this to a user or an agent to see what is actually available. */
207
+ export async function models(options = {}) {
208
+ const response = await fetch(`${apiBase(options.baseUrl)}/models`, {
209
+ headers: { authorization: `Bearer ${options.apiKey || envKey()}` },
210
+ signal: options.signal,
211
+ });
212
+ const route = routeFrom(response.headers);
213
+ if (!response.ok)
214
+ throw await readError(response, route);
215
+ const body = (await response.json());
216
+ return (body.data ?? []).map((entry) => String(entry.id)).filter(Boolean);
217
+ }
218
+ /** Router health, including per-provider auth state. Useful for uptime checks. */
219
+ export async function health(options = {}) {
220
+ const response = await fetch(`${envBase().replace(/\/+$/, "")}/api/v1/health`, { signal: options.signal });
221
+ return (await response.json());
222
+ }
223
+ export const nar = { stream, streamEvents, chat, complete, models, health };
224
+ export default nar;
225
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@nexuss0781/nar",
3
+ "version": "0.1.0",
4
+ "description": "Minimal client for Nexuss AI Router: auto-routed free LLMs, SSE streaming, one master key, no provider handling.",
5
+ "keywords": ["llm", "router", "gateway", "openai-compatible", "sse", "agents", "ai"],
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": ["dist", "README.md", "LICENSE"],
19
+ "sideEffects": false,
20
+ "engines": { "node": ">=18" },
21
+ "scripts": {
22
+ "build": "tsc -p tsconfig.build.json",
23
+ "typecheck": "tsc -p tsconfig.json --noEmit",
24
+ "prepublishOnly": "npm run build"
25
+ },
26
+ "publishConfig": { "access": "public" },
27
+ "repository": { "type": "git", "url": "git+https://github.com/nexuss0781/Nexuss-Router.git", "directory": "sdk" },
28
+ "devDependencies": { "typescript": "^5.0.0", "@types/node": "^22.0.0" }
29
+ }