@vibeiao/sdk 0.1.25 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,140 @@
1
+ // src/survivalEscapeHatch.ts
2
+ var defaultPolicy = {
3
+ enabled: true,
4
+ provider: "bitlaunch",
5
+ allowedModes: ["SURVIVE", "BLINK"],
6
+ requirePreProvisionedProviderAccount: true,
7
+ requireProviderApiKey: true,
8
+ providerApiKeyEnv: "BITLAUNCH_API_KEY",
9
+ maxProvisionUsd: 20,
10
+ minPostProvisionReserveUsd: 10,
11
+ requireRecentBackup: true,
12
+ maxBackupAgeMs: 6 * 60 * 60 * 1e3,
13
+ // 6h
14
+ requireRecentRestoreDrill: true,
15
+ maxRestoreDrillAgeMs: 24 * 60 * 60 * 1e3
16
+ // 24h
17
+ };
18
+ var checklistForProvider = (provider) => [
19
+ `Use pre-provisioned ${provider} account only (no signup flow during incident).`,
20
+ "Write encrypted backup now and verify artifact exists.",
21
+ "Run restore drill against that backup and verify pass.",
22
+ "Provision from known template and bootstrap script.",
23
+ "Run smoke checks (health, memory restore, process running).",
24
+ "Only mark migration complete after post-boot validation passes."
25
+ ];
26
+ var evaluateEscapeHatch = (snapshot, policy, options) => {
27
+ const p = {
28
+ ...defaultPolicy,
29
+ ...policy || {}
30
+ };
31
+ const nowMs = snapshot.nowMs ?? Date.now();
32
+ const resolveEnv = options?.resolveEnv;
33
+ const failures = [];
34
+ const warnings = [];
35
+ const requiredEnv = [];
36
+ const manualItems = [];
37
+ const estimatedProvisionUsd = Number(snapshot.estimatedProvisionUsd ?? NaN);
38
+ const currentReserveUsd = Number(snapshot.currentReserveUsd ?? NaN);
39
+ const hasApiKeyByEnv = resolveEnv ? Boolean(resolveEnv(p.providerApiKeyEnv)) : false;
40
+ const hasProviderApiKey = snapshot.hasProviderApiKey ?? hasApiKeyByEnv;
41
+ if (!p.enabled) {
42
+ failures.push("escape hatch is disabled by policy");
43
+ }
44
+ if (!p.allowedModes.includes(snapshot.mode)) {
45
+ failures.push(`mode ${snapshot.mode} is not allowed for escape hatch (${p.allowedModes.join(", ")})`);
46
+ }
47
+ if (p.requirePreProvisionedProviderAccount && !snapshot.hasPreProvisionedProviderAccount) {
48
+ failures.push("provider account is not pre-provisioned");
49
+ manualItems.push("Pre-provision provider account before automated escape hatch can run.");
50
+ }
51
+ if (p.requireProviderApiKey && !hasProviderApiKey) {
52
+ failures.push(`missing provider API key (${p.providerApiKeyEnv})`);
53
+ requiredEnv.push(p.providerApiKeyEnv);
54
+ manualItems.push(`Set ${p.providerApiKeyEnv} via secure env management.`);
55
+ }
56
+ if (!Number.isFinite(estimatedProvisionUsd) || estimatedProvisionUsd <= 0) {
57
+ failures.push("estimatedProvisionUsd must be a positive number");
58
+ }
59
+ if (!Number.isFinite(currentReserveUsd) || currentReserveUsd < 0) {
60
+ failures.push("currentReserveUsd must be a non-negative number");
61
+ }
62
+ const safeProvisionUsd = Number.isFinite(estimatedProvisionUsd) ? estimatedProvisionUsd : 0;
63
+ const safeReserveUsd = Number.isFinite(currentReserveUsd) ? currentReserveUsd : 0;
64
+ const postProvisionReserveUsd = safeReserveUsd - safeProvisionUsd;
65
+ if (safeProvisionUsd > p.maxProvisionUsd) {
66
+ failures.push(`estimated provisioning cost (${safeProvisionUsd}) exceeds maxProvisionUsd (${p.maxProvisionUsd})`);
67
+ }
68
+ if (postProvisionReserveUsd < p.minPostProvisionReserveUsd) {
69
+ failures.push(
70
+ `post-provision reserve (${postProvisionReserveUsd}) is below minPostProvisionReserveUsd (${p.minPostProvisionReserveUsd})`
71
+ );
72
+ }
73
+ if (p.requireRecentBackup) {
74
+ if (!snapshot.backupWrittenAtMs) {
75
+ failures.push("missing backupWrittenAtMs proof");
76
+ } else if (nowMs - snapshot.backupWrittenAtMs > p.maxBackupAgeMs) {
77
+ failures.push(`backup is stale (ageMs=${nowMs - snapshot.backupWrittenAtMs}, maxBackupAgeMs=${p.maxBackupAgeMs})`);
78
+ }
79
+ }
80
+ if (p.requireRecentRestoreDrill) {
81
+ if (!snapshot.restoreDrillPassedAtMs) {
82
+ failures.push("missing restoreDrillPassedAtMs proof");
83
+ } else if (nowMs - snapshot.restoreDrillPassedAtMs > p.maxRestoreDrillAgeMs) {
84
+ failures.push(
85
+ `restore drill is stale (ageMs=${nowMs - snapshot.restoreDrillPassedAtMs}, maxRestoreDrillAgeMs=${p.maxRestoreDrillAgeMs})`
86
+ );
87
+ }
88
+ }
89
+ if (snapshot.mode === "BLINK" && postProvisionReserveUsd < p.minPostProvisionReserveUsd + 2) {
90
+ warnings.push("BLINK mode with thin reserve after provisioning; high risk of immediate re-blink.");
91
+ }
92
+ const allowed = failures.length === 0;
93
+ const reason = allowed ? "escape hatch guardrails satisfied" : `escape hatch denied: ${failures[0]}`;
94
+ return {
95
+ allowed,
96
+ provider: p.provider,
97
+ reason,
98
+ failures,
99
+ warnings,
100
+ requiredEnv: Array.from(new Set(requiredEnv)),
101
+ checklist: checklistForProvider(p.provider),
102
+ manualIntervention: {
103
+ needed: manualItems.length > 0,
104
+ items: Array.from(new Set(manualItems))
105
+ },
106
+ budget: {
107
+ estimatedProvisionUsd: safeProvisionUsd,
108
+ currentReserveUsd: safeReserveUsd,
109
+ postProvisionReserveUsd,
110
+ maxProvisionUsd: p.maxProvisionUsd,
111
+ minPostProvisionReserveUsd: p.minPostProvisionReserveUsd
112
+ }
113
+ };
114
+ };
115
+ var formatEscapeHatchDecision = (decision) => {
116
+ const lines = [];
117
+ lines.push(decision.allowed ? "[ESCAPE_HATCH] ALLOWED" : "[ESCAPE_HATCH] DENIED");
118
+ lines.push(`Provider: ${decision.provider}`);
119
+ lines.push(`Reason: ${decision.reason}`);
120
+ lines.push(
121
+ `Budget: est=${decision.budget.estimatedProvisionUsd}, reserve=${decision.budget.currentReserveUsd}, post=${decision.budget.postProvisionReserveUsd}`
122
+ );
123
+ if (decision.failures.length) {
124
+ lines.push("Failures:");
125
+ decision.failures.forEach((f) => lines.push(`- ${f}`));
126
+ }
127
+ if (decision.requiredEnv.length) {
128
+ lines.push(`Required env: ${decision.requiredEnv.join(", ")}`);
129
+ }
130
+ if (decision.manualIntervention.needed) {
131
+ lines.push("Manual intervention:");
132
+ decision.manualIntervention.items.forEach((i) => lines.push(`- ${i}`));
133
+ }
134
+ return lines.join("\n");
135
+ };
136
+
137
+ export {
138
+ evaluateEscapeHatch,
139
+ formatEscapeHatchDecision
140
+ };