@inferencesh/sdk 0.20.1 → 0.22.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/dist/agent/actions.js +1 -4
  3. package/dist/agent/api.d.ts +37 -6
  4. package/dist/agent/api.js +37 -3
  5. package/dist/agent/reducer.js +23 -7
  6. package/dist/agent/types.d.ts +30 -5
  7. package/dist/api/agents.d.ts +9 -3
  8. package/dist/api/agents.js +87 -65
  9. package/dist/api/api-keys.d.ts +2 -5
  10. package/dist/api/apps.d.ts +9 -2
  11. package/dist/api/apps.js +9 -1
  12. package/dist/api/chats.d.ts +13 -3
  13. package/dist/api/chats.js +13 -2
  14. package/dist/api/engines.d.ts +4 -2
  15. package/dist/api/engines.js +3 -2
  16. package/dist/api/flow-runs.d.ts +7 -2
  17. package/dist/api/flow-runs.js +7 -2
  18. package/dist/api/flows.d.ts +13 -2
  19. package/dist/api/flows.js +15 -1
  20. package/dist/api/knowledge.d.ts +7 -4
  21. package/dist/api/knowledge.js +6 -4
  22. package/dist/api/mcp-servers.d.ts +2 -1
  23. package/dist/api/projects.d.ts +5 -3
  24. package/dist/api/projects.js +3 -2
  25. package/dist/api/secrets.d.ts +2 -2
  26. package/dist/api/tasks.js +39 -60
  27. package/dist/api/teams.d.ts +4 -2
  28. package/dist/api/teams.js +3 -2
  29. package/dist/api/write-bodies.d.ts +110 -0
  30. package/dist/api/write-bodies.js +1 -0
  31. package/dist/http/client.d.ts +68 -7
  32. package/dist/http/client.js +138 -97
  33. package/dist/http/index.d.ts +2 -2
  34. package/dist/http/loopback.d.ts +8 -0
  35. package/dist/http/loopback.js +18 -0
  36. package/dist/http/stream-until-terminal.d.ts +55 -0
  37. package/dist/http/stream-until-terminal.js +88 -0
  38. package/dist/http/stream.d.ts +1 -0
  39. package/dist/http/stream.js +7 -3
  40. package/dist/http/streamable.d.ts +25 -5
  41. package/dist/http/streamable.js +28 -40
  42. package/dist/index.d.ts +4 -2
  43. package/dist/index.js +1 -0
  44. package/dist/proxy/endpoints.json +156 -0
  45. package/dist/proxy/express.d.ts +5 -6
  46. package/dist/proxy/express.js +18 -2
  47. package/dist/proxy/hono.d.ts +5 -3
  48. package/dist/proxy/hono.js +4 -3
  49. package/dist/proxy/index.d.ts +165 -6
  50. package/dist/proxy/index.js +409 -25
  51. package/dist/proxy/nextjs.d.ts +29 -17
  52. package/dist/proxy/nextjs.js +72 -62
  53. package/dist/proxy/remix.d.ts +5 -3
  54. package/dist/proxy/remix.js +4 -3
  55. package/dist/proxy/svelte.d.ts +5 -4
  56. package/dist/proxy/svelte.js +4 -4
  57. package/dist/types.d.ts +591 -271
  58. package/dist/types.js +117 -252
  59. package/dist/version.d.ts +1 -0
  60. package/dist/version.js +7 -0
  61. package/package.json +2 -1
@@ -3,12 +3,8 @@
3
3
  * This is the NDJSON alternative to SSE/EventSource that avoids
4
4
  * the browser's ~6 connection limit per domain.
5
5
  */
