4runr-os 2.10.2 → 2.10.4

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.
@@ -0,0 +1,469 @@
1
+ // This is your Prisma schema file,
2
+ // learn more about it in the docs: https://pris.ly/d/prisma-schema
3
+
4
+ generator client {
5
+ provider = "prisma-client-js"
6
+ binaryTargets = ["native", "linux-musl-openssl-3.0.x", "linux-musl"]
7
+ }
8
+
9
+ datasource db {
10
+ provider = "postgresql"
11
+ url = env("DATABASE_URL")
12
+ }
13
+
14
+ model Agent {
15
+ id String @id @default(uuid())
16
+ name String
17
+ role String
18
+ createdBy String
19
+ publicKey String
20
+ status String @default("active")
21
+ createdAt DateTime @default(now())
22
+ tokens Token[]
23
+ requestLogs RequestLog[]
24
+ policies Policy[]
25
+ policyLogs PolicyLog[]
26
+ tokenRegistries TokenRegistry[]
27
+ }
28
+
29
+ model Token {
30
+ id String @id @default(uuid())
31
+ agentId String
32
+ agent Agent @relation(fields: [agentId], references: [id])
33
+ encrypted String
34
+ expiresAt DateTime
35
+ revoked Boolean @default(false)
36
+ createdAt DateTime @default(now())
37
+ }
38
+
39
+ model RequestLog {
40
+ id String @id @default(uuid())
41
+ agentId String
42
+ agent Agent @relation(fields: [agentId], references: [id])
43
+ tool String
44
+ action String
45
+ responseTime Int // milliseconds
46
+ statusCode Int
47
+ success Boolean
48
+ createdAt DateTime @default(now())
49
+ }
50
+
51
+ model Policy {
52
+ id String @id @default(uuid())
53
+ name String
54
+ description String?
55
+ agentId String? // null for role-based policies
56
+ role String? // null for agent-specific policies
57
+ spec String // JSON policy specification
58
+ specHash String // SHA256 hash of spec for change detection
59
+ active Boolean @default(true)
60
+ createdAt DateTime @default(now())
61
+ updatedAt DateTime @updatedAt
62
+ agent Agent? @relation(fields: [agentId], references: [id])
63
+ policyLogs PolicyLog[]
64
+ quotaCounters QuotaCounter[]
65
+ }
66
+
67
+ model PolicyLog {
68
+ id String @id @default(uuid())
69
+ policyId String
70
+ agentId String
71
+ tool String
72
+ action String
73
+ decision String // "allow", "deny"
74
+ reason String? // denial reason
75
+ requestData String? // JSON request data (truncated)
76
+ responseData String? // JSON response data (truncated)
77
+ createdAt DateTime @default(now())
78
+ policy Policy @relation(fields: [policyId], references: [id])
79
+ agent Agent @relation(fields: [agentId], references: [id])
80
+ }
81
+
82
+ model QuotaCounter {
83
+ id String @id @default(uuid())
84
+ policyId String
85
+ quotaKey String // e.g., "serpapi:search:2024-01-15"
86
+ current Int @default(0)
87
+ resetAt DateTime
88
+ createdAt DateTime @default(now())
89
+ updatedAt DateTime @updatedAt
90
+ policy Policy @relation(fields: [policyId], references: [id])
91
+ }
92
+
93
+ model ToolCredential {
94
+ id String @id @default(uuid())
95
+ tool String // e.g., "serpapi", "openai", "gmail_send"
96
+ version String // e.g., "v1", "v2"
97
+ isActive Boolean @default(false)
98
+ encryptedCredential String // Encrypted credential data
99
+ metadata String? // JSON metadata (encrypted)
100
+ createdAt DateTime @default(now())
101
+ updatedAt DateTime @updatedAt
102
+ activatedAt DateTime?
103
+ deactivatedAt DateTime?
104
+
105
+ @@unique([tool, version])
106
+ }
107
+
108
+ model TokenRegistry {
109
+ id String @id @default(uuid())
110
+ tokenId String @unique // The token ID issued to the agent
111
+ agentId String
112
+ payloadHash String // SHA256 hash of the token payload
113
+ issuedAt DateTime @default(now())
114
+ expiresAt DateTime
115
+ isRevoked Boolean @default(false)
116
+ revokedAt DateTime?
117
+ agent Agent @relation(fields: [agentId], references: [id])
118
+ }
119
+
120
+ // ====== AI AGENT RUNTIME MODELS (TASK-001) ======
121
+
122
+ model RuntimeAgent {
123
+ id String @id @default(uuid())
124
+ name String
125
+ language AgentLanguage
126
+ sourceType SourceType @default(ZIP) // ZIP | GIT (ZIP for MVP)
127
+ sourceUri String? // path to extracted zip in local FS (MVP)
128
+ entrypoint String // index.js | main.py
129
+ env Json // non-secret env vars (secrets via Gateway only)
130
+ limitsCpu Float? // e.g. 0.5
131
+ limitsMemMb Int? // e.g. 256
132
+ networkMode NetworkMode @default(NONE) // NONE | EGRESS (via Gateway proxy only)
133
+ status AgentStatus @default(READY)
134
+ // NEW: restart policy & limits
135
+ maxRestarts Int @default(2) // per run
136
+ restartBackoffMs Int @default(5000)
137
+ // NEW: tags for demo functionality (JSON array for SQLite compatibility)
138
+ tags Json @default("[]")
139
+ createdAt DateTime @default(now())
140
+ updatedAt DateTime @updatedAt
141
+
142
+ runs RuntimeRun[]
143
+ schedules RuntimeSchedule[]
144
+
145
+ @@map("runtime_agents")
146
+ }
147
+
148
+ model RuntimeRun {
149
+ id String @id @default(uuid())
150
+ agentId String
151
+ agent RuntimeAgent @relation(fields: [agentId], references: [id], onDelete: Cascade)
152
+ status RunStatus @default(QUEUED) // QUEUED | RUNNING | SUCCEEDED | FAILED | KILLED
153
+ startedAt DateTime?
154
+ endedAt DateTime?
155
+ exitCode Int?
156
+ reason String? // containerId or failure reason
157
+ // NEW: restart policy & limits
158
+ cpuSeconds Float? // cumulative seconds
159
+ maxMemMb Int? // peak RSS MB
160
+ restarts Int @default(0)
161
+ lastSampleAt DateTime? // last stats collection time
162
+ // NEW: trigger tracking
163
+ triggeredBy TriggerType @default(MANUAL)
164
+ // (optional) logsPtr for future
165
+ createdAt DateTime @default(now())
166
+
167
+ @@map("runtime_runs")
168
+ }
169
+
170
+ model RuntimeSchedule {
171
+ id String @id @default(uuid())
172
+ agentId String
173
+ agent RuntimeAgent @relation(fields: [agentId], references: [id], onDelete: Cascade)
174
+ cronExpr String
175
+ enabled Boolean @default(true)
176
+ lastRunAt DateTime?
177
+ nextRunAt DateTime?
178
+ createdAt DateTime @default(now())
179
+
180
+ @@map("runtime_schedules")
181
+ }
182
+
183
+ enum AgentLanguage {
184
+ NODE
185
+ PYTHON
186
+ }
187
+
188
+ enum SourceType {
189
+ ZIP
190
+ GIT
191
+ }
192
+
193
+ enum NetworkMode {
194
+ NONE
195
+ EGRESS
196
+ }
197
+
198
+ enum AgentStatus {
199
+ READY
200
+ ERROR
201
+ DISABLED
202
+ }
203
+
204
+ enum RunStatus {
205
+ CREATED
206
+ QUEUED
207
+ RUNNING
208
+ COMPLETED
209
+ FAILED
210
+ KILLED
211
+ SUCCEEDED // Alias for COMPLETED (keeping for RuntimeRun compatibility)
212
+ }
213
+
214
+ enum TriggerType {
215
+ MANUAL
216
+ SCHEDULE
217
+ }
218
+
219
+ // ====== GATEWAY RUNS MODELS ======
220
+
221
+ model Run {
222
+ id String @id @default(uuid())
223
+ name String
224
+ status RunStatus @default(CREATED)
225
+ input Json? // JSON input payload
226
+ output Json? // JSON output payload
227
+ logs Json @default("[]") // Array of log entries
228
+ clientToken String? @unique // Idempotency token from client
229
+ tags String[] @default([])
230
+ startedAt DateTime?
231
+ completedAt DateTime?
232
+ createdAt DateTime @default(now())
233
+ updatedAt DateTime @updatedAt
234
+
235
+ @@map("runs")
236
+ @@index([status])
237
+ @@index([clientToken])
238
+ @@index([createdAt])
239
+ }
240
+
241
+ model ApiKey {
242
+ id String @id @default(cuid())
243
+ key String @unique // Hashed or opaque token
244
+ label String?
245
+ revoked Boolean @default(false)
246
+ userId String? // Link to user if applicable
247
+ user User? @relation(fields: [userId], references: [id], onDelete: Cascade)
248
+ createdAt DateTime @default(now())
249
+ revokedAt DateTime?
250
+
251
+ @@map("api_keys")
252
+ @@index([key])
253
+ @@index([userId])
254
+ }
255
+
256
+ // ====== RBAC MODELS ======
257
+
258
+ model User {
259
+ id String @id @default(uuid())
260
+ email String @unique
261
+ username String @unique
262
+ passwordHash String? // Optional - can use external auth
263
+ status UserStatus @default(ACTIVE)
264
+ createdAt DateTime @default(now())
265
+ updatedAt DateTime @updatedAt
266
+ lastLoginAt DateTime?
267
+
268
+ // MFA fields
269
+ mfaEnabled Boolean @default(false)
270
+ mfaSecret String? // TOTP secret (encrypted)
271
+ mfaBackupCodes String[] @default([]) // Encrypted backup codes
272
+ mfaVerifiedAt DateTime? // When MFA was last verified
273
+
274
+ // Relations
275
+ roles UserRole[]
276
+ apiKeys ApiKey[]
277
+ sessions UserSession[]
278
+
279
+ @@map("users")
280
+ @@index([email])
281
+ @@index([username])
282
+ @@index([status])
283
+ }
284
+
285
+ model Role {
286
+ id String @id @default(uuid())
287
+ name String @unique
288
+ description String?
289
+ isSystem Boolean @default(false) // System roles cannot be deleted
290
+ createdAt DateTime @default(now())
291
+ updatedAt DateTime @updatedAt
292
+
293
+ // Relations
294
+ users UserRole[]
295
+ permissions RolePermission[]
296
+
297
+ @@map("roles")
298
+ @@index([name])
299
+ }
300
+
301
+ model Permission {
302
+ id String @id @default(uuid())
303
+ resource String // e.g., "runs", "agents", "users"
304
+ action String // e.g., "create", "read", "update", "delete", "execute"
305
+ description String?
306
+ createdAt DateTime @default(now())
307
+
308
+ // Relations
309
+ roles RolePermission[]
310
+
311
+ @@unique([resource, action])
312
+ @@map("permissions")
313
+ @@index([resource])
314
+ @@index([action])
315
+ }
316
+
317
+ model UserRole {
318
+ id String @id @default(uuid())
319
+ userId String
320
+ roleId String
321
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
322
+ role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
323
+ assignedAt DateTime @default(now())
324
+ assignedBy String? // User ID who assigned this role
325
+
326
+ @@unique([userId, roleId])
327
+ @@map("user_roles")
328
+ @@index([userId])
329
+ @@index([roleId])
330
+ }
331
+
332
+ model RolePermission {
333
+ id String @id @default(uuid())
334
+ roleId String
335
+ permissionId String
336
+ role Role @relation(fields: [roleId], references: [id], onDelete: Cascade)
337
+ permission Permission @relation(fields: [permissionId], references: [id], onDelete: Cascade)
338
+ grantedAt DateTime @default(now())
339
+ grantedBy String? // User ID who granted this permission
340
+
341
+ @@unique([roleId, permissionId])
342
+ @@map("role_permissions")
343
+ @@index([roleId])
344
+ @@index([permissionId])
345
+ }
346
+
347
+ model UserSession {
348
+ id String @id @default(uuid())
349
+ userId String
350
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
351
+ token String @unique // JWT or session token
352
+ ipAddress String?
353
+ userAgent String?
354
+ expiresAt DateTime
355
+ createdAt DateTime @default(now())
356
+ lastActivityAt DateTime @default(now())
357
+
358
+ @@map("user_sessions")
359
+ @@index([userId])
360
+ @@index([token])
361
+ @@index([expiresAt])
362
+ }
363
+
364
+ enum UserStatus {
365
+ ACTIVE
366
+ INACTIVE
367
+ SUSPENDED
368
+ DELETED
369
+ }
370
+
371
+ // ====== IDEMPOTENCY & CACHE MODELS ======
372
+
373
+ model IdempotencyToken {
374
+ id String @id @default(uuid())
375
+ token String @unique
376
+ runId String? // Reference to Run if applicable
377
+ response Json? // Cached response
378
+ expiresAt DateTime
379
+ createdAt DateTime @default(now())
380
+
381
+ @@map("idempotency_tokens")
382
+ @@index([token])
383
+ @@index([expiresAt])
384
+ }
385
+
386
+ model CacheEntry {
387
+ id String @id @default(uuid())
388
+ key String @unique
389
+ value Json
390
+ expiresAt DateTime?
391
+ createdAt DateTime @default(now())
392
+ updatedAt DateTime @updatedAt
393
+
394
+ @@map("cache_entries")
395
+ @@index([key])
396
+ @@index([expiresAt])
397
+ }
398
+
399
+ // ====== CHAT HISTORY MODELS ======
400
+
401
+ model ChatHistory {
402
+ id String @id @default(uuid())
403
+ clientId String // Client identifier (e.g., machine ID, user ID)
404
+ agentName String
405
+ agentDescription String?
406
+ messages Json // Array of { role: 'user' | 'assistant', content: string }
407
+ createdAt DateTime @default(now())
408
+ lastMessageAt DateTime @default(now())
409
+ updatedAt DateTime @updatedAt
410
+
411
+ @@map("chat_history")
412
+ @@index([clientId])
413
+ @@index([clientId, lastMessageAt])
414
+ }
415
+
416
+ // ====== DEVKIT AGENT & TOOL MANAGEMENT ======
417
+
418
+ model DevKitAgent {
419
+ id String @id @default(uuid())
420
+ name String
421
+ slug String @unique
422
+ description String?
423
+ type String // lead_agent, education_agent, custom, etc.
424
+ model String // gpt-4, claude-3, etc.
425
+ config Json @default("{}") // { prompt, temperature, maxTokens, etc. }
426
+ createdAt DateTime @default(now())
427
+ updatedAt DateTime @updatedAt
428
+
429
+ // Relations
430
+ tools DevKitAgentTool[]
431
+
432
+ @@map("devkit_agents")
433
+ @@index([slug])
434
+ @@index([type])
435
+ }
436
+
437
+ model DevKitTool {
438
+ id String @id @default(uuid())
439
+ name String
440
+ kind String // http, email, vector, database, etc.
441
+ config Json @default("{}") // Flexible config per kind
442
+ enabled Boolean @default(true)
443
+ createdAt DateTime @default(now())
444
+ updatedAt DateTime @updatedAt
445
+
446
+ // Relations
447
+ agents DevKitAgentTool[]
448
+
449
+ @@map("devkit_tools")
450
+ @@index([kind])
451
+ @@index([enabled])
452
+ }
453
+
454
+ model DevKitAgentTool {
455
+ id String @id @default(uuid())
456
+ agentId String
457
+ toolId String
458
+ configOverride Json? // Optional per-agent tool configuration
459
+ createdAt DateTime @default(now())
460
+
461
+ // Relations
462
+ agent DevKitAgent @relation(fields: [agentId], references: [id], onDelete: Cascade)
463
+ tool DevKitTool @relation(fields: [toolId], references: [id], onDelete: Cascade)
464
+
465
+ @@unique([agentId, toolId])
466
+ @@map("devkit_agent_tools")
467
+ @@index([agentId])
468
+ @@index([toolId])
469
+ }
@@ -20,8 +20,31 @@ if (!fs.existsSync(packageJson)) {
20
20
  process.exit(0); // No gateway in this install
21
21
  }
