@agentforge-ai/cli 0.4.2 → 0.5.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/default/README.md +81 -81
- package/dist/default/convex/agents.ts +204 -0
- package/dist/default/convex/apiKeys.ts +133 -0
- package/dist/default/convex/cronJobs.ts +224 -0
- package/dist/default/convex/files.ts +103 -0
- package/dist/default/convex/folders.ts +110 -0
- package/dist/default/convex/heartbeat.ts +371 -0
- package/dist/default/convex/logs.ts +66 -0
- package/dist/default/convex/mastraIntegration.ts +184 -0
- package/dist/default/convex/mcpConnections.ts +127 -0
- package/dist/default/convex/messages.ts +90 -0
- package/dist/default/convex/projects.ts +114 -0
- package/dist/default/convex/sessions.ts +174 -0
- package/dist/default/convex/settings.ts +79 -0
- package/dist/default/convex/skills.ts +178 -0
- package/dist/default/convex/threads.ts +100 -0
- package/dist/default/convex/usage.ts +195 -0
- package/dist/default/convex/vault.ts +383 -0
- package/dist/default/dashboard/app/main.tsx +7 -3
- package/dist/default/dashboard/app/routes/agents.tsx +103 -161
- package/dist/default/dashboard/app/routes/chat.tsx +163 -317
- package/dist/default/dashboard/app/routes/connections.tsx +247 -386
- package/dist/default/dashboard/app/routes/cron.tsx +127 -286
- package/dist/default/dashboard/app/routes/files.tsx +184 -167
- package/dist/default/dashboard/app/routes/index.tsx +63 -96
- package/dist/default/dashboard/app/routes/projects.tsx +106 -225
- package/dist/default/dashboard/app/routes/sessions.tsx +87 -253
- package/dist/default/dashboard/app/routes/settings.tsx +316 -532
- package/dist/default/dashboard/app/routes/skills.tsx +329 -216
- package/dist/default/dashboard/app/routes/usage.tsx +107 -150
- package/dist/default/dashboard/tsconfig.json +3 -2
- package/dist/default/dashboard/vite.config.ts +6 -0
- package/dist/index.js +279 -50
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/templates/default/README.md +81 -81
- package/templates/default/convex/agents.ts +204 -0
- package/templates/default/convex/apiKeys.ts +133 -0
- package/templates/default/convex/cronJobs.ts +224 -0
- package/templates/default/convex/files.ts +103 -0
- package/templates/default/convex/folders.ts +110 -0
- package/templates/default/convex/heartbeat.ts +371 -0
- package/templates/default/convex/logs.ts +66 -0
- package/templates/default/convex/mastraIntegration.ts +184 -0
- package/templates/default/convex/mcpConnections.ts +127 -0
- package/templates/default/convex/messages.ts +90 -0
- package/templates/default/convex/projects.ts +114 -0
- package/templates/default/convex/sessions.ts +174 -0
- package/templates/default/convex/settings.ts +79 -0
- package/templates/default/convex/skills.ts +178 -0
- package/templates/default/convex/threads.ts +100 -0
- package/templates/default/convex/usage.ts +195 -0
- package/templates/default/convex/vault.ts +383 -0
- package/templates/default/dashboard/app/main.tsx +7 -3
- package/templates/default/dashboard/app/routes/agents.tsx +103 -161
- package/templates/default/dashboard/app/routes/chat.tsx +163 -317
- package/templates/default/dashboard/app/routes/connections.tsx +247 -386
- package/templates/default/dashboard/app/routes/cron.tsx +127 -286
- package/templates/default/dashboard/app/routes/files.tsx +184 -167
- package/templates/default/dashboard/app/routes/index.tsx +63 -96
- package/templates/default/dashboard/app/routes/projects.tsx +106 -225
- package/templates/default/dashboard/app/routes/sessions.tsx +87 -253
- package/templates/default/dashboard/app/routes/settings.tsx +316 -532
- package/templates/default/dashboard/app/routes/skills.tsx +329 -216
- package/templates/default/dashboard/app/routes/usage.tsx +107 -150
- package/templates/default/dashboard/tsconfig.json +3 -2
- package/templates/default/dashboard/vite.config.ts +6 -0
package/package.json
CHANGED
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
#
|
|
1
|
+
# AgentForge Project
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Welcome to your new AgentForge project! This project was scaffolded by the `@agentforge-ai/cli`.
|
|
4
4
|
|
|
5
|
-
## Quick Start
|
|
5
|
+
## 🚀 Quick Start
|
|
6
|
+
|
|
7
|
+
### 1. Install Dependencies
|
|
8
|
+
|
|
9
|
+
If this wasn't done automatically during project creation, install the dependencies for the root project and the dashboard:
|
|
6
10
|
|
|
7
11
|
```bash
|
|
8
|
-
#
|
|
9
|
-
|
|
12
|
+
# Install root dependencies
|
|
13
|
+
pnpm install
|
|
14
|
+
|
|
15
|
+
# Install dashboard dependencies
|
|
16
|
+
cd dashboard
|
|
17
|
+
pnpm install
|
|
18
|
+
cd ..
|
|
19
|
+
```
|
|
10
20
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
### 2. Configure Your Environment
|
|
22
|
+
|
|
23
|
+
Copy the example environment file and add your API keys. You only need to configure one LLM provider to get started.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cp .env.example .env.local
|
|
27
|
+
```
|
|
14
28
|
|
|
15
|
-
|
|
29
|
+
Edit `.env.local` with your keys:
|
|
30
|
+
|
|
31
|
+
```dotenv
|
|
32
|
+
# Convex URL will be set automatically by `npx convex dev`
|
|
33
|
+
CONVEX_URL=
|
|
34
|
+
|
|
35
|
+
# Add your preferred LLM provider API key
|
|
36
|
+
OPENAI_API_KEY=sk-...
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 3. Start the Development Servers
|
|
40
|
+
|
|
41
|
+
AgentForge requires two concurrent processes for local development:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Terminal 1: Start the Convex backend
|
|
16
45
|
npx convex dev
|
|
17
46
|
|
|
18
|
-
#
|
|
19
|
-
|
|
47
|
+
# Terminal 2: Launch the web dashboard
|
|
48
|
+
agentforge dashboard
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- The **Convex backend** syncs your database schema and functions, providing a dashboard at `http://localhost:8187`.
|
|
52
|
+
- The **web dashboard** provides a UI for managing your agents, available at `http://localhost:3000`.
|
|
53
|
+
|
|
54
|
+
## 🤖 Your First Agent
|
|
55
|
+
|
|
56
|
+
Your default agent is defined in `src/agent.ts`. You can customize its instructions, model, and tools.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
// src/agent.ts
|
|
60
|
+
import { Agent } from '@agentforge-ai/core';
|
|
61
|
+
|
|
62
|
+
const myAgent = new Agent({
|
|
63
|
+
id: 'my-first-agent',
|
|
64
|
+
name: 'My First Agent',
|
|
65
|
+
instructions: 'You are a helpful AI assistant...',
|
|
66
|
+
model: 'openai:gpt-4o-mini',
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export default myAgent;
|
|
20
70
|
```
|
|
21
71
|
|
|
22
72
|
## Project Structure
|
|
23
73
|
|
|
24
74
|
```
|
|
25
|
-
|
|
26
|
-
|
|
75
|
+
/my-agent-project
|
|
76
|
+
├── convex/ # Convex backend (database schema, functions)
|
|
77
|
+
├── dashboard/ # Web dashboard frontend (Vite + React)
|
|
78
|
+
├── skills/ # Custom agent skills
|
|
79
|
+
├── workspace/ # Default local file storage for agents
|
|
27
80
|
├── src/
|
|
28
|
-
│ └── agent.ts
|
|
29
|
-
├──
|
|
30
|
-
├── .env.example # Environment variable template
|
|
81
|
+
│ └── agent.ts # Your main agent definition
|
|
82
|
+
├── .env.local # Your local environment variables
|
|
31
83
|
├── package.json
|
|
32
84
|
└── tsconfig.json
|
|
33
85
|
```
|
|
34
86
|
|
|
35
|
-
##
|
|
87
|
+
## CLI Commands
|
|
88
|
+
|
|
89
|
+
Use the `agentforge` CLI to manage your project from the terminal.
|
|
36
90
|
|
|
37
91
|
```bash
|
|
38
|
-
#
|
|
39
|
-
agentforge agents list
|
|
40
|
-
agentforge agents create # Create a new agent (interactive)
|
|
41
|
-
agentforge agents inspect <id> # Show agent details
|
|
42
|
-
agentforge agents edit <id> # Edit an agent
|
|
43
|
-
agentforge agents delete <id> # Delete an agent
|
|
44
|
-
|
|
45
|
-
# Chat
|
|
46
|
-
agentforge chat <agent-id> # Start chatting with an agent
|
|
47
|
-
agentforge chat --session <id> # Resume a session
|
|
48
|
-
|
|
49
|
-
# Sessions & Threads
|
|
50
|
-
agentforge sessions list # List all sessions
|
|
51
|
-
agentforge threads list # List all threads
|
|
52
|
-
|
|
53
|
-
# Skills
|
|
54
|
-
agentforge skills list # List installed skills
|
|
55
|
-
agentforge skills install <name> # Install a skill
|
|
56
|
-
agentforge skills search <query> # Search available skills
|
|
57
|
-
|
|
58
|
-
# Cron Jobs
|
|
59
|
-
agentforge cron list # List cron jobs
|
|
60
|
-
agentforge cron create # Create a cron job (interactive)
|
|
61
|
-
|
|
62
|
-
# MCP Connections
|
|
63
|
-
agentforge mcp list # List MCP connections
|
|
64
|
-
agentforge mcp add # Add a connection (interactive)
|
|
65
|
-
agentforge mcp test <id> # Test connection health
|
|
66
|
-
|
|
67
|
-
# Files & Projects
|
|
68
|
-
agentforge files list # List files
|
|
69
|
-
agentforge files upload <path> # Upload a file
|
|
70
|
-
agentforge projects list # List projects
|
|
71
|
-
agentforge projects create <name> # Create a project
|
|
72
|
-
|
|
73
|
-
# Configuration & Vault
|
|
74
|
-
agentforge config list # List all config
|
|
75
|
-
agentforge vault list # List secrets (masked)
|
|
76
|
-
agentforge vault add <name> <value> # Store a secret
|
|
77
|
-
|
|
78
|
-
# Utilities
|
|
79
|
-
agentforge status # Show system health
|
|
80
|
-
agentforge logs # Tail recent logs
|
|
81
|
-
agentforge dashboard # Open the web dashboard
|
|
82
|
-
agentforge deploy # Deploy to production
|
|
83
|
-
```
|
|
92
|
+
# List all agents
|
|
93
|
+
agentforge agents list
|
|
84
94
|
|
|
85
|
-
|
|
95
|
+
# Chat with your agent
|
|
96
|
+
agentforge chat my-first-agent
|
|
86
97
|
|
|
87
|
-
|
|
98
|
+
# See all available commands
|
|
99
|
+
agentforge --help
|
|
100
|
+
```
|
|
88
101
|
|
|
89
|
-
| Provider | Model Format | API Key Variable |
|
|
90
|
-
|----------|-------------|-----------------|
|
|
91
|
-
| OpenAI | `openai:gpt-4o-mini` | `OPENAI_API_KEY` |
|
|
92
|
-
| OpenRouter | `openrouter:anthropic/claude-3.5-sonnet` | `OPENROUTER_API_KEY` |
|
|
93
|
-
| Anthropic | `anthropic:claude-3-5-sonnet-20241022` | `ANTHROPIC_API_KEY` |
|
|
94
|
-
| Google | `google:gemini-2.0-flash` | `GOOGLE_API_KEY` |
|
|
95
|
-
| xAI | `xai:grok-2` | `XAI_API_KEY` |
|
|
96
102
|
|
|
97
|
-
## Web Dashboard
|
|
98
103
|
|
|
99
|
-
Launch the dashboard for a visual interface:
|
|
100
104
|
|
|
101
|
-
```bash
|
|
102
|
-
agentforge dashboard
|
|
103
|
-
```
|
|
104
105
|
|
|
105
106
|
## Learn More
|
|
106
107
|
|
|
107
|
-
- [AgentForge Documentation](https://github.com/Agentic-Engineering-Agency/agentforge)
|
|
108
|
-
- [
|
|
109
|
-
- [
|
|
110
|
-
- [Mastra Docs](https://mastra.ai/docs)
|
|
108
|
+
- **[AgentForge Documentation](https://github.com/Agentic-Engineering-Agency/agentforge)**: Main project repository and documentation.
|
|
109
|
+
- **[Convex Docs](https://docs.convex.dev)**: Learn more about the real-time backend.
|
|
110
|
+
- **[Mastra Docs](https://mastra.ai/docs)**: Understand the core AI engine.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query, action } from "./_generated/server";
|
|
3
|
+
import { api } from "./_generated/api";
|
|
4
|
+
|
|
5
|
+
// Query: Get all agents
|
|
6
|
+
export const list = query({
|
|
7
|
+
args: {
|
|
8
|
+
userId: v.optional(v.string()),
|
|
9
|
+
},
|
|
10
|
+
handler: async (ctx, args) => {
|
|
11
|
+
if (args.userId) {
|
|
12
|
+
return await ctx.db
|
|
13
|
+
.query("agents")
|
|
14
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
15
|
+
.take(100).collect();
|
|
16
|
+
}
|
|
17
|
+
return await ctx.db.query("agents").take(100).collect();
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Query: Get agent by ID
|
|
22
|
+
export const get = query({
|
|
23
|
+
args: { id: v.string() },
|
|
24
|
+
handler: async (ctx, args) => {
|
|
25
|
+
return await ctx.db
|
|
26
|
+
.query("agents")
|
|
27
|
+
.withIndex("byAgentId", (q) => q.eq("id", args.id))
|
|
28
|
+
.first();
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Query: Get active agents
|
|
33
|
+
export const listActive = query({
|
|
34
|
+
args: {
|
|
35
|
+
userId: v.optional(v.string()),
|
|
36
|
+
},
|
|
37
|
+
handler: async (ctx, args) => {
|
|
38
|
+
let query = ctx.db
|
|
39
|
+
.query("agents")
|
|
40
|
+
.withIndex("byIsActive", (q) => q.eq("isActive", true));
|
|
41
|
+
|
|
42
|
+
const agents = await query.take(100).collect();
|
|
43
|
+
|
|
44
|
+
if (args.userId) {
|
|
45
|
+
return agents.filter((agent) => agent.userId === args.userId);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return agents;
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// Mutation: Create a new agent
|
|
53
|
+
export const create = mutation({
|
|
54
|
+
args: {
|
|
55
|
+
id: v.string(),
|
|
56
|
+
name: v.string(),
|
|
57
|
+
description: v.optional(v.string()),
|
|
58
|
+
instructions: v.string(),
|
|
59
|
+
model: v.string(),
|
|
60
|
+
provider: v.string(),
|
|
61
|
+
tools: v.optional(v.any()),
|
|
62
|
+
temperature: v.optional(v.number()),
|
|
63
|
+
maxTokens: v.optional(v.number()),
|
|
64
|
+
topP: v.optional(v.number()),
|
|
65
|
+
userId: v.optional(v.string()),
|
|
66
|
+
},
|
|
67
|
+
handler: async (ctx, args) => {
|
|
68
|
+
const now = Date.now();
|
|
69
|
+
const agentId = await ctx.db.insert("agents", {
|
|
70
|
+
...args,
|
|
71
|
+
isActive: true,
|
|
72
|
+
createdAt: now,
|
|
73
|
+
updatedAt: now,
|
|
74
|
+
});
|
|
75
|
+
return agentId;
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Mutation: Update an agent
|
|
80
|
+
export const update = mutation({
|
|
81
|
+
args: {
|
|
82
|
+
id: v.string(),
|
|
83
|
+
name: v.optional(v.string()),
|
|
84
|
+
description: v.optional(v.string()),
|
|
85
|
+
instructions: v.optional(v.string()),
|
|
86
|
+
model: v.optional(v.string()),
|
|
87
|
+
provider: v.optional(v.string()),
|
|
88
|
+
tools: v.optional(v.any()),
|
|
89
|
+
temperature: v.optional(v.number()),
|
|
90
|
+
maxTokens: v.optional(v.number()),
|
|
91
|
+
topP: v.optional(v.number()),
|
|
92
|
+
isActive: v.optional(v.boolean()),
|
|
93
|
+
},
|
|
94
|
+
handler: async (ctx, args) => {
|
|
95
|
+
const { id, ...updates } = args;
|
|
96
|
+
const agent = await ctx.db
|
|
97
|
+
.query("agents")
|
|
98
|
+
.withIndex("byAgentId", (q) => q.eq("id", id))
|
|
99
|
+
.first();
|
|
100
|
+
|
|
101
|
+
if (!agent) {
|
|
102
|
+
throw new Error(`Agent with id ${id} not found`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
await ctx.db.patch(agent._id, {
|
|
106
|
+
...updates,
|
|
107
|
+
updatedAt: Date.now(),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
return agent._id;
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Mutation: Delete an agent
|
|
115
|
+
export const remove = mutation({
|
|
116
|
+
args: { id: v.string() },
|
|
117
|
+
handler: async (ctx, args) => {
|
|
118
|
+
const agent = await ctx.db
|
|
119
|
+
.query("agents")
|
|
120
|
+
.withIndex("byAgentId", (q) => q.eq("id", args.id))
|
|
121
|
+
.first();
|
|
122
|
+
|
|
123
|
+
if (!agent) {
|
|
124
|
+
throw new Error(`Agent with id ${args.id} not found`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
await ctx.db.delete(agent._id);
|
|
128
|
+
return { success: true };
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Mutation: Toggle agent active status
|
|
133
|
+
export const toggleActive = mutation({
|
|
134
|
+
args: { id: v.string() },
|
|
135
|
+
handler: async (ctx, args) => {
|
|
136
|
+
const agent = await ctx.db
|
|
137
|
+
.query("agents")
|
|
138
|
+
.withIndex("byAgentId", (q) => q.eq("id", args.id))
|
|
139
|
+
.first();
|
|
140
|
+
|
|
141
|
+
if (!agent) {
|
|
142
|
+
throw new Error(`Agent with id ${args.id} not found`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
await ctx.db.patch(agent._id, {
|
|
146
|
+
isActive: !agent.isActive,
|
|
147
|
+
updatedAt: Date.now(),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
return { success: true, isActive: !agent.isActive };
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Action: Run agent with Mastra (to be implemented with Mastra integration)
|
|
155
|
+
export const run = action({
|
|
156
|
+
args: {
|
|
157
|
+
agentId: v.string(),
|
|
158
|
+
prompt: v.string(),
|
|
159
|
+
threadId: v.optional(v.id("threads")),
|
|
160
|
+
userId: v.optional(v.string()),
|
|
161
|
+
},
|
|
162
|
+
handler: async (ctx, args) => {
|
|
163
|
+
// Get agent configuration
|
|
164
|
+
const agent = await ctx.runQuery(api.agents.get, { id: args.agentId });
|
|
165
|
+
|
|
166
|
+
if (!agent) {
|
|
167
|
+
throw new Error(`Agent with id ${args.agentId} not found`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Create or get thread
|
|
171
|
+
let threadId = args.threadId;
|
|
172
|
+
if (!threadId) {
|
|
173
|
+
threadId = await ctx.runMutation(api.threads.create, {
|
|
174
|
+
agentId: args.agentId,
|
|
175
|
+
userId: args.userId,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Add user message
|
|
180
|
+
await ctx.runMutation(api.messages.add, {
|
|
181
|
+
threadId,
|
|
182
|
+
role: "user",
|
|
183
|
+
content: args.prompt,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// TODO: Integrate with Mastra to run the agent
|
|
187
|
+
// This will be implemented in the Mastra integration phase
|
|
188
|
+
// For now, return a placeholder response
|
|
189
|
+
const response = {
|
|
190
|
+
threadId,
|
|
191
|
+
message: "Agent execution will be implemented with Mastra integration",
|
|
192
|
+
agentId: args.agentId,
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Add assistant message placeholder
|
|
196
|
+
await ctx.runMutation(api.messages.add, {
|
|
197
|
+
threadId,
|
|
198
|
+
role: "assistant",
|
|
199
|
+
content: response.message,
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return response;
|
|
203
|
+
},
|
|
204
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { v } from "convex/values";
|
|
2
|
+
import { mutation, query } from "./_generated/server";
|
|
3
|
+
|
|
4
|
+
// Query: List API keys
|
|
5
|
+
export const list = query({
|
|
6
|
+
args: {
|
|
7
|
+
userId: v.optional(v.string()),
|
|
8
|
+
provider: v.optional(v.string()),
|
|
9
|
+
},
|
|
10
|
+
handler: async (ctx, args) => {
|
|
11
|
+
if (args.provider) {
|
|
12
|
+
const keys = await ctx.db
|
|
13
|
+
.query("apiKeys")
|
|
14
|
+
.withIndex("byProvider", (q) => q.eq("provider", args.provider))
|
|
15
|
+
.take(100).collect();
|
|
16
|
+
|
|
17
|
+
if (args.userId) {
|
|
18
|
+
return keys.filter((k) => k.userId === args.userId);
|
|
19
|
+
}
|
|
20
|
+
return keys;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (args.userId) {
|
|
24
|
+
return await ctx.db
|
|
25
|
+
.query("apiKeys")
|
|
26
|
+
.withIndex("byUserId", (q) => q.eq("userId", args.userId))
|
|
27
|
+
.take(100).collect();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return await ctx.db.query("apiKeys").take(100).collect();
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// Query: Get API key by ID
|
|
35
|
+
export const get = query({
|
|
36
|
+
args: { id: v.id("apiKeys") },
|
|
37
|
+
handler: async (ctx, args) => {
|
|
38
|
+
return await ctx.db.get(args.id);
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// Query: Get active API key for provider
|
|
43
|
+
export const getActiveForProvider = query({
|
|
44
|
+
args: {
|
|
45
|
+
provider: v.string(),
|
|
46
|
+
userId: v.optional(v.string()),
|
|
47
|
+
},
|
|
48
|
+
handler: async (ctx, args) => {
|
|
49
|
+
const keys = await ctx.db
|
|
50
|
+
.query("apiKeys")
|
|
51
|
+
.withIndex("byProvider", (q) => q.eq("provider", args.provider))
|
|
52
|
+
.take(100).collect();
|
|
53
|
+
|
|
54
|
+
const activeKeys = keys.filter((k) => k.isActive);
|
|
55
|
+
|
|
56
|
+
if (args.userId) {
|
|
57
|
+
return activeKeys.find((k) => k.userId === args.userId);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return activeKeys[0];
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Mutation: Create API key
|
|
65
|
+
export const create = mutation({
|
|
66
|
+
args: {
|
|
67
|
+
provider: v.string(),
|
|
68
|
+
keyName: v.string(),
|
|
69
|
+
encryptedKey: v.string(),
|
|
70
|
+
userId: v.optional(v.string()),
|
|
71
|
+
},
|
|
72
|
+
handler: async (ctx, args) => {
|
|
73
|
+
const keyId = await ctx.db.insert("apiKeys", {
|
|
74
|
+
...args,
|
|
75
|
+
isActive: true,
|
|
76
|
+
createdAt: Date.now(),
|
|
77
|
+
});
|
|
78
|
+
return keyId;
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
// Mutation: Update API key
|
|
83
|
+
export const update = mutation({
|
|
84
|
+
args: {
|
|
85
|
+
id: v.id("apiKeys"),
|
|
86
|
+
keyName: v.optional(v.string()),
|
|
87
|
+
encryptedKey: v.optional(v.string()),
|
|
88
|
+
isActive: v.optional(v.boolean()),
|
|
89
|
+
},
|
|
90
|
+
handler: async (ctx, args) => {
|
|
91
|
+
const { id, ...updates } = args;
|
|
92
|
+
await ctx.db.patch(id, updates);
|
|
93
|
+
return id;
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Mutation: Toggle API key active status
|
|
98
|
+
export const toggleActive = mutation({
|
|
99
|
+
args: { id: v.id("apiKeys") },
|
|
100
|
+
handler: async (ctx, args) => {
|
|
101
|
+
const key = await ctx.db.get(args.id);
|
|
102
|
+
|
|
103
|
+
if (!key) {
|
|
104
|
+
throw new Error(`API key not found`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
await ctx.db.patch(args.id, {
|
|
108
|
+
isActive: !key.isActive,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return { success: true, isActive: !key.isActive };
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Mutation: Update last used timestamp
|
|
116
|
+
export const updateLastUsed = mutation({
|
|
117
|
+
args: { id: v.id("apiKeys") },
|
|
118
|
+
handler: async (ctx, args) => {
|
|
119
|
+
await ctx.db.patch(args.id, {
|
|
120
|
+
lastUsedAt: Date.now(),
|
|
121
|
+
});
|
|
122
|
+
return args.id;
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Mutation: Delete API key
|
|
127
|
+
export const remove = mutation({
|
|
128
|
+
args: { id: v.id("apiKeys") },
|
|
129
|
+
handler: async (ctx, args) => {
|
|
130
|
+
await ctx.db.delete(args.id);
|
|
131
|
+
return { success: true };
|
|
132
|
+
},
|
|
133
|
+
});
|