sync: editable workflows from private repo

- Users can edit/create workflows from Settings → Workflows tab
- File-based storage for custom workflows
- Bundled workflows serve as fallback when no user file exists
- Added createEdge tool access for workflows (Quick Link)
- Cleaned up stale terminology in workflow prompts

Files synced:
- src/services/workflows/workflowFileService.ts (NEW)
- app/api/workflows/[key]/route.ts (NEW)
- src/services/workflows/registry.ts
- src/components/settings/WorkflowsViewer.tsx
- src/tools/infrastructure/registry.ts
- src/services/agents/wiseRAHExecutor.ts
- src/config/workflows/integrate.ts
- src/config/prompts/wise-rah.ts
This commit is contained in:
“BeeRad”
2026-01-04 16:30:25 +11:00
parent 8190df8cf2
commit a3647c0693
8 changed files with 804 additions and 100 deletions
+132
View File
@@ -0,0 +1,132 @@
import { NextRequest, NextResponse } from 'next/server';
import { saveWorkflow, deleteWorkflow, userWorkflowExists } from '@/services/workflows/workflowFileService';
import { WorkflowRegistry, BUNDLED_WORKFLOW_KEYS } from '@/services/workflows/registry';
// PUT /api/workflows/[key] - Create or update a workflow
export async function PUT(
request: NextRequest,
{ params }: { params: Promise<{ key: string }> }
) {
try {
const { key } = await params;
const body = await request.json();
// Validate required fields
if (!body.displayName || typeof body.displayName !== 'string') {
return NextResponse.json(
{ success: false, error: 'displayName is required' },
{ status: 400 }
);
}
if (!body.instructions || typeof body.instructions !== 'string') {
return NextResponse.json(
{ success: false, error: 'instructions is required' },
{ status: 400 }
);
}
// Save the workflow
saveWorkflow({
key,
displayName: body.displayName.trim(),
description: body.description?.trim() || '',
instructions: body.instructions,
enabled: body.enabled !== false,
requiresFocusedNode: body.requiresFocusedNode !== false,
});
return NextResponse.json({ success: true });
} catch (error) {
console.error('Error saving workflow:', error);
return NextResponse.json(
{ success: false, error: 'Failed to save workflow' },
{ status: 500 }
);
}
}
// DELETE /api/workflows/[key] - Delete a workflow (resets to default if bundled)
export async function DELETE(
request: NextRequest,
{ params }: { params: Promise<{ key: string }> }
) {
try {
const { key } = await params;
// Check if user file exists
if (!userWorkflowExists(key)) {
// If it's a bundled workflow, nothing to delete
if (BUNDLED_WORKFLOW_KEYS.has(key)) {
return NextResponse.json(
{ success: false, error: 'Cannot delete bundled workflow (no user override exists)' },
{ status: 400 }
);
}
return NextResponse.json(
{ success: false, error: 'Workflow not found' },
{ status: 404 }
);
}
const deleted = deleteWorkflow(key);
if (!deleted) {
return NextResponse.json(
{ success: false, error: 'Failed to delete workflow' },
{ status: 500 }
);
}
// If this was a bundled workflow, it will now fall back to default
const isBundled = BUNDLED_WORKFLOW_KEYS.has(key);
return NextResponse.json({
success: true,
resetToDefault: isBundled,
});
} catch (error) {
console.error('Error deleting workflow:', error);
return NextResponse.json(
{ success: false, error: 'Failed to delete workflow' },
{ status: 500 }
);
}
}
// GET /api/workflows/[key] - Get a single workflow
export async function GET(
request: NextRequest,
{ params }: { params: Promise<{ key: string }> }
) {
try {
const { key } = await params;
const workflow = await WorkflowRegistry.getWorkflowByKey(key);
if (!workflow) {
return NextResponse.json(
{ success: false, error: 'Workflow not found' },
{ status: 404 }
);
}
// Include metadata about whether it's user-modified
const hasUserOverride = userWorkflowExists(key);
const isBundled = BUNDLED_WORKFLOW_KEYS.has(key);
return NextResponse.json({
success: true,
data: {
...workflow,
isBundled,
hasUserOverride,
},
});
} catch (error) {
console.error('Error fetching workflow:', error);
return NextResponse.json(
{ success: false, error: 'Failed to fetch workflow' },
{ status: 500 }
);
}
}