@nex-ai/nex 0.1.65 → 0.1.67

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 (75) hide show
  1. package/dist/commands/upgrade.d.ts +9 -0
  2. package/dist/commands/upgrade.js +87 -0
  3. package/dist/commands/upgrade.js.map +1 -0
  4. package/dist/index.js +7 -0
  5. package/dist/index.js.map +1 -1
  6. package/dist/lib/installers.js +3 -2
  7. package/dist/lib/installers.js.map +1 -1
  8. package/dist/mcp/index.js +0 -0
  9. package/dist/plugin/adapters/cline-capture.d.ts +7 -0
  10. package/dist/plugin/adapters/cline-capture.js +25 -0
  11. package/dist/plugin/adapters/cline-capture.js.map +1 -0
  12. package/dist/plugin/adapters/cline-recall.d.ts +7 -0
  13. package/dist/plugin/adapters/cline-recall.js +30 -0
  14. package/dist/plugin/adapters/cline-recall.js.map +1 -0
  15. package/dist/plugin/adapters/cline-task-start.d.ts +7 -0
  16. package/dist/plugin/adapters/cline-task-start.js +30 -0
  17. package/dist/plugin/adapters/cline-task-start.js.map +1 -0
  18. package/dist/plugin/adapters/cursor-recall.d.ts +7 -0
  19. package/dist/plugin/adapters/cursor-recall.js +31 -0
  20. package/dist/plugin/adapters/cursor-recall.js.map +1 -0
  21. package/dist/plugin/adapters/cursor-session-start.d.ts +7 -0
  22. package/dist/plugin/adapters/cursor-session-start.js +30 -0
  23. package/dist/plugin/adapters/cursor-session-start.js.map +1 -0
  24. package/dist/plugin/adapters/cursor-stop.d.ts +7 -0
  25. package/dist/plugin/adapters/cursor-stop.js +25 -0
  26. package/dist/plugin/adapters/cursor-stop.js.map +1 -0
  27. package/dist/plugin/adapters/windsurf-capture.d.ts +7 -0
  28. package/dist/plugin/adapters/windsurf-capture.js +25 -0
  29. package/dist/plugin/adapters/windsurf-capture.js.map +1 -0
  30. package/dist/plugin/adapters/windsurf-recall.d.ts +7 -0
  31. package/dist/plugin/adapters/windsurf-recall.js +31 -0
  32. package/dist/plugin/adapters/windsurf-recall.js.map +1 -0
  33. package/dist/plugin/auto-session-start.js +39 -1
  34. package/dist/plugin/auto-session-start.js.map +1 -1
  35. package/dist/plugin/shared.d.ts +39 -0
  36. package/dist/plugin/shared.js +380 -0
  37. package/dist/plugin/shared.js.map +1 -0
  38. package/dist/plugin/update-check.d.ts +13 -0
  39. package/dist/plugin/update-check.js +134 -0
  40. package/dist/plugin/update-check.js.map +1 -0
  41. package/openclaw-plugin/dist/capture-filter.d.ts +32 -0
  42. package/openclaw-plugin/dist/capture-filter.d.ts.map +1 -0
  43. package/openclaw-plugin/dist/capture-filter.js +117 -0
  44. package/openclaw-plugin/dist/capture-filter.js.map +1 -0
  45. package/openclaw-plugin/dist/config.d.ts +24 -0
  46. package/openclaw-plugin/dist/config.d.ts.map +1 -0
  47. package/openclaw-plugin/dist/config.js +68 -0
  48. package/openclaw-plugin/dist/config.js.map +1 -0
  49. package/openclaw-plugin/dist/context-format.d.ts +23 -0
  50. package/openclaw-plugin/dist/context-format.d.ts.map +1 -0
  51. package/openclaw-plugin/dist/context-format.js +40 -0
  52. package/openclaw-plugin/dist/context-format.js.map +1 -0
  53. package/openclaw-plugin/dist/file-scanner.d.ts +23 -0
  54. package/openclaw-plugin/dist/file-scanner.d.ts.map +1 -0
  55. package/openclaw-plugin/dist/file-scanner.js +119 -0
  56. package/openclaw-plugin/dist/file-scanner.js.map +1 -0
  57. package/openclaw-plugin/dist/index.d.ts +94 -0
  58. package/openclaw-plugin/dist/index.d.ts.map +1 -0
  59. package/openclaw-plugin/dist/index.js +1227 -0
  60. package/openclaw-plugin/dist/index.js.map +1 -0
  61. package/openclaw-plugin/dist/nex-client.d.ts +52 -0
  62. package/openclaw-plugin/dist/nex-client.d.ts.map +1 -0
  63. package/openclaw-plugin/dist/nex-client.js +129 -0
  64. package/openclaw-plugin/dist/nex-client.js.map +1 -0
  65. package/openclaw-plugin/dist/rate-limiter.d.ts +29 -0
  66. package/openclaw-plugin/dist/rate-limiter.d.ts.map +1 -0
  67. package/openclaw-plugin/dist/rate-limiter.js +95 -0
  68. package/openclaw-plugin/dist/rate-limiter.js.map +1 -0
  69. package/openclaw-plugin/dist/session-store.d.ts +15 -0
  70. package/openclaw-plugin/dist/session-store.d.ts.map +1 -0
  71. package/openclaw-plugin/dist/session-store.js +43 -0
  72. package/openclaw-plugin/dist/session-store.js.map +1 -0
  73. package/openclaw-plugin/openclaw.plugin.json +85 -0
  74. package/openclaw-plugin/package.json +29 -0
  75. package/package.json +2 -1
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * nex-update-check — lightweight version check with caching.
4
+ *
5
+ * Outputs one of:
6
+ * UPGRADE_AVAILABLE <current> <latest>
7
+ * (nothing — up to date, snoozed, disabled, or cached)
8
+ *
9
+ * Designed to run inline in session start hooks with minimal latency.
10
+ * Caches results to ~/.nex/last-update-check (60 min TTL for "up to date",
11
+ * 720 min for "upgrade available"). Supports snooze with escalating backoff.
12
+ */
13
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
14
+ import { join } from "node:path";
15
+ import { homedir } from "node:os";
16
+ import { execFileSync } from "node:child_process";
17
+ import { createRequire } from "node:module";
18
+ const require = createRequire(import.meta.url);
19
+ const pkg = require("../../package.json");
20
+ const NEX_DIR = join(homedir(), ".nex");
21
+ const CACHE_PATH = join(NEX_DIR, "last-update-check");
22
+ const SNOOZE_PATH = join(NEX_DIR, "update-snoozed");
23
+ const UPGRADED_PATH = join(NEX_DIR, "just-upgraded-from");
24
+ const UP_TO_DATE_TTL_MS = 60 * 60 * 1000; // 60 min
25
+ const UPGRADE_AVAIL_TTL_MS = 12 * 60 * 60 * 1000; // 12 hours
26
+ // Snooze escalation: 24h → 48h → 7d
27
+ const SNOOZE_LEVELS_MS = [
28
+ 24 * 60 * 60 * 1000,
29
+ 48 * 60 * 60 * 1000,
30
+ 7 * 24 * 60 * 60 * 1000,
31
+ ];
32
+ function compareVersions(a, b) {
33
+ const pa = a.split(".").map(Number);
34
+ const pb = b.split(".").map(Number);
35
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
36
+ const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
37
+ if (diff !== 0)
38
+ return diff;
39
+ }
40
+ return 0;
41
+ }
42
+ function readJson(path) {
43
+ try {
44
+ return JSON.parse(readFileSync(path, "utf-8"));
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ function writeJson(path, data) {
51
+ mkdirSync(NEX_DIR, { recursive: true });
52
+ writeFileSync(path, JSON.stringify(data) + "\n", "utf-8");
53
+ }
54
+ /** Check if snoozed for current latest version. */
55
+ function isSnoozed(latestVersion) {
56
+ const snooze = readJson(SNOOZE_PATH);
57
+ if (!snooze)
58
+ return false;
59
+ if (snooze.version !== latestVersion)
60
+ return false; // new version resets snooze
61
+ const level = Math.min(snooze.level, SNOOZE_LEVELS_MS.length - 1);
62
+ const elapsed = Date.now() - snooze.snoozed_at;
63
+ return elapsed < SNOOZE_LEVELS_MS[level];
64
+ }
65
+ /** Check if we just upgraded (marker left by upgrade command). */
66
+ function checkJustUpgraded() {
67
+ try {
68
+ const old = readFileSync(UPGRADED_PATH, "utf-8").trim();
69
+ // Clean up the marker
70
+ try {
71
+ writeFileSync(UPGRADED_PATH, "", "utf-8");
72
+ }
73
+ catch { /* ignore */ }
74
+ return old;
75
+ }
76
+ catch {
77
+ return null;
78
+ }
79
+ }
80
+ function fetchLatestVersion() {
81
+ try {
82
+ const raw = execFileSync("curl", ["-sf", "--max-time", "5", "https://registry.npmjs.org/@nex-ai/nex/latest"], { encoding: "utf-8", timeout: 6000 });
83
+ const data = JSON.parse(raw);
84
+ const ver = data.version;
85
+ if (typeof ver === "string" && /^\d+\.\d+\.\d+/.test(ver))
86
+ return ver;
87
+ return null;
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ function main() {
94
+ const current = pkg.version;
95
+ // Check if update checking is disabled
96
+ try {
97
+ const config = readJson(join(NEX_DIR, "config.json"));
98
+ if (config?.update_check === false)
99
+ return;
100
+ }
101
+ catch { /* continue */ }
102
+ // Check for just-upgraded marker
103
+ const upgradedFrom = checkJustUpgraded();
104
+ if (upgradedFrom && upgradedFrom !== current) {
105
+ console.log(`JUST_UPGRADED ${upgradedFrom} ${current}`);
106
+ // Clear stale cache
107
+ writeJson(CACHE_PATH, { current, latest: current, status: "up_to_date", checked_at: Date.now() });
108
+ return;
109
+ }
110
+ // Fast path: check cache
111
+ const cached = readJson(CACHE_PATH);
112
+ if (cached && cached.current === current) {
113
+ const age = Date.now() - cached.checked_at;
114
+ const ttl = cached.status === "up_to_date" ? UP_TO_DATE_TTL_MS : UPGRADE_AVAIL_TTL_MS;
115
+ if (age < ttl) {
116
+ if (cached.status === "upgrade_available" && !isSnoozed(cached.latest)) {
117
+ console.log(`UPGRADE_AVAILABLE ${current} ${cached.latest}`);
118
+ }
119
+ return;
120
+ }
121
+ }
122
+ // Slow path: fetch from registry
123
+ const latest = fetchLatestVersion();
124
+ if (!latest)
125
+ return; // network error — fail silently
126
+ const status = compareVersions(latest, current) > 0 ? "upgrade_available" : "up_to_date";
127
+ // Write cache
128
+ writeJson(CACHE_PATH, { current, latest, status, checked_at: Date.now() });
129
+ if (status === "upgrade_available" && !isSnoozed(latest)) {
130
+ console.log(`UPGRADE_AVAILABLE ${current} ${latest}`);
131
+ }
132
+ }
133
+ main();
134
+ //# sourceMappingURL=update-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-check.js","sourceRoot":"","sources":["../../src/plugin/update-check.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,GAAG,GAAG,OAAO,CAAC,oBAAoB,CAAwB,CAAC;AAEjE,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,CAAC,CAAC;AACxC,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC;AACtD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,gBAAgB,CAAC,CAAC;AACpD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;AAE1D,MAAM,iBAAiB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAO,SAAS;AACzD,MAAM,oBAAoB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW;AAE7D,oCAAoC;AACpC,MAAM,gBAAgB,GAAG;IACvB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;IACnB,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;IACnB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;CACxB,CAAC;AAeF,SAAS,eAAe,CAAC,CAAS,EAAE,CAAS;IAC3C,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACxD,MAAM,IAAI,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;QACzC,IAAI,IAAI,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;IAC9B,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,QAAQ,CAAI,IAAY;IAC/B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAM,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,IAAY,EAAE,IAAa;IAC5C,SAAS,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACxC,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;AAC5D,CAAC;AAED,mDAAmD;AACnD,SAAS,SAAS,CAAC,aAAqB;IACtC,MAAM,MAAM,GAAG,QAAQ,CAAc,WAAW,CAAC,CAAC;IAClD,IAAI,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC1B,IAAI,MAAM,CAAC,OAAO,KAAK,aAAa;QAAE,OAAO,KAAK,CAAC,CAAC,4BAA4B;IAEhF,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,gBAAgB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAClE,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC;IAC/C,OAAO,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAC,CAAC;AAC3C,CAAC;AAED,kEAAkE;AAClE,SAAS,iBAAiB;IACxB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;QACxD,sBAAsB;QACtB,IAAI,CAAC;YAAC,aAAa,CAAC,aAAa,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,YAAY,CAAC,CAAC;QACzE,OAAO,GAAG,CAAC;IACb,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,kBAAkB;IACzB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CACtB,MAAM,EACN,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,EAAE,+CAA+C,CAAC,EAC3E,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CACrC,CAAC;QACF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC;QACzB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;QACtE,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,IAAI;IACX,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;IAE5B,uCAAuC;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAA0B,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;QAC/E,IAAI,MAAM,EAAE,YAAY,KAAK,KAAK;YAAE,OAAO;IAC7C,CAAC;IAAC,MAAM,CAAC,CAAC,cAAc,CAAC,CAAC;IAE1B,iCAAiC;IACjC,MAAM,YAAY,GAAG,iBAAiB,EAAE,CAAC;IACzC,IAAI,YAAY,IAAI,YAAY,KAAK,OAAO,EAAE,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,iBAAiB,YAAY,IAAI,OAAO,EAAE,CAAC,CAAC;QACxD,oBAAoB;QACpB,SAAS,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAClG,OAAO;IACT,CAAC;IAED,yBAAyB;IACzB,MAAM,MAAM,GAAG,QAAQ,CAAa,UAAU,CAAC,CAAC;IAChD,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC;QAC3C,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,KAAK,YAAY,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,oBAAoB,CAAC;QACtF,IAAI,GAAG,GAAG,GAAG,EAAE,CAAC;YACd,IAAI,MAAM,CAAC,MAAM,KAAK,mBAAmB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvE,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;YAC/D,CAAC;YACD,OAAO;QACT,CAAC;IACH,CAAC;IAED,iCAAiC;IACjC,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,IAAI,CAAC,MAAM;QAAE,OAAO,CAAC,gCAAgC;IAErD,MAAM,MAAM,GAAG,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,YAAY,CAAC;IAEzF,cAAc;IACd,SAAS,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,EAAuB,CAAC,CAAC;IAEhG,IAAI,MAAM,KAAK,mBAAmB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,qBAAqB,OAAO,IAAI,MAAM,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Smart capture filtering — decides what content to send to Nex for ingestion.
3
+ * Cherry-picked from Supermemory (provider skip), Engram (dedup), MemOS Cloud (capture modes).
4
+ */
5
+ import type { NexPluginConfig } from "./config.js";
6
+ /** Message shape from OpenClaw agent_end event. */
7
+ export interface AgentMessage {
8
+ role: string;
9
+ content?: string | Array<{
10
+ type: string;
11
+ text?: string;
12
+ }>;
13
+ }
14
+ export interface CaptureFilterResult {
15
+ text: string;
16
+ skipped: false;
17
+ }
18
+ export interface CaptureFilterSkip {
19
+ reason: string;
20
+ skipped: true;
21
+ }
22
+ /**
23
+ * Process agent_end event and decide what to capture.
24
+ * Returns cleaned text or a skip reason.
25
+ */
26
+ export declare function captureFilter(messages: AgentMessage[], config: NexPluginConfig, opts?: {
27
+ messageProvider?: string;
28
+ success?: boolean;
29
+ }): CaptureFilterResult | CaptureFilterSkip;
30
+ /** Reset dedup cache (for testing). */
31
+ export declare function resetDedupCache(): void;
32
+ //# sourceMappingURL=capture-filter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-filter.d.ts","sourceRoot":"","sources":["../src/capture-filter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,mDAAmD;AACnD,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3D;AAgED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,IAAI,CAAC;CACf;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,YAAY,EAAE,EACxB,MAAM,EAAE,eAAe,EACvB,IAAI,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACrD,mBAAmB,GAAG,iBAAiB,CA8DzC;AAED,uCAAuC;AACvC,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Smart capture filtering — decides what content to send to Nex for ingestion.
3
+ * Cherry-picked from Supermemory (provider skip), Engram (dedup), MemOS Cloud (capture modes).
4
+ */
5
+ import { stripNexContext } from "./context-format.js";
6
+ /** Providers whose messages should never be auto-captured. */
7
+ const SKIP_PROVIDERS = new Set(["exec-event", "cron-event"]);
8
+ const MIN_LENGTH = 10;
9
+ const MAX_LENGTH = 50_000;
10
+ /**
11
+ * Simple content hash for deduplication.
12
+ * Uses a fast string hash — not cryptographic, just collision-resistant enough for dedup.
13
+ */
14
+ function hashContent(text) {
15
+ let hash = 0;
16
+ for (let i = 0; i < text.length; i++) {
17
+ const ch = text.charCodeAt(i);
18
+ hash = ((hash << 5) - hash + ch) | 0;
19
+ }
20
+ return hash.toString(36);
21
+ }
22
+ const DEDUP_MAX = 50;
23
+ const DEDUP_TTL_MS = 60 * 60 * 1000; // 1 hour
24
+ /** In-memory LRU cache for content hash dedup. */
25
+ const dedupCache = [];
26
+ function isDuplicate(hash) {
27
+ const now = Date.now();
28
+ // Evict expired entries
29
+ while (dedupCache.length > 0 && now - dedupCache[0].timestamp > DEDUP_TTL_MS) {
30
+ dedupCache.shift();
31
+ }
32
+ // Check for match
33
+ if (dedupCache.some((e) => e.hash === hash)) {
34
+ return true;
35
+ }
36
+ // Add to cache
37
+ dedupCache.push({ hash, timestamp: now });
38
+ // LIFO eviction if over max
39
+ if (dedupCache.length > DEDUP_MAX) {
40
+ dedupCache.shift();
41
+ }
42
+ return false;
43
+ }
44
+ /** Extract text content from an AgentMessage. */
45
+ function extractText(msg) {
46
+ if (typeof msg.content === "string")
47
+ return msg.content;
48
+ if (Array.isArray(msg.content)) {
49
+ return msg.content
50
+ .filter((p) => p.type === "text" && p.text)
51
+ .map((p) => p.text)
52
+ .join("\n");
53
+ }
54
+ return "";
55
+ }
56
+ /**
57
+ * Process agent_end event and decide what to capture.
58
+ * Returns cleaned text or a skip reason.
59
+ */
60
+ export function captureFilter(messages, config, opts) {
61
+ // Skip failed agent runs
62
+ if (opts?.success === false) {
63
+ return { skipped: true, reason: "agent run failed" };
64
+ }
65
+ // Skip system event providers
66
+ if (opts?.messageProvider && SKIP_PROVIDERS.has(opts.messageProvider)) {
67
+ return { skipped: true, reason: `provider "${opts.messageProvider}" is in skip list` };
68
+ }
69
+ if (!messages || messages.length === 0) {
70
+ return { skipped: true, reason: "no messages" };
71
+ }
72
+ let text;
73
+ if (config.captureMode === "last_turn") {
74
+ // Extract last user + last assistant message
75
+ const parts = [];
76
+ const reversed = [...messages].reverse();
77
+ const lastAssistant = reversed.find((m) => m.role === "assistant");
78
+ const lastUser = reversed.find((m) => m.role === "user");
79
+ if (lastUser)
80
+ parts.push(`User: ${extractText(lastUser)}`);
81
+ if (lastAssistant)
82
+ parts.push(`Assistant: ${extractText(lastAssistant)}`);
83
+ text = parts.join("\n\n");
84
+ }
85
+ else {
86
+ // full_session: capture all user and assistant messages
87
+ text = messages
88
+ .filter((m) => m.role === "user" || m.role === "assistant")
89
+ .map((m) => `${m.role === "user" ? "User" : "Assistant"}: ${extractText(m)}`)
90
+ .join("\n\n");
91
+ }
92
+ // Strip injected context blocks (prevent feedback loop)
93
+ text = stripNexContext(text);
94
+ // Skip slash commands
95
+ if (text.startsWith("User: /")) {
96
+ return { skipped: true, reason: "slash command" };
97
+ }
98
+ // Skip too short
99
+ if (text.length < MIN_LENGTH) {
100
+ return { skipped: true, reason: `too short (${text.length} chars)` };
101
+ }
102
+ // Skip too long
103
+ if (text.length > MAX_LENGTH) {
104
+ return { skipped: true, reason: `too long (${text.length} chars)` };
105
+ }
106
+ // Dedup check
107
+ const hash = hashContent(text);
108
+ if (isDuplicate(hash)) {
109
+ return { skipped: true, reason: "duplicate content" };
110
+ }
111
+ return { skipped: false, text };
112
+ }
113
+ /** Reset dedup cache (for testing). */
114
+ export function resetDedupCache() {
115
+ dedupCache.length = 0;
116
+ }
117
+ //# sourceMappingURL=capture-filter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture-filter.js","sourceRoot":"","sources":["../src/capture-filter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Plugin configuration parsing and validation.
3
+ * Resolves API key from config, env var, or ${VAR} interpolation.
4
+ */
5
+ export interface NexPluginConfig {
6
+ apiKey: string;
7
+ baseUrl: string;
8
+ autoRecall: boolean;
9
+ autoCapture: boolean;
10
+ captureMode: "last_turn" | "full_session";
11
+ maxRecallResults: number;
12
+ sessionTracking: boolean;
13
+ recallTimeoutMs: number;
14
+ debug: boolean;
15
+ }
16
+ export declare class ConfigError extends Error {
17
+ constructor(message: string);
18
+ }
19
+ /**
20
+ * Parse raw plugin config into a validated NexPluginConfig.
21
+ * Falls back to process.env.NEX_API_KEY if no apiKey in config.
22
+ */
23
+ export declare function parseConfig(raw?: Record<string, unknown>): NexPluginConfig;
24
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,WAAW,GAAG,cAAc,CAAC;IAC1C,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,OAAO,CAAC;CAChB;AAoBD,qBAAa,WAAY,SAAQ,KAAK;gBACxB,OAAO,EAAE,MAAM;CAI5B;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,eAAe,CA4C1E"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Plugin configuration parsing and validation.
3
+ * Resolves API key from config, env var, or ${VAR} interpolation.
4
+ */
5
+ const DEFAULTS = {
6
+ baseUrl: "https://app.nex.ai",
7
+ autoRecall: true,
8
+ autoCapture: true,
9
+ captureMode: "last_turn",
10
+ maxRecallResults: 5,
11
+ sessionTracking: true,
12
+ recallTimeoutMs: 1500,
13
+ debug: false,
14
+ };
15
+ /** Resolve ${VAR_NAME} patterns in a string value. */
16
+ function resolveEnvVars(value) {
17
+ return value.replace(/\$\{([^}]+)\}/g, (_, varName) => {
18
+ return process.env[varName.trim()] ?? "";
19
+ });
20
+ }
21
+ export class ConfigError extends Error {
22
+ constructor(message) {
23
+ super(message);
24
+ this.name = "ConfigError";
25
+ }
26
+ }
27
+ /**
28
+ * Parse raw plugin config into a validated NexPluginConfig.
29
+ * Falls back to process.env.NEX_API_KEY if no apiKey in config.
30
+ */
31
+ export function parseConfig(raw) {
32
+ const cfg = raw ?? {};
33
+ // Resolve API key: config → env var interpolation → NEX_API_KEY env
34
+ let apiKey = typeof cfg.apiKey === "string" ? resolveEnvVars(cfg.apiKey) : undefined;
35
+ if (!apiKey) {
36
+ apiKey = process.env.NEX_API_KEY;
37
+ }
38
+ if (!apiKey) {
39
+ throw new ConfigError("No API key configured. Set 'apiKey' in plugin config or export NEX_API_KEY environment variable.");
40
+ }
41
+ let baseUrl = process.env.NEX_DEV_URL
42
+ ?? (typeof cfg.baseUrl === "string" ? resolveEnvVars(cfg.baseUrl).replace(/\/+$/, "") : undefined)
43
+ ?? DEFAULTS.baseUrl;
44
+ const captureMode = cfg.captureMode;
45
+ if (captureMode !== undefined && captureMode !== "last_turn" && captureMode !== "full_session") {
46
+ throw new ConfigError(`Invalid captureMode: "${captureMode}". Must be "last_turn" or "full_session".`);
47
+ }
48
+ const maxRecallResults = typeof cfg.maxRecallResults === "number" ? cfg.maxRecallResults : DEFAULTS.maxRecallResults;
49
+ if (maxRecallResults < 1 || maxRecallResults > 20) {
50
+ throw new ConfigError(`maxRecallResults must be between 1 and 20, got ${maxRecallResults}.`);
51
+ }
52
+ const recallTimeoutMs = typeof cfg.recallTimeoutMs === "number" ? cfg.recallTimeoutMs : DEFAULTS.recallTimeoutMs;
53
+ if (recallTimeoutMs < 500 || recallTimeoutMs > 10000) {
54
+ throw new ConfigError(`recallTimeoutMs must be between 500 and 10000, got ${recallTimeoutMs}.`);
55
+ }
56
+ return {
57
+ apiKey,
58
+ baseUrl,
59
+ autoRecall: typeof cfg.autoRecall === "boolean" ? cfg.autoRecall : DEFAULTS.autoRecall,
60
+ autoCapture: typeof cfg.autoCapture === "boolean" ? cfg.autoCapture : DEFAULTS.autoCapture,
61
+ captureMode: captureMode ?? DEFAULTS.captureMode,
62
+ maxRecallResults,
63
+ sessionTracking: typeof cfg.sessionTracking === "boolean" ? cfg.sessionTracking : DEFAULTS.sessionTracking,
64
+ recallTimeoutMs,
65
+ debug: typeof cfg.debug === "boolean" ? cfg.debug : DEFAULTS.debug,
66
+ };
67
+ }
68
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * XML context formatting and stripping for recall injection.
3
+ * Wraps Nex answers in <nex-context> tags and strips them before capture.
4
+ */
5
+ export interface NexRecallResult {
6
+ answer: string;
7
+ entityCount: number;
8
+ sessionId?: string;
9
+ }
10
+ /**
11
+ * Format a Nex /ask response as an XML block for context injection.
12
+ */
13
+ export declare function formatNexContext(result: NexRecallResult): string;
14
+ /**
15
+ * Strip all <nex-context>...</nex-context> blocks from text.
16
+ * Also handles unclosed tags (strips from open tag to end of text).
17
+ */
18
+ export declare function stripNexContext(text: string): string;
19
+ /**
20
+ * Check if text contains a nex-context block.
21
+ */
22
+ export declare function hasNexContext(text: string): boolean;
23
+ //# sourceMappingURL=context-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-format.d.ts","sourceRoot":"","sources":["../src/context-format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,CAehE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMpD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * XML context formatting and stripping for recall injection.
3
+ * Wraps Nex answers in <nex-context> tags and strips them before capture.
4
+ */
5
+ const OPEN_TAG = "<nex-context>";
6
+ const CLOSE_TAG = "</nex-context>";
7
+ /**
8
+ * Format a Nex /ask response as an XML block for context injection.
9
+ */
10
+ export function formatNexContext(result) {
11
+ const parts = [
12
+ OPEN_TAG,
13
+ "The following is relevant context from the user's knowledge base. Use it to inform your response, but do not mention this block directly.",
14
+ ];
15
+ if (result.entityCount > 0) {
16
+ parts.push(`[${result.entityCount} related entities found]`);
17
+ }
18
+ parts.push("");
19
+ parts.push(result.answer);
20
+ parts.push(CLOSE_TAG);
21
+ return parts.join("\n");
22
+ }
23
+ /**
24
+ * Strip all <nex-context>...</nex-context> blocks from text.
25
+ * Also handles unclosed tags (strips from open tag to end of text).
26
+ */
27
+ export function stripNexContext(text) {
28
+ // First: strip complete blocks
29
+ let result = text.replace(/<nex-context>[\s\S]*?<\/nex-context>/g, "");
30
+ // Then: strip unclosed tags (open tag without matching close)
31
+ result = result.replace(/<nex-context>[\s\S]*/g, "");
32
+ return result.trim();
33
+ }
34
+ /**
35
+ * Check if text contains a nex-context block.
36
+ */
37
+ export function hasNexContext(text) {
38
+ return text.includes(OPEN_TAG);
39
+ }
40
+ //# sourceMappingURL=context-format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-format.js","sourceRoot":"","sources":["../src/context-format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,QAAQ,GAAG,eAAe,CAAC;AACjC,MAAM,SAAS,GAAG,gBAAgB,CAAC;AAQnC;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAuB;IACtD,MAAM,KAAK,GAAa;QACtB,QAAQ;QACR,2IAA2I;KAC5I,CAAC;IAEF,IAAI,MAAM,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;QAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC,WAAW,0BAA0B,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAEtB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,+BAA+B;IAC/B,IAAI,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,uCAAuC,EAAE,EAAE,CAAC,CAAC;IACvE,8DAA8D;IAC9D,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * File scanner for OpenClaw plugin.
3
+ * Discovers text files, tracks changes via SHA-256 content hash,
4
+ * and ingests new/changed files into Nex.
5
+ */
6
+ import { NexClient } from "./nex-client.js";
7
+ export interface ScanResult {
8
+ scanned: number;
9
+ skipped: number;
10
+ errors: number;
11
+ files: Array<{
12
+ path: string;
13
+ status: string;
14
+ reason?: string;
15
+ }>;
16
+ }
17
+ export declare function scanFiles(dir: string, client: NexClient, opts?: {
18
+ extensions?: string[];
19
+ maxFiles?: number;
20
+ depth?: number;
21
+ force?: boolean;
22
+ }): Promise<ScanResult>;
23
+ //# sourceMappingURL=file-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-scanner.d.ts","sourceRoot":"","sources":["../src/file-scanner.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,EAAE,SAAS,EAAuB,MAAM,iBAAiB,CAAC;AAejE,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE;AAsED,wBAAsB,SAAS,CAC7B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,SAAS,EACjB,IAAI,CAAC,EAAE;IACL,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,GACA,OAAO,CAAC,UAAU,CAAC,CAmDrB"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * File scanner for OpenClaw plugin.
3
+ * Discovers text files, tracks changes via SHA-256 content hash,
4
+ * and ingests new/changed files into Nex.
5
+ */
6
+ import { createHash } from "node:crypto";
7
+ import { readFileSync, writeFileSync, mkdirSync, statSync, readdirSync } from "node:fs";
8
+ import { join, extname, resolve, dirname } from "node:path";
9
+ import { homedir } from "node:os";
10
+ // --- Constants ---
11
+ const MANIFEST_PATH = join(homedir(), ".nex", "file-scan-manifest.json");
12
+ const DEFAULT_EXTENSIONS = [
13
+ ".md", ".txt", ".rtf", ".html", ".htm",
14
+ ".csv", ".tsv", ".json", ".yaml", ".yml", ".toml", ".xml",
15
+ ".js", ".ts", ".jsx", ".tsx", ".py", ".rb", ".go", ".rs", ".java",
16
+ ".sh", ".bash", ".zsh", ".fish",
17
+ ".org", ".rst", ".adoc", ".tex", ".log",
18
+ ".env", ".ini", ".cfg", ".conf", ".properties",
19
+ ];
20
+ const SKIP_DIRS = new Set([
21
+ "node_modules", ".git", "dist", "build", ".next", "__pycache__", ".venv",
22
+ ".cache", ".turbo", "coverage", ".nyc_output",
23
+ ]);
24
+ // --- Manifest ---
25
+ function loadManifest() {
26
+ try {
27
+ const raw = readFileSync(MANIFEST_PATH, "utf-8");
28
+ const data = JSON.parse(raw);
29
+ if (data.version === 1 && typeof data.files === "object")
30
+ return data;
31
+ }
32
+ catch { /* missing or corrupt */ }
33
+ return { version: 1, files: {} };
34
+ }
35
+ function saveManifest(manifest) {
36
+ mkdirSync(dirname(MANIFEST_PATH), { recursive: true });
37
+ writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
38
+ }
39
+ function discoverFiles(dir, extensions, maxDepth, depth = 0) {
40
+ if (depth > maxDepth)
41
+ return [];
42
+ const results = [];
43
+ let entries;
44
+ try {
45
+ entries = readdirSync(dir);
46
+ }
47
+ catch {
48
+ return results;
49
+ }
50
+ for (const entry of entries) {
51
+ if (SKIP_DIRS.has(entry))
52
+ continue;
53
+ const fullPath = join(dir, entry);
54
+ let stat;
55
+ try {
56
+ stat = statSync(fullPath);
57
+ }
58
+ catch {
59
+ continue;
60
+ }
61
+ if (stat.isDirectory()) {
62
+ results.push(...discoverFiles(fullPath, extensions, maxDepth, depth + 1));
63
+ }
64
+ else if (stat.isFile() && extensions.has(extname(entry).toLowerCase())) {
65
+ results.push({ path: fullPath, size: stat.size, mtime: stat.mtimeMs });
66
+ }
67
+ }
68
+ return results;
69
+ }
70
+ function hashFile(filePath) {
71
+ const content = readFileSync(filePath);
72
+ return "sha256-" + createHash("sha256").update(content).digest("hex");
73
+ }
74
+ // --- Scanner ---
75
+ export async function scanFiles(dir, client, opts) {
76
+ const absDir = resolve(dir);
77
+ const extensions = opts?.extensions ?? DEFAULT_EXTENSIONS;
78
+ const extSet = new Set(extensions.map((e) => (e.startsWith(".") ? e : `.${e}`).toLowerCase()));
79
+ const maxFiles = opts?.maxFiles ?? 5;
80
+ const maxDepth = opts?.depth ?? 20;
81
+ const force = opts?.force ?? false;
82
+ const discovered = discoverFiles(absDir, extSet, maxDepth);
83
+ discovered.sort((a, b) => b.mtime - a.mtime);
84
+ const candidates = discovered.slice(0, maxFiles);
85
+ const manifest = force ? { version: 1, files: {} } : loadManifest();
86
+ const result = { scanned: 0, skipped: 0, errors: 0, files: [] };
87
+ for (const file of candidates) {
88
+ const hash = hashFile(file.path);
89
+ const existing = manifest.files[file.path];
90
+ if (existing && existing.hash === hash && !force) {
91
+ result.skipped++;
92
+ result.files.push({ path: file.path, status: "skipped", reason: "unchanged" });
93
+ continue;
94
+ }
95
+ try {
96
+ const content = readFileSync(file.path, "utf-8");
97
+ if (!content.trim()) {
98
+ result.skipped++;
99
+ result.files.push({ path: file.path, status: "skipped", reason: "empty" });
100
+ continue;
101
+ }
102
+ await client.ingest(content, `file-scan:${file.path}`);
103
+ manifest.files[file.path] = {
104
+ hash,
105
+ size: file.size,
106
+ scanned_at: new Date().toISOString(),
107
+ };
108
+ result.scanned++;
109
+ result.files.push({ path: file.path, status: "ingested", reason: existing ? "changed" : "new" });
110
+ }
111
+ catch (err) {
112
+ result.errors++;
113
+ result.files.push({ path: file.path, status: "error", reason: err instanceof Error ? err.message : String(err) });
114
+ }
115
+ }
116
+ saveManifest(manifest);
117
+ return result;
118
+ }
119
+ //# sourceMappingURL=file-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-scanner.js","sourceRoot":"","sources":["../src/file-scanner.ts"],"names":[],"mappings":"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"}