loki-mode 5.7.2 → 5.7.3
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/VERSION +1 -1
- package/api/README.md +297 -0
- package/api/client.ts +377 -0
- package/api/middleware/auth.ts +129 -0
- package/api/middleware/cors.ts +145 -0
- package/api/middleware/error.ts +226 -0
- package/api/mod.ts +58 -0
- package/api/openapi.yaml +614 -0
- package/api/routes/events.ts +165 -0
- package/api/routes/health.ts +169 -0
- package/api/routes/sessions.ts +262 -0
- package/api/routes/tasks.ts +182 -0
- package/api/server.js +637 -0
- package/api/server.ts +328 -0
- package/api/server_test.ts +265 -0
- package/api/services/cli-bridge.ts +503 -0
- package/api/services/event-bus.ts +189 -0
- package/api/services/state-watcher.ts +517 -0
- package/api/test.js +494 -0
- package/api/types/api.ts +122 -0
- package/api/types/events.ts +132 -0
- package/autonomy/loki +28 -2
- package/package.json +3 -2
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides optional token-based authentication for the API.
|
|
5
|
+
* By default, only allows localhost connections.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface AuthConfig {
|
|
9
|
+
// Allow localhost without auth (default: true)
|
|
10
|
+
allowLocalhost: boolean;
|
|
11
|
+
|
|
12
|
+
// API token for remote access (optional)
|
|
13
|
+
apiToken?: string;
|
|
14
|
+
|
|
15
|
+
// Allowed origins for CORS
|
|
16
|
+
allowedOrigins: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const defaultConfig: AuthConfig = {
|
|
20
|
+
allowLocalhost: true,
|
|
21
|
+
apiToken: Deno.env.get("LOKI_API_TOKEN"),
|
|
22
|
+
allowedOrigins: ["http://localhost:*", "http://127.0.0.1:*"],
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
let config = { ...defaultConfig };
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Configure authentication
|
|
29
|
+
*/
|
|
30
|
+
export function configureAuth(newConfig: Partial<AuthConfig>): void {
|
|
31
|
+
config = { ...config, ...newConfig };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Authentication middleware
|
|
36
|
+
*/
|
|
37
|
+
export function authMiddleware(
|
|
38
|
+
handler: (req: Request) => Promise<Response> | Response
|
|
39
|
+
): (req: Request) => Promise<Response> {
|
|
40
|
+
return async (req: Request): Promise<Response> => {
|
|
41
|
+
const authResult = checkAuth(req);
|
|
42
|
+
|
|
43
|
+
if (!authResult.allowed) {
|
|
44
|
+
return new Response(
|
|
45
|
+
JSON.stringify({
|
|
46
|
+
error: authResult.reason,
|
|
47
|
+
code: "AUTH_FAILED",
|
|
48
|
+
}),
|
|
49
|
+
{
|
|
50
|
+
status: 401,
|
|
51
|
+
headers: {
|
|
52
|
+
"Content-Type": "application/json",
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return handler(req);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Check if request is authenticated
|
|
64
|
+
*/
|
|
65
|
+
export function checkAuth(req: Request): { allowed: boolean; reason?: string } {
|
|
66
|
+
const url = new URL(req.url);
|
|
67
|
+
const host = url.hostname;
|
|
68
|
+
|
|
69
|
+
// Allow localhost connections if enabled
|
|
70
|
+
if (config.allowLocalhost) {
|
|
71
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "::1") {
|
|
72
|
+
return { allowed: true };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Check API token
|
|
77
|
+
if (config.apiToken) {
|
|
78
|
+
const authHeader = req.headers.get("Authorization");
|
|
79
|
+
|
|
80
|
+
if (authHeader) {
|
|
81
|
+
// Support Bearer token format
|
|
82
|
+
const match = authHeader.match(/^Bearer\s+(.+)$/i);
|
|
83
|
+
if (match && match[1] === config.apiToken) {
|
|
84
|
+
return { allowed: true };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Support X-API-Key header
|
|
88
|
+
const apiKey = req.headers.get("X-API-Key");
|
|
89
|
+
if (apiKey === config.apiToken) {
|
|
90
|
+
return { allowed: true };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
allowed: false,
|
|
96
|
+
reason: "Invalid or missing API token",
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// No token configured, deny remote access
|
|
101
|
+
return {
|
|
102
|
+
allowed: false,
|
|
103
|
+
reason: "Remote access not configured. Set LOKI_API_TOKEN to enable.",
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Generate a secure random token
|
|
109
|
+
*/
|
|
110
|
+
export function generateToken(): string {
|
|
111
|
+
const bytes = new Uint8Array(32);
|
|
112
|
+
crypto.getRandomValues(bytes);
|
|
113
|
+
return Array.from(bytes)
|
|
114
|
+
.map((b) => b.toString(16).padStart(2, "0"))
|
|
115
|
+
.join("");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get current auth config (for debugging)
|
|
120
|
+
*/
|
|
121
|
+
export function getAuthConfig(): Omit<AuthConfig, "apiToken"> & {
|
|
122
|
+
hasToken: boolean;
|
|
123
|
+
} {
|
|
124
|
+
return {
|
|
125
|
+
allowLocalhost: config.allowLocalhost,
|
|
126
|
+
allowedOrigins: config.allowedOrigins,
|
|
127
|
+
hasToken: !!config.apiToken,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CORS Middleware
|
|
3
|
+
*
|
|
4
|
+
* Handles Cross-Origin Resource Sharing for browser clients.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface CorsConfig {
|
|
8
|
+
allowedOrigins: string[];
|
|
9
|
+
allowedMethods: string[];
|
|
10
|
+
allowedHeaders: string[];
|
|
11
|
+
exposeHeaders: string[];
|
|
12
|
+
maxAge: number;
|
|
13
|
+
credentials: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const defaultConfig: CorsConfig = {
|
|
17
|
+
allowedOrigins: ["*"],
|
|
18
|
+
allowedMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
|
|
19
|
+
allowedHeaders: [
|
|
20
|
+
"Content-Type",
|
|
21
|
+
"Authorization",
|
|
22
|
+
"X-API-Key",
|
|
23
|
+
"X-Request-ID",
|
|
24
|
+
"Accept",
|
|
25
|
+
"Cache-Control",
|
|
26
|
+
],
|
|
27
|
+
exposeHeaders: ["X-Request-ID", "X-Session-ID"],
|
|
28
|
+
maxAge: 86400, // 24 hours
|
|
29
|
+
credentials: true,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
let config = { ...defaultConfig };
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Configure CORS
|
|
36
|
+
*/
|
|
37
|
+
export function configureCors(newConfig: Partial<CorsConfig>): void {
|
|
38
|
+
config = { ...config, ...newConfig };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get CORS headers for a request
|
|
43
|
+
*/
|
|
44
|
+
export function getCorsHeaders(req: Request): Headers {
|
|
45
|
+
const headers = new Headers();
|
|
46
|
+
const origin = req.headers.get("Origin");
|
|
47
|
+
|
|
48
|
+
// Check if origin is allowed
|
|
49
|
+
const allowedOrigin = isOriginAllowed(origin);
|
|
50
|
+
|
|
51
|
+
if (allowedOrigin) {
|
|
52
|
+
headers.set("Access-Control-Allow-Origin", allowedOrigin);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
headers.set(
|
|
56
|
+
"Access-Control-Allow-Methods",
|
|
57
|
+
config.allowedMethods.join(", ")
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
headers.set(
|
|
61
|
+
"Access-Control-Allow-Headers",
|
|
62
|
+
config.allowedHeaders.join(", ")
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
headers.set(
|
|
66
|
+
"Access-Control-Expose-Headers",
|
|
67
|
+
config.exposeHeaders.join(", ")
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
headers.set("Access-Control-Max-Age", config.maxAge.toString());
|
|
71
|
+
|
|
72
|
+
if (config.credentials) {
|
|
73
|
+
headers.set("Access-Control-Allow-Credentials", "true");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return headers;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Check if origin is allowed
|
|
81
|
+
*/
|
|
82
|
+
function isOriginAllowed(origin: string | null): string | null {
|
|
83
|
+
if (!origin) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const allowed of config.allowedOrigins) {
|
|
88
|
+
// Wildcard match all
|
|
89
|
+
if (allowed === "*") {
|
|
90
|
+
return origin;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Exact match
|
|
94
|
+
if (allowed === origin) {
|
|
95
|
+
return origin;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Wildcard pattern (e.g., "http://localhost:*")
|
|
99
|
+
if (allowed.includes("*")) {
|
|
100
|
+
const pattern = allowed
|
|
101
|
+
.replace(/\./g, "\\.")
|
|
102
|
+
.replace(/\*/g, ".*");
|
|
103
|
+
const regex = new RegExp(`^${pattern}$`);
|
|
104
|
+
if (regex.test(origin)) {
|
|
105
|
+
return origin;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* CORS middleware
|
|
115
|
+
*/
|
|
116
|
+
export function corsMiddleware(
|
|
117
|
+
handler: (req: Request) => Promise<Response> | Response
|
|
118
|
+
): (req: Request) => Promise<Response> {
|
|
119
|
+
return async (req: Request): Promise<Response> => {
|
|
120
|
+
const corsHeaders = getCorsHeaders(req);
|
|
121
|
+
|
|
122
|
+
// Handle preflight requests
|
|
123
|
+
if (req.method === "OPTIONS") {
|
|
124
|
+
return new Response(null, {
|
|
125
|
+
status: 204,
|
|
126
|
+
headers: corsHeaders,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Handle actual request
|
|
131
|
+
const response = await handler(req);
|
|
132
|
+
|
|
133
|
+
// Add CORS headers to response
|
|
134
|
+
const newHeaders = new Headers(response.headers);
|
|
135
|
+
for (const [key, value] of corsHeaders) {
|
|
136
|
+
newHeaders.set(key, value);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return new Response(response.body, {
|
|
140
|
+
status: response.status,
|
|
141
|
+
statusText: response.statusText,
|
|
142
|
+
headers: newHeaders,
|
|
143
|
+
});
|
|
144
|
+
};
|
|
145
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Handling Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent error responses and logging.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { ApiError } from "../types/api.ts";
|
|
8
|
+
|
|
9
|
+
// Error codes
|
|
10
|
+
export const ErrorCodes = {
|
|
11
|
+
// Client errors (4xx)
|
|
12
|
+
BAD_REQUEST: "BAD_REQUEST",
|
|
13
|
+
UNAUTHORIZED: "UNAUTHORIZED",
|
|
14
|
+
FORBIDDEN: "FORBIDDEN",
|
|
15
|
+
NOT_FOUND: "NOT_FOUND",
|
|
16
|
+
METHOD_NOT_ALLOWED: "METHOD_NOT_ALLOWED",
|
|
17
|
+
CONFLICT: "CONFLICT",
|
|
18
|
+
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
19
|
+
|
|
20
|
+
// Server errors (5xx)
|
|
21
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
22
|
+
NOT_IMPLEMENTED: "NOT_IMPLEMENTED",
|
|
23
|
+
SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE",
|
|
24
|
+
TIMEOUT: "TIMEOUT",
|
|
25
|
+
|
|
26
|
+
// Loki-specific errors
|
|
27
|
+
SESSION_NOT_FOUND: "SESSION_NOT_FOUND",
|
|
28
|
+
SESSION_ALREADY_RUNNING: "SESSION_ALREADY_RUNNING",
|
|
29
|
+
PROVIDER_NOT_AVAILABLE: "PROVIDER_NOT_AVAILABLE",
|
|
30
|
+
CLI_ERROR: "CLI_ERROR",
|
|
31
|
+
} as const;
|
|
32
|
+
|
|
33
|
+
type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];
|
|
34
|
+
|
|
35
|
+
// HTTP status codes for error codes
|
|
36
|
+
const errorStatusMap: Record<ErrorCode, number> = {
|
|
37
|
+
BAD_REQUEST: 400,
|
|
38
|
+
UNAUTHORIZED: 401,
|
|
39
|
+
FORBIDDEN: 403,
|
|
40
|
+
NOT_FOUND: 404,
|
|
41
|
+
METHOD_NOT_ALLOWED: 405,
|
|
42
|
+
CONFLICT: 409,
|
|
43
|
+
VALIDATION_ERROR: 422,
|
|
44
|
+
INTERNAL_ERROR: 500,
|
|
45
|
+
NOT_IMPLEMENTED: 501,
|
|
46
|
+
SERVICE_UNAVAILABLE: 503,
|
|
47
|
+
TIMEOUT: 504,
|
|
48
|
+
SESSION_NOT_FOUND: 404,
|
|
49
|
+
SESSION_ALREADY_RUNNING: 409,
|
|
50
|
+
PROVIDER_NOT_AVAILABLE: 503,
|
|
51
|
+
CLI_ERROR: 500,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Custom API error class
|
|
56
|
+
*/
|
|
57
|
+
export class LokiApiError extends Error {
|
|
58
|
+
code: ErrorCode;
|
|
59
|
+
status: number;
|
|
60
|
+
details?: Record<string, unknown>;
|
|
61
|
+
|
|
62
|
+
constructor(
|
|
63
|
+
message: string,
|
|
64
|
+
code: ErrorCode,
|
|
65
|
+
details?: Record<string, unknown>
|
|
66
|
+
) {
|
|
67
|
+
super(message);
|
|
68
|
+
this.name = "LokiApiError";
|
|
69
|
+
this.code = code;
|
|
70
|
+
this.status = errorStatusMap[code] || 500;
|
|
71
|
+
this.details = details;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
toJSON(): ApiError {
|
|
75
|
+
return {
|
|
76
|
+
error: this.message,
|
|
77
|
+
code: this.code,
|
|
78
|
+
details: this.details,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
toResponse(): Response {
|
|
83
|
+
return new Response(JSON.stringify(this.toJSON()), {
|
|
84
|
+
status: this.status,
|
|
85
|
+
headers: {
|
|
86
|
+
"Content-Type": "application/json",
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Error middleware wrapper
|
|
94
|
+
*/
|
|
95
|
+
export function errorMiddleware(
|
|
96
|
+
handler: (req: Request) => Promise<Response> | Response
|
|
97
|
+
): (req: Request) => Promise<Response> {
|
|
98
|
+
return async (req: Request): Promise<Response> => {
|
|
99
|
+
try {
|
|
100
|
+
return await handler(req);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
return handleError(err, req);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Handle an error and return appropriate response
|
|
109
|
+
*/
|
|
110
|
+
export function handleError(err: unknown, req?: Request): Response {
|
|
111
|
+
// Log error for debugging
|
|
112
|
+
const requestInfo = req
|
|
113
|
+
? `${req.method} ${new URL(req.url).pathname}`
|
|
114
|
+
: "unknown request";
|
|
115
|
+
|
|
116
|
+
console.error(`Error handling ${requestInfo}:`, err);
|
|
117
|
+
|
|
118
|
+
// Handle known API errors
|
|
119
|
+
if (err instanceof LokiApiError) {
|
|
120
|
+
return err.toResponse();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Handle Deno-specific errors
|
|
124
|
+
if (err instanceof Deno.errors.NotFound) {
|
|
125
|
+
return new LokiApiError("Resource not found", ErrorCodes.NOT_FOUND).toResponse();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (err instanceof Deno.errors.PermissionDenied) {
|
|
129
|
+
return new LokiApiError(
|
|
130
|
+
"Permission denied",
|
|
131
|
+
ErrorCodes.FORBIDDEN
|
|
132
|
+
).toResponse();
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Handle JSON parsing errors
|
|
136
|
+
if (err instanceof SyntaxError && err.message.includes("JSON")) {
|
|
137
|
+
return new LokiApiError(
|
|
138
|
+
"Invalid JSON in request body",
|
|
139
|
+
ErrorCodes.BAD_REQUEST
|
|
140
|
+
).toResponse();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Handle timeout errors
|
|
144
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
145
|
+
return new LokiApiError(
|
|
146
|
+
"Request timed out",
|
|
147
|
+
ErrorCodes.TIMEOUT
|
|
148
|
+
).toResponse();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Default to internal error
|
|
152
|
+
const message =
|
|
153
|
+
err instanceof Error ? err.message : "An unexpected error occurred";
|
|
154
|
+
|
|
155
|
+
return new LokiApiError(
|
|
156
|
+
message,
|
|
157
|
+
ErrorCodes.INTERNAL_ERROR,
|
|
158
|
+
Deno.env.get("LOKI_DEBUG") ? { stack: (err as Error).stack } : undefined
|
|
159
|
+
).toResponse();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Validate request body against expected fields
|
|
164
|
+
*/
|
|
165
|
+
export function validateBody<T extends Record<string, unknown>>(
|
|
166
|
+
body: unknown,
|
|
167
|
+
required: (keyof T)[],
|
|
168
|
+
optional: (keyof T)[] = []
|
|
169
|
+
): T {
|
|
170
|
+
if (!body || typeof body !== "object") {
|
|
171
|
+
throw new LokiApiError(
|
|
172
|
+
"Request body must be a JSON object",
|
|
173
|
+
ErrorCodes.BAD_REQUEST
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const obj = body as Record<string, unknown>;
|
|
178
|
+
|
|
179
|
+
// Check required fields
|
|
180
|
+
for (const field of required) {
|
|
181
|
+
if (!(field in obj)) {
|
|
182
|
+
throw new LokiApiError(
|
|
183
|
+
`Missing required field: ${String(field)}`,
|
|
184
|
+
ErrorCodes.VALIDATION_ERROR,
|
|
185
|
+
{ field: String(field) }
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Check for unknown fields
|
|
191
|
+
const allowedFields = new Set([...required, ...optional]);
|
|
192
|
+
for (const field of Object.keys(obj)) {
|
|
193
|
+
if (!allowedFields.has(field)) {
|
|
194
|
+
throw new LokiApiError(
|
|
195
|
+
`Unknown field: ${field}`,
|
|
196
|
+
ErrorCodes.VALIDATION_ERROR,
|
|
197
|
+
{ field }
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return obj as T;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Create a simple error response
|
|
207
|
+
*/
|
|
208
|
+
export function errorResponse(
|
|
209
|
+
message: string,
|
|
210
|
+
code: ErrorCode = ErrorCodes.INTERNAL_ERROR,
|
|
211
|
+
details?: Record<string, unknown>
|
|
212
|
+
): Response {
|
|
213
|
+
return new LokiApiError(message, code, details).toResponse();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Create a success response
|
|
218
|
+
*/
|
|
219
|
+
export function successResponse<T>(data: T, status = 200): Response {
|
|
220
|
+
return new Response(JSON.stringify(data), {
|
|
221
|
+
status,
|
|
222
|
+
headers: {
|
|
223
|
+
"Content-Type": "application/json",
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
}
|
package/api/mod.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loki Mode API Module
|
|
3
|
+
*
|
|
4
|
+
* Exports for programmatic use
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Types
|
|
8
|
+
export type {
|
|
9
|
+
Session,
|
|
10
|
+
SessionStatus,
|
|
11
|
+
Phase,
|
|
12
|
+
Task,
|
|
13
|
+
TaskStatus,
|
|
14
|
+
StartSessionRequest,
|
|
15
|
+
StartSessionResponse,
|
|
16
|
+
SessionStatusResponse,
|
|
17
|
+
TaskSummary,
|
|
18
|
+
AgentSummary,
|
|
19
|
+
InjectInputRequest,
|
|
20
|
+
ApiError,
|
|
21
|
+
HealthResponse,
|
|
22
|
+
} from "./types/api.ts";
|
|
23
|
+
|
|
24
|
+
export type {
|
|
25
|
+
SSEEvent,
|
|
26
|
+
EventType,
|
|
27
|
+
EventFilter,
|
|
28
|
+
SessionEventData,
|
|
29
|
+
PhaseEventData,
|
|
30
|
+
TaskEventData,
|
|
31
|
+
AgentEventData,
|
|
32
|
+
LogEventData,
|
|
33
|
+
MetricsEventData,
|
|
34
|
+
InputRequestedEventData,
|
|
35
|
+
HeartbeatEventData,
|
|
36
|
+
AnySSEEvent,
|
|
37
|
+
} from "./types/events.ts";
|
|
38
|
+
|
|
39
|
+
// Services
|
|
40
|
+
export { eventBus } from "./services/event-bus.ts";
|
|
41
|
+
export { cliBridge } from "./services/cli-bridge.ts";
|
|
42
|
+
export { stateWatcher } from "./services/state-watcher.ts";
|
|
43
|
+
|
|
44
|
+
// Middleware
|
|
45
|
+
export { authMiddleware, configureAuth, generateToken } from "./middleware/auth.ts";
|
|
46
|
+
export { corsMiddleware, configureCors } from "./middleware/cors.ts";
|
|
47
|
+
export {
|
|
48
|
+
errorMiddleware,
|
|
49
|
+
LokiApiError,
|
|
50
|
+
ErrorCodes,
|
|
51
|
+
handleError,
|
|
52
|
+
validateBody,
|
|
53
|
+
errorResponse,
|
|
54
|
+
successResponse,
|
|
55
|
+
} from "./middleware/error.ts";
|
|
56
|
+
|
|
57
|
+
// Server
|
|
58
|
+
export { createHandler, routeRequest, parseArgs } from "./server.ts";
|