22
22
 
23
+ const prismaSchema = path.join(__dirname, '..', 'prisma', 'schema.prisma');
24
+
25
+ function ensurePrismaClient() {
26
+ if (!fs.existsSync(prismaSchema)) {
27
+ return;
28
+ }
29
+ try {
30
+ console.log('🔧 Prisma: generating client for Gateway (global 4runr-os install)...');
31
+ execSync('npx prisma generate --schema=../../prisma/schema.prisma', {
32
+ cwd: gatewayDir,
33
+ stdio: 'inherit',
34
+ shell: process.platform === 'win32',
35
+ });
36
+ console.log('✓ Prisma client ready for Gateway');
37
+ } catch (err) {
38
+ console.warn(
39
+ '⚠️ prisma generate failed. OS auto-start Gateway may fail until you run:\n' +
40
+ ' cd apps/gateway && npx prisma generate --schema=../../prisma/schema.prisma',
41
+ );
42
+ }
43
+ }
44
+
23
45
  if (fs.existsSync(nodeModules) && fs.readdirSync(nodeModules).length > 0) {
24
- process.exit(0); // Already has deps (e.g. from prepublish)
46
+ ensurePrismaClient();
47
+ process.exit(0); // Deps already present (e.g. from prepublish) — still (re)generate Prisma
25
48
  }
26
49
 
27
50
  try {
@@ -36,3 +59,5 @@ try {
36
59
  console.warn('⚠️ Gateway dependency install failed. Start gateway from repo with: cd apps/gateway && npm start');
37
60
  // Don't fail the main install
38
61
  }
62
+
63
+ ensurePrismaClient();