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/fix.ts
CHANGED
|
@@ -1,158 +1,158 @@
|
|
|
1
|
-
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
2
|
-
import { dirname } from "path";
|
|
3
|
-
import { daemonRequest } from "../daemon/client";
|
|
4
|
-
import type { Symptom, PatchOp, DiagnosisResult } from "../core/immune";
|
|
5
|
-
|
|
6
|
-
const SEVERITY_ICON: Record<string, string> = {
|
|
7
|
-
critical: "[!]",
|
|
8
|
-
warning: "[~]",
|
|
9
|
-
info: "[i]",
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
function applyPatch(patch: PatchOp): boolean {
|
|
13
|
-
// Map JSON-Patch path to filesystem path (strip leading /)
|
|
14
|
-
const filePath = patch.path.replace(/^\//, "");
|
|
15
|
-
|
|
16
|
-
// Guard: reject path traversal attempts
|
|
17
|
-
if (filePath.includes("..") || filePath.startsWith("/") || /^[A-Za-z]:/.test(filePath)) return false;
|
|
18
|
-
|
|
19
|
-
if (patch.op === "add") {
|
|
20
|
-
if (existsSync(filePath)) return false; // don't overwrite
|
|
21
|
-
const dir = dirname(filePath);
|
|
22
|
-
if (dir !== ".") mkdirSync(dir, { recursive: true });
|
|
23
|
-
writeFileSync(filePath, patch.value ?? "", "utf-8");
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (patch.op === "replace") {
|
|
28
|
-
const dir = dirname(filePath);
|
|
29
|
-
if (dir !== ".") mkdirSync(dir, { recursive: true });
|
|
30
|
-
writeFileSync(filePath, patch.value ?? "", "utf-8");
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// remove, move, copy, test — not needed yet
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async function learnAntibody(symptom: Symptom): Promise<void> {
|
|
39
|
-
await fetch(
|
|
40
|
-
`http://127.0.0.1:${(await getDaemonPort())}/antibodies/learn`,
|
|
41
|
-
{
|
|
42
|
-
method: "POST",
|
|
43
|
-
headers: { "Content-Type": "application/json" },
|
|
44
|
-
body: JSON.stringify({
|
|
45
|
-
id: symptom.id,
|
|
46
|
-
patternType: symptom.patternType,
|
|
47
|
-
fileTarget: symptom.fileTarget,
|
|
48
|
-
patches: symptom.patches,
|
|
49
|
-
}),
|
|
50
|
-
}
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function getDaemonPort(): Promise<number> {
|
|
55
|
-
const { readFileSync } = await import("fs");
|
|
56
|
-
const { resolveWorkspacePaths } = await import("../constants");
|
|
57
|
-
const paths = resolveWorkspacePaths();
|
|
58
|
-
return parseInt(readFileSync(paths.portFile, "utf-8").trim(), 10);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export async function fixCommand() {
|
|
62
|
-
let diagnosis: DiagnosisResult;
|
|
63
|
-
try {
|
|
64
|
-
diagnosis = await daemonRequest<DiagnosisResult>("/diagnose");
|
|
65
|
-
} catch (err: unknown) {
|
|
66
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
67
|
-
console.error(`[afd fix] ${msg}`);
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (diagnosis.symptoms.length === 0) {
|
|
72
|
-
console.log("[afd fix] No symptoms detected. System is healthy.");
|
|
73
|
-
if (diagnosis.healthy.length > 0) {
|
|
74
|
-
console.log(`[afd fix] Passed checks: ${diagnosis.healthy.join(", ")}`);
|
|
75
|
-
}
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Display symptoms
|
|
80
|
-
console.log(`\n[afd fix] Found ${diagnosis.symptoms.length} symptom(s):\n`);
|
|
81
|
-
|
|
82
|
-
for (const s of diagnosis.symptoms) {
|
|
83
|
-
const icon = SEVERITY_ICON[s.severity] ?? "[?]";
|
|
84
|
-
console.log(` ${icon} ${s.id}: ${s.title} (${s.severity})`);
|
|
85
|
-
console.log(` ${s.description}`);
|
|
86
|
-
if (s.patches.length > 0) {
|
|
87
|
-
console.log(` Patch: ${s.patches.map(p => `${p.op} ${p.path}`).join(", ")}`);
|
|
88
|
-
}
|
|
89
|
-
console.log();
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Extract phase: inject hologram context for AI consumers
|
|
93
|
-
const symptomsWithHologram = diagnosis.symptoms as (Symptom & { hologram?: string })[];
|
|
94
|
-
const holograms = symptomsWithHologram.filter(s => s.hologram);
|
|
95
|
-
if (holograms.length > 0) {
|
|
96
|
-
console.log("[afd fix] Hologram Context (Extract phase — token-optimized file structures):\n");
|
|
97
|
-
for (const s of holograms) {
|
|
98
|
-
console.log(` --- ${s.fileTarget} ---`);
|
|
99
|
-
console.log(` Here is the structural hologram of the file to help you understand`);
|
|
100
|
-
console.log(` its interfaces without consuming too many tokens:\n`);
|
|
101
|
-
for (const line of s.hologram!.split("\n")) {
|
|
102
|
-
console.log(` ${line}`);
|
|
103
|
-
}
|
|
104
|
-
console.log(`\n Now, generate the JSON-Patch based on the structure above.\n`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Back-stage: dump full JSON-Patch for AI consumers
|
|
109
|
-
const allPatches = diagnosis.symptoms.flatMap(s =>
|
|
110
|
-
s.patches.map(p => ({ symptomId: s.id, ...p }))
|
|
111
|
-
);
|
|
112
|
-
console.log("[afd fix] JSON-Patch (back-stage):");
|
|
113
|
-
console.log(JSON.stringify(allPatches, null, 2));
|
|
114
|
-
console.log();
|
|
115
|
-
|
|
116
|
-
// Prompt user
|
|
117
|
-
process.stdout.write("Apply these fixes? [Y/n] ");
|
|
118
|
-
const answer = await readLine();
|
|
119
|
-
|
|
120
|
-
if (answer.toLowerCase() === "n") {
|
|
121
|
-
console.log("[afd fix] Aborted.");
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Apply patches and learn antibodies
|
|
126
|
-
let applied = 0;
|
|
127
|
-
for (const symptom of diagnosis.symptoms) {
|
|
128
|
-
if (symptom.patches.length === 0) continue;
|
|
129
|
-
let success = true;
|
|
130
|
-
for (const patch of symptom.patches) {
|
|
131
|
-
if (!applyPatch(patch)) {
|
|
132
|
-
console.log(` [skip] ${patch.op} ${patch.path} (already exists or unsupported)`);
|
|
133
|
-
success = false;
|
|
134
|
-
} else {
|
|
135
|
-
console.log(` [done] ${patch.op} ${patch.path}`);
|
|
136
|
-
applied++;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (success) {
|
|
140
|
-
await learnAntibody(symptom);
|
|
141
|
-
console.log(` [immune] Learned antibody: ${symptom.id}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
console.log(`\n[afd fix] Applied ${applied} patch(es). Immune system updated.`);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function readLine(): Promise<string> {
|
|
149
|
-
return new Promise((resolve) => {
|
|
150
|
-
const buf: Buffer[] = [];
|
|
151
|
-
process.stdin.setEncoding("utf-8");
|
|
152
|
-
process.stdin.resume();
|
|
153
|
-
process.stdin.once("data", (chunk: string) => {
|
|
154
|
-
process.stdin.pause();
|
|
155
|
-
resolve(chunk.toString().trim());
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
}
|
|
1
|
+
import { writeFileSync, mkdirSync, existsSync } from "fs";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
import { daemonRequest } from "../daemon/client";
|
|
4
|
+
import type { Symptom, PatchOp, DiagnosisResult } from "../core/immune";
|
|
5
|
+
|
|
6
|
+
const SEVERITY_ICON: Record<string, string> = {
|
|
7
|
+
critical: "[!]",
|
|
8
|
+
warning: "[~]",
|
|
9
|
+
info: "[i]",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
function applyPatch(patch: PatchOp): boolean {
|
|
13
|
+
// Map JSON-Patch path to filesystem path (strip leading /)
|
|
14
|
+
const filePath = patch.path.replace(/^\//, "");
|
|
15
|
+
|
|
16
|
+
// Guard: reject path traversal attempts
|
|
17
|
+
if (filePath.includes("..") || filePath.startsWith("/") || /^[A-Za-z]:/.test(filePath)) return false;
|
|
18
|
+
|
|
19
|
+
if (patch.op === "add") {
|
|
20
|
+
if (existsSync(filePath)) return false; // don't overwrite
|
|
21
|
+
const dir = dirname(filePath);
|
|
22
|
+
if (dir !== ".") mkdirSync(dir, { recursive: true });
|
|
23
|
+
writeFileSync(filePath, patch.value ?? "", "utf-8");
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (patch.op === "replace") {
|
|
28
|
+
const dir = dirname(filePath);
|
|
29
|
+
if (dir !== ".") mkdirSync(dir, { recursive: true });
|
|
30
|
+
writeFileSync(filePath, patch.value ?? "", "utf-8");
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// remove, move, copy, test — not needed yet
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function learnAntibody(symptom: Symptom): Promise<void> {
|
|
39
|
+
await fetch(
|
|
40
|
+
`http://127.0.0.1:${(await getDaemonPort())}/antibodies/learn`,
|
|
41
|
+
{
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: { "Content-Type": "application/json" },
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
id: symptom.id,
|
|
46
|
+
patternType: symptom.patternType,
|
|
47
|
+
fileTarget: symptom.fileTarget,
|
|
48
|
+
patches: symptom.patches,
|
|
49
|
+
}),
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function getDaemonPort(): Promise<number> {
|
|
55
|
+
const { readFileSync } = await import("fs");
|
|
56
|
+
const { resolveWorkspacePaths } = await import("../constants");
|
|
57
|
+
const paths = resolveWorkspacePaths();
|
|
58
|
+
return parseInt(readFileSync(paths.portFile, "utf-8").trim(), 10);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function fixCommand() {
|
|
62
|
+
let diagnosis: DiagnosisResult;
|
|
63
|
+
try {
|
|
64
|
+
diagnosis = await daemonRequest<DiagnosisResult>("/diagnose");
|
|
65
|
+
} catch (err: unknown) {
|
|
66
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
67
|
+
console.error(`[afd fix] ${msg}`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (diagnosis.symptoms.length === 0) {
|
|
72
|
+
console.log("[afd fix] No symptoms detected. System is healthy.");
|
|
73
|
+
if (diagnosis.healthy.length > 0) {
|
|
74
|
+
console.log(`[afd fix] Passed checks: ${diagnosis.healthy.join(", ")}`);
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Display symptoms
|
|
80
|
+
console.log(`\n[afd fix] Found ${diagnosis.symptoms.length} symptom(s):\n`);
|
|
81
|
+
|
|
82
|
+
for (const s of diagnosis.symptoms) {
|
|
83
|
+
const icon = SEVERITY_ICON[s.severity] ?? "[?]";
|
|
84
|
+
console.log(` ${icon} ${s.id}: ${s.title} (${s.severity})`);
|
|
85
|
+
console.log(` ${s.description}`);
|
|
86
|
+
if (s.patches.length > 0) {
|
|
87
|
+
console.log(` Patch: ${s.patches.map(p => `${p.op} ${p.path}`).join(", ")}`);
|
|
88
|
+
}
|
|
89
|
+
console.log();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Extract phase: inject hologram context for AI consumers
|
|
93
|
+
const symptomsWithHologram = diagnosis.symptoms as (Symptom & { hologram?: string })[];
|
|
94
|
+
const holograms = symptomsWithHologram.filter(s => s.hologram);
|
|
95
|
+
if (holograms.length > 0) {
|
|
96
|
+
console.log("[afd fix] Hologram Context (Extract phase — token-optimized file structures):\n");
|
|
97
|
+
for (const s of holograms) {
|
|
98
|
+
console.log(` --- ${s.fileTarget} ---`);
|
|
99
|
+
console.log(` Here is the structural hologram of the file to help you understand`);
|
|
100
|
+
console.log(` its interfaces without consuming too many tokens:\n`);
|
|
101
|
+
for (const line of s.hologram!.split("\n")) {
|
|
102
|
+
console.log(` ${line}`);
|
|
103
|
+
}
|
|
104
|
+
console.log(`\n Now, generate the JSON-Patch based on the structure above.\n`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Back-stage: dump full JSON-Patch for AI consumers
|
|
109
|
+
const allPatches = diagnosis.symptoms.flatMap(s =>
|
|
110
|
+
s.patches.map(p => ({ symptomId: s.id, ...p }))
|
|
111
|
+
);
|
|
112
|
+
console.log("[afd fix] JSON-Patch (back-stage):");
|
|
113
|
+
console.log(JSON.stringify(allPatches, null, 2));
|
|
114
|
+
console.log();
|
|
115
|
+
|
|
116
|
+
// Prompt user
|
|
117
|
+
process.stdout.write("Apply these fixes? [Y/n] ");
|
|
118
|
+
const answer = await readLine();
|
|
119
|
+
|
|
120
|
+
if (answer.toLowerCase() === "n") {
|
|
121
|
+
console.log("[afd fix] Aborted.");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Apply patches and learn antibodies
|
|
126
|
+
let applied = 0;
|
|
127
|
+
for (const symptom of diagnosis.symptoms) {
|
|
128
|
+
if (symptom.patches.length === 0) continue;
|
|
129
|
+
let success = true;
|
|
130
|
+
for (const patch of symptom.patches) {
|
|
131
|
+
if (!applyPatch(patch)) {
|
|
132
|
+
console.log(` [skip] ${patch.op} ${patch.path} (already exists or unsupported)`);
|
|
133
|
+
success = false;
|
|
134
|
+
} else {
|
|
135
|
+
console.log(` [done] ${patch.op} ${patch.path}`);
|
|
136
|
+
applied++;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (success) {
|
|
140
|
+
await learnAntibody(symptom);
|
|
141
|
+
console.log(` [immune] Learned antibody: ${symptom.id}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(`\n[afd fix] Applied ${applied} patch(es). Immune system updated.`);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function readLine(): Promise<string> {
|
|
149
|
+
return new Promise((resolve) => {
|
|
150
|
+
const buf: Buffer[] = [];
|
|
151
|
+
process.stdin.setEncoding("utf-8");
|
|
152
|
+
process.stdin.resume();
|
|
153
|
+
process.stdin.once("data", (chunk: string) => {
|
|
154
|
+
process.stdin.pause();
|
|
155
|
+
resolve(chunk.toString().trim());
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
package/src/commands/lang.ts
CHANGED
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { getSystemLanguage, getSupportedLanguages, setLanguageOverride } from "../core/locale";
|
|
2
|
-
import type { SupportedLang } from "../core/locale";
|
|
3
|
-
import { writeConfig, getConfigPath } from "../core/config";
|
|
4
|
-
import { getMessages, t } from "../core/i18n/messages";
|
|
5
|
-
|
|
6
|
-
export function langCommand(targetLang?: string, options?: { list?: boolean }) {
|
|
7
|
-
const currentLang = getSystemLanguage();
|
|
8
|
-
const msg = getMessages(currentLang);
|
|
9
|
-
const supported = getSupportedLanguages();
|
|
10
|
-
|
|
11
|
-
// afd lang --list
|
|
12
|
-
if (options?.list) {
|
|
13
|
-
console.log(msg.LANG_LIST_TITLE);
|
|
14
|
-
for (const lang of supported) {
|
|
15
|
-
const marker = lang === currentLang ? " ← current" : "";
|
|
16
|
-
console.log(` ${lang}${marker}`);
|
|
17
|
-
}
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// afd lang (no argument) — show current
|
|
22
|
-
if (!targetLang) {
|
|
23
|
-
console.log(t(msg.LANG_CURRENT, { lang: currentLang }));
|
|
24
|
-
return;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// afd lang <en|ko> — change language
|
|
28
|
-
if (!supported.includes(targetLang as SupportedLang)) {
|
|
29
|
-
console.error(t(msg.LANG_INVALID, { lang: targetLang, supported: supported.join(", ") }));
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const newLang = targetLang as SupportedLang;
|
|
34
|
-
writeConfig({ lang: newLang });
|
|
35
|
-
setLanguageOverride(newLang);
|
|
36
|
-
|
|
37
|
-
// Print feedback in the NEW language
|
|
38
|
-
const newMsg = getMessages(newLang);
|
|
39
|
-
console.log(t(newMsg.LANG_CHANGED, { lang: newLang }));
|
|
40
|
-
console.log(t(newMsg.LANG_SAVED, { path: getConfigPath() }));
|
|
41
|
-
}
|
|
1
|
+
import { getSystemLanguage, getSupportedLanguages, setLanguageOverride } from "../core/locale";
|
|
2
|
+
import type { SupportedLang } from "../core/locale";
|
|
3
|
+
import { writeConfig, getConfigPath } from "../core/config";
|
|
4
|
+
import { getMessages, t } from "../core/i18n/messages";
|
|
5
|
+
|
|
6
|
+
export function langCommand(targetLang?: string, options?: { list?: boolean }) {
|
|
7
|
+
const currentLang = getSystemLanguage();
|
|
8
|
+
const msg = getMessages(currentLang);
|
|
9
|
+
const supported = getSupportedLanguages();
|
|
10
|
+
|
|
11
|
+
// afd lang --list
|
|
12
|
+
if (options?.list) {
|
|
13
|
+
console.log(msg.LANG_LIST_TITLE);
|
|
14
|
+
for (const lang of supported) {
|
|
15
|
+
const marker = lang === currentLang ? " ← current" : "";
|
|
16
|
+
console.log(` ${lang}${marker}`);
|
|
17
|
+
}
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// afd lang (no argument) — show current
|
|
22
|
+
if (!targetLang) {
|
|
23
|
+
console.log(t(msg.LANG_CURRENT, { lang: currentLang }));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// afd lang <en|ko> — change language
|
|
28
|
+
if (!supported.includes(targetLang as SupportedLang)) {
|
|
29
|
+
console.error(t(msg.LANG_INVALID, { lang: targetLang, supported: supported.join(", ") }));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const newLang = targetLang as SupportedLang;
|
|
34
|
+
writeConfig({ lang: newLang });
|
|
35
|
+
setLanguageOverride(newLang);
|
|
36
|
+
|
|
37
|
+
// Print feedback in the NEW language
|
|
38
|
+
const newMsg = getMessages(newLang);
|
|
39
|
+
console.log(t(newMsg.LANG_CHANGED, { lang: newLang }));
|
|
40
|
+
console.log(t(newMsg.LANG_SAVED, { path: getConfigPath() }));
|
|
41
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* afd plugin — Third-party validator plugin manager
|
|
3
|
+
*
|
|
4
|
+
* Sub-commands:
|
|
5
|
+
* afd plugin install <npm-package> — install a validator plugin from npm
|
|
6
|
+
* afd plugin list — list installed plugins
|
|
7
|
+
* afd plugin remove <name> — uninstall a plugin
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { installPlugin, listPlugins, removePlugin } from "../core/plugin-manager";
|
|
11
|
+
import { getSystemLanguage } from "../core/locale";
|
|
12
|
+
|
|
13
|
+
const msgs = {
|
|
14
|
+
en: {
|
|
15
|
+
usage: `Usage:
|
|
16
|
+
afd plugin install <npm-package> Install a validator plugin from npm
|
|
17
|
+
afd plugin list List installed plugins
|
|
18
|
+
afd plugin remove <name> Uninstall a plugin`,
|
|
19
|
+
noPlugins: "No plugins installed.",
|
|
20
|
+
installing: "Installing",
|
|
21
|
+
installed: "Installed Plugins",
|
|
22
|
+
},
|
|
23
|
+
ko: {
|
|
24
|
+
usage: `사용법:
|
|
25
|
+
afd plugin install <npm-package> npm 플러그인 설치
|
|
26
|
+
afd plugin list 설치된 플러그인 목록
|
|
27
|
+
afd plugin remove <name> 플러그인 제거`,
|
|
28
|
+
noPlugins: "설치된 플러그인이 없습니다.",
|
|
29
|
+
installing: "설치 중",
|
|
30
|
+
installed: "설치된 플러그인",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const BOX = { tl: "┌", tr: "┐", bl: "└", br: "┘", h: "─", v: "│", ml: "├", mr: "┤" };
|
|
35
|
+
const W = 58;
|
|
36
|
+
function hline(l: string, r: string) { return `${l}${BOX.h.repeat(W)}${r}`; }
|
|
37
|
+
function row(s: string) {
|
|
38
|
+
const pad = Math.max(0, W - 2 - s.length);
|
|
39
|
+
return `${BOX.v} ${s}${" ".repeat(pad)} ${BOX.v}`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function pluginCommand(subcommand?: string, arg?: string) {
|
|
43
|
+
const lang = getSystemLanguage();
|
|
44
|
+
const m = msgs[lang];
|
|
45
|
+
|
|
46
|
+
if (!subcommand) {
|
|
47
|
+
console.log(m.usage);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
switch (subcommand) {
|
|
52
|
+
case "install": {
|
|
53
|
+
if (!arg) {
|
|
54
|
+
console.error("Usage: afd plugin install <npm-package>");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
console.log(`🔌 ${m.installing}: ${arg} …`);
|
|
58
|
+
const result = installPlugin(arg);
|
|
59
|
+
console.log(hline(BOX.tl, BOX.tr));
|
|
60
|
+
if (result.success) {
|
|
61
|
+
console.log(row(`✅ ${result.message}`));
|
|
62
|
+
if (result.manifest?.description) {
|
|
63
|
+
console.log(row(` ${result.manifest.description}`));
|
|
64
|
+
}
|
|
65
|
+
console.log(row(" Hot-reload active — daemon picks this up instantly."));
|
|
66
|
+
} else {
|
|
67
|
+
console.log(row(`❌ ${result.message}`));
|
|
68
|
+
}
|
|
69
|
+
console.log(hline(BOX.bl, BOX.br));
|
|
70
|
+
if (!result.success) process.exit(1);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
case "list": {
|
|
75
|
+
const plugins = listPlugins();
|
|
76
|
+
console.log(hline(BOX.tl, BOX.tr));
|
|
77
|
+
console.log(row(`🔌 ${m.installed}`));
|
|
78
|
+
console.log(hline(BOX.ml, BOX.mr));
|
|
79
|
+
if (plugins.length === 0) {
|
|
80
|
+
console.log(row(m.noPlugins));
|
|
81
|
+
} else {
|
|
82
|
+
for (const p of plugins) {
|
|
83
|
+
console.log(row(`📦 ${p.package}@${p.version}`));
|
|
84
|
+
if (p.description) console.log(row(` ${p.description}`));
|
|
85
|
+
console.log(row(` validator: .afd/validators/${p.validatorFile}`));
|
|
86
|
+
console.log(row(` installed: ${p.installDate.slice(0, 10)}`));
|
|
87
|
+
console.log(row(""));
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
console.log(hline(BOX.bl, BOX.br));
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
case "remove": {
|
|
95
|
+
if (!arg) {
|
|
96
|
+
console.error("Usage: afd plugin remove <name>");
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
const result = removePlugin(arg);
|
|
100
|
+
console.log(hline(BOX.tl, BOX.tr));
|
|
101
|
+
console.log(row(result.success ? `✅ ${result.message}` : `❌ ${result.message}`));
|
|
102
|
+
console.log(hline(BOX.bl, BOX.br));
|
|
103
|
+
if (!result.success) process.exit(1);
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
default:
|
|
108
|
+
console.log(m.usage);
|
|
109
|
+
}
|
|
110
|
+
}
|
package/src/commands/restart.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { stopCommand } from "./stop";
|
|
2
|
-
import { startCommand } from "./start";
|
|
3
|
-
import { getSystemLanguage } from "../core/locale";
|
|
4
|
-
import { getMessages } from "../core/i18n/messages";
|
|
5
|
-
|
|
6
|
-
export async function restartCommand() {
|
|
7
|
-
const lang = getSystemLanguage();
|
|
8
|
-
const msg = getMessages(lang);
|
|
9
|
-
|
|
10
|
-
console.log(msg.DAEMON_RESTARTING);
|
|
11
|
-
|
|
12
|
-
await stopCommand();
|
|
13
|
-
await startCommand();
|
|
14
|
-
}
|
|
1
|
+
import { stopCommand } from "./stop";
|
|
2
|
+
import { startCommand } from "./start";
|
|
3
|
+
import { getSystemLanguage } from "../core/locale";
|
|
4
|
+
import { getMessages } from "../core/i18n/messages";
|
|
5
|
+
|
|
6
|
+
export async function restartCommand() {
|
|
7
|
+
const lang = getSystemLanguage();
|
|
8
|
+
const msg = getMessages(lang);
|
|
9
|
+
|
|
10
|
+
console.log(msg.DAEMON_RESTARTING);
|
|
11
|
+
|
|
12
|
+
await stopCommand();
|
|
13
|
+
await startCommand();
|
|
14
|
+
}
|