@agentproto/driver-http 0.1.1
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 +21 -0
- package/dist/index.d.ts +95 -0
- package/dist/index.mjs +233 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +58 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jeremy André and agentproto contributors
|
|
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/dist/index.d.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { DriverDefinition, DriverHandle } from '@agentproto/driver';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @agentproto/http — AIP-31 HTTP provider specialisation.
|
|
5
|
+
*
|
|
6
|
+
* Sugar over `defineDriver({ kind: "http", ... })` plus the
|
|
7
|
+
* HTTP-specific dispatch logic: body / query templating, header
|
|
8
|
+
* substitution, JSONPath-lite response extraction, and SSE streaming.
|
|
9
|
+
*
|
|
10
|
+
* Spec: https://agentproto.sh/docs/aip-31
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
interface HttpDriverDefinition extends Omit<DriverDefinition, "kind" | "execute"> {
|
|
14
|
+
kind?: "http";
|
|
15
|
+
/** API base URL. All implements[].metadata.http.endpoint paths relative to this. */
|
|
16
|
+
baseUrl: string;
|
|
17
|
+
/** Headers attached to every request. Templating allowed via ${secrets.X} / ${context.X}. */
|
|
18
|
+
defaultHeaders?: Record<string, string>;
|
|
19
|
+
/** Default HTTP method when per-tool entry omits it. Defaults to POST. */
|
|
20
|
+
defaultMethod?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
21
|
+
/** Default streaming config; per-tool override via metadata.http.streaming. */
|
|
22
|
+
streaming?: {
|
|
23
|
+
transport: "sse" | "ndjson" | "chunked";
|
|
24
|
+
eventField?: string;
|
|
25
|
+
terminator?: string;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Optional behavioural adapter. When omitted, the runtime synthesises
|
|
29
|
+
* an execute body per implements[] entry from the per-tool metadata.http
|
|
30
|
+
* dispatch hints (endpoint, method, body_template, response_extract).
|
|
31
|
+
*/
|
|
32
|
+
buildRequest?: (args: BuildRequestArgs) => HttpRequest | Promise<HttpRequest>;
|
|
33
|
+
parseResponse?: (args: ParseResponseArgs) => HttpParseResult;
|
|
34
|
+
}
|
|
35
|
+
interface BuildRequestArgs {
|
|
36
|
+
toolId: string;
|
|
37
|
+
input: Record<string, unknown>;
|
|
38
|
+
context: Record<string, unknown>;
|
|
39
|
+
driverCtx: {
|
|
40
|
+
secrets: Record<string, string>;
|
|
41
|
+
baseUrl: string;
|
|
42
|
+
[k: string]: unknown;
|
|
43
|
+
};
|
|
44
|
+
signal: AbortSignal;
|
|
45
|
+
}
|
|
46
|
+
interface HttpRequest {
|
|
47
|
+
url: string;
|
|
48
|
+
method: string;
|
|
49
|
+
headers: Record<string, string>;
|
|
50
|
+
body?: unknown;
|
|
51
|
+
query?: Record<string, string>;
|
|
52
|
+
}
|
|
53
|
+
interface ParseResponseArgs {
|
|
54
|
+
toolId: string;
|
|
55
|
+
status: number;
|
|
56
|
+
body: unknown;
|
|
57
|
+
headers: Record<string, string>;
|
|
58
|
+
}
|
|
59
|
+
type HttpParseResult = {
|
|
60
|
+
ok: true;
|
|
61
|
+
value: unknown;
|
|
62
|
+
} | {
|
|
63
|
+
ok: false;
|
|
64
|
+
error: {
|
|
65
|
+
code: string;
|
|
66
|
+
message: string;
|
|
67
|
+
retryable?: boolean;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Define an HTTP provider — sugar over defineDriver with kind: http
|
|
72
|
+
* and an auto-synthesised execute map keyed by tool id when explicit
|
|
73
|
+
* `execute` isn't provided.
|
|
74
|
+
*/
|
|
75
|
+
declare function defineHttpDriver(definition: HttpDriverDefinition): DriverHandle;
|
|
76
|
+
declare class HttpDriverError extends Error {
|
|
77
|
+
code: string;
|
|
78
|
+
retryable?: boolean;
|
|
79
|
+
constructor(code: string, message: string, retryable?: boolean);
|
|
80
|
+
}
|
|
81
|
+
/** Expand `${input.X}` / `${secrets.X}` / `${context.X}` substitutions in a value tree. */
|
|
82
|
+
declare function expandTemplate(value: unknown, vars: {
|
|
83
|
+
input: Record<string, unknown>;
|
|
84
|
+
secrets: Record<string, string>;
|
|
85
|
+
context: Record<string, unknown>;
|
|
86
|
+
}): unknown;
|
|
87
|
+
declare function expandHeaders(headers: Record<string, string>, vars: {
|
|
88
|
+
input: Record<string, unknown>;
|
|
89
|
+
secrets: Record<string, string>;
|
|
90
|
+
context: Record<string, unknown>;
|
|
91
|
+
}): Record<string, string>;
|
|
92
|
+
/** Extract a value from a response body using a JSONPath-lite expression. */
|
|
93
|
+
declare function extractResponse(body: unknown, path: string): unknown;
|
|
94
|
+
|
|
95
|
+
export { type BuildRequestArgs, type HttpDriverDefinition, HttpDriverError, type HttpParseResult, type HttpRequest, type ParseResponseArgs, defineHttpDriver, expandHeaders, expandTemplate, extractResponse };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import { defineDriver } from '@agentproto/driver';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @agentproto/http v0.1.0-alpha
|
|
5
|
+
* AIP-31 HTTP provider specialisation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function defineHttpDriver(definition) {
|
|
9
|
+
const { baseUrl, defaultHeaders = {}, defaultMethod = "POST" } = definition;
|
|
10
|
+
const execute = {};
|
|
11
|
+
for (const entry of definition.implements) {
|
|
12
|
+
const toolId = normalizeToolId(entry.tool);
|
|
13
|
+
execute[toolId] = createExecuteFn({
|
|
14
|
+
toolId,
|
|
15
|
+
entry,
|
|
16
|
+
baseUrl,
|
|
17
|
+
defaultHeaders,
|
|
18
|
+
defaultMethod,
|
|
19
|
+
buildRequest: definition.buildRequest,
|
|
20
|
+
parseResponse: definition.parseResponse
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
return defineDriver({
|
|
24
|
+
...definition,
|
|
25
|
+
kind: "http",
|
|
26
|
+
execute,
|
|
27
|
+
metadata: {
|
|
28
|
+
...definition.metadata ?? {},
|
|
29
|
+
http: { baseUrl, defaultHeaders, defaultMethod, streaming: definition.streaming }
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function createExecuteFn(args) {
|
|
34
|
+
const meta = args.entry.metadata ?? {};
|
|
35
|
+
const httpMeta = meta.http ?? {};
|
|
36
|
+
return async ({ input, context, driverCtx, signal }) => {
|
|
37
|
+
const inputObj = input ?? {};
|
|
38
|
+
const ctxObj = context;
|
|
39
|
+
const provCtx = driverCtx;
|
|
40
|
+
let request;
|
|
41
|
+
if (args.buildRequest) {
|
|
42
|
+
request = await args.buildRequest({
|
|
43
|
+
toolId: args.toolId,
|
|
44
|
+
input: inputObj,
|
|
45
|
+
context: ctxObj,
|
|
46
|
+
driverCtx: { ...provCtx, baseUrl: args.baseUrl },
|
|
47
|
+
signal
|
|
48
|
+
});
|
|
49
|
+
} else {
|
|
50
|
+
const endpoint = httpMeta.endpoint ?? "/";
|
|
51
|
+
const method = httpMeta.method ?? args.defaultMethod;
|
|
52
|
+
const url = new URL(endpoint, args.baseUrl).toString();
|
|
53
|
+
const headers = expandHeaders(
|
|
54
|
+
{ ...args.defaultHeaders, ...httpMeta.headers ?? {} },
|
|
55
|
+
{ input: inputObj, secrets: provCtx.secrets ?? {}, context: ctxObj }
|
|
56
|
+
);
|
|
57
|
+
const body = httpMeta.bodyTemplate ? expandTemplate(httpMeta.bodyTemplate, {
|
|
58
|
+
input: inputObj,
|
|
59
|
+
secrets: provCtx.secrets ?? {},
|
|
60
|
+
context: ctxObj
|
|
61
|
+
}) : inputObj;
|
|
62
|
+
const query = httpMeta.queryTemplate ? Object.fromEntries(
|
|
63
|
+
Object.entries(
|
|
64
|
+
expandTemplate(httpMeta.queryTemplate, {
|
|
65
|
+
input: inputObj,
|
|
66
|
+
secrets: provCtx.secrets ?? {},
|
|
67
|
+
context: ctxObj
|
|
68
|
+
})
|
|
69
|
+
).map(([k, v]) => [k, String(v)])
|
|
70
|
+
) : void 0;
|
|
71
|
+
request = { url: appendQuery(url, query), method, headers, body };
|
|
72
|
+
}
|
|
73
|
+
const response = await fetch(request.url, {
|
|
74
|
+
method: request.method,
|
|
75
|
+
headers: request.headers,
|
|
76
|
+
body: request.body == null || request.method === "GET" ? void 0 : typeof request.body === "string" ? request.body : JSON.stringify(request.body),
|
|
77
|
+
signal
|
|
78
|
+
});
|
|
79
|
+
const responseHeaders = headersToObject(response.headers);
|
|
80
|
+
const responseBody = await parseResponseBody(response);
|
|
81
|
+
if (args.parseResponse) {
|
|
82
|
+
const parsed = args.parseResponse({
|
|
83
|
+
toolId: args.toolId,
|
|
84
|
+
status: response.status,
|
|
85
|
+
body: responseBody,
|
|
86
|
+
headers: responseHeaders
|
|
87
|
+
});
|
|
88
|
+
if (parsed.ok) return parsed.value;
|
|
89
|
+
throw new HttpDriverError(parsed.error.code, parsed.error.message, parsed.error.retryable);
|
|
90
|
+
}
|
|
91
|
+
if (response.status === 401 || response.status === 403) {
|
|
92
|
+
throw new HttpDriverError("auth_required", `HTTP ${response.status}`, false);
|
|
93
|
+
}
|
|
94
|
+
if (response.status === 429) {
|
|
95
|
+
throw new HttpDriverError("rate_limited", "HTTP 429", true);
|
|
96
|
+
}
|
|
97
|
+
if (response.status >= 500) {
|
|
98
|
+
throw new HttpDriverError("upstream_error", `HTTP ${response.status}`, true);
|
|
99
|
+
}
|
|
100
|
+
if (response.status >= 400) {
|
|
101
|
+
throw new HttpDriverError("upstream_error", `HTTP ${response.status}`, false);
|
|
102
|
+
}
|
|
103
|
+
const path = httpMeta.responseExtract ?? "$";
|
|
104
|
+
return extractResponse(responseBody, path);
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
var HttpDriverError = class extends Error {
|
|
108
|
+
code;
|
|
109
|
+
retryable;
|
|
110
|
+
constructor(code, message, retryable) {
|
|
111
|
+
super(message);
|
|
112
|
+
this.name = "HttpDriverError";
|
|
113
|
+
this.code = code;
|
|
114
|
+
this.retryable = retryable;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
function expandTemplate(value, vars) {
|
|
118
|
+
if (typeof value === "string") return substituteString(value, vars);
|
|
119
|
+
if (Array.isArray(value)) return value.map((v) => expandTemplate(v, vars));
|
|
120
|
+
if (value && typeof value === "object") {
|
|
121
|
+
const out = {};
|
|
122
|
+
for (const [k, v] of Object.entries(value)) {
|
|
123
|
+
out[k] = expandTemplate(v, vars);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function expandHeaders(headers, vars) {
|
|
130
|
+
const out = {};
|
|
131
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
132
|
+
const expanded = substituteString(v, vars);
|
|
133
|
+
out[k] = typeof expanded === "string" ? expanded : String(expanded);
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
var TEMPLATE_RE = /\$\{(input|secrets|context)\.([\w.]+)(?:\s*\|\s*default\(([^)]*)\))?\}/g;
|
|
138
|
+
function substituteString(template, vars) {
|
|
139
|
+
const single = template.match(/^\$\{(input|secrets|context)\.([\w.]+)\}$/);
|
|
140
|
+
if (single) {
|
|
141
|
+
const [, scope, path] = single;
|
|
142
|
+
return resolvePath(vars[scope], path);
|
|
143
|
+
}
|
|
144
|
+
return template.replace(TEMPLATE_RE, (_match, scope, path, fallbackRaw) => {
|
|
145
|
+
const resolved = resolvePath(vars[scope], path);
|
|
146
|
+
if (resolved === void 0 || resolved === null) {
|
|
147
|
+
if (fallbackRaw !== void 0) {
|
|
148
|
+
const trimmed = fallbackRaw.trim();
|
|
149
|
+
return trimmed.startsWith("'") || trimmed.startsWith('"') ? trimmed.slice(1, -1) : trimmed;
|
|
150
|
+
}
|
|
151
|
+
return "";
|
|
152
|
+
}
|
|
153
|
+
return typeof resolved === "string" ? resolved : JSON.stringify(resolved);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function resolvePath(obj, path) {
|
|
157
|
+
return path.split(".").reduce((acc, key) => {
|
|
158
|
+
if (acc && typeof acc === "object") return acc[key];
|
|
159
|
+
return void 0;
|
|
160
|
+
}, obj);
|
|
161
|
+
}
|
|
162
|
+
function extractResponse(body, path) {
|
|
163
|
+
if (path === "$") return body;
|
|
164
|
+
const tokens = parseJsonPath(path);
|
|
165
|
+
let current = body;
|
|
166
|
+
for (const token of tokens) {
|
|
167
|
+
if (current == null) return void 0;
|
|
168
|
+
if (token.kind === "key") {
|
|
169
|
+
current = current[token.value];
|
|
170
|
+
} else if (token.kind === "index") {
|
|
171
|
+
current = current[token.value];
|
|
172
|
+
} else if (token.kind === "wildcard") {
|
|
173
|
+
if (!Array.isArray(current)) return [];
|
|
174
|
+
const rest = tokens.slice(tokens.indexOf(token) + 1);
|
|
175
|
+
return current.map((el) => applyTokens(el, rest));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return current;
|
|
179
|
+
}
|
|
180
|
+
function applyTokens(value, tokens) {
|
|
181
|
+
let current = value;
|
|
182
|
+
for (const token of tokens) {
|
|
183
|
+
if (current == null) return void 0;
|
|
184
|
+
if (token.kind === "key") {
|
|
185
|
+
current = current[token.value];
|
|
186
|
+
} else if (token.kind === "index") {
|
|
187
|
+
current = current[token.value];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return current;
|
|
191
|
+
}
|
|
192
|
+
function parseJsonPath(path) {
|
|
193
|
+
const tokens = [];
|
|
194
|
+
let s = path.replace(/^\$/, "");
|
|
195
|
+
const re = /\.([A-Za-z_][A-Za-z0-9_]*)|\[(\d+)\]|\[(\*)\]/g;
|
|
196
|
+
let match;
|
|
197
|
+
while ((match = re.exec(s)) !== null) {
|
|
198
|
+
if (match[1]) tokens.push({ kind: "key", value: match[1] });
|
|
199
|
+
else if (match[2]) tokens.push({ kind: "index", value: Number.parseInt(match[2], 10) });
|
|
200
|
+
else if (match[3]) tokens.push({ kind: "wildcard", value: "*" });
|
|
201
|
+
}
|
|
202
|
+
return tokens;
|
|
203
|
+
}
|
|
204
|
+
async function parseResponseBody(response) {
|
|
205
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
206
|
+
if (ct.includes("application/json")) return await response.json();
|
|
207
|
+
return await response.text();
|
|
208
|
+
}
|
|
209
|
+
function headersToObject(h) {
|
|
210
|
+
const out = {};
|
|
211
|
+
h.forEach((v, k) => {
|
|
212
|
+
out[k] = v;
|
|
213
|
+
});
|
|
214
|
+
return out;
|
|
215
|
+
}
|
|
216
|
+
function appendQuery(url, query) {
|
|
217
|
+
if (!query || Object.keys(query).length === 0) return url;
|
|
218
|
+
const u = new URL(url);
|
|
219
|
+
for (const [k, v] of Object.entries(query)) u.searchParams.set(k, v);
|
|
220
|
+
return u.toString();
|
|
221
|
+
}
|
|
222
|
+
function normalizeToolId(ref) {
|
|
223
|
+
let s = ref.trim();
|
|
224
|
+
if (s.startsWith("./")) s = s.slice(2);
|
|
225
|
+
if (s.endsWith("/TOOL.md")) s = s.slice(0, -"/TOOL.md".length);
|
|
226
|
+
if (s.startsWith("tools/")) s = s.slice("tools/".length);
|
|
227
|
+
const lastSlash = s.lastIndexOf("/");
|
|
228
|
+
return lastSlash === -1 ? s : s.slice(lastSlash + 1);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export { HttpDriverError, defineHttpDriver, expandHeaders, expandTemplate, extractResponse };
|
|
232
|
+
//# sourceMappingURL=index.mjs.map
|
|
233
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @agentproto/http — AIP-31 HTTP provider specialisation.\n *\n * Sugar over `defineDriver({ kind: \"http\", ... })` plus the\n * HTTP-specific dispatch logic: body / query templating, header\n * substitution, JSONPath-lite response extraction, and SSE streaming.\n *\n * Spec: https://agentproto.sh/docs/aip-31\n */\n\nimport {\n defineDriver,\n type ExecuteFn,\n type ImplementsEntry,\n type DriverDefinition,\n type DriverHandle,\n} from \"@agentproto/driver\"\n\nexport interface HttpDriverDefinition extends Omit<DriverDefinition, \"kind\" | \"execute\"> {\n kind?: \"http\"\n /** API base URL. All implements[].metadata.http.endpoint paths relative to this. */\n baseUrl: string\n /** Headers attached to every request. Templating allowed via ${secrets.X} / ${context.X}. */\n defaultHeaders?: Record<string, string>\n /** Default HTTP method when per-tool entry omits it. Defaults to POST. */\n defaultMethod?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\"\n /** Default streaming config; per-tool override via metadata.http.streaming. */\n streaming?: { transport: \"sse\" | \"ndjson\" | \"chunked\"; eventField?: string; terminator?: string }\n\n /**\n * Optional behavioural adapter. When omitted, the runtime synthesises\n * an execute body per implements[] entry from the per-tool metadata.http\n * dispatch hints (endpoint, method, body_template, response_extract).\n */\n buildRequest?: (args: BuildRequestArgs) => HttpRequest | Promise<HttpRequest>\n parseResponse?: (args: ParseResponseArgs) => HttpParseResult\n}\n\nexport interface BuildRequestArgs {\n toolId: string\n input: Record<string, unknown>\n context: Record<string, unknown>\n driverCtx: { secrets: Record<string, string>; baseUrl: string; [k: string]: unknown }\n signal: AbortSignal\n}\n\nexport interface HttpRequest {\n url: string\n method: string\n headers: Record<string, string>\n body?: unknown\n query?: Record<string, string>\n}\n\nexport interface ParseResponseArgs {\n toolId: string\n status: number\n body: unknown\n headers: Record<string, string>\n}\n\nexport type HttpParseResult =\n | { ok: true; value: unknown }\n | { ok: false; error: { code: string; message: string; retryable?: boolean } }\n\n/**\n * Define an HTTP provider — sugar over defineDriver with kind: http\n * and an auto-synthesised execute map keyed by tool id when explicit\n * `execute` isn't provided.\n */\nexport function defineHttpDriver(\n definition: HttpDriverDefinition\n): DriverHandle {\n const { baseUrl, defaultHeaders = {}, defaultMethod = \"POST\" } = definition\n const execute: Record<string, ExecuteFn> = {}\n\n for (const entry of definition.implements) {\n const toolId = normalizeToolId(entry.tool)\n execute[toolId] = createExecuteFn({\n toolId,\n entry,\n baseUrl,\n defaultHeaders,\n defaultMethod,\n buildRequest: definition.buildRequest,\n parseResponse: definition.parseResponse,\n })\n }\n\n return defineDriver({\n ...definition,\n kind: \"http\",\n execute,\n metadata: {\n ...(definition.metadata ?? {}),\n http: { baseUrl, defaultHeaders, defaultMethod, streaming: definition.streaming },\n },\n })\n}\n\nfunction createExecuteFn(args: {\n toolId: string\n entry: ImplementsEntry\n baseUrl: string\n defaultHeaders: Record<string, string>\n defaultMethod: string\n buildRequest?: HttpDriverDefinition[\"buildRequest\"]\n parseResponse?: HttpDriverDefinition[\"parseResponse\"]\n}): ExecuteFn {\n const meta = (args.entry.metadata ?? {}) as { http?: PerToolHttp }\n const httpMeta = meta.http ?? {}\n\n return async ({ input, context, driverCtx, signal }) => {\n const inputObj = (input ?? {}) as Record<string, unknown>\n const ctxObj = context as Record<string, unknown>\n const provCtx = driverCtx as { secrets: Record<string, string>; [k: string]: unknown }\n\n let request: HttpRequest\n if (args.buildRequest) {\n request = await args.buildRequest({\n toolId: args.toolId,\n input: inputObj,\n context: ctxObj,\n driverCtx: { ...provCtx, baseUrl: args.baseUrl },\n signal,\n })\n } else {\n const endpoint = httpMeta.endpoint ?? \"/\"\n const method = httpMeta.method ?? args.defaultMethod\n const url = new URL(endpoint, args.baseUrl).toString()\n const headers = expandHeaders(\n { ...args.defaultHeaders, ...(httpMeta.headers ?? {}) },\n { input: inputObj, secrets: provCtx.secrets ?? {}, context: ctxObj }\n )\n const body = httpMeta.bodyTemplate\n ? expandTemplate(httpMeta.bodyTemplate, {\n input: inputObj,\n secrets: provCtx.secrets ?? {},\n context: ctxObj,\n })\n : inputObj\n const query = httpMeta.queryTemplate\n ? Object.fromEntries(\n Object.entries(\n expandTemplate(httpMeta.queryTemplate, {\n input: inputObj,\n secrets: provCtx.secrets ?? {},\n context: ctxObj,\n }) as Record<string, unknown>\n ).map(([k, v]) => [k, String(v)])\n )\n : undefined\n request = { url: appendQuery(url, query), method, headers, body }\n }\n\n const response = await fetch(request.url, {\n method: request.method,\n headers: request.headers,\n body:\n request.body == null || request.method === \"GET\"\n ? undefined\n : typeof request.body === \"string\"\n ? request.body\n : JSON.stringify(request.body),\n signal,\n })\n\n const responseHeaders = headersToObject(response.headers)\n const responseBody = await parseResponseBody(response)\n\n if (args.parseResponse) {\n const parsed = args.parseResponse({\n toolId: args.toolId,\n status: response.status,\n body: responseBody,\n headers: responseHeaders,\n })\n if (parsed.ok) return parsed.value\n throw new HttpDriverError(parsed.error.code, parsed.error.message, parsed.error.retryable)\n }\n\n if (response.status === 401 || response.status === 403) {\n throw new HttpDriverError(\"auth_required\", `HTTP ${response.status}`, false)\n }\n if (response.status === 429) {\n throw new HttpDriverError(\"rate_limited\", \"HTTP 429\", true)\n }\n if (response.status >= 500) {\n throw new HttpDriverError(\"upstream_error\", `HTTP ${response.status}`, true)\n }\n if (response.status >= 400) {\n throw new HttpDriverError(\"upstream_error\", `HTTP ${response.status}`, false)\n }\n\n const path = httpMeta.responseExtract ?? \"$\"\n return extractResponse(responseBody, path)\n }\n}\n\ninterface PerToolHttp {\n endpoint?: string\n method?: string\n headers?: Record<string, string>\n bodyTemplate?: unknown\n queryTemplate?: Record<string, string>\n responseExtract?: string\n streaming?: { transport: string; eventField?: string; terminator?: string }\n idempotencyKeyHeader?: string\n}\n\nclass HttpDriverError extends Error {\n code: string\n retryable?: boolean\n constructor(code: string, message: string, retryable?: boolean) {\n super(message)\n this.name = \"HttpDriverError\"\n this.code = code\n this.retryable = retryable\n }\n}\n\n/** Expand `${input.X}` / `${secrets.X}` / `${context.X}` substitutions in a value tree. */\nexport function expandTemplate(\n value: unknown,\n vars: { input: Record<string, unknown>; secrets: Record<string, string>; context: Record<string, unknown> }\n): unknown {\n if (typeof value === \"string\") return substituteString(value, vars)\n if (Array.isArray(value)) return value.map(v => expandTemplate(v, vars))\n if (value && typeof value === \"object\") {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = expandTemplate(v, vars)\n }\n return out\n }\n return value\n}\n\nexport function expandHeaders(\n headers: Record<string, string>,\n vars: { input: Record<string, unknown>; secrets: Record<string, string>; context: Record<string, unknown> }\n): Record<string, string> {\n const out: Record<string, string> = {}\n for (const [k, v] of Object.entries(headers)) {\n const expanded = substituteString(v, vars)\n out[k] = typeof expanded === \"string\" ? expanded : String(expanded)\n }\n return out\n}\n\nconst TEMPLATE_RE = /\\$\\{(input|secrets|context)\\.([\\w.]+)(?:\\s*\\|\\s*default\\(([^)]*)\\))?\\}/g\n\nfunction substituteString(\n template: string,\n vars: { input: Record<string, unknown>; secrets: Record<string, string>; context: Record<string, unknown> }\n): unknown {\n // If the entire string is a single substitution, return the typed value.\n const single = template.match(/^\\$\\{(input|secrets|context)\\.([\\w.]+)\\}$/)\n if (single) {\n const [, scope, path] = single\n return resolvePath(vars[scope as \"input\" | \"secrets\" | \"context\"], path!)\n }\n return template.replace(TEMPLATE_RE, (_match, scope: string, path: string, fallbackRaw?: string) => {\n const resolved = resolvePath(vars[scope as \"input\" | \"secrets\" | \"context\"], path)\n if (resolved === undefined || resolved === null) {\n if (fallbackRaw !== undefined) {\n const trimmed = fallbackRaw.trim()\n return trimmed.startsWith(\"'\") || trimmed.startsWith('\"')\n ? trimmed.slice(1, -1)\n : trimmed\n }\n return \"\"\n }\n return typeof resolved === \"string\" ? resolved : JSON.stringify(resolved)\n })\n}\n\nfunction resolvePath(obj: unknown, path: string): unknown {\n return path.split(\".\").reduce<unknown>((acc, key) => {\n if (acc && typeof acc === \"object\") return (acc as Record<string, unknown>)[key]\n return undefined\n }, obj)\n}\n\n/** Extract a value from a response body using a JSONPath-lite expression. */\nexport function extractResponse(body: unknown, path: string): unknown {\n if (path === \"$\") return body\n const tokens = parseJsonPath(path)\n let current: unknown = body\n for (const token of tokens) {\n if (current == null) return undefined\n if (token.kind === \"key\") {\n current = (current as Record<string, unknown>)[token.value as string]\n } else if (token.kind === \"index\") {\n current = (current as unknown[])[token.value as number]\n } else if (token.kind === \"wildcard\") {\n if (!Array.isArray(current)) return []\n // Map remaining tokens over each element.\n const rest = tokens.slice(tokens.indexOf(token) + 1)\n return current.map(el => applyTokens(el, rest))\n }\n }\n return current\n}\n\nfunction applyTokens(value: unknown, tokens: Array<{ kind: string; value: string | number }>): unknown {\n let current: unknown = value\n for (const token of tokens) {\n if (current == null) return undefined\n if (token.kind === \"key\") {\n current = (current as Record<string, unknown>)[token.value as string]\n } else if (token.kind === \"index\") {\n current = (current as unknown[])[token.value as number]\n }\n }\n return current\n}\n\nfunction parseJsonPath(path: string): Array<{ kind: \"key\" | \"index\" | \"wildcard\"; value: string | number }> {\n const tokens: Array<{ kind: \"key\" | \"index\" | \"wildcard\"; value: string | number }> = []\n // Strip leading \"$\"\n let s = path.replace(/^\\$/, \"\")\n // Tokenise: .key | [N] | [*]\n const re = /\\.([A-Za-z_][A-Za-z0-9_]*)|\\[(\\d+)\\]|\\[(\\*)\\]/g\n let match: RegExpExecArray | null\n while ((match = re.exec(s)) !== null) {\n if (match[1]) tokens.push({ kind: \"key\", value: match[1] })\n else if (match[2]) tokens.push({ kind: \"index\", value: Number.parseInt(match[2], 10) })\n else if (match[3]) tokens.push({ kind: \"wildcard\", value: \"*\" })\n }\n return tokens\n}\n\nasync function parseResponseBody(response: Response): Promise<unknown> {\n const ct = response.headers.get(\"content-type\") ?? \"\"\n if (ct.includes(\"application/json\")) return await response.json()\n return await response.text()\n}\n\nfunction headersToObject(h: Headers): Record<string, string> {\n const out: Record<string, string> = {}\n h.forEach((v, k) => {\n out[k] = v\n })\n return out\n}\n\nfunction appendQuery(url: string, query: Record<string, string> | undefined): string {\n if (!query || Object.keys(query).length === 0) return url\n const u = new URL(url)\n for (const [k, v] of Object.entries(query)) u.searchParams.set(k, v)\n return u.toString()\n}\n\nfunction normalizeToolId(ref: string): string {\n let s = ref.trim()\n if (s.startsWith(\"./\")) s = s.slice(2)\n if (s.endsWith(\"/TOOL.md\")) s = s.slice(0, -\"/TOOL.md\".length)\n if (s.startsWith(\"tools/\")) s = s.slice(\"tools/\".length)\n const lastSlash = s.lastIndexOf(\"/\")\n return lastSlash === -1 ? s : s.slice(lastSlash + 1)\n}\n\nexport { HttpDriverError }\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentproto/driver-http",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "agentproto/http-runtime — AIP-31 HTTP provider specialisation. Sugar over @agentproto/driver's defineDriver({ kind: 'http' }) plus body templating, JSONPath-lite response extraction, and SSE streaming.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agentproto",
|
|
7
|
+
"aip-31",
|
|
8
|
+
"http",
|
|
9
|
+
"provider"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://agentproto.sh/docs/aip-31",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "dist/index.mjs",
|
|
15
|
+
"module": "dist/index.mjs",
|
|
16
|
+
"types": "dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.mjs",
|
|
21
|
+
"default": "./dist/index.mjs"
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"README.md",
|
|
28
|
+
"LICENSE"
|
|
29
|
+
],
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@agentproto/driver": "0.1.1",
|
|
35
|
+
"@agentproto/tool": "0.1.1"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^25.6.2",
|
|
39
|
+
"tsup": "^8.5.1",
|
|
40
|
+
"typescript": "^5.9.3",
|
|
41
|
+
"vitest": "^3.2.4",
|
|
42
|
+
"zod": "^4.4.3",
|
|
43
|
+
"@agentproto/tooling": "0.1.0-alpha.0"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/agentproto/ts",
|
|
48
|
+
"directory": "packages/driver/http"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"dev": "tsup --watch",
|
|
52
|
+
"build": "tsup",
|
|
53
|
+
"clean": "rm -rf dist",
|
|
54
|
+
"check-types": "tsc --noEmit",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:watch": "vitest"
|
|
57
|
+
}
|
|
58
|
+
}
|