@renseiai/agentfactory-nextjs 0.8.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/LICENSE +21 -0
- package/README.md +159 -0
- package/dist/src/__tests__/middleware-edge-safety.test.d.ts +2 -0
- package/dist/src/__tests__/middleware-edge-safety.test.d.ts.map +1 -0
- package/dist/src/__tests__/middleware-edge-safety.test.js +74 -0
- package/dist/src/__tests__/poll-project-filter.test.d.ts +2 -0
- package/dist/src/__tests__/poll-project-filter.test.d.ts.map +1 -0
- package/dist/src/__tests__/poll-project-filter.test.js +83 -0
- package/dist/src/__tests__/subpath-exports.test.d.ts +2 -0
- package/dist/src/__tests__/subpath-exports.test.d.ts.map +1 -0
- package/dist/src/__tests__/subpath-exports.test.js +35 -0
- package/dist/src/__tests__/webhook-project-filter.test.d.ts +2 -0
- package/dist/src/__tests__/webhook-project-filter.test.d.ts.map +1 -0
- package/dist/src/__tests__/webhook-project-filter.test.js +48 -0
- package/dist/src/factory.d.ts +140 -0
- package/dist/src/factory.d.ts.map +1 -0
- package/dist/src/factory.js +127 -0
- package/dist/src/handlers/cleanup.d.ts +44 -0
- package/dist/src/handlers/cleanup.d.ts.map +1 -0
- package/dist/src/handlers/cleanup.js +34 -0
- package/dist/src/handlers/config.d.ts +11 -0
- package/dist/src/handlers/config.d.ts.map +1 -0
- package/dist/src/handlers/config.js +20 -0
- package/dist/src/handlers/issue-tracker-proxy/index.d.ts +34 -0
- package/dist/src/handlers/issue-tracker-proxy/index.d.ts.map +1 -0
- package/dist/src/handlers/issue-tracker-proxy/index.js +230 -0
- package/dist/src/handlers/issue-tracker-proxy/serializer.d.ts +28 -0
- package/dist/src/handlers/issue-tracker-proxy/serializer.d.ts.map +1 -0
- package/dist/src/handlers/issue-tracker-proxy/serializer.js +95 -0
- package/dist/src/handlers/issue-tracker-proxy/types.d.ts +9 -0
- package/dist/src/handlers/issue-tracker-proxy/types.d.ts.map +1 -0
- package/dist/src/handlers/issue-tracker-proxy/types.js +4 -0
- package/dist/src/handlers/oauth/callback.d.ts +36 -0
- package/dist/src/handlers/oauth/callback.d.ts.map +1 -0
- package/dist/src/handlers/oauth/callback.js +96 -0
- package/dist/src/handlers/public/session-detail.d.ts +31 -0
- package/dist/src/handlers/public/session-detail.d.ts.map +1 -0
- package/dist/src/handlers/public/session-detail.js +91 -0
- package/dist/src/handlers/public/sessions-list.d.ts +22 -0
- package/dist/src/handlers/public/sessions-list.d.ts.map +1 -0
- package/dist/src/handlers/public/sessions-list.js +75 -0
- package/dist/src/handlers/public/stats.d.ts +28 -0
- package/dist/src/handlers/public/stats.d.ts.map +1 -0
- package/dist/src/handlers/public/stats.js +66 -0
- package/dist/src/handlers/sessions/activity.d.ts +15 -0
- package/dist/src/handlers/sessions/activity.d.ts.map +1 -0
- package/dist/src/handlers/sessions/activity.js +93 -0
- package/dist/src/handlers/sessions/claim.d.ts +15 -0
- package/dist/src/handlers/sessions/claim.d.ts.map +1 -0
- package/dist/src/handlers/sessions/claim.js +139 -0
- package/dist/src/handlers/sessions/completion.d.ts +16 -0
- package/dist/src/handlers/sessions/completion.d.ts.map +1 -0
- package/dist/src/handlers/sessions/completion.js +82 -0
- package/dist/src/handlers/sessions/external-urls.d.ts +15 -0
- package/dist/src/handlers/sessions/external-urls.d.ts.map +1 -0
- package/dist/src/handlers/sessions/external-urls.js +70 -0
- package/dist/src/handlers/sessions/get.d.ts +19 -0
- package/dist/src/handlers/sessions/get.d.ts.map +1 -0
- package/dist/src/handlers/sessions/get.js +47 -0
- package/dist/src/handlers/sessions/list.d.ts +27 -0
- package/dist/src/handlers/sessions/list.d.ts.map +1 -0
- package/dist/src/handlers/sessions/list.js +51 -0
- package/dist/src/handlers/sessions/lock-refresh.d.ts +14 -0
- package/dist/src/handlers/sessions/lock-refresh.d.ts.map +1 -0
- package/dist/src/handlers/sessions/lock-refresh.js +38 -0
- package/dist/src/handlers/sessions/progress.d.ts +15 -0
- package/dist/src/handlers/sessions/progress.d.ts.map +1 -0
- package/dist/src/handlers/sessions/progress.js +94 -0
- package/dist/src/handlers/sessions/prompts.d.ts +15 -0
- package/dist/src/handlers/sessions/prompts.d.ts.map +1 -0
- package/dist/src/handlers/sessions/prompts.js +91 -0
- package/dist/src/handlers/sessions/status.d.ts +19 -0
- package/dist/src/handlers/sessions/status.d.ts.map +1 -0
- package/dist/src/handlers/sessions/status.js +187 -0
- package/dist/src/handlers/sessions/tool-error.d.ts +15 -0
- package/dist/src/handlers/sessions/tool-error.d.ts.map +1 -0
- package/dist/src/handlers/sessions/tool-error.js +103 -0
- package/dist/src/handlers/sessions/transfer-ownership.d.ts +14 -0
- package/dist/src/handlers/sessions/transfer-ownership.d.ts.map +1 -0
- package/dist/src/handlers/sessions/transfer-ownership.js +56 -0
- package/dist/src/handlers/workers/get-delete.d.ts +15 -0
- package/dist/src/handlers/workers/get-delete.d.ts.map +1 -0
- package/dist/src/handlers/workers/get-delete.js +58 -0
- package/dist/src/handlers/workers/heartbeat.d.ts +14 -0
- package/dist/src/handlers/workers/heartbeat.d.ts.map +1 -0
- package/dist/src/handlers/workers/heartbeat.js +42 -0
- package/dist/src/handlers/workers/list.d.ts +22 -0
- package/dist/src/handlers/workers/list.d.ts.map +1 -0
- package/dist/src/handlers/workers/list.js +33 -0
- package/dist/src/handlers/workers/poll.d.ts +14 -0
- package/dist/src/handlers/workers/poll.d.ts.map +1 -0
- package/dist/src/handlers/workers/poll.js +96 -0
- package/dist/src/handlers/workers/register.d.ts +9 -0
- package/dist/src/handlers/workers/register.d.ts.map +1 -0
- package/dist/src/handlers/workers/register.js +45 -0
- package/dist/src/index.d.ts +52 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +56 -0
- package/dist/src/linear-client-resolver.d.ts +59 -0
- package/dist/src/linear-client-resolver.d.ts.map +1 -0
- package/dist/src/linear-client-resolver.js +104 -0
- package/dist/src/middleware/cron-auth.d.ts +21 -0
- package/dist/src/middleware/cron-auth.d.ts.map +1 -0
- package/dist/src/middleware/cron-auth.js +46 -0
- package/dist/src/middleware/factory.d.ts +33 -0
- package/dist/src/middleware/factory.d.ts.map +1 -0
- package/dist/src/middleware/factory.js +185 -0
- package/dist/src/middleware/index.d.ts +16 -0
- package/dist/src/middleware/index.d.ts.map +1 -0
- package/dist/src/middleware/index.js +14 -0
- package/dist/src/middleware/types.d.ts +35 -0
- package/dist/src/middleware/types.d.ts.map +1 -0
- package/dist/src/middleware/types.js +4 -0
- package/dist/src/middleware/worker-auth.d.ts +25 -0
- package/dist/src/middleware/worker-auth.d.ts.map +1 -0
- package/dist/src/middleware/worker-auth.js +43 -0
- package/dist/src/orchestrator/error-formatting.d.ts +8 -0
- package/dist/src/orchestrator/error-formatting.d.ts.map +1 -0
- package/dist/src/orchestrator/error-formatting.js +35 -0
- package/dist/src/orchestrator/index.d.ts +4 -0
- package/dist/src/orchestrator/index.d.ts.map +1 -0
- package/dist/src/orchestrator/index.js +2 -0
- package/dist/src/orchestrator/types.d.ts +53 -0
- package/dist/src/orchestrator/types.d.ts.map +1 -0
- package/dist/src/orchestrator/types.js +4 -0
- package/dist/src/orchestrator/webhook-orchestrator.d.ts +32 -0
- package/dist/src/orchestrator/webhook-orchestrator.d.ts.map +1 -0
- package/dist/src/orchestrator/webhook-orchestrator.js +373 -0
- package/dist/src/types.d.ts +101 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +7 -0
- package/dist/src/webhook/governor-bridge.d.ts +23 -0
- package/dist/src/webhook/governor-bridge.d.ts.map +1 -0
- package/dist/src/webhook/governor-bridge.js +36 -0
- package/dist/src/webhook/handlers/issue-updated.d.ts +15 -0
- package/dist/src/webhook/handlers/issue-updated.d.ts.map +1 -0
- package/dist/src/webhook/handlers/issue-updated.js +771 -0
- package/dist/src/webhook/handlers/session-created.d.ts +9 -0
- package/dist/src/webhook/handlers/session-created.d.ts.map +1 -0
- package/dist/src/webhook/handlers/session-created.js +337 -0
- package/dist/src/webhook/handlers/session-prompted.d.ts +9 -0
- package/dist/src/webhook/handlers/session-prompted.d.ts.map +1 -0
- package/dist/src/webhook/handlers/session-prompted.js +199 -0
- package/dist/src/webhook/handlers/session-updated.d.ts +9 -0
- package/dist/src/webhook/handlers/session-updated.d.ts.map +1 -0
- package/dist/src/webhook/handlers/session-updated.js +29 -0
- package/dist/src/webhook/processor.d.ts +22 -0
- package/dist/src/webhook/processor.d.ts.map +1 -0
- package/dist/src/webhook/processor.js +98 -0
- package/dist/src/webhook/signature.d.ts +16 -0
- package/dist/src/webhook/signature.d.ts.map +1 -0
- package/dist/src/webhook/signature.js +23 -0
- package/dist/src/webhook/utils.d.ts +61 -0
- package/dist/src/webhook/utils.d.ts.map +1 -0
- package/dist/src/webhook/utils.js +166 -0
- package/package.json +86 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/workers
|
|
3
|
+
*
|
|
4
|
+
* List all registered workers.
|
|
5
|
+
* Used by dashboard to display worker status.
|
|
6
|
+
*/
|
|
7
|
+
import { NextResponse } from 'next/server';
|
|
8
|
+
export declare function createWorkerListHandler(): () => Promise<NextResponse<{
|
|
9
|
+
workers: import("@renseiai/agentfactory-server").WorkerInfo[];
|
|
10
|
+
summary: {
|
|
11
|
+
queueLength: number;
|
|
12
|
+
totalCapacity: number;
|
|
13
|
+
totalActive: number;
|
|
14
|
+
availableCapacity: number;
|
|
15
|
+
totalWorkers: number;
|
|
16
|
+
activeWorkers: number;
|
|
17
|
+
};
|
|
18
|
+
}> | NextResponse<{
|
|
19
|
+
error: string;
|
|
20
|
+
message: string;
|
|
21
|
+
}>>;
|
|
22
|
+
//# sourceMappingURL=list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../src/handlers/workers/list.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAK1C,wBAAgB,uBAAuB;;;;;;;;;;;;;IA2BtC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/workers
|
|
3
|
+
*
|
|
4
|
+
* List all registered workers.
|
|
5
|
+
* Used by dashboard to display worker status.
|
|
6
|
+
*/
|
|
7
|
+
import { NextResponse } from 'next/server';
|
|
8
|
+
import { listWorkers, getTotalCapacity, getQueueLength, createLogger } from '@renseiai/agentfactory-server';
|
|
9
|
+
const log = createLogger('api:workers:list');
|
|
10
|
+
export function createWorkerListHandler() {
|
|
11
|
+
return async function GET() {
|
|
12
|
+
try {
|
|
13
|
+
const [workers, queueLength] = await Promise.all([
|
|
14
|
+
listWorkers(),
|
|
15
|
+
getQueueLength(),
|
|
16
|
+
]);
|
|
17
|
+
const capacity = await getTotalCapacity(workers);
|
|
18
|
+
return NextResponse.json({
|
|
19
|
+
workers,
|
|
20
|
+
summary: {
|
|
21
|
+
totalWorkers: workers.length,
|
|
22
|
+
activeWorkers: workers.filter((w) => w.status === 'active').length,
|
|
23
|
+
...capacity,
|
|
24
|
+
queueLength,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
log.error('Failed to list workers', { error });
|
|
30
|
+
return NextResponse.json({ error: 'Internal Server Error', message: 'Failed to list workers' }, { status: 500 });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/workers/[id]/poll
|
|
3
|
+
*
|
|
4
|
+
* Poll for pending work items and follow-up prompts.
|
|
5
|
+
*/
|
|
6
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
7
|
+
interface RouteParams {
|
|
8
|
+
params: Promise<{
|
|
9
|
+
id: string;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
export declare function createWorkerPollHandler(): (request: NextRequest, { params }: RouteParams) => Promise<NextResponse<unknown>>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=poll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../../../src/handlers/workers/poll.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAavD,UAAU,WAAW;IACnB,MAAM,EAAE,OAAO,CAAC;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAChC;AAED,wBAAgB,uBAAuB,KACX,SAAS,WAAW,EAAE,YAAY,WAAW,oCAsGxE"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/workers/[id]/poll
|
|
3
|
+
*
|
|
4
|
+
* Poll for pending work items and follow-up prompts.
|
|
5
|
+
*/
|
|
6
|
+
import { NextResponse } from 'next/server';
|
|
7
|
+
import { requireWorkerAuth } from '../../middleware/worker-auth.js';
|
|
8
|
+
import { getWorker, peekWork, getPendingPrompts, maybeCleanupOrphans, createLogger, } from '@renseiai/agentfactory-server';
|
|
9
|
+
const log = createLogger('api:workers:poll');
|
|
10
|
+
export function createWorkerPollHandler() {
|
|
11
|
+
return async function GET(request, { params }) {
|
|
12
|
+
const authError = requireWorkerAuth(request);
|
|
13
|
+
if (authError)
|
|
14
|
+
return authError;
|
|
15
|
+
const { id: workerId } = await params;
|
|
16
|
+
try {
|
|
17
|
+
maybeCleanupOrphans().catch((err) => {
|
|
18
|
+
log.error('Background orphan cleanup failed', { error: err });
|
|
19
|
+
});
|
|
20
|
+
const worker = await getWorker(workerId);
|
|
21
|
+
if (!worker) {
|
|
22
|
+
return NextResponse.json({ error: 'Not Found', message: 'Worker not found' }, { status: 404 });
|
|
23
|
+
}
|
|
24
|
+
// Use activeSessions.length (authoritative Redis set) instead of
|
|
25
|
+
// activeCount (heartbeat-reported, can be stale after re-registration)
|
|
26
|
+
const availableCapacity = worker.capacity - worker.activeSessions.length;
|
|
27
|
+
let work = [];
|
|
28
|
+
if (availableCapacity > 0) {
|
|
29
|
+
const desiredCount = Math.min(availableCapacity, 5);
|
|
30
|
+
const workerProjects = worker.projects;
|
|
31
|
+
const hasProjectFilter = workerProjects && workerProjects.length > 0;
|
|
32
|
+
// Over-fetch when filtering, since some items may not match
|
|
33
|
+
const fetchLimit = hasProjectFilter ? Math.min(desiredCount * 4, 50) : desiredCount;
|
|
34
|
+
const allWork = await peekWork(fetchLimit);
|
|
35
|
+
if (hasProjectFilter) {
|
|
36
|
+
// Only accept work tagged with a project this worker serves.
|
|
37
|
+
// Untagged work is excluded — it should only be picked up by
|
|
38
|
+
// workers with no project filter, preventing cross-repo execution.
|
|
39
|
+
work = allWork
|
|
40
|
+
.filter(w => w.projectName && workerProjects.includes(w.projectName))
|
|
41
|
+
.slice(0, desiredCount);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
work = allWork.slice(0, desiredCount);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const pendingPrompts = {};
|
|
48
|
+
let totalPendingPrompts = 0;
|
|
49
|
+
if (worker.activeSessions.length > 0) {
|
|
50
|
+
await Promise.all(worker.activeSessions.map(async (sessionId) => {
|
|
51
|
+
const prompts = await getPendingPrompts(sessionId);
|
|
52
|
+
if (prompts.length > 0) {
|
|
53
|
+
pendingPrompts[sessionId] = prompts;
|
|
54
|
+
totalPendingPrompts += prompts.length;
|
|
55
|
+
}
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
if (work.length > 0 || totalPendingPrompts > 0) {
|
|
59
|
+
log.info('Poll result with items', {
|
|
60
|
+
workerId,
|
|
61
|
+
availableCapacity,
|
|
62
|
+
workCount: work.length,
|
|
63
|
+
workItems: work.map((w) => ({
|
|
64
|
+
sessionId: w.sessionId,
|
|
65
|
+
issueIdentifier: w.issueIdentifier,
|
|
66
|
+
workType: w.workType,
|
|
67
|
+
projectName: w.projectName,
|
|
68
|
+
})),
|
|
69
|
+
activeSessionCount: worker.activeSessions.length,
|
|
70
|
+
pendingPromptsCount: totalPendingPrompts,
|
|
71
|
+
pendingPromptsBySession: Object.entries(pendingPrompts).map(([sessionId, prompts]) => ({
|
|
72
|
+
sessionId,
|
|
73
|
+
count: prompts.length,
|
|
74
|
+
promptIds: prompts.map((p) => p.id),
|
|
75
|
+
})),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
log.debug('Poll result (empty)', {
|
|
80
|
+
workerId,
|
|
81
|
+
availableCapacity,
|
|
82
|
+
activeSessionCount: worker.activeSessions.length,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
return NextResponse.json({
|
|
86
|
+
work,
|
|
87
|
+
pendingPrompts,
|
|
88
|
+
hasPendingPrompts: totalPendingPrompts > 0,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
log.error('Failed to poll for work', { error, workerId });
|
|
93
|
+
return NextResponse.json({ error: 'Internal Server Error', message: 'Failed to poll for work' }, { status: 500 });
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/workers/register
|
|
3
|
+
*
|
|
4
|
+
* Register a new worker with the coordinator.
|
|
5
|
+
* Returns worker ID and configuration.
|
|
6
|
+
*/
|
|
7
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
8
|
+
export declare function createWorkerRegisterHandler(): (request: NextRequest) => Promise<NextResponse<unknown>>;
|
|
9
|
+
//# sourceMappingURL=register.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../../src/handlers/workers/register.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAMvD,wBAAgB,2BAA2B,KACd,SAAS,WAAW,oCAwDhD"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/workers/register
|
|
3
|
+
*
|
|
4
|
+
* Register a new worker with the coordinator.
|
|
5
|
+
* Returns worker ID and configuration.
|
|
6
|
+
*/
|
|
7
|
+
import { NextResponse } from 'next/server';
|
|
8
|
+
import { requireWorkerAuth } from '../../middleware/worker-auth.js';
|
|
9
|
+
import { registerWorker, createLogger } from '@renseiai/agentfactory-server';
|
|
10
|
+
const log = createLogger('api:workers:register');
|
|
11
|
+
export function createWorkerRegisterHandler() {
|
|
12
|
+
return async function POST(request) {
|
|
13
|
+
const authError = requireWorkerAuth(request);
|
|
14
|
+
if (authError)
|
|
15
|
+
return authError;
|
|
16
|
+
try {
|
|
17
|
+
const body = await request.json();
|
|
18
|
+
const { hostname, capacity, version, projects } = body;
|
|
19
|
+
if (!hostname || typeof hostname !== 'string') {
|
|
20
|
+
return NextResponse.json({ error: 'Bad Request', message: 'hostname is required' }, { status: 400 });
|
|
21
|
+
}
|
|
22
|
+
if (!capacity || typeof capacity !== 'number' || capacity < 1) {
|
|
23
|
+
return NextResponse.json({ error: 'Bad Request', message: 'capacity must be a positive number' }, { status: 400 });
|
|
24
|
+
}
|
|
25
|
+
if (projects !== undefined && !Array.isArray(projects)) {
|
|
26
|
+
return NextResponse.json({ error: 'Bad Request', message: 'projects must be an array of strings' }, { status: 400 });
|
|
27
|
+
}
|
|
28
|
+
const result = await registerWorker(hostname, capacity, version, projects);
|
|
29
|
+
if (!result) {
|
|
30
|
+
return NextResponse.json({ error: 'Service Unavailable', message: 'Failed to register worker' }, { status: 503 });
|
|
31
|
+
}
|
|
32
|
+
log.info('Worker registered via API', {
|
|
33
|
+
workerId: result.workerId,
|
|
34
|
+
hostname,
|
|
35
|
+
capacity,
|
|
36
|
+
projects: projects?.length ? projects : 'all',
|
|
37
|
+
});
|
|
38
|
+
return NextResponse.json(result, { status: 201 });
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
log.error('Failed to register worker', { error });
|
|
42
|
+
return NextResponse.json({ error: 'Internal Server Error', message: 'Failed to register worker' }, { status: 500 });
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @renseiai/agentfactory-nextjs
|
|
3
|
+
*
|
|
4
|
+
* Next.js API route handlers for AgentFactory.
|
|
5
|
+
* Provides webhook processing, worker/session management, public stats,
|
|
6
|
+
* OAuth callback, middleware factory, webhook orchestrator, and Linear client resolver.
|
|
7
|
+
*/
|
|
8
|
+
export type { LinearClientResolver, RouteConfig, WebhookConfig, AutoTriggerConfig, CronConfig, RouteHandler, } from './types.js';
|
|
9
|
+
export { createAllRoutes } from './factory.js';
|
|
10
|
+
export type { AllRoutes, AllRoutesConfig } from './factory.js';
|
|
11
|
+
export { createOAuthCallbackHandler } from './handlers/oauth/callback.js';
|
|
12
|
+
export type { OAuthConfig } from './handlers/oauth/callback.js';
|
|
13
|
+
export { createAgentFactoryMiddleware } from './middleware/factory.js';
|
|
14
|
+
export type { MiddlewareConfig } from './middleware/types.js';
|
|
15
|
+
export { createWebhookOrchestrator, formatErrorForComment, } from './orchestrator/index.js';
|
|
16
|
+
export type { WebhookOrchestratorConfig, WebhookOrchestratorHooks, WebhookOrchestratorInstance, } from './orchestrator/index.js';
|
|
17
|
+
export { createDefaultLinearClientResolver } from './linear-client-resolver.js';
|
|
18
|
+
export type { DefaultLinearClientResolverConfig } from './linear-client-resolver.js';
|
|
19
|
+
export { verifyCronAuth } from './middleware/cron-auth.js';
|
|
20
|
+
export { verifyWorkerAuth, requireWorkerAuth, unauthorizedResponse, isWorkerAuthConfigured, } from './middleware/worker-auth.js';
|
|
21
|
+
export { createWebhookHandler } from './webhook/processor.js';
|
|
22
|
+
export { verifyWebhookSignature } from './webhook/signature.js';
|
|
23
|
+
export { setGovernorEventBus, getGovernorEventBus, publishGovernorEvent } from './webhook/governor-bridge.js';
|
|
24
|
+
export { createWorkerRegisterHandler } from './handlers/workers/register.js';
|
|
25
|
+
export { createWorkerListHandler } from './handlers/workers/list.js';
|
|
26
|
+
export { createWorkerGetHandler, createWorkerDeleteHandler } from './handlers/workers/get-delete.js';
|
|
27
|
+
export { createWorkerHeartbeatHandler } from './handlers/workers/heartbeat.js';
|
|
28
|
+
export { createWorkerPollHandler } from './handlers/workers/poll.js';
|
|
29
|
+
export { createSessionListHandler } from './handlers/sessions/list.js';
|
|
30
|
+
export type { AgentSessionResponse } from './handlers/sessions/list.js';
|
|
31
|
+
export { createSessionGetHandler } from './handlers/sessions/get.js';
|
|
32
|
+
export { createSessionClaimHandler } from './handlers/sessions/claim.js';
|
|
33
|
+
export { createSessionStatusPostHandler, createSessionStatusGetHandler } from './handlers/sessions/status.js';
|
|
34
|
+
export { createSessionLockRefreshHandler } from './handlers/sessions/lock-refresh.js';
|
|
35
|
+
export { createSessionPromptsGetHandler, createSessionPromptsPostHandler } from './handlers/sessions/prompts.js';
|
|
36
|
+
export { createSessionTransferOwnershipHandler } from './handlers/sessions/transfer-ownership.js';
|
|
37
|
+
export { createSessionActivityHandler } from './handlers/sessions/activity.js';
|
|
38
|
+
export { createSessionCompletionHandler } from './handlers/sessions/completion.js';
|
|
39
|
+
export { createSessionExternalUrlsHandler } from './handlers/sessions/external-urls.js';
|
|
40
|
+
export { createSessionProgressHandler } from './handlers/sessions/progress.js';
|
|
41
|
+
export { createSessionToolErrorHandler } from './handlers/sessions/tool-error.js';
|
|
42
|
+
export { createPublicStatsHandler } from './handlers/public/stats.js';
|
|
43
|
+
export type { PublicStatsResponse } from './handlers/public/stats.js';
|
|
44
|
+
export { createPublicSessionsListHandler } from './handlers/public/sessions-list.js';
|
|
45
|
+
export type { PublicSessionResponse } from './handlers/public/sessions-list.js';
|
|
46
|
+
export { createPublicSessionDetailHandler } from './handlers/public/session-detail.js';
|
|
47
|
+
export type { PublicSessionDetailResponse } from './handlers/public/session-detail.js';
|
|
48
|
+
export { createCleanupHandler } from './handlers/cleanup.js';
|
|
49
|
+
export { createConfigHandler } from './handlers/config.js';
|
|
50
|
+
export { createIssueTrackerProxyHandler } from './handlers/issue-tracker-proxy/index.js';
|
|
51
|
+
export type { ProxyHandlerConfig } from './handlers/issue-tracker-proxy/types.js';
|
|
52
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @renseiai/agentfactory-nextjs
|
|
3
|
+
*
|
|
4
|
+
* Next.js API route handlers for AgentFactory.
|
|
5
|
+
* Provides webhook processing, worker/session management, public stats,
|
|
6
|
+
* OAuth callback, middleware factory, webhook orchestrator, and Linear client resolver.
|
|
7
|
+
*/
|
|
8
|
+
// Factory
|
|
9
|
+
export { createAllRoutes } from './factory.js';
|
|
10
|
+
// OAuth handler
|
|
11
|
+
export { createOAuthCallbackHandler } from './handlers/oauth/callback.js';
|
|
12
|
+
// Middleware factory
|
|
13
|
+
export { createAgentFactoryMiddleware } from './middleware/factory.js';
|
|
14
|
+
// Webhook orchestrator
|
|
15
|
+
export { createWebhookOrchestrator, formatErrorForComment, } from './orchestrator/index.js';
|
|
16
|
+
// Default Linear client resolver
|
|
17
|
+
export { createDefaultLinearClientResolver } from './linear-client-resolver.js';
|
|
18
|
+
// Middleware (existing)
|
|
19
|
+
export { verifyCronAuth } from './middleware/cron-auth.js';
|
|
20
|
+
export { verifyWorkerAuth, requireWorkerAuth, unauthorizedResponse, isWorkerAuthConfigured, } from './middleware/worker-auth.js';
|
|
21
|
+
// Webhook
|
|
22
|
+
export { createWebhookHandler } from './webhook/processor.js';
|
|
23
|
+
export { verifyWebhookSignature } from './webhook/signature.js';
|
|
24
|
+
// Governor bridge
|
|
25
|
+
export { setGovernorEventBus, getGovernorEventBus, publishGovernorEvent } from './webhook/governor-bridge.js';
|
|
26
|
+
// Individual handler factories (for custom wiring)
|
|
27
|
+
// Worker handlers
|
|
28
|
+
export { createWorkerRegisterHandler } from './handlers/workers/register.js';
|
|
29
|
+
export { createWorkerListHandler } from './handlers/workers/list.js';
|
|
30
|
+
export { createWorkerGetHandler, createWorkerDeleteHandler } from './handlers/workers/get-delete.js';
|
|
31
|
+
export { createWorkerHeartbeatHandler } from './handlers/workers/heartbeat.js';
|
|
32
|
+
export { createWorkerPollHandler } from './handlers/workers/poll.js';
|
|
33
|
+
// Session handlers (no Linear dependency)
|
|
34
|
+
export { createSessionListHandler } from './handlers/sessions/list.js';
|
|
35
|
+
export { createSessionGetHandler } from './handlers/sessions/get.js';
|
|
36
|
+
export { createSessionClaimHandler } from './handlers/sessions/claim.js';
|
|
37
|
+
export { createSessionStatusPostHandler, createSessionStatusGetHandler } from './handlers/sessions/status.js';
|
|
38
|
+
export { createSessionLockRefreshHandler } from './handlers/sessions/lock-refresh.js';
|
|
39
|
+
export { createSessionPromptsGetHandler, createSessionPromptsPostHandler } from './handlers/sessions/prompts.js';
|
|
40
|
+
export { createSessionTransferOwnershipHandler } from './handlers/sessions/transfer-ownership.js';
|
|
41
|
+
// Session handlers (Linear forwarding)
|
|
42
|
+
export { createSessionActivityHandler } from './handlers/sessions/activity.js';
|
|
43
|
+
export { createSessionCompletionHandler } from './handlers/sessions/completion.js';
|
|
44
|
+
export { createSessionExternalUrlsHandler } from './handlers/sessions/external-urls.js';
|
|
45
|
+
export { createSessionProgressHandler } from './handlers/sessions/progress.js';
|
|
46
|
+
export { createSessionToolErrorHandler } from './handlers/sessions/tool-error.js';
|
|
47
|
+
// Public handlers
|
|
48
|
+
export { createPublicStatsHandler } from './handlers/public/stats.js';
|
|
49
|
+
export { createPublicSessionsListHandler } from './handlers/public/sessions-list.js';
|
|
50
|
+
export { createPublicSessionDetailHandler } from './handlers/public/session-detail.js';
|
|
51
|
+
// Cleanup handler
|
|
52
|
+
export { createCleanupHandler } from './handlers/cleanup.js';
|
|
53
|
+
// Config handler
|
|
54
|
+
export { createConfigHandler } from './handlers/config.js';
|
|
55
|
+
// Issue tracker proxy handler (centralized API gateway for agents/governors)
|
|
56
|
+
export { createIssueTrackerProxyHandler } from './handlers/issue-tracker-proxy/index.js';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Linear Client Resolver
|
|
3
|
+
*
|
|
4
|
+
* Provides a generic LinearClientResolver that uses:
|
|
5
|
+
* 1. Workspace-specific OAuth tokens from Redis (if configured)
|
|
6
|
+
* 2. Falls back to a global API key from environment for read-only operations
|
|
7
|
+
* (only when no organizationId is provided)
|
|
8
|
+
*
|
|
9
|
+
* IMPORTANT: When an organizationId IS provided but no OAuth token is found,
|
|
10
|
+
* we throw instead of falling back. The personal API key cannot call Agent API
|
|
11
|
+
* endpoints (createAgentActivity, createAgentSessionOnIssue, etc.) so falling
|
|
12
|
+
* back would just waste rate limit quota on guaranteed-to-fail requests.
|
|
13
|
+
*
|
|
14
|
+
* Workspace clients are cached with a 5-minute TTL so all requests within
|
|
15
|
+
* the dashboard process share ONE client per workspace (and therefore one
|
|
16
|
+
* token bucket + one circuit breaker).
|
|
17
|
+
*/
|
|
18
|
+
import { type CircuitBreakerStrategy, type RateLimiterStrategy } from '@renseiai/agentfactory-linear';
|
|
19
|
+
import type { LinearClientResolver } from './types.js';
|
|
20
|
+
/**
|
|
21
|
+
* Configuration for the default Linear client resolver.
|
|
22
|
+
*/
|
|
23
|
+
export interface DefaultLinearClientResolverConfig {
|
|
24
|
+
/** Environment variable name for the API key (default: 'LINEAR_ACCESS_TOKEN') */
|
|
25
|
+
apiKeyEnvVar?: string;
|
|
26
|
+
/** Cache TTL in ms for workspace clients (default: 300_000 = 5 min) */
|
|
27
|
+
clientCacheTtlMs?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Injectable rate limiter strategy shared across all workspace clients.
|
|
30
|
+
* Use this to inject a Redis-backed rate limiter for cross-process coordination.
|
|
31
|
+
*/
|
|
32
|
+
rateLimiterStrategy?: RateLimiterStrategy;
|
|
33
|
+
/**
|
|
34
|
+
* Injectable circuit breaker strategy shared across all workspace clients.
|
|
35
|
+
* Use this to inject a Redis-backed circuit breaker for cross-process coordination.
|
|
36
|
+
*/
|
|
37
|
+
circuitBreakerStrategy?: CircuitBreakerStrategy;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a default Linear client resolver.
|
|
41
|
+
*
|
|
42
|
+
* Resolves Linear clients with workspace-aware OAuth token support:
|
|
43
|
+
* - If an organizationId is provided and Redis is configured, attempts to
|
|
44
|
+
* fetch a workspace-specific OAuth token.
|
|
45
|
+
* - If organizationId is provided but no OAuth token is found, throws an error
|
|
46
|
+
* (prevents wasting quota on Agent API calls that require OAuth).
|
|
47
|
+
* - If no organizationId is provided, uses the global API key (for read-only operations).
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* ```typescript
|
|
51
|
+
* import { createDefaultLinearClientResolver } from '@renseiai/agentfactory-nextjs'
|
|
52
|
+
*
|
|
53
|
+
* const resolver = createDefaultLinearClientResolver()
|
|
54
|
+
* // Use in route config:
|
|
55
|
+
* const routes = createAllRoutes({ linearClient: resolver, ... })
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export declare function createDefaultLinearClientResolver(config?: DefaultLinearClientResolverConfig): LinearClientResolver;
|
|
59
|
+
//# sourceMappingURL=linear-client-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linear-client-resolver.d.ts","sourceRoot":"","sources":["../../src/linear-client-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAGL,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,EACzB,MAAM,+BAA+B,CAAA;AAMtC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAatD;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAChD,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,sBAAsB,CAAA;CAChD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iCAAiC,CAC/C,MAAM,CAAC,EAAE,iCAAiC,GACzC,oBAAoB,CAwFtB"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default Linear Client Resolver
|
|
3
|
+
*
|
|
4
|
+
* Provides a generic LinearClientResolver that uses:
|
|
5
|
+
* 1. Workspace-specific OAuth tokens from Redis (if configured)
|
|
6
|
+
* 2. Falls back to a global API key from environment for read-only operations
|
|
7
|
+
* (only when no organizationId is provided)
|
|
8
|
+
*
|
|
9
|
+
* IMPORTANT: When an organizationId IS provided but no OAuth token is found,
|
|
10
|
+
* we throw instead of falling back. The personal API key cannot call Agent API
|
|
11
|
+
* endpoints (createAgentActivity, createAgentSessionOnIssue, etc.) so falling
|
|
12
|
+
* back would just waste rate limit quota on guaranteed-to-fail requests.
|
|
13
|
+
*
|
|
14
|
+
* Workspace clients are cached with a 5-minute TTL so all requests within
|
|
15
|
+
* the dashboard process share ONE client per workspace (and therefore one
|
|
16
|
+
* token bucket + one circuit breaker).
|
|
17
|
+
*/
|
|
18
|
+
import { createLinearAgentClient, } from '@renseiai/agentfactory-linear';
|
|
19
|
+
import { getAccessToken, isRedisConfigured, createLogger, } from '@renseiai/agentfactory-server';
|
|
20
|
+
const log = createLogger('linear-client-resolver');
|
|
21
|
+
/** Default cache TTL: 5 minutes */
|
|
22
|
+
const CLIENT_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
23
|
+
/**
|
|
24
|
+
* Create a default Linear client resolver.
|
|
25
|
+
*
|
|
26
|
+
* Resolves Linear clients with workspace-aware OAuth token support:
|
|
27
|
+
* - If an organizationId is provided and Redis is configured, attempts to
|
|
28
|
+
* fetch a workspace-specific OAuth token.
|
|
29
|
+
* - If organizationId is provided but no OAuth token is found, throws an error
|
|
30
|
+
* (prevents wasting quota on Agent API calls that require OAuth).
|
|
31
|
+
* - If no organizationId is provided, uses the global API key (for read-only operations).
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* import { createDefaultLinearClientResolver } from '@renseiai/agentfactory-nextjs'
|
|
36
|
+
*
|
|
37
|
+
* const resolver = createDefaultLinearClientResolver()
|
|
38
|
+
* // Use in route config:
|
|
39
|
+
* const routes = createAllRoutes({ linearClient: resolver, ... })
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function createDefaultLinearClientResolver(config) {
|
|
43
|
+
const apiKeyEnvVar = config?.apiKeyEnvVar ?? 'LINEAR_ACCESS_TOKEN';
|
|
44
|
+
const cacheTtlMs = config?.clientCacheTtlMs ?? CLIENT_CACHE_TTL_MS;
|
|
45
|
+
let _globalClient = null;
|
|
46
|
+
/** Per-workspace client cache: workspaceId → { client, expiresAt } */
|
|
47
|
+
const clientCache = new Map();
|
|
48
|
+
function getGlobalClient() {
|
|
49
|
+
if (!_globalClient) {
|
|
50
|
+
const apiKey = process.env[apiKeyEnvVar];
|
|
51
|
+
if (!apiKey) {
|
|
52
|
+
throw new Error(`${apiKeyEnvVar} not set - Linear API operations will fail`);
|
|
53
|
+
}
|
|
54
|
+
_globalClient = createLinearAgentClient({
|
|
55
|
+
apiKey,
|
|
56
|
+
rateLimiterStrategy: config?.rateLimiterStrategy,
|
|
57
|
+
circuitBreakerStrategy: config?.circuitBreakerStrategy,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return _globalClient;
|
|
61
|
+
}
|
|
62
|
+
async function getClientForWorkspace(workspaceId) {
|
|
63
|
+
// Check cache first
|
|
64
|
+
const cached = clientCache.get(workspaceId);
|
|
65
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
66
|
+
return cached.client;
|
|
67
|
+
}
|
|
68
|
+
// Remove expired entry
|
|
69
|
+
if (cached) {
|
|
70
|
+
clientCache.delete(workspaceId);
|
|
71
|
+
}
|
|
72
|
+
if (isRedisConfigured()) {
|
|
73
|
+
const accessToken = await getAccessToken(workspaceId);
|
|
74
|
+
if (accessToken) {
|
|
75
|
+
log.debug('Using OAuth token from Redis', { workspaceId });
|
|
76
|
+
const client = createLinearAgentClient({
|
|
77
|
+
apiKey: accessToken,
|
|
78
|
+
rateLimiterStrategy: config?.rateLimiterStrategy,
|
|
79
|
+
circuitBreakerStrategy: config?.circuitBreakerStrategy,
|
|
80
|
+
});
|
|
81
|
+
// Cache the client
|
|
82
|
+
clientCache.set(workspaceId, {
|
|
83
|
+
client,
|
|
84
|
+
expiresAt: Date.now() + cacheTtlMs,
|
|
85
|
+
});
|
|
86
|
+
return client;
|
|
87
|
+
}
|
|
88
|
+
// OAuth token not found — DO NOT fall back to personal API key.
|
|
89
|
+
// Personal API keys cannot call Agent API endpoints, so falling back
|
|
90
|
+
// just wastes rate limit quota on guaranteed 400 errors.
|
|
91
|
+
log.error('No OAuth token for workspace — re-authenticate at /oauth/authorize', { workspaceId });
|
|
92
|
+
throw new Error(`No OAuth token for workspace ${workspaceId}. Re-authenticate required.`);
|
|
93
|
+
}
|
|
94
|
+
// Redis not configured — cannot resolve workspace-specific tokens.
|
|
95
|
+
// This is a configuration error when organizationId is explicitly provided.
|
|
96
|
+
log.error('Redis not configured but workspace-specific client requested', { workspaceId });
|
|
97
|
+
throw new Error(`Cannot resolve OAuth token for workspace ${workspaceId} — Redis is not configured.`);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
getClient: async (organizationId) => organizationId
|
|
101
|
+
? await getClientForWorkspace(organizationId)
|
|
102
|
+
: getGlobalClient(),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron Authentication Middleware
|
|
3
|
+
*
|
|
4
|
+
* Verifies cron job requests using CRON_SECRET or Vercel Cron headers.
|
|
5
|
+
* In development, allows requests without secret.
|
|
6
|
+
*/
|
|
7
|
+
import { NextRequest } from 'next/server';
|
|
8
|
+
/**
|
|
9
|
+
* Verify cron authentication
|
|
10
|
+
*
|
|
11
|
+
* Requires CRON_SECRET in production to prevent abuse.
|
|
12
|
+
* In development, allows requests without secret.
|
|
13
|
+
*
|
|
14
|
+
* @param request - Next.js request object
|
|
15
|
+
* @param cronSecret - Optional override for CRON_SECRET env var
|
|
16
|
+
*/
|
|
17
|
+
export declare function verifyCronAuth(request: NextRequest, cronSecret?: string): {
|
|
18
|
+
authorized: boolean;
|
|
19
|
+
reason?: string;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=cron-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron-auth.d.ts","sourceRoot":"","sources":["../../../src/middleware/cron-auth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAKzC;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,WAAW,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB;IAAE,UAAU,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAkC1C"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cron Authentication Middleware
|
|
3
|
+
*
|
|
4
|
+
* Verifies cron job requests using CRON_SECRET or Vercel Cron headers.
|
|
5
|
+
* In development, allows requests without secret.
|
|
6
|
+
*/
|
|
7
|
+
import { createLogger } from '@renseiai/agentfactory-server';
|
|
8
|
+
const log = createLogger('cron-auth');
|
|
9
|
+
/**
|
|
10
|
+
* Verify cron authentication
|
|
11
|
+
*
|
|
12
|
+
* Requires CRON_SECRET in production to prevent abuse.
|
|
13
|
+
* In development, allows requests without secret.
|
|
14
|
+
*
|
|
15
|
+
* @param request - Next.js request object
|
|
16
|
+
* @param cronSecret - Optional override for CRON_SECRET env var
|
|
17
|
+
*/
|
|
18
|
+
export function verifyCronAuth(request, cronSecret) {
|
|
19
|
+
const secret = cronSecret ?? process.env.CRON_SECRET;
|
|
20
|
+
const isProduction = process.env.NODE_ENV === 'production' ||
|
|
21
|
+
process.env.VERCEL_ENV === 'production';
|
|
22
|
+
const isVercel = !!process.env.VERCEL;
|
|
23
|
+
// Check if running as Vercel Cron (trusted header only on Vercel)
|
|
24
|
+
const vercelCron = request.headers.get('x-vercel-cron');
|
|
25
|
+
if (isVercel && vercelCron) {
|
|
26
|
+
return { authorized: true };
|
|
27
|
+
}
|
|
28
|
+
// Check Authorization header
|
|
29
|
+
const authHeader = request.headers.get('authorization');
|
|
30
|
+
if (secret && authHeader === `Bearer ${secret}`) {
|
|
31
|
+
return { authorized: true };
|
|
32
|
+
}
|
|
33
|
+
// In production, CRON_SECRET is required
|
|
34
|
+
if (isProduction) {
|
|
35
|
+
if (!secret) {
|
|
36
|
+
return { authorized: false, reason: 'CRON_SECRET not configured' };
|
|
37
|
+
}
|
|
38
|
+
return { authorized: false, reason: 'Invalid or missing authorization' };
|
|
39
|
+
}
|
|
40
|
+
// In development, allow without secret but log warning
|
|
41
|
+
if (!secret) {
|
|
42
|
+
log.warn('CRON_SECRET not configured - allowing request in development');
|
|
43
|
+
return { authorized: true };
|
|
44
|
+
}
|
|
45
|
+
return { authorized: false, reason: 'Invalid authorization' };
|
|
46
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Middleware Factory — Edge Runtime Compatible
|
|
3
|
+
*
|
|
4
|
+
* Creates a Next.js middleware function that handles authentication,
|
|
5
|
+
* rate limiting, and security for AgentFactory API routes.
|
|
6
|
+
*
|
|
7
|
+
* IMPORTANT: This module runs in the Edge Runtime. It MUST NOT import
|
|
8
|
+
* from @renseiai/agentfactory-server (which uses Node.js crypto/ioredis).
|
|
9
|
+
* All utilities are inlined for Edge compatibility.
|
|
10
|
+
*/
|
|
11
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
12
|
+
import type { MiddlewareConfig } from './types.js';
|
|
13
|
+
/**
|
|
14
|
+
* Create an AgentFactory middleware function with configurable routes
|
|
15
|
+
* and rate limiting.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // In middleware.ts:
|
|
20
|
+
* import { createAgentFactoryMiddleware } from '@renseiai/agentfactory-nextjs'
|
|
21
|
+
*
|
|
22
|
+
* const { middleware, matcherConfig } = createAgentFactoryMiddleware()
|
|
23
|
+
* export { middleware }
|
|
24
|
+
* export const config = matcherConfig
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function createAgentFactoryMiddleware(userConfig?: MiddlewareConfig): {
|
|
28
|
+
middleware: (request: NextRequest) => NextResponse | undefined;
|
|
29
|
+
matcherConfig: {
|
|
30
|
+
matcher: string[];
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/middleware/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AA0GlD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,4BAA4B,CAAC,UAAU,CAAC,EAAE,gBAAgB;0BAO3C,WAAW,KAAG,YAAY,GAAG,SAAS;;;;EAkHpE"}
|