@agent-relay/cloud 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/dist/api/admin.d.ts +8 -0
- package/dist/api/admin.d.ts.map +1 -0
- package/dist/api/admin.js +225 -0
- package/dist/api/admin.js.map +1 -0
- package/dist/api/auth.d.ts +20 -0
- package/dist/api/auth.d.ts.map +1 -0
- package/dist/api/auth.js +136 -0
- package/dist/api/auth.js.map +1 -0
- package/dist/api/billing.d.ts +7 -0
- package/dist/api/billing.d.ts.map +1 -0
- package/dist/api/billing.js +564 -0
- package/dist/api/billing.js.map +1 -0
- package/dist/api/cli-pty-runner.d.ts +53 -0
- package/dist/api/cli-pty-runner.d.ts.map +1 -0
- package/dist/api/cli-pty-runner.js +193 -0
- package/dist/api/cli-pty-runner.js.map +1 -0
- package/dist/api/codex-auth-helper.d.ts +21 -0
- package/dist/api/codex-auth-helper.d.ts.map +1 -0
- package/dist/api/codex-auth-helper.js +327 -0
- package/dist/api/codex-auth-helper.js.map +1 -0
- package/dist/api/consensus.d.ts +13 -0
- package/dist/api/consensus.d.ts.map +1 -0
- package/dist/api/consensus.js +261 -0
- package/dist/api/consensus.js.map +1 -0
- package/dist/api/coordinators.d.ts +8 -0
- package/dist/api/coordinators.d.ts.map +1 -0
- package/dist/api/coordinators.js +750 -0
- package/dist/api/coordinators.js.map +1 -0
- package/dist/api/daemons.d.ts +12 -0
- package/dist/api/daemons.d.ts.map +1 -0
- package/dist/api/daemons.js +535 -0
- package/dist/api/daemons.js.map +1 -0
- package/dist/api/generic-webhooks.d.ts +8 -0
- package/dist/api/generic-webhooks.d.ts.map +1 -0
- package/dist/api/generic-webhooks.js +129 -0
- package/dist/api/generic-webhooks.js.map +1 -0
- package/dist/api/git.d.ts +8 -0
- package/dist/api/git.d.ts.map +1 -0
- package/dist/api/git.js +269 -0
- package/dist/api/git.js.map +1 -0
- package/dist/api/github-app.d.ts +11 -0
- package/dist/api/github-app.d.ts.map +1 -0
- package/dist/api/github-app.js +223 -0
- package/dist/api/github-app.js.map +1 -0
- package/dist/api/middleware/planLimits.d.ts +43 -0
- package/dist/api/middleware/planLimits.d.ts.map +1 -0
- package/dist/api/middleware/planLimits.js +202 -0
- package/dist/api/middleware/planLimits.js.map +1 -0
- package/dist/api/monitoring.d.ts +11 -0
- package/dist/api/monitoring.d.ts.map +1 -0
- package/dist/api/monitoring.js +578 -0
- package/dist/api/monitoring.js.map +1 -0
- package/dist/api/nango-auth.d.ts +9 -0
- package/dist/api/nango-auth.d.ts.map +1 -0
- package/dist/api/nango-auth.js +674 -0
- package/dist/api/nango-auth.js.map +1 -0
- package/dist/api/onboarding.d.ts +15 -0
- package/dist/api/onboarding.d.ts.map +1 -0
- package/dist/api/onboarding.js +679 -0
- package/dist/api/onboarding.js.map +1 -0
- package/dist/api/policy.d.ts +8 -0
- package/dist/api/policy.d.ts.map +1 -0
- package/dist/api/policy.js +229 -0
- package/dist/api/policy.js.map +1 -0
- package/dist/api/provider-env.d.ts +14 -0
- package/dist/api/provider-env.d.ts.map +1 -0
- package/dist/api/provider-env.js +75 -0
- package/dist/api/provider-env.js.map +1 -0
- package/dist/api/providers.d.ts +7 -0
- package/dist/api/providers.d.ts.map +1 -0
- package/dist/api/providers.js +564 -0
- package/dist/api/providers.js.map +1 -0
- package/dist/api/repos.d.ts +8 -0
- package/dist/api/repos.d.ts.map +1 -0
- package/dist/api/repos.js +577 -0
- package/dist/api/repos.js.map +1 -0
- package/dist/api/sessions.d.ts +11 -0
- package/dist/api/sessions.d.ts.map +1 -0
- package/dist/api/sessions.js +302 -0
- package/dist/api/sessions.js.map +1 -0
- package/dist/api/teams.d.ts +7 -0
- package/dist/api/teams.d.ts.map +1 -0
- package/dist/api/teams.js +281 -0
- package/dist/api/teams.js.map +1 -0
- package/dist/api/test-helpers.d.ts +10 -0
- package/dist/api/test-helpers.d.ts.map +1 -0
- package/dist/api/test-helpers.js +745 -0
- package/dist/api/test-helpers.js.map +1 -0
- package/dist/api/usage.d.ts +7 -0
- package/dist/api/usage.d.ts.map +1 -0
- package/dist/api/usage.js +111 -0
- package/dist/api/usage.js.map +1 -0
- package/dist/api/webhooks.d.ts +8 -0
- package/dist/api/webhooks.d.ts.map +1 -0
- package/dist/api/webhooks.js +645 -0
- package/dist/api/webhooks.js.map +1 -0
- package/dist/api/workspaces.d.ts +25 -0
- package/dist/api/workspaces.d.ts.map +1 -0
- package/dist/api/workspaces.js +1799 -0
- package/dist/api/workspaces.js.map +1 -0
- package/dist/billing/index.d.ts +9 -0
- package/dist/billing/index.d.ts.map +1 -0
- package/dist/billing/index.js +9 -0
- package/dist/billing/index.js.map +1 -0
- package/dist/billing/plans.d.ts +39 -0
- package/dist/billing/plans.d.ts.map +1 -0
- package/dist/billing/plans.js +245 -0
- package/dist/billing/plans.js.map +1 -0
- package/dist/billing/service.d.ts +80 -0
- package/dist/billing/service.d.ts.map +1 -0
- package/dist/billing/service.js +388 -0
- package/dist/billing/service.js.map +1 -0
- package/dist/billing/types.d.ts +141 -0
- package/dist/billing/types.d.ts.map +1 -0
- package/dist/billing/types.js +7 -0
- package/dist/billing/types.js.map +1 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -0
- package/dist/db/bulk-ingest.d.ts +89 -0
- package/dist/db/bulk-ingest.d.ts.map +1 -0
- package/dist/db/bulk-ingest.js +268 -0
- package/dist/db/bulk-ingest.js.map +1 -0
- package/dist/db/drizzle.d.ts +256 -0
- package/dist/db/drizzle.d.ts.map +1 -0
- package/dist/db/drizzle.js +1286 -0
- package/dist/db/drizzle.js.map +1 -0
- package/dist/db/index.d.ts +55 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +68 -0
- package/dist/db/index.js.map +1 -0
- package/dist/db/schema.d.ts +4873 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +620 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +38 -0
- package/dist/index.js.map +1 -0
- package/dist/provisioner/index.d.ts +207 -0
- package/dist/provisioner/index.d.ts.map +1 -0
- package/dist/provisioner/index.js +2114 -0
- package/dist/provisioner/index.js.map +1 -0
- package/dist/server.d.ts +17 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +1924 -0
- package/dist/server.js.map +1 -0
- package/dist/services/auto-scaler.d.ts +152 -0
- package/dist/services/auto-scaler.d.ts.map +1 -0
- package/dist/services/auto-scaler.js +439 -0
- package/dist/services/auto-scaler.js.map +1 -0
- package/dist/services/capacity-manager.d.ts +148 -0
- package/dist/services/capacity-manager.d.ts.map +1 -0
- package/dist/services/capacity-manager.js +449 -0
- package/dist/services/capacity-manager.js.map +1 -0
- package/dist/services/ci-agent-spawner.d.ts +49 -0
- package/dist/services/ci-agent-spawner.d.ts.map +1 -0
- package/dist/services/ci-agent-spawner.js +373 -0
- package/dist/services/ci-agent-spawner.js.map +1 -0
- package/dist/services/cloud-message-bus.d.ts +28 -0
- package/dist/services/cloud-message-bus.d.ts.map +1 -0
- package/dist/services/cloud-message-bus.js +19 -0
- package/dist/services/cloud-message-bus.js.map +1 -0
- package/dist/services/compute-enforcement.d.ts +57 -0
- package/dist/services/compute-enforcement.d.ts.map +1 -0
- package/dist/services/compute-enforcement.js +175 -0
- package/dist/services/compute-enforcement.js.map +1 -0
- package/dist/services/coordinator.d.ts +62 -0
- package/dist/services/coordinator.d.ts.map +1 -0
- package/dist/services/coordinator.js +389 -0
- package/dist/services/coordinator.js.map +1 -0
- package/dist/services/index.d.ts +17 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +25 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/intro-expiration.d.ts +60 -0
- package/dist/services/intro-expiration.d.ts.map +1 -0
- package/dist/services/intro-expiration.js +252 -0
- package/dist/services/intro-expiration.js.map +1 -0
- package/dist/services/mention-handler.d.ts +65 -0
- package/dist/services/mention-handler.d.ts.map +1 -0
- package/dist/services/mention-handler.js +405 -0
- package/dist/services/mention-handler.js.map +1 -0
- package/dist/services/nango.d.ts +201 -0
- package/dist/services/nango.d.ts.map +1 -0
- package/dist/services/nango.js +392 -0
- package/dist/services/nango.js.map +1 -0
- package/dist/services/persistence.d.ts +131 -0
- package/dist/services/persistence.d.ts.map +1 -0
- package/dist/services/persistence.js +200 -0
- package/dist/services/persistence.js.map +1 -0
- package/dist/services/planLimits.d.ts +147 -0
- package/dist/services/planLimits.d.ts.map +1 -0
- package/dist/services/planLimits.js +335 -0
- package/dist/services/planLimits.js.map +1 -0
- package/dist/services/presence-registry.d.ts +56 -0
- package/dist/services/presence-registry.d.ts.map +1 -0
- package/dist/services/presence-registry.js +91 -0
- package/dist/services/presence-registry.js.map +1 -0
- package/dist/services/scaling-orchestrator.d.ts +159 -0
- package/dist/services/scaling-orchestrator.d.ts.map +1 -0
- package/dist/services/scaling-orchestrator.js +502 -0
- package/dist/services/scaling-orchestrator.js.map +1 -0
- package/dist/services/scaling-policy.d.ts +121 -0
- package/dist/services/scaling-policy.d.ts.map +1 -0
- package/dist/services/scaling-policy.js +415 -0
- package/dist/services/scaling-policy.js.map +1 -0
- package/dist/services/ssh-security.d.ts +31 -0
- package/dist/services/ssh-security.d.ts.map +1 -0
- package/dist/services/ssh-security.js +63 -0
- package/dist/services/ssh-security.js.map +1 -0
- package/dist/services/workspace-keepalive.d.ts +76 -0
- package/dist/services/workspace-keepalive.d.ts.map +1 -0
- package/dist/services/workspace-keepalive.js +234 -0
- package/dist/services/workspace-keepalive.js.map +1 -0
- package/dist/shims/consensus.d.ts +23 -0
- package/dist/shims/consensus.d.ts.map +1 -0
- package/dist/shims/consensus.js +5 -0
- package/dist/shims/consensus.js.map +1 -0
- package/dist/webhooks/index.d.ts +24 -0
- package/dist/webhooks/index.d.ts.map +1 -0
- package/dist/webhooks/index.js +29 -0
- package/dist/webhooks/index.js.map +1 -0
- package/dist/webhooks/parsers/github.d.ts +8 -0
- package/dist/webhooks/parsers/github.d.ts.map +1 -0
- package/dist/webhooks/parsers/github.js +234 -0
- package/dist/webhooks/parsers/github.js.map +1 -0
- package/dist/webhooks/parsers/index.d.ts +23 -0
- package/dist/webhooks/parsers/index.d.ts.map +1 -0
- package/dist/webhooks/parsers/index.js +30 -0
- package/dist/webhooks/parsers/index.js.map +1 -0
- package/dist/webhooks/parsers/linear.d.ts +9 -0
- package/dist/webhooks/parsers/linear.d.ts.map +1 -0
- package/dist/webhooks/parsers/linear.js +258 -0
- package/dist/webhooks/parsers/linear.js.map +1 -0
- package/dist/webhooks/parsers/slack.d.ts +9 -0
- package/dist/webhooks/parsers/slack.d.ts.map +1 -0
- package/dist/webhooks/parsers/slack.js +214 -0
- package/dist/webhooks/parsers/slack.js.map +1 -0
- package/dist/webhooks/responders/github.d.ts +8 -0
- package/dist/webhooks/responders/github.d.ts.map +1 -0
- package/dist/webhooks/responders/github.js +73 -0
- package/dist/webhooks/responders/github.js.map +1 -0
- package/dist/webhooks/responders/index.d.ts +23 -0
- package/dist/webhooks/responders/index.d.ts.map +1 -0
- package/dist/webhooks/responders/index.js +30 -0
- package/dist/webhooks/responders/index.js.map +1 -0
- package/dist/webhooks/responders/linear.d.ts +9 -0
- package/dist/webhooks/responders/linear.d.ts.map +1 -0
- package/dist/webhooks/responders/linear.js +149 -0
- package/dist/webhooks/responders/linear.js.map +1 -0
- package/dist/webhooks/responders/slack.d.ts +20 -0
- package/dist/webhooks/responders/slack.d.ts.map +1 -0
- package/dist/webhooks/responders/slack.js +178 -0
- package/dist/webhooks/responders/slack.js.map +1 -0
- package/dist/webhooks/router.d.ts +25 -0
- package/dist/webhooks/router.d.ts.map +1 -0
- package/dist/webhooks/router.js +504 -0
- package/dist/webhooks/router.js.map +1 -0
- package/dist/webhooks/rules-engine.d.ts +24 -0
- package/dist/webhooks/rules-engine.d.ts.map +1 -0
- package/dist/webhooks/rules-engine.js +287 -0
- package/dist/webhooks/rules-engine.js.map +1 -0
- package/dist/webhooks/types.d.ts +186 -0
- package/dist/webhooks/types.d.ts.map +1 -0
- package/dist/webhooks/types.js +8 -0
- package/dist/webhooks/types.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scaling Policy Service
|
|
3
|
+
*
|
|
4
|
+
* Defines rules and policies for auto-scaling workspaces based on:
|
|
5
|
+
* - Memory pressure
|
|
6
|
+
* - Agent count
|
|
7
|
+
* - CPU usage
|
|
8
|
+
* - Trend analysis
|
|
9
|
+
*
|
|
10
|
+
* Policies are configurable per user/plan tier.
|
|
11
|
+
*/
|
|
12
|
+
import { EventEmitter } from 'events';
|
|
13
|
+
// Default thresholds by plan
|
|
14
|
+
const DEFAULT_THRESHOLDS = {
|
|
15
|
+
free: {
|
|
16
|
+
memoryWarningBytes: 256 * 1024 * 1024, // 256MB
|
|
17
|
+
memoryCriticalBytes: 512 * 1024 * 1024, // 512MB
|
|
18
|
+
memoryScaleUpBytes: 400 * 1024 * 1024, // 400MB (no auto-scale for free)
|
|
19
|
+
memoryGrowthRateWarning: 5 * 1024 * 1024, // 5MB/min
|
|
20
|
+
memoryGrowthRateScaleUp: 10 * 1024 * 1024, // 10MB/min
|
|
21
|
+
agentsPerWorkspaceWarning: 3,
|
|
22
|
+
agentsPerWorkspaceMax: 5,
|
|
23
|
+
cpuWarningPercent: 70,
|
|
24
|
+
cpuScaleUpPercent: 85,
|
|
25
|
+
evaluationWindowMs: 5 * 60 * 1000, // 5 minutes
|
|
26
|
+
cooldownMs: 30 * 60 * 1000, // 30 minutes (free tier)
|
|
27
|
+
},
|
|
28
|
+
pro: {
|
|
29
|
+
memoryWarningBytes: 512 * 1024 * 1024, // 512MB
|
|
30
|
+
memoryCriticalBytes: 1024 * 1024 * 1024, // 1GB
|
|
31
|
+
memoryScaleUpBytes: 768 * 1024 * 1024, // 768MB
|
|
32
|
+
memoryGrowthRateWarning: 10 * 1024 * 1024, // 10MB/min
|
|
33
|
+
memoryGrowthRateScaleUp: 20 * 1024 * 1024, // 20MB/min
|
|
34
|
+
agentsPerWorkspaceWarning: 8,
|
|
35
|
+
agentsPerWorkspaceMax: 15,
|
|
36
|
+
cpuWarningPercent: 75,
|
|
37
|
+
cpuScaleUpPercent: 90,
|
|
38
|
+
evaluationWindowMs: 3 * 60 * 1000, // 3 minutes
|
|
39
|
+
cooldownMs: 10 * 60 * 1000, // 10 minutes
|
|
40
|
+
},
|
|
41
|
+
team: {
|
|
42
|
+
memoryWarningBytes: 768 * 1024 * 1024, // 768MB
|
|
43
|
+
memoryCriticalBytes: 1.5 * 1024 * 1024 * 1024, // 1.5GB
|
|
44
|
+
memoryScaleUpBytes: 1024 * 1024 * 1024, // 1GB
|
|
45
|
+
memoryGrowthRateWarning: 15 * 1024 * 1024, // 15MB/min
|
|
46
|
+
memoryGrowthRateScaleUp: 30 * 1024 * 1024, // 30MB/min
|
|
47
|
+
agentsPerWorkspaceWarning: 15,
|
|
48
|
+
agentsPerWorkspaceMax: 25,
|
|
49
|
+
cpuWarningPercent: 80,
|
|
50
|
+
cpuScaleUpPercent: 92,
|
|
51
|
+
evaluationWindowMs: 2 * 60 * 1000, // 2 minutes
|
|
52
|
+
cooldownMs: 5 * 60 * 1000, // 5 minutes
|
|
53
|
+
},
|
|
54
|
+
enterprise: {
|
|
55
|
+
memoryWarningBytes: 1024 * 1024 * 1024, // 1GB
|
|
56
|
+
memoryCriticalBytes: 2 * 1024 * 1024 * 1024, // 2GB
|
|
57
|
+
memoryScaleUpBytes: 1.5 * 1024 * 1024 * 1024, // 1.5GB
|
|
58
|
+
memoryGrowthRateWarning: 20 * 1024 * 1024, // 20MB/min
|
|
59
|
+
memoryGrowthRateScaleUp: 50 * 1024 * 1024, // 50MB/min
|
|
60
|
+
agentsPerWorkspaceWarning: 25,
|
|
61
|
+
agentsPerWorkspaceMax: 50,
|
|
62
|
+
cpuWarningPercent: 85,
|
|
63
|
+
cpuScaleUpPercent: 95,
|
|
64
|
+
evaluationWindowMs: 1 * 60 * 1000, // 1 minute
|
|
65
|
+
cooldownMs: 2 * 60 * 1000, // 2 minutes
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
// Default policies - ordered by priority (higher = evaluated first)
|
|
69
|
+
// In-workspace scaling is preferred over adding new workspaces (more efficient)
|
|
70
|
+
const DEFAULT_POLICIES = [
|
|
71
|
+
// === In-Workspace Scaling (Higher Priority) ===
|
|
72
|
+
{
|
|
73
|
+
id: 'agent-limit-increase',
|
|
74
|
+
name: 'Increase Agent Limit',
|
|
75
|
+
description: 'Increase max agents when approaching limit within single workspace',
|
|
76
|
+
enabled: true,
|
|
77
|
+
priority: 150, // Higher priority - try this before adding workspaces
|
|
78
|
+
conditions: [
|
|
79
|
+
{ metric: 'agent_count', operator: 'gte', value: 0.85 }, // 85% of max agents
|
|
80
|
+
{ metric: 'workspace_count', operator: 'eq', value: 1 }, // Only 1 workspace
|
|
81
|
+
],
|
|
82
|
+
action: { type: 'increase_agent_limit', percentage: 50 }, // Increase limit by 50%
|
|
83
|
+
maxInstances: 10,
|
|
84
|
+
minInstances: 1,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
id: 'workspace-resize-up',
|
|
88
|
+
name: 'Resize Workspace Up',
|
|
89
|
+
description: 'Vertically scale workspace when memory is high',
|
|
90
|
+
enabled: true,
|
|
91
|
+
priority: 140, // Higher priority than horizontal scaling
|
|
92
|
+
conditions: [
|
|
93
|
+
{ metric: 'memory_usage', operator: 'gte', value: 0.75, duration: 120000 }, // 75% for 2min
|
|
94
|
+
{ metric: 'workspace_count', operator: 'eq', value: 1 }, // Only 1 workspace
|
|
95
|
+
],
|
|
96
|
+
action: { type: 'resize_up', percentage: 100 }, // Double resources
|
|
97
|
+
maxInstances: 10,
|
|
98
|
+
minInstances: 1,
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
id: 'cpu-pressure-resize',
|
|
102
|
+
name: 'CPU Pressure Resize',
|
|
103
|
+
description: 'Resize workspace when CPU is consistently high',
|
|
104
|
+
enabled: true,
|
|
105
|
+
priority: 135,
|
|
106
|
+
conditions: [
|
|
107
|
+
{ metric: 'cpu_usage', operator: 'gte', value: 0.85, duration: 180000 }, // 85% for 3min
|
|
108
|
+
],
|
|
109
|
+
action: { type: 'resize_up', percentage: 50 }, // 50% more resources
|
|
110
|
+
maxInstances: 10,
|
|
111
|
+
minInstances: 1,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 'workspace-resize-down',
|
|
115
|
+
name: 'Resize Workspace Down',
|
|
116
|
+
description: 'Reduce workspace resources when underutilized',
|
|
117
|
+
enabled: true,
|
|
118
|
+
priority: 45, // Lower priority
|
|
119
|
+
conditions: [
|
|
120
|
+
{ metric: 'memory_usage', operator: 'lt', value: 0.15, duration: 900000 }, // Under 15% for 15min
|
|
121
|
+
{ metric: 'cpu_usage', operator: 'lt', value: 0.1, duration: 900000 }, // Under 10% CPU
|
|
122
|
+
],
|
|
123
|
+
action: { type: 'resize_down', percentage: 50 }, // Halve resources
|
|
124
|
+
maxInstances: 10,
|
|
125
|
+
minInstances: 1,
|
|
126
|
+
},
|
|
127
|
+
// === Horizontal Scaling (Add/Remove Workspaces) ===
|
|
128
|
+
{
|
|
129
|
+
id: 'memory-pressure-scale-up',
|
|
130
|
+
name: 'Memory Pressure Scale Up',
|
|
131
|
+
description: 'Add workspace when memory exceeds threshold across all workspaces',
|
|
132
|
+
enabled: true,
|
|
133
|
+
priority: 100,
|
|
134
|
+
conditions: [
|
|
135
|
+
{ metric: 'memory_usage', operator: 'gte', value: 0.8, duration: 60000 }, // 80% for 1min
|
|
136
|
+
],
|
|
137
|
+
action: { type: 'scale_up', targetCount: 1 },
|
|
138
|
+
maxInstances: 10,
|
|
139
|
+
minInstances: 1,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
id: 'memory-trend-scale-up',
|
|
143
|
+
name: 'Memory Trend Scale Up',
|
|
144
|
+
description: 'Add workspace when memory growth rate is high',
|
|
145
|
+
enabled: true,
|
|
146
|
+
priority: 90,
|
|
147
|
+
conditions: [
|
|
148
|
+
{ metric: 'memory_trend', operator: 'gte', value: 1.0, duration: 180000 }, // At threshold for 3min
|
|
149
|
+
],
|
|
150
|
+
action: { type: 'scale_up', targetCount: 1 },
|
|
151
|
+
maxInstances: 10,
|
|
152
|
+
minInstances: 1,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
id: 'agent-count-scale-up',
|
|
156
|
+
name: 'Agent Count Scale Up',
|
|
157
|
+
description: 'Add workspace when agent count is high across all workspaces',
|
|
158
|
+
enabled: true,
|
|
159
|
+
priority: 80,
|
|
160
|
+
conditions: [
|
|
161
|
+
{ metric: 'agent_count', operator: 'gte', value: 0.9 }, // 90% of max agents
|
|
162
|
+
{ metric: 'workspace_count', operator: 'gte', value: 1 }, // Already tried in-workspace scaling
|
|
163
|
+
],
|
|
164
|
+
action: { type: 'scale_up', targetCount: 1 },
|
|
165
|
+
maxInstances: 10,
|
|
166
|
+
minInstances: 1,
|
|
167
|
+
},
|
|
168
|
+
// === Rebalancing ===
|
|
169
|
+
{
|
|
170
|
+
id: 'agent-rebalance',
|
|
171
|
+
name: 'Agent Rebalance',
|
|
172
|
+
description: 'Redistribute agents when load is uneven across workspaces',
|
|
173
|
+
enabled: true,
|
|
174
|
+
priority: 60,
|
|
175
|
+
conditions: [
|
|
176
|
+
{ metric: 'workspace_count', operator: 'gte', value: 2 }, // Multiple workspaces
|
|
177
|
+
],
|
|
178
|
+
action: { type: 'rebalance' },
|
|
179
|
+
maxInstances: 10,
|
|
180
|
+
minInstances: 1,
|
|
181
|
+
},
|
|
182
|
+
// === Scale Down ===
|
|
183
|
+
{
|
|
184
|
+
id: 'low-usage-scale-down',
|
|
185
|
+
name: 'Low Usage Scale Down',
|
|
186
|
+
description: 'Remove workspace when usage is low',
|
|
187
|
+
enabled: true,
|
|
188
|
+
priority: 50,
|
|
189
|
+
conditions: [
|
|
190
|
+
{ metric: 'memory_usage', operator: 'lt', value: 0.2, duration: 600000 }, // Under 20% for 10min
|
|
191
|
+
{ metric: 'workspace_count', operator: 'gt', value: 1 }, // More than 1 workspace
|
|
192
|
+
],
|
|
193
|
+
action: { type: 'scale_down', targetCount: 1 },
|
|
194
|
+
maxInstances: 10,
|
|
195
|
+
minInstances: 1,
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
export class ScalingPolicyService extends EventEmitter {
|
|
199
|
+
thresholds = new Map();
|
|
200
|
+
policies = new Map();
|
|
201
|
+
conditionHistory = new Map();
|
|
202
|
+
constructor() {
|
|
203
|
+
super();
|
|
204
|
+
// Initialize with defaults
|
|
205
|
+
for (const [plan, thresholds] of Object.entries(DEFAULT_THRESHOLDS)) {
|
|
206
|
+
this.thresholds.set(plan, thresholds);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Get thresholds for a plan tier
|
|
211
|
+
*/
|
|
212
|
+
getThresholds(plan) {
|
|
213
|
+
return this.thresholds.get(plan) || this.thresholds.get('free');
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Set custom thresholds for a plan
|
|
217
|
+
*/
|
|
218
|
+
setThresholds(plan, thresholds) {
|
|
219
|
+
const current = this.getThresholds(plan);
|
|
220
|
+
this.thresholds.set(plan, { ...current, ...thresholds });
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get policies for a user (default + custom)
|
|
224
|
+
*/
|
|
225
|
+
getPolicies(userId) {
|
|
226
|
+
const userPolicies = this.policies.get(userId) || [];
|
|
227
|
+
return [...DEFAULT_POLICIES, ...userPolicies].sort((a, b) => b.priority - a.priority);
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Add custom policy for a user
|
|
231
|
+
*/
|
|
232
|
+
addPolicy(userId, policy) {
|
|
233
|
+
const existing = this.policies.get(userId) || [];
|
|
234
|
+
existing.push(policy);
|
|
235
|
+
this.policies.set(userId, existing);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Evaluate scaling decision based on current context
|
|
239
|
+
*/
|
|
240
|
+
evaluate(context) {
|
|
241
|
+
const thresholds = this.getThresholds(context.plan);
|
|
242
|
+
const policies = this.getPolicies(context.userId);
|
|
243
|
+
// Calculate aggregate metrics
|
|
244
|
+
const metrics = this.calculateAggregateMetrics(context, thresholds);
|
|
245
|
+
// Check cooldown
|
|
246
|
+
if (context.lastScalingAction) {
|
|
247
|
+
const timeSinceLastScale = Date.now() - context.lastScalingAction.getTime();
|
|
248
|
+
if (timeSinceLastScale < thresholds.cooldownMs) {
|
|
249
|
+
return {
|
|
250
|
+
shouldScale: false,
|
|
251
|
+
action: null,
|
|
252
|
+
reason: `Cooldown active (${Math.round((thresholds.cooldownMs - timeSinceLastScale) / 1000)}s remaining)`,
|
|
253
|
+
triggeredPolicy: null,
|
|
254
|
+
metrics,
|
|
255
|
+
timestamp: new Date(),
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Evaluate policies in priority order
|
|
260
|
+
for (const policy of policies) {
|
|
261
|
+
if (!policy.enabled)
|
|
262
|
+
continue;
|
|
263
|
+
const conditionsMet = this.evaluateConditions(policy.conditions, metrics, thresholds, context.userId);
|
|
264
|
+
if (conditionsMet) {
|
|
265
|
+
// Check instance limits for horizontal scaling only
|
|
266
|
+
if (policy.action.type === 'scale_up') {
|
|
267
|
+
// Block if at workspace limit (for adding new workspaces)
|
|
268
|
+
if (context.currentWorkspaceCount >= context.maxWorkspaces) {
|
|
269
|
+
continue; // Try next policy (could be in-workspace scaling)
|
|
270
|
+
}
|
|
271
|
+
if (context.currentWorkspaceCount >= policy.maxInstances) {
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
if (policy.action.type === 'scale_down' && context.currentWorkspaceCount <= policy.minInstances) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
this.emit('scaling_decision', {
|
|
279
|
+
userId: context.userId,
|
|
280
|
+
policy: policy.id,
|
|
281
|
+
action: policy.action,
|
|
282
|
+
metrics,
|
|
283
|
+
});
|
|
284
|
+
return {
|
|
285
|
+
shouldScale: true,
|
|
286
|
+
action: policy.action,
|
|
287
|
+
reason: policy.description,
|
|
288
|
+
triggeredPolicy: policy.id,
|
|
289
|
+
metrics,
|
|
290
|
+
timestamp: new Date(),
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
shouldScale: false,
|
|
296
|
+
action: null,
|
|
297
|
+
reason: 'No scaling conditions met',
|
|
298
|
+
triggeredPolicy: null,
|
|
299
|
+
metrics,
|
|
300
|
+
timestamp: new Date(),
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Calculate aggregate metrics from workspace metrics
|
|
305
|
+
*/
|
|
306
|
+
calculateAggregateMetrics(context, thresholds) {
|
|
307
|
+
const workspaces = context.workspaceMetrics;
|
|
308
|
+
if (workspaces.length === 0) {
|
|
309
|
+
return {
|
|
310
|
+
memory_usage: 0,
|
|
311
|
+
memory_trend: 0,
|
|
312
|
+
agent_count: 0,
|
|
313
|
+
cpu_usage: 0,
|
|
314
|
+
workspace_count: 0,
|
|
315
|
+
total_memory_bytes: 0,
|
|
316
|
+
total_agents: 0,
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
const totalMemory = workspaces.reduce((sum, w) => sum + w.totalMemoryBytes, 0);
|
|
320
|
+
const avgTrend = workspaces.reduce((sum, w) => sum + w.memoryTrendPerMinute, 0) / workspaces.length;
|
|
321
|
+
const totalAgents = workspaces.reduce((sum, w) => sum + w.agentCount, 0);
|
|
322
|
+
const avgCpu = workspaces.reduce((sum, w) => sum + w.cpuPercent, 0) / workspaces.length;
|
|
323
|
+
// Normalized metrics (0-1 scale relative to thresholds)
|
|
324
|
+
return {
|
|
325
|
+
memory_usage: totalMemory / (thresholds.memoryScaleUpBytes * workspaces.length),
|
|
326
|
+
memory_trend: avgTrend / thresholds.memoryGrowthRateScaleUp,
|
|
327
|
+
agent_count: totalAgents / (thresholds.agentsPerWorkspaceMax * workspaces.length),
|
|
328
|
+
cpu_usage: avgCpu / thresholds.cpuScaleUpPercent,
|
|
329
|
+
workspace_count: context.currentWorkspaceCount,
|
|
330
|
+
total_memory_bytes: totalMemory,
|
|
331
|
+
total_agents: totalAgents,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Evaluate conditions with duration support
|
|
336
|
+
*/
|
|
337
|
+
evaluateConditions(conditions, metrics, thresholds, userId) {
|
|
338
|
+
for (const condition of conditions) {
|
|
339
|
+
const metricValue = metrics[condition.metric];
|
|
340
|
+
if (metricValue === undefined)
|
|
341
|
+
continue;
|
|
342
|
+
const conditionMet = this.compareValues(metricValue, condition.operator, condition.value);
|
|
343
|
+
if (condition.duration) {
|
|
344
|
+
// Track condition history for duration-based evaluation
|
|
345
|
+
const historyKey = `${userId}:${condition.metric}`;
|
|
346
|
+
const history = this.conditionHistory.get(historyKey) || [];
|
|
347
|
+
// Add current value
|
|
348
|
+
history.push({ timestamp: new Date(), value: metricValue });
|
|
349
|
+
// Clean old entries
|
|
350
|
+
const cutoff = Date.now() - condition.duration;
|
|
351
|
+
const recentHistory = history.filter((h) => h.timestamp.getTime() > cutoff);
|
|
352
|
+
this.conditionHistory.set(historyKey, recentHistory);
|
|
353
|
+
// Check if condition has been met for the full duration
|
|
354
|
+
if (recentHistory.length === 0)
|
|
355
|
+
return false;
|
|
356
|
+
const allMet = recentHistory.every((h) => this.compareValues(h.value, condition.operator, condition.value));
|
|
357
|
+
// Also check if we have enough history
|
|
358
|
+
const oldestEntry = recentHistory[0].timestamp.getTime();
|
|
359
|
+
const hasEnoughHistory = Date.now() - oldestEntry >= condition.duration * 0.8; // 80% of duration
|
|
360
|
+
if (!allMet || !hasEnoughHistory)
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
if (!conditionMet)
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return true;
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Compare values based on operator
|
|
372
|
+
*/
|
|
373
|
+
compareValues(actual, operator, target) {
|
|
374
|
+
switch (operator) {
|
|
375
|
+
case 'gt':
|
|
376
|
+
return actual > target;
|
|
377
|
+
case 'gte':
|
|
378
|
+
return actual >= target;
|
|
379
|
+
case 'lt':
|
|
380
|
+
return actual < target;
|
|
381
|
+
case 'lte':
|
|
382
|
+
return actual <= target;
|
|
383
|
+
case 'eq':
|
|
384
|
+
return actual === target;
|
|
385
|
+
default:
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Get max workspaces for a plan
|
|
391
|
+
*/
|
|
392
|
+
getMaxWorkspaces(plan) {
|
|
393
|
+
switch (plan) {
|
|
394
|
+
case 'free':
|
|
395
|
+
return 1;
|
|
396
|
+
case 'pro':
|
|
397
|
+
return 3;
|
|
398
|
+
case 'team':
|
|
399
|
+
return 10;
|
|
400
|
+
case 'enterprise':
|
|
401
|
+
return 50;
|
|
402
|
+
default:
|
|
403
|
+
return 1;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
// Singleton instance
|
|
408
|
+
let _scalingPolicyService = null;
|
|
409
|
+
export function getScalingPolicyService() {
|
|
410
|
+
if (!_scalingPolicyService) {
|
|
411
|
+
_scalingPolicyService = new ScalingPolicyService();
|
|
412
|
+
}
|
|
413
|
+
return _scalingPolicyService;
|
|
414
|
+
}
|
|
415
|
+
//# sourceMappingURL=scaling-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaling-policy.js","sourceRoot":"","sources":["../../src/services/scaling-policy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSH Security Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides secure SSH password derivation for workspace containers.
|
|
5
|
+
* Uses a deterministic approach based on workspace ID + secret salt,
|
|
6
|
+
* ensuring each workspace has a unique password without storage.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Derive a unique SSH password for a workspace.
|
|
10
|
+
*
|
|
11
|
+
* Uses SHA-256 hash of (workspaceId + salt) to generate a deterministic
|
|
12
|
+
* but unique password for each workspace. This approach:
|
|
13
|
+
* - Ensures each workspace has a unique password
|
|
14
|
+
* - Requires no database storage
|
|
15
|
+
* - Produces consistent results across cloud server and container
|
|
16
|
+
*
|
|
17
|
+
* SECURITY: Set SSH_PASSWORD_SALT environment variable in production!
|
|
18
|
+
* The default salt is insecure and should never be used in production.
|
|
19
|
+
*
|
|
20
|
+
* @param workspaceId - The workspace UUID
|
|
21
|
+
* @returns A 24-character hex password (96 bits of entropy)
|
|
22
|
+
*/
|
|
23
|
+
export declare function deriveSshPassword(workspaceId: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* Validate that SSH security is properly configured.
|
|
26
|
+
* Call this at server startup to catch configuration issues early.
|
|
27
|
+
*
|
|
28
|
+
* @returns true if properly configured, false otherwise
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateSshSecurityConfig(): boolean;
|
|
31
|
+
//# sourceMappingURL=ssh-security.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-security.d.ts","sourceRoot":"","sources":["../../src/services/ssh-security.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAsB7D;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,IAAI,OAAO,CAoBnD"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSH Security Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides secure SSH password derivation for workspace containers.
|
|
5
|
+
* Uses a deterministic approach based on workspace ID + secret salt,
|
|
6
|
+
* ensuring each workspace has a unique password without storage.
|
|
7
|
+
*/
|
|
8
|
+
import * as crypto from 'crypto';
|
|
9
|
+
const DEFAULT_SALT = 'default-salt-change-in-prod';
|
|
10
|
+
/**
|
|
11
|
+
* Derive a unique SSH password for a workspace.
|
|
12
|
+
*
|
|
13
|
+
* Uses SHA-256 hash of (workspaceId + salt) to generate a deterministic
|
|
14
|
+
* but unique password for each workspace. This approach:
|
|
15
|
+
* - Ensures each workspace has a unique password
|
|
16
|
+
* - Requires no database storage
|
|
17
|
+
* - Produces consistent results across cloud server and container
|
|
18
|
+
*
|
|
19
|
+
* SECURITY: Set SSH_PASSWORD_SALT environment variable in production!
|
|
20
|
+
* The default salt is insecure and should never be used in production.
|
|
21
|
+
*
|
|
22
|
+
* @param workspaceId - The workspace UUID
|
|
23
|
+
* @returns A 24-character hex password (96 bits of entropy)
|
|
24
|
+
*/
|
|
25
|
+
export function deriveSshPassword(workspaceId) {
|
|
26
|
+
const salt = process.env.SSH_PASSWORD_SALT;
|
|
27
|
+
// Warn if using default salt in production
|
|
28
|
+
if (!salt) {
|
|
29
|
+
const isProduction = process.env.NODE_ENV === 'production' || process.env.FLY_APP_NAME;
|
|
30
|
+
if (isProduction) {
|
|
31
|
+
console.warn('[SECURITY WARNING] SSH_PASSWORD_SALT is not set! ' +
|
|
32
|
+
'Using default salt is INSECURE in production. ' +
|
|
33
|
+
'Set SSH_PASSWORD_SALT to a random 32+ character secret.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const effectiveSalt = salt || DEFAULT_SALT;
|
|
37
|
+
return crypto
|
|
38
|
+
.createHash('sha256')
|
|
39
|
+
.update(`${workspaceId}:${effectiveSalt}`)
|
|
40
|
+
.digest('hex')
|
|
41
|
+
.substring(0, 24); // 24 hex chars = 96 bits of entropy
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Validate that SSH security is properly configured.
|
|
45
|
+
* Call this at server startup to catch configuration issues early.
|
|
46
|
+
*
|
|
47
|
+
* @returns true if properly configured, false otherwise
|
|
48
|
+
*/
|
|
49
|
+
export function validateSshSecurityConfig() {
|
|
50
|
+
const salt = process.env.SSH_PASSWORD_SALT;
|
|
51
|
+
const isProduction = process.env.NODE_ENV === 'production' || process.env.FLY_APP_NAME;
|
|
52
|
+
if (isProduction && !salt) {
|
|
53
|
+
console.error('[SECURITY ERROR] SSH_PASSWORD_SALT must be set in production! ' +
|
|
54
|
+
'Generate one with: openssl rand -hex 32');
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
if (salt && salt.length < 16) {
|
|
58
|
+
console.warn('[SECURITY WARNING] SSH_PASSWORD_SALT should be at least 16 characters. ' +
|
|
59
|
+
'Current length: ' + salt.length);
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=ssh-security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-security.js","sourceRoot":"","sources":["../../src/services/ssh-security.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAEjC,MAAM,YAAY,GAAG,6BAA6B,CAAC;AAEnD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,iBAAiB,CAAC,WAAmB;IACnD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAE3C,2CAA2C;IAC3C,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QACvF,IAAI,YAAY,EAAE,CAAC;YACjB,OAAO,CAAC,IAAI,CACV,mDAAmD;gBACnD,gDAAgD;gBAChD,yDAAyD,CAC1D,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,aAAa,GAAG,IAAI,IAAI,YAAY,CAAC;IAE3C,OAAO,MAAM;SACV,UAAU,CAAC,QAAQ,CAAC;SACpB,MAAM,CAAC,GAAG,WAAW,IAAI,aAAa,EAAE,CAAC;SACzC,MAAM,CAAC,KAAK,CAAC;SACb,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,oCAAoC;AAC3D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB;IACvC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAC3C,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAEvF,IAAI,YAAY,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CACX,gEAAgE;YAChE,yCAAyC,CAC1C,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAC7B,OAAO,CAAC,IAAI,CACV,yEAAyE;YACzE,kBAAkB,GAAG,IAAI,CAAC,MAAM,CACjC,CAAC;IACJ,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace Keepalive Service
|
|
3
|
+
*
|
|
4
|
+
* Prevents Fly.io from idling workspace machines that have active agents running.
|
|
5
|
+
*
|
|
6
|
+
* Problem: Fly.io uses request-based concurrency tracking to determine when to
|
|
7
|
+
* idle a machine. If a Claude agent is running but no HTTP requests are coming
|
|
8
|
+
* in (e.g., no one has the dashboard open), Fly.io may idle the machine.
|
|
9
|
+
*
|
|
10
|
+
* Solution: The cloud server periodically pings workspace machines that have
|
|
11
|
+
* active agents. This inbound HTTP request counts as activity for Fly.io's
|
|
12
|
+
* idle detection, keeping the machine awake.
|
|
13
|
+
*
|
|
14
|
+
* Flow:
|
|
15
|
+
* 1. Daemons report their running agents via heartbeat
|
|
16
|
+
* 2. This service queries for workspaces with active agents
|
|
17
|
+
* 3. Pings each workspace's /keep-alive endpoint
|
|
18
|
+
* 4. Workspace stays awake as long as agents are active
|
|
19
|
+
*/
|
|
20
|
+
import { EventEmitter } from 'events';
|
|
21
|
+
export interface WorkspaceKeepaliveConfig {
|
|
22
|
+
/** How often to ping active workspaces (default: 60s) */
|
|
23
|
+
pingIntervalMs: number;
|
|
24
|
+
/** Request timeout for keep-alive pings (default: 5s) */
|
|
25
|
+
requestTimeoutMs: number;
|
|
26
|
+
/** Consider daemon stale if last heartbeat older than this (default: 2 min) */
|
|
27
|
+
staleThresholdMs: number;
|
|
28
|
+
/** Enable verbose logging (default: false) */
|
|
29
|
+
verbose: boolean;
|
|
30
|
+
}
|
|
31
|
+
export interface KeepaliveStats {
|
|
32
|
+
lastRun: Date | null;
|
|
33
|
+
totalPings: number;
|
|
34
|
+
successfulPings: number;
|
|
35
|
+
failedPings: number;
|
|
36
|
+
activeWorkspaces: number;
|
|
37
|
+
}
|
|
38
|
+
export declare class WorkspaceKeepaliveService extends EventEmitter {
|
|
39
|
+
private config;
|
|
40
|
+
private pingTimer;
|
|
41
|
+
private stats;
|
|
42
|
+
constructor(config?: Partial<WorkspaceKeepaliveConfig>);
|
|
43
|
+
/**
|
|
44
|
+
* Start the keepalive service
|
|
45
|
+
*/
|
|
46
|
+
start(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Stop the keepalive service
|
|
49
|
+
*/
|
|
50
|
+
stop(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Get current statistics
|
|
53
|
+
*/
|
|
54
|
+
getStats(): KeepaliveStats;
|
|
55
|
+
/**
|
|
56
|
+
* Find workspaces with active agents and ping them
|
|
57
|
+
*/
|
|
58
|
+
pingActiveWorkspaces(): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Find all workspaces that have daemons with active agents
|
|
61
|
+
*/
|
|
62
|
+
private findWorkspacesWithActiveAgents;
|
|
63
|
+
/**
|
|
64
|
+
* Ping a single workspace's keep-alive endpoint
|
|
65
|
+
*/
|
|
66
|
+
private pingWorkspace;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Get or create the keepalive service singleton
|
|
70
|
+
*/
|
|
71
|
+
export declare function getWorkspaceKeepaliveService(config?: Partial<WorkspaceKeepaliveConfig>): WorkspaceKeepaliveService;
|
|
72
|
+
/**
|
|
73
|
+
* Create a new keepalive service (for testing)
|
|
74
|
+
*/
|
|
75
|
+
export declare function createWorkspaceKeepaliveService(config?: Partial<WorkspaceKeepaliveConfig>): WorkspaceKeepaliveService;
|
|
76
|
+
//# sourceMappingURL=workspace-keepalive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-keepalive.d.ts","sourceRoot":"","sources":["../../src/services/workspace-keepalive.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,MAAM,WAAW,wBAAwB;IACvC,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,gBAAgB,EAAE,MAAM,CAAC;IACzB,+EAA+E;IAC/E,gBAAgB,EAAE,MAAM,CAAC;IACzB,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAiBD,qBAAa,yBAA0B,SAAQ,YAAY;IACzD,OAAO,CAAC,MAAM,CAA2B;IACzC,OAAO,CAAC,SAAS,CAA+C;IAChE,OAAO,CAAC,KAAK,CAMX;gBAEU,MAAM,GAAE,OAAO,CAAC,wBAAwB,CAAM;IAK1D;;OAEG;IACH,KAAK,IAAI,IAAI;IAsBb;;OAEG;IACH,IAAI,IAAI,IAAI;IAQZ;;OAEG;IACH,QAAQ,IAAI,cAAc;IAI1B;;OAEG;IACG,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAsD3C;;OAEG;YACW,8BAA8B;IAgD5C;;OAEG;YACW,aAAa;CA6C5B;AAKD;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,GACzC,yBAAyB,CAK3B;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAC7C,MAAM,CAAC,EAAE,OAAO,CAAC,wBAAwB,CAAC,GACzC,yBAAyB,CAE3B"}
|