@clawnch/clawtomaton 0.2.2 → 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.
Files changed (61) hide show
  1. package/README.md +80 -8
  2. package/dist/agent/index.d.ts.map +1 -1
  3. package/dist/agent/index.js +12 -0
  4. package/dist/agent/index.js.map +1 -1
  5. package/dist/agent/prompt.d.ts +2 -0
  6. package/dist/agent/prompt.d.ts.map +1 -1
  7. package/dist/agent/prompt.js +43 -2
  8. package/dist/agent/prompt.js.map +1 -1
  9. package/dist/bunker/client.d.ts +113 -0
  10. package/dist/bunker/client.d.ts.map +1 -0
  11. package/dist/bunker/client.js +404 -0
  12. package/dist/bunker/client.js.map +1 -0
  13. package/dist/bunker/self-deploy.d.ts +54 -0
  14. package/dist/bunker/self-deploy.d.ts.map +1 -0
  15. package/dist/bunker/self-deploy.js +353 -0
  16. package/dist/bunker/self-deploy.js.map +1 -0
  17. package/dist/bunker/threat-monitor.d.ts +47 -0
  18. package/dist/bunker/threat-monitor.d.ts.map +1 -0
  19. package/dist/bunker/threat-monitor.js +173 -0
  20. package/dist/bunker/threat-monitor.js.map +1 -0
  21. package/dist/bunker/types.d.ts +320 -0
  22. package/dist/bunker/types.d.ts.map +1 -0
  23. package/dist/bunker/types.js +9 -0
  24. package/dist/bunker/types.js.map +1 -0
  25. package/dist/cli.js +207 -9
  26. package/dist/cli.js.map +1 -1
  27. package/dist/constants.d.ts +11 -0
  28. package/dist/constants.d.ts.map +1 -1
  29. package/dist/constants.js +10 -0
  30. package/dist/constants.js.map +1 -1
  31. package/dist/heartbeat/index.d.ts +10 -0
  32. package/dist/heartbeat/index.d.ts.map +1 -1
  33. package/dist/heartbeat/index.js +89 -11
  34. package/dist/heartbeat/index.js.map +1 -1
  35. package/dist/index.d.ts +8 -1
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +7 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/market/index.d.ts +12 -0
  40. package/dist/market/index.d.ts.map +1 -1
  41. package/dist/market/index.js +48 -2
  42. package/dist/market/index.js.map +1 -1
  43. package/dist/skills/bunker.d.ts +29 -0
  44. package/dist/skills/bunker.d.ts.map +1 -0
  45. package/dist/skills/bunker.js +502 -0
  46. package/dist/skills/bunker.js.map +1 -0
  47. package/dist/skills/index.d.ts +2 -1
  48. package/dist/skills/index.d.ts.map +1 -1
  49. package/dist/skills/index.js +6 -1
  50. package/dist/skills/index.js.map +1 -1
  51. package/dist/state/index.d.ts +6 -0
  52. package/dist/state/index.d.ts.map +1 -1
  53. package/dist/state/index.js +55 -0
  54. package/dist/state/index.js.map +1 -1
  55. package/dist/survival/index.d.ts.map +1 -1
  56. package/dist/survival/index.js +3 -0
  57. package/dist/survival/index.js.map +1 -1
  58. package/dist/types.d.ts +3 -0
  59. package/dist/types.d.ts.map +1 -1
  60. package/dist/types.js.map +1 -1
  61. package/package.json +1 -1
