@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 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 { validateParams, routerErrorToStatus } from "@rcrsr/rill-agent";
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
- server = serve({ fetch: app.fetch, port }, () => {
22
- options?.serverTweaks?.(server);
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 !== void 0) {
29
- server.close();
30
- server = void 0;
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
- app.post("/agents/:name/run", async (c) => {
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
- const params = body["params"] ?? {};
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
- ...typeof body["timeout"] === "number" ? { timeout: body["timeout"] } : {}
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", async (c) => {
77
- let body;
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.19.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.0.1",
16
- "hono": "^4.12.16",
17
- "@rcrsr/rill-agent": "~0.19.0"
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.0",
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": "eslint --config ../../../eslint.config.js src/",
42
+ "lint": "oxlint --config ../../../.oxlintrc.json src/ tests/",
40
43
  "check": "pnpm run build && pnpm run test && pnpm run lint"
41
44
  }
42
45
  }