autonomous-flow-daemon 1.0.0 → 1.6.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 (59) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/README.ko.md +142 -125
  3. package/README.md +119 -134
  4. package/package.json +11 -5
  5. package/src/adapters/index.ts +247 -35
  6. package/src/cli.ts +79 -1
  7. package/src/commands/benchmark.ts +187 -0
  8. package/src/commands/diagnose.ts +56 -14
  9. package/src/commands/doctor.ts +243 -0
  10. package/src/commands/evolution.ts +107 -0
  11. package/src/commands/fix.ts +22 -2
  12. package/src/commands/hooks.ts +136 -0
  13. package/src/commands/lang.ts +41 -0
  14. package/src/commands/mcp.ts +129 -0
  15. package/src/commands/restart.ts +14 -0
  16. package/src/commands/score.ts +192 -64
  17. package/src/commands/start.ts +137 -37
  18. package/src/commands/stats.ts +103 -0
  19. package/src/commands/status.ts +157 -0
  20. package/src/commands/stop.ts +42 -9
  21. package/src/commands/sync.ts +253 -20
  22. package/src/commands/vaccine.ts +177 -0
  23. package/src/constants.ts +26 -1
  24. package/src/core/boast.ts +280 -0
  25. package/src/core/config.ts +49 -0
  26. package/src/core/db.ts +74 -3
  27. package/src/core/discovery.ts +65 -0
  28. package/src/core/evolution.ts +215 -0
  29. package/src/core/hologram/engine.ts +71 -0
  30. package/src/core/hologram/fallback.ts +11 -0
  31. package/src/core/hologram/incremental.ts +227 -0
  32. package/src/core/hologram/py-extractor.ts +132 -0
  33. package/src/core/hologram/ts-extractor.ts +320 -0
  34. package/src/core/hologram/types.ts +25 -0
  35. package/src/core/hologram.ts +64 -236
  36. package/src/core/hook-manager.ts +259 -0
  37. package/src/core/i18n/messages.ts +309 -0
  38. package/src/core/immune.ts +8 -123
  39. package/src/core/locale.ts +88 -0
  40. package/src/core/log-rotate.ts +33 -0
  41. package/src/core/log-utils.ts +38 -0
  42. package/src/core/lru-map.ts +61 -0
  43. package/src/core/notify.ts +53 -14
  44. package/src/core/rule-engine.ts +287 -0
  45. package/src/core/semantic-diff.ts +432 -0
  46. package/src/core/telemetry.ts +94 -0
  47. package/src/core/vaccine-registry.ts +212 -0
  48. package/src/core/workspace.ts +28 -0
  49. package/src/core/yaml-minimal.ts +176 -0
  50. package/src/daemon/client.ts +34 -6
  51. package/src/daemon/event-batcher.ts +108 -0
  52. package/src/daemon/guards.ts +13 -0
  53. package/src/daemon/http-routes.ts +293 -0
  54. package/src/daemon/mcp-handler.ts +270 -0
  55. package/src/daemon/server.ts +492 -273
  56. package/src/daemon/types.ts +100 -0
  57. package/src/daemon/workspace-map.ts +92 -0
  58. package/src/platform.ts +60 -0
  59. package/src/version.ts +15 -0
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Shared types for the afd daemon modules.
3
+ */
4
+
5
+ import type { DetectionResult } from "../adapters/index";
6
+ import type { PatchOp } from "../core/immune";
7
+ import type { LruStringMap } from "../core/lru-map";
8
+
9
+ // ── Constants ──
10
+ export const DOUBLE_TAP_WINDOW_MS = 30_000;
11
+ export const MASS_EVENT_THRESHOLD = 3;
12
+ export const MASS_EVENT_WINDOW_MS = 1_000;
13
+ export const TAP_CLEANUP_INTERVAL_MS = 60_000;
14
+ export const SELF_WRITE_DEBOUNCE_MS = 100;
15
+ export const MAX_SSE_CLIENTS = 20;
16
+ export const VALIDATOR_TIMEOUT_MS = 500;
17
+ export const VALIDATORS_DIR = ".afd/validators";
18
+
19
+ // ── Types ──
20
+ export type ValidatorFn = (newContent: string, filePath: string) => boolean;
21
+
22
+ export interface HologramStats {
23
+ totalRequests: number;
24
+ totalOriginalChars: number;
25
+ totalHologramChars: number;
26
+ sessionOriginalChars: number;
27
+ sessionHologramChars: number;
28
+ }
29
+
30
+ export interface MistakeEntry {
31
+ mistake_type: string;
32
+ description: string;
33
+ timestamp: number;
34
+ }
35
+
36
+ export interface DaemonState {
37
+ startedAt: number;
38
+ filesDetected: number;
39
+ lastEvent: string | null;
40
+ lastEventAt: number | null;
41
+ watchedFiles: Set<string>;
42
+ hologramStats: HologramStats;
43
+ ecosystems: DetectionResult[];
44
+ autoHealCount: number;
45
+ autoHealLog: { id: string; at: number }[];
46
+ recentUnlinks: number[];
47
+ firstTapTimestamps: Map<string, number>;
48
+ suppressionSkippedCount: number;
49
+ dormantTransitions: { antibodyId: string; at: number }[];
50
+ totalFileBytesSaved: number;
51
+ totalSavedTokens: number;
52
+ fileSnapshots: LruStringMap;
53
+ sseClients: Set<ReadableStreamDefaultController<Uint8Array>>;
54
+ customValidators: Map<string, ValidatorFn>;
55
+ mistakeCache: Map<string, MistakeEntry[]>;
56
+ }
57
+
58
+ export interface DaemonOptions {
59
+ mcp?: boolean;
60
+ }
61
+
62
+ export { type PatchOp, type DetectionResult };
63
+
64
+ /**
65
+ * DaemonContext — shared dependency bag passed to all daemon modules.
66
+ * Created once in main() and threaded through MCP/HTTP handlers.
67
+ */
68
+ export interface DaemonContext {
69
+ state: DaemonState;
70
+ db: { query: (sql: string) => { get: () => unknown }; prepare: (sql: string) => { run: (...args: unknown[]) => void; get: (...args: unknown[]) => unknown; all: () => unknown[] } };
71
+ ws: { root: string; afdDir: string; pidFile: string; portFile: string; dbFile: string; logFile: string; quarantineDir: string };
72
+
73
+ // Prepared statements
74
+ insertEvent: { run: (...args: unknown[]) => void };
75
+ insertAntibody: { run: (...args: unknown[]) => void };
76
+ listAntibodies: { all: () => unknown[] };
77
+ antibodyIds: { all: () => { id: string }[] };
78
+ countAntibodies: { get: () => { cnt: number } };
79
+ getDailyAll: { all: () => { date: string; requests: number; original_chars: number; hologram_chars: number }[] };
80
+ insertTelemetry: { run: (...args: unknown[]) => void };
81
+ insertMistakeHistory: { run: (...args: unknown[]) => void };
82
+ queryMistakesByFile: { all: (...args: unknown[]) => MistakeEntry[] };
83
+ deleteMistakeOverflow: { run: (...args: unknown[]) => void };
84
+
85
+ // Helper functions
86
+ seam: (phase: string, msg: string) => void;
87
+ persistHologramStats: (originalChars: number, hologramChars: number) => void;
88
+ safeHologram: (filePath: string, source: string) => Promise<string>;
89
+ getWorkspaceMap: () => string;
90
+ today: () => string;
91
+
92
+ // Discovery
93
+ discoveryTargets: string[];
94
+
95
+ // Options
96
+ options: DaemonOptions;
97
+
98
+ // Mutable port (set after Bun.serve)
99
+ port: number;
100
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Workspace Map — cached project structure with export signatures.
3
+ * Provides afd://workspace-map MCP resource.
4
+ */
5
+
6
+ import { readdirSync, readFileSync, lstatSync } from "fs";
7
+ import { join } from "path";
8
+
9
+ const MAX_WALK_DEPTH = 8;
10
+ const SKIP_DIRS = new Set(["node_modules", ".afd", ".git", "dist", "coverage"]);
11
+ const CODE_EXTS = /\.[tj]sx?$/;
12
+ const DOC_EXTS = /\.(ts|js|tsx|jsx|json|md)$/;
13
+
14
+ /** Build a workspace map: file tree with sizes and export signatures */
15
+ function buildWorkspaceMap(): string {
16
+ const cwd = process.cwd();
17
+ const lines: string[] = [`# Workspace Map — ${cwd}`, `# Generated: ${new Date().toISOString()}`, ""];
18
+
19
+ function walk(dir: string, prefix: string, depth: number) {
20
+ if (depth > MAX_WALK_DEPTH) return;
21
+ let entries: string[];
22
+ try { entries = readdirSync(dir).sort(); } catch { return; }
23
+ for (const entry of entries) {
24
+ if (SKIP_DIRS.has(entry)) continue;
25
+ const fullPath = join(dir, entry);
26
+ try {
27
+ const lst = lstatSync(fullPath);
28
+ if (lst.isSymbolicLink()) continue;
29
+ if (lst.isDirectory()) {
30
+ lines.push(`${prefix}${entry}/`);
31
+ walk(fullPath, prefix + " ", depth + 1);
32
+ continue;
33
+ }
34
+ if (!DOC_EXTS.test(entry)) continue;
35
+ const sizeKB = (lst.size / 1024).toFixed(1);
36
+ if (CODE_EXTS.test(entry) && lst.size < 100 * 1024) {
37
+ try {
38
+ const source = readFileSync(fullPath, "utf-8");
39
+ const exports = source.match(/export\s+(?:async\s+)?(?:function|const|class|interface|type|enum)\s+(\w+)/g);
40
+ const sigs = exports ? exports.map(e => e.replace(/^export\s+(async\s+)?/, "").trim()).slice(0, 5).join(", ") : "";
41
+ const extra = exports && exports.length > 5 ? ` +${exports.length - 5} more` : "";
42
+ lines.push(`${prefix}${entry} (${sizeKB}KB)${sigs ? ` → ${sigs}${extra}` : ""}`);
43
+ } catch {
44
+ lines.push(`${prefix}${entry} (${sizeKB}KB)`);
45
+ }
46
+ } else {
47
+ lines.push(`${prefix}${entry} (${sizeKB}KB)`);
48
+ }
49
+ } catch { /* skip */ }
50
+ }
51
+ }
52
+
53
+ walk(join(cwd, "src"), " ", 0);
54
+
55
+ lines.push("", "# Root files");
56
+ for (const f of ["CLAUDE.md", "package.json", ".claudeignore", ".mcp.json"]) {
57
+ try {
58
+ const st = lstatSync(join(cwd, f));
59
+ lines.push(` ${f} (${(st.size / 1024).toFixed(1)}KB)`);
60
+ } catch { /* not found */ }
61
+ }
62
+
63
+ return lines.join("\n");
64
+ }
65
+
66
+ /**
67
+ * Creates a workspace map manager with lazy caching.
68
+ * Returns getWorkspaceMap() and markMapDirty() functions.
69
+ */
70
+ export function createWorkspaceMap() {
71
+ let cache = "";
72
+ let dirty = true;
73
+ let timer: ReturnType<typeof setTimeout> | null = null;
74
+
75
+ function get(): string {
76
+ if (dirty || !cache) {
77
+ cache = buildWorkspaceMap();
78
+ dirty = false;
79
+ }
80
+ return cache;
81
+ }
82
+
83
+ function markDirty() {
84
+ dirty = true;
85
+ if (timer) clearTimeout(timer);
86
+ timer = setTimeout(() => { get(); }, 5000);
87
+ }
88
+
89
+ function getTimer() { return timer; }
90
+
91
+ return { get, markDirty, getTimer };
92
+ }
@@ -0,0 +1,60 @@
1
+ import { platform } from "os";
2
+ import type { SpawnOptions } from "child_process";
3
+ import { execSync } from "child_process";
4
+
5
+ export const IS_WINDOWS = platform() === "win32";
6
+ export const IS_MACOS = platform() === "darwin";
7
+ export const IS_LINUX = platform() === "linux";
8
+
9
+ /**
10
+ * Returns spawn options appropriate for detaching a background daemon.
11
+ * On Windows, `shell: true` is required for `detached` to create a new console.
12
+ */
13
+ export function detachedSpawnOptions(
14
+ logFd: number,
15
+ ): SpawnOptions {
16
+ const base: SpawnOptions = {
17
+ detached: true,
18
+ stdio: ["ignore", logFd, logFd],
19
+ cwd: process.cwd(),
20
+ env: { ...process.env },
21
+ };
22
+
23
+ if (IS_WINDOWS) {
24
+ // Windows needs shell:true for detached to work properly
25
+ // and windowsHide to prevent a console flash
26
+ return { ...base, shell: true, windowsHide: true };
27
+ }
28
+
29
+ return base;
30
+ }
31
+
32
+ const DIAGNOSE_ARGS = "diagnose --format a2a --auto-heal";
33
+
34
+ /**
35
+ * Resolve the hook command for invoking afd diagnose.
36
+ * Priority:
37
+ * 1. Global `afd` binary (npm/bun global install)
38
+ * 2. `bunx afd` fallback (Bun environment)
39
+ * 3. `npx afd` fallback (Node environment)
40
+ */
41
+ export function resolveHookCommand(): string {
42
+ if (isCommandAvailable("afd")) {
43
+ return `afd ${DIAGNOSE_ARGS}`;
44
+ }
45
+ if (isCommandAvailable("bunx")) {
46
+ return `bunx afd ${DIAGNOSE_ARGS}`;
47
+ }
48
+ return `npx -y afd ${DIAGNOSE_ARGS}`;
49
+ }
50
+
51
+ /** Check if a command exists on the system PATH */
52
+ function isCommandAvailable(cmd: string): boolean {
53
+ try {
54
+ const check = IS_WINDOWS ? `where ${cmd}` : `command -v ${cmd}`;
55
+ execSync(check, { stdio: "ignore" });
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
package/src/version.ts ADDED
@@ -0,0 +1,15 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+
4
+ let version = "0.0.0-unknown";
5
+ try {
6
+ const raw = readFileSync(resolve(import.meta.dirname, "..", "package.json"), "utf-8");
7
+ const pkg = JSON.parse(raw);
8
+ if (typeof pkg.version === "string" && pkg.version) {
9
+ version = pkg.version;
10
+ }
11
+ } catch {
12
+ // Bundled binary or missing package.json — fallback silently
13
+ }
14
+
15
+ export const APP_VERSION: string = version;