web-agent-bridge 3.2.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +84 -72
- package/README.ar.md +1304 -1152
- package/README.md +298 -1635
- package/bin/agent-runner.js +474 -474
- package/bin/cli.js +237 -138
- package/bin/wab-init.js +223 -0
- package/bin/wab.js +80 -80
- package/examples/azure-dns-wab.js +83 -0
- package/examples/bidi-agent.js +119 -119
- package/examples/cloudflare-wab-dns.js +121 -0
- package/examples/cpanel-wab-dns.js +114 -0
- package/examples/cross-site-agent.js +91 -91
- package/examples/dns-discovery-agent.js +166 -0
- package/examples/gcp-dns-wab.js +76 -0
- package/examples/governance-agent.js +169 -0
- package/examples/mcp-agent.js +94 -94
- package/examples/next-app-router/README.md +44 -44
- package/examples/plesk-wab-dns.js +103 -0
- package/examples/puppeteer-agent.js +108 -108
- package/examples/route53-wab-dns.js +144 -0
- package/examples/saas-dashboard/README.md +55 -55
- package/examples/safe-mode-agent.js +96 -0
- package/examples/shopify-hydrogen/README.md +74 -74
- package/examples/vision-agent.js +171 -171
- package/examples/wab-sign.js +74 -0
- package/examples/wab-verify.js +60 -0
- package/examples/wordpress-elementor/README.md +77 -77
- package/package.json +19 -6
- package/public/.well-known/agent-tools.json +180 -180
- package/public/.well-known/ai-assets.json +59 -59
- package/public/.well-known/security.txt +8 -0
- package/public/.well-known/wab.json +28 -0
- package/public/activate.html +368 -0
- package/public/adoption-metrics.html +188 -0
- package/public/agent-workspace.html +349 -349
- package/public/ai.html +198 -198
- package/public/api.html +413 -412
- package/public/azure-dns-integration.html +289 -0
- package/public/browser.html +486 -486
- package/public/cloudflare-integration.html +380 -0
- package/public/commander-dashboard.html +243 -243
- package/public/cookies.html +210 -210
- package/public/cpanel-integration.html +398 -0
- package/public/css/agent-workspace.css +1713 -1713
- package/public/css/premium.css +317 -317
- package/public/css/styles.css +1263 -1235
- package/public/dashboard.html +707 -706
- package/public/dns.html +436 -0
- package/public/docs.html +588 -587
- package/public/feed.xml +89 -89
- package/public/gcp-dns-integration.html +318 -0
- package/public/growth.html +465 -463
- package/public/index.html +1266 -982
- package/public/integrations.html +556 -0
- package/public/js/activate.js +145 -0
- package/public/js/agent-workspace.js +1740 -1740
- package/public/js/auth-nav.js +65 -31
- package/public/js/auth-redirect.js +12 -12
- package/public/js/cookie-consent.js +56 -56
- package/public/js/dns.js +438 -0
- package/public/js/wab-demo-page.js +721 -721
- package/public/js/ws-client.js +74 -74
- package/public/llms-full.txt +360 -360
- package/public/llms.txt +125 -125
- package/public/login.html +85 -85
- package/public/mesh-dashboard.html +328 -328
- package/public/openapi.json +669 -580
- package/public/phone-shield.html +281 -0
- package/public/plesk-integration.html +375 -0
- package/public/premium-dashboard.html +2489 -2489
- package/public/premium.html +793 -793
- package/public/privacy.html +297 -297
- package/public/provider-onboarding.html +172 -0
- package/public/provider-sandbox.html +134 -0
- package/public/providers.html +359 -0
- package/public/register.html +105 -105
- package/public/registrar-integrations.html +141 -0
- package/public/robots.txt +99 -87
- package/public/route53-integration.html +531 -0
- package/public/script/wab-consent.d.ts +36 -36
- package/public/script/wab-consent.js +104 -104
- package/public/script/wab-schema.js +131 -131
- package/public/script/wab.d.ts +108 -108
- package/public/script/wab.min.js +580 -580
- package/public/security.txt +8 -0
- package/public/shieldqr.html +231 -0
- package/public/sitemap.xml +6 -0
- package/public/terms.html +256 -256
- package/public/wab-trust.html +200 -0
- package/public/wab-vs-protocols.html +210 -0
- package/public/whitepaper.html +449 -0
- package/script/ai-agent-bridge.js +1754 -1754
- package/sdk/README.md +99 -99
- package/sdk/agent-mesh.js +449 -449
- package/sdk/auto-discovery.js +288 -0
- package/sdk/commander.js +262 -262
- package/sdk/governance.js +262 -0
- package/sdk/index.d.ts +464 -464
- package/sdk/index.js +25 -1
- package/sdk/multi-agent.js +318 -318
- package/sdk/package.json +2 -2
- package/sdk/safe-mode.js +221 -0
- package/sdk/safety-shield.js +219 -0
- package/sdk/schema-discovery.js +83 -83
- package/server/adapters/index.js +520 -520
- package/server/config/plans.js +367 -367
- package/server/config/secrets.js +102 -102
- package/server/control-plane/index.js +301 -301
- package/server/data-plane/index.js +354 -354
- package/server/index.js +670 -427
- package/server/llm/index.js +404 -404
- package/server/middleware/adminAuth.js +35 -35
- package/server/middleware/auth.js +50 -50
- package/server/middleware/featureGate.js +88 -88
- package/server/middleware/rateLimits.js +100 -100
- package/server/middleware/sensitiveAction.js +157 -0
- package/server/migrations/001_add_analytics_indexes.sql +7 -7
- package/server/migrations/002_premium_features.sql +418 -418
- package/server/migrations/003_ads_integer_cents.sql +33 -33
- package/server/migrations/004_agent_os.sql +158 -158
- package/server/migrations/005_marketplace_metering.sql +126 -126
- package/server/migrations/007_governance.sql +106 -0
- package/server/migrations/008_plans.sql +144 -0
- package/server/migrations/009_shieldqr.sql +30 -0
- package/server/migrations/010_extended_trust.sql +33 -0
- package/server/models/adapters/index.js +33 -33
- package/server/models/adapters/mysql.js +183 -183
- package/server/models/adapters/postgresql.js +172 -172
- package/server/models/adapters/sqlite.js +7 -7
- package/server/models/db.js +740 -681
- package/server/observability/failure-analysis.js +337 -337
- package/server/observability/index.js +394 -394
- package/server/protocol/capabilities.js +223 -223
- package/server/protocol/index.js +243 -243
- package/server/protocol/schema.js +584 -584
- package/server/registry/certification.js +271 -271
- package/server/registry/index.js +326 -326
- package/server/routes/admin-plans.js +76 -0
- package/server/routes/admin-premium.js +673 -671
- package/server/routes/admin-shieldqr.js +90 -0
- package/server/routes/admin-trust-monitor.js +83 -0
- package/server/routes/admin.js +549 -261
- package/server/routes/ads.js +130 -130
- package/server/routes/agent-workspace.js +540 -540
- package/server/routes/api.js +150 -150
- package/server/routes/auth.js +71 -71
- package/server/routes/billing.js +57 -45
- package/server/routes/commander.js +316 -316
- package/server/routes/demo-showcase.js +332 -332
- package/server/routes/demo-store.js +154 -0
- package/server/routes/discovery.js +2348 -417
- package/server/routes/gateway.js +173 -157
- package/server/routes/governance.js +208 -0
- package/server/routes/license.js +251 -240
- package/server/routes/mesh.js +469 -469
- package/server/routes/noscript.js +543 -543
- package/server/routes/plans.js +33 -0
- package/server/routes/premium-v2.js +686 -686
- package/server/routes/premium.js +724 -724
- package/server/routes/providers.js +650 -0
- package/server/routes/runtime.js +2148 -2147
- package/server/routes/shieldqr.js +88 -0
- package/server/routes/sovereign.js +465 -385
- package/server/routes/universal.js +200 -185
- package/server/routes/wab-api.js +850 -501
- package/server/runtime/container-worker.js +111 -111
- package/server/runtime/container.js +448 -448
- package/server/runtime/distributed-worker.js +362 -362
- package/server/runtime/event-bus.js +210 -210
- package/server/runtime/index.js +253 -253
- package/server/runtime/queue.js +599 -599
- package/server/runtime/replay.js +666 -666
- package/server/runtime/sandbox.js +266 -266
- package/server/runtime/scheduler.js +534 -534
- package/server/runtime/session-engine.js +293 -293
- package/server/runtime/state-manager.js +188 -188
- package/server/security/cross-site-redactor.js +196 -0
- package/server/security/dry-run.js +180 -0
- package/server/security/human-gate-rate-limit.js +147 -0
- package/server/security/human-gate-transports.js +178 -0
- package/server/security/human-gate.js +281 -0
- package/server/security/index.js +368 -368
- package/server/security/intent-engine.js +245 -0
- package/server/security/reward-guard.js +171 -0
- package/server/security/rollback-store.js +239 -0
- package/server/security/token-scope.js +404 -0
- package/server/security/url-policy.js +139 -0
- package/server/services/agent-chat.js +506 -506
- package/server/services/agent-learning.js +601 -575
- package/server/services/agent-memory.js +625 -625
- package/server/services/agent-mesh.js +555 -539
- package/server/services/agent-symphony.js +717 -717
- package/server/services/agent-tasks.js +1807 -1807
- package/server/services/api-key-engine.js +292 -261
- package/server/services/cluster.js +894 -894
- package/server/services/commander.js +738 -738
- package/server/services/edge-compute.js +440 -440
- package/server/services/email.js +233 -204
- package/server/services/governance.js +466 -0
- package/server/services/hosted-runtime.js +205 -205
- package/server/services/lfd.js +635 -635
- package/server/services/local-ai.js +389 -389
- package/server/services/marketplace.js +270 -270
- package/server/services/metering.js +182 -182
- package/server/services/modules/affiliate-intelligence.js +93 -93
- package/server/services/modules/agent-firewall.js +90 -90
- package/server/services/modules/bounty.js +89 -89
- package/server/services/modules/collective-bargaining.js +92 -92
- package/server/services/modules/dark-pattern.js +66 -66
- package/server/services/modules/gov-intelligence.js +45 -45
- package/server/services/modules/neural.js +55 -55
- package/server/services/modules/notary.js +49 -49
- package/server/services/modules/price-time-machine.js +86 -86
- package/server/services/modules/protocol.js +104 -104
- package/server/services/negotiation.js +439 -439
- package/server/services/plans.js +214 -0
- package/server/services/plugins.js +771 -771
- package/server/services/premium.js +1 -1
- package/server/services/price-intelligence.js +566 -566
- package/server/services/price-shield.js +1137 -1137
- package/server/services/provider-clients.js +740 -0
- package/server/services/reputation.js +465 -465
- package/server/services/search-engine.js +357 -357
- package/server/services/security.js +513 -513
- package/server/services/self-healing.js +843 -843
- package/server/services/shieldqr.js +322 -0
- package/server/services/sovereign-shield.js +542 -0
- package/server/services/ssl-inspector.js +42 -0
- package/server/services/ssl-monitor.js +167 -0
- package/server/services/stripe.js +205 -192
- package/server/services/swarm.js +788 -788
- package/server/services/universal-scraper.js +662 -661
- package/server/services/verification.js +481 -481
- package/server/services/vision.js +1163 -1163
- package/server/services/wab-crypto.js +178 -0
- package/server/utils/cache.js +125 -125
- package/server/utils/migrate.js +81 -81
- package/server/utils/safe-fetch.js +228 -0
- package/server/utils/secureFields.js +50 -50
- package/server/ws.js +161 -161
- package/templates/artisan-marketplace.yaml +104 -104
- package/templates/book-price-scout.yaml +98 -98
- package/templates/electronics-price-tracker.yaml +108 -108
- package/templates/flight-deal-hunter.yaml +113 -113
- package/templates/freelancer-direct.yaml +116 -116
- package/templates/grocery-price-compare.yaml +93 -93
- package/templates/hotel-direct-booking.yaml +113 -113
- package/templates/local-services.yaml +98 -98
- package/templates/olive-oil-tunisia.yaml +88 -88
- package/templates/organic-farm-fresh.yaml +101 -101
- package/templates/restaurant-direct.yaml +97 -97
- package/public/score.html +0 -263
- package/server/migrations/006_growth_suite.sql +0 -138
- package/server/routes/growth.js +0 -962
- package/server/services/fairness-engine.js +0 -409
- package/server/services/fairness.js +0 -420
|
@@ -1,301 +1,301 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* WAB Control Plane
|
|
5
|
-
*
|
|
6
|
-
* Management layer for the Agent OS. Handles:
|
|
7
|
-
* - Agent lifecycle management
|
|
8
|
-
* - Policy enforcement
|
|
9
|
-
* - Deployment management
|
|
10
|
-
* - Configuration distribution
|
|
11
|
-
*
|
|
12
|
-
* The Control Plane is separated from the Data Plane.
|
|
13
|
-
* It decides WHAT to do and WHO can do it.
|
|
14
|
-
* The Data Plane executes the actual work.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const crypto = require('crypto');
|
|
18
|
-
const { bus } = require('../runtime/event-bus');
|
|
19
|
-
const { identity } = require('../security');
|
|
20
|
-
|
|
21
|
-
// ─── Agent Lifecycle Manager ────────────────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
class AgentManager {
|
|
24
|
-
constructor() {
|
|
25
|
-
this._deployments = new Map(); // deploymentId → deployment config
|
|
26
|
-
this._assignments = new Map(); // agentId → Set<siteId>
|
|
27
|
-
this._healthChecks = new Map(); // agentId → last health check
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Deploy an agent to the runtime
|
|
32
|
-
*/
|
|
33
|
-
deploy(agentId, config = {}) {
|
|
34
|
-
const agent = identity.getAgent(agentId);
|
|
35
|
-
if (!agent) throw new Error(`Agent not found: ${agentId}`);
|
|
36
|
-
|
|
37
|
-
const deploymentId = `deploy_${crypto.randomBytes(12).toString('hex')}`;
|
|
38
|
-
const deployment = {
|
|
39
|
-
id: deploymentId,
|
|
40
|
-
agentId,
|
|
41
|
-
config: {
|
|
42
|
-
autoRestart: config.autoRestart !== false,
|
|
43
|
-
maxRetries: config.maxRetries || 5,
|
|
44
|
-
healthCheckInterval: config.healthCheckInterval || 60_000,
|
|
45
|
-
resources: {
|
|
46
|
-
maxMemory: config.maxMemory || 256 * 1024 * 1024,
|
|
47
|
-
maxCpu: config.maxCpu || 80, // percentage
|
|
48
|
-
maxTasks: config.maxTasks || 10,
|
|
49
|
-
},
|
|
50
|
-
environment: config.environment || 'production',
|
|
51
|
-
version: config.version || '1.0.0',
|
|
52
|
-
},
|
|
53
|
-
status: 'deployed',
|
|
54
|
-
restartCount: 0,
|
|
55
|
-
deployedAt: Date.now(),
|
|
56
|
-
lastHealthCheck: null,
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
this._deployments.set(deploymentId, deployment);
|
|
60
|
-
bus.emit('agent.deployed', { agentId, deploymentId });
|
|
61
|
-
|
|
62
|
-
return deployment;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Assign agent to sites
|
|
67
|
-
*/
|
|
68
|
-
assign(agentId, siteIds) {
|
|
69
|
-
if (!this._assignments.has(agentId)) this._assignments.set(agentId, new Set());
|
|
70
|
-
const set = this._assignments.get(agentId);
|
|
71
|
-
for (const siteId of siteIds) set.add(siteId);
|
|
72
|
-
bus.emit('agent.assigned', { agentId, sites: siteIds });
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Unassign agent from sites
|
|
77
|
-
*/
|
|
78
|
-
unassign(agentId, siteIds) {
|
|
79
|
-
const set = this._assignments.get(agentId);
|
|
80
|
-
if (!set) return;
|
|
81
|
-
for (const siteId of siteIds) set.delete(siteId);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Get sites assigned to an agent
|
|
86
|
-
*/
|
|
87
|
-
getAssignments(agentId) {
|
|
88
|
-
const set = this._assignments.get(agentId);
|
|
89
|
-
return set ? [...set] : [];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Record a health check
|
|
94
|
-
*/
|
|
95
|
-
recordHealthCheck(agentId, health) {
|
|
96
|
-
this._healthChecks.set(agentId, {
|
|
97
|
-
...health,
|
|
98
|
-
timestamp: Date.now(),
|
|
99
|
-
status: health.healthy ? 'healthy' : 'unhealthy',
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
if (!health.healthy) {
|
|
103
|
-
bus.emit('agent.unhealthy', { agentId, reason: health.reason || 'unknown' });
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Get agent health
|
|
109
|
-
*/
|
|
110
|
-
getHealth(agentId) {
|
|
111
|
-
return this._healthChecks.get(agentId) || null;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Undeploy an agent
|
|
116
|
-
*/
|
|
117
|
-
undeploy(deploymentId) {
|
|
118
|
-
const deployment = this._deployments.get(deploymentId);
|
|
119
|
-
if (deployment) {
|
|
120
|
-
deployment.status = 'undeployed';
|
|
121
|
-
bus.emit('agent.undeployed', { agentId: deployment.agentId, deploymentId });
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* List all deployments
|
|
127
|
-
*/
|
|
128
|
-
listDeployments(filter = {}) {
|
|
129
|
-
const result = [];
|
|
130
|
-
for (const [, d] of this._deployments) {
|
|
131
|
-
if (filter.status && d.status !== filter.status) continue;
|
|
132
|
-
if (filter.agentId && d.agentId !== filter.agentId) continue;
|
|
133
|
-
result.push(d);
|
|
134
|
-
}
|
|
135
|
-
return result;
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// ─── Policy Engine ──────────────────────────────────────────────────────────
|
|
140
|
-
|
|
141
|
-
class PolicyEngine {
|
|
142
|
-
constructor() {
|
|
143
|
-
this._policies = new Map(); // policyId → policy definition
|
|
144
|
-
this._bindings = new Map(); // entityId → Set<policyId>
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Create a policy
|
|
149
|
-
*/
|
|
150
|
-
createPolicy(policy) {
|
|
151
|
-
const policyId = `policy_${crypto.randomBytes(12).toString('hex')}`;
|
|
152
|
-
const def = {
|
|
153
|
-
id: policyId,
|
|
154
|
-
name: policy.name || 'Unnamed Policy',
|
|
155
|
-
description: policy.description || '',
|
|
156
|
-
type: policy.type || 'agent', // agent, site, global
|
|
157
|
-
|
|
158
|
-
// Rules
|
|
159
|
-
rules: (policy.rules || []).map(r => ({
|
|
160
|
-
id: `rule_${crypto.randomBytes(6).toString('hex')}`,
|
|
161
|
-
action: r.action, // allow, deny, require
|
|
162
|
-
resource: r.resource, // capability, selector, domain, rate
|
|
163
|
-
condition: r.condition || {}, // { equals, contains, pattern, min, max }
|
|
164
|
-
effect: r.effect || 'deny', // allow, deny, audit
|
|
165
|
-
})),
|
|
166
|
-
|
|
167
|
-
// Rate limits
|
|
168
|
-
rateLimit: policy.rateLimit || null,
|
|
169
|
-
|
|
170
|
-
// Time constraints
|
|
171
|
-
schedule: policy.schedule || null, // { start, end, timezone, days: [] }
|
|
172
|
-
|
|
173
|
-
priority: policy.priority || 0,
|
|
174
|
-
enabled: policy.enabled !== false,
|
|
175
|
-
createdAt: Date.now(),
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
this._policies.set(policyId, def);
|
|
179
|
-
return def;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Bind a policy to an entity (agent, site, global)
|
|
184
|
-
*/
|
|
185
|
-
bind(entityId, policyId) {
|
|
186
|
-
if (!this._bindings.has(entityId)) this._bindings.set(entityId, new Set());
|
|
187
|
-
this._bindings.get(entityId).add(policyId);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Unbind a policy
|
|
192
|
-
*/
|
|
193
|
-
unbind(entityId, policyId) {
|
|
194
|
-
const bindings = this._bindings.get(entityId);
|
|
195
|
-
if (bindings) bindings.delete(policyId);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Evaluate policies for an entity against an action
|
|
200
|
-
*/
|
|
201
|
-
evaluate(entityId, action, context = {}) {
|
|
202
|
-
const policyIds = this._bindings.get(entityId) || new Set();
|
|
203
|
-
const globalIds = this._bindings.get('*') || new Set();
|
|
204
|
-
const allIds = new Set([...policyIds, ...globalIds]);
|
|
205
|
-
|
|
206
|
-
const results = [];
|
|
207
|
-
let finalEffect = 'allow'; // default allow
|
|
208
|
-
|
|
209
|
-
// Sort policies by priority
|
|
210
|
-
const policies = [];
|
|
211
|
-
for (const pid of allIds) {
|
|
212
|
-
const policy = this._policies.get(pid);
|
|
213
|
-
if (policy && policy.enabled) policies.push(policy);
|
|
214
|
-
}
|
|
215
|
-
policies.sort((a, b) => b.priority - a.priority);
|
|
216
|
-
|
|
217
|
-
for (const policy of policies) {
|
|
218
|
-
for (const rule of policy.rules) {
|
|
219
|
-
if (rule.action !== action && rule.action !== '*') continue;
|
|
220
|
-
|
|
221
|
-
const match = this._evaluateCondition(rule.condition, context);
|
|
222
|
-
if (match) {
|
|
223
|
-
results.push({
|
|
224
|
-
policyId: policy.id,
|
|
225
|
-
policyName: policy.name,
|
|
226
|
-
ruleId: rule.id,
|
|
227
|
-
effect: rule.effect,
|
|
228
|
-
resource: rule.resource,
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
if (rule.effect === 'deny') finalEffect = 'deny';
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
return {
|
|
237
|
-
allowed: finalEffect === 'allow',
|
|
238
|
-
effect: finalEffect,
|
|
239
|
-
evaluatedPolicies: results,
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Evaluate a rule condition
|
|
245
|
-
*/
|
|
246
|
-
_evaluateCondition(condition, context) {
|
|
247
|
-
if (!condition || Object.keys(condition).length === 0) return true;
|
|
248
|
-
|
|
249
|
-
for (const [key, value] of Object.entries(condition)) {
|
|
250
|
-
const contextValue = context[key];
|
|
251
|
-
if (contextValue === undefined) return false;
|
|
252
|
-
|
|
253
|
-
if (typeof value === 'object' && value !== null) {
|
|
254
|
-
if (value.equals !== undefined && contextValue !== value.equals) return false;
|
|
255
|
-
if (value.contains && !String(contextValue).includes(value.contains)) return false;
|
|
256
|
-
if (value.pattern && !new RegExp(value.pattern).test(String(contextValue))) return false;
|
|
257
|
-
if (value.min !== undefined && contextValue < value.min) return false;
|
|
258
|
-
if (value.max !== undefined && contextValue > value.max) return false;
|
|
259
|
-
if (value.in && !value.in.includes(contextValue)) return false;
|
|
260
|
-
} else {
|
|
261
|
-
if (contextValue !== value) return false;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return true;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
/**
|
|
268
|
-
* Get policy
|
|
269
|
-
*/
|
|
270
|
-
getPolicy(policyId) {
|
|
271
|
-
return this._policies.get(policyId) || null;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* List policies
|
|
276
|
-
*/
|
|
277
|
-
listPolicies(entityId) {
|
|
278
|
-
if (entityId) {
|
|
279
|
-
const ids = this._bindings.get(entityId) || new Set();
|
|
280
|
-
return [...ids].map(id => this._policies.get(id)).filter(Boolean);
|
|
281
|
-
}
|
|
282
|
-
return Array.from(this._policies.values());
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Delete a policy
|
|
287
|
-
*/
|
|
288
|
-
deletePolicy(policyId) {
|
|
289
|
-
this._policies.delete(policyId);
|
|
290
|
-
for (const [, bindings] of this._bindings) {
|
|
291
|
-
bindings.delete(policyId);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
// ─── Singletons ─────────────────────────────────────────────────────────────
|
|
297
|
-
|
|
298
|
-
const agentManager = new AgentManager();
|
|
299
|
-
const policyEngine = new PolicyEngine();
|
|
300
|
-
|
|
301
|
-
module.exports = { AgentManager, PolicyEngine, agentManager, policyEngine };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* WAB Control Plane
|
|
5
|
+
*
|
|
6
|
+
* Management layer for the Agent OS. Handles:
|
|
7
|
+
* - Agent lifecycle management
|
|
8
|
+
* - Policy enforcement
|
|
9
|
+
* - Deployment management
|
|
10
|
+
* - Configuration distribution
|
|
11
|
+
*
|
|
12
|
+
* The Control Plane is separated from the Data Plane.
|
|
13
|
+
* It decides WHAT to do and WHO can do it.
|
|
14
|
+
* The Data Plane executes the actual work.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const crypto = require('crypto');
|
|
18
|
+
const { bus } = require('../runtime/event-bus');
|
|
19
|
+
const { identity } = require('../security');
|
|
20
|
+
|
|
21
|
+
// ─── Agent Lifecycle Manager ────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
class AgentManager {
|
|
24
|
+
constructor() {
|
|
25
|
+
this._deployments = new Map(); // deploymentId → deployment config
|
|
26
|
+
this._assignments = new Map(); // agentId → Set<siteId>
|
|
27
|
+
this._healthChecks = new Map(); // agentId → last health check
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Deploy an agent to the runtime
|
|
32
|
+
*/
|
|
33
|
+
deploy(agentId, config = {}) {
|
|
34
|
+
const agent = identity.getAgent(agentId);
|
|
35
|
+
if (!agent) throw new Error(`Agent not found: ${agentId}`);
|
|
36
|
+
|
|
37
|
+
const deploymentId = `deploy_${crypto.randomBytes(12).toString('hex')}`;
|
|
38
|
+
const deployment = {
|
|
39
|
+
id: deploymentId,
|
|
40
|
+
agentId,
|
|
41
|
+
config: {
|
|
42
|
+
autoRestart: config.autoRestart !== false,
|
|
43
|
+
maxRetries: config.maxRetries || 5,
|
|
44
|
+
healthCheckInterval: config.healthCheckInterval || 60_000,
|
|
45
|
+
resources: {
|
|
46
|
+
maxMemory: config.maxMemory || 256 * 1024 * 1024,
|
|
47
|
+
maxCpu: config.maxCpu || 80, // percentage
|
|
48
|
+
maxTasks: config.maxTasks || 10,
|
|
49
|
+
},
|
|
50
|
+
environment: config.environment || 'production',
|
|
51
|
+
version: config.version || '1.0.0',
|
|
52
|
+
},
|
|
53
|
+
status: 'deployed',
|
|
54
|
+
restartCount: 0,
|
|
55
|
+
deployedAt: Date.now(),
|
|
56
|
+
lastHealthCheck: null,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
this._deployments.set(deploymentId, deployment);
|
|
60
|
+
bus.emit('agent.deployed', { agentId, deploymentId });
|
|
61
|
+
|
|
62
|
+
return deployment;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Assign agent to sites
|
|
67
|
+
*/
|
|
68
|
+
assign(agentId, siteIds) {
|
|
69
|
+
if (!this._assignments.has(agentId)) this._assignments.set(agentId, new Set());
|
|
70
|
+
const set = this._assignments.get(agentId);
|
|
71
|
+
for (const siteId of siteIds) set.add(siteId);
|
|
72
|
+
bus.emit('agent.assigned', { agentId, sites: siteIds });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Unassign agent from sites
|
|
77
|
+
*/
|
|
78
|
+
unassign(agentId, siteIds) {
|
|
79
|
+
const set = this._assignments.get(agentId);
|
|
80
|
+
if (!set) return;
|
|
81
|
+
for (const siteId of siteIds) set.delete(siteId);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get sites assigned to an agent
|
|
86
|
+
*/
|
|
87
|
+
getAssignments(agentId) {
|
|
88
|
+
const set = this._assignments.get(agentId);
|
|
89
|
+
return set ? [...set] : [];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Record a health check
|
|
94
|
+
*/
|
|
95
|
+
recordHealthCheck(agentId, health) {
|
|
96
|
+
this._healthChecks.set(agentId, {
|
|
97
|
+
...health,
|
|
98
|
+
timestamp: Date.now(),
|
|
99
|
+
status: health.healthy ? 'healthy' : 'unhealthy',
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
if (!health.healthy) {
|
|
103
|
+
bus.emit('agent.unhealthy', { agentId, reason: health.reason || 'unknown' });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get agent health
|
|
109
|
+
*/
|
|
110
|
+
getHealth(agentId) {
|
|
111
|
+
return this._healthChecks.get(agentId) || null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Undeploy an agent
|
|
116
|
+
*/
|
|
117
|
+
undeploy(deploymentId) {
|
|
118
|
+
const deployment = this._deployments.get(deploymentId);
|
|
119
|
+
if (deployment) {
|
|
120
|
+
deployment.status = 'undeployed';
|
|
121
|
+
bus.emit('agent.undeployed', { agentId: deployment.agentId, deploymentId });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* List all deployments
|
|
127
|
+
*/
|
|
128
|
+
listDeployments(filter = {}) {
|
|
129
|
+
const result = [];
|
|
130
|
+
for (const [, d] of this._deployments) {
|
|
131
|
+
if (filter.status && d.status !== filter.status) continue;
|
|
132
|
+
if (filter.agentId && d.agentId !== filter.agentId) continue;
|
|
133
|
+
result.push(d);
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ─── Policy Engine ──────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
class PolicyEngine {
|
|
142
|
+
constructor() {
|
|
143
|
+
this._policies = new Map(); // policyId → policy definition
|
|
144
|
+
this._bindings = new Map(); // entityId → Set<policyId>
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Create a policy
|
|
149
|
+
*/
|
|
150
|
+
createPolicy(policy) {
|
|
151
|
+
const policyId = `policy_${crypto.randomBytes(12).toString('hex')}`;
|
|
152
|
+
const def = {
|
|
153
|
+
id: policyId,
|
|
154
|
+
name: policy.name || 'Unnamed Policy',
|
|
155
|
+
description: policy.description || '',
|
|
156
|
+
type: policy.type || 'agent', // agent, site, global
|
|
157
|
+
|
|
158
|
+
// Rules
|
|
159
|
+
rules: (policy.rules || []).map(r => ({
|
|
160
|
+
id: `rule_${crypto.randomBytes(6).toString('hex')}`,
|
|
161
|
+
action: r.action, // allow, deny, require
|
|
162
|
+
resource: r.resource, // capability, selector, domain, rate
|
|
163
|
+
condition: r.condition || {}, // { equals, contains, pattern, min, max }
|
|
164
|
+
effect: r.effect || 'deny', // allow, deny, audit
|
|
165
|
+
})),
|
|
166
|
+
|
|
167
|
+
// Rate limits
|
|
168
|
+
rateLimit: policy.rateLimit || null,
|
|
169
|
+
|
|
170
|
+
// Time constraints
|
|
171
|
+
schedule: policy.schedule || null, // { start, end, timezone, days: [] }
|
|
172
|
+
|
|
173
|
+
priority: policy.priority || 0,
|
|
174
|
+
enabled: policy.enabled !== false,
|
|
175
|
+
createdAt: Date.now(),
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
this._policies.set(policyId, def);
|
|
179
|
+
return def;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Bind a policy to an entity (agent, site, global)
|
|
184
|
+
*/
|
|
185
|
+
bind(entityId, policyId) {
|
|
186
|
+
if (!this._bindings.has(entityId)) this._bindings.set(entityId, new Set());
|
|
187
|
+
this._bindings.get(entityId).add(policyId);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Unbind a policy
|
|
192
|
+
*/
|
|
193
|
+
unbind(entityId, policyId) {
|
|
194
|
+
const bindings = this._bindings.get(entityId);
|
|
195
|
+
if (bindings) bindings.delete(policyId);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Evaluate policies for an entity against an action
|
|
200
|
+
*/
|
|
201
|
+
evaluate(entityId, action, context = {}) {
|
|
202
|
+
const policyIds = this._bindings.get(entityId) || new Set();
|
|
203
|
+
const globalIds = this._bindings.get('*') || new Set();
|
|
204
|
+
const allIds = new Set([...policyIds, ...globalIds]);
|
|
205
|
+
|
|
206
|
+
const results = [];
|
|
207
|
+
let finalEffect = 'allow'; // default allow
|
|
208
|
+
|
|
209
|
+
// Sort policies by priority
|
|
210
|
+
const policies = [];
|
|
211
|
+
for (const pid of allIds) {
|
|
212
|
+
const policy = this._policies.get(pid);
|
|
213
|
+
if (policy && policy.enabled) policies.push(policy);
|
|
214
|
+
}
|
|
215
|
+
policies.sort((a, b) => b.priority - a.priority);
|
|
216
|
+
|
|
217
|
+
for (const policy of policies) {
|
|
218
|
+
for (const rule of policy.rules) {
|
|
219
|
+
if (rule.action !== action && rule.action !== '*') continue;
|
|
220
|
+
|
|
221
|
+
const match = this._evaluateCondition(rule.condition, context);
|
|
222
|
+
if (match) {
|
|
223
|
+
results.push({
|
|
224
|
+
policyId: policy.id,
|
|
225
|
+
policyName: policy.name,
|
|
226
|
+
ruleId: rule.id,
|
|
227
|
+
effect: rule.effect,
|
|
228
|
+
resource: rule.resource,
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
if (rule.effect === 'deny') finalEffect = 'deny';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
allowed: finalEffect === 'allow',
|
|
238
|
+
effect: finalEffect,
|
|
239
|
+
evaluatedPolicies: results,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Evaluate a rule condition
|
|
245
|
+
*/
|
|
246
|
+
_evaluateCondition(condition, context) {
|
|
247
|
+
if (!condition || Object.keys(condition).length === 0) return true;
|
|
248
|
+
|
|
249
|
+
for (const [key, value] of Object.entries(condition)) {
|
|
250
|
+
const contextValue = context[key];
|
|
251
|
+
if (contextValue === undefined) return false;
|
|
252
|
+
|
|
253
|
+
if (typeof value === 'object' && value !== null) {
|
|
254
|
+
if (value.equals !== undefined && contextValue !== value.equals) return false;
|
|
255
|
+
if (value.contains && !String(contextValue).includes(value.contains)) return false;
|
|
256
|
+
if (value.pattern && !new RegExp(value.pattern).test(String(contextValue))) return false;
|
|
257
|
+
if (value.min !== undefined && contextValue < value.min) return false;
|
|
258
|
+
if (value.max !== undefined && contextValue > value.max) return false;
|
|
259
|
+
if (value.in && !value.in.includes(contextValue)) return false;
|
|
260
|
+
} else {
|
|
261
|
+
if (contextValue !== value) return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Get policy
|
|
269
|
+
*/
|
|
270
|
+
getPolicy(policyId) {
|
|
271
|
+
return this._policies.get(policyId) || null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* List policies
|
|
276
|
+
*/
|
|
277
|
+
listPolicies(entityId) {
|
|
278
|
+
if (entityId) {
|
|
279
|
+
const ids = this._bindings.get(entityId) || new Set();
|
|
280
|
+
return [...ids].map(id => this._policies.get(id)).filter(Boolean);
|
|
281
|
+
}
|
|
282
|
+
return Array.from(this._policies.values());
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Delete a policy
|
|
287
|
+
*/
|
|
288
|
+
deletePolicy(policyId) {
|
|
289
|
+
this._policies.delete(policyId);
|
|
290
|
+
for (const [, bindings] of this._bindings) {
|
|
291
|
+
bindings.delete(policyId);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// ─── Singletons ─────────────────────────────────────────────────────────────
|
|
297
|
+
|
|
298
|
+
const agentManager = new AgentManager();
|
|
299
|
+
const policyEngine = new PolicyEngine();
|
|
300
|
+
|
|
301
|
+
module.exports = { AgentManager, PolicyEngine, agentManager, policyEngine };
|