stagent 0.1.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 +191 -0
- package/README.md +399 -0
- package/components.json +21 -0
- package/dist/cli.js +171 -0
- package/drizzle.config.ts +12 -0
- package/next.config.mjs +15 -0
- package/package.json +114 -0
- package/postcss.config.mjs +8 -0
- package/public/icon-512.png +0 -0
- package/public/icon.svg +13 -0
- package/public/readme/home-workspace.png +0 -0
- package/public/readme/inbox-approvals.png +0 -0
- package/public/readme/workflow-blueprints.png +0 -0
- package/public/stagent-s-128.png +0 -0
- package/public/stagent-s-64.png +0 -0
- package/src/app/api/blueprints/[id]/instantiate/route.ts +27 -0
- package/src/app/api/blueprints/[id]/route.ts +39 -0
- package/src/app/api/blueprints/import/route.ts +68 -0
- package/src/app/api/blueprints/route.ts +29 -0
- package/src/app/api/command-palette/recent/route.ts +31 -0
- package/src/app/api/data/clear/route.ts +22 -0
- package/src/app/api/data/seed/route.ts +22 -0
- package/src/app/api/documents/[id]/file/route.ts +44 -0
- package/src/app/api/documents/[id]/route.ts +123 -0
- package/src/app/api/documents/route.ts +59 -0
- package/src/app/api/logs/stream/route.ts +101 -0
- package/src/app/api/notifications/[id]/route.ts +36 -0
- package/src/app/api/notifications/mark-all-read/route.ts +13 -0
- package/src/app/api/notifications/pending-approvals/route.ts +10 -0
- package/src/app/api/notifications/pending-approvals/stream/route.ts +101 -0
- package/src/app/api/notifications/route.ts +34 -0
- package/src/app/api/permissions/route.ts +46 -0
- package/src/app/api/profiles/[id]/route.ts +79 -0
- package/src/app/api/profiles/[id]/test/route.ts +42 -0
- package/src/app/api/profiles/import/route.ts +108 -0
- package/src/app/api/profiles/route.ts +50 -0
- package/src/app/api/projects/[id]/route.ts +72 -0
- package/src/app/api/projects/route.ts +53 -0
- package/src/app/api/schedules/[id]/route.ts +185 -0
- package/src/app/api/schedules/route.ts +117 -0
- package/src/app/api/settings/budgets/route.ts +24 -0
- package/src/app/api/settings/openai/route.ts +24 -0
- package/src/app/api/settings/route.ts +21 -0
- package/src/app/api/settings/test/route.ts +26 -0
- package/src/app/api/tasks/[id]/cancel/route.ts +21 -0
- package/src/app/api/tasks/[id]/execute/route.ts +90 -0
- package/src/app/api/tasks/[id]/logs/route.ts +95 -0
- package/src/app/api/tasks/[id]/output/route.ts +47 -0
- package/src/app/api/tasks/[id]/respond/route.ts +64 -0
- package/src/app/api/tasks/[id]/resume/route.ts +76 -0
- package/src/app/api/tasks/[id]/route.ts +77 -0
- package/src/app/api/tasks/assist/route.ts +35 -0
- package/src/app/api/tasks/route.ts +82 -0
- package/src/app/api/uploads/[id]/route.ts +81 -0
- package/src/app/api/uploads/cleanup/route.ts +7 -0
- package/src/app/api/uploads/route.ts +66 -0
- package/src/app/api/workflows/[id]/execute/route.ts +82 -0
- package/src/app/api/workflows/[id]/route.ts +133 -0
- package/src/app/api/workflows/[id]/status/route.ts +54 -0
- package/src/app/api/workflows/[id]/steps/[stepId]/retry/route.ts +22 -0
- package/src/app/api/workflows/route.ts +61 -0
- package/src/app/apple-icon.tsx +31 -0
- package/src/app/costs/page.tsx +256 -0
- package/src/app/dashboard/page.tsx +44 -0
- package/src/app/documents/[id]/page.tsx +46 -0
- package/src/app/documents/page.tsx +45 -0
- package/src/app/error.tsx +26 -0
- package/src/app/global-error.tsx +23 -0
- package/src/app/globals.css +733 -0
- package/src/app/icon.tsx +30 -0
- package/src/app/inbox/loading.tsx +15 -0
- package/src/app/inbox/page.tsx +35 -0
- package/src/app/layout.tsx +78 -0
- package/src/app/manifest.ts +32 -0
- package/src/app/monitor/page.tsx +37 -0
- package/src/app/page.tsx +162 -0
- package/src/app/profiles/[id]/edit/page.tsx +39 -0
- package/src/app/profiles/[id]/page.tsx +33 -0
- package/src/app/profiles/new/page.tsx +22 -0
- package/src/app/profiles/page.tsx +19 -0
- package/src/app/projects/[id]/page.tsx +134 -0
- package/src/app/projects/loading.tsx +17 -0
- package/src/app/projects/page.tsx +32 -0
- package/src/app/schedules/[id]/page.tsx +47 -0
- package/src/app/schedules/page.tsx +18 -0
- package/src/app/settings/loading.tsx +24 -0
- package/src/app/settings/page.tsx +27 -0
- package/src/app/tasks/[id]/page.tsx +45 -0
- package/src/app/tasks/new/page.tsx +27 -0
- package/src/app/workflows/[id]/edit/page.tsx +66 -0
- package/src/app/workflows/[id]/page.tsx +37 -0
- package/src/app/workflows/blueprints/[id]/page.tsx +40 -0
- package/src/app/workflows/blueprints/new/page.tsx +20 -0
- package/src/app/workflows/blueprints/page.tsx +11 -0
- package/src/app/workflows/new/page.tsx +36 -0
- package/src/app/workflows/page.tsx +18 -0
- package/src/components/charts/donut-ring.tsx +64 -0
- package/src/components/charts/mini-bar.tsx +75 -0
- package/src/components/charts/sparkline.tsx +107 -0
- package/src/components/costs/cost-dashboard.tsx +877 -0
- package/src/components/costs/cost-filters.tsx +179 -0
- package/src/components/dashboard/activity-feed.tsx +95 -0
- package/src/components/dashboard/greeting.tsx +30 -0
- package/src/components/dashboard/priority-queue.tsx +79 -0
- package/src/components/dashboard/quick-actions.tsx +62 -0
- package/src/components/dashboard/recent-projects.tsx +79 -0
- package/src/components/dashboard/stats-cards.tsx +114 -0
- package/src/components/documents/document-browser.tsx +235 -0
- package/src/components/documents/document-detail-view.tsx +367 -0
- package/src/components/documents/document-grid.tsx +78 -0
- package/src/components/documents/document-preview.tsx +68 -0
- package/src/components/documents/document-table.tsx +119 -0
- package/src/components/documents/document-upload-dialog.tsx +153 -0
- package/src/components/documents/types.ts +6 -0
- package/src/components/documents/utils.ts +57 -0
- package/src/components/monitoring/connection-indicator.tsx +14 -0
- package/src/components/monitoring/log-entry.tsx +79 -0
- package/src/components/monitoring/log-filters.tsx +57 -0
- package/src/components/monitoring/log-stream.tsx +144 -0
- package/src/components/monitoring/monitor-overview-wrapper.tsx +64 -0
- package/src/components/monitoring/monitor-overview.tsx +119 -0
- package/src/components/notifications/failure-action.tsx +38 -0
- package/src/components/notifications/inbox-list.tsx +165 -0
- package/src/components/notifications/message-response.tsx +196 -0
- package/src/components/notifications/notification-item.tsx +250 -0
- package/src/components/notifications/pending-approval-host.tsx +478 -0
- package/src/components/notifications/permission-action.tsx +37 -0
- package/src/components/notifications/permission-response-actions.tsx +126 -0
- package/src/components/notifications/unread-badge.tsx +35 -0
- package/src/components/profiles/profile-browser.tsx +117 -0
- package/src/components/profiles/profile-card.tsx +78 -0
- package/src/components/profiles/profile-detail-view.tsx +564 -0
- package/src/components/profiles/profile-form-view.tsx +480 -0
- package/src/components/profiles/profile-import-dialog.tsx +113 -0
- package/src/components/projects/project-card.tsx +58 -0
- package/src/components/projects/project-create-dialog.tsx +140 -0
- package/src/components/projects/project-detail.tsx +68 -0
- package/src/components/projects/project-edit-dialog.tsx +219 -0
- package/src/components/projects/project-list.tsx +108 -0
- package/src/components/schedules/schedule-create-dialog.tsx +403 -0
- package/src/components/schedules/schedule-detail-view.tsx +274 -0
- package/src/components/schedules/schedule-list.tsx +242 -0
- package/src/components/schedules/schedule-status-badge.tsx +16 -0
- package/src/components/settings/api-key-form.tsx +141 -0
- package/src/components/settings/auth-config-section.tsx +141 -0
- package/src/components/settings/auth-method-selector.tsx +67 -0
- package/src/components/settings/auth-status-badge.tsx +40 -0
- package/src/components/settings/auth-status-dot.tsx +59 -0
- package/src/components/settings/budget-guardrails-section.tsx +842 -0
- package/src/components/settings/data-management-section.tsx +141 -0
- package/src/components/settings/openai-runtime-section.tsx +104 -0
- package/src/components/settings/permissions-section.tsx +91 -0
- package/src/components/shared/app-sidebar.tsx +123 -0
- package/src/components/shared/card-skeleton.tsx +42 -0
- package/src/components/shared/command-palette.tsx +250 -0
- package/src/components/shared/confirm-dialog.tsx +52 -0
- package/src/components/shared/empty-state.tsx +24 -0
- package/src/components/shared/error-state.tsx +32 -0
- package/src/components/shared/form-section-card.tsx +33 -0
- package/src/components/shared/section-heading.tsx +14 -0
- package/src/components/shared/stagent-logo.tsx +21 -0
- package/src/components/shared/theme-toggle.tsx +46 -0
- package/src/components/tasks/ai-assist-panel.tsx +210 -0
- package/src/components/tasks/content-preview.tsx +89 -0
- package/src/components/tasks/empty-board.tsx +12 -0
- package/src/components/tasks/file-upload.tsx +120 -0
- package/src/components/tasks/kanban-board.tsx +275 -0
- package/src/components/tasks/kanban-column.tsx +75 -0
- package/src/components/tasks/skeleton-board.tsx +21 -0
- package/src/components/tasks/task-attachments.tsx +114 -0
- package/src/components/tasks/task-card.tsx +101 -0
- package/src/components/tasks/task-create-panel.tsx +360 -0
- package/src/components/tasks/task-detail-view.tsx +356 -0
- package/src/components/ui/alert-dialog.tsx +196 -0
- package/src/components/ui/badge.tsx +50 -0
- package/src/components/ui/button.tsx +71 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox.tsx +32 -0
- package/src/components/ui/command.tsx +184 -0
- package/src/components/ui/dialog.tsx +158 -0
- package/src/components/ui/dropdown-menu.tsx +257 -0
- package/src/components/ui/form.tsx +167 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +24 -0
- package/src/components/ui/popover.tsx +89 -0
- package/src/components/ui/progress.tsx +31 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/scroll-area.tsx +58 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +143 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +63 -0
- package/src/components/ui/sonner.tsx +36 -0
- package/src/components/ui/switch.tsx +35 -0
- package/src/components/ui/table.tsx +116 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +57 -0
- package/src/components/workflows/blueprint-editor.tsx +109 -0
- package/src/components/workflows/blueprint-gallery.tsx +155 -0
- package/src/components/workflows/blueprint-preview.tsx +240 -0
- package/src/components/workflows/loop-status-view.tsx +272 -0
- package/src/components/workflows/swarm-dashboard.tsx +185 -0
- package/src/components/workflows/workflow-form-view.tsx +1376 -0
- package/src/components/workflows/workflow-list.tsx +230 -0
- package/src/components/workflows/workflow-status-view.tsx +477 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/instrumentation.ts +7 -0
- package/src/lib/agents/claude-agent.ts +737 -0
- package/src/lib/agents/execution-manager.ts +27 -0
- package/src/lib/agents/profiles/assignment-validation.ts +75 -0
- package/src/lib/agents/profiles/builtins/code-reviewer/SKILL.md +21 -0
- package/src/lib/agents/profiles/builtins/code-reviewer/profile.yaml +28 -0
- package/src/lib/agents/profiles/builtins/data-analyst/SKILL.md +25 -0
- package/src/lib/agents/profiles/builtins/data-analyst/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/devops-engineer/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/devops-engineer/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/document-writer/SKILL.md +16 -0
- package/src/lib/agents/profiles/builtins/document-writer/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/general/SKILL.md +13 -0
- package/src/lib/agents/profiles/builtins/general/profile.yaml +18 -0
- package/src/lib/agents/profiles/builtins/health-fitness-coach/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/health-fitness-coach/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/learning-coach/SKILL.md +35 -0
- package/src/lib/agents/profiles/builtins/learning-coach/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/SKILL.md +26 -0
- package/src/lib/agents/profiles/builtins/project-manager/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/researcher/SKILL.md +15 -0
- package/src/lib/agents/profiles/builtins/researcher/profile.yaml +27 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/SKILL.md +34 -0
- package/src/lib/agents/profiles/builtins/shopping-assistant/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/technical-writer/SKILL.md +31 -0
- package/src/lib/agents/profiles/builtins/technical-writer/profile.yaml +29 -0
- package/src/lib/agents/profiles/builtins/travel-planner/SKILL.md +23 -0
- package/src/lib/agents/profiles/builtins/travel-planner/profile.yaml +26 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/SKILL.md +24 -0
- package/src/lib/agents/profiles/builtins/wealth-manager/profile.yaml +26 -0
- package/src/lib/agents/profiles/compatibility.ts +109 -0
- package/src/lib/agents/profiles/registry.ts +293 -0
- package/src/lib/agents/profiles/test-runner.ts +18 -0
- package/src/lib/agents/profiles/test-types.ts +20 -0
- package/src/lib/agents/profiles/types.ts +43 -0
- package/src/lib/agents/router.ts +56 -0
- package/src/lib/agents/runtime/catalog.ts +85 -0
- package/src/lib/agents/runtime/claude-sdk.ts +12 -0
- package/src/lib/agents/runtime/claude.ts +370 -0
- package/src/lib/agents/runtime/codex-app-server-client.ts +289 -0
- package/src/lib/agents/runtime/index.ts +167 -0
- package/src/lib/agents/runtime/openai-codex.ts +1089 -0
- package/src/lib/agents/runtime/task-assist-types.ts +8 -0
- package/src/lib/agents/runtime/types.ts +30 -0
- package/src/lib/constants/settings.ts +13 -0
- package/src/lib/constants/status-colors.ts +44 -0
- package/src/lib/constants/task-status.ts +49 -0
- package/src/lib/data/clear.ts +63 -0
- package/src/lib/data/seed-data/documents.ts +715 -0
- package/src/lib/data/seed-data/logs.ts +195 -0
- package/src/lib/data/seed-data/notifications.ts +141 -0
- package/src/lib/data/seed-data/profiles.ts +175 -0
- package/src/lib/data/seed-data/projects.ts +61 -0
- package/src/lib/data/seed-data/schedules.ts +108 -0
- package/src/lib/data/seed-data/tasks.ts +341 -0
- package/src/lib/data/seed-data/usage-ledger.ts +130 -0
- package/src/lib/data/seed-data/workflows.ts +213 -0
- package/src/lib/data/seed.ts +129 -0
- package/src/lib/db/index.ts +221 -0
- package/src/lib/db/migrations/0000_aromatic_gargoyle.sql +59 -0
- package/src/lib/db/migrations/0001_first_iron_patriot.sql +6 -0
- package/src/lib/db/migrations/0002_add_resume_count.sql +1 -0
- package/src/lib/db/migrations/0003_add_settings.sql +5 -0
- package/src/lib/db/migrations/0004_add_documents.sql +20 -0
- package/src/lib/db/migrations/0005_add_document_preprocessing.sql +4 -0
- package/src/lib/db/migrations/0006_add_agent_profile.sql +2 -0
- package/src/lib/db/migrations/0007_add_usage_metering_ledger.sql +30 -0
- package/src/lib/db/migrations/0008_add_document_version.sql +1 -0
- package/src/lib/db/migrations/meta/0000_snapshot.json +416 -0
- package/src/lib/db/migrations/meta/0001_snapshot.json +461 -0
- package/src/lib/db/migrations/meta/0002_snapshot.json +469 -0
- package/src/lib/db/migrations/meta/_journal.json +27 -0
- package/src/lib/db/schema.ts +227 -0
- package/src/lib/documents/cleanup.ts +50 -0
- package/src/lib/documents/context-builder.ts +75 -0
- package/src/lib/documents/output-scanner.ts +166 -0
- package/src/lib/documents/processor.ts +120 -0
- package/src/lib/documents/processors/image.ts +21 -0
- package/src/lib/documents/processors/office.ts +36 -0
- package/src/lib/documents/processors/pdf.ts +12 -0
- package/src/lib/documents/processors/spreadsheet.ts +18 -0
- package/src/lib/documents/processors/text.ts +8 -0
- package/src/lib/documents/registry.ts +25 -0
- package/src/lib/notifications/actionable.ts +108 -0
- package/src/lib/notifications/permissions.ts +169 -0
- package/src/lib/queries/chart-data.ts +184 -0
- package/src/lib/schedules/interval-parser.ts +110 -0
- package/src/lib/schedules/scheduler.ts +220 -0
- package/src/lib/settings/auth.ts +98 -0
- package/src/lib/settings/budget-guardrails.ts +590 -0
- package/src/lib/settings/helpers.ts +23 -0
- package/src/lib/settings/openai-auth.ts +80 -0
- package/src/lib/settings/permissions.ts +102 -0
- package/src/lib/usage/ledger.ts +489 -0
- package/src/lib/usage/pricing.ts +68 -0
- package/src/lib/utils/crypto.ts +90 -0
- package/src/lib/utils/format-timestamp.ts +46 -0
- package/src/lib/utils/session-cleanup.ts +26 -0
- package/src/lib/utils/stagent-paths.ts +18 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/validators/blueprint.ts +43 -0
- package/src/lib/validators/profile.ts +64 -0
- package/src/lib/validators/project.ts +17 -0
- package/src/lib/validators/settings.ts +57 -0
- package/src/lib/validators/task.ts +30 -0
- package/src/lib/workflows/blueprints/builtins/code-review-pipeline.yaml +72 -0
- package/src/lib/workflows/blueprints/builtins/documentation-generation.yaml +62 -0
- package/src/lib/workflows/blueprints/builtins/investment-research.yaml +81 -0
- package/src/lib/workflows/blueprints/builtins/meal-planning.yaml +73 -0
- package/src/lib/workflows/blueprints/builtins/product-research.yaml +72 -0
- package/src/lib/workflows/blueprints/builtins/research-report.yaml +77 -0
- package/src/lib/workflows/blueprints/builtins/sprint-planning.yaml +77 -0
- package/src/lib/workflows/blueprints/builtins/travel-planning.yaml +80 -0
- package/src/lib/workflows/blueprints/instantiator.ts +131 -0
- package/src/lib/workflows/blueprints/registry.ts +128 -0
- package/src/lib/workflows/blueprints/template.ts +58 -0
- package/src/lib/workflows/blueprints/types.ts +38 -0
- package/src/lib/workflows/definition-validation.ts +121 -0
- package/src/lib/workflows/engine.ts +1113 -0
- package/src/lib/workflows/loop-executor.ts +270 -0
- package/src/lib/workflows/parallel.ts +55 -0
- package/src/lib/workflows/swarm.ts +97 -0
- package/src/lib/workflows/types.ts +112 -0
- package/tsconfig.json +41 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { createProfile, reloadProfiles } from "@/lib/agents/profiles/registry";
|
|
3
|
+
import { ProfileConfigSchema } from "@/lib/validators/profile";
|
|
4
|
+
import yaml from "js-yaml";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* POST /api/profiles/import
|
|
8
|
+
*
|
|
9
|
+
* Import a profile from a GitHub URL. Supports:
|
|
10
|
+
* - Raw GitHub URLs to profile.yaml (fetches adjacent SKILL.md)
|
|
11
|
+
* - GitHub repo directory URLs (e.g. github.com/user/repo/tree/main/.claude/skills/my-profile)
|
|
12
|
+
*/
|
|
13
|
+
export async function POST(req: NextRequest) {
|
|
14
|
+
try {
|
|
15
|
+
const { url } = await req.json();
|
|
16
|
+
|
|
17
|
+
if (!url || typeof url !== "string") {
|
|
18
|
+
return NextResponse.json({ error: "url is required" }, { status: 400 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Convert GitHub web URL to raw content URL
|
|
22
|
+
const rawBase = toRawGitHubUrl(url);
|
|
23
|
+
if (!rawBase) {
|
|
24
|
+
return NextResponse.json(
|
|
25
|
+
{ error: "Only GitHub URLs are supported (github.com or raw.githubusercontent.com)" },
|
|
26
|
+
{ status: 400 }
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Fetch profile.yaml
|
|
31
|
+
const yamlUrl = rawBase.endsWith("profile.yaml") ? rawBase : `${rawBase}/profile.yaml`;
|
|
32
|
+
const yamlRes = await fetch(yamlUrl);
|
|
33
|
+
if (!yamlRes.ok) {
|
|
34
|
+
return NextResponse.json(
|
|
35
|
+
{ error: `Failed to fetch profile.yaml: ${yamlRes.status}` },
|
|
36
|
+
{ status: 400 }
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const yamlText = await yamlRes.text();
|
|
40
|
+
const parsed = yaml.load(yamlText);
|
|
41
|
+
const result = ProfileConfigSchema.safeParse(parsed);
|
|
42
|
+
if (!result.success) {
|
|
43
|
+
return NextResponse.json(
|
|
44
|
+
{ error: `Invalid profile.yaml: ${result.error.issues.map((i) => i.message).join(", ")}` },
|
|
45
|
+
{ status: 400 }
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Fetch SKILL.md (optional — profile works without it)
|
|
50
|
+
const skillBase = yamlUrl.replace(/profile\.yaml$/, "");
|
|
51
|
+
const skillUrl = `${skillBase}SKILL.md`;
|
|
52
|
+
let skillMd = "";
|
|
53
|
+
try {
|
|
54
|
+
const skillRes = await fetch(skillUrl);
|
|
55
|
+
if (skillRes.ok) {
|
|
56
|
+
skillMd = await skillRes.text();
|
|
57
|
+
}
|
|
58
|
+
} catch {
|
|
59
|
+
// SKILL.md is optional — continue without it
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Create profile via registry
|
|
63
|
+
createProfile(result.data, skillMd);
|
|
64
|
+
reloadProfiles();
|
|
65
|
+
|
|
66
|
+
return NextResponse.json(
|
|
67
|
+
{ ok: true, id: result.data.id, name: result.data.name },
|
|
68
|
+
{ status: 201 }
|
|
69
|
+
);
|
|
70
|
+
} catch (err: unknown) {
|
|
71
|
+
const message = err instanceof Error ? err.message : "Import failed";
|
|
72
|
+
return NextResponse.json({ error: message }, { status: 400 });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Convert various GitHub URL formats to raw content base URL.
|
|
78
|
+
*
|
|
79
|
+
* Supports:
|
|
80
|
+
* - https://raw.githubusercontent.com/user/repo/branch/path → as-is
|
|
81
|
+
* - https://github.com/user/repo/tree/branch/path → raw.githubusercontent.com
|
|
82
|
+
* - https://github.com/user/repo/blob/branch/path → raw.githubusercontent.com
|
|
83
|
+
*/
|
|
84
|
+
function toRawGitHubUrl(url: string): string | null {
|
|
85
|
+
try {
|
|
86
|
+
const u = new URL(url);
|
|
87
|
+
|
|
88
|
+
// Already a raw URL
|
|
89
|
+
if (u.hostname === "raw.githubusercontent.com") {
|
|
90
|
+
return url.replace(/\/$/, "");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (u.hostname !== "github.com") return null;
|
|
94
|
+
|
|
95
|
+
// github.com/user/repo/tree/branch/path or github.com/user/repo/blob/branch/path
|
|
96
|
+
const match = u.pathname.match(
|
|
97
|
+
/^\/([^/]+)\/([^/]+)\/(tree|blob)\/([^/]+)\/(.+)/
|
|
98
|
+
);
|
|
99
|
+
if (match) {
|
|
100
|
+
const [, owner, repo, , branch, filePath] = match;
|
|
101
|
+
return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`.replace(/\/$/, "");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return null;
|
|
105
|
+
} catch {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { listProfiles, createProfile, isBuiltin } from "@/lib/agents/profiles/registry";
|
|
3
|
+
import { ProfileConfigSchema } from "@/lib/validators/profile";
|
|
4
|
+
|
|
5
|
+
export async function GET() {
|
|
6
|
+
const profiles = listProfiles().map((p) => ({
|
|
7
|
+
id: p.id,
|
|
8
|
+
name: p.name,
|
|
9
|
+
description: p.description,
|
|
10
|
+
domain: p.domain,
|
|
11
|
+
tags: p.tags,
|
|
12
|
+
skillMd: p.skillMd,
|
|
13
|
+
allowedTools: p.allowedTools,
|
|
14
|
+
mcpServers: p.mcpServers,
|
|
15
|
+
canUseToolPolicy: p.canUseToolPolicy,
|
|
16
|
+
temperature: p.temperature,
|
|
17
|
+
maxTurns: p.maxTurns,
|
|
18
|
+
outputFormat: p.outputFormat,
|
|
19
|
+
version: p.version,
|
|
20
|
+
author: p.author,
|
|
21
|
+
source: p.source,
|
|
22
|
+
tests: p.tests,
|
|
23
|
+
supportedRuntimes: p.supportedRuntimes,
|
|
24
|
+
runtimeOverrides: p.runtimeOverrides,
|
|
25
|
+
isBuiltin: isBuiltin(p.id),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
return NextResponse.json(profiles);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function POST(req: NextRequest) {
|
|
32
|
+
try {
|
|
33
|
+
const body = await req.json();
|
|
34
|
+
const { skillMd, ...configFields } = body;
|
|
35
|
+
|
|
36
|
+
const result = ProfileConfigSchema.safeParse(configFields);
|
|
37
|
+
if (!result.success) {
|
|
38
|
+
return NextResponse.json(
|
|
39
|
+
{ error: result.error.issues.map((i) => i.message).join(", ") },
|
|
40
|
+
{ status: 400 }
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
createProfile(result.data, skillMd ?? "");
|
|
45
|
+
return NextResponse.json({ ok: true }, { status: 201 });
|
|
46
|
+
} catch (err: unknown) {
|
|
47
|
+
const message = err instanceof Error ? err.message : "Failed to create profile";
|
|
48
|
+
return NextResponse.json({ error: message }, { status: 400 });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { db } from "@/lib/db";
|
|
3
|
+
import { projects, tasks } from "@/lib/db/schema";
|
|
4
|
+
import { eq } from "drizzle-orm";
|
|
5
|
+
import { updateProjectSchema } from "@/lib/validators/project";
|
|
6
|
+
|
|
7
|
+
export async function GET(
|
|
8
|
+
_req: NextRequest,
|
|
9
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
10
|
+
) {
|
|
11
|
+
const { id } = await params;
|
|
12
|
+
const [project] = await db
|
|
13
|
+
.select()
|
|
14
|
+
.from(projects)
|
|
15
|
+
.where(eq(projects.id, id));
|
|
16
|
+
|
|
17
|
+
if (!project) {
|
|
18
|
+
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
19
|
+
}
|
|
20
|
+
return NextResponse.json(project);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function PATCH(
|
|
24
|
+
req: NextRequest,
|
|
25
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
26
|
+
) {
|
|
27
|
+
const { id } = await params;
|
|
28
|
+
const body = await req.json();
|
|
29
|
+
const parsed = updateProjectSchema.safeParse(body);
|
|
30
|
+
if (!parsed.success) {
|
|
31
|
+
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
await db
|
|
35
|
+
.update(projects)
|
|
36
|
+
.set({ ...parsed.data, updatedAt: new Date() })
|
|
37
|
+
.where(eq(projects.id, id));
|
|
38
|
+
|
|
39
|
+
const [updated] = await db
|
|
40
|
+
.select()
|
|
41
|
+
.from(projects)
|
|
42
|
+
.where(eq(projects.id, id));
|
|
43
|
+
|
|
44
|
+
if (!updated) {
|
|
45
|
+
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
46
|
+
}
|
|
47
|
+
return NextResponse.json(updated);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function DELETE(
|
|
51
|
+
_req: NextRequest,
|
|
52
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
53
|
+
) {
|
|
54
|
+
const { id } = await params;
|
|
55
|
+
const [existing] = await db
|
|
56
|
+
.select()
|
|
57
|
+
.from(projects)
|
|
58
|
+
.where(eq(projects.id, id));
|
|
59
|
+
|
|
60
|
+
if (!existing) {
|
|
61
|
+
return NextResponse.json({ error: "Not found" }, { status: 404 });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Disassociate tasks before deleting project (avoids FK constraint failure)
|
|
65
|
+
await db
|
|
66
|
+
.update(tasks)
|
|
67
|
+
.set({ projectId: null, updatedAt: new Date() })
|
|
68
|
+
.where(eq(tasks.projectId, id));
|
|
69
|
+
|
|
70
|
+
await db.delete(projects).where(eq(projects.id, id));
|
|
71
|
+
return NextResponse.json({ success: true });
|
|
72
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { db } from "@/lib/db";
|
|
3
|
+
import { projects, tasks } from "@/lib/db/schema";
|
|
4
|
+
import { eq, count } from "drizzle-orm";
|
|
5
|
+
import { createProjectSchema } from "@/lib/validators/project";
|
|
6
|
+
|
|
7
|
+
export async function GET() {
|
|
8
|
+
const result = await db
|
|
9
|
+
.select({
|
|
10
|
+
id: projects.id,
|
|
11
|
+
name: projects.name,
|
|
12
|
+
description: projects.description,
|
|
13
|
+
workingDirectory: projects.workingDirectory,
|
|
14
|
+
status: projects.status,
|
|
15
|
+
createdAt: projects.createdAt,
|
|
16
|
+
updatedAt: projects.updatedAt,
|
|
17
|
+
taskCount: count(tasks.id),
|
|
18
|
+
})
|
|
19
|
+
.from(projects)
|
|
20
|
+
.leftJoin(tasks, eq(tasks.projectId, projects.id))
|
|
21
|
+
.groupBy(projects.id)
|
|
22
|
+
.orderBy(projects.createdAt);
|
|
23
|
+
|
|
24
|
+
return NextResponse.json(result);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function POST(req: NextRequest) {
|
|
28
|
+
const body = await req.json();
|
|
29
|
+
const parsed = createProjectSchema.safeParse(body);
|
|
30
|
+
if (!parsed.success) {
|
|
31
|
+
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const now = new Date();
|
|
35
|
+
const id = crypto.randomUUID();
|
|
36
|
+
|
|
37
|
+
await db.insert(projects).values({
|
|
38
|
+
id,
|
|
39
|
+
name: parsed.data.name,
|
|
40
|
+
description: parsed.data.description ?? null,
|
|
41
|
+
workingDirectory: parsed.data.workingDirectory ?? null,
|
|
42
|
+
status: "active",
|
|
43
|
+
createdAt: now,
|
|
44
|
+
updatedAt: now,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const [project] = await db
|
|
48
|
+
.select()
|
|
49
|
+
.from(projects)
|
|
50
|
+
.where(eq(projects.id, id));
|
|
51
|
+
|
|
52
|
+
return NextResponse.json(project, { status: 201 });
|
|
53
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { db } from "@/lib/db";
|
|
3
|
+
import { schedules, tasks } from "@/lib/db/schema";
|
|
4
|
+
import { eq, like } from "drizzle-orm";
|
|
5
|
+
import { parseInterval, computeNextFireTime } from "@/lib/schedules/interval-parser";
|
|
6
|
+
import { resolveAgentRuntime } from "@/lib/agents/runtime/catalog";
|
|
7
|
+
import { validateRuntimeProfileAssignment } from "@/lib/agents/profiles/assignment-validation";
|
|
8
|
+
|
|
9
|
+
export async function GET(
|
|
10
|
+
_req: NextRequest,
|
|
11
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
12
|
+
) {
|
|
13
|
+
const { id } = await params;
|
|
14
|
+
|
|
15
|
+
const [schedule] = await db
|
|
16
|
+
.select()
|
|
17
|
+
.from(schedules)
|
|
18
|
+
.where(eq(schedules.id, id));
|
|
19
|
+
|
|
20
|
+
if (!schedule) {
|
|
21
|
+
return NextResponse.json({ error: "Schedule not found" }, { status: 404 });
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Fetch child tasks (firing history) by title pattern match
|
|
25
|
+
const childTasks = await db
|
|
26
|
+
.select()
|
|
27
|
+
.from(tasks)
|
|
28
|
+
.where(like(tasks.title, `${schedule.name} — firing #%`));
|
|
29
|
+
|
|
30
|
+
return NextResponse.json({
|
|
31
|
+
...schedule,
|
|
32
|
+
firingHistory: childTasks.sort(
|
|
33
|
+
(a, b) =>
|
|
34
|
+
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
|
35
|
+
),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function PATCH(
|
|
40
|
+
req: NextRequest,
|
|
41
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
42
|
+
) {
|
|
43
|
+
const { id } = await params;
|
|
44
|
+
const body = await req.json();
|
|
45
|
+
const { status, name, prompt, interval, assignedAgent, agentProfile } = body as {
|
|
46
|
+
status?: string;
|
|
47
|
+
name?: string;
|
|
48
|
+
prompt?: string;
|
|
49
|
+
interval?: string;
|
|
50
|
+
assignedAgent?: string;
|
|
51
|
+
agentProfile?: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const [schedule] = await db
|
|
55
|
+
.select()
|
|
56
|
+
.from(schedules)
|
|
57
|
+
.where(eq(schedules.id, id));
|
|
58
|
+
|
|
59
|
+
if (!schedule) {
|
|
60
|
+
return NextResponse.json({ error: "Schedule not found" }, { status: 404 });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const now = new Date();
|
|
64
|
+
const updates: Record<string, unknown> = { updatedAt: now };
|
|
65
|
+
|
|
66
|
+
// Status transitions
|
|
67
|
+
if (status) {
|
|
68
|
+
if (status === "paused") {
|
|
69
|
+
if (schedule.status !== "active") {
|
|
70
|
+
return NextResponse.json(
|
|
71
|
+
{ error: "Can only pause an active schedule" },
|
|
72
|
+
{ status: 409 }
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
updates.status = "paused";
|
|
76
|
+
updates.nextFireAt = null;
|
|
77
|
+
} else if (status === "active") {
|
|
78
|
+
if (schedule.status !== "paused") {
|
|
79
|
+
return NextResponse.json(
|
|
80
|
+
{ error: "Can only resume a paused schedule" },
|
|
81
|
+
{ status: 409 }
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
updates.status = "active";
|
|
85
|
+
updates.nextFireAt = computeNextFireTime(schedule.cronExpression, now);
|
|
86
|
+
} else {
|
|
87
|
+
return NextResponse.json(
|
|
88
|
+
{ error: `Invalid status: ${status}` },
|
|
89
|
+
{ status: 400 }
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Field updates (only when active or paused)
|
|
95
|
+
if (name !== undefined) {
|
|
96
|
+
if (!name.trim()) {
|
|
97
|
+
return NextResponse.json({ error: "Name cannot be empty" }, { status: 400 });
|
|
98
|
+
}
|
|
99
|
+
updates.name = name.trim();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (prompt !== undefined) {
|
|
103
|
+
if (!prompt.trim()) {
|
|
104
|
+
return NextResponse.json({ error: "Prompt cannot be empty" }, { status: 400 });
|
|
105
|
+
}
|
|
106
|
+
updates.prompt = prompt.trim();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (interval !== undefined) {
|
|
110
|
+
try {
|
|
111
|
+
const cronExpression = parseInterval(interval);
|
|
112
|
+
updates.cronExpression = cronExpression;
|
|
113
|
+
// Recompute next fire time if schedule is active
|
|
114
|
+
if ((updates.status ?? schedule.status) === "active") {
|
|
115
|
+
updates.nextFireAt = computeNextFireTime(cronExpression, now);
|
|
116
|
+
}
|
|
117
|
+
} catch (err) {
|
|
118
|
+
return NextResponse.json(
|
|
119
|
+
{ error: (err as Error).message },
|
|
120
|
+
{ status: 400 }
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (agentProfile !== undefined) {
|
|
126
|
+
updates.agentProfile = agentProfile || null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (assignedAgent !== undefined) {
|
|
130
|
+
if (assignedAgent) {
|
|
131
|
+
try {
|
|
132
|
+
updates.assignedAgent = resolveAgentRuntime(assignedAgent);
|
|
133
|
+
} catch (error) {
|
|
134
|
+
return NextResponse.json(
|
|
135
|
+
{ error: error instanceof Error ? error.message : String(error) },
|
|
136
|
+
{ status: 400 }
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
updates.assignedAgent = null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const compatibilityError = validateRuntimeProfileAssignment({
|
|
145
|
+
profileId:
|
|
146
|
+
agentProfile !== undefined ? agentProfile || null : schedule.agentProfile,
|
|
147
|
+
runtimeId:
|
|
148
|
+
assignedAgent !== undefined
|
|
149
|
+
? assignedAgent || null
|
|
150
|
+
: schedule.assignedAgent,
|
|
151
|
+
context: "Schedule profile",
|
|
152
|
+
});
|
|
153
|
+
if (compatibilityError) {
|
|
154
|
+
return NextResponse.json({ error: compatibilityError }, { status: 400 });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
await db.update(schedules).set(updates).where(eq(schedules.id, id));
|
|
158
|
+
|
|
159
|
+
const [updated] = await db
|
|
160
|
+
.select()
|
|
161
|
+
.from(schedules)
|
|
162
|
+
.where(eq(schedules.id, id));
|
|
163
|
+
|
|
164
|
+
return NextResponse.json(updated);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function DELETE(
|
|
168
|
+
_req: NextRequest,
|
|
169
|
+
{ params }: { params: Promise<{ id: string }> }
|
|
170
|
+
) {
|
|
171
|
+
const { id } = await params;
|
|
172
|
+
|
|
173
|
+
const [schedule] = await db
|
|
174
|
+
.select()
|
|
175
|
+
.from(schedules)
|
|
176
|
+
.where(eq(schedules.id, id));
|
|
177
|
+
|
|
178
|
+
if (!schedule) {
|
|
179
|
+
return NextResponse.json({ error: "Schedule not found" }, { status: 404 });
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
await db.delete(schedules).where(eq(schedules.id, id));
|
|
183
|
+
|
|
184
|
+
return NextResponse.json({ deleted: true });
|
|
185
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { db } from "@/lib/db";
|
|
3
|
+
import { schedules } from "@/lib/db/schema";
|
|
4
|
+
import { desc, eq } from "drizzle-orm";
|
|
5
|
+
import { parseInterval, computeNextFireTime } from "@/lib/schedules/interval-parser";
|
|
6
|
+
import { resolveAgentRuntime } from "@/lib/agents/runtime/catalog";
|
|
7
|
+
import { validateRuntimeProfileAssignment } from "@/lib/agents/profiles/assignment-validation";
|
|
8
|
+
|
|
9
|
+
export async function GET() {
|
|
10
|
+
const result = await db
|
|
11
|
+
.select()
|
|
12
|
+
.from(schedules)
|
|
13
|
+
.orderBy(desc(schedules.createdAt));
|
|
14
|
+
|
|
15
|
+
return NextResponse.json(result);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function POST(req: NextRequest) {
|
|
19
|
+
const body = await req.json();
|
|
20
|
+
const {
|
|
21
|
+
name,
|
|
22
|
+
prompt,
|
|
23
|
+
interval,
|
|
24
|
+
projectId,
|
|
25
|
+
assignedAgent,
|
|
26
|
+
agentProfile,
|
|
27
|
+
recurs,
|
|
28
|
+
maxFirings,
|
|
29
|
+
expiresInHours,
|
|
30
|
+
} =
|
|
31
|
+
body as {
|
|
32
|
+
name?: string;
|
|
33
|
+
prompt?: string;
|
|
34
|
+
interval?: string;
|
|
35
|
+
projectId?: string;
|
|
36
|
+
assignedAgent?: string;
|
|
37
|
+
agentProfile?: string;
|
|
38
|
+
recurs?: boolean;
|
|
39
|
+
maxFirings?: number;
|
|
40
|
+
expiresInHours?: number;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (!name?.trim()) {
|
|
44
|
+
return NextResponse.json({ error: "Name is required" }, { status: 400 });
|
|
45
|
+
}
|
|
46
|
+
if (!prompt?.trim()) {
|
|
47
|
+
return NextResponse.json({ error: "Prompt is required" }, { status: 400 });
|
|
48
|
+
}
|
|
49
|
+
if (!interval?.trim()) {
|
|
50
|
+
return NextResponse.json({ error: "Interval is required" }, { status: 400 });
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Parse interval into cron expression
|
|
54
|
+
let cronExpression: string;
|
|
55
|
+
try {
|
|
56
|
+
cronExpression = parseInterval(interval);
|
|
57
|
+
} catch (err) {
|
|
58
|
+
return NextResponse.json(
|
|
59
|
+
{ error: (err as Error).message },
|
|
60
|
+
{ status: 400 }
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (assignedAgent !== undefined && assignedAgent !== null && assignedAgent !== "") {
|
|
65
|
+
try {
|
|
66
|
+
resolveAgentRuntime(assignedAgent);
|
|
67
|
+
} catch (error) {
|
|
68
|
+
return NextResponse.json(
|
|
69
|
+
{ error: error instanceof Error ? error.message : String(error) },
|
|
70
|
+
{ status: 400 }
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const compatibilityError = validateRuntimeProfileAssignment({
|
|
76
|
+
profileId: agentProfile,
|
|
77
|
+
runtimeId: assignedAgent,
|
|
78
|
+
context: "Schedule profile",
|
|
79
|
+
});
|
|
80
|
+
if (compatibilityError) {
|
|
81
|
+
return NextResponse.json({ error: compatibilityError }, { status: 400 });
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const id = crypto.randomUUID();
|
|
85
|
+
const now = new Date();
|
|
86
|
+
const nextFireAt = computeNextFireTime(cronExpression, now);
|
|
87
|
+
const shouldRecur = recurs !== false; // default to true
|
|
88
|
+
|
|
89
|
+
const expiresAt = expiresInHours
|
|
90
|
+
? new Date(now.getTime() + expiresInHours * 60 * 60 * 1000)
|
|
91
|
+
: null;
|
|
92
|
+
|
|
93
|
+
await db.insert(schedules).values({
|
|
94
|
+
id,
|
|
95
|
+
name: name.trim(),
|
|
96
|
+
prompt: prompt.trim(),
|
|
97
|
+
cronExpression,
|
|
98
|
+
projectId: projectId || null,
|
|
99
|
+
assignedAgent: assignedAgent || null,
|
|
100
|
+
agentProfile: agentProfile || null,
|
|
101
|
+
recurs: shouldRecur,
|
|
102
|
+
status: "active",
|
|
103
|
+
maxFirings: maxFirings ?? null,
|
|
104
|
+
firingCount: 0,
|
|
105
|
+
expiresAt,
|
|
106
|
+
nextFireAt,
|
|
107
|
+
createdAt: now,
|
|
108
|
+
updatedAt: now,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const [created] = await db
|
|
112
|
+
.select()
|
|
113
|
+
.from(schedules)
|
|
114
|
+
.where(eq(schedules.id, id));
|
|
115
|
+
|
|
116
|
+
return NextResponse.json(created, { status: 201 });
|
|
117
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import {
|
|
3
|
+
getBudgetGuardrailSnapshot,
|
|
4
|
+
setBudgetPolicy,
|
|
5
|
+
} from "@/lib/settings/budget-guardrails";
|
|
6
|
+
import { updateBudgetPolicySchema } from "@/lib/validators/settings";
|
|
7
|
+
|
|
8
|
+
export async function GET() {
|
|
9
|
+
const snapshot = await getBudgetGuardrailSnapshot();
|
|
10
|
+
return NextResponse.json(snapshot);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function POST(req: NextRequest) {
|
|
14
|
+
const body = await req.json();
|
|
15
|
+
const parsed = updateBudgetPolicySchema.safeParse(body);
|
|
16
|
+
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
await setBudgetPolicy(parsed.data);
|
|
22
|
+
const snapshot = await getBudgetGuardrailSnapshot();
|
|
23
|
+
return NextResponse.json(snapshot);
|
|
24
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import {
|
|
3
|
+
getOpenAIAuthSettings,
|
|
4
|
+
setOpenAIAuthSettings,
|
|
5
|
+
} from "@/lib/settings/openai-auth";
|
|
6
|
+
import { updateOpenAISettingsSchema } from "@/lib/validators/settings";
|
|
7
|
+
|
|
8
|
+
export async function GET() {
|
|
9
|
+
const settings = await getOpenAIAuthSettings();
|
|
10
|
+
return NextResponse.json(settings);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function POST(req: NextRequest) {
|
|
14
|
+
const body = await req.json();
|
|
15
|
+
const parsed = updateOpenAISettingsSchema.safeParse(body);
|
|
16
|
+
|
|
17
|
+
if (!parsed.success) {
|
|
18
|
+
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
await setOpenAIAuthSettings(parsed.data);
|
|
22
|
+
const updated = await getOpenAIAuthSettings();
|
|
23
|
+
return NextResponse.json(updated);
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getAuthSettings, setAuthSettings } from "@/lib/settings/auth";
|
|
3
|
+
import { updateAuthSettingsSchema } from "@/lib/validators/settings";
|
|
4
|
+
|
|
5
|
+
export async function GET() {
|
|
6
|
+
const settings = await getAuthSettings();
|
|
7
|
+
return NextResponse.json(settings);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function POST(req: NextRequest) {
|
|
11
|
+
const body = await req.json();
|
|
12
|
+
const parsed = updateAuthSettingsSchema.safeParse(body);
|
|
13
|
+
|
|
14
|
+
if (!parsed.success) {
|
|
15
|
+
return NextResponse.json({ error: parsed.error.flatten() }, { status: 400 });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
await setAuthSettings(parsed.data);
|
|
19
|
+
const updated = await getAuthSettings();
|
|
20
|
+
return NextResponse.json(updated);
|
|
21
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { NextResponse } from "next/server";
|
|
2
|
+
import { DEFAULT_AGENT_RUNTIME } from "@/lib/agents/runtime/catalog";
|
|
3
|
+
import { getRuntimeSummary, testRuntimeConnection } from "@/lib/agents/runtime";
|
|
4
|
+
|
|
5
|
+
export async function POST(req: Request) {
|
|
6
|
+
try {
|
|
7
|
+
const body = (await req.json().catch(() => ({}))) as {
|
|
8
|
+
runtime?: string;
|
|
9
|
+
};
|
|
10
|
+
const runtimeId = body.runtime ?? DEFAULT_AGENT_RUNTIME;
|
|
11
|
+
const result = await testRuntimeConnection(runtimeId);
|
|
12
|
+
const summary = getRuntimeSummary(runtimeId);
|
|
13
|
+
return NextResponse.json({
|
|
14
|
+
...result,
|
|
15
|
+
runtime: summary.runtime.id,
|
|
16
|
+
capabilities: summary.capabilities,
|
|
17
|
+
});
|
|
18
|
+
} catch (error: unknown) {
|
|
19
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20
|
+
|
|
21
|
+
return NextResponse.json(
|
|
22
|
+
{ connected: false, error: message },
|
|
23
|
+
{ status: 200 } // 200 so the client can read the error
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|