@rcrsr/rill-agent-foundry 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 +87 -32
- package/dist/index.js +320 -180
- package/package.json +14 -11
package/dist/index.d.ts
CHANGED
|
@@ -250,12 +250,23 @@ export interface SessionManager {
|
|
|
250
250
|
*/
|
|
251
251
|
activeCount(): number;
|
|
252
252
|
}
|
|
253
|
+
/**
|
|
254
|
+
* Options accepted by createSessionManager.
|
|
255
|
+
*/
|
|
256
|
+
export interface SessionManagerOptions {
|
|
257
|
+
/**
|
|
258
|
+
* Maximum number of concurrently open sessions. Overrides the
|
|
259
|
+
* MAX_CONCURRENT_SESSIONS env var when provided.
|
|
260
|
+
*/
|
|
261
|
+
readonly maxConcurrentSessions?: number | undefined;
|
|
262
|
+
}
|
|
253
263
|
/**
|
|
254
264
|
* Create a bounded pool manager for concurrent rill sessions.
|
|
255
265
|
*
|
|
256
|
-
* Max capacity
|
|
266
|
+
* Max capacity resolution order: options.maxConcurrentSessions, then the
|
|
267
|
+
* MAX_CONCURRENT_SESSIONS env var, then DEFAULT_MAX_SESSIONS.
|
|
257
268
|
*/
|
|
258
|
-
export declare function createSessionManager(): SessionManager;
|
|
269
|
+
export declare function createSessionManager(options?: SessionManagerOptions): SessionManager;
|
|
259
270
|
export interface HandlerDescription {
|
|
260
271
|
readonly name: string;
|
|
261
272
|
readonly description?: string | undefined;
|
|
@@ -266,6 +277,17 @@ export interface HandlerDescription {
|
|
|
266
277
|
readonly description?: string | undefined;
|
|
267
278
|
readonly defaultValue?: unknown;
|
|
268
279
|
}>;
|
|
280
|
+
/**
|
|
281
|
+
* Handler return type annotation, formatted with the same grammar as
|
|
282
|
+
* parameter type strings (e.g. `stream(dict(content: string)):string`).
|
|
283
|
+
* Undefined when the handler closure has no `:T` annotation, or when the
|
|
284
|
+
* rill-build emitting the handler is too old to expose the field.
|
|
285
|
+
*/
|
|
286
|
+
readonly returnType?: string | undefined;
|
|
287
|
+
}
|
|
288
|
+
export interface InitContext {
|
|
289
|
+
readonly globalVars?: Record<string, string> | undefined;
|
|
290
|
+
readonly ahiResolver?: ((agentName: string, request: RunRequest) => Promise<RunResponse>) | undefined;
|
|
269
291
|
}
|
|
270
292
|
export interface RunRequest {
|
|
271
293
|
readonly params?: Record<string, unknown> | undefined;
|
|
@@ -275,13 +297,25 @@ export interface RunContext {
|
|
|
275
297
|
readonly sessionVars?: Record<string, string> | undefined;
|
|
276
298
|
readonly onLog?: ((message: string) => void) | undefined;
|
|
277
299
|
readonly onChunk?: ((chunk: unknown) => Promise<void>) | undefined;
|
|
300
|
+
readonly signal?: AbortSignal | undefined;
|
|
278
301
|
}
|
|
279
302
|
export interface RunResponse {
|
|
280
303
|
readonly state: "completed" | "error";
|
|
281
304
|
readonly result: unknown;
|
|
282
305
|
readonly streamed?: boolean | undefined;
|
|
283
306
|
}
|
|
307
|
+
export interface AgentHandler {
|
|
308
|
+
describe(): HandlerDescription | null;
|
|
309
|
+
init(context?: InitContext): Promise<void>;
|
|
310
|
+
execute(request?: RunRequest, context?: RunContext): Promise<RunResponse>;
|
|
311
|
+
dispose(): Promise<void>;
|
|
312
|
+
}
|
|
313
|
+
export interface AgentManifest {
|
|
314
|
+
readonly defaultAgent: string;
|
|
315
|
+
readonly agents: ReadonlyMap<string, AgentHandler>;
|
|
316
|
+
}
|
|
284
317
|
export interface AgentRouter {
|
|
318
|
+
readonly manifest: AgentManifest;
|
|
285
319
|
run(agentName: string, request: RunRequest, context?: RunContext): Promise<RunResponse>;
|
|
286
320
|
describe(agentName: string): HandlerDescription | null;
|
|
287
321
|
agents(): string[];
|
|
@@ -293,50 +327,30 @@ export interface AgentRouter {
|
|
|
293
327
|
*
|
|
294
328
|
* State mapping: 'completed' → 'completed', 'error' → 'failed'.
|
|
295
329
|
* Result is coerced to string; errors are encoded in the response body.
|
|
330
|
+
*
|
|
331
|
+
* When `debugErrors` is false (default), a failed-state error message is
|
|
332
|
+
* redacted with the same generic message `buildErrorResponse` uses for
|
|
333
|
+
* SERVER_ERROR, so internal detail does not leak through the sync response
|
|
334
|
+
* path when it is suppressed everywhere else. The same redaction is applied
|
|
335
|
+
* to the output message `text`, since it otherwise carries the same raw
|
|
336
|
+
* failure detail. When true, the raw message is passed through verbatim.
|
|
296
337
|
*/
|
|
297
|
-
export declare function buildSyncResponse(result: RunResponse, responseId: string): FoundryResponse;
|
|
338
|
+
export declare function buildSyncResponse(result: RunResponse, responseId: string, debugErrors?: boolean): FoundryResponse;
|
|
298
339
|
/**
|
|
299
340
|
* Build a non-streaming JSON error response body.
|
|
300
341
|
*
|
|
301
342
|
* When debug is true, the original message is passed through verbatim.
|
|
302
343
|
* When debug is false or absent, a generic message is returned based on
|
|
303
|
-
* the error code
|
|
344
|
+
* the error code (FOUNDRY_AGENT_DEBUG_ERRORS=false behavior).
|
|
304
345
|
*/
|
|
305
346
|
export declare function buildErrorResponse(code: string, message: string, debug?: boolean): ErrorResponse;
|
|
306
347
|
/**
|
|
307
348
|
* Generate Foundry tool definitions from the default agent's handler descriptions.
|
|
308
349
|
*
|
|
309
|
-
* Only the default agent's handlers are exposed
|
|
350
|
+
* Only the default agent's handlers are exposed.
|
|
310
351
|
* Returns an empty array when describe() returns null.
|
|
311
352
|
*/
|
|
312
353
|
export declare function generateToolDefinitions(router: AgentRouter): FoundryToolDefinition[];
|
|
313
|
-
export interface StreamOptions {
|
|
314
|
-
readonly onError?: ((err: unknown) => void) | undefined;
|
|
315
|
-
/** IdGenerator scoped to the request for correlated message IDs. */
|
|
316
|
-
readonly idGenerator?: IdGenerator | undefined;
|
|
317
|
-
/** Session ID echoed back in x-agent-session-id response header. */
|
|
318
|
-
readonly sessionId?: string | undefined;
|
|
319
|
-
/** Invocation ID echoed back in x-agent-invocation-id response header. */
|
|
320
|
-
readonly invocationId?: string | undefined;
|
|
321
|
-
/** Pre-built x-aml-foundry-agents-metadata JSON string. */
|
|
322
|
-
readonly metadataHeader?: string | undefined;
|
|
323
|
-
/**
|
|
324
|
-
* When true, raw error messages are forwarded to clients. When false
|
|
325
|
-
* (default), error events emit a generic message to avoid leaking
|
|
326
|
-
* internal details. Mirrors the harness `debugErrors` option used by
|
|
327
|
-
* `buildErrorResponse`.
|
|
328
|
-
*/
|
|
329
|
-
readonly debugErrors?: boolean | undefined;
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Stream a Foundry Responses lifecycle via SSE.
|
|
333
|
-
* Delegates to createFoundryStreamResponse for all paths.
|
|
334
|
-
*/
|
|
335
|
-
export declare function streamFoundryResponse(_c: unknown, responseId: string, resultStream: AsyncIterable<{
|
|
336
|
-
value?: unknown;
|
|
337
|
-
}>, options: StreamOptions & {
|
|
338
|
-
resultPromise?: Promise<string>;
|
|
339
|
-
}): Response;
|
|
340
354
|
export interface ConversationsClient {
|
|
341
355
|
saveItems(conversationId: string, items: ReadonlyArray<unknown>): Promise<void>;
|
|
342
356
|
}
|
|
@@ -394,5 +408,46 @@ export interface FoundryHarness {
|
|
|
394
408
|
* GET /metrics — FoundryMetrics JSON
|
|
395
409
|
*/
|
|
396
410
|
export declare function createFoundryHarness(router: AgentRouter, options?: FoundryHarnessOptions): FoundryHarness;
|
|
411
|
+
export interface RillHarnessLogger {
|
|
412
|
+
info(...args: unknown[]): void;
|
|
413
|
+
warn(...args: unknown[]): void;
|
|
414
|
+
error(...args: unknown[]): void;
|
|
415
|
+
}
|
|
416
|
+
export interface RillCompiledPackage {
|
|
417
|
+
readonly mount: string;
|
|
418
|
+
readonly buildOutput: {
|
|
419
|
+
readonly outputPath: string;
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
export interface RillServeContext {
|
|
423
|
+
readonly config: Record<string, unknown>;
|
|
424
|
+
readonly logger: RillHarnessLogger;
|
|
425
|
+
readonly packages: readonly RillCompiledPackage[];
|
|
426
|
+
readonly requestedMount: string | undefined;
|
|
427
|
+
readonly args: readonly string[];
|
|
428
|
+
readonly onShutdown: (handler: () => void | Promise<void>) => void;
|
|
429
|
+
readonly onSourceChange: (handler: () => void | Promise<void>) => void;
|
|
430
|
+
}
|
|
431
|
+
export interface RillPostBuildContext {
|
|
432
|
+
readonly outputDir: string;
|
|
433
|
+
readonly packages: readonly RillCompiledPackage[];
|
|
434
|
+
readonly logger: RillHarnessLogger;
|
|
435
|
+
}
|
|
436
|
+
export interface RillHarness {
|
|
437
|
+
readonly name: string;
|
|
438
|
+
readonly postBuild?: (ctx: RillPostBuildContext) => Promise<void>;
|
|
439
|
+
readonly serve?: (ctx: RillServeContext) => Promise<number>;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Default export consumed by the rill CLI (`rill install --replace`,
|
|
443
|
+
* `rill run`) when this package is declared as a bundle harness. `serve`
|
|
444
|
+
* assembles a router from the bundle's compiled packages and hosts it over the
|
|
445
|
+
* Foundry Responses harness on `config.port` (default 3000).
|
|
446
|
+
*/
|
|
447
|
+
declare const harness: RillHarness;
|
|
448
|
+
|
|
449
|
+
export {
|
|
450
|
+
harness as default,
|
|
451
|
+
};
|
|
397
452
|
|
|
398
453
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -80,6 +80,38 @@ function findLastUserText(items) {
|
|
|
80
80
|
}
|
|
81
81
|
return text;
|
|
82
82
|
}
|
|
83
|
+
function isContentPart(value) {
|
|
84
|
+
return typeof value === "object" && value !== null && value.type === "input_text" && typeof value.text === "string";
|
|
85
|
+
}
|
|
86
|
+
function isValidContent(content) {
|
|
87
|
+
if (typeof content === "string") {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return Array.isArray(content) && content.every(isContentPart);
|
|
91
|
+
}
|
|
92
|
+
function isInputItem(item) {
|
|
93
|
+
if (typeof item !== "object" || item === null) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
const type = item.type;
|
|
97
|
+
if (type === "message") {
|
|
98
|
+
const role = item.role;
|
|
99
|
+
const content = item.content;
|
|
100
|
+
return typeof role === "string" && isValidContent(content);
|
|
101
|
+
}
|
|
102
|
+
if (type === "function_call_output") {
|
|
103
|
+
const callId = item.call_id;
|
|
104
|
+
const output = item.output;
|
|
105
|
+
return typeof callId === "string" && typeof output === "string";
|
|
106
|
+
}
|
|
107
|
+
if (type === "function_call") {
|
|
108
|
+
const callId = item.call_id;
|
|
109
|
+
const name = item.name;
|
|
110
|
+
const args = item.arguments;
|
|
111
|
+
return typeof callId === "string" && typeof name === "string" && typeof args === "string";
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
83
115
|
function extractFunctionCallOutputs(items) {
|
|
84
116
|
const callMap = /* @__PURE__ */ new Map();
|
|
85
117
|
for (const item of items) {
|
|
@@ -120,6 +152,11 @@ function extractInput(input) {
|
|
|
120
152
|
if (!Array.isArray(input) || input.length === 0) {
|
|
121
153
|
throw new InputError("Missing required field: input");
|
|
122
154
|
}
|
|
155
|
+
for (const item of input) {
|
|
156
|
+
if (!isInputItem(item)) {
|
|
157
|
+
throw new InputError("Invalid input item shape");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
123
160
|
const items = input;
|
|
124
161
|
const hasFunctionCallOutput = items.some(
|
|
125
162
|
(item) => item.type === "function_call_output"
|
|
@@ -136,25 +173,47 @@ function extractInput(input) {
|
|
|
136
173
|
|
|
137
174
|
// src/session.ts
|
|
138
175
|
var DEFAULT_MAX_SESSIONS = 10;
|
|
139
|
-
function createSessionManager() {
|
|
176
|
+
function createSessionManager(options) {
|
|
140
177
|
const raw = process.env["MAX_CONCURRENT_SESSIONS"];
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
const
|
|
178
|
+
const parsedEnv = raw !== void 0 ? parseInt(raw, 10) : NaN;
|
|
179
|
+
const envMax = Number.isFinite(parsedEnv) && parsedEnv > 0 ? parsedEnv : DEFAULT_MAX_SESSIONS;
|
|
180
|
+
const max = options?.maxConcurrentSessions ?? envMax;
|
|
181
|
+
const activeTokens = /* @__PURE__ */ new Map();
|
|
182
|
+
const tokensBySession = /* @__PURE__ */ new Map();
|
|
144
183
|
return {
|
|
145
184
|
acquire(conversationId) {
|
|
146
|
-
if (
|
|
185
|
+
if (activeTokens.size >= max) {
|
|
147
186
|
throw new CapacityError(max);
|
|
148
187
|
}
|
|
149
188
|
const sessionId = conversationId ?? generateId("sess_");
|
|
150
|
-
|
|
189
|
+
const token = generateId("tok_");
|
|
190
|
+
activeTokens.set(token, sessionId);
|
|
191
|
+
const tokens = tokensBySession.get(sessionId);
|
|
192
|
+
if (tokens === void 0) {
|
|
193
|
+
tokensBySession.set(sessionId, /* @__PURE__ */ new Set([token]));
|
|
194
|
+
} else {
|
|
195
|
+
tokens.add(token);
|
|
196
|
+
}
|
|
151
197
|
return sessionId;
|
|
152
198
|
},
|
|
153
199
|
release(sessionId) {
|
|
154
|
-
|
|
200
|
+
const tokens = tokensBySession.get(sessionId);
|
|
201
|
+
if (tokens === void 0) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const iterResult = tokens.values().next();
|
|
205
|
+
if (iterResult.done) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const token = iterResult.value;
|
|
209
|
+
tokens.delete(token);
|
|
210
|
+
if (tokens.size === 0) {
|
|
211
|
+
tokensBySession.delete(sessionId);
|
|
212
|
+
}
|
|
213
|
+
activeTokens.delete(token);
|
|
155
214
|
},
|
|
156
215
|
activeCount() {
|
|
157
|
-
return
|
|
216
|
+
return activeTokens.size;
|
|
158
217
|
}
|
|
159
218
|
};
|
|
160
219
|
}
|
|
@@ -172,10 +231,12 @@ function coerceResult(result) {
|
|
|
172
231
|
}
|
|
173
232
|
return JSON.stringify(result);
|
|
174
233
|
}
|
|
175
|
-
function buildSyncResponse(result, responseId) {
|
|
234
|
+
function buildSyncResponse(result, responseId, debugErrors = false) {
|
|
176
235
|
const status = result.state === "completed" ? "completed" : "failed";
|
|
177
|
-
const
|
|
236
|
+
const rawText = coerceResult(result.result);
|
|
178
237
|
const msgId = generateId("msg_");
|
|
238
|
+
const errorMessage = debugErrors ? rawText : GENERIC_MESSAGES["SERVER_ERROR"] ?? FALLBACK_MESSAGE;
|
|
239
|
+
const text = status === "failed" && !debugErrors ? errorMessage : rawText;
|
|
179
240
|
return {
|
|
180
241
|
id: responseId,
|
|
181
242
|
object: "response",
|
|
@@ -196,7 +257,7 @@ function buildSyncResponse(result, responseId) {
|
|
|
196
257
|
]
|
|
197
258
|
}
|
|
198
259
|
],
|
|
199
|
-
error: status === "failed" ? { code: "SERVER_ERROR", message:
|
|
260
|
+
error: status === "failed" ? { code: "SERVER_ERROR", message: errorMessage } : null,
|
|
200
261
|
metadata: {},
|
|
201
262
|
temperature: 0,
|
|
202
263
|
top_p: 0,
|
|
@@ -262,7 +323,184 @@ function generateToolDefinitions(router) {
|
|
|
262
323
|
];
|
|
263
324
|
}
|
|
264
325
|
|
|
326
|
+
// src/conversations.ts
|
|
327
|
+
var PersistenceError = class extends Error {
|
|
328
|
+
statusCode;
|
|
329
|
+
constructor(message) {
|
|
330
|
+
super(message);
|
|
331
|
+
this.name = "PersistenceError";
|
|
332
|
+
this.statusCode = 502;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
var API_VERSION = "2025-11-15-preview";
|
|
336
|
+
var TOKEN_SCOPE = "https://ai.azure.com/.default";
|
|
337
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
338
|
+
function createConversationsClient(projectEndpoint, credential) {
|
|
339
|
+
return {
|
|
340
|
+
async saveItems(conversationId, items) {
|
|
341
|
+
let token;
|
|
342
|
+
try {
|
|
343
|
+
const accessToken = await credential.getToken(TOKEN_SCOPE);
|
|
344
|
+
if (accessToken === null) {
|
|
345
|
+
throw new Error(`Failed to acquire token for scope ${TOKEN_SCOPE}`);
|
|
346
|
+
}
|
|
347
|
+
token = accessToken.token;
|
|
348
|
+
} catch (err) {
|
|
349
|
+
if (err instanceof CredentialError) {
|
|
350
|
+
throw err;
|
|
351
|
+
}
|
|
352
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
353
|
+
throw new CredentialError(message);
|
|
354
|
+
}
|
|
355
|
+
const encodedConversationId = encodeURIComponent(conversationId);
|
|
356
|
+
const url = new URL(
|
|
357
|
+
`${projectEndpoint}/openai/conversations/${encodedConversationId}/items`
|
|
358
|
+
);
|
|
359
|
+
url.searchParams.set("api-version", API_VERSION);
|
|
360
|
+
const controller = new AbortController();
|
|
361
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
362
|
+
try {
|
|
363
|
+
let response;
|
|
364
|
+
try {
|
|
365
|
+
response = await fetch(url.toString(), {
|
|
366
|
+
method: "POST",
|
|
367
|
+
headers: {
|
|
368
|
+
"Content-Type": "application/json",
|
|
369
|
+
Authorization: `Bearer ${token}`
|
|
370
|
+
},
|
|
371
|
+
body: JSON.stringify({ items }),
|
|
372
|
+
signal: controller.signal
|
|
373
|
+
});
|
|
374
|
+
} catch (err) {
|
|
375
|
+
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
376
|
+
throw new PersistenceError(
|
|
377
|
+
isTimeout ? "Conversations API timeout" : `Conversations API error: ${err instanceof Error ? err.message : String(err)}`
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
if (!response.ok) {
|
|
381
|
+
throw new PersistenceError(
|
|
382
|
+
`Conversations API error: ${response.status}`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
} finally {
|
|
386
|
+
clearTimeout(timer);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// src/telemetry.ts
|
|
393
|
+
import { trace } from "@opentelemetry/api";
|
|
394
|
+
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
395
|
+
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
396
|
+
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
397
|
+
var sdk;
|
|
398
|
+
function initTelemetry(options) {
|
|
399
|
+
if (!process.env["OTEL_EXPORTER_OTLP_ENDPOINT"]) return;
|
|
400
|
+
if (sdk !== void 0) return;
|
|
401
|
+
const resource = resourceFromAttributes({
|
|
402
|
+
"service.name": options?.agentName ?? "rill-foundry-harness",
|
|
403
|
+
"service.version": options?.agentVersion ?? "0.0.0"
|
|
404
|
+
});
|
|
405
|
+
const traceExporter = new OTLPTraceExporter();
|
|
406
|
+
sdk = new NodeSDK({ resource, traceExporter });
|
|
407
|
+
sdk.start();
|
|
408
|
+
}
|
|
409
|
+
function getTracer() {
|
|
410
|
+
return trace.getTracer("rill-foundry-harness");
|
|
411
|
+
}
|
|
412
|
+
async function shutdownTelemetry() {
|
|
413
|
+
if (sdk === void 0) return;
|
|
414
|
+
const instance = sdk;
|
|
415
|
+
sdk = void 0;
|
|
416
|
+
await instance.shutdown();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// src/harness.ts
|
|
420
|
+
import "hono";
|
|
421
|
+
import { SpanStatusCode } from "@opentelemetry/api";
|
|
422
|
+
import { DefaultAzureCredential } from "@azure/identity";
|
|
423
|
+
import { validateParams, routerErrorToStatus as routerErrorToStatus2 } from "@rcrsr/rill-agent";
|
|
424
|
+
|
|
425
|
+
// ../../shared/hono-kit/src/index.ts
|
|
426
|
+
import { existsSync } from "fs";
|
|
427
|
+
import path from "path";
|
|
428
|
+
import { Hono } from "hono";
|
|
429
|
+
import { serve } from "@hono/node-server";
|
|
430
|
+
function assertJsonObject(parsed) {
|
|
431
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
432
|
+
throw new Error("Request body must be a JSON object");
|
|
433
|
+
}
|
|
434
|
+
return parsed;
|
|
435
|
+
}
|
|
436
|
+
function createHarnessLifecycle(options) {
|
|
437
|
+
const app = new Hono();
|
|
438
|
+
let server;
|
|
439
|
+
async function listen(port) {
|
|
440
|
+
if (server !== void 0) {
|
|
441
|
+
throw new Error("Server is already listening");
|
|
442
|
+
}
|
|
443
|
+
return new Promise((resolve, reject) => {
|
|
444
|
+
const started = serve({ fetch: app.fetch, port }, () => {
|
|
445
|
+
started.off("error", onError);
|
|
446
|
+
options?.serverTweaks?.(started);
|
|
447
|
+
resolve();
|
|
448
|
+
});
|
|
449
|
+
function onError(err) {
|
|
450
|
+
server = void 0;
|
|
451
|
+
reject(err);
|
|
452
|
+
}
|
|
453
|
+
started.once("error", onError);
|
|
454
|
+
server = started;
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
async function close() {
|
|
458
|
+
if (server === void 0) return;
|
|
459
|
+
const current = server;
|
|
460
|
+
server = void 0;
|
|
461
|
+
if ("closeAllConnections" in current) {
|
|
462
|
+
current.closeAllConnections();
|
|
463
|
+
}
|
|
464
|
+
await new Promise((resolve, reject) => {
|
|
465
|
+
current.close((err) => {
|
|
466
|
+
if (err) reject(err);
|
|
467
|
+
else resolve();
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
return { app, listen, close };
|
|
472
|
+
}
|
|
473
|
+
function compiledPackageEntries(ctx) {
|
|
474
|
+
return ctx.packages.map((p) => ({
|
|
475
|
+
name: p.mount,
|
|
476
|
+
dir: p.buildOutput.outputPath
|
|
477
|
+
}));
|
|
478
|
+
}
|
|
479
|
+
function readHarnessPort(config, fallback) {
|
|
480
|
+
const p = config["port"];
|
|
481
|
+
if (typeof p === "number" && Number.isInteger(p)) return p;
|
|
482
|
+
if (typeof p === "string" && /^\d+$/.test(p)) return Number(p);
|
|
483
|
+
return fallback;
|
|
484
|
+
}
|
|
485
|
+
function assertCompiledHandlers(ctx) {
|
|
486
|
+
for (const pkg of ctx.packages) {
|
|
487
|
+
const handlerPath = path.join(pkg.buildOutput.outputPath, "handler.js");
|
|
488
|
+
if (!existsSync(handlerPath)) {
|
|
489
|
+
throw new Error(`missing handler file: ${handlerPath}`);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
async function runRillServe(ctx, start) {
|
|
494
|
+
const handle = await start(compiledPackageEntries(ctx));
|
|
495
|
+
ctx.onShutdown(async () => {
|
|
496
|
+
await handle.close();
|
|
497
|
+
});
|
|
498
|
+
return new Promise(() => {
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
|
|
265
502
|
// src/stream.ts
|
|
503
|
+
import { routerErrorToStatus } from "@rcrsr/rill-agent";
|
|
266
504
|
var REDACTED_ERROR_MESSAGE = "Internal server error";
|
|
267
505
|
var encoder = new TextEncoder();
|
|
268
506
|
function sseChunk(event, data) {
|
|
@@ -321,6 +559,7 @@ function createFoundryStreamResponse(responseId, options) {
|
|
|
321
559
|
}
|
|
322
560
|
}
|
|
323
561
|
function emitCompletion(controller, fullText) {
|
|
562
|
+
if (closed) return;
|
|
324
563
|
controller.enqueue(
|
|
325
564
|
ev("response.output_text.done", {
|
|
326
565
|
type: "response.output_text.done",
|
|
@@ -345,8 +584,10 @@ function createFoundryStreamResponse(responseId, options) {
|
|
|
345
584
|
function emitError(controller, err) {
|
|
346
585
|
clearKeepAlive();
|
|
347
586
|
options.onError?.(err);
|
|
587
|
+
if (closed) return;
|
|
348
588
|
const rawMessage = err instanceof Error ? err.message : String(err);
|
|
349
589
|
const message = options.debugErrors === true ? rawMessage : REDACTED_ERROR_MESSAGE;
|
|
590
|
+
const code = routerErrorToStatus(err) === 404 ? "NOT_FOUND" : "SERVER_ERROR";
|
|
350
591
|
controller.enqueue(
|
|
351
592
|
encoder.encode(
|
|
352
593
|
sseChunk(
|
|
@@ -354,7 +595,7 @@ function createFoundryStreamResponse(responseId, options) {
|
|
|
354
595
|
JSON.stringify({
|
|
355
596
|
type: "error",
|
|
356
597
|
sequence_number: seq++,
|
|
357
|
-
code
|
|
598
|
+
code,
|
|
358
599
|
message,
|
|
359
600
|
param: ""
|
|
360
601
|
})
|
|
@@ -410,152 +651,6 @@ function createFoundryStreamResponse(responseId, options) {
|
|
|
410
651
|
headers: sseHeaders(options)
|
|
411
652
|
});
|
|
412
653
|
}
|
|
413
|
-
function streamFoundryResponse(_c, responseId, resultStream, options) {
|
|
414
|
-
const resultPromise = options.resultPromise ?? (async () => {
|
|
415
|
-
let text = "";
|
|
416
|
-
for await (const chunk of resultStream) {
|
|
417
|
-
if (chunk.value !== null && chunk.value !== void 0) {
|
|
418
|
-
text += typeof chunk.value === "string" ? chunk.value : JSON.stringify(chunk.value);
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
return text;
|
|
422
|
-
})();
|
|
423
|
-
return createFoundryStreamResponse(responseId, {
|
|
424
|
-
...options,
|
|
425
|
-
resultPromise
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// src/conversations.ts
|
|
430
|
-
var PersistenceError = class extends Error {
|
|
431
|
-
statusCode;
|
|
432
|
-
constructor(message) {
|
|
433
|
-
super(message);
|
|
434
|
-
this.name = "PersistenceError";
|
|
435
|
-
this.statusCode = 502;
|
|
436
|
-
}
|
|
437
|
-
};
|
|
438
|
-
var API_VERSION = "2025-11-15-preview";
|
|
439
|
-
var TOKEN_SCOPE = "https://ai.azure.com/.default";
|
|
440
|
-
var REQUEST_TIMEOUT_MS = 3e4;
|
|
441
|
-
function createConversationsClient(projectEndpoint, credential) {
|
|
442
|
-
return {
|
|
443
|
-
async saveItems(conversationId, items) {
|
|
444
|
-
let token;
|
|
445
|
-
try {
|
|
446
|
-
const accessToken = await credential.getToken(TOKEN_SCOPE);
|
|
447
|
-
if (accessToken === null) {
|
|
448
|
-
throw new Error(`Failed to acquire token for scope ${TOKEN_SCOPE}`);
|
|
449
|
-
}
|
|
450
|
-
token = accessToken.token;
|
|
451
|
-
} catch (err) {
|
|
452
|
-
if (err instanceof CredentialError) {
|
|
453
|
-
throw err;
|
|
454
|
-
}
|
|
455
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
456
|
-
throw new CredentialError(message);
|
|
457
|
-
}
|
|
458
|
-
const encodedConversationId = encodeURIComponent(conversationId);
|
|
459
|
-
const url = new URL(
|
|
460
|
-
`${projectEndpoint}/openai/conversations/${encodedConversationId}/items`
|
|
461
|
-
);
|
|
462
|
-
url.searchParams.set("api-version", API_VERSION);
|
|
463
|
-
const controller = new AbortController();
|
|
464
|
-
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
465
|
-
try {
|
|
466
|
-
let response;
|
|
467
|
-
try {
|
|
468
|
-
response = await fetch(url.toString(), {
|
|
469
|
-
method: "POST",
|
|
470
|
-
headers: {
|
|
471
|
-
"Content-Type": "application/json",
|
|
472
|
-
Authorization: `Bearer ${token}`
|
|
473
|
-
},
|
|
474
|
-
body: JSON.stringify({ items }),
|
|
475
|
-
signal: controller.signal
|
|
476
|
-
});
|
|
477
|
-
} catch (err) {
|
|
478
|
-
const isTimeout = err instanceof Error && err.name === "AbortError";
|
|
479
|
-
throw new PersistenceError(
|
|
480
|
-
isTimeout ? "Conversations API timeout" : `Conversations API error: ${err instanceof Error ? err.message : String(err)}`
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
|
-
if (!response.ok) {
|
|
484
|
-
throw new PersistenceError(
|
|
485
|
-
`Conversations API error: ${response.status}`
|
|
486
|
-
);
|
|
487
|
-
}
|
|
488
|
-
} finally {
|
|
489
|
-
clearTimeout(timer);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
// src/telemetry.ts
|
|
496
|
-
import { trace } from "@opentelemetry/api";
|
|
497
|
-
import { NodeSDK } from "@opentelemetry/sdk-node";
|
|
498
|
-
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
|
499
|
-
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
500
|
-
var sdk;
|
|
501
|
-
function initTelemetry(options) {
|
|
502
|
-
if (!process.env["OTEL_EXPORTER_OTLP_ENDPOINT"]) return;
|
|
503
|
-
if (sdk !== void 0) return;
|
|
504
|
-
const resource = resourceFromAttributes({
|
|
505
|
-
"service.name": options?.agentName ?? "rill-foundry-harness",
|
|
506
|
-
"service.version": options?.agentVersion ?? "0.0.0"
|
|
507
|
-
});
|
|
508
|
-
const traceExporter = new OTLPTraceExporter();
|
|
509
|
-
sdk = new NodeSDK({ resource, traceExporter });
|
|
510
|
-
sdk.start();
|
|
511
|
-
}
|
|
512
|
-
function getTracer() {
|
|
513
|
-
return trace.getTracer("rill-foundry-harness");
|
|
514
|
-
}
|
|
515
|
-
async function shutdownTelemetry() {
|
|
516
|
-
if (sdk === void 0) return;
|
|
517
|
-
const instance = sdk;
|
|
518
|
-
sdk = void 0;
|
|
519
|
-
await instance.shutdown();
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// src/harness.ts
|
|
523
|
-
import "hono";
|
|
524
|
-
import { SpanStatusCode } from "@opentelemetry/api";
|
|
525
|
-
import { DefaultAzureCredential } from "@azure/identity";
|
|
526
|
-
import { validateParams, routerErrorToStatus } from "@rcrsr/rill-agent";
|
|
527
|
-
|
|
528
|
-
// ../../shared/hono-kit/src/index.ts
|
|
529
|
-
import { Hono } from "hono";
|
|
530
|
-
import { serve } from "@hono/node-server";
|
|
531
|
-
function assertJsonObject(parsed) {
|
|
532
|
-
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
533
|
-
throw new Error("Request body must be a JSON object");
|
|
534
|
-
}
|
|
535
|
-
return parsed;
|
|
536
|
-
}
|
|
537
|
-
function createHarnessLifecycle(options) {
|
|
538
|
-
const app = new Hono();
|
|
539
|
-
let server;
|
|
540
|
-
async function listen(port) {
|
|
541
|
-
if (server !== void 0) {
|
|
542
|
-
throw new Error("Server is already listening");
|
|
543
|
-
}
|
|
544
|
-
return new Promise((resolve) => {
|
|
545
|
-
server = serve({ fetch: app.fetch, port }, () => {
|
|
546
|
-
options?.serverTweaks?.(server);
|
|
547
|
-
resolve();
|
|
548
|
-
});
|
|
549
|
-
});
|
|
550
|
-
}
|
|
551
|
-
async function close() {
|
|
552
|
-
if (server !== void 0) {
|
|
553
|
-
server.close();
|
|
554
|
-
server = void 0;
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
return { app, listen, close };
|
|
558
|
-
}
|
|
559
654
|
|
|
560
655
|
// src/harness.ts
|
|
561
656
|
var DEFAULT_PORT = 8088;
|
|
@@ -583,7 +678,26 @@ function resolveConversationId(conversation) {
|
|
|
583
678
|
if (typeof conversation === "string") {
|
|
584
679
|
return conversation;
|
|
585
680
|
}
|
|
586
|
-
|
|
681
|
+
if (typeof conversation === "object" && conversation !== null && typeof conversation.id === "string") {
|
|
682
|
+
return conversation.id;
|
|
683
|
+
}
|
|
684
|
+
throw new InputError("conversation.id must be a string");
|
|
685
|
+
}
|
|
686
|
+
function resolveMetadataResponseId(metadata) {
|
|
687
|
+
if (metadata === void 0 || metadata === null) {
|
|
688
|
+
return void 0;
|
|
689
|
+
}
|
|
690
|
+
if (typeof metadata !== "object") {
|
|
691
|
+
throw new InputError("metadata must be an object");
|
|
692
|
+
}
|
|
693
|
+
const responseId = metadata["response_id"];
|
|
694
|
+
if (responseId === void 0) {
|
|
695
|
+
return void 0;
|
|
696
|
+
}
|
|
697
|
+
if (typeof responseId !== "string") {
|
|
698
|
+
throw new InputError("metadata.response_id must be a string");
|
|
699
|
+
}
|
|
700
|
+
return responseId;
|
|
587
701
|
}
|
|
588
702
|
function createFoundryHarness(router, options) {
|
|
589
703
|
const port = resolvePort(options);
|
|
@@ -595,7 +709,9 @@ function createFoundryHarness(router, options) {
|
|
|
595
709
|
agentName: agentName ?? router.defaultAgent(),
|
|
596
710
|
agentVersion
|
|
597
711
|
});
|
|
598
|
-
const sessions = createSessionManager(
|
|
712
|
+
const sessions = createSessionManager({
|
|
713
|
+
maxConcurrentSessions: options?.maxConcurrentSessions
|
|
714
|
+
});
|
|
599
715
|
const projectEndpoint = process.env["FOUNDRY_PROJECT_ENDPOINT"];
|
|
600
716
|
const azureCredential = projectEndpoint !== void 0 ? new DefaultAzureCredential() : void 0;
|
|
601
717
|
const conversationsClient = projectEndpoint !== void 0 && azureCredential !== void 0 ? createConversationsClient(projectEndpoint, azureCredential) : void 0;
|
|
@@ -684,7 +800,19 @@ function createFoundryHarness(router, options) {
|
|
|
684
800
|
500
|
|
685
801
|
);
|
|
686
802
|
}
|
|
687
|
-
|
|
803
|
+
let conversationId;
|
|
804
|
+
let metadataResponseId;
|
|
805
|
+
try {
|
|
806
|
+
conversationId = resolveConversationId(body.conversation);
|
|
807
|
+
metadataResponseId = resolveMetadataResponseId(body.metadata);
|
|
808
|
+
} catch (err) {
|
|
809
|
+
errorCount++;
|
|
810
|
+
const msg = err instanceof InputError ? err.message : String(err);
|
|
811
|
+
return c.json(
|
|
812
|
+
buildErrorResponse("INVALID_REQUEST", msg, debugErrors),
|
|
813
|
+
400
|
|
814
|
+
);
|
|
815
|
+
}
|
|
688
816
|
const agentName_ = extracted.targetAgent ?? router.defaultAgent();
|
|
689
817
|
const validationError = validateParams(
|
|
690
818
|
extracted.params,
|
|
@@ -718,10 +846,7 @@ function createFoundryHarness(router, options) {
|
|
|
718
846
|
500
|
|
719
847
|
);
|
|
720
848
|
}
|
|
721
|
-
const idGen = createIdGenerator(
|
|
722
|
-
body.metadata?.["response_id"],
|
|
723
|
-
conversationId
|
|
724
|
-
);
|
|
849
|
+
const idGen = createIdGenerator(metadataResponseId, conversationId);
|
|
725
850
|
const responseId = idGen.responseId;
|
|
726
851
|
const invocationId = c.req.header("x-agent-invocation-id") ?? generateId("inv_");
|
|
727
852
|
const agentSessionId = c.req.query("session") ?? c.req.header("x-agent-session-id") ?? sessionId;
|
|
@@ -813,8 +938,7 @@ function createFoundryHarness(router, options) {
|
|
|
813
938
|
}
|
|
814
939
|
};
|
|
815
940
|
const onChunk = async (chunk) => {
|
|
816
|
-
|
|
817
|
-
pushChunk(text);
|
|
941
|
+
pushChunk(coerceResult(chunk));
|
|
818
942
|
};
|
|
819
943
|
const streamContext = { ...runContext, onChunk };
|
|
820
944
|
router.run(agentName_, runRequest, streamContext).then((result) => {
|
|
@@ -822,10 +946,8 @@ function createFoundryHarness(router, options) {
|
|
|
822
946
|
span.end();
|
|
823
947
|
sessions.release(sessionId);
|
|
824
948
|
if (!result.streamed) {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
if (text !== "") pushChunk(text);
|
|
828
|
-
}
|
|
949
|
+
const text = coerceResult(result.result);
|
|
950
|
+
if (text !== "") pushChunk(text);
|
|
829
951
|
}
|
|
830
952
|
endChunks();
|
|
831
953
|
}).catch((err) => {
|
|
@@ -839,7 +961,6 @@ function createFoundryHarness(router, options) {
|
|
|
839
961
|
});
|
|
840
962
|
return createFoundryStreamResponse(responseId, {
|
|
841
963
|
chunks,
|
|
842
|
-
idGenerator: idGen,
|
|
843
964
|
onError: (_err) => {
|
|
844
965
|
errorCount++;
|
|
845
966
|
},
|
|
@@ -870,7 +991,7 @@ function createFoundryHarness(router, options) {
|
|
|
870
991
|
span.end();
|
|
871
992
|
errorCount++;
|
|
872
993
|
const msg = err instanceof Error ? err.message : String(err);
|
|
873
|
-
const status =
|
|
994
|
+
const status = routerErrorToStatus2(err);
|
|
874
995
|
if (status === 404) {
|
|
875
996
|
return c.json(buildErrorResponse("NOT_FOUND", msg, debugErrors), 404);
|
|
876
997
|
}
|
|
@@ -880,7 +1001,7 @@ function createFoundryHarness(router, options) {
|
|
|
880
1001
|
);
|
|
881
1002
|
}
|
|
882
1003
|
span.end();
|
|
883
|
-
const response = buildSyncResponse(result, responseId);
|
|
1004
|
+
const response = buildSyncResponse(result, responseId, debugErrors);
|
|
884
1005
|
const shouldStore = body.store === true;
|
|
885
1006
|
if (shouldStore && conversationId !== void 0 && conversationsClient !== void 0) {
|
|
886
1007
|
try {
|
|
@@ -926,11 +1047,11 @@ function createFoundryHarness(router, options) {
|
|
|
926
1047
|
);
|
|
927
1048
|
}
|
|
928
1049
|
} catch (err) {
|
|
929
|
-
if (
|
|
930
|
-
|
|
931
|
-
throw new CredentialError(message);
|
|
1050
|
+
if (err instanceof CredentialError) {
|
|
1051
|
+
throw err;
|
|
932
1052
|
}
|
|
933
|
-
|
|
1053
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1054
|
+
throw new CredentialError(message);
|
|
934
1055
|
}
|
|
935
1056
|
}
|
|
936
1057
|
await lifecycle.listen(port);
|
|
@@ -948,6 +1069,25 @@ function createFoundryHarness(router, options) {
|
|
|
948
1069
|
}
|
|
949
1070
|
return { listen, close, app, metrics };
|
|
950
1071
|
}
|
|
1072
|
+
|
|
1073
|
+
// src/index.ts
|
|
1074
|
+
import { createRouter, assembleManifest } from "@rcrsr/rill-agent";
|
|
1075
|
+
var HARNESS_NAME = "@rcrsr/rill-agent-foundry";
|
|
1076
|
+
var harness = {
|
|
1077
|
+
name: HARNESS_NAME,
|
|
1078
|
+
postBuild: async (ctx) => {
|
|
1079
|
+
assertCompiledHandlers(ctx);
|
|
1080
|
+
},
|
|
1081
|
+
serve: (ctx) => runRillServe(ctx, async (entries) => {
|
|
1082
|
+
const router = await createRouter(await assembleManifest(entries));
|
|
1083
|
+
const port = readHarnessPort(ctx.config, 3e3);
|
|
1084
|
+
const server = createFoundryHarness(router, { port });
|
|
1085
|
+
await server.listen();
|
|
1086
|
+
ctx.logger.info(`[${HARNESS_NAME}] listening on :${port}`);
|
|
1087
|
+
return server;
|
|
1088
|
+
})
|
|
1089
|
+
};
|
|
1090
|
+
var index_default = harness;
|
|
951
1091
|
export {
|
|
952
1092
|
CapacityError,
|
|
953
1093
|
CredentialError,
|
|
@@ -959,11 +1099,11 @@ export {
|
|
|
959
1099
|
createFoundryHarness,
|
|
960
1100
|
createIdGenerator,
|
|
961
1101
|
createSessionManager,
|
|
1102
|
+
index_default as default,
|
|
962
1103
|
extractInput,
|
|
963
1104
|
generateId,
|
|
964
1105
|
generateToolDefinitions,
|
|
965
1106
|
getTracer,
|
|
966
1107
|
initTelemetry,
|
|
967
|
-
shutdownTelemetry
|
|
968
|
-
streamFoundryResponse
|
|
1108
|
+
shutdownTelemetry
|
|
969
1109
|
};
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rcrsr/rill-agent-foundry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "rill agent foundry — Azure-hosted harness factory",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Andre Bremer",
|
|
7
7
|
"type": "module",
|
|
8
|
+
"rill": {
|
|
9
|
+
"role": "harness"
|
|
10
|
+
},
|
|
8
11
|
"main": "dist/index.js",
|
|
9
12
|
"types": "dist/index.d.ts",
|
|
10
13
|
"keywords": [
|
|
@@ -19,19 +22,19 @@
|
|
|
19
22
|
"@opentelemetry/api": "^1.9.0"
|
|
20
23
|
},
|
|
21
24
|
"dependencies": {
|
|
22
|
-
"@azure/identity": "^4.
|
|
23
|
-
"@hono/node-server": "^2.
|
|
24
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.
|
|
25
|
-
"@opentelemetry/resources": "^2.
|
|
26
|
-
"@opentelemetry/sdk-node": "^0.
|
|
27
|
-
"
|
|
28
|
-
"
|
|
25
|
+
"@azure/identity": "^4.13.2",
|
|
26
|
+
"@hono/node-server": "^2.1.1",
|
|
27
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.222.0",
|
|
28
|
+
"@opentelemetry/resources": "^2.11.0",
|
|
29
|
+
"@opentelemetry/sdk-node": "^0.222.0",
|
|
30
|
+
"@rcrsr/rill-agent": "~0.21.0",
|
|
31
|
+
"hono": "^4.13.7"
|
|
29
32
|
},
|
|
30
33
|
"devDependencies": {
|
|
31
34
|
"@opentelemetry/api": "^1.9.0",
|
|
35
|
+
"@rcrsr/rill-agent-hono-kit": "^0.21.0",
|
|
32
36
|
"dts-bundle-generator": "^9.5.1",
|
|
33
|
-
"tsup": "^8.5.
|
|
34
|
-
"@rcrsr/rill-agent-hono-kit": "^0.19.0"
|
|
37
|
+
"tsup": "^8.5.1"
|
|
35
38
|
},
|
|
36
39
|
"files": [
|
|
37
40
|
"dist"
|
|
@@ -52,7 +55,7 @@
|
|
|
52
55
|
"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);}\"",
|
|
53
56
|
"test": "vitest run",
|
|
54
57
|
"typecheck": "tsc --noEmit",
|
|
55
|
-
"lint": "
|
|
58
|
+
"lint": "oxlint --config ../../../.oxlintrc.json src/ tests/",
|
|
56
59
|
"check": "pnpm run build && pnpm run test && pnpm run lint"
|
|
57
60
|
}
|
|
58
61
|
}
|