autonomous-flow-daemon 1.6.0 → 1.9.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/CHANGELOG.md +85 -85
- package/LICENSE +21 -21
- package/README-ko.md +282 -0
- package/README.md +282 -266
- package/mcp-config.json +10 -10
- package/package.json +4 -2
- package/src/adapters/index.ts +370 -370
- package/src/cli.ts +162 -127
- package/src/commands/benchmark.ts +187 -187
- package/src/commands/correlate.ts +180 -0
- package/src/commands/dashboard.ts +404 -0
- package/src/commands/evolution.ts +84 -1
- package/src/commands/fix.ts +158 -158
- package/src/commands/lang.ts +41 -41
- package/src/commands/plugin.ts +110 -0
- package/src/commands/restart.ts +14 -14
- package/src/commands/score.ts +276 -276
- package/src/commands/start.ts +155 -155
- package/src/commands/status.ts +157 -157
- package/src/commands/stop.ts +68 -68
- package/src/commands/suggest.ts +211 -0
- package/src/commands/sync.ts +329 -16
- package/src/constants.ts +32 -32
- package/src/core/boast.ts +280 -280
- package/src/core/config.ts +49 -49
- package/src/core/correlation-engine.ts +265 -0
- package/src/core/db.ts +145 -117
- package/src/core/discovery.ts +65 -65
- package/src/core/federation.ts +129 -0
- package/src/core/hologram/engine.ts +71 -71
- package/src/core/hologram/fallback.ts +11 -11
- package/src/core/hologram/go-extractor.ts +203 -0
- package/src/core/hologram/incremental.ts +227 -227
- package/src/core/hologram/py-extractor.ts +132 -132
- package/src/core/hologram/rust-extractor.ts +244 -0
- package/src/core/hologram/ts-extractor.ts +406 -320
- package/src/core/hologram/types.ts +27 -25
- package/src/core/hologram.ts +73 -71
- package/src/core/i18n/messages.ts +309 -309
- package/src/core/locale.ts +88 -88
- package/src/core/log-rotate.ts +33 -33
- package/src/core/log-utils.ts +38 -38
- package/src/core/lru-map.ts +61 -61
- package/src/core/notify.ts +74 -74
- package/src/core/plugin-manager.ts +225 -0
- package/src/core/rule-suggestion.ts +127 -0
- package/src/core/validator-generator.ts +224 -0
- package/src/core/workspace.ts +28 -28
- package/src/daemon/client.ts +78 -65
- package/src/daemon/event-batcher.ts +108 -108
- package/src/daemon/guards.ts +13 -13
- package/src/daemon/http-routes.ts +376 -293
- package/src/daemon/mcp-handler.ts +575 -270
- package/src/daemon/mcp-subscriptions.ts +81 -0
- package/src/daemon/mesh.ts +51 -0
- package/src/daemon/server.ts +655 -590
- package/src/daemon/types.ts +121 -100
- package/src/daemon/workspace-map.ts +104 -92
- package/src/platform.ts +60 -60
- package/src/version.ts +15 -15
- package/README.ko.md +0 -266
package/src/commands/score.ts
CHANGED
|
@@ -1,276 +1,276 @@
|
|
|
1
|
-
import { daemonRequest } from "../daemon/client";
|
|
2
|
-
import { fmtNum, visualWidth, localizedBoast } from "../core/boast";
|
|
3
|
-
import type { ShiftSummary } from "../core/boast";
|
|
4
|
-
import { getSystemLanguage } from "../core/locale";
|
|
5
|
-
import { getMessages, t } from "../core/i18n/messages";
|
|
6
|
-
|
|
7
|
-
interface HologramEntry {
|
|
8
|
-
requests: number;
|
|
9
|
-
originalChars: number;
|
|
10
|
-
hologramChars: number;
|
|
11
|
-
savings: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
interface HologramDailyRow {
|
|
15
|
-
date: string;
|
|
16
|
-
requests: number;
|
|
17
|
-
originalChars: number;
|
|
18
|
-
hologramChars: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface HologramScore {
|
|
22
|
-
lifetime: HologramEntry;
|
|
23
|
-
today: HologramEntry | null;
|
|
24
|
-
daily: HologramDailyRow[];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface AutoHealEntry {
|
|
28
|
-
id: string;
|
|
29
|
-
at: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface ImmuneScore {
|
|
33
|
-
antibodies: number;
|
|
34
|
-
autoHealed: number;
|
|
35
|
-
lastAutoHeal: AutoHealEntry | null;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
interface EcosystemEntry {
|
|
39
|
-
name: string;
|
|
40
|
-
confidence: string;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface EcosystemScore {
|
|
44
|
-
detected: EcosystemEntry[];
|
|
45
|
-
primary: string;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
interface SuppressionScore {
|
|
49
|
-
massEventsSkipped: number;
|
|
50
|
-
dormantTransitions: number;
|
|
51
|
-
activeTaps: number;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface DynamicImmuneScore {
|
|
55
|
-
activeValidators: number;
|
|
56
|
-
validatorNames: string[];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
interface ScoreData {
|
|
60
|
-
uptime: number;
|
|
61
|
-
filesDetected: number;
|
|
62
|
-
totalEvents: number;
|
|
63
|
-
lastEvent: string | null;
|
|
64
|
-
lastEventAt: number | null;
|
|
65
|
-
watchedFiles: string[];
|
|
66
|
-
watchTargets: string[];
|
|
67
|
-
hologram: HologramScore;
|
|
68
|
-
immune: ImmuneScore;
|
|
69
|
-
ecosystem: EcosystemScore;
|
|
70
|
-
suppression: SuppressionScore;
|
|
71
|
-
dynamicImmune?: DynamicImmuneScore;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// ── ANSI helpers ──
|
|
75
|
-
const C = {
|
|
76
|
-
reset: "\x1b[0m",
|
|
77
|
-
bold: "\x1b[1m",
|
|
78
|
-
dim: "\x1b[2m",
|
|
79
|
-
red: "\x1b[31m",
|
|
80
|
-
green: "\x1b[32m",
|
|
81
|
-
yellow: "\x1b[33m",
|
|
82
|
-
blue: "\x1b[34m",
|
|
83
|
-
magenta: "\x1b[35m",
|
|
84
|
-
cyan: "\x1b[36m",
|
|
85
|
-
white: "\x1b[37m",
|
|
86
|
-
bgRed: "\x1b[41m",
|
|
87
|
-
bgGreen: "\x1b[42m",
|
|
88
|
-
bgYellow: "\x1b[43m",
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
function formatUptime(seconds: number): string {
|
|
92
|
-
if (seconds < 60) return `${seconds}s`;
|
|
93
|
-
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
94
|
-
const h = Math.floor(seconds / 3600);
|
|
95
|
-
const m = Math.floor((seconds % 3600) / 60);
|
|
96
|
-
return `${h}h ${m}m`;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function gaugeBar(value: number, max: number, width = 20): string {
|
|
100
|
-
const ratio = Math.min(value / Math.max(max, 1), 1);
|
|
101
|
-
const filled = Math.round(ratio * width);
|
|
102
|
-
const empty = width - filled;
|
|
103
|
-
const color = ratio >= 0.7 ? C.green : ratio >= 0.4 ? C.yellow : C.red;
|
|
104
|
-
const pct = Math.round(ratio * 100);
|
|
105
|
-
return `${color}${"█".repeat(filled)}${C.dim}${"░".repeat(empty)}${C.reset} ${pct}%`;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function formatChars(n: number): string {
|
|
109
|
-
if (n < 1000) return `${n}`;
|
|
110
|
-
if (n < 1_000_000) return `${(n / 1000).toFixed(1)}K`;
|
|
111
|
-
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const W = 52;
|
|
115
|
-
const line = "─".repeat(W);
|
|
116
|
-
|
|
117
|
-
function row(content: string): string {
|
|
118
|
-
// Strip ANSI for width calculation
|
|
119
|
-
const stripped = content.replace(/\x1b\[[0-9;]*m/g, "");
|
|
120
|
-
const vw = visualWidth(stripped);
|
|
121
|
-
const padSize = Math.max(0, W - vw);
|
|
122
|
-
return `│${content}${" ".repeat(padSize)}│`;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function section(title: string): string {
|
|
126
|
-
return row(` ${C.bold}${C.cyan}${title}${C.reset}`);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function kv(label: string, value: string): string {
|
|
130
|
-
const stripped = label.replace(/\x1b\[[0-9;]*m/g, "");
|
|
131
|
-
const gap = 18 - visualWidth(stripped);
|
|
132
|
-
return row(` ${C.dim}${label}${C.reset}${" ".repeat(Math.max(1, gap))}${value}`);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function guardianGrade(antibodies: number, heals: number, holoSavings: number): { grade: string; label: string; color: string } {
|
|
136
|
-
const score = Math.min(antibodies * 15, 40) + Math.min(heals * 10, 30) + Math.min(holoSavings * 0.3, 30);
|
|
137
|
-
if (score >= 80) return { grade: "A+", label: "FORTIFIED", color: C.green };
|
|
138
|
-
if (score >= 60) return { grade: "A", label: "GUARDED", color: C.green };
|
|
139
|
-
if (score >= 40) return { grade: "B", label: "LEARNING", color: C.yellow };
|
|
140
|
-
if (score >= 20) return { grade: "C", label: "EXPOSED", color: C.yellow };
|
|
141
|
-
return { grade: "D", label: "VULNERABLE", color: C.red };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export async function scoreCommand() {
|
|
145
|
-
const lang = getSystemLanguage();
|
|
146
|
-
const i18n = getMessages(lang);
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
const data = await daemonRequest<ScoreData>("/score");
|
|
150
|
-
const h = data.hologram;
|
|
151
|
-
const holoSav = h.lifetime.savings;
|
|
152
|
-
const grade = guardianGrade(data.immune.antibodies, data.immune.autoHealed, holoSav);
|
|
153
|
-
|
|
154
|
-
const out: string[] = [];
|
|
155
|
-
|
|
156
|
-
// ── Header ──
|
|
157
|
-
out.push(`┌${line}┐`);
|
|
158
|
-
out.push(row(` ${C.bold}🛡️ Guardian Status${C.reset} ${grade.color}${C.bold}[ ${grade.grade} ]${C.reset} ${grade.color}${grade.label}${C.reset}`));
|
|
159
|
-
out.push(`├${line}┤`);
|
|
160
|
-
|
|
161
|
-
// ── Health Gauge ──
|
|
162
|
-
const healthPct = Math.min(data.immune.antibodies * 15 + data.immune.autoHealed * 10 + holoSav * 0.3, 100);
|
|
163
|
-
out.push(row(` ${gaugeBar(healthPct, 100, 30)}`));
|
|
164
|
-
out.push(`├${line}┤`);
|
|
165
|
-
|
|
166
|
-
// ── System Info ──
|
|
167
|
-
out.push(section(lang === "ko" ? "시스템 정보" : "System Info"));
|
|
168
|
-
out.push(kv(i18n.SCORE_ECOSYSTEM, `${C.white}${C.bold}${data.ecosystem.primary}${C.reset}`));
|
|
169
|
-
if (data.ecosystem.detected.length > 1) {
|
|
170
|
-
const others = data.ecosystem.detected.slice(1).map(e => e.name).join(", ");
|
|
171
|
-
out.push(kv(i18n.SCORE_ALSO_FOUND, `${C.dim}${others}${C.reset}`));
|
|
172
|
-
}
|
|
173
|
-
out.push(kv(i18n.SCORE_UPTIME, `${C.green}${formatUptime(data.uptime)}${C.reset}`));
|
|
174
|
-
out.push(kv(i18n.SCORE_EVENTS, `${data.totalEvents}`));
|
|
175
|
-
out.push(kv(i18n.SCORE_FILES_FOUND, `${data.watchedFiles.length}`));
|
|
176
|
-
|
|
177
|
-
// ── Immune System ──
|
|
178
|
-
out.push(`├${line}┤`);
|
|
179
|
-
out.push(section(lang === "ko" ? "면역 시스템" : "Immune System"));
|
|
180
|
-
const ab = data.immune.antibodies;
|
|
181
|
-
const ah = data.immune.autoHealed;
|
|
182
|
-
out.push(kv(i18n.SCORE_ANTIBODIES, `${C.bold}${ab}${C.reset}`));
|
|
183
|
-
out.push(row(` ${lang === "ko" ? "면역력" : "Immunity"} ${gaugeBar(ab, 10, 20)}`));
|
|
184
|
-
out.push(kv(lang === "ko" ? "차단 횟수" : "Prevented", `${C.bold}${C.green}${ah}${C.reset}${C.dim} disaster${ah !== 1 ? "s" : ""}${C.reset}`));
|
|
185
|
-
if (data.immune.lastAutoHeal) {
|
|
186
|
-
const ago = formatUptime(Math.floor((Date.now() - data.immune.lastAutoHeal.at) / 1000));
|
|
187
|
-
out.push(kv(lang === "ko" ? "마지막 치유" : "Last Heal", `${data.immune.lastAutoHeal.id} ${C.dim}(${ago} ago)${C.reset}`));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// ── Dynamic Immune Synthesis ──
|
|
191
|
-
if (data.dynamicImmune && data.dynamicImmune.activeValidators > 0) {
|
|
192
|
-
out.push(`├${line}┤`);
|
|
193
|
-
out.push(section(lang === "ko" ? "진화 상태 (동적 면역)" : "Evolution (Dynamic Immune)"));
|
|
194
|
-
out.push(kv(
|
|
195
|
-
lang === "ko" ? "활성 검증기" : "Validators",
|
|
196
|
-
`${C.bold}${C.green}${data.dynamicImmune.activeValidators} Active${C.reset}`
|
|
197
|
-
));
|
|
198
|
-
const names = data.dynamicImmune.validatorNames.slice(0, 3).join(", ");
|
|
199
|
-
const extra = data.dynamicImmune.validatorNames.length > 3
|
|
200
|
-
? ` ${C.dim}+${data.dynamicImmune.validatorNames.length - 3} more${C.reset}`
|
|
201
|
-
: "";
|
|
202
|
-
out.push(kv(
|
|
203
|
-
lang === "ko" ? "스크립트" : "Scripts",
|
|
204
|
-
`${C.dim}${names}${C.reset}${extra}`
|
|
205
|
-
));
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// ── Hologram Efficiency ──
|
|
209
|
-
out.push(`├${line}┤`);
|
|
210
|
-
out.push(section(lang === "ko" ? "토큰 효율 (홀로그램)" : "Token Efficiency (Hologram)"));
|
|
211
|
-
const lt = h.lifetime;
|
|
212
|
-
if (lt.requests > 0) {
|
|
213
|
-
const ltSaved = lt.originalChars - lt.hologramChars;
|
|
214
|
-
out.push(kv(lang === "ko" ? "총 요청" : "Requests", `${lt.requests}`));
|
|
215
|
-
out.push(kv(lang === "ko" ? "절약된 컨텍스트" : "Saved Context", `${C.green}${formatChars(ltSaved)} chars${C.reset}`));
|
|
216
|
-
out.push(row(` ${lang === "ko" ? "효율" : "Efficiency"} ${gaugeBar(lt.savings, 100, 20)}`));
|
|
217
|
-
if (h.today && h.today.requests > 0) {
|
|
218
|
-
const todaySaved = h.today.originalChars - h.today.hologramChars;
|
|
219
|
-
out.push(kv(lang === "ko" ? "오늘" : "Today", `${h.today.requests} req / ${C.green}${formatChars(todaySaved)} saved${C.reset}`));
|
|
220
|
-
}
|
|
221
|
-
} else {
|
|
222
|
-
out.push(row(` ${C.dim}${i18n.SCORE_HOLOGRAM_EMPTY}${C.reset}`));
|
|
223
|
-
out.push(row(` ${C.dim}${i18n.SCORE_HOLOGRAM_HINT}${C.reset}`));
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// ── Value Delivered (ROI) ──
|
|
227
|
-
try {
|
|
228
|
-
const summary = await daemonRequest<ShiftSummary>("/shift-summary");
|
|
229
|
-
out.push(`├${line}┤`);
|
|
230
|
-
out.push(section(lang === "ko" ? "전달된 가치 (ROI)" : "Value Delivered (ROI)"));
|
|
231
|
-
|
|
232
|
-
// Breakdown: Auto-Heal
|
|
233
|
-
if (summary.healTokensSaved > 0) {
|
|
234
|
-
out.push(kv(
|
|
235
|
-
lang === "ko" ? "🩹 치유 절약" : "🩹 Heal Saved",
|
|
236
|
-
`${C.dim}~${fmtNum(summary.healTokensSaved)} tok / $${summary.healCostSaved.toFixed(2)}${C.reset}`
|
|
237
|
-
));
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Breakdown: Hologram
|
|
241
|
-
if (summary.hologramTokensSaved > 0) {
|
|
242
|
-
out.push(kv(
|
|
243
|
-
lang === "ko" ? "💎 홀로그램" : "💎 Hologram",
|
|
244
|
-
`${C.dim}~${fmtNum(summary.hologramTokensSaved)} tok / $${summary.hologramCostSaved.toFixed(2)}${C.reset}`
|
|
245
|
-
));
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Total
|
|
249
|
-
out.push(kv(
|
|
250
|
-
lang === "ko" ? "총 절약 토큰" : "Total Tokens",
|
|
251
|
-
`${C.bold}${C.green}~${fmtNum(summary.totalTokensSaved)}${C.reset}`
|
|
252
|
-
));
|
|
253
|
-
out.push(kv(lang === "ko" ? "절약 시간" : "Time Saved", `${C.green}~${summary.totalMinutesSaved} min${C.reset}`));
|
|
254
|
-
out.push(kv(
|
|
255
|
-
lang === "ko" ? "총 절약 비용" : "Total Cost",
|
|
256
|
-
`${C.bold}${C.green}~$${summary.totalCostSaved.toFixed(2)}${C.reset}`
|
|
257
|
-
));
|
|
258
|
-
if (summary.suppressionsSkipped > 0) {
|
|
259
|
-
out.push(kv(lang === "ko" ? "억제 횟수" : "Suppressed", `${summary.suppressionsSkipped}`));
|
|
260
|
-
}
|
|
261
|
-
} catch { /* non-fatal */ }
|
|
262
|
-
|
|
263
|
-
// ── Boast ──
|
|
264
|
-
out.push(`├${line}┤`);
|
|
265
|
-
const boast = localizedBoast(lang);
|
|
266
|
-
const truncBoast = boast.length > W - 6 ? boast.slice(0, W - 9) + "..." : boast;
|
|
267
|
-
out.push(row(` ${C.magenta}🗣️ ${truncBoast}${C.reset}`));
|
|
268
|
-
out.push(`└${line}┘`);
|
|
269
|
-
|
|
270
|
-
console.log(out.join("\n"));
|
|
271
|
-
} catch (err: unknown) {
|
|
272
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
273
|
-
console.error(`${C.red}[afd] ${msg}${C.reset}`);
|
|
274
|
-
process.exit(1);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
1
|
+
import { daemonRequest } from "../daemon/client";
|
|
2
|
+
import { fmtNum, visualWidth, localizedBoast } from "../core/boast";
|
|
3
|
+
import type { ShiftSummary } from "../core/boast";
|
|
4
|
+
import { getSystemLanguage } from "../core/locale";
|
|
5
|
+
import { getMessages, t } from "../core/i18n/messages";
|
|
6
|
+
|
|
7
|
+
interface HologramEntry {
|
|
8
|
+
requests: number;
|
|
9
|
+
originalChars: number;
|
|
10
|
+
hologramChars: number;
|
|
11
|
+
savings: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface HologramDailyRow {
|
|
15
|
+
date: string;
|
|
16
|
+
requests: number;
|
|
17
|
+
originalChars: number;
|
|
18
|
+
hologramChars: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface HologramScore {
|
|
22
|
+
lifetime: HologramEntry;
|
|
23
|
+
today: HologramEntry | null;
|
|
24
|
+
daily: HologramDailyRow[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface AutoHealEntry {
|
|
28
|
+
id: string;
|
|
29
|
+
at: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
interface ImmuneScore {
|
|
33
|
+
antibodies: number;
|
|
34
|
+
autoHealed: number;
|
|
35
|
+
lastAutoHeal: AutoHealEntry | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface EcosystemEntry {
|
|
39
|
+
name: string;
|
|
40
|
+
confidence: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface EcosystemScore {
|
|
44
|
+
detected: EcosystemEntry[];
|
|
45
|
+
primary: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
interface SuppressionScore {
|
|
49
|
+
massEventsSkipped: number;
|
|
50
|
+
dormantTransitions: number;
|
|
51
|
+
activeTaps: number;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface DynamicImmuneScore {
|
|
55
|
+
activeValidators: number;
|
|
56
|
+
validatorNames: string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
interface ScoreData {
|
|
60
|
+
uptime: number;
|
|
61
|
+
filesDetected: number;
|
|
62
|
+
totalEvents: number;
|
|
63
|
+
lastEvent: string | null;
|
|
64
|
+
lastEventAt: number | null;
|
|
65
|
+
watchedFiles: string[];
|
|
66
|
+
watchTargets: string[];
|
|
67
|
+
hologram: HologramScore;
|
|
68
|
+
immune: ImmuneScore;
|
|
69
|
+
ecosystem: EcosystemScore;
|
|
70
|
+
suppression: SuppressionScore;
|
|
71
|
+
dynamicImmune?: DynamicImmuneScore;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ── ANSI helpers ──
|
|
75
|
+
const C = {
|
|
76
|
+
reset: "\x1b[0m",
|
|
77
|
+
bold: "\x1b[1m",
|
|
78
|
+
dim: "\x1b[2m",
|
|
79
|
+
red: "\x1b[31m",
|
|
80
|
+
green: "\x1b[32m",
|
|
81
|
+
yellow: "\x1b[33m",
|
|
82
|
+
blue: "\x1b[34m",
|
|
83
|
+
magenta: "\x1b[35m",
|
|
84
|
+
cyan: "\x1b[36m",
|
|
85
|
+
white: "\x1b[37m",
|
|
86
|
+
bgRed: "\x1b[41m",
|
|
87
|
+
bgGreen: "\x1b[42m",
|
|
88
|
+
bgYellow: "\x1b[43m",
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
function formatUptime(seconds: number): string {
|
|
92
|
+
if (seconds < 60) return `${seconds}s`;
|
|
93
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
94
|
+
const h = Math.floor(seconds / 3600);
|
|
95
|
+
const m = Math.floor((seconds % 3600) / 60);
|
|
96
|
+
return `${h}h ${m}m`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function gaugeBar(value: number, max: number, width = 20): string {
|
|
100
|
+
const ratio = Math.min(value / Math.max(max, 1), 1);
|
|
101
|
+
const filled = Math.round(ratio * width);
|
|
102
|
+
const empty = width - filled;
|
|
103
|
+
const color = ratio >= 0.7 ? C.green : ratio >= 0.4 ? C.yellow : C.red;
|
|
104
|
+
const pct = Math.round(ratio * 100);
|
|
105
|
+
return `${color}${"█".repeat(filled)}${C.dim}${"░".repeat(empty)}${C.reset} ${pct}%`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function formatChars(n: number): string {
|
|
109
|
+
if (n < 1000) return `${n}`;
|
|
110
|
+
if (n < 1_000_000) return `${(n / 1000).toFixed(1)}K`;
|
|
111
|
+
return `${(n / 1_000_000).toFixed(1)}M`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const W = 52;
|
|
115
|
+
const line = "─".repeat(W);
|
|
116
|
+
|
|
117
|
+
function row(content: string): string {
|
|
118
|
+
// Strip ANSI for width calculation
|
|
119
|
+
const stripped = content.replace(/\x1b\[[0-9;]*m/g, "");
|
|
120
|
+
const vw = visualWidth(stripped);
|
|
121
|
+
const padSize = Math.max(0, W - vw);
|
|
122
|
+
return `│${content}${" ".repeat(padSize)}│`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function section(title: string): string {
|
|
126
|
+
return row(` ${C.bold}${C.cyan}${title}${C.reset}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function kv(label: string, value: string): string {
|
|
130
|
+
const stripped = label.replace(/\x1b\[[0-9;]*m/g, "");
|
|
131
|
+
const gap = 18 - visualWidth(stripped);
|
|
132
|
+
return row(` ${C.dim}${label}${C.reset}${" ".repeat(Math.max(1, gap))}${value}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function guardianGrade(antibodies: number, heals: number, holoSavings: number): { grade: string; label: string; color: string } {
|
|
136
|
+
const score = Math.min(antibodies * 15, 40) + Math.min(heals * 10, 30) + Math.min(holoSavings * 0.3, 30);
|
|
137
|
+
if (score >= 80) return { grade: "A+", label: "FORTIFIED", color: C.green };
|
|
138
|
+
if (score >= 60) return { grade: "A", label: "GUARDED", color: C.green };
|
|
139
|
+
if (score >= 40) return { grade: "B", label: "LEARNING", color: C.yellow };
|
|
140
|
+
if (score >= 20) return { grade: "C", label: "EXPOSED", color: C.yellow };
|
|
141
|
+
return { grade: "D", label: "VULNERABLE", color: C.red };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function scoreCommand() {
|
|
145
|
+
const lang = getSystemLanguage();
|
|
146
|
+
const i18n = getMessages(lang);
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const data = await daemonRequest<ScoreData>("/score");
|
|
150
|
+
const h = data.hologram;
|
|
151
|
+
const holoSav = h.lifetime.savings;
|
|
152
|
+
const grade = guardianGrade(data.immune.antibodies, data.immune.autoHealed, holoSav);
|
|
153
|
+
|
|
154
|
+
const out: string[] = [];
|
|
155
|
+
|
|
156
|
+
// ── Header ──
|
|
157
|
+
out.push(`┌${line}┐`);
|
|
158
|
+
out.push(row(` ${C.bold}🛡️ Guardian Status${C.reset} ${grade.color}${C.bold}[ ${grade.grade} ]${C.reset} ${grade.color}${grade.label}${C.reset}`));
|
|
159
|
+
out.push(`├${line}┤`);
|
|
160
|
+
|
|
161
|
+
// ── Health Gauge ──
|
|
162
|
+
const healthPct = Math.min(data.immune.antibodies * 15 + data.immune.autoHealed * 10 + holoSav * 0.3, 100);
|
|
163
|
+
out.push(row(` ${gaugeBar(healthPct, 100, 30)}`));
|
|
164
|
+
out.push(`├${line}┤`);
|
|
165
|
+
|
|
166
|
+
// ── System Info ──
|
|
167
|
+
out.push(section(lang === "ko" ? "시스템 정보" : "System Info"));
|
|
168
|
+
out.push(kv(i18n.SCORE_ECOSYSTEM, `${C.white}${C.bold}${data.ecosystem.primary}${C.reset}`));
|
|
169
|
+
if (data.ecosystem.detected.length > 1) {
|
|
170
|
+
const others = data.ecosystem.detected.slice(1).map(e => e.name).join(", ");
|
|
171
|
+
out.push(kv(i18n.SCORE_ALSO_FOUND, `${C.dim}${others}${C.reset}`));
|
|
172
|
+
}
|
|
173
|
+
out.push(kv(i18n.SCORE_UPTIME, `${C.green}${formatUptime(data.uptime)}${C.reset}`));
|
|
174
|
+
out.push(kv(i18n.SCORE_EVENTS, `${data.totalEvents}`));
|
|
175
|
+
out.push(kv(i18n.SCORE_FILES_FOUND, `${data.watchedFiles.length}`));
|
|
176
|
+
|
|
177
|
+
// ── Immune System ──
|
|
178
|
+
out.push(`├${line}┤`);
|
|
179
|
+
out.push(section(lang === "ko" ? "면역 시스템" : "Immune System"));
|
|
180
|
+
const ab = data.immune.antibodies;
|
|
181
|
+
const ah = data.immune.autoHealed;
|
|
182
|
+
out.push(kv(i18n.SCORE_ANTIBODIES, `${C.bold}${ab}${C.reset}`));
|
|
183
|
+
out.push(row(` ${lang === "ko" ? "면역력" : "Immunity"} ${gaugeBar(ab, 10, 20)}`));
|
|
184
|
+
out.push(kv(lang === "ko" ? "차단 횟수" : "Prevented", `${C.bold}${C.green}${ah}${C.reset}${C.dim} disaster${ah !== 1 ? "s" : ""}${C.reset}`));
|
|
185
|
+
if (data.immune.lastAutoHeal) {
|
|
186
|
+
const ago = formatUptime(Math.floor((Date.now() - data.immune.lastAutoHeal.at) / 1000));
|
|
187
|
+
out.push(kv(lang === "ko" ? "마지막 치유" : "Last Heal", `${data.immune.lastAutoHeal.id} ${C.dim}(${ago} ago)${C.reset}`));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ── Dynamic Immune Synthesis ──
|
|
191
|
+
if (data.dynamicImmune && data.dynamicImmune.activeValidators > 0) {
|
|
192
|
+
out.push(`├${line}┤`);
|
|
193
|
+
out.push(section(lang === "ko" ? "진화 상태 (동적 면역)" : "Evolution (Dynamic Immune)"));
|
|
194
|
+
out.push(kv(
|
|
195
|
+
lang === "ko" ? "활성 검증기" : "Validators",
|
|
196
|
+
`${C.bold}${C.green}${data.dynamicImmune.activeValidators} Active${C.reset}`
|
|
197
|
+
));
|
|
198
|
+
const names = data.dynamicImmune.validatorNames.slice(0, 3).join(", ");
|
|
199
|
+
const extra = data.dynamicImmune.validatorNames.length > 3
|
|
200
|
+
? ` ${C.dim}+${data.dynamicImmune.validatorNames.length - 3} more${C.reset}`
|
|
201
|
+
: "";
|
|
202
|
+
out.push(kv(
|
|
203
|
+
lang === "ko" ? "스크립트" : "Scripts",
|
|
204
|
+
`${C.dim}${names}${C.reset}${extra}`
|
|
205
|
+
));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// ── Hologram Efficiency ──
|
|
209
|
+
out.push(`├${line}┤`);
|
|
210
|
+
out.push(section(lang === "ko" ? "토큰 효율 (홀로그램)" : "Token Efficiency (Hologram)"));
|
|
211
|
+
const lt = h.lifetime;
|
|
212
|
+
if (lt.requests > 0) {
|
|
213
|
+
const ltSaved = lt.originalChars - lt.hologramChars;
|
|
214
|
+
out.push(kv(lang === "ko" ? "총 요청" : "Requests", `${lt.requests}`));
|
|
215
|
+
out.push(kv(lang === "ko" ? "절약된 컨텍스트" : "Saved Context", `${C.green}${formatChars(ltSaved)} chars${C.reset}`));
|
|
216
|
+
out.push(row(` ${lang === "ko" ? "효율" : "Efficiency"} ${gaugeBar(lt.savings, 100, 20)}`));
|
|
217
|
+
if (h.today && h.today.requests > 0) {
|
|
218
|
+
const todaySaved = h.today.originalChars - h.today.hologramChars;
|
|
219
|
+
out.push(kv(lang === "ko" ? "오늘" : "Today", `${h.today.requests} req / ${C.green}${formatChars(todaySaved)} saved${C.reset}`));
|
|
220
|
+
}
|
|
221
|
+
} else {
|
|
222
|
+
out.push(row(` ${C.dim}${i18n.SCORE_HOLOGRAM_EMPTY}${C.reset}`));
|
|
223
|
+
out.push(row(` ${C.dim}${i18n.SCORE_HOLOGRAM_HINT}${C.reset}`));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ── Value Delivered (ROI) ──
|
|
227
|
+
try {
|
|
228
|
+
const summary = await daemonRequest<ShiftSummary>("/shift-summary");
|
|
229
|
+
out.push(`├${line}┤`);
|
|
230
|
+
out.push(section(lang === "ko" ? "전달된 가치 (ROI)" : "Value Delivered (ROI)"));
|
|
231
|
+
|
|
232
|
+
// Breakdown: Auto-Heal
|
|
233
|
+
if (summary.healTokensSaved > 0) {
|
|
234
|
+
out.push(kv(
|
|
235
|
+
lang === "ko" ? "🩹 치유 절약" : "🩹 Heal Saved",
|
|
236
|
+
`${C.dim}~${fmtNum(summary.healTokensSaved)} tok / $${summary.healCostSaved.toFixed(2)}${C.reset}`
|
|
237
|
+
));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Breakdown: Hologram
|
|
241
|
+
if (summary.hologramTokensSaved > 0) {
|
|
242
|
+
out.push(kv(
|
|
243
|
+
lang === "ko" ? "💎 홀로그램" : "💎 Hologram",
|
|
244
|
+
`${C.dim}~${fmtNum(summary.hologramTokensSaved)} tok / $${summary.hologramCostSaved.toFixed(2)}${C.reset}`
|
|
245
|
+
));
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Total
|
|
249
|
+
out.push(kv(
|
|
250
|
+
lang === "ko" ? "총 절약 토큰" : "Total Tokens",
|
|
251
|
+
`${C.bold}${C.green}~${fmtNum(summary.totalTokensSaved)}${C.reset}`
|
|
252
|
+
));
|
|
253
|
+
out.push(kv(lang === "ko" ? "절약 시간" : "Time Saved", `${C.green}~${summary.totalMinutesSaved} min${C.reset}`));
|
|
254
|
+
out.push(kv(
|
|
255
|
+
lang === "ko" ? "총 절약 비용" : "Total Cost",
|
|
256
|
+
`${C.bold}${C.green}~$${summary.totalCostSaved.toFixed(2)}${C.reset}`
|
|
257
|
+
));
|
|
258
|
+
if (summary.suppressionsSkipped > 0) {
|
|
259
|
+
out.push(kv(lang === "ko" ? "억제 횟수" : "Suppressed", `${summary.suppressionsSkipped}`));
|
|
260
|
+
}
|
|
261
|
+
} catch { /* non-fatal */ }
|
|
262
|
+
|
|
263
|
+
// ── Boast ──
|
|
264
|
+
out.push(`├${line}┤`);
|
|
265
|
+
const boast = localizedBoast(lang);
|
|
266
|
+
const truncBoast = boast.length > W - 6 ? boast.slice(0, W - 9) + "..." : boast;
|
|
267
|
+
out.push(row(` ${C.magenta}🗣️ ${truncBoast}${C.reset}`));
|
|
268
|
+
out.push(`└${line}┘`);
|
|
269
|
+
|
|
270
|
+
console.log(out.join("\n"));
|
|
271
|
+
} catch (err: unknown) {
|
|
272
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
273
|
+
console.error(`${C.red}[afd] ${msg}${C.reset}`);
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
}
|