@rcrsr/rill-agent-http 0.19.0 → 0.21.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/dist/index.d.ts +64 -0
- package/dist/index.js +98 -38
- package/package.json +10 -7
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
import { Hono } from 'hono';
|
|
4
4
|
|
|
5
|
+
export interface RillHarnessLogger {
|
|
6
|
+
info(...args: unknown[]): void;
|
|
7
|
+
warn(...args: unknown[]): void;
|
|
8
|
+
error(...args: unknown[]): void;
|
|
9
|
+
}
|
|
10
|
+
export interface RillCompiledPackage {
|
|
11
|
+
readonly mount: string;
|
|
12
|
+
readonly buildOutput: {
|
|
13
|
+
readonly outputPath: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export interface RillServeContext {
|
|
17
|
+
readonly config: Record<string, unknown>;
|
|
18
|
+
readonly logger: RillHarnessLogger;
|
|
19
|
+
readonly packages: readonly RillCompiledPackage[];
|
|
20
|
+
readonly requestedMount: string | undefined;
|
|
21
|
+
readonly args: readonly string[];
|
|
22
|
+
readonly onShutdown: (handler: () => void | Promise<void>) => void;
|
|
23
|
+
readonly onSourceChange: (handler: () => void | Promise<void>) => void;
|
|
24
|
+
}
|
|
25
|
+
export interface RillPostBuildContext {
|
|
26
|
+
readonly outputDir: string;
|
|
27
|
+
readonly packages: readonly RillCompiledPackage[];
|
|
28
|
+
readonly logger: RillHarnessLogger;
|
|
29
|
+
}
|
|
30
|
+
export interface RillHarness {
|
|
31
|
+
readonly name: string;
|
|
32
|
+
readonly postBuild?: (ctx: RillPostBuildContext) => Promise<void>;
|
|
33
|
+
readonly serve?: (ctx: RillServeContext) => Promise<number>;
|
|
34
|
+
}
|
|
5
35
|
export interface HandlerDescription {
|
|
6
36
|
readonly name: string;
|
|
7
37
|
readonly description?: string | undefined;
|
|
@@ -12,6 +42,17 @@ export interface HandlerDescription {
|
|
|
12
42
|
readonly description?: string | undefined;
|
|
13
43
|
readonly defaultValue?: unknown;
|
|
14
44
|
}>;
|
|
45
|
+
/**
|
|
46
|
+
* Handler return type annotation, formatted with the same grammar as
|
|
47
|
+
* parameter type strings (e.g. `stream(dict(content: string)):string`).
|
|
48
|
+
* Undefined when the handler closure has no `:T` annotation, or when the
|
|
49
|
+
* rill-build emitting the handler is too old to expose the field.
|
|
50
|
+
*/
|
|
51
|
+
readonly returnType?: string | undefined;
|
|
52
|
+
}
|
|
53
|
+
export interface InitContext {
|
|
54
|
+
readonly globalVars?: Record<string, string> | undefined;
|
|
55
|
+
readonly ahiResolver?: ((agentName: string, request: RunRequest) => Promise<RunResponse>) | undefined;
|
|
15
56
|
}
|
|
16
57
|
export interface RunRequest {
|
|
17
58
|
readonly params?: Record<string, unknown> | undefined;
|
|
@@ -21,13 +62,25 @@ export interface RunContext {
|
|
|
21
62
|
readonly sessionVars?: Record<string, string> | undefined;
|
|
22
63
|
readonly onLog?: ((message: string) => void) | undefined;
|
|
23
64
|
readonly onChunk?: ((chunk: unknown) => Promise<void>) | undefined;
|
|
65
|
+
readonly signal?: AbortSignal | undefined;
|
|
24
66
|
}
|
|
25
67
|
export interface RunResponse {
|
|
26
68
|
readonly state: "completed" | "error";
|
|
27
69
|
readonly result: unknown;
|
|
28
70
|
readonly streamed?: boolean | undefined;
|
|
29
71
|
}
|
|
72
|
+
export interface AgentHandler {
|
|
73
|
+
describe(): HandlerDescription | null;
|
|
74
|
+
init(context?: InitContext): Promise<void>;
|
|
75
|
+
execute(request?: RunRequest, context?: RunContext): Promise<RunResponse>;
|
|
76
|
+
dispose(): Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
export interface AgentManifest {
|
|
79
|
+
readonly defaultAgent: string;
|
|
80
|
+
readonly agents: ReadonlyMap<string, AgentHandler>;
|
|
81
|
+
}
|
|
30
82
|
export interface AgentRouter {
|
|
83
|
+
readonly manifest: AgentManifest;
|
|
31
84
|
run(agentName: string, request: RunRequest, context?: RunContext): Promise<RunResponse>;
|
|
32
85
|
describe(agentName: string): HandlerDescription | null;
|
|
33
86
|
agents(): string[];
|
|
@@ -48,5 +101,16 @@ export interface HttpHarness {
|
|
|
48
101
|
* POST /run — execute the default agent
|
|
49
102
|
*/
|
|
50
103
|
export declare function httpHarness(router: AgentRouter): HttpHarness;
|
|
104
|
+
/**
|
|
105
|
+
* Default export consumed by the rill CLI (`rill install --replace`,
|
|
106
|
+
* `rill run`) when this package is declared as a bundle harness. `serve`
|
|
107
|
+
* assembles a router from the bundle's compiled packages and hosts it over the
|
|
108
|
+
* HTTP harness on `config.port` (default 3000).
|
|
109
|
+
*/
|
|
110
|
+
declare const harness: RillHarness;
|
|
111
|
+
|
|
112
|
+
export {
|
|
113
|
+
harness as default,
|
|
114
|
+
};
|
|
51
115
|
|
|
52
116
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
validateParams,
|
|
4
|
+
routerErrorToStatus,
|
|
5
|
+
createRouter,
|
|
6
|
+
assembleManifest
|
|
7
|
+
} from "@rcrsr/rill-agent";
|
|
3
8
|
|
|
4
9
|
// ../../shared/hono-kit/src/index.ts
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
|
+
import path from "path";
|
|
5
12
|
import { Hono } from "hono";
|
|
6
13
|
import { serve } from "@hono/node-server";
|
|
7
14
|
function assertJsonObject(parsed) {
|
|
@@ -17,23 +24,67 @@ function createHarnessLifecycle(options) {
|
|
|
17
24
|
if (server !== void 0) {
|
|
18
25
|
throw new Error("Server is already listening");
|
|
19
26
|
}
|
|
20
|
-
return new Promise((resolve) => {
|
|
21
|
-
|
|
22
|
-
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const started = serve({ fetch: app.fetch, port }, () => {
|
|
29
|
+
started.off("error", onError);
|
|
30
|
+
options?.serverTweaks?.(started);
|
|
23
31
|
resolve();
|
|
24
32
|
});
|
|
33
|
+
function onError(err) {
|
|
34
|
+
server = void 0;
|
|
35
|
+
reject(err);
|
|
36
|
+
}
|
|
37
|
+
started.once("error", onError);
|
|
38
|
+
server = started;
|
|
25
39
|
});
|
|
26
40
|
}
|
|
27
41
|
async function close() {
|
|
28
|
-
if (server
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
if (server === void 0) return;
|
|
43
|
+
const current = server;
|
|
44
|
+
server = void 0;
|
|
45
|
+
if ("closeAllConnections" in current) {
|
|
46
|
+
current.closeAllConnections();
|
|
31
47
|
}
|
|
48
|
+
await new Promise((resolve, reject) => {
|
|
49
|
+
current.close((err) => {
|
|
50
|
+
if (err) reject(err);
|
|
51
|
+
else resolve();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
32
54
|
}
|
|
33
55
|
return { app, listen, close };
|
|
34
56
|
}
|
|
57
|
+
function compiledPackageEntries(ctx) {
|
|
58
|
+
return ctx.packages.map((p) => ({
|
|
59
|
+
name: p.mount,
|
|
60
|
+
dir: p.buildOutput.outputPath
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
function readHarnessPort(config, fallback) {
|
|
64
|
+
const p = config["port"];
|
|
65
|
+
if (typeof p === "number" && Number.isInteger(p)) return p;
|
|
66
|
+
if (typeof p === "string" && /^\d+$/.test(p)) return Number(p);
|
|
67
|
+
return fallback;
|
|
68
|
+
}
|
|
69
|
+
function assertCompiledHandlers(ctx) {
|
|
70
|
+
for (const pkg of ctx.packages) {
|
|
71
|
+
const handlerPath = path.join(pkg.buildOutput.outputPath, "handler.js");
|
|
72
|
+
if (!existsSync(handlerPath)) {
|
|
73
|
+
throw new Error(`missing handler file: ${handlerPath}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function runRillServe(ctx, start) {
|
|
78
|
+
const handle = await start(compiledPackageEntries(ctx));
|
|
79
|
+
ctx.onShutdown(async () => {
|
|
80
|
+
await handle.close();
|
|
81
|
+
});
|
|
82
|
+
return new Promise(() => {
|
|
83
|
+
});
|
|
84
|
+
}
|
|
35
85
|
|
|
36
86
|
// src/index.ts
|
|
87
|
+
var HARNESS_NAME = "@rcrsr/rill-agent-http";
|
|
37
88
|
function httpHarness(router) {
|
|
38
89
|
const lifecycle = createHarnessLifecycle();
|
|
39
90
|
const { app } = lifecycle;
|
|
@@ -46,8 +97,7 @@ function httpHarness(router) {
|
|
|
46
97
|
}));
|
|
47
98
|
return c.json({ agents });
|
|
48
99
|
});
|
|
49
|
-
|
|
50
|
-
const name = c.req.param("name");
|
|
100
|
+
async function handleRun(c, name) {
|
|
51
101
|
let body;
|
|
52
102
|
try {
|
|
53
103
|
const parsed = await c.req.json();
|
|
@@ -55,14 +105,32 @@ function httpHarness(router) {
|
|
|
55
105
|
} catch {
|
|
56
106
|
return c.json({ error: "Request body must be a JSON object" }, 400);
|
|
57
107
|
}
|
|
58
|
-
|
|
108
|
+
let params;
|
|
109
|
+
try {
|
|
110
|
+
params = body["params"] === void 0 ? {} : assertJsonObject(body["params"]);
|
|
111
|
+
} catch {
|
|
112
|
+
return c.json({ error: 'Parameter "params" must be a JSON object' }, 400);
|
|
113
|
+
}
|
|
59
114
|
const validationError = validateParams(params, name, router);
|
|
60
115
|
if (validationError !== null) {
|
|
61
116
|
return c.json({ error: validationError }, 400);
|
|
62
117
|
}
|
|
118
|
+
const rawTimeout = body["timeout"];
|
|
119
|
+
let timeout;
|
|
120
|
+
if (rawTimeout !== void 0) {
|
|
121
|
+
if (typeof rawTimeout !== "number" || !Number.isFinite(rawTimeout) || rawTimeout <= 0) {
|
|
122
|
+
return c.json(
|
|
123
|
+
{
|
|
124
|
+
error: 'Parameter "timeout" must be a finite number greater than 0'
|
|
125
|
+
},
|
|
126
|
+
400
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
timeout = rawTimeout;
|
|
130
|
+
}
|
|
63
131
|
const request = {
|
|
64
132
|
params,
|
|
65
|
-
...
|
|
133
|
+
...timeout !== void 0 ? { timeout } : {}
|
|
66
134
|
};
|
|
67
135
|
try {
|
|
68
136
|
const response = await router.run(name, request);
|
|
@@ -72,38 +140,30 @@ function httpHarness(router) {
|
|
|
72
140
|
const message = err instanceof Error ? err.message : String(err);
|
|
73
141
|
return c.json({ error: message }, status);
|
|
74
142
|
}
|
|
75
|
-
}
|
|
76
|
-
app.post("/run",
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
const parsed = await c.req.json();
|
|
80
|
-
body = assertJsonObject(parsed);
|
|
81
|
-
} catch {
|
|
82
|
-
return c.json({ error: "Request body must be a JSON object" }, 400);
|
|
83
|
-
}
|
|
84
|
-
const params = body["params"] ?? {};
|
|
85
|
-
const defaultName = router.defaultAgent();
|
|
86
|
-
const validationError = validateParams(params, defaultName, router);
|
|
87
|
-
if (validationError !== null) {
|
|
88
|
-
return c.json({ error: validationError }, 400);
|
|
89
|
-
}
|
|
90
|
-
const request = {
|
|
91
|
-
params,
|
|
92
|
-
...typeof body["timeout"] === "number" ? { timeout: body["timeout"] } : {}
|
|
93
|
-
};
|
|
94
|
-
try {
|
|
95
|
-
const response = await router.run("", request);
|
|
96
|
-
return c.json(response);
|
|
97
|
-
} catch (err) {
|
|
98
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
99
|
-
return c.json({ error: message }, 500);
|
|
100
|
-
}
|
|
101
|
-
});
|
|
143
|
+
}
|
|
144
|
+
app.post("/agents/:name/run", (c) => handleRun(c, c.req.param("name")));
|
|
145
|
+
app.post("/run", (c) => handleRun(c, ""));
|
|
102
146
|
async function listen(port = 3e3) {
|
|
103
147
|
return lifecycle.listen(port);
|
|
104
148
|
}
|
|
105
149
|
return { listen, close: lifecycle.close, app };
|
|
106
150
|
}
|
|
151
|
+
var harness = {
|
|
152
|
+
name: HARNESS_NAME,
|
|
153
|
+
postBuild: async (ctx) => {
|
|
154
|
+
assertCompiledHandlers(ctx);
|
|
155
|
+
},
|
|
156
|
+
serve: (ctx) => runRillServe(ctx, async (entries) => {
|
|
157
|
+
const router = await createRouter(await assembleManifest(entries));
|
|
158
|
+
const server = httpHarness(router);
|
|
159
|
+
const port = readHarnessPort(ctx.config, 3e3);
|
|
160
|
+
await server.listen(port);
|
|
161
|
+
ctx.logger.info(`[${HARNESS_NAME}] listening on :${port}`);
|
|
162
|
+
return server;
|
|
163
|
+
})
|
|
164
|
+
};
|
|
165
|
+
var index_default = harness;
|
|
107
166
|
export {
|
|
167
|
+
index_default as default,
|
|
108
168
|
httpHarness
|
|
109
169
|
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rcrsr/rill-agent-http",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "rill agent HTTP harness — Hono-based HTTP server wrapping AgentRouter",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Andre Bremer",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"rill": {
|
|
9
|
+
"role": "harness"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"types": "./dist/index.d.ts",
|
|
@@ -12,14 +15,14 @@
|
|
|
12
15
|
}
|
|
13
16
|
},
|
|
14
17
|
"dependencies": {
|
|
15
|
-
"@hono/node-server": "^2.
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
+
"@hono/node-server": "^2.1.1",
|
|
19
|
+
"@rcrsr/rill-agent": "~0.21.0",
|
|
20
|
+
"hono": "^4.13.7"
|
|
18
21
|
},
|
|
19
22
|
"devDependencies": {
|
|
23
|
+
"@rcrsr/rill-agent-hono-kit": "^0.21.0",
|
|
20
24
|
"dts-bundle-generator": "^9.5.1",
|
|
21
|
-
"tsup": "^8.5.
|
|
22
|
-
"@rcrsr/rill-agent-hono-kit": "^0.19.0"
|
|
25
|
+
"tsup": "^8.5.1"
|
|
23
26
|
},
|
|
24
27
|
"files": [
|
|
25
28
|
"dist"
|
|
@@ -36,7 +39,7 @@
|
|
|
36
39
|
"build": "tsup && dts-bundle-generator --config dts-bundle-generator.config.cjs && node -e \"const fs=require('fs');const walk=(d)=>fs.readdirSync(d,{withFileTypes:true}).flatMap(e=>e.isDirectory()?walk(d+'/'+e.name):[d+'/'+e.name]);const hits=walk('dist').filter(f=>fs.readFileSync(f,'utf8').includes('@rcrsr/rill-agent-hono-kit'));if(hits.length){console.error('hono-kit leak in dist:',hits);process.exit(1);}\"",
|
|
37
40
|
"test": "vitest run",
|
|
38
41
|
"typecheck": "tsc --noEmit",
|
|
39
|
-
"lint": "
|
|
42
|
+
"lint": "oxlint --config ../../../.oxlintrc.json src/ tests/",
|
|
40
43
|
"check": "pnpm run build && pnpm run test && pnpm run lint"
|
|
41
44
|
}
|
|
42
45
|
}
|