palaryn 0.1.0 → 0.3.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/README.md +243 -588
- package/dist/sdk/typescript/src/client.js +2 -2
- package/dist/sdk/typescript/src/client.js.map +1 -1
- package/dist/src/audit/logger.d.ts +10 -0
- package/dist/src/audit/logger.d.ts.map +1 -1
- package/dist/src/audit/logger.js +52 -38
- package/dist/src/audit/logger.js.map +1 -1
- package/dist/src/auth/routes.js.map +1 -1
- package/dist/src/budget/manager.d.ts +5 -0
- package/dist/src/budget/manager.d.ts.map +1 -1
- package/dist/src/budget/manager.js +32 -0
- package/dist/src/budget/manager.js.map +1 -1
- package/dist/src/budget/model-pricing.d.ts +20 -0
- package/dist/src/budget/model-pricing.d.ts.map +1 -0
- package/dist/src/budget/model-pricing.js +107 -0
- package/dist/src/budget/model-pricing.js.map +1 -0
- package/dist/src/budget/usage-extractor.d.ts +3 -1
- package/dist/src/budget/usage-extractor.d.ts.map +1 -1
- package/dist/src/budget/usage-extractor.js +47 -3
- package/dist/src/budget/usage-extractor.js.map +1 -1
- package/dist/src/config/defaults.d.ts.map +1 -1
- package/dist/src/config/defaults.js +65 -13
- package/dist/src/config/defaults.js.map +1 -1
- package/dist/src/dlp/tool-patterns.d.ts +7 -0
- package/dist/src/dlp/tool-patterns.d.ts.map +1 -0
- package/dist/src/dlp/tool-patterns.js +34 -0
- package/dist/src/dlp/tool-patterns.js.map +1 -0
- package/dist/src/executor/filesystem-executor.d.ts +28 -0
- package/dist/src/executor/filesystem-executor.d.ts.map +1 -0
- package/dist/src/executor/filesystem-executor.js +192 -0
- package/dist/src/executor/filesystem-executor.js.map +1 -0
- package/dist/src/executor/http-executor.d.ts.map +1 -1
- package/dist/src/executor/http-executor.js +4 -0
- package/dist/src/executor/http-executor.js.map +1 -1
- package/dist/src/executor/index.d.ts +4 -0
- package/dist/src/executor/index.d.ts.map +1 -1
- package/dist/src/executor/index.js +9 -1
- package/dist/src/executor/index.js.map +1 -1
- package/dist/src/executor/shell-executor.d.ts +22 -0
- package/dist/src/executor/shell-executor.d.ts.map +1 -0
- package/dist/src/executor/shell-executor.js +119 -0
- package/dist/src/executor/shell-executor.js.map +1 -0
- package/dist/src/executor/sql-executor.d.ts +29 -0
- package/dist/src/executor/sql-executor.d.ts.map +1 -0
- package/dist/src/executor/sql-executor.js +114 -0
- package/dist/src/executor/sql-executor.js.map +1 -0
- package/dist/src/executor/websocket-executor.d.ts +26 -0
- package/dist/src/executor/websocket-executor.d.ts.map +1 -0
- package/dist/src/executor/websocket-executor.js +205 -0
- package/dist/src/executor/websocket-executor.js.map +1 -0
- package/dist/src/interceptor/index.d.ts +2 -0
- package/dist/src/interceptor/index.d.ts.map +1 -0
- package/dist/src/interceptor/index.js +6 -0
- package/dist/src/interceptor/index.js.map +1 -0
- package/dist/src/interceptor/provider-interceptor.d.ts +36 -0
- package/dist/src/interceptor/provider-interceptor.d.ts.map +1 -0
- package/dist/src/interceptor/provider-interceptor.js +302 -0
- package/dist/src/interceptor/provider-interceptor.js.map +1 -0
- package/dist/src/mcp/auth-verifier.d.ts.map +1 -1
- package/dist/src/mcp/auth-verifier.js +3 -2
- package/dist/src/mcp/auth-verifier.js.map +1 -1
- package/dist/src/mcp/bridge.d.ts +14 -10
- package/dist/src/mcp/bridge.d.ts.map +1 -1
- package/dist/src/mcp/bridge.js +51 -227
- package/dist/src/mcp/bridge.js.map +1 -1
- package/dist/src/mcp/http-transport.d.ts.map +1 -1
- package/dist/src/mcp/http-transport.js +101 -65
- package/dist/src/mcp/http-transport.js.map +1 -1
- package/dist/src/mcp/tool-definitions.d.ts +41 -0
- package/dist/src/mcp/tool-definitions.d.ts.map +1 -0
- package/dist/src/mcp/tool-definitions.js +491 -0
- package/dist/src/mcp/tool-definitions.js.map +1 -0
- package/dist/src/middleware/auth.js.map +1 -1
- package/dist/src/middleware/session.js.map +1 -1
- package/dist/src/middleware/validate.d.ts +8 -0
- package/dist/src/middleware/validate.d.ts.map +1 -1
- package/dist/src/middleware/validate.js +45 -0
- package/dist/src/middleware/validate.js.map +1 -1
- package/dist/src/policy/engine.d.ts +4 -0
- package/dist/src/policy/engine.d.ts.map +1 -1
- package/dist/src/policy/engine.js +117 -0
- package/dist/src/policy/engine.js.map +1 -1
- package/dist/src/saas/routes.d.ts.map +1 -1
- package/dist/src/saas/routes.js +327 -10
- package/dist/src/saas/routes.js.map +1 -1
- package/dist/src/server/app.d.ts.map +1 -1
- package/dist/src/server/app.js +19 -2
- package/dist/src/server/app.js.map +1 -1
- package/dist/src/server/gateway.d.ts.map +1 -1
- package/dist/src/server/gateway.js +17 -0
- package/dist/src/server/gateway.js.map +1 -1
- package/dist/src/server/index.d.ts.map +1 -1
- package/dist/src/server/index.js +18 -0
- package/dist/src/server/index.js.map +1 -1
- package/dist/src/storage/interfaces.d.ts +14 -3
- package/dist/src/storage/interfaces.d.ts.map +1 -1
- package/dist/src/storage/memory.d.ts +2 -0
- package/dist/src/storage/memory.d.ts.map +1 -1
- package/dist/src/storage/memory.js +6 -0
- package/dist/src/storage/memory.js.map +1 -1
- package/dist/src/storage/postgres.d.ts +5 -0
- package/dist/src/storage/postgres.d.ts.map +1 -1
- package/dist/src/storage/postgres.js +16 -0
- package/dist/src/storage/postgres.js.map +1 -1
- package/dist/src/storage/redis.d.ts +10 -0
- package/dist/src/storage/redis.d.ts.map +1 -1
- package/dist/src/storage/redis.js +65 -0
- package/dist/src/storage/redis.js.map +1 -1
- package/dist/src/types/budget.d.ts +4 -0
- package/dist/src/types/budget.d.ts.map +1 -1
- package/dist/src/types/config.d.ts +58 -0
- package/dist/src/types/config.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +1 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/policy.d.ts +11 -1
- package/dist/src/types/policy.d.ts.map +1 -1
- package/dist/src/types/tool-result.d.ts +11 -0
- package/dist/src/types/tool-result.d.ts.map +1 -1
- package/dist/tests/unit/app-routes.test.d.ts +2 -0
- package/dist/tests/unit/app-routes.test.d.ts.map +1 -0
- package/dist/tests/unit/app-routes.test.js +715 -0
- package/dist/tests/unit/app-routes.test.js.map +1 -0
- package/dist/tests/unit/audit-logger.test.js +105 -0
- package/dist/tests/unit/audit-logger.test.js.map +1 -1
- package/dist/tests/unit/auth-providers.test.d.ts +2 -0
- package/dist/tests/unit/auth-providers.test.d.ts.map +1 -0
- package/dist/tests/unit/auth-providers.test.js +279 -0
- package/dist/tests/unit/auth-providers.test.js.map +1 -0
- package/dist/tests/unit/auth-routes-extended.test.d.ts +2 -0
- package/dist/tests/unit/auth-routes-extended.test.d.ts.map +1 -0
- package/dist/tests/unit/auth-routes-extended.test.js +993 -0
- package/dist/tests/unit/auth-routes-extended.test.js.map +1 -0
- package/dist/tests/unit/auth-verifier.test.d.ts +2 -0
- package/dist/tests/unit/auth-verifier.test.d.ts.map +1 -0
- package/dist/tests/unit/auth-verifier.test.js +505 -0
- package/dist/tests/unit/auth-verifier.test.js.map +1 -0
- package/dist/tests/unit/billing-routes.test.d.ts +2 -0
- package/dist/tests/unit/billing-routes.test.d.ts.map +1 -0
- package/dist/tests/unit/billing-routes.test.js +432 -0
- package/dist/tests/unit/billing-routes.test.js.map +1 -0
- package/dist/tests/unit/config-defaults.test.d.ts +2 -0
- package/dist/tests/unit/config-defaults.test.d.ts.map +1 -0
- package/dist/tests/unit/config-defaults.test.js +119 -0
- package/dist/tests/unit/config-defaults.test.js.map +1 -0
- package/dist/tests/unit/defaults.test.js +0 -10
- package/dist/tests/unit/defaults.test.js.map +1 -1
- package/dist/tests/unit/filesystem-executor.test.d.ts +2 -0
- package/dist/tests/unit/filesystem-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/filesystem-executor.test.js +280 -0
- package/dist/tests/unit/filesystem-executor.test.js.map +1 -0
- package/dist/tests/unit/gateway-branches.test.d.ts +2 -0
- package/dist/tests/unit/gateway-branches.test.d.ts.map +1 -0
- package/dist/tests/unit/gateway-branches.test.js +1039 -0
- package/dist/tests/unit/gateway-branches.test.js.map +1 -0
- package/dist/tests/unit/http-executor-branches.test.d.ts +2 -0
- package/dist/tests/unit/http-executor-branches.test.d.ts.map +1 -0
- package/dist/tests/unit/http-executor-branches.test.js +495 -0
- package/dist/tests/unit/http-executor-branches.test.js.map +1 -0
- package/dist/tests/unit/logger.test.d.ts +2 -0
- package/dist/tests/unit/logger.test.d.ts.map +1 -0
- package/dist/tests/unit/logger.test.js +97 -0
- package/dist/tests/unit/logger.test.js.map +1 -0
- package/dist/tests/unit/metrics.test.js +102 -0
- package/dist/tests/unit/metrics.test.js.map +1 -1
- package/dist/tests/unit/model-pricing.test.d.ts +2 -0
- package/dist/tests/unit/model-pricing.test.d.ts.map +1 -0
- package/dist/tests/unit/model-pricing.test.js +87 -0
- package/dist/tests/unit/model-pricing.test.js.map +1 -0
- package/dist/tests/unit/oauth-stores.test.d.ts +2 -0
- package/dist/tests/unit/oauth-stores.test.d.ts.map +1 -0
- package/dist/tests/unit/oauth-stores.test.js +260 -0
- package/dist/tests/unit/oauth-stores.test.js.map +1 -0
- package/dist/tests/unit/policy-engine.test.js +466 -0
- package/dist/tests/unit/policy-engine.test.js.map +1 -1
- package/dist/tests/unit/provider-interceptor.test.d.ts +2 -0
- package/dist/tests/unit/provider-interceptor.test.d.ts.map +1 -0
- package/dist/tests/unit/provider-interceptor.test.js +472 -0
- package/dist/tests/unit/provider-interceptor.test.js.map +1 -0
- package/dist/tests/unit/saas-routes-branches.test.d.ts +2 -0
- package/dist/tests/unit/saas-routes-branches.test.d.ts.map +1 -0
- package/dist/tests/unit/saas-routes-branches.test.js +2040 -0
- package/dist/tests/unit/saas-routes-branches.test.js.map +1 -0
- package/dist/tests/unit/saas-routes-crud.test.d.ts +2 -0
- package/dist/tests/unit/saas-routes-crud.test.d.ts.map +1 -0
- package/dist/tests/unit/saas-routes-crud.test.js +332 -0
- package/dist/tests/unit/saas-routes-crud.test.js.map +1 -0
- package/dist/tests/unit/saas-routes-data.test.d.ts +2 -0
- package/dist/tests/unit/saas-routes-data.test.d.ts.map +1 -0
- package/dist/tests/unit/saas-routes-data.test.js +405 -0
- package/dist/tests/unit/saas-routes-data.test.js.map +1 -0
- package/dist/tests/unit/saas-routes.test.js +3 -3
- package/dist/tests/unit/saas-routes.test.js.map +1 -1
- package/dist/tests/unit/shell-executor.test.d.ts +2 -0
- package/dist/tests/unit/shell-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/shell-executor.test.js +145 -0
- package/dist/tests/unit/shell-executor.test.js.map +1 -0
- package/dist/tests/unit/sql-executor.test.d.ts +2 -0
- package/dist/tests/unit/sql-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/sql-executor.test.js +177 -0
- package/dist/tests/unit/sql-executor.test.js.map +1 -0
- package/dist/tests/unit/stream-proxy.test.d.ts +2 -0
- package/dist/tests/unit/stream-proxy.test.d.ts.map +1 -0
- package/dist/tests/unit/stream-proxy.test.js +147 -0
- package/dist/tests/unit/stream-proxy.test.js.map +1 -0
- package/dist/tests/unit/tool-definitions.test.d.ts +2 -0
- package/dist/tests/unit/tool-definitions.test.d.ts.map +1 -0
- package/dist/tests/unit/tool-definitions.test.js +184 -0
- package/dist/tests/unit/tool-definitions.test.js.map +1 -0
- package/dist/tests/unit/usage-extractor.test.js +140 -0
- package/dist/tests/unit/usage-extractor.test.js.map +1 -1
- package/dist/tests/unit/webhook-handler.test.d.ts +2 -0
- package/dist/tests/unit/webhook-handler.test.d.ts.map +1 -0
- package/dist/tests/unit/webhook-handler.test.js +453 -0
- package/dist/tests/unit/webhook-handler.test.js.map +1 -0
- package/dist/tests/unit/webhook-routes.test.d.ts +2 -0
- package/dist/tests/unit/webhook-routes.test.d.ts.map +1 -0
- package/dist/tests/unit/webhook-routes.test.js +69 -0
- package/dist/tests/unit/webhook-routes.test.js.map +1 -0
- package/dist/tests/unit/websocket-executor.test.d.ts +2 -0
- package/dist/tests/unit/websocket-executor.test.d.ts.map +1 -0
- package/dist/tests/unit/websocket-executor.test.js +121 -0
- package/dist/tests/unit/websocket-executor.test.js.map +1 -0
- package/package.json +8 -2
- package/policy-packs/demo_fail.yaml +41 -0
- package/policy-packs/full_tools.yaml +136 -0
- package/src/admin/index.ts +1 -0
- package/src/admin/routes.ts +509 -0
- package/src/admin/templates.ts +572 -0
- package/src/anomaly/detector.ts +717 -0
- package/src/anomaly/index.ts +1 -0
- package/src/approval/manager.ts +569 -0
- package/src/approval/webhook.ts +133 -0
- package/src/audit/logger.ts +490 -0
- package/src/auth/index.ts +5 -0
- package/src/auth/password.ts +21 -0
- package/src/auth/pkce.ts +22 -0
- package/src/auth/providers.ts +208 -0
- package/src/auth/routes.ts +521 -0
- package/src/auth/session.ts +84 -0
- package/src/billing/index.ts +6 -0
- package/src/billing/plan-enforcer.ts +135 -0
- package/src/billing/routes.ts +229 -0
- package/src/billing/stripe-client.ts +58 -0
- package/src/billing/webhook-handler.ts +182 -0
- package/src/billing/webhook-routes.ts +28 -0
- package/src/budget/manager.ts +679 -0
- package/src/budget/model-pricing.ts +119 -0
- package/src/budget/usage-extractor.ts +214 -0
- package/src/cli.ts +91 -0
- package/src/config/defaults.ts +261 -0
- package/src/config/validate.ts +88 -0
- package/src/dlp/composite-scanner.ts +213 -0
- package/src/dlp/index.ts +9 -0
- package/src/dlp/interfaces.ts +34 -0
- package/src/dlp/patterns.ts +30 -0
- package/src/dlp/prompt-injection-backend.ts +181 -0
- package/src/dlp/prompt-injection-patterns.ts +302 -0
- package/src/dlp/regex-backend.ts +181 -0
- package/src/dlp/scanner.ts +502 -0
- package/src/dlp/text-normalizer.ts +225 -0
- package/src/dlp/tool-patterns.ts +35 -0
- package/src/dlp/trufflehog-backend.ts +190 -0
- package/src/executor/filesystem-executor.ts +196 -0
- package/src/executor/http-executor.ts +330 -0
- package/src/executor/index.ts +9 -0
- package/src/executor/interfaces.ts +11 -0
- package/src/executor/noop-executor.ts +23 -0
- package/src/executor/registry.ts +64 -0
- package/src/executor/shell-executor.ts +148 -0
- package/src/executor/slack-executor.ts +176 -0
- package/src/executor/sql-executor.ts +146 -0
- package/src/executor/websocket-executor.ts +211 -0
- package/src/index.ts +24 -0
- package/src/interceptor/index.ts +1 -0
- package/src/interceptor/provider-interceptor.ts +315 -0
- package/src/mcp/auth-verifier.ts +152 -0
- package/src/mcp/bridge.ts +703 -0
- package/src/mcp/http-transport.ts +672 -0
- package/src/mcp/index.ts +9 -0
- package/src/mcp/oauth-pages.ts +139 -0
- package/src/mcp/oauth-postgres-stores.ts +278 -0
- package/src/mcp/oauth-provider.ts +536 -0
- package/src/mcp/oauth-stores.ts +202 -0
- package/src/mcp/server.ts +55 -0
- package/src/mcp/tool-definitions.ts +562 -0
- package/src/metrics/collector.ts +357 -0
- package/src/metrics/index.ts +1 -0
- package/src/middleware/auth.ts +814 -0
- package/src/middleware/session.ts +85 -0
- package/src/middleware/validate.ts +130 -0
- package/src/policy/engine.ts +815 -0
- package/src/policy/index.ts +2 -0
- package/src/policy/opa-engine.ts +829 -0
- package/src/proxy/forward-proxy.ts +649 -0
- package/src/proxy/index.ts +1 -0
- package/src/ratelimit/limiter.ts +196 -0
- package/src/replay/engine.ts +142 -0
- package/src/replay/index.ts +1 -0
- package/src/saas/index.ts +1 -0
- package/src/saas/routes.ts +2161 -0
- package/src/server/app.ts +981 -0
- package/src/server/errors.ts +49 -0
- package/src/server/gateway.ts +1130 -0
- package/src/server/index.ts +307 -0
- package/src/server/logger.ts +255 -0
- package/src/server/stream-proxy.ts +202 -0
- package/src/storage/file-persistence.ts +315 -0
- package/src/storage/index.ts +4 -0
- package/src/storage/interfaces.ts +287 -0
- package/src/storage/memory.ts +686 -0
- package/src/storage/postgres.ts +1831 -0
- package/src/storage/redis.ts +835 -0
- package/src/tracing/index.ts +1 -0
- package/src/tracing/provider.ts +100 -0
- package/src/trust/calculator.ts +141 -0
- package/src/trust/index.ts +7 -0
- package/src/types/budget.ts +36 -0
- package/src/types/config.ts +278 -0
- package/src/types/events.ts +41 -0
- package/src/types/express.d.ts +14 -0
- package/src/types/index.ts +7 -0
- package/src/types/policy.ts +83 -0
- package/src/types/stripe-config.ts +11 -0
- package/src/types/subscription.ts +59 -0
- package/src/types/tool-call.ts +47 -0
- package/src/types/tool-result.ts +82 -0
- package/src/types/user.ts +125 -0
- package/tsconfig.json +24 -0
package/dist/src/saas/routes.js
CHANGED
|
@@ -41,6 +41,7 @@ const memory_1 = require("../storage/memory");
|
|
|
41
41
|
const calculator_1 = require("../trust/calculator");
|
|
42
42
|
const engine_1 = require("../replay/engine");
|
|
43
43
|
const plan_enforcer_1 = require("../billing/plan-enforcer");
|
|
44
|
+
const model_pricing_1 = require("../budget/model-pricing");
|
|
44
45
|
/** Extract a route param as string (Express 5 returns string | string[]). */
|
|
45
46
|
function param(req, name) {
|
|
46
47
|
const val = req.params[name];
|
|
@@ -313,7 +314,7 @@ function createSaaSRouter(deps) {
|
|
|
313
314
|
res.json({
|
|
314
315
|
api_keys: keys.map(k => ({
|
|
315
316
|
id: k.id,
|
|
316
|
-
|
|
317
|
+
prefix: k.key_prefix,
|
|
317
318
|
name: k.name,
|
|
318
319
|
roles: k.roles,
|
|
319
320
|
tags: k.tags || [],
|
|
@@ -376,7 +377,7 @@ function createSaaSRouter(deps) {
|
|
|
376
377
|
res.status(201).json({
|
|
377
378
|
id: apiKey.id,
|
|
378
379
|
key: rawKey,
|
|
379
|
-
|
|
380
|
+
prefix: keyPrefix,
|
|
380
381
|
name: apiKey.name,
|
|
381
382
|
roles: apiKey.roles,
|
|
382
383
|
tags: apiKey.tags,
|
|
@@ -495,6 +496,201 @@ function createSaaSRouter(deps) {
|
|
|
495
496
|
offset,
|
|
496
497
|
});
|
|
497
498
|
});
|
|
499
|
+
router.delete('/workspaces/:id/traces', (req, res) => {
|
|
500
|
+
if (!requireSession(req, res))
|
|
501
|
+
return;
|
|
502
|
+
const user = req.sessionUser;
|
|
503
|
+
const workspaceId = param(req, 'id');
|
|
504
|
+
const membership = workspaceMemberStore.getByWorkspaceAndUser(workspaceId, user.id);
|
|
505
|
+
if (!membership || (membership.role !== 'owner' && membership.role !== 'admin')) {
|
|
506
|
+
res.status(403).json({ error: 'Admin or owner access required' });
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
gateway.getAuditLogger().clear();
|
|
510
|
+
res.json({ status: 'ok', message: 'All traces cleared' });
|
|
511
|
+
});
|
|
512
|
+
router.delete('/workspaces/:id/traces/:taskId', (req, res) => {
|
|
513
|
+
if (!requireSession(req, res))
|
|
514
|
+
return;
|
|
515
|
+
const user = req.sessionUser;
|
|
516
|
+
const workspaceId = param(req, 'id');
|
|
517
|
+
const taskId = param(req, 'taskId');
|
|
518
|
+
const membership = workspaceMemberStore.getByWorkspaceAndUser(workspaceId, user.id);
|
|
519
|
+
if (!membership || (membership.role !== 'owner' && membership.role !== 'admin')) {
|
|
520
|
+
res.status(403).json({ error: 'Admin or owner access required' });
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
gateway.getAuditLogger().deleteByTaskId(taskId);
|
|
524
|
+
res.json({ status: 'ok', message: `Trace ${taskId} deleted` });
|
|
525
|
+
});
|
|
526
|
+
router.delete('/workspaces/:id/sessions/:sessionId', (req, res) => {
|
|
527
|
+
if (!requireSession(req, res))
|
|
528
|
+
return;
|
|
529
|
+
const user = req.sessionUser;
|
|
530
|
+
const workspaceId = param(req, 'id');
|
|
531
|
+
const sessionId = param(req, 'sessionId');
|
|
532
|
+
const membership = workspaceMemberStore.getByWorkspaceAndUser(workspaceId, user.id);
|
|
533
|
+
if (!membership || (membership.role !== 'owner' && membership.role !== 'admin')) {
|
|
534
|
+
res.status(403).json({ error: 'Admin or owner access required' });
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
gateway.getAuditLogger().deleteBySessionId(sessionId);
|
|
538
|
+
res.json({ status: 'ok', message: `Session ${sessionId} traces deleted` });
|
|
539
|
+
});
|
|
540
|
+
// ---------------------------------------------------------------------------
|
|
541
|
+
// Sessions (grouped traces)
|
|
542
|
+
// ---------------------------------------------------------------------------
|
|
543
|
+
router.get('/workspaces/:id/sessions', (req, res) => {
|
|
544
|
+
if (!requireSession(req, res))
|
|
545
|
+
return;
|
|
546
|
+
const user = req.sessionUser;
|
|
547
|
+
const workspaceId = param(req, 'id');
|
|
548
|
+
const membership = workspaceMemberStore.getByWorkspaceAndUser(workspaceId, user.id);
|
|
549
|
+
if (!membership) {
|
|
550
|
+
res.status(403).json({ error: 'Not a member of this workspace' });
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
const limit = Math.min(parseInt(req.query.limit) || 50, 200);
|
|
554
|
+
const offset = parseInt(req.query.offset) || 0;
|
|
555
|
+
const allEvents = gateway.getAuditLogger().getAllEvents();
|
|
556
|
+
const wsForSessions = workspaceStore.getById(workspaceId);
|
|
557
|
+
const slugForSessions = wsForSessions?.slug;
|
|
558
|
+
const wsEvents = allEvents.filter(e => e.workspace_id === workspaceId || (slugForSessions && e.workspace_id === slugForSessions));
|
|
559
|
+
// Group events by session_id (falling back to tool_call_id for old events)
|
|
560
|
+
const sessionMap = new Map();
|
|
561
|
+
for (const e of wsEvents) {
|
|
562
|
+
const key = e.session_id || e.tool_call_id || e.task_id;
|
|
563
|
+
if (!sessionMap.has(key))
|
|
564
|
+
sessionMap.set(key, []);
|
|
565
|
+
sessionMap.get(key).push(e);
|
|
566
|
+
}
|
|
567
|
+
// Build session summaries
|
|
568
|
+
const sessions = [];
|
|
569
|
+
for (const [sessionId, events] of sessionMap) {
|
|
570
|
+
events.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
571
|
+
const first = events[0];
|
|
572
|
+
const last = events[events.length - 1];
|
|
573
|
+
// Count unique tool_call_ids (each represents one tool call)
|
|
574
|
+
const toolCallIds = new Set(events.map(e => e.tool_call_id));
|
|
575
|
+
const receivedEvents = events.filter(e => e.event_type === 'TOOL_CALL_RECEIVED');
|
|
576
|
+
const toolCallCount = Math.max(receivedEvents.length, 1);
|
|
577
|
+
// Unique tools
|
|
578
|
+
const toolsUsed = [...new Set(events.map(e => e.tool_name).filter(Boolean))];
|
|
579
|
+
// Derive status counts per tool_call_id
|
|
580
|
+
const statusCounts = { ok: 0, blocked: 0, error: 0, approval: 0, redacted: 0 };
|
|
581
|
+
for (const tcId of toolCallIds) {
|
|
582
|
+
const tcEvents = events.filter(e => e.tool_call_id === tcId);
|
|
583
|
+
const hasDeny = tcEvents.some(e => e.event_type === 'POLICY_DECIDED' && e.metadata?.decision === 'deny');
|
|
584
|
+
const hasApproval = tcEvents.some(e => e.event_type === 'APPROVAL_REQUESTED');
|
|
585
|
+
const hasError = tcEvents.some(e => e.event_type === 'TOOL_EXECUTED' && e.metadata?.status === 'error');
|
|
586
|
+
const hasRedaction = tcEvents.some(e => e.event_type === 'DLP_SCANNED' && Array.isArray(e.metadata?.detected) && e.metadata.detected.length > 0);
|
|
587
|
+
if (hasDeny)
|
|
588
|
+
statusCounts.blocked++;
|
|
589
|
+
else if (hasError)
|
|
590
|
+
statusCounts.error++;
|
|
591
|
+
else if (hasApproval)
|
|
592
|
+
statusCounts.approval++;
|
|
593
|
+
else
|
|
594
|
+
statusCounts.ok++;
|
|
595
|
+
// Redacted is orthogonal — a call can be ok AND redacted
|
|
596
|
+
if (hasRedaction)
|
|
597
|
+
statusCounts.redacted++;
|
|
598
|
+
}
|
|
599
|
+
// Overall status: worst status across calls
|
|
600
|
+
let overallStatus = 'ok';
|
|
601
|
+
if (statusCounts.blocked > 0)
|
|
602
|
+
overallStatus = 'blocked';
|
|
603
|
+
else if (statusCounts.error > 0)
|
|
604
|
+
overallStatus = 'error';
|
|
605
|
+
else if (statusCounts.approval > 0)
|
|
606
|
+
overallStatus = 'approval';
|
|
607
|
+
sessions.push({
|
|
608
|
+
session_id: sessionId,
|
|
609
|
+
tool_call_count: toolCallCount,
|
|
610
|
+
first_timestamp: first.timestamp,
|
|
611
|
+
last_timestamp: last.timestamp,
|
|
612
|
+
duration_ms: new Date(last.timestamp).getTime() - new Date(first.timestamp).getTime(),
|
|
613
|
+
actor_id: first.actor_id || '',
|
|
614
|
+
platform: first.metadata?.platform || 'unknown',
|
|
615
|
+
tools_used: toolsUsed,
|
|
616
|
+
overall_status: overallStatus,
|
|
617
|
+
status_counts: statusCounts,
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
// Sort newest first
|
|
621
|
+
sessions.sort((a, b) => b.first_timestamp.localeCompare(a.first_timestamp));
|
|
622
|
+
res.json({
|
|
623
|
+
sessions: sessions.slice(offset, offset + limit),
|
|
624
|
+
total: sessions.length,
|
|
625
|
+
});
|
|
626
|
+
});
|
|
627
|
+
router.get('/workspaces/:id/sessions/:sessionId', (req, res) => {
|
|
628
|
+
if (!requireSession(req, res))
|
|
629
|
+
return;
|
|
630
|
+
const user = req.sessionUser;
|
|
631
|
+
const workspaceId = param(req, 'id');
|
|
632
|
+
const sessionId = param(req, 'sessionId');
|
|
633
|
+
const membership = workspaceMemberStore.getByWorkspaceAndUser(workspaceId, user.id);
|
|
634
|
+
if (!membership) {
|
|
635
|
+
res.status(403).json({ error: 'Not a member of this workspace' });
|
|
636
|
+
return;
|
|
637
|
+
}
|
|
638
|
+
const allEvents = gateway.getAuditLogger().getAllEvents();
|
|
639
|
+
const workspace = workspaceStore.getById(workspaceId);
|
|
640
|
+
const wsSlug = workspace?.slug;
|
|
641
|
+
// Find events by session_id, filtered to this workspace
|
|
642
|
+
const sessionEvents = allEvents.filter(e => e.session_id === sessionId &&
|
|
643
|
+
(e.workspace_id === workspaceId || (wsSlug && e.workspace_id === wsSlug)));
|
|
644
|
+
// If no events found by session_id, try treating sessionId as a tool_call_id (backward compat)
|
|
645
|
+
const events = sessionEvents.length > 0
|
|
646
|
+
? sessionEvents
|
|
647
|
+
: allEvents.filter(e => e.tool_call_id === sessionId &&
|
|
648
|
+
(e.workspace_id === workspaceId || (wsSlug && e.workspace_id === wsSlug)));
|
|
649
|
+
// Group by tool_call_id
|
|
650
|
+
const toolCallMap = new Map();
|
|
651
|
+
for (const e of events) {
|
|
652
|
+
const tcId = e.tool_call_id;
|
|
653
|
+
if (!toolCallMap.has(tcId))
|
|
654
|
+
toolCallMap.set(tcId, []);
|
|
655
|
+
toolCallMap.get(tcId).push(e);
|
|
656
|
+
}
|
|
657
|
+
const toolCalls = [];
|
|
658
|
+
for (const [tcId, tcEvents] of toolCallMap) {
|
|
659
|
+
tcEvents.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
660
|
+
const first = tcEvents[0];
|
|
661
|
+
const last = tcEvents[tcEvents.length - 1];
|
|
662
|
+
const resultEvent = tcEvents.find(e => e.event_type === 'TOOL_RESULT_RETURNED');
|
|
663
|
+
const durationMs = resultEvent?.metadata?.duration_ms ||
|
|
664
|
+
(new Date(last.timestamp).getTime() - new Date(first.timestamp).getTime());
|
|
665
|
+
// Derive status
|
|
666
|
+
const hasDeny = tcEvents.some(e => e.event_type === 'POLICY_DECIDED' && e.metadata?.decision === 'deny');
|
|
667
|
+
const hasApproval = tcEvents.some(e => e.event_type === 'APPROVAL_REQUESTED');
|
|
668
|
+
const hasError = tcEvents.some(e => e.event_type === 'TOOL_EXECUTED' && e.metadata?.status === 'error');
|
|
669
|
+
let status = 'ok';
|
|
670
|
+
if (hasDeny)
|
|
671
|
+
status = 'blocked';
|
|
672
|
+
else if (hasError)
|
|
673
|
+
status = 'error';
|
|
674
|
+
else if (hasApproval)
|
|
675
|
+
status = 'approval';
|
|
676
|
+
toolCalls.push({
|
|
677
|
+
tool_call_id: tcId,
|
|
678
|
+
task_id: first.task_id,
|
|
679
|
+
tool_name: first.tool_name || 'unknown',
|
|
680
|
+
timestamp: first.timestamp,
|
|
681
|
+
duration_ms: durationMs,
|
|
682
|
+
status,
|
|
683
|
+
events: tcEvents,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
// Sort by timestamp
|
|
687
|
+
toolCalls.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
688
|
+
res.json({
|
|
689
|
+
session_id: sessionId,
|
|
690
|
+
tool_calls: toolCalls,
|
|
691
|
+
total_events: events.length,
|
|
692
|
+
});
|
|
693
|
+
});
|
|
498
694
|
// ---------------------------------------------------------------------------
|
|
499
695
|
// Budgets
|
|
500
696
|
// ---------------------------------------------------------------------------
|
|
@@ -712,6 +908,23 @@ function createSaaSRouter(deps) {
|
|
|
712
908
|
// ---------------------------------------------------------------------------
|
|
713
909
|
// Policy Rule Generation (LLM-powered)
|
|
714
910
|
// ---------------------------------------------------------------------------
|
|
911
|
+
// Per-user rate limiter for LLM rule generation: 10 requests per hour
|
|
912
|
+
const LLM_RATE_LIMIT_MAX = 10;
|
|
913
|
+
const LLM_RATE_LIMIT_WINDOW_MS = 60 * 60 * 1000; // 1 hour
|
|
914
|
+
const llmRateLimitHits = new Map();
|
|
915
|
+
// Periodic cleanup to prevent memory leaks
|
|
916
|
+
setInterval(() => {
|
|
917
|
+
const now = Date.now();
|
|
918
|
+
for (const [userId, timestamps] of llmRateLimitHits) {
|
|
919
|
+
const valid = timestamps.filter(t => now - t < LLM_RATE_LIMIT_WINDOW_MS);
|
|
920
|
+
if (valid.length === 0) {
|
|
921
|
+
llmRateLimitHits.delete(userId);
|
|
922
|
+
}
|
|
923
|
+
else {
|
|
924
|
+
llmRateLimitHits.set(userId, valid);
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
}, LLM_RATE_LIMIT_WINDOW_MS).unref();
|
|
715
928
|
router.post('/workspaces/:id/policies/generate-rule', async (req, res) => {
|
|
716
929
|
if (!requireSession(req, res))
|
|
717
930
|
return;
|
|
@@ -733,6 +946,21 @@ function createSaaSRouter(deps) {
|
|
|
733
946
|
return;
|
|
734
947
|
}
|
|
735
948
|
const trimmed = description.trim().slice(0, 500);
|
|
949
|
+
// Per-user rate limit check (after validation, before LLM call)
|
|
950
|
+
const now = Date.now();
|
|
951
|
+
const userTimestamps = llmRateLimitHits.get(user.id) || [];
|
|
952
|
+
const windowStart = now - LLM_RATE_LIMIT_WINDOW_MS;
|
|
953
|
+
const validTimestamps = userTimestamps.filter(t => t > windowStart);
|
|
954
|
+
if (validTimestamps.length >= LLM_RATE_LIMIT_MAX) {
|
|
955
|
+
const retryAfterMs = LLM_RATE_LIMIT_WINDOW_MS - (now - validTimestamps[0]);
|
|
956
|
+
res.status(429).json({
|
|
957
|
+
error: `You've reached the limit of ${LLM_RATE_LIMIT_MAX} AI generations per hour. Please try again later.`,
|
|
958
|
+
retry_after_ms: retryAfterMs,
|
|
959
|
+
});
|
|
960
|
+
return;
|
|
961
|
+
}
|
|
962
|
+
validTimestamps.push(now);
|
|
963
|
+
llmRateLimitHits.set(user.id, validTimestamps);
|
|
736
964
|
const systemPrompt = `You are a policy rule generator for Palaryn, an AI agent gateway. Given a natural language description, generate a single JSON PolicyRule object.
|
|
737
965
|
|
|
738
966
|
The PolicyRule schema:
|
|
@@ -862,20 +1090,38 @@ Output: {"name":"approve-slack-writes","description":"Require approval for write
|
|
|
862
1090
|
}
|
|
863
1091
|
const limit = Math.min(parseInt(req.query.limit) || 50, 200);
|
|
864
1092
|
const offset = parseInt(req.query.offset) || 0;
|
|
1093
|
+
const severity = req.query.severity;
|
|
1094
|
+
const tool = req.query.tool;
|
|
1095
|
+
const actor = req.query.actor;
|
|
1096
|
+
const q = req.query.q;
|
|
865
1097
|
const allDlpEvents = gateway.getAuditLogger().getEventsByType('DLP_SCANNED');
|
|
866
1098
|
const workspace = workspaceStore.getById(workspaceId);
|
|
867
1099
|
const wsSlug = workspace?.slug;
|
|
868
1100
|
const wsEvents = allDlpEvents
|
|
869
1101
|
.filter(e => e.workspace_id === workspaceId || (wsSlug && e.workspace_id === wsSlug))
|
|
870
1102
|
.sort((a, b) => b.timestamp.localeCompare(a.timestamp));
|
|
871
|
-
//
|
|
1103
|
+
// Apply optional filters
|
|
1104
|
+
let filtered = wsEvents;
|
|
1105
|
+
if (severity)
|
|
1106
|
+
filtered = filtered.filter(e => e.metadata?.severity === severity);
|
|
1107
|
+
if (tool)
|
|
1108
|
+
filtered = filtered.filter(e => e.tool_name.toLowerCase().includes(tool.toLowerCase()));
|
|
1109
|
+
if (actor)
|
|
1110
|
+
filtered = filtered.filter(e => e.actor_id.toLowerCase().includes(actor.toLowerCase()));
|
|
1111
|
+
if (q) {
|
|
1112
|
+
const ql = q.toLowerCase();
|
|
1113
|
+
filtered = filtered.filter(e => e.tool_name.toLowerCase().includes(ql) ||
|
|
1114
|
+
e.actor_id.toLowerCase().includes(ql) ||
|
|
1115
|
+
(e.metadata?.detected || []).some(p => p.toLowerCase().includes(ql)));
|
|
1116
|
+
}
|
|
1117
|
+
// Compute severity stats over filtered results
|
|
872
1118
|
let high = 0, medium = 0, low = 0;
|
|
873
1119
|
const patternSet = new Set();
|
|
874
|
-
for (const e of
|
|
875
|
-
const
|
|
876
|
-
if (
|
|
1120
|
+
for (const e of filtered) {
|
|
1121
|
+
const sev = e.metadata?.severity;
|
|
1122
|
+
if (sev === 'high')
|
|
877
1123
|
high++;
|
|
878
|
-
else if (
|
|
1124
|
+
else if (sev === 'medium')
|
|
879
1125
|
medium++;
|
|
880
1126
|
else
|
|
881
1127
|
low++;
|
|
@@ -886,12 +1132,12 @@ Output: {"name":"approve-slack-writes","description":"Require approval for write
|
|
|
886
1132
|
}
|
|
887
1133
|
}
|
|
888
1134
|
res.json({
|
|
889
|
-
events:
|
|
890
|
-
total:
|
|
1135
|
+
events: filtered.slice(offset, offset + limit),
|
|
1136
|
+
total: filtered.length,
|
|
891
1137
|
limit,
|
|
892
1138
|
offset,
|
|
893
1139
|
stats: {
|
|
894
|
-
total:
|
|
1140
|
+
total: filtered.length,
|
|
895
1141
|
high,
|
|
896
1142
|
medium,
|
|
897
1143
|
low,
|
|
@@ -1140,11 +1386,16 @@ Output: {"name":"approve-slack-writes","description":"Require approval for write
|
|
|
1140
1386
|
const denyCount = allStats.reduce((acc, s) => acc + (s.policy_breakdown['deny'] || 0), 0);
|
|
1141
1387
|
const shield_score = {
|
|
1142
1388
|
score: totalDecisions > 0 ? Math.round(((allowCount + transformCount) / totalDecisions) * 100) : 100,
|
|
1389
|
+
total: totalDecisions,
|
|
1143
1390
|
breakdown: {
|
|
1144
1391
|
allowed_percent: totalDecisions > 0 ? Math.round((allowCount / totalDecisions) * 100) : 100,
|
|
1145
1392
|
transformed_percent: totalDecisions > 0 ? Math.round((transformCount / totalDecisions) * 100) : 0,
|
|
1146
1393
|
approval_percent: totalDecisions > 0 ? Math.round((approvalCount / totalDecisions) * 100) : 0,
|
|
1147
1394
|
blocked_percent: totalDecisions > 0 ? Math.round((denyCount / totalDecisions) * 100) : 0,
|
|
1395
|
+
allowed_count: allowCount,
|
|
1396
|
+
transformed_count: transformCount,
|
|
1397
|
+
approval_count: approvalCount,
|
|
1398
|
+
blocked_count: denyCount,
|
|
1148
1399
|
},
|
|
1149
1400
|
};
|
|
1150
1401
|
// Pipeline throughput from stats (merged across all matching workspace IDs)
|
|
@@ -1561,6 +1812,72 @@ Output: {"name":"approve-slack-writes","description":"Require approval for write
|
|
|
1561
1812
|
});
|
|
1562
1813
|
res.json({ status: 'reset', config: config.budget, is_custom: false });
|
|
1563
1814
|
});
|
|
1815
|
+
// ---------------------------------------------------------------------------
|
|
1816
|
+
// Model Pricing: Get merged pricing (built-in + workspace overrides)
|
|
1817
|
+
// ---------------------------------------------------------------------------
|
|
1818
|
+
router.get('/workspaces/:id/model-pricing', (req, res) => {
|
|
1819
|
+
if (!requireSession(req, res))
|
|
1820
|
+
return;
|
|
1821
|
+
const user = req.sessionUser;
|
|
1822
|
+
const workspaceId = param(req, 'id');
|
|
1823
|
+
const membership = workspaceMemberStore.getByWorkspaceAndUser(workspaceId, user.id);
|
|
1824
|
+
if (!membership) {
|
|
1825
|
+
res.status(403).json({ error: 'Not a member of this workspace' });
|
|
1826
|
+
return;
|
|
1827
|
+
}
|
|
1828
|
+
// Get workspace-level budget config overrides (if any)
|
|
1829
|
+
const wsConfig = budgetConfigStore.getByWorkspaceId(workspaceId);
|
|
1830
|
+
const workspaceOverrides = wsConfig?.token_pricing;
|
|
1831
|
+
// Merge: built-in pricing as base, workspace overrides on top
|
|
1832
|
+
const merged = {};
|
|
1833
|
+
for (const [model, pricing] of Object.entries(model_pricing_1.MODEL_PRICING)) {
|
|
1834
|
+
merged[model] = {
|
|
1835
|
+
input_per_token: pricing.input_per_token,
|
|
1836
|
+
output_per_token: pricing.output_per_token,
|
|
1837
|
+
source: 'built-in',
|
|
1838
|
+
};
|
|
1839
|
+
}
|
|
1840
|
+
if (workspaceOverrides) {
|
|
1841
|
+
for (const [model, pricing] of Object.entries(workspaceOverrides)) {
|
|
1842
|
+
merged[model] = {
|
|
1843
|
+
input_per_token: pricing.input_per_token,
|
|
1844
|
+
output_per_token: pricing.output_per_token,
|
|
1845
|
+
source: 'workspace',
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
res.json({
|
|
1850
|
+
models: merged,
|
|
1851
|
+
has_workspace_overrides: !!workspaceOverrides,
|
|
1852
|
+
});
|
|
1853
|
+
});
|
|
1854
|
+
// ---------------------------------------------------------------------------
|
|
1855
|
+
// Admin: Update Workspace Plan (platform admin only)
|
|
1856
|
+
// ---------------------------------------------------------------------------
|
|
1857
|
+
const VALID_PLANS = ['free', 'pro', 'business', 'enterprise'];
|
|
1858
|
+
router.put('/workspaces/:id/plan', (req, res) => {
|
|
1859
|
+
if (!requireSession(req, res))
|
|
1860
|
+
return;
|
|
1861
|
+
const user = req.sessionUser;
|
|
1862
|
+
const adminEmail = process.env.SEED_ADMIN_EMAIL;
|
|
1863
|
+
if (!adminEmail || user.email !== adminEmail) {
|
|
1864
|
+
res.status(403).json({ error: 'Platform admin access required' });
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
const { plan } = req.body || {};
|
|
1868
|
+
if (!plan || !VALID_PLANS.includes(plan)) {
|
|
1869
|
+
res.status(400).json({ error: `Invalid plan. Must be one of: ${VALID_PLANS.join(', ')}` });
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1872
|
+
const workspaceId = param(req, 'id');
|
|
1873
|
+
const workspace = workspaceStore.getById(workspaceId);
|
|
1874
|
+
if (!workspace) {
|
|
1875
|
+
res.status(404).json({ error: 'Workspace not found' });
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
const updated = workspaceStore.update(workspaceId, { plan, updated_at: new Date().toISOString() });
|
|
1879
|
+
res.json(updated);
|
|
1880
|
+
});
|
|
1564
1881
|
return router;
|
|
1565
1882
|
}
|
|
1566
1883
|
//# sourceMappingURL=routes.js.map
|