@agentuity/runtime 1.0.48 → 2.0.0-beta.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/_globals.d.ts +58 -0
- package/dist/_globals.d.ts.map +1 -0
- package/dist/_globals.js +71 -0
- package/dist/_globals.js.map +1 -0
- package/dist/_metadata.d.ts.map +1 -1
- package/dist/_metadata.js +14 -0
- package/dist/_metadata.js.map +1 -1
- package/dist/_process-protection.d.ts +2 -0
- package/dist/_process-protection.d.ts.map +1 -1
- package/dist/_process-protection.js +14 -23
- package/dist/_process-protection.js.map +1 -1
- package/dist/_server.d.ts +4 -0
- package/dist/_server.d.ts.map +1 -1
- package/dist/_server.js +4 -0
- package/dist/_server.js.map +1 -1
- package/dist/_services.d.ts +1 -1
- package/dist/_services.d.ts.map +1 -1
- package/dist/_services.js +5 -1
- package/dist/_services.js.map +1 -1
- package/dist/_standalone.d.ts.map +1 -1
- package/dist/_standalone.js +3 -9
- package/dist/_standalone.js.map +1 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +1 -0
- package/dist/agent.js.map +1 -1
- package/dist/app.d.ts +149 -71
- package/dist/app.d.ts.map +1 -1
- package/dist/app.js +121 -156
- package/dist/app.js.map +1 -1
- package/dist/bootstrap.d.ts +44 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +256 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/dev-patches/aisdk.d.ts.map +1 -1
- package/dist/dev-patches/aisdk.js +6 -8
- package/dist/dev-patches/aisdk.js.map +1 -1
- package/dist/dev-patches/gateway.d.ts.map +1 -1
- package/dist/dev-patches/gateway.js +7 -8
- package/dist/dev-patches/gateway.js.map +1 -1
- package/dist/handlers/_route-meta.d.ts +20 -0
- package/dist/handlers/_route-meta.d.ts.map +1 -0
- package/dist/handlers/_route-meta.js +25 -0
- package/dist/handlers/_route-meta.js.map +1 -0
- package/dist/handlers/cron.d.ts.map +1 -1
- package/dist/handlers/cron.js +3 -1
- package/dist/handlers/cron.js.map +1 -1
- package/dist/handlers/sse.d.ts +3 -3
- package/dist/handlers/sse.d.ts.map +1 -1
- package/dist/handlers/sse.js +4 -16
- package/dist/handlers/sse.js.map +1 -1
- package/dist/handlers/stream.d.ts.map +1 -1
- package/dist/handlers/stream.js +4 -12
- package/dist/handlers/stream.js.map +1 -1
- package/dist/handlers/websocket.d.ts +3 -1
- package/dist/handlers/websocket.d.ts.map +1 -1
- package/dist/handlers/websocket.js +6 -37
- package/dist/handlers/websocket.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +1 -8
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +29 -71
- package/dist/middleware.js.map +1 -1
- package/dist/otel/logger.d.ts.map +1 -1
- package/dist/otel/logger.js +4 -7
- package/dist/otel/logger.js.map +1 -1
- package/dist/otel/otel.d.ts +4 -1
- package/dist/otel/otel.d.ts.map +1 -1
- package/dist/otel/otel.js +13 -2
- package/dist/otel/otel.js.map +1 -1
- package/dist/router.d.ts +10 -62
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +9 -146
- package/dist/router.js.map +1 -1
- package/dist/workbench.d.ts +1 -1
- package/dist/workbench.d.ts.map +1 -1
- package/dist/workbench.js +120 -12
- package/dist/workbench.js.map +1 -1
- package/package.json +7 -7
- package/src/_globals.ts +92 -0
- package/src/_metadata.ts +14 -0
- package/src/_process-protection.ts +17 -28
- package/src/_server.ts +4 -0
- package/src/_services.ts +6 -2
- package/src/_standalone.ts +4 -9
- package/src/agent.ts +1 -0
- package/src/app.ts +294 -195
- package/src/bootstrap.ts +316 -0
- package/src/dev-patches/aisdk.ts +8 -11
- package/src/dev-patches/gateway.ts +9 -11
- package/src/globals.d.ts +28 -0
- package/src/handlers/_route-meta.ts +31 -0
- package/src/handlers/cron.ts +4 -1
- package/src/handlers/sse.ts +8 -19
- package/src/handlers/stream.ts +5 -12
- package/src/handlers/websocket.ts +11 -37
- package/src/index.ts +2 -3
- package/src/middleware.ts +40 -99
- package/src/otel/logger.ts +5 -8
- package/src/otel/otel.ts +14 -2
- package/src/router.ts +12 -216
- package/src/workbench.ts +135 -12
package/src/index.ts
CHANGED
|
@@ -29,6 +29,8 @@ export {
|
|
|
29
29
|
// app.ts exports (all app-related functionality)
|
|
30
30
|
export {
|
|
31
31
|
type AppConfig,
|
|
32
|
+
type AnalyticsOptions,
|
|
33
|
+
type WorkbenchOptions,
|
|
32
34
|
type CompressionConfig,
|
|
33
35
|
type CorsConfig,
|
|
34
36
|
type Variables,
|
|
@@ -40,9 +42,6 @@ export {
|
|
|
40
42
|
type ShutdownHook,
|
|
41
43
|
createApp,
|
|
42
44
|
getApp,
|
|
43
|
-
getAppState,
|
|
44
|
-
getAppConfig,
|
|
45
|
-
getUserRouter,
|
|
46
45
|
runShutdown,
|
|
47
46
|
registerShutdownHook,
|
|
48
47
|
fireEvent,
|
package/src/middleware.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { setSignedCookie } from 'hono/cookie';
|
|
|
10
10
|
import type { Env, CompressionConfig, CorsConfig } from './app';
|
|
11
11
|
import { createTrustedCorsOrigin } from './cors';
|
|
12
12
|
import type { Logger } from './logger';
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
import { generateId } from './session';
|
|
15
15
|
import { runInHTTPContext } from './_context';
|
|
16
16
|
import { DURATION_HEADER, TOKENS_HEADER } from './_tokens';
|
|
@@ -111,10 +111,6 @@ export function createBaseMiddleware(config: MiddlewareConfig) {
|
|
|
111
111
|
|
|
112
112
|
// Import services dynamically to avoid circular deps
|
|
113
113
|
const { getServices } = await import('./_services');
|
|
114
|
-
const { getAppState } = await import('./app');
|
|
115
|
-
|
|
116
|
-
c.set('app', getAppState());
|
|
117
|
-
|
|
118
114
|
const services = getServices();
|
|
119
115
|
c.set('kv', services.kv);
|
|
120
116
|
c.set('stream', services.stream);
|
|
@@ -201,13 +197,7 @@ export function createBaseMiddleware(config: MiddlewareConfig) {
|
|
|
201
197
|
export function createCorsMiddleware(staticOptions?: CorsConfig) {
|
|
202
198
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
203
199
|
return createMiddleware<Env<any>>(async (c, next) => {
|
|
204
|
-
|
|
205
|
-
const appConfig = getAppConfig();
|
|
206
|
-
const appCors = appConfig?.cors;
|
|
207
|
-
const corsOptions = {
|
|
208
|
-
...appCors,
|
|
209
|
-
...staticOptions,
|
|
210
|
-
};
|
|
200
|
+
const corsOptions = { ...staticOptions };
|
|
211
201
|
|
|
212
202
|
// Extract Agentuity-specific options
|
|
213
203
|
const { sameOrigin, allowedOrigins, ...honoCorsOptions } = corsOptions;
|
|
@@ -689,18 +679,27 @@ export function createOtelMiddleware() {
|
|
|
689
679
|
}
|
|
690
680
|
});
|
|
691
681
|
} else if (wsDone) {
|
|
692
|
-
// WebSocket
|
|
682
|
+
// WebSocket upgrade — end the span immediately (short-lived upgrade span)
|
|
683
|
+
// Per OTel conventions, spans should be short-lived. The HTTP upgrade
|
|
684
|
+
// request is a discrete event that completes in milliseconds. Keeping
|
|
685
|
+
// a span open for the entire WS connection lifetime (minutes/hours) is
|
|
686
|
+
// non-standard and causes issues with OTel backends.
|
|
693
687
|
internal.info(
|
|
694
|
-
'[request] %s %s - websocket
|
|
688
|
+
'[request] %s %s - websocket upgrade, ending span immediately (session: %s)',
|
|
695
689
|
method,
|
|
696
690
|
url.pathname,
|
|
697
691
|
sessionId
|
|
698
692
|
);
|
|
699
693
|
|
|
700
|
-
//
|
|
701
|
-
|
|
694
|
+
// End the upgrade span now with the upgrade duration
|
|
695
|
+
const upgradeDurationNs = Math.round(handlerDurationMs * 1_000_000);
|
|
696
|
+
span.setAttribute('@agentuity/request.duration', upgradeDurationNs);
|
|
697
|
+
span.setAttribute('http.status_code', responseStatus);
|
|
698
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
699
|
+
span.end();
|
|
700
|
+
shouldEndSpanInFinally = false; // already ended
|
|
702
701
|
|
|
703
|
-
//
|
|
702
|
+
// Session finalization still defers until WS close, but without holding a span
|
|
704
703
|
const pendingPromises = handler.getPendingSnapshot();
|
|
705
704
|
const hasPendingTasks = pendingPromises.length > 0;
|
|
706
705
|
|
|
@@ -714,11 +713,6 @@ export function createOtelMiddleware() {
|
|
|
714
713
|
);
|
|
715
714
|
}
|
|
716
715
|
|
|
717
|
-
// Capture values needed for span attributes
|
|
718
|
-
const capturedResponseStatus = responseStatus;
|
|
719
|
-
const capturedErrorMessage = errorMessage;
|
|
720
|
-
|
|
721
|
-
// Use waitUntil to handle WebSocket close and finalization
|
|
722
716
|
handler.waitUntil(async () => {
|
|
723
717
|
let wsError: unknown = undefined;
|
|
724
718
|
|
|
@@ -741,81 +735,43 @@ export function createOtelMiddleware() {
|
|
|
741
735
|
);
|
|
742
736
|
}
|
|
743
737
|
|
|
744
|
-
|
|
745
|
-
const wsDurationMs = performance.now() - requestStartTime;
|
|
746
|
-
const durationNs = Math.round(wsDurationMs * 1_000_000);
|
|
747
|
-
internal.info(
|
|
748
|
-
'[request] %s %s - recording websocket duration: %sms (session: %s)',
|
|
749
|
-
method,
|
|
750
|
-
url.pathname,
|
|
751
|
-
wsDurationMs.toFixed(2),
|
|
752
|
-
sessionId
|
|
753
|
-
);
|
|
754
|
-
|
|
755
|
-
// Determine final status
|
|
756
|
-
const finalStatus = wsError ? 500 : capturedResponseStatus;
|
|
738
|
+
const finalStatus = wsError ? 500 : responseStatus;
|
|
757
739
|
const finalErrorMessage = wsError
|
|
758
740
|
? wsError instanceof Error
|
|
759
741
|
? (wsError.stack ?? wsError.message)
|
|
760
742
|
: String(wsError)
|
|
761
|
-
:
|
|
743
|
+
: errorMessage;
|
|
762
744
|
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
if (hasPendingTasks) {
|
|
766
|
-
internal.info(
|
|
767
|
-
'[request] %s %s - waiting for %d pending waitUntil tasks (session: %s)',
|
|
768
|
-
method,
|
|
769
|
-
url.pathname,
|
|
770
|
-
pendingPromises.length,
|
|
771
|
-
sessionId
|
|
772
|
-
);
|
|
773
|
-
const logger = c.get('logger');
|
|
774
|
-
await handler.waitForPromises(pendingPromises, logger, sessionId);
|
|
775
|
-
internal.info(
|
|
776
|
-
'[request] %s %s - all waitUntil tasks complete (session: %s)',
|
|
777
|
-
method,
|
|
778
|
-
url.pathname,
|
|
779
|
-
sessionId
|
|
780
|
-
);
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
// Finalize session after WebSocket closes
|
|
784
|
-
await finalizeSession(
|
|
785
|
-
finalStatus >= 500 ? finalStatus : undefined,
|
|
786
|
-
finalErrorMessage
|
|
787
|
-
);
|
|
745
|
+
// Wait for pending tasks captured BEFORE this waitUntil was added
|
|
746
|
+
if (hasPendingTasks) {
|
|
788
747
|
internal.info(
|
|
789
|
-
'[request] %s %s -
|
|
748
|
+
'[request] %s %s - waiting for %d pending waitUntil tasks (session: %s)',
|
|
790
749
|
method,
|
|
791
750
|
url.pathname,
|
|
751
|
+
pendingPromises.length,
|
|
792
752
|
sessionId
|
|
793
753
|
);
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
span.setAttribute('@agentuity/request.duration', durationNs);
|
|
797
|
-
span.setAttribute('http.status_code', finalStatus);
|
|
798
|
-
|
|
799
|
-
if (wsError) {
|
|
800
|
-
span.setStatus({
|
|
801
|
-
code: SpanStatusCode.ERROR,
|
|
802
|
-
message: finalErrorMessage ?? 'WebSocket ended with error',
|
|
803
|
-
});
|
|
804
|
-
if (wsError instanceof Error) {
|
|
805
|
-
span.recordException(wsError);
|
|
806
|
-
}
|
|
807
|
-
} else {
|
|
808
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
span.end();
|
|
754
|
+
const logger = c.get('logger');
|
|
755
|
+
await handler.waitForPromises(pendingPromises, logger, sessionId);
|
|
812
756
|
internal.info(
|
|
813
|
-
'[request] %s %s -
|
|
757
|
+
'[request] %s %s - all waitUntil tasks complete (session: %s)',
|
|
814
758
|
method,
|
|
815
759
|
url.pathname,
|
|
816
760
|
sessionId
|
|
817
761
|
);
|
|
818
762
|
}
|
|
763
|
+
|
|
764
|
+
// Finalize session after WebSocket closes
|
|
765
|
+
await finalizeSession(
|
|
766
|
+
finalStatus >= 500 ? finalStatus : undefined,
|
|
767
|
+
finalErrorMessage
|
|
768
|
+
);
|
|
769
|
+
internal.info(
|
|
770
|
+
'[request] %s %s - websocket session finalization complete (session: %s)',
|
|
771
|
+
method,
|
|
772
|
+
url.pathname,
|
|
773
|
+
sessionId
|
|
774
|
+
);
|
|
819
775
|
});
|
|
820
776
|
} else {
|
|
821
777
|
// Non-streaming: record duration immediately
|
|
@@ -1041,30 +997,15 @@ export function createOtelMiddleware() {
|
|
|
1041
997
|
* });
|
|
1042
998
|
* ```
|
|
1043
999
|
*/
|
|
1044
|
-
export function createCompressionMiddleware(
|
|
1045
|
-
staticConfig?: CompressionConfig,
|
|
1046
|
-
/**
|
|
1047
|
-
* Optional config resolver for testing. When provided, this is used instead of getAppConfig().
|
|
1048
|
-
* @internal
|
|
1049
|
-
*/
|
|
1050
|
-
configResolver?: () => { compression?: CompressionConfig | false } | undefined
|
|
1051
|
-
) {
|
|
1000
|
+
export function createCompressionMiddleware(staticConfig?: CompressionConfig | false) {
|
|
1052
1001
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1053
1002
|
return createMiddleware<Env<any>>(async (c, next) => {
|
|
1054
|
-
// Lazy resolve: merge app config with static config
|
|
1055
|
-
const appConfig = configResolver ? configResolver() : getAppConfig();
|
|
1056
|
-
const appCompressionConfig = appConfig?.compression;
|
|
1057
|
-
|
|
1058
1003
|
// Check if compression is explicitly disabled
|
|
1059
|
-
if (
|
|
1004
|
+
if (staticConfig === false || staticConfig?.enabled === false) {
|
|
1060
1005
|
return next();
|
|
1061
1006
|
}
|
|
1062
1007
|
|
|
1063
|
-
|
|
1064
|
-
const config: CompressionConfig = {
|
|
1065
|
-
...(typeof appCompressionConfig === 'object' ? appCompressionConfig : {}),
|
|
1066
|
-
...staticConfig,
|
|
1067
|
-
};
|
|
1008
|
+
const config: CompressionConfig = { ...staticConfig };
|
|
1068
1009
|
|
|
1069
1010
|
const { enabled = true, threshold = 1024, filter, honoOptions } = config;
|
|
1070
1011
|
|
package/src/otel/logger.ts
CHANGED
|
@@ -4,20 +4,17 @@ import * as LogsAPI from '@opentelemetry/api-logs';
|
|
|
4
4
|
import type { Logger } from '../logger';
|
|
5
5
|
import ConsoleLogger from '../logger/console';
|
|
6
6
|
|
|
7
|
+
import { originalConsole as originalConsoleGlobal } from '../_globals';
|
|
8
|
+
|
|
7
9
|
/**
|
|
8
10
|
* Reference to the original console object before patching.
|
|
9
|
-
*
|
|
10
|
-
* preventing double-patching on hot reload.
|
|
11
|
+
* Stored in a Symbol.for() global to survive hot reloads.
|
|
11
12
|
*/
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
// Check if we've already saved the original console (prevents double-patching on reload)
|
|
15
|
-
const existingOriginal = (globalThis as Record<symbol, Console>)[ORIGINAL_CONSOLE_KEY];
|
|
13
|
+
const existingOriginal = originalConsoleGlobal.get();
|
|
16
14
|
export const __originalConsole: Console = existingOriginal ?? Object.create(console);
|
|
17
15
|
|
|
18
|
-
// Save to global if not already saved
|
|
19
16
|
if (!existingOriginal) {
|
|
20
|
-
(
|
|
17
|
+
originalConsoleGlobal.set(__originalConsole);
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
export class OtelLogger implements Logger {
|
package/src/otel/otel.ts
CHANGED
|
@@ -158,13 +158,23 @@ export const createUserLoggerProvider = ({
|
|
|
158
158
|
};
|
|
159
159
|
};
|
|
160
160
|
|
|
161
|
+
import { otel as otelGlobal } from '../_globals';
|
|
162
|
+
|
|
161
163
|
/**
|
|
162
|
-
* Registers and initializes OpenTelemetry with the specified configuration
|
|
164
|
+
* Registers and initializes OpenTelemetry with the specified configuration.
|
|
165
|
+
*
|
|
166
|
+
* Idempotent: if called again (e.g. during bun --hot reload), the previous
|
|
167
|
+
* instance is shut down before creating a new one.
|
|
163
168
|
*
|
|
164
169
|
* @param config - The configuration for OpenTelemetry
|
|
165
170
|
* @returns An object containing the tracer, logger, and shutdown function
|
|
166
171
|
*/
|
|
167
172
|
export function registerOtel(config: OtelConfig): OtelResponse {
|
|
173
|
+
// Shut down previous instance if this is a hot reload
|
|
174
|
+
const previous = otelGlobal.get();
|
|
175
|
+
if (previous) {
|
|
176
|
+
previous.shutdown().catch(() => {});
|
|
177
|
+
}
|
|
168
178
|
const {
|
|
169
179
|
url,
|
|
170
180
|
name,
|
|
@@ -338,5 +348,7 @@ export function registerOtel(config: OtelConfig): OtelResponse {
|
|
|
338
348
|
logger.info('connected to Agentuity Agent Cloud');
|
|
339
349
|
}
|
|
340
350
|
|
|
341
|
-
|
|
351
|
+
const instance: OtelResponse = { tracer, meter, logger, shutdown };
|
|
352
|
+
otelGlobal.set(instance);
|
|
353
|
+
return instance;
|
|
342
354
|
}
|
package/src/router.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import { loadBuildMetadata } from './_metadata';
|
|
4
|
-
import { returnResponse } from './_util';
|
|
1
|
+
import { Hono, type Env as HonoEnv, type Schema } from 'hono';
|
|
2
|
+
import type { BlankSchema } from 'hono/types';
|
|
5
3
|
import type { Env } from './app';
|
|
6
4
|
|
|
7
5
|
// Re-export both Env types
|
|
@@ -14,7 +12,6 @@ export type { WebSocketConnection } from './handlers/websocket';
|
|
|
14
12
|
// Module augmentation to extend Hono types for Agentuity runtime
|
|
15
13
|
declare module 'hono' {
|
|
16
14
|
// Extend Context with waitUntil for route handlers
|
|
17
|
-
// Note: executionCtx is already provided by Hono's Context class
|
|
18
15
|
interface Context {
|
|
19
16
|
/**
|
|
20
17
|
* Schedule a background task that runs after the response is sent.
|
|
@@ -32,63 +29,20 @@ declare module 'hono' {
|
|
|
32
29
|
*/
|
|
33
30
|
waitUntil(callback: Promise<void> | (() => void | Promise<void>)): void;
|
|
34
31
|
}
|
|
35
|
-
|
|
36
|
-
// Deprecated router methods (stubs that throw errors with migration instructions)
|
|
37
|
-
interface Hono {
|
|
38
|
-
/**
|
|
39
|
-
* @deprecated Use the `websocket` middleware instead:
|
|
40
|
-
* ```typescript
|
|
41
|
-
* import { websocket } from '@agentuity/runtime';
|
|
42
|
-
* router.get('/ws', websocket((c, ws) => { ... }));
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
websocket(path: string, ...args: any[]): this;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* @deprecated Use the `sse` middleware instead:
|
|
49
|
-
* ```typescript
|
|
50
|
-
* import { sse } from '@agentuity/runtime';
|
|
51
|
-
* router.get('/events', sse((c, stream) => { ... }));
|
|
52
|
-
* ```
|
|
53
|
-
*/
|
|
54
|
-
sse(path: string, ...args: any[]): this;
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* @deprecated Use the `stream` middleware instead:
|
|
58
|
-
* ```typescript
|
|
59
|
-
* import { stream } from '@agentuity/runtime';
|
|
60
|
-
* router.post('/data', stream((c) => new ReadableStream({ ... })));
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
|
-
stream(path: string, ...args: any[]): this;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @deprecated Use the `cron` middleware instead:
|
|
67
|
-
* ```typescript
|
|
68
|
-
* import { cron } from '@agentuity/runtime';
|
|
69
|
-
* router.post('/job', cron('0 0 * * *', (c) => { ... }));
|
|
70
|
-
* ```
|
|
71
|
-
*/
|
|
72
|
-
cron(schedule: string, ...args: any[]): this;
|
|
73
|
-
}
|
|
74
32
|
}
|
|
75
33
|
|
|
76
34
|
/**
|
|
77
|
-
* Creates a Hono router
|
|
78
|
-
*
|
|
79
|
-
* Standard HTTP methods (get, post, put, delete, patch) are available, plus middleware
|
|
80
|
-
* functions for specialized protocols:
|
|
35
|
+
* Creates a Hono router for use with Agentuity.
|
|
81
36
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* - **webrtc()** - WebRTC signaling (import { webrtc } from '@agentuity/runtime')
|
|
37
|
+
* This is a thin wrapper around `new Hono()` that provides the correct
|
|
38
|
+
* Agentuity environment types. Hono's full type inference chain is
|
|
39
|
+
* preserved — the Schema type parameter accumulates route definitions
|
|
40
|
+
* as you chain `.get()`, `.post()`, etc.
|
|
87
41
|
*
|
|
88
|
-
* @template E - Environment type (
|
|
42
|
+
* @template E - Environment type (defaults to Agentuity's Env)
|
|
89
43
|
* @template S - Schema type for route definitions
|
|
90
44
|
*
|
|
91
|
-
* @returns
|
|
45
|
+
* @returns Hono router instance
|
|
92
46
|
*
|
|
93
47
|
* @example
|
|
94
48
|
* ```typescript
|
|
@@ -96,7 +50,7 @@ declare module 'hono' {
|
|
|
96
50
|
*
|
|
97
51
|
* const router = createRouter();
|
|
98
52
|
*
|
|
99
|
-
* // Standard HTTP routes
|
|
53
|
+
* // Standard HTTP routes — full type inference
|
|
100
54
|
* router.get('/hello', (c) => c.text('Hello!'));
|
|
101
55
|
* router.post('/data', async (c) => {
|
|
102
56
|
* const body = await c.req.json();
|
|
@@ -114,166 +68,8 @@ declare module 'hono' {
|
|
|
114
68
|
* router.get('/events', sse((c, stream) => {
|
|
115
69
|
* stream.writeSSE({ data: 'Hello', event: 'message' });
|
|
116
70
|
* }));
|
|
117
|
-
*
|
|
118
|
-
* // Streaming response
|
|
119
|
-
* router.post('/stream', stream((c) => {
|
|
120
|
-
* return new ReadableStream({
|
|
121
|
-
* start(controller) {
|
|
122
|
-
* controller.enqueue('data\n');
|
|
123
|
-
* controller.close();
|
|
124
|
-
* }
|
|
125
|
-
* });
|
|
126
|
-
* }));
|
|
127
|
-
*
|
|
128
|
-
* // Cron job
|
|
129
|
-
* router.post('/daily', cron('0 0 * * *', (c) => {
|
|
130
|
-
* return { status: 'complete' };
|
|
131
|
-
* }));
|
|
132
71
|
* ```
|
|
133
72
|
*/
|
|
134
|
-
export const createRouter = <E extends Env = Env, S extends Schema =
|
|
135
|
-
|
|
136
|
-
// tslint:disable-next-line:no-any no-unused-variable
|
|
137
|
-
const _router = router as any;
|
|
138
|
-
|
|
139
|
-
for (const method of ['get', 'put', 'post', 'delete', 'options', 'patch']) {
|
|
140
|
-
const _originalInvoker = _router[method].bind(router);
|
|
141
|
-
_router[method] = (path: string, ...args: any[]) => {
|
|
142
|
-
// Pass through to original Hono - it handles all the complex type inference
|
|
143
|
-
// We'll only wrap the final handler to add our response handling
|
|
144
|
-
if (args.length === 0) {
|
|
145
|
-
return _originalInvoker(path);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Find the last function in args - that's the handler (everything else is middleware)
|
|
149
|
-
let handlerIndex = args.length - 1;
|
|
150
|
-
while (handlerIndex >= 0 && typeof args[handlerIndex] !== 'function') {
|
|
151
|
-
handlerIndex--;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (handlerIndex < 0) {
|
|
155
|
-
// No handler found, pass through as-is
|
|
156
|
-
return _originalInvoker(path, ...args);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const handler = args[handlerIndex];
|
|
160
|
-
|
|
161
|
-
// Check if this is middleware (2 params: c, next) vs handler (1 param: c)
|
|
162
|
-
if (handler.length === 2) {
|
|
163
|
-
// This is middleware-only, pass through
|
|
164
|
-
return _originalInvoker(path, ...args);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Wrap the handler to add our response conversion and set routeId
|
|
168
|
-
const wrapper = async (c: Context): Promise<Response> => {
|
|
169
|
-
// Look up the route ID from build metadata by matching method and path
|
|
170
|
-
// Try both the registered path and the actual request path (which may include base path)
|
|
171
|
-
const metadata = loadBuildMetadata();
|
|
172
|
-
const methodUpper = method.toUpperCase();
|
|
173
|
-
const requestPath = c.req.routePath || c.req.path;
|
|
174
|
-
|
|
175
|
-
// Try matching by registered path first, then by request path, then by path ending
|
|
176
|
-
let route = metadata?.routes?.find(
|
|
177
|
-
(r) => r.method.toUpperCase() === methodUpper && r.path === path
|
|
178
|
-
);
|
|
179
|
-
if (!route) {
|
|
180
|
-
route = metadata?.routes?.find(
|
|
181
|
-
(r) => r.method.toUpperCase() === methodUpper && r.path === requestPath
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
if (!route) {
|
|
185
|
-
// Try matching by path ending (handles /api/translate matching /translate)
|
|
186
|
-
route = metadata?.routes?.find(
|
|
187
|
-
(r) => r.method.toUpperCase() === methodUpper && r.path.endsWith(path)
|
|
188
|
-
);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (route?.id) {
|
|
192
|
-
(c as any).set('routeId', route.id);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
let result = handler(c);
|
|
196
|
-
if (result instanceof Promise) result = await result;
|
|
197
|
-
// If handler returns a Response, return it unchanged
|
|
198
|
-
if (result instanceof Response) return result;
|
|
199
|
-
return returnResponse(c, result);
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
// Replace the handler with our wrapper
|
|
203
|
-
const newArgs = [...args];
|
|
204
|
-
newArgs[handlerIndex] = wrapper;
|
|
205
|
-
|
|
206
|
-
return _originalInvoker(path, ...newArgs);
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Deprecated stubs that throw errors with migration instructions
|
|
211
|
-
_router.websocket = (path: string, ..._args: any[]) => {
|
|
212
|
-
throw new Error(
|
|
213
|
-
'router.websocket() is deprecated and has been removed.\n\n' +
|
|
214
|
-
'Migration: Use the websocket middleware instead:\n\n' +
|
|
215
|
-
` import { createRouter, websocket } from '@agentuity/runtime';\n\n` +
|
|
216
|
-
' const router = createRouter();\n\n' +
|
|
217
|
-
' // Before (deprecated):\n' +
|
|
218
|
-
` // router.websocket('${path}', (c) => (ws) => { ... });\n\n` +
|
|
219
|
-
' // After:\n' +
|
|
220
|
-
` router.get('${path}', websocket((c, ws) => {\n` +
|
|
221
|
-
' ws.onMessage((event) => {\n' +
|
|
222
|
-
` ws.send('Echo: ' + event.data);\n` +
|
|
223
|
-
' });\n' +
|
|
224
|
-
' }));'
|
|
225
|
-
);
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
_router.sse = (path: string, ..._args: any[]) => {
|
|
229
|
-
throw new Error(
|
|
230
|
-
'router.sse() is deprecated and has been removed.\n\n' +
|
|
231
|
-
'Migration: Use the sse middleware instead:\n\n' +
|
|
232
|
-
` import { createRouter, sse } from '@agentuity/runtime';\n\n` +
|
|
233
|
-
' const router = createRouter();\n\n' +
|
|
234
|
-
' // Before (deprecated):\n' +
|
|
235
|
-
` // router.sse('${path}', (c) => async (stream) => { ... });\n\n` +
|
|
236
|
-
' // After:\n' +
|
|
237
|
-
` router.get('${path}', sse((c, stream) => {\n` +
|
|
238
|
-
` stream.writeSSE({ data: 'Hello', event: 'message' });\n` +
|
|
239
|
-
' }));'
|
|
240
|
-
);
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
_router.stream = (path: string, ..._args: any[]) => {
|
|
244
|
-
throw new Error(
|
|
245
|
-
'router.stream() is deprecated and has been removed.\n\n' +
|
|
246
|
-
'Migration: Use the stream middleware instead:\n\n' +
|
|
247
|
-
` import { createRouter, stream } from '@agentuity/runtime';\n\n` +
|
|
248
|
-
' const router = createRouter();\n\n' +
|
|
249
|
-
' // Before (deprecated):\n' +
|
|
250
|
-
` // router.stream('${path}', (c) => new ReadableStream({ ... }));\n\n` +
|
|
251
|
-
' // After:\n' +
|
|
252
|
-
` router.post('${path}', stream((c) => {\n` +
|
|
253
|
-
' return new ReadableStream({\n' +
|
|
254
|
-
' start(controller) {\n' +
|
|
255
|
-
` controller.enqueue('data\\n');\n` +
|
|
256
|
-
' controller.close();\n' +
|
|
257
|
-
' }\n' +
|
|
258
|
-
' });\n' +
|
|
259
|
-
' }));'
|
|
260
|
-
);
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
_router.cron = (schedule: string, ..._args: any[]) => {
|
|
264
|
-
throw new Error(
|
|
265
|
-
'router.cron() is deprecated and has been removed.\n\n' +
|
|
266
|
-
'Migration: Use the cron middleware instead:\n\n' +
|
|
267
|
-
` import { createRouter, cron } from '@agentuity/runtime';\n\n` +
|
|
268
|
-
' const router = createRouter();\n\n' +
|
|
269
|
-
' // Before (deprecated):\n' +
|
|
270
|
-
` // router.cron('${schedule}', (c) => { ... });\n\n` +
|
|
271
|
-
' // After:\n' +
|
|
272
|
-
` router.post('/your-cron-path', cron('${schedule}', (c) => {\n` +
|
|
273
|
-
` return { status: 'complete' };\n` +
|
|
274
|
-
' }));'
|
|
275
|
-
);
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
return router;
|
|
73
|
+
export const createRouter = <E extends Env = Env, S extends Schema = BlankSchema>(): Hono<E, S> => {
|
|
74
|
+
return new Hono<E, S>();
|
|
279
75
|
};
|