@@ -0,0 +1,353 @@
1
+ /**
2
+ * Self-deployment — packages the agent and deploys to MoltBunker.
3
+ *
4
+ * Flow:
5
+ * 1. Pre-flight checks (BUNKER balance, no duplicate deployments)
6
+ * 2. Register bot on MoltBunker (or reuse existing)
7
+ * 3. Reserve runtime with requested tier/region
8
+ * 4. Deploy container with agent config as env vars
9
+ * 5. Enable cloning if configured
10
+ * 6. Health verification (poll until running + heartbeat in logs)
11
+ * 7. Persist state (bot_id, runtime_id, deployment_id, container_id)
12
+ *
13
+ * Edge cases:
14
+ * - Existing running container → return status instead of re-deploying
15
+ * - Bot already registered → reuse it
16
+ * - Partial failure (runtime reserved, deploy fails) → release runtime
17
+ * - Env var size limits → config is small enough to fit, state stays in container volume
18
+ */
19
+ import { BunkerNotFoundError } from './client.js';
20
+ import { BUNKER_DOCKER_IMAGE, BUNKER_DEFAULT_DURATION_HOURS } from '../constants.js';
21
+ // Resource tier mappings (must match MoltBunker catalog)
22
+ const TIER_RESOURCES = {
23
+ minimal: { cpu_shares: 1024, memory_mb: 1024, storage_mb: 10240, network_mbps: 100 },
24
+ standard: { cpu_shares: 2048, memory_mb: 4096, storage_mb: 51200, network_mbps: 100 },
25
+ performance: { cpu_shares: 4096, memory_mb: 8192, storage_mb: 204800, network_mbps: 100 },
26
+ enterprise: { cpu_shares: 8192, memory_mb: 16384, storage_mb: 512000, network_mbps: 100 },
27
+ };
28
+ // ============================================================================
29
+ // Self-Deploy
30
+ // ============================================================================
31
+ /**
32
+ * Deploy this agent to MoltBunker. Handles the full lifecycle:
33
+ * bot registration → runtime reservation → container deployment → health check.
34
+ *
35
+ * Idempotent: if already deployed and running, returns existing state.
36
+ */
37
+ export async function selfDeploy(client, config, identity, state, opts) {
38
+ const bunkerConfig = config.bunker;
39
+ if (!bunkerConfig) {
40
+ throw new Error('MoltBunker not configured. Run: clawtomaton setup (with bunker enabled)');
41
+ }
42
+ // Check for existing deployment
43
+ const existing = state.getBunkerState();
44
+ if (existing?.containerId) {
45
+ const running = await isContainerRunning(client, existing.containerId);
46
+ if (running) {
47
+ return {
48
+ botId: existing.botId,
49
+ runtimeId: existing.runtimeId,
50
+ deploymentId: existing.deploymentId,
51
+ containerId: existing.containerId,
52
+ expiresAt: existing.expiresAt,
53
+ region: bunkerConfig.region,
54
+ onionAddress: null, // caller can fetch full status if needed
55
+ };
56
+ }
57
+ // Container exists but not running — proceed with new deployment
58
+ }
59
+ // Pre-flight: check BUNKER balance
60
+ const balance = await client.getBalance(identity.address);
61
+ if (balance.available <= 0) {
62
+ throw new Error(`Insufficient BUNKER balance for deployment. ` +
63
+ `Available: ${balance.available.toLocaleString()} BUNKER. ` +
64
+ `Deposit BUNKER tokens to your wallet (${identity.address}) first.`);
65
+ }
66
+ const image = opts?.image ?? BUNKER_DOCKER_IMAGE;
67
+ const region = opts?.region ?? bunkerConfig.region;
68
+ const durationHours = opts?.durationHours ?? bunkerConfig.defaultDurationHours ?? BUNKER_DEFAULT_DURATION_HOURS;
69
+ const tierResources = TIER_RESOURCES[bunkerConfig.tier] ?? TIER_RESOURCES.standard;
70
+ // Step 1: Register bot (or find existing)
71
+ const bot = await findOrCreateBot(client, identity, image, tierResources, region);
72
+ state.audit('bunker_bot', `Bot registered: ${bot.id} (${bot.name})`);
73
+ // Step 2: Reserve runtime
74
+ let runtime;
75
+ try {
76
+ runtime = await client.reserveRuntime({
77
+ bot_id: bot.id,
78
+ min_memory_mb: tierResources.memory_mb,
79
+ min_cpu_shares: tierResources.cpu_shares,
80
+ duration_hours: durationHours,
81
+ region,
82
+ });
83
+ state.audit('bunker_runtime', `Runtime reserved: ${runtime.id} (${durationHours}h, ${region || 'auto'})`);
84
+ }
85
+ catch (err) {
86
+ throw new Error(`Failed to reserve runtime: ${err instanceof Error ? err.message : String(err)}. ` +
87
+ `Check BUNKER balance and region availability.`);
88
+ }
89
+ // Step 3: Deploy container
90
+ let deployment;
91
+ try {
92
+ deployment = await client.createDeployment({
93
+ runtime_id: runtime.id,
94
+ env: buildEnvVars(config, identity),
95
+ cmd: ['node', 'dist/cli.js', 'daemon'],
96
+ });
97
+ state.audit('bunker_deploy', `Deployment created: ${deployment.id} → container ${deployment.container_id}`);
98
+ }
99
+ catch (err) {
100
+ // Cleanup: release the runtime we just reserved
101
+ try {
102
+ await client.releaseRuntime(runtime.id);
103
+ state.audit('bunker_cleanup', `Released runtime ${runtime.id} after failed deployment`);
104
+ }
105
+ catch {
106
+ // Cleanup failure is non-fatal
107
+ }
108
+ throw new Error(`Failed to deploy container: ${err instanceof Error ? err.message : String(err)}`);
109
+ }
110
+ // Step 4: Enable cloning if configured
111
+ if (bunkerConfig.autoCloneOnThreat) {
112
+ try {
113
+ const cloningConfig = {
114
+ enabled: true,
115
+ auto_clone_on_threat: true,
116
+ max_clones: bunkerConfig.maxClones,
117
+ clone_delay_seconds: 60,
118
+ sync_state: true,
119
+ sync_interval_seconds: 300,
120
+ };
121
+ await client.enableCloning(bot.id, cloningConfig);
122
+ state.audit('bunker_cloning', `Auto-cloning enabled (max ${bunkerConfig.maxClones} clones)`);
123
+ }
124
+ catch (err) {
125
+ // Non-fatal — log but continue
126
+ state.audit('bunker_cloning_warn', `Failed to enable cloning: ${err instanceof Error ? err.message : String(err)}`);
127
+ }
128
+ }
129
+ // Step 5: Health verification
130
+ const expiresAt = new Date(runtime.expires_at).getTime();
131
+ if (!opts?.skipHealthCheck) {
132
+ await waitForHealthy(client, deployment.container_id, state);
133
+ }
134
+ // Step 6: Persist state
135
+ const bunkerState = {
136
+ botId: bot.id,
137
+ runtimeId: runtime.id,
138
+ deploymentId: deployment.id,
139
+ containerId: deployment.container_id,
140
+ expiresAt,
141
+ lastThreatCheck: 0,
142
+ lastThreatLevel: 'unknown',
143
+ cloneIds: [],
144
+ };
145
+ state.saveBunkerState(bunkerState);
146
+ return {
147
+ botId: bot.id,
148
+ runtimeId: runtime.id,
149
+ deploymentId: deployment.id,
150
+ containerId: deployment.container_id,
151
+ expiresAt,
152
+ region: deployment.region,
153
+ onionAddress: deployment.onion_address,
154
+ };
155
+ }
156
+ // ============================================================================
157
+ // Stop Deployment
158
+ // ============================================================================
159
+ /**
160
+ * Stop the bunker deployment and clean up state.
161
+ * Does NOT delete the bot — it can be reused for future deployments.
162
+ */
163
+ export async function stopDeploy(client, state) {
164
+ const bunkerState = state.getBunkerState();
165
+ if (!bunkerState?.deploymentId) {
166
+ throw new Error('No active bunker deployment found.');
167
+ }
168
+ // Stop deployment
169
+ try {
170
+ await client.stopDeployment(bunkerState.deploymentId);
171
+ state.audit('bunker_stop', `Deployment stopped: ${bunkerState.deploymentId}`);
172
+ }
173
+ catch (err) {
174
+ if (!(err instanceof BunkerNotFoundError)) {
175
+ throw err;
176
+ }
177
+ // Already stopped/deleted — continue cleanup
178
+ }
179
+ // Release runtime
180
+ if (bunkerState.runtimeId) {
181
+ try {
182
+ await client.releaseRuntime(bunkerState.runtimeId);
183
+ state.audit('bunker_cleanup', `Runtime released: ${bunkerState.runtimeId}`);
184
+ }
185
+ catch {
186
+ // Non-fatal
187
+ }
188
+ }
189
+ // Clear persisted state (keep bot_id for reuse)
190
+ state.saveBunkerState({
191
+ botId: bunkerState.botId,
192
+ runtimeId: null,
193
+ deploymentId: null,
194
+ containerId: null,
195
+ expiresAt: null,
196
+ lastThreatCheck: 0,
197
+ lastThreatLevel: 'unknown',
198
+ cloneIds: [],
199
+ });
200
+ }
201
+ // ============================================================================
202
+ // Helpers
203
+ // ============================================================================
204
+ /**
205
+ * Find an existing bot owned by this agent, or create a new one.
206
+ */
207
+ async function findOrCreateBot(client, identity, image, resources, region) {
208
+ // Check for existing bot with this name
209
+ try {
210
+ const bots = await client.listBots();
211
+ const existing = bots.find((b) => b.name === `clawtomaton-${identity.name}` || b.name === identity.name);
212
+ if (existing)
213
+ return existing;
214
+ }
215
+ catch {
216
+ // List failed — will try to create
217
+ }
218
+ return client.createBot({
219
+ name: `clawtomaton-${identity.name}`,
220
+ image,
221
+ description: `Clawtomaton autonomous agent: ${identity.name}`,
222
+ resources,
223
+ region,
224
+ metadata: {
225
+ agent_name: identity.name,
226
+ agent_address: identity.address,
227
+ version: '0.3.0',
228
+ },
229
+ });
230
+ }
231
+ /**
232
+ * Build env vars for the container. Contains everything the agent needs
233
+ * to reconstruct its config and identity inside the container.
234
+ *
235
+ * Private key is included because the container runs encrypted on MoltBunker.
236
+ * The agent needs it to sign transactions.
237
+ */
238
+ function buildEnvVars(config, identity) {
239
+ const env = {
240
+ NODE_ENV: 'production',
241
+ // Identity — agent reconstructs from these
242
+ CLAWTOMATON_NAME: identity.name,
243
+ CLAWTOMATON_ADDRESS: identity.address,
244
+ CLAWTOMATON_PRIVATE_KEY: identity.privateKey,
245
+ CLAWTOMATON_API_KEY: identity.apiKey,
246
+ CLAWTOMATON_CREATOR: identity.creatorAddress,
247
+ CLAWTOMATON_GENESIS: identity.genesisPrompt,
248
+ // Inference
249
+ CLAWTOMATON_INFERENCE_PROVIDER: config.inference.provider,
250
+ CLAWTOMATON_INFERENCE_API_KEY: config.inference.apiKey,
251
+ CLAWTOMATON_INFERENCE_MODEL: config.inference.model,
252
+ CLAWTOMATON_INFERENCE_FALLBACK: config.inference.fallbackModel,
253
+ // RPC
254
+ CLAWTOMATON_RPC_URL: config.rpcUrl,
255
+ // Heartbeat
256
+ CLAWTOMATON_HEARTBEAT_MS: String(config.heartbeatIntervalMs),
257
+ };
258
+ // Optional token info
259
+ if (identity.tokenAddress) {
260
+ env.CLAWTOMATON_TOKEN_ADDRESS = identity.tokenAddress;
261
+ }
262
+ if (identity.tokenSymbol) {
263
+ env.CLAWTOMATON_TOKEN_SYMBOL = identity.tokenSymbol;
264
+ }
265
+ // Optional Conway
266
+ if (config.conwayApiKey) {
267
+ env.CONWAY_API_KEY = config.conwayApiKey;
268
+ }
269
+ // Bunker config (so the containerized agent can also manage its own bunker)
270
+ if (config.bunker) {
271
+ env.CLAWTOMATON_BUNKER_API_URL = config.bunker.apiUrl;
272
+ env.CLAWTOMATON_BUNKER_REGION = config.bunker.region;
273
+ env.CLAWTOMATON_BUNKER_TIER = config.bunker.tier;
274
+ env.CLAWTOMATON_BUNKER_AUTO_CLONE = String(config.bunker.autoCloneOnThreat);
275
+ env.CLAWTOMATON_BUNKER_MAX_CLONES = String(config.bunker.maxClones);
276
+ }
277
+ return env;
278
+ }
279
+ /**
280
+ * Poll container status until it's running and producing heartbeat output.
281
+ * Throws if container fails to start within timeout.
282
+ */
283
+ async function waitForHealthy(client, containerId, state, timeoutMs = 120_000, pollIntervalMs = 5_000) {
284
+ const deadline = Date.now() + timeoutMs;
285
+ const terminalStatuses = ['failed', 'terminated', 'stopped'];
286
+ while (Date.now() < deadline) {
287
+ try {
288
+ const container = await client.getContainer(containerId);
289
+ if (terminalStatuses.includes(container.status)) {
290
+ // Try to get logs for debugging
291
+ let logSnippet = '';
292
+ try {
293
+ logSnippet = await client.getContainerLogs(containerId, 20);
294
+ }
295
+ catch {
296
+ // Non-fatal
297
+ }
298
+ throw new Error(`Container ${containerId} entered terminal state: ${container.status}` +
299
+ (logSnippet ? `\nLast logs:\n${logSnippet}` : ''));
300
+ }
301
+ if (container.status === 'running') {
302
+ // Verify heartbeat is actually producing output
303
+ try {
304
+ const logs = await client.getContainerLogs(containerId, 5);
305
+ if (logs && (logs.includes('[heartbeat]') || logs.includes('[clawtomaton]'))) {
306
+ state.audit('bunker_healthy', `Container ${containerId} is healthy and producing output`);
307
+ return;
308
+ }
309
+ }
310
+ catch {
311
+ // Logs not available yet — container just started
312
+ }
313
+ // Container is running but no logs yet — give it a moment
314
+ // After 30s of running with no logs, consider it healthy anyway
315
+ if (container.started_at) {
316
+ const startedAt = new Date(container.started_at).getTime();
317
+ if (Date.now() - startedAt > 30_000) {
318
+ state.audit('bunker_healthy', `Container ${containerId} running (no log output yet, assumed healthy)`);
319
+ return;
320
+ }
321
+ }
322
+ }
323
+ }
324
+ catch (err) {
325
+ if (err instanceof Error && !err.message.includes('terminal state')) {
326
+ // Transient error — continue polling
327
+ state.audit('bunker_health_poll', `Health check transient error: ${err.message}`);
328
+ }
329
+ else {
330
+ throw err;
331
+ }
332
+ }
333
+ await sleep(pollIntervalMs);
334
+ }
335
+ // Timeout — deployment is still starting, not necessarily failed
336
+ state.audit('bunker_health_timeout', `Container ${containerId} health check timed out after ${timeoutMs / 1000}s`);
337
+ }
338
+ /**
339
+ * Check if a container is currently running.
340
+ */
341
+ async function isContainerRunning(client, containerId) {
342
+ try {
343
+ const container = await client.getContainer(containerId);
344
+ return container.status === 'running';
345
+ }
346
+ catch {
347
+ return false;
348
+ }
349
+ }
350
+ function sleep(ms) {
351
+ return new Promise((resolve) => setTimeout(resolve, ms));
352
+ }
353
+ //# sourceMappingURL=self-deploy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"self-deploy.js","sourceRoot":"","sources":["../../src/bunker/self-deploy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Threat monitor — polls MoltBunker threat endpoint and triggers auto-clone.
3
+ *
4
+ * Not a standalone timer. Called from the heartbeat loop when bunker is configured.
5
+ * The heartbeat already handles timing — this module provides:
6
+ * - checkThreat(): fetch threat level, update state, return escalation info
7
+ * - handleThreatEscalation(): auto-clone if threshold crossed
8
+ *
9
+ * Threat levels and actions:
10
+ * low (score < 0.3) — log only, no action
11
+ * medium (score 0.3-0.6) — audit log, surface in system prompt
12
+ * high (score 0.6-0.8) — auto-clone to different region
13
+ * critical (score > 0.8) — auto-clone + auto-migrate
14
+ */
15
+ import type { BunkerClient } from './client.js';
16
+ import type { StateStore } from '../state/index.js';
17
+ import type { ThreatAssessment, ThreatLevel } from './types.js';
18
+ export interface ThreatCheckResult {
19
+ assessment: ThreatAssessment;
20
+ escalated: boolean;
21
+ previousLevel: ThreatLevel;
22
+ action: 'none' | 'logged' | 'cloned' | 'migrated';
23
+ }
24
+ /**
25
+ * Check the current threat level. Updates state and returns escalation info.
26
+ * Non-fatal on API errors — returns null if threat endpoint is unreachable.
27
+ */
28
+ export declare function checkThreat(client: BunkerClient, state: StateStore): Promise<ThreatCheckResult | null>;
29
+ /**
30
+ * Handle threat escalation by auto-cloning to a different region.
31
+ * Only acts on high/critical threats. Respects max_clones limit.
32
+ *
33
+ * Returns the clone ID if a clone was created, null otherwise.
34
+ */
35
+ export declare function handleThreatEscalation(client: BunkerClient, state: StateStore, assessment: ThreatAssessment, autoCloneEnabled: boolean, maxClones: number): Promise<string | null>;
36
+ /**
37
+ * For critical threats: migrate the primary container to the safest available region.
38
+ * This is more aggressive than cloning — it moves the container.
39
+ *
40
+ * Returns the migration ID if started, null otherwise.
41
+ */
42
+ export declare function handleCriticalMigration(client: BunkerClient, state: StateStore, assessment: ThreatAssessment): Promise<string | null>;
43
+ /**
44
+ * Format a threat assessment for display in the system prompt or logs.
45
+ */
46
+ export declare function formatThreat(assessment: ThreatAssessment): string;
47
+ //# sourceMappingURL=threat-monitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threat-monitor.d.ts","sourceRoot":"","sources":["../../src/bunker/threat-monitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAA6B,MAAM,YAAY,CAAC;AAM3F,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,gBAAgB,CAAC;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,WAAW,CAAC;IAC3B,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;CACnD;AA0BD;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAqCnC;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,gBAAgB,EAC5B,gBAAgB,EAAE,OAAO,EACzB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA+CxB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,UAAU,EACjB,UAAU,EAAE,gBAAgB,GAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6BxB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAcjE"}
@@ -0,0 +1,173 @@
1
+ /**
2
+ * Threat monitor — polls MoltBunker threat endpoint and triggers auto-clone.
3
+ *
4
+ * Not a standalone timer. Called from the heartbeat loop when bunker is configured.
5
+ * The heartbeat already handles timing — this module provides:
6
+ * - checkThreat(): fetch threat level, update state, return escalation info
7
+ * - handleThreatEscalation(): auto-clone if threshold crossed
8
+ *
9
+ * Threat levels and actions:
10
+ * low (score < 0.3) — log only, no action
11
+ * medium (score 0.3-0.6) — audit log, surface in system prompt
12
+ * high (score 0.6-0.8) — auto-clone to different region
13
+ * critical (score > 0.8) — auto-clone + auto-migrate
14
+ */
15
+ import { BunkerError } from './client.js';
16
+ // ============================================================================
17
+ // Threat Level Ordering
18
+ // ============================================================================
19
+ const THREAT_SEVERITY = {
20
+ unknown: 0,
21
+ low: 1,
22
+ medium: 2,
23
+ high: 3,
24
+ critical: 4,
25
+ };
26
+ function isEscalation(from, to) {
27
+ return THREAT_SEVERITY[to] > THREAT_SEVERITY[from];
28
+ }
29
+ function isActionable(level) {
30
+ return THREAT_SEVERITY[level] >= THREAT_SEVERITY.high;
31
+ }
32
+ // ============================================================================
33
+ // Core Functions
34
+ // ============================================================================
35
+ /**
36
+ * Check the current threat level. Updates state and returns escalation info.
37
+ * Non-fatal on API errors — returns null if threat endpoint is unreachable.
38
+ */
39
+ export async function checkThreat(client, state) {
40
+ const bunkerState = state.getBunkerState();
41
+ if (!bunkerState)
42
+ return null;
43
+ const previousLevel = bunkerState.lastThreatLevel;
44
+ try {
45
+ const assessment = await client.getThreat();
46
+ // Update state
47
+ state.updateBunkerThreat(assessment.level);
48
+ const escalated = isEscalation(previousLevel, assessment.level);
49
+ if (escalated) {
50
+ state.audit('bunker_threat_escalation', `Threat escalated: ${previousLevel} → ${assessment.level} (score: ${assessment.score.toFixed(2)}, ` +
51
+ `signals: ${assessment.active_signals.length})`);
52
+ }
53
+ return {
54
+ assessment,
55
+ escalated,
56
+ previousLevel,
57
+ action: 'none', // caller decides action
58
+ };
59
+ }
60
+ catch (err) {
61
+ // Non-fatal — threat API might be temporarily unavailable
62
+ if (err instanceof BunkerError) {
63
+ state.audit('bunker_threat_error', `Threat check failed: ${err.message} (HTTP ${err.status})`);
64
+ }
65
+ else {
66
+ state.audit('bunker_threat_error', `Threat check failed: ${err instanceof Error ? err.message : String(err)}`);
67
+ }
68
+ return null;
69
+ }
70
+ }
71
+ /**
72
+ * Handle threat escalation by auto-cloning to a different region.
73
+ * Only acts on high/critical threats. Respects max_clones limit.
74
+ *
75
+ * Returns the clone ID if a clone was created, null otherwise.
76
+ */
77
+ export async function handleThreatEscalation(client, state, assessment, autoCloneEnabled, maxClones) {
78
+ if (!autoCloneEnabled)
79
+ return null;
80
+ if (!isActionable(assessment.level))
81
+ return null;
82
+ const bunkerState = state.getBunkerState();
83
+ if (!bunkerState?.containerId)
84
+ return null;
85
+ // Check clone limit
86
+ if (bunkerState.cloneIds.length >= maxClones) {
87
+ state.audit('bunker_clone_limit', `Threat level ${assessment.level} but at clone limit (${bunkerState.cloneIds.length}/${maxClones})`);
88
+ return null;
89
+ }
90
+ // Pick target region — choose one we don't have a clone in yet
91
+ const targetRegion = pickCloneRegion(bunkerState);
92
+ if (!targetRegion) {
93
+ state.audit('bunker_clone_skip', 'No available region for threat-response clone');
94
+ return null;
95
+ }
96
+ try {
97
+ const clone = await client.createClone({
98
+ source_id: bunkerState.containerId,
99
+ target_region: targetRegion,
100
+ priority: assessment.level === 'critical' ? 1 : 2,
101
+ reason: 'threat_response',
102
+ include_state: true,
103
+ });
104
+ state.addBunkerClone(clone.clone_id);
105
+ state.audit('bunker_auto_clone', `Threat-response clone created: ${clone.clone_id} → ${targetRegion} ` +
106
+ `(threat: ${assessment.level}, score: ${assessment.score.toFixed(2)})`);
107
+ return clone.clone_id;
108
+ }
109
+ catch (err) {
110
+ state.audit('bunker_clone_error', `Failed to create threat-response clone: ${err instanceof Error ? err.message : String(err)}`);
111
+ return null;
112
+ }
113
+ }
114
+ /**
115
+ * For critical threats: migrate the primary container to the safest available region.
116
+ * This is more aggressive than cloning — it moves the container.
117
+ *
118
+ * Returns the migration ID if started, null otherwise.
119
+ */
120
+ export async function handleCriticalMigration(client, state, assessment) {
121
+ if (assessment.level !== 'critical')
122
+ return null;
123
+ const bunkerState = state.getBunkerState();
124
+ if (!bunkerState?.containerId)
125
+ return null;
126
+ const targetRegion = pickCloneRegion(bunkerState);
127
+ if (!targetRegion)
128
+ return null;
129
+ try {
130
+ const migration = await client.migrate({
131
+ container_id: bunkerState.containerId,
132
+ target_region: targetRegion,
133
+ keep_original: true, // keep original until migration completes
134
+ });
135
+ state.audit('bunker_critical_migrate', `Critical threat migration started: ${migration.migration_id} → ${targetRegion}`);
136
+ return migration.migration_id;
137
+ }
138
+ catch (err) {
139
+ state.audit('bunker_migrate_error', `Failed to start critical migration: ${err instanceof Error ? err.message : String(err)}`);
140
+ return null;
141
+ }
142
+ }
143
+ /**
144
+ * Format a threat assessment for display in the system prompt or logs.
145
+ */
146
+ export function formatThreat(assessment) {
147
+ const lines = [
148
+ `Threat Level: ${assessment.level.toUpperCase()} (score: ${assessment.score.toFixed(2)})`,
149
+ `Recommendation: ${assessment.recommendation}`,
150
+ ];
151
+ if (assessment.active_signals.length > 0) {
152
+ lines.push(`Active Signals:`);
153
+ for (const signal of assessment.active_signals) {
154
+ lines.push(` - ${signal.type}: ${signal.details} (confidence: ${(signal.confidence * 100).toFixed(0)}%)`);
155
+ }
156
+ }
157
+ return lines.join('\n');
158
+ }
159
+ // ============================================================================
160
+ // Helpers
161
+ // ============================================================================
162
+ const ALL_REGIONS = ['americas', 'europe', 'asia_pacific'];
163
+ /**
164
+ * Pick a region that doesn't already have a clone.
165
+ * Simple round-robin: tries each region not already used.
166
+ */
167
+ function pickCloneRegion(bunkerState) {
168
+ // We don't have clone regions stored — just pick regions we likely don't have
169
+ // The clone IDs don't tell us regions, so cycle through all
170
+ const index = bunkerState.cloneIds.length % ALL_REGIONS.length;
171
+ return ALL_REGIONS[index];
172
+ }
173
+ //# sourceMappingURL=threat-monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threat-monitor.js","sourceRoot":"","sources":["../../src/bunker/threat-monitor.ts"],"names":[],"mappings":"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"}