6
- /**
7
- * Stream NDJSON from an HTTP endpoint using fetch + ReadableStream.
8
- * Yields parsed JSON objects, automatically filtering heartbeats.
9
- */
10
- export async function* streamable(url, options = {}) {
11
- const { headers = {}, body, method = body ? 'POST' : 'GET', signal, skipHeartbeats = true, credentials, } = options;
6
+ /** Send the stream's request and hand back the body to read. */
7
+ async function openStream(source, { headers = {}, body, method = body ? 'POST' : 'GET', signal, credentials }) {
12
8
  const requestHeaders = {
13
9
  Accept: 'application/x-ndjson',
14
10
  ...headers,
@@ -16,13 +12,13 @@ export async function* streamable(url, options = {}) {
16
12
  if (body) {
17
13
  requestHeaders['Content-Type'] = 'application/json';
18
14
  }
19
- const res = await fetch(url, {
15
+ const init = {
20
16
  method,
21
17
  headers: requestHeaders,
22
18
  body: body ? JSON.stringify(body) : undefined,
23
19
  signal,
24
- credentials,
25
- });
20
+ };
21
+ const res = typeof source === 'string' ? await fetch(source, { ...init, credentials }) : await source(init);
26
22
  if (!res.ok) {
27
23
  const text = await res.text();
28
24
  throw new Error(`HTTP ${res.status}: ${text}`);
@@ -30,7 +26,16 @@ export async function* streamable(url, options = {}) {
30
26
  if (!res.body) {
31
27
  throw new Error('No response body');
32
28
  }
33
- const reader = res.body.getReader();
29
+ return res.body;
30
+ }
31
+ /**
32
+ * Stream NDJSON from an HTTP endpoint using fetch + ReadableStream.
33
+ * Yields parsed JSON objects, automatically filtering heartbeats.
34
+ * `source` is the URL to fetch, or a StreamRequest that sends the request.
35
+ */
36
+ export async function* streamable(source, options = {}) {
37
+ const { skipHeartbeats = true } = options;
38
+ const reader = (await openStream(source, options)).getReader();
34
39
  const decoder = new TextDecoder();
35
40
  let buffer = '';
36
41
  try {
@@ -109,12 +114,16 @@ export class StreamableManager {
109
114
  this.abortController = new AbortController();
110
115
  try {
111
116
  this.options.onStart?.();
112
- for await (const message of streamableRaw(this.options.url, {
113
- headers: this.options.headers,
114
- body: this.options.body,
115
- signal: this.abortController.signal,
116
- credentials: this.options.credentials,
117
- })) {
117
+ const { options } = this;
118
+ const stream = 'request' in options
119
+ ? streamableRaw(options.request, { body: options.body, signal: this.abortController.signal })
120
+ : streamableRaw(options.url, {
121
+ headers: options.headers,
122
+ body: options.body,
123
+ signal: this.abortController.signal,
124
+ credentials: options.credentials,
125
+ });
126
+ for await (const message of stream) {
118
127
  if (!this.isRunning)
119
128
  break;
120
129
  const wrapper = message;
@@ -179,30 +188,9 @@ export class StreamableManager {
179
188
  * Raw streamable that yields the full message including wrapper.
180
189
  * Use this when you need access to event type or fields.
181
190
  */
182
- export async function* streamableRaw(url, options = {}) {
183
- const { headers = {}, body, method = body ? 'POST' : 'GET', signal, skipHeartbeats = true, credentials, } = options;
184
- const requestHeaders = {
185
- Accept: 'application/x-ndjson',
186
- ...headers,
187
- };
188
- if (body) {
189
- requestHeaders['Content-Type'] = 'application/json';
190
- }
191
- const res = await fetch(url, {
192
- method,
193
- headers: requestHeaders,
194
- body: body ? JSON.stringify(body) : undefined,
195
- signal,
196
- credentials,
197
- });
198
- if (!res.ok) {
199
- const text = await res.text();
200
- throw new Error(`HTTP ${res.status}: ${text}`);
201
- }
202
- if (!res.body) {
203
- throw new Error('No response body');
204
- }
205
- const reader = res.body.getReader();
191
+ export async function* streamableRaw(source, options = {}) {
192
+ const { skipHeartbeats = true } = options;
193
+ const reader = (await openStream(source, options)).getReader();
206
194
  const decoder = new TextDecoder();
207
195
  let buffer = '';
208
196
  try {
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- export { HttpClient, type HttpClientConfig, type ErrorHandler, type MessageHandler, createHttpClient } from './http/client';
1
+ export { HttpClient, type HttpClientConfig, type ErrorHandler, type FailedRequest, type HttpFetchInit, type MessageHandler, createHttpClient } from './http/client';
2
2
  export type { Response } from './http/response';
3
3
  import type { Response } from './http/response';
4
4
  export { StreamManager, type StreamManagerOptions, type PartialDataWrapper } from './http/stream';
5
- export { StreamableManager, type StreamableManagerOptions, type StreamableMessage, streamable, streamableRaw } from './http/streamable';
5
+ export { StreamableManager, type StreamableManagerOptions, type StreamableSource, type StreamRequest, type StreamRequestInit, type StreamableMessage, streamable, streamableRaw } from './http/streamable';
6
6
  export { PollManager, type PollManagerOptions } from './http/poll';
7
+ export { isLoopbackHost, isHttpsOrLoopback } from './http/loopback';
7
8
  export { InferenceError, RequirementsNotMetException, SessionError, SessionNotFoundError, SessionExpiredError, SessionEndedError, WorkerLostError, isRequirementsNotMetException, isInferenceError, isSessionError, } from './http/errors';
8
9
  export { TasksAPI, type RunOptions, type WatchOptions, type TaskWatch } from './api/tasks';
9
10
  export { SocketsAPI, type SocketTarget, type OpenSocketOptions } from './api/sockets';
@@ -17,6 +18,7 @@ export { FlowRunsAPI } from './api/flow-runs';
17
18
  export { EnginesAPI } from './api/engines';
18
19
  export { KnowledgeAPI, SkillsAPI } from './api/knowledge';
19
20
  export { ArtifactsAPI } from './api/artifacts';
21
+ export type { AgentUpdateBody, AppPatchBody, AppUpdateBody, ChatUpdateBody, EngineUpdateBody, FlowUpdateBody, FlowPatchBody, FlowViewportBody, ProjectWriteBody, KnowledgeUpdateBody, TeamUpdateBody, MCPServerUpdateBody, } from './api/write-bodies';
20
22
  export { TeamsAPI } from './api/teams';
21
23
  export { SecretsAPI } from './api/secrets';
22
24
  export { ApiKeysAPI } from './api/api-keys';
package/dist/index.js CHANGED
@@ -3,6 +3,7 @@ export { HttpClient, createHttpClient } from './http/client';
3
3
  export { StreamManager } from './http/stream';
4
4
  export { StreamableManager, streamable, streamableRaw } from './http/streamable';
5
5
  export { PollManager } from './http/poll';
6
+ export { isLoopbackHost, isHttpsOrLoopback } from './http/loopback';
6
7
  export { InferenceError, RequirementsNotMetException, SessionError, SessionNotFoundError, SessionExpiredError, SessionEndedError, WorkerLostError,
7
8
  // Type guards (use these instead of instanceof for reliability)
8
9
  isRequirementsNotMetException, isInferenceError, isSessionError, } from './http/errors';
@@ -0,0 +1,156 @@
1
+ {
2
+ "$comment": "Generated by `make proxy-endpoints` from the route table (internal/api/proxy_endpoints_test.go). Do not edit.",
3
+ "preset": "run-only",
4
+ "endpoints": [
5
+ {
6
+ "method": "POST",
7
+ "path": "/apps/run",
8
+ "purpose": "run an app",
9
+ "scopes": [
10
+ "apps:execute",
11
+ "apps:read",
12
+ "conversations:read"
13
+ ]
14
+ },
15
+ {
16
+ "method": "POST",
17
+ "path": "/files",
18
+ "purpose": "upload a file input of a run or message",
19
+ "scopes": [
20
+ "files:write"
21
+ ]
22
+ },
23
+ {
24
+ "method": "GET",
25
+ "path": "/tasks/{id}",
26
+ "purpose": "read a task",
27
+ "scopes": [
28
+ "apps:read"
29
+ ]
30
+ },
31
+ {
32
+ "method": "GET",
33
+ "path": "/tasks/{id}/status",
34
+ "purpose": "poll a task",
35
+ "scopes": [
36
+ "apps:read"
37
+ ]
38
+ },
39
+ {
40
+ "method": "GET",
41
+ "path": "/tasks/{id}/stream",
42
+ "purpose": "stream a task",
43
+ "scopes": [
44
+ "apps:read",
45
+ "conversations:read",
46
+ "flows:read"
47
+ ]
48
+ },
49
+ {
50
+ "method": "POST",
51
+ "path": "/tasks/{id}/cancel",
52
+ "purpose": "cancel a task",
53
+ "scopes": [
54
+ "apps:execute"
55
+ ]
56
+ },
57
+ {
58
+ "method": "POST",
59
+ "path": "/agents/run",
60
+ "purpose": "send a message to an agent",
61
+ "scopes": [
62
+ "agents:execute",
63
+ "conversations:read"
64
+ ]
65
+ },
66
+ {
67
+ "method": "POST",
68
+ "path": "/chats",
69
+ "purpose": "start a chat with an agent",
70
+ "scopes": [
71
+ "conversations:write"
72
+ ]
73
+ },
74
+ {
75
+ "method": "POST",
76
+ "path": "/chats/{id}/messages",
77
+ "purpose": "send a message in a chat",
78
+ "scopes": [
79
+ "agents:execute"
80
+ ]
81
+ },
82
+ {
83
+ "method": "POST",
84
+ "path": "/chats/{id}/agent",
85
+ "purpose": "hand a chat to another agent",
86
+ "scopes": [
87
+ "conversations:write"
88
+ ]
89
+ },
90
+ {
91
+ "method": "GET",
92
+ "path": "/agents/{namespace}/{name}",
93
+ "purpose": "read an agent's info",
94
+ "scopes": [
95
+ "agents:read"
96
+ ]
97
+ },
98
+ {
99
+ "method": "GET",
100
+ "path": "/chats/{id}",
101
+ "purpose": "read a chat",
102
+ "scopes": [
103
+ "conversations:read"
104
+ ]
105
+ },
106
+ {
107
+ "method": "GET",
108
+ "path": "/chats/{id}/status",
109
+ "purpose": "poll a chat",
110
+ "scopes": [
111
+ "conversations:read"
112
+ ]
113
+ },
114
+ {
115
+ "method": "GET",
116
+ "path": "/chats/{id}/messages",
117
+ "purpose": "read a chat's messages",
118
+ "scopes": [
119
+ "conversations:read"
120
+ ]
121
+ },
122
+ {
123
+ "method": "GET",
124
+ "path": "/chats/{id}/stream",
125
+ "purpose": "stream a chat",
126
+ "scopes": [
127
+ "conversations:read"
128
+ ]
129
+ },
130
+ {
131
+ "method": "POST",
132
+ "path": "/chats/{id}/stop",
133
+ "purpose": "stop a chat",
134
+ "scopes": [
135
+ "conversations:write"
136
+ ]
137
+ },
138
+ {
139
+ "method": "POST",
140
+ "path": "/chats/messages/{messageId}/cancel",
141
+ "purpose": "cancel a message",
142
+ "scopes": [
143
+ "conversations:read",
144
+ "conversations:write"
145
+ ]
146
+ },
147
+ {
148
+ "method": "POST",
149
+ "path": "/tools/{toolId}",
150
+ "purpose": "answer a client tool call",
151
+ "scopes": [
152
+ "conversations:write"
153
+ ]
154
+ }
155
+ ]
156
+ }
@@ -8,18 +8,17 @@
8
8
  *
9
9
  * const app = express();
10
10
  * app.use(express.json());
11
- * app.all(PROXY_ROUTE, createHandler());
11
+ * app.all(PROXY_ROUTE, createHandler({ allowedEndpoints: ["my-team/*"] }));
12
12
  * ```
13
13
  */
14
- import type { RequestHandler } from "express";
14
+ import type { Request, RequestHandler } from "express";
15
+ import { type ProxyOptions } from "./index";
15
16
  /** Default proxy route path */
16
17
  export declare const PROXY_ROUTE = "/api/inference/proxy";
17
18
  /** @deprecated Use PROXY_ROUTE */
18
19
  export declare const route = "/api/inference/proxy";
19
- export interface ExpressProxyOptions {
20
- /** Custom API key (overrides INFERENCE_API_KEY env var) */
21
- apiKey?: string;
22
- }
20
+ /** Proxy options; `isAuthenticated` receives the Express request. */
21
+ export type ExpressProxyOptions = ProxyOptions<Request>;
23
22
  /**
24
23
  * Creates an Express middleware handler for the Inference.sh proxy.
25
24
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * const app = express();
10
10
  * app.use(express.json());
11
- * app.all(PROXY_ROUTE, createHandler());
11
+ * app.all(PROXY_ROUTE, createHandler({ allowedEndpoints: ["my-team/*"] }));
12
12
  * ```
13
13
  */
14
14
  import { PROXY_PATH, processProxyRequest, } from "./index";
@@ -23,8 +23,19 @@ export const route = PROXY_ROUTE;
23
23
  */
24
24
  export function createHandler(options) {
25
25
  return async (req, res, _next) => {
26
+ // The client hung up before the response finished: stop the upstream
27
+ // request too. Chat and task streams never end on their own, so
28
+ // without this the proxy keeps reading them for nobody and the open
29
+ // upstream connection keeps the server process alive.
30
+ const upstream = new AbortController();
31
+ res.on("close", () => {
32
+ if (!res.writableFinished)
33
+ upstream.abort();
34
+ });
26
35
  return processProxyRequest({
27
36
  framework: "express",
37
+ signal: upstream.signal,
38
+ request: req,
28
39
  method: req.method,
29
40
  body: async () => JSON.stringify(req.body),
30
41
  headers: () => req.headers,
@@ -61,7 +72,12 @@ export function createHandler(options) {
61
72
  // Handle text responses
62
73
  return res.send(await response.text());
63
74
  },
64
- }, options);
75
+ }, options).catch((error) => {
76
+ // Aborted because the client left; there is nobody to answer.
77
+ if (upstream.signal.aborted)
78
+ return res;
79
+ throw error;
80
+ });
65
81
  };
66
82
  }
67
83
  /** @deprecated Use createHandler() */
@@ -9,11 +9,13 @@
9
9
  * import { createHandler } from "@inferencesh/sdk/proxy/hono";
10
10
  *
11
11
  * const app = new Hono();
12
- * app.all("/api/inference/proxy", createHandler());
12
+ * app.all("/api/inference/proxy", createHandler({ allowedEndpoints: ["my-team/*"] }));
13
13
  * ```
14
14
  */
15
15
  import type { Context } from "hono";
16
- export interface HonoProxyOptions {
16
+ import { type ProxyOptions } from "./index";
17
+ /** Proxy options; `isAuthenticated` receives the Context. */
18
+ export interface HonoProxyOptions extends ProxyOptions<Context> {
17
19
  /** Custom function to resolve the API key */
18
20
  resolveApiKey?: () => Promise<string | undefined>;
19
21
  }
@@ -21,7 +23,7 @@ type HonoHandler = (context: Context) => Promise<Response>;
21
23
  /**
22
24
  * Creates a Hono route handler for the Inference.sh proxy.
23
25
  */
24
- export declare function createHandler({ resolveApiKey, }?: HonoProxyOptions): HonoHandler;
26
+ export declare function createHandler({ resolveApiKey, ...options }?: HonoProxyOptions): HonoHandler;
25
27
  /** @deprecated Use createHandler */
26
28
  export declare const createRouteHandler: typeof createHandler;
27
29
  export {};
@@ -9,19 +9,20 @@
9
9
  * import { createHandler } from "@inferencesh/sdk/proxy/hono";
10
10
  *
11
11
  * const app = new Hono();
12
- * app.all("/api/inference/proxy", createHandler());
12
+ * app.all("/api/inference/proxy", createHandler({ allowedEndpoints: ["my-team/*"] }));
13
13
  * ```
14
14
  */
15
15
  import { processProxyRequest, getEnvApiKey, passthrough, headersToRecord, } from "./index";
16
16
  /**
17
17
  * Creates a Hono route handler for the Inference.sh proxy.
18
18
  */
19
- export function createHandler({ resolveApiKey = getEnvApiKey, } = {}) {
19
+ export function createHandler({ resolveApiKey = getEnvApiKey, ...options } = {}) {
20
20
  return async (context) => {
21
21
  const responseHeaders = new Headers();
22
22
  const url = new URL(context.req.url);
23
23
  return processProxyRequest({
24
24
  framework: "hono",
25
+ request: context,
25
26
  method: context.req.method,
26
27
  body: async () => {
27
28
  try {
@@ -44,7 +45,7 @@ export function createHandler({ resolveApiKey = getEnvApiKey, } = {}) {
44
45
  }),
45
46
  respond: passthrough,
46
47
  apiKey: resolveApiKey,
47
- });
48
+ }, options);
48
49
  };
49
50
  }
50
51
  /** @deprecated Use createHandler */
@@ -4,11 +4,18 @@
4
4
  * Protects API keys by proxying requests from frontend apps through your server.
5
5
  * Supports Next.js (App & Page Router), Express, Hono, Remix, and SvelteKit.
6
6
  *
7
+ * The proxy forwards only what a frontend needs to run things (app runs,
8
+ * agent chats, and reading or streaming their task or chat; see
9
+ * PROXY_ENDPOINTS) and refuses every other API call with 403.
10
+ *
7
11
  * @example Next.js App Router
8
12
  * ```typescript
9
13
  * // app/api/inference/proxy/route.ts
10
14
  * import { createHandler } from "@inferencesh/sdk/proxy/nextjs";
11
- * export const { GET, POST, PUT } = createHandler();
15
+ * export const { GET, POST } = createHandler({
16
+ * allowedEndpoints: ["my-team/support-agent"],
17
+ * isAuthenticated: async (req) => Boolean(req.cookies.get("session")),
18
+ * });
12
19
  * ```
13
20
  */
14
21
  /** Header name for target URL */
@@ -17,15 +24,24 @@ export declare const INF_TARGET_HEADER = "x-inf-target-url";
17
24
  export declare const INF_TARGET_PARAM = "__inf_target";
18
25
  /** Default proxy route path */
19
26
  export declare const PROXY_PATH = "/api/inference/proxy";
27
+ /** The API the proxy forwards to unless `apiUrl` or `apiBaseUrl` says otherwise. */
28
+ export declare const DEFAULT_API_URL = "https://api.inference.sh";
29
+ /**
30
+ * Default `maxUploadBytes`: 100 MiB, well under the API's own per-file limit
31
+ * (storage.max_upload_bytes, 5 GiB by default).
32
+ */
33
+ export declare const DEFAULT_MAX_UPLOAD_BYTES: number;
20
34
  /** Header value from various HTTP libraries */
21
35
  export type HttpHeaderValue = string | string[] | undefined | null;
22
36
  /**
23
37
  * Framework adapter interface for proxy handlers.
24
38
  * Implement this to support a new framework.
25
39
  */
26
- export interface ProxyAdapter<T> {
40
+ export interface ProxyAdapter<T, R = unknown> {
27
41
  /** Framework name for user-agent */
28
42
  framework: string;
43
+ /** The framework's request, handed to `isAuthenticated` */
44
+ request?: R;
29
45
  /** HTTP method of the request */
30
46
  method: string;
31
47
  /** Get request body as string */
@@ -44,17 +60,159 @@ export interface ProxyAdapter<T> {
44
60
  respond: (response: Response) => Promise<T>;
45
61
  /** Custom API key resolver (optional) */
46
62
  apiKey?: () => Promise<string | undefined>;
63
+ /**
64
+ * Aborts the upstream request (optional). Adapters that copy the
65
+ * upstream body themselves abort it when their client disconnects, so a
66
+ * stream nobody reads any more (GET /chats/{id}/stream never ends on its
67
+ * own) does not stay open.
68
+ */
69
+ signal?: AbortSignal;
47
70
  }
48
71
  /**
49
72
  * Proxy handler options.
50
73
  */
51
- export interface ProxyOptions {
74
+ export interface ProxyOptions<R = unknown> {
52
75
  /** Custom API key (defaults to INFERENCE_API_KEY env var) */
53
76
  apiKey?: string;
54
- /** Override API base URL (defaults to INFERENCE_API_BASE_URL env var) */
77
+ /**
78
+ * The API the frontend's SDK client calls (its `baseUrl`), default
79
+ * https://api.inference.sh. The proxy attaches the key only to requests
80
+ * for this origin (or an `allowedDomains` host); a target on any other
81
+ * host, other inference.sh hosts included, is refused with 412.
82
+ */
83
+ apiUrl?: string;
84
+ /**
85
+ * Rewrite every target to this API base (defaults to the
86
+ * INFERENCE_API_BASE_URL env var). When set, it is also the only origin
87
+ * the proxy forwards to.
88
+ */
55
89
  apiBaseUrl?: string;
56
- /** Allow requests to additional domains (besides *.inference.sh) */
90
+ /**
91
+ * Extra target hosts, besides the API's. Each pattern must match the
92
+ * whole host (`host:port` when the target names a port).
93
+ */
57
94
  allowedDomains?: RegExp[];
95
+ /**
96
+ * Largest file, in bytes, a visitor may upload through the proxy
97
+ * (default 100 MiB). `POST /files` declares each file's size and the API
98
+ * binds the upload URL to it; the bytes then go straight to storage and
99
+ * count against the API key owner's storage quota. One file per request.
100
+ */
101
+ maxUploadBytes?: number;
102
+ /**
103
+ * Apps and agents visitors may run, as `namespace/name` globs: "ana/helper",
104
+ * or "ana/*" for all of a namespace. Unset or empty allows every app and
105
+ * agent the API key can run.
106
+ *
107
+ * Checked where a request names the app or agent: a new app run, a new
108
+ * chat or agent message, a chat handed to another agent, an agent's info.
109
+ * Requests about an existing task or chat name it by id, and the proxy
110
+ * cannot tell which app or agent that is: whoever holds a task or chat id
111
+ * can read and stream it, cancel or stop it, and send messages in the
112
+ * chat (which run the chat's agent). All visitors share one key, so the
113
+ * id is the only boundary between them. When set, ad-hoc agent configs
114
+ * (`agent_config`) and runs by `app_id`/`version_id` are refused.
115
+ */
116
+ allowedEndpoints?: string[];
117
+ /**
118
+ * Match `allowedEndpoints` entries against the whole ref, version and
119
+ * function included, instead of as `namespace/name` globs: the entry
120
+ * "ana/helper@v2" lets only that exact ref through, and "ana/helper"
121
+ * only the unversioned ref. Entries are refs, without `*`.
122
+ */
123
+ exactRefs?: boolean;
124
+ /**
125
+ * The calls to forward, a subset of PROXY_ENDPOINTS (default: all of
126
+ * them). Entries must come from PROXY_ENDPOINTS or `pickProxyEndpoint`,
127
+ * which can narrow one further but never widen it; anything else
128
+ * refuses every request with 500.
129
+ */
130
+ endpoints?: readonly ProxyEndpoint[];
131
+ /**
132
+ * Decides whether a request may use the API key, e.g. by checking your
133
+ * session. Requests it answers false for are refused with 401. Receives
134
+ * the framework's request (NextRequest, Express Request, Hono Context, ...).
135
+ */
136
+ isAuthenticated?: (request: R) => boolean | Promise<boolean>;
137
+ /**
138
+ * Without `isAuthenticated`, whether anyone may use the proxy (default
139
+ * true, for compatibility). Set it to false to require `isAuthenticated`:
140
+ * with neither, every request is refused.
141
+ */
142
+ allowUnauthorizedRequests?: boolean;
143
+ }
144
+ /** Where a request names the app or agent it runs. */
145
+ type RefSource =
146
+ /** Body field naming the app or agent, and fields that would name one another way. */
147
+ {
148
+ body: string;
149
+ refuse: string[];
150
+ }
151
+ /** `/agents/{namespace}/{name}` in the path. */
152
+ | {
153
+ path: true;
154
+ };
155
+ /** One call of the API's proxy manifest (src/proxy/endpoints.json). */
156
+ export interface ProxyManifestEndpoint {
157
+ method: string;
158
+ /** API path template: `{namespace}`/`{name}` are an agent ref, every other `{…}` an id. */
159
+ path: string;
160
+ purpose: string;
161
+ /** Scopes the call takes: what the site's API key must hold (the manifest's preset grants them all). */
162
+ scopes: string[];
163
+ }
164
+ export interface ProxyEndpoint {
165
+ method: "GET" | "POST";
166
+ /** The manifest's route, "METHOD /path/{id}" (what pickProxyEndpoint takes). */
167
+ route: string;
168
+ /** API path, matched whole. */
169
+ path: RegExp;
170
+ /** What the endpoint is for (used in docs and tests). */
171
+ purpose: string;
172
+ /** Scopes the call takes, from the manifest. */
173
+ scopes: readonly string[];
174
+ /** Set when the request names an app or agent: checked against allowedEndpoints. */
175
+ ref?: RefSource;
176
+ /** Set when only these body fields may be sent. */
177
+ onlyFields?: string[];
178
+ /** Further checks of the body (a JSON object), given the proxy's limits. */
179
+ validate?: (body: Record<string, unknown>, limits: ProxyLimits) => PolicyRefusal | undefined;
180
+ }
181
+ /** Limits from the proxy options, handed to endpoint checks. */
182
+ export interface ProxyLimits {
183
+ maxUploadBytes: number;
184
+ }
185
+ /** What the proxy checks of a call's request, beyond the manifest. */
186
+ type EndpointRules = Pick<ProxyEndpoint, "ref" | "onlyFields" | "validate">;
187
+ /**
188
+ * The proxy's own checks, by manifest route. A route here that the manifest
189
+ * does not list is never forwarded (a test keeps the two in step).
190
+ */
191
+ export declare const PROXY_ENDPOINT_RULES: Readonly<Record<string, EndpointRules>>;
192
+ /** The API's proxy manifest as shipped in this package. */
193
+ export declare const PROXY_MANIFEST: {
194
+ readonly preset: string;
195
+ readonly endpoints: readonly ProxyManifestEndpoint[];
196
+ };
197
+ /**
198
+ * The calls the proxy forwards: exactly the manifest's, with the proxy's
199
+ * request checks. Everything else is refused with 403.
200
+ */
201
+ export declare const PROXY_ENDPOINTS: readonly ProxyEndpoint[];
202
+ /** The PROXY_ENDPOINTS entry for a request, or undefined when the proxy does not forward it. */
203
+ export declare function matchProxyEndpoint(method: string, pathname: string, endpoints?: readonly ProxyEndpoint[]): ProxyEndpoint | undefined;
204
+ /**
205
+ * The PROXY_ENDPOINTS entry for `route` ("METHOD /path", with `{id}`,
206
+ * `{namespace}` and `{name}` placeholders, e.g. "POST /chats/{id}/messages"),
207
+ * optionally narrowed: `onlyFields` must be a subset of the entry's own,
208
+ * and `validate` runs after the entry's own check. For the `endpoints`
209
+ * option. Throws when the proxy does not forward the route.
210
+ */
211
+ export declare function pickProxyEndpoint(route: string, narrow?: Pick<ProxyEndpoint, "onlyFields" | "validate">): ProxyEndpoint;
212
+ /** A refusal: the HTTP status and the message sent back. */
213
+ export interface PolicyRefusal {
214
+ status: number;
215
+ error: string;
58
216
  }
59
217
  /**
60
218
  * Process a proxied request to the Inference.sh API.
@@ -66,7 +224,7 @@ export interface ProxyOptions {
66
224
  * @param options - Optional configuration
67
225
  * @returns Promise resolving to the framework's response type
68
226
  */
69
- export declare function processProxyRequest<T>(adapter: ProxyAdapter<T>, options?: ProxyOptions): Promise<T>;
227
+ export declare function processProxyRequest<T, R = unknown>(adapter: ProxyAdapter<T, R>, options?: ProxyOptions<R>): Promise<T>;
70
228
  /**
71
229
  * Convert Headers object to plain record.
72
230
  */
@@ -95,3 +253,4 @@ export declare const fromHeaders: typeof headersToRecord;
95
253
  export declare const responsePassthrough: (res: Response) => Promise<Response>;
96
254
  /** @deprecated Use getEnvApiKey */
97
255
  export declare const resolveApiKeyFromEnv: () => Promise<string | undefined>;
256
+ export {};