haansi 0.1.2 → 0.1.4
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/dist/haansi.js +216 -722
- package/package.json +1 -1
package/dist/haansi.js
CHANGED
|
@@ -44,10 +44,10 @@ function prompt(question) {
|
|
|
44
44
|
input: process.stdin,
|
|
45
45
|
output: process.stdout
|
|
46
46
|
});
|
|
47
|
-
return new Promise((
|
|
47
|
+
return new Promise((resolve4) => {
|
|
48
48
|
rl.question(question, (answer) => {
|
|
49
49
|
rl.close();
|
|
50
|
-
|
|
50
|
+
resolve4(answer.trim());
|
|
51
51
|
});
|
|
52
52
|
});
|
|
53
53
|
}
|
|
@@ -380,7 +380,7 @@ var require_main = __commonJS({
|
|
|
380
380
|
return { parsed: parsedAll };
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
|
-
function
|
|
383
|
+
function config5(options) {
|
|
384
384
|
if (_dotenvKey(options).length === 0) {
|
|
385
385
|
return DotenvModule.configDotenv(options);
|
|
386
386
|
}
|
|
@@ -447,7 +447,7 @@ var require_main = __commonJS({
|
|
|
447
447
|
configDotenv,
|
|
448
448
|
_configVault,
|
|
449
449
|
_parseVault,
|
|
450
|
-
config:
|
|
450
|
+
config: config5,
|
|
451
451
|
decrypt,
|
|
452
452
|
parse: parse4,
|
|
453
453
|
populate
|
|
@@ -515,506 +515,6 @@ var init_logger = __esm({
|
|
|
515
515
|
}
|
|
516
516
|
});
|
|
517
517
|
|
|
518
|
-
// ../service-capture/claude-sessions/reader.ts
|
|
519
|
-
var import_fs, import_fs2, import_path, import_os, logger, CLAUDE_DIR, PROJECTS_DIR, ClaudeSessionReader;
|
|
520
|
-
var init_reader = __esm({
|
|
521
|
-
"../service-capture/claude-sessions/reader.ts"() {
|
|
522
|
-
"use strict";
|
|
523
|
-
import_fs = require("fs");
|
|
524
|
-
import_fs2 = require("fs");
|
|
525
|
-
import_path = require("path");
|
|
526
|
-
import_os = require("os");
|
|
527
|
-
init_logger();
|
|
528
|
-
logger = createLogger("claude-session-reader");
|
|
529
|
-
CLAUDE_DIR = (0, import_path.join)((0, import_os.homedir)(), ".claude");
|
|
530
|
-
PROJECTS_DIR = (0, import_path.join)(CLAUDE_DIR, "projects");
|
|
531
|
-
ClaudeSessionReader = class {
|
|
532
|
-
/**
|
|
533
|
-
* Convert a filesystem path to the ~/.claude/projects/ directory name format.
|
|
534
|
-
* Claude Code replaces both '/' and '_' with '-'.
|
|
535
|
-
* e.g. /Users/foo/git_repos/haansi-platform → -Users-foo-git-repos-haansi-platform
|
|
536
|
-
*/
|
|
537
|
-
static projectDirName(absolutePath) {
|
|
538
|
-
return absolutePath.replace(/[/_]/g, "-");
|
|
539
|
-
}
|
|
540
|
-
/**
|
|
541
|
-
* Returns the ~/.claude/projects/[encoded-path]/ directory for a given project.
|
|
542
|
-
*/
|
|
543
|
-
static getProjectSessionsDir(projectAbsolutePath) {
|
|
544
|
-
return (0, import_path.join)(PROJECTS_DIR, this.projectDirName(projectAbsolutePath));
|
|
545
|
-
}
|
|
546
|
-
/**
|
|
547
|
-
* List all project directories in ~/.claude/projects/.
|
|
548
|
-
*/
|
|
549
|
-
listProjectDirs() {
|
|
550
|
-
if (!(0, import_fs.existsSync)(PROJECTS_DIR)) return [];
|
|
551
|
-
return (0, import_fs2.readdirSync)(PROJECTS_DIR).map((name) => (0, import_path.join)(PROJECTS_DIR, name)).filter((p) => (0, import_fs2.statSync)(p).isDirectory());
|
|
552
|
-
}
|
|
553
|
-
/**
|
|
554
|
-
* Read and parse sessions-index.json for a given project sessions directory.
|
|
555
|
-
* Returns null if the file doesn't exist or can't be parsed.
|
|
556
|
-
*/
|
|
557
|
-
readSessionsIndex(sessionsDir) {
|
|
558
|
-
const indexPath = (0, import_path.join)(sessionsDir, "sessions-index.json");
|
|
559
|
-
if (!(0, import_fs.existsSync)(indexPath)) return null;
|
|
560
|
-
try {
|
|
561
|
-
const raw = (0, import_fs.readFileSync)(indexPath, "utf-8");
|
|
562
|
-
return JSON.parse(raw);
|
|
563
|
-
} catch (err) {
|
|
564
|
-
logger.warn("Failed to parse sessions-index.json", {
|
|
565
|
-
sessionsDir,
|
|
566
|
-
err: String(err)
|
|
567
|
-
});
|
|
568
|
-
return null;
|
|
569
|
-
}
|
|
570
|
-
}
|
|
571
|
-
/**
|
|
572
|
-
* Parse all JSONL entries from a session file. Returns raw entries in file order.
|
|
573
|
-
* Lines that fail to parse are skipped with a warning.
|
|
574
|
-
*/
|
|
575
|
-
parseSessionFile(filePath) {
|
|
576
|
-
if (!(0, import_fs.existsSync)(filePath)) {
|
|
577
|
-
logger.warn("Session file not found", { filePath });
|
|
578
|
-
return [];
|
|
579
|
-
}
|
|
580
|
-
const entries = [];
|
|
581
|
-
const lines = (0, import_fs.readFileSync)(filePath, "utf-8").split("\n");
|
|
582
|
-
for (let i = 0; i < lines.length; i++) {
|
|
583
|
-
const line = lines[i].trim();
|
|
584
|
-
if (!line) continue;
|
|
585
|
-
try {
|
|
586
|
-
entries.push(JSON.parse(line));
|
|
587
|
-
} catch {
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
return entries;
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Extract plain text from a message content value.
|
|
594
|
-
* Handles both legacy string content and structured block arrays.
|
|
595
|
-
*/
|
|
596
|
-
extractText(content) {
|
|
597
|
-
if (typeof content === "string") return content;
|
|
598
|
-
return content.filter(
|
|
599
|
-
(b) => b.type === "text" || b.type === "thinking"
|
|
600
|
-
).map((b) => "text" in b ? b.text : b.thinking ?? "").join("\n").trim();
|
|
601
|
-
}
|
|
602
|
-
/**
|
|
603
|
-
* Extract tool_use blocks from a message content array (assistant turns).
|
|
604
|
-
*/
|
|
605
|
-
extractToolCalls(content) {
|
|
606
|
-
if (typeof content === "string") return [];
|
|
607
|
-
return content.filter(
|
|
608
|
-
(b) => b.type === "tool_use"
|
|
609
|
-
).map((b) => ({
|
|
610
|
-
id: b.id,
|
|
611
|
-
name: b.name,
|
|
612
|
-
input: b.input
|
|
613
|
-
}));
|
|
614
|
-
}
|
|
615
|
-
/**
|
|
616
|
-
* Extract tool_result blocks from a message content array (user turns).
|
|
617
|
-
*/
|
|
618
|
-
extractToolResults(content) {
|
|
619
|
-
if (typeof content === "string") return [];
|
|
620
|
-
return content.filter(
|
|
621
|
-
(b) => b.type === "tool_result"
|
|
622
|
-
).map((b) => {
|
|
623
|
-
const rawContent = b.content;
|
|
624
|
-
let text;
|
|
625
|
-
if (typeof rawContent === "string") {
|
|
626
|
-
text = rawContent;
|
|
627
|
-
} else if (Array.isArray(rawContent)) {
|
|
628
|
-
text = rawContent.map((item) => "text" in item ? item.text ?? "" : "").join("\n");
|
|
629
|
-
} else {
|
|
630
|
-
text = "";
|
|
631
|
-
}
|
|
632
|
-
return {
|
|
633
|
-
toolUseId: b.tool_use_id,
|
|
634
|
-
content: text,
|
|
635
|
-
isError: b.is_error ?? false
|
|
636
|
-
};
|
|
637
|
-
});
|
|
638
|
-
}
|
|
639
|
-
/**
|
|
640
|
-
* Convert raw JSONL entries into ordered ClauseTurns.
|
|
641
|
-
* Reconstruction order: build a parentUuid → child map, then walk from roots.
|
|
642
|
-
* Falls back to file order if the graph has cycles or disconnected nodes.
|
|
643
|
-
*/
|
|
644
|
-
reconstructThread(entries) {
|
|
645
|
-
const messageEntries = entries.filter(
|
|
646
|
-
(e) => (e.type === "user" || e.type === "assistant") && e.message
|
|
647
|
-
);
|
|
648
|
-
if (messageEntries.length === 0) return [];
|
|
649
|
-
const byUuid = /* @__PURE__ */ new Map();
|
|
650
|
-
const childrenOf = /* @__PURE__ */ new Map();
|
|
651
|
-
for (const entry of messageEntries) {
|
|
652
|
-
byUuid.set(entry.uuid, entry);
|
|
653
|
-
const parent = entry.parentUuid ?? "__root__";
|
|
654
|
-
if (!childrenOf.has(parent)) childrenOf.set(parent, []);
|
|
655
|
-
childrenOf.get(parent).push(entry.uuid);
|
|
656
|
-
}
|
|
657
|
-
const ordered = [];
|
|
658
|
-
const visited = /* @__PURE__ */ new Set();
|
|
659
|
-
const queue = childrenOf.get("__root__") ?? [];
|
|
660
|
-
while (queue.length > 0) {
|
|
661
|
-
const uuid3 = queue.shift();
|
|
662
|
-
if (visited.has(uuid3)) continue;
|
|
663
|
-
visited.add(uuid3);
|
|
664
|
-
const entry = byUuid.get(uuid3);
|
|
665
|
-
if (entry) {
|
|
666
|
-
ordered.push(entry);
|
|
667
|
-
const children = childrenOf.get(uuid3) ?? [];
|
|
668
|
-
queue.push(...children);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
for (const entry of messageEntries) {
|
|
672
|
-
if (!visited.has(entry.uuid)) {
|
|
673
|
-
ordered.push(entry);
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
return ordered.map((entry) => {
|
|
677
|
-
const content = entry.message.content;
|
|
678
|
-
return {
|
|
679
|
-
uuid: entry.uuid,
|
|
680
|
-
parentUuid: entry.parentUuid,
|
|
681
|
-
role: entry.message.role,
|
|
682
|
-
text: this.extractText(content),
|
|
683
|
-
toolCalls: this.extractToolCalls(content),
|
|
684
|
-
toolResults: this.extractToolResults(content),
|
|
685
|
-
timestamp: entry.timestamp ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
686
|
-
isSidechain: entry.isSidechain ?? false,
|
|
687
|
-
agentId: entry.agentId,
|
|
688
|
-
gitBranch: entry.gitBranch
|
|
689
|
-
};
|
|
690
|
-
});
|
|
691
|
-
}
|
|
692
|
-
/**
|
|
693
|
-
* Read a single session JSONL file and return a ClaudeSession.
|
|
694
|
-
* Uses index metadata when provided for faster startup (avoids full parse for summary/firstPrompt).
|
|
695
|
-
*/
|
|
696
|
-
readSession(filePath, indexEntry) {
|
|
697
|
-
const entries = this.parseSessionFile(filePath);
|
|
698
|
-
const turns = this.reconstructThread(entries);
|
|
699
|
-
const stat = (0, import_fs2.statSync)(filePath);
|
|
700
|
-
const sessionId = indexEntry?.sessionId ?? (0, import_path.basename)(filePath, ".jsonl");
|
|
701
|
-
const summaryEntry = entries.find((e) => e.type === "summary");
|
|
702
|
-
const summary = indexEntry?.summary ?? summaryEntry?.summary;
|
|
703
|
-
const firstUserTurn = turns.find(
|
|
704
|
-
(t) => t.role === "user" && !t.isSidechain
|
|
705
|
-
);
|
|
706
|
-
const firstPrompt = indexEntry?.firstPrompt ?? firstUserTurn?.text?.slice(0, 500) ?? "";
|
|
707
|
-
return {
|
|
708
|
-
sessionId,
|
|
709
|
-
projectPath: indexEntry?.projectPath ?? "",
|
|
710
|
-
filePath,
|
|
711
|
-
fileMtime: stat.mtimeMs,
|
|
712
|
-
gitBranch: indexEntry?.gitBranch ?? turns[0]?.gitBranch,
|
|
713
|
-
firstPrompt,
|
|
714
|
-
summary,
|
|
715
|
-
created: indexEntry?.created ?? turns[0]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
716
|
-
modified: indexEntry?.modified ?? turns[turns.length - 1]?.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
717
|
-
turns,
|
|
718
|
-
subagentSessions: [],
|
|
719
|
-
// populated separately by ingester if needed
|
|
720
|
-
messageCount: turns.length
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
/**
|
|
724
|
-
* Read all sessions for a project directory using the sessions-index for efficiency.
|
|
725
|
-
* Only reads JSONL files that are present and parseable.
|
|
726
|
-
*/
|
|
727
|
-
readAllSessions(sessionsDir) {
|
|
728
|
-
const index = this.readSessionsIndex(sessionsDir);
|
|
729
|
-
const sessions = [];
|
|
730
|
-
if (!index) {
|
|
731
|
-
logger.warn(
|
|
732
|
-
"No sessions-index.json found, falling back to directory scan",
|
|
733
|
-
{ sessionsDir }
|
|
734
|
-
);
|
|
735
|
-
return this.scanSessionFiles(sessionsDir);
|
|
736
|
-
}
|
|
737
|
-
const mainEntries = index.entries.filter((e) => !e.isSidechain);
|
|
738
|
-
for (const entry of mainEntries) {
|
|
739
|
-
if (!(0, import_fs.existsSync)(entry.fullPath)) continue;
|
|
740
|
-
try {
|
|
741
|
-
const session = this.readSession(entry.fullPath, entry);
|
|
742
|
-
sessions.push(session);
|
|
743
|
-
} catch (err) {
|
|
744
|
-
logger.warn("Failed to read session", {
|
|
745
|
-
sessionId: entry.sessionId,
|
|
746
|
-
err: String(err)
|
|
747
|
-
});
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
logger.info(`Read ${sessions.length} sessions from index`, { sessionsDir });
|
|
751
|
-
return sessions;
|
|
752
|
-
}
|
|
753
|
-
/**
|
|
754
|
-
* Fallback: scan a directory for *.jsonl files and parse each one.
|
|
755
|
-
* Skips the subagents/ subdirectory (handled separately).
|
|
756
|
-
*/
|
|
757
|
-
scanSessionFiles(sessionsDir) {
|
|
758
|
-
if (!(0, import_fs.existsSync)(sessionsDir)) return [];
|
|
759
|
-
const sessions = [];
|
|
760
|
-
const entries = (0, import_fs2.readdirSync)(sessionsDir);
|
|
761
|
-
for (const name of entries) {
|
|
762
|
-
if (!name.endsWith(".jsonl")) continue;
|
|
763
|
-
const filePath = (0, import_path.join)(sessionsDir, name);
|
|
764
|
-
try {
|
|
765
|
-
sessions.push(this.readSession(filePath));
|
|
766
|
-
} catch (err) {
|
|
767
|
-
logger.warn("Failed to parse session file", { name, err: String(err) });
|
|
768
|
-
}
|
|
769
|
-
}
|
|
770
|
-
return sessions;
|
|
771
|
-
}
|
|
772
|
-
};
|
|
773
|
-
}
|
|
774
|
-
});
|
|
775
|
-
|
|
776
|
-
// ../service-capture/claude-sessions/ingester.ts
|
|
777
|
-
async function main() {
|
|
778
|
-
const projectPath = process.env.CLAUDE_PROJECT_PATH ?? process.cwd();
|
|
779
|
-
const forceAll = process.argv.includes("--force-all");
|
|
780
|
-
logger2.info("Claude Session Ingester starting", { projectPath, forceAll });
|
|
781
|
-
const ingester = new ClaudeSessionIngester({ projectPath });
|
|
782
|
-
const result = await ingester.scan({ forceAll });
|
|
783
|
-
console.log("\n=== Scan Results ===");
|
|
784
|
-
console.log(`Total sessions scanned : ${result.totalScanned}`);
|
|
785
|
-
console.log(`New sessions : ${result.newSessions.length}`);
|
|
786
|
-
console.log(`Updated sessions : ${result.updatedSessions.length}`);
|
|
787
|
-
console.log(`Unchanged (skipped) : ${result.unchangedCount}`);
|
|
788
|
-
const toProcess = [...result.newSessions, ...result.updatedSessions];
|
|
789
|
-
if (toProcess.length === 0) {
|
|
790
|
-
console.log("\nNo new sessions to process.");
|
|
791
|
-
return;
|
|
792
|
-
}
|
|
793
|
-
console.log("\n=== Sessions to Process ===");
|
|
794
|
-
for (const session of toProcess) {
|
|
795
|
-
const preview = session.firstPrompt.slice(0, 100).replace(/\n/g, " ");
|
|
796
|
-
console.log(
|
|
797
|
-
`
|
|
798
|
-
[${session.sessionId.slice(0, 8)}] ${session.gitBranch ?? "unknown-branch"}`
|
|
799
|
-
);
|
|
800
|
-
console.log(` Turns : ${session.messageCount}`);
|
|
801
|
-
console.log(` Created : ${session.created.slice(0, 19)}`);
|
|
802
|
-
console.log(` Modified : ${session.modified.slice(0, 19)}`);
|
|
803
|
-
console.log(` Preview : ${preview}${preview.length === 100 ? "\u2026" : ""}`);
|
|
804
|
-
}
|
|
805
|
-
console.log(
|
|
806
|
-
"\n\u2713 Reader and ingester working. Next step: resolution detection."
|
|
807
|
-
);
|
|
808
|
-
}
|
|
809
|
-
var import_dotenv, import_path2, import_fs3, import_os2, logger2, DEFAULT_STATE_FILE, STATE_VERSION, ClaudeSessionIngester;
|
|
810
|
-
var init_ingester = __esm({
|
|
811
|
-
"../service-capture/claude-sessions/ingester.ts"() {
|
|
812
|
-
"use strict";
|
|
813
|
-
import_dotenv = __toESM(require_main());
|
|
814
|
-
import_path2 = require("path");
|
|
815
|
-
import_fs3 = require("fs");
|
|
816
|
-
import_os2 = require("os");
|
|
817
|
-
init_reader();
|
|
818
|
-
init_logger();
|
|
819
|
-
(0, import_dotenv.config)({ path: (0, import_path2.resolve)(__dirname, "../../../.env") });
|
|
820
|
-
logger2 = createLogger("claude-session-ingester");
|
|
821
|
-
DEFAULT_STATE_FILE = (0, import_path2.join)(
|
|
822
|
-
(0, import_os2.homedir)(),
|
|
823
|
-
".claude",
|
|
824
|
-
"haansi-mining-state.json"
|
|
825
|
-
);
|
|
826
|
-
STATE_VERSION = 1;
|
|
827
|
-
ClaudeSessionIngester = class {
|
|
828
|
-
reader;
|
|
829
|
-
stateFile;
|
|
830
|
-
projectPath;
|
|
831
|
-
constructor(options) {
|
|
832
|
-
this.reader = new ClaudeSessionReader();
|
|
833
|
-
this.projectPath = options?.projectPath ?? process.env.CLAUDE_PROJECT_PATH ?? process.cwd();
|
|
834
|
-
this.stateFile = options?.stateFile ?? process.env.CLAUDE_STATE_FILE ?? DEFAULT_STATE_FILE;
|
|
835
|
-
}
|
|
836
|
-
// ---------------------------------------------------------------------------
|
|
837
|
-
// State management
|
|
838
|
-
// ---------------------------------------------------------------------------
|
|
839
|
-
loadState() {
|
|
840
|
-
if (!(0, import_fs3.existsSync)(this.stateFile)) {
|
|
841
|
-
return {
|
|
842
|
-
version: STATE_VERSION,
|
|
843
|
-
lastRunAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
844
|
-
projectPath: this.projectPath,
|
|
845
|
-
processed: {}
|
|
846
|
-
};
|
|
847
|
-
}
|
|
848
|
-
try {
|
|
849
|
-
return JSON.parse((0, import_fs3.readFileSync)(this.stateFile, "utf-8"));
|
|
850
|
-
} catch {
|
|
851
|
-
logger2.warn("Could not parse state file, starting fresh", {
|
|
852
|
-
stateFile: this.stateFile
|
|
853
|
-
});
|
|
854
|
-
return {
|
|
855
|
-
version: STATE_VERSION,
|
|
856
|
-
lastRunAt: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
857
|
-
projectPath: this.projectPath,
|
|
858
|
-
processed: {}
|
|
859
|
-
};
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
saveState(state) {
|
|
863
|
-
(0, import_fs3.writeFileSync)(this.stateFile, JSON.stringify(state, null, 2), "utf-8");
|
|
864
|
-
logger2.debug("State saved", { stateFile: this.stateFile });
|
|
865
|
-
}
|
|
866
|
-
// ---------------------------------------------------------------------------
|
|
867
|
-
// Core scan logic
|
|
868
|
-
// ---------------------------------------------------------------------------
|
|
869
|
-
/**
|
|
870
|
-
* Scan for new or changed sessions relative to the persisted state.
|
|
871
|
-
* Returns only sessions that need processing (new or mtime changed).
|
|
872
|
-
*/
|
|
873
|
-
async scan(options) {
|
|
874
|
-
const sessionsDir = ClaudeSessionReader.getProjectSessionsDir(
|
|
875
|
-
this.projectPath
|
|
876
|
-
);
|
|
877
|
-
if (!(0, import_fs3.existsSync)(sessionsDir)) {
|
|
878
|
-
logger2.warn("No Claude sessions directory found for project", {
|
|
879
|
-
projectPath: this.projectPath,
|
|
880
|
-
sessionsDir
|
|
881
|
-
});
|
|
882
|
-
return {
|
|
883
|
-
newSessions: [],
|
|
884
|
-
updatedSessions: [],
|
|
885
|
-
unchangedCount: 0,
|
|
886
|
-
totalScanned: 0
|
|
887
|
-
};
|
|
888
|
-
}
|
|
889
|
-
logger2.info("Scanning for Claude sessions", { sessionsDir });
|
|
890
|
-
return this.scanDirs([sessionsDir], options);
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* Scan ALL project directories under ~/.claude/projects/ regardless of project path.
|
|
894
|
-
* Returns combined results across every project.
|
|
895
|
-
*/
|
|
896
|
-
async scanAll(options) {
|
|
897
|
-
const dirs = this.reader.listProjectDirs();
|
|
898
|
-
logger2.info("Scanning all Claude project directories", {
|
|
899
|
-
count: dirs.length
|
|
900
|
-
});
|
|
901
|
-
return this.scanDirs(dirs, options);
|
|
902
|
-
}
|
|
903
|
-
async scanDirs(sessionsDirs, options) {
|
|
904
|
-
const state = this.loadState();
|
|
905
|
-
const newSessions = [];
|
|
906
|
-
const updatedSessions = [];
|
|
907
|
-
let unchangedCount = 0;
|
|
908
|
-
let totalScanned = 0;
|
|
909
|
-
for (const sessionsDir of sessionsDirs) {
|
|
910
|
-
const allSessions = this.reader.readAllSessions(sessionsDir);
|
|
911
|
-
totalScanned += allSessions.length;
|
|
912
|
-
for (const session of allSessions) {
|
|
913
|
-
if (session.turns.length < 2) {
|
|
914
|
-
unchangedCount++;
|
|
915
|
-
continue;
|
|
916
|
-
}
|
|
917
|
-
const record2 = state.processed[session.sessionId];
|
|
918
|
-
if (options?.forceAll) {
|
|
919
|
-
newSessions.push(session);
|
|
920
|
-
continue;
|
|
921
|
-
}
|
|
922
|
-
if (!record2) {
|
|
923
|
-
newSessions.push(session);
|
|
924
|
-
} else if (session.fileMtime > record2.fileMtime) {
|
|
925
|
-
updatedSessions.push(session);
|
|
926
|
-
} else {
|
|
927
|
-
unchangedCount++;
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
logger2.info("Scan complete", {
|
|
932
|
-
dirs: sessionsDirs.length,
|
|
933
|
-
total: totalScanned,
|
|
934
|
-
new: newSessions.length,
|
|
935
|
-
updated: updatedSessions.length,
|
|
936
|
-
unchanged: unchangedCount
|
|
937
|
-
});
|
|
938
|
-
return { newSessions, updatedSessions, unchangedCount, totalScanned };
|
|
939
|
-
}
|
|
940
|
-
/**
|
|
941
|
-
* Mark a session as processed and persist state.
|
|
942
|
-
*/
|
|
943
|
-
/**
|
|
944
|
-
* Return the number of lines already uploaded for this session (0 if unknown).
|
|
945
|
-
*/
|
|
946
|
-
getUploadedLineCount(sessionId) {
|
|
947
|
-
const state = this.loadState();
|
|
948
|
-
return state.processed[sessionId]?.uploadedLineCount ?? 0;
|
|
949
|
-
}
|
|
950
|
-
/**
|
|
951
|
-
* Return whether this session was previously finalized.
|
|
952
|
-
*/
|
|
953
|
-
isFinalized(sessionId) {
|
|
954
|
-
const state = this.loadState();
|
|
955
|
-
return state.processed[sessionId]?.finalized ?? false;
|
|
956
|
-
}
|
|
957
|
-
/**
|
|
958
|
-
* Return all session IDs that are tracked but not yet finalized.
|
|
959
|
-
* These are sessions that were previously uploaded as open and may
|
|
960
|
-
* need finalization if they no longer appear in the scan results.
|
|
961
|
-
*/
|
|
962
|
-
getOpenSessionIds() {
|
|
963
|
-
const state = this.loadState();
|
|
964
|
-
return Object.values(state.processed).filter((r) => r.finalized === false).map((r) => r.sessionId);
|
|
965
|
-
}
|
|
966
|
-
/**
|
|
967
|
-
* Mark a session as finalized without re-reading its file.
|
|
968
|
-
* Used when a previously-open session is no longer receiving new lines.
|
|
969
|
-
*/
|
|
970
|
-
markFinalized(sessionId) {
|
|
971
|
-
const state = this.loadState();
|
|
972
|
-
const record2 = state.processed[sessionId];
|
|
973
|
-
if (record2) {
|
|
974
|
-
record2.finalized = true;
|
|
975
|
-
record2.processedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
976
|
-
state.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
977
|
-
this.saveState(state);
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
markProcessed(session, status, extras) {
|
|
981
|
-
const state = this.loadState();
|
|
982
|
-
state.processed[session.sessionId] = {
|
|
983
|
-
sessionId: session.sessionId,
|
|
984
|
-
filePath: session.filePath,
|
|
985
|
-
fileMtime: session.fileMtime,
|
|
986
|
-
processedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
987
|
-
status,
|
|
988
|
-
...extras
|
|
989
|
-
};
|
|
990
|
-
state.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
991
|
-
this.saveState(state);
|
|
992
|
-
}
|
|
993
|
-
/**
|
|
994
|
-
* Return persisted processing records (useful for reporting).
|
|
995
|
-
*/
|
|
996
|
-
getState() {
|
|
997
|
-
return this.loadState();
|
|
998
|
-
}
|
|
999
|
-
/**
|
|
1000
|
-
* Reset state for a specific session (forces re-processing on next scan).
|
|
1001
|
-
*/
|
|
1002
|
-
resetSession(sessionId) {
|
|
1003
|
-
const state = this.loadState();
|
|
1004
|
-
delete state.processed[sessionId];
|
|
1005
|
-
this.saveState(state);
|
|
1006
|
-
logger2.info("Reset session state", { sessionId });
|
|
1007
|
-
}
|
|
1008
|
-
};
|
|
1009
|
-
if (require.main === module) {
|
|
1010
|
-
main().catch((err) => {
|
|
1011
|
-
logger2.error("Ingester failed", err);
|
|
1012
|
-
process.exit(1);
|
|
1013
|
-
});
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
});
|
|
1017
|
-
|
|
1018
518
|
// ../service-capture/claude-sessions/collector.ts
|
|
1019
519
|
var collector_exports = {};
|
|
1020
520
|
__export(collector_exports, {
|
|
@@ -1029,34 +529,85 @@ function edgeScrub(content) {
|
|
|
1029
529
|
}
|
|
1030
530
|
function resolveToken() {
|
|
1031
531
|
if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
|
|
1032
|
-
if ((0,
|
|
532
|
+
if ((0, import_fs.existsSync)(CREDENTIALS_FILE2)) {
|
|
1033
533
|
try {
|
|
1034
|
-
const creds = JSON.parse((0,
|
|
534
|
+
const creds = JSON.parse((0, import_fs.readFileSync)(CREDENTIALS_FILE2, "utf-8"));
|
|
1035
535
|
if (creds.token) return creds.token;
|
|
1036
536
|
} catch {
|
|
1037
|
-
|
|
537
|
+
logger.warn("Could not parse credentials file", {
|
|
1038
538
|
file: CREDENTIALS_FILE2
|
|
1039
539
|
});
|
|
1040
540
|
}
|
|
1041
541
|
}
|
|
1042
542
|
return null;
|
|
1043
543
|
}
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
544
|
+
function discoverSessions() {
|
|
545
|
+
if (!(0, import_fs.existsSync)(CLAUDE_PROJECTS_DIR)) return [];
|
|
546
|
+
const sessions = [];
|
|
547
|
+
const projectDirs = (0, import_fs.readdirSync)(CLAUDE_PROJECTS_DIR).map((name) => (0, import_path.join)(CLAUDE_PROJECTS_DIR, name)).filter((p) => {
|
|
548
|
+
try {
|
|
549
|
+
return (0, import_fs.statSync)(p).isDirectory();
|
|
550
|
+
} catch {
|
|
551
|
+
return false;
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
for (const dir of projectDirs) {
|
|
555
|
+
const files = (0, import_fs.readdirSync)(dir).filter((f) => f.endsWith(".jsonl"));
|
|
556
|
+
for (const file2 of files) {
|
|
557
|
+
const filePath = (0, import_path.join)(dir, file2);
|
|
558
|
+
const sessionId = (0, import_path.basename)(file2, ".jsonl");
|
|
559
|
+
try {
|
|
560
|
+
const stat = (0, import_fs.statSync)(filePath);
|
|
561
|
+
const content = (0, import_fs.readFileSync)(filePath, "utf-8");
|
|
562
|
+
const lineCount = content.split("\n").filter((l) => l.trim()).length;
|
|
563
|
+
if (lineCount >= MIN_LINES) {
|
|
564
|
+
sessions.push({
|
|
565
|
+
sessionId,
|
|
566
|
+
filePath,
|
|
567
|
+
lineCount,
|
|
568
|
+
sizeBytes: stat.size
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
} catch {
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return sessions;
|
|
576
|
+
}
|
|
577
|
+
async function apiGet(path, apiUrl, token) {
|
|
578
|
+
const response = await fetch(`${apiUrl}/api/v1${path}`, {
|
|
579
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
580
|
+
});
|
|
581
|
+
if (!response.ok) {
|
|
582
|
+
const text = await response.text().catch(() => "");
|
|
583
|
+
throw new Error(`API ${response.status}: ${text.slice(0, 200)}`);
|
|
1055
584
|
}
|
|
585
|
+
return response.json();
|
|
586
|
+
}
|
|
587
|
+
async function getUploadedSessions(apiUrl, token) {
|
|
588
|
+
try {
|
|
589
|
+
const data = await apiGet("/sessions/uploaded", apiUrl, token);
|
|
590
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
591
|
+
for (const s of data.sessions) {
|
|
592
|
+
map2.set(s.sessionId, s.lineCount);
|
|
593
|
+
}
|
|
594
|
+
return map2;
|
|
595
|
+
} catch {
|
|
596
|
+
logger.warn("Could not fetch uploaded sessions list, will upload all");
|
|
597
|
+
return /* @__PURE__ */ new Map();
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
async function uploadSession(sessionId, content, apiUrl, token) {
|
|
601
|
+
const body = (0, import_zlib.gzipSync)(Buffer.from(content, "utf-8"));
|
|
1056
602
|
const response = await fetch(`${apiUrl}/api/v1/sessions/raw`, {
|
|
1057
603
|
method: "POST",
|
|
1058
|
-
headers
|
|
1059
|
-
|
|
604
|
+
headers: {
|
|
605
|
+
Authorization: `Bearer ${token}`,
|
|
606
|
+
"Content-Type": "application/x-ndjson",
|
|
607
|
+
"Content-Encoding": "gzip",
|
|
608
|
+
"x-session-id": sessionId
|
|
609
|
+
},
|
|
610
|
+
body: new Uint8Array(body)
|
|
1060
611
|
});
|
|
1061
612
|
if (!response.ok) {
|
|
1062
613
|
const text = await response.text().catch(() => "");
|
|
@@ -1073,119 +624,61 @@ async function collect(options) {
|
|
|
1073
624
|
"HAANSI_TOKEN is required. Set it as env var or in ~/.haansi/credentials.json"
|
|
1074
625
|
);
|
|
1075
626
|
}
|
|
1076
|
-
const
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
...scanResult.newSessions,
|
|
1082
|
-
...scanResult.updatedSessions
|
|
1083
|
-
];
|
|
1084
|
-
const activeSessionIds = new Set(activeSessions.map((s) => s.sessionId));
|
|
1085
|
-
const sessions = options.limit ? activeSessions.slice(0, options.limit) : activeSessions;
|
|
627
|
+
const localSessions = discoverSessions();
|
|
628
|
+
logger.info(`Found ${localSessions.length} session files on disk`);
|
|
629
|
+
if (localSessions.length === 0) {
|
|
630
|
+
return { uploaded: 0, skipped: 0, errors: 0 };
|
|
631
|
+
}
|
|
1086
632
|
let uploaded = 0;
|
|
633
|
+
let skipped = 0;
|
|
1087
634
|
let errors = 0;
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
const rawContent = (0, import_fs4.readFileSync)(session.filePath, "utf-8");
|
|
1097
|
-
const allLines = rawContent.split("\n").filter((l) => l.trim().length > 0);
|
|
1098
|
-
const totalLines = allLines.length;
|
|
1099
|
-
let uploadedLineCount = ingester.getUploadedLineCount(
|
|
1100
|
-
session.sessionId
|
|
1101
|
-
);
|
|
1102
|
-
const wasPreviouslyFinalized = ingester.isFinalized(session.sessionId);
|
|
1103
|
-
if (wasPreviouslyFinalized && totalLines > uploadedLineCount) {
|
|
1104
|
-
uploadedLineCount = 0;
|
|
1105
|
-
}
|
|
1106
|
-
if (totalLines < uploadedLineCount) {
|
|
1107
|
-
uploadedLineCount = 0;
|
|
1108
|
-
}
|
|
1109
|
-
const deltaLines = allLines.slice(uploadedLineCount);
|
|
1110
|
-
if (deltaLines.length === 0) {
|
|
1111
|
-
continue;
|
|
1112
|
-
}
|
|
1113
|
-
const deltaContent = edgeScrub(deltaLines.join("\n") + "\n");
|
|
1114
|
-
if (options.dryRun) {
|
|
1115
|
-
logger3.info(
|
|
1116
|
-
`[dry-run] Would upload delta for session ${shortId} (${deltaLines.length} new lines, open)`
|
|
1117
|
-
);
|
|
1118
|
-
uploaded++;
|
|
1119
|
-
continue;
|
|
1120
|
-
}
|
|
1121
|
-
await uploadSession(
|
|
1122
|
-
session.sessionId,
|
|
1123
|
-
deltaContent,
|
|
1124
|
-
apiUrl,
|
|
1125
|
-
token,
|
|
1126
|
-
true
|
|
1127
|
-
);
|
|
1128
|
-
ingester.markProcessed(session, "pending", {
|
|
1129
|
-
uploadedLineCount: totalLines,
|
|
1130
|
-
finalized: false
|
|
1131
|
-
});
|
|
1132
|
-
uploaded++;
|
|
1133
|
-
logger3.info(
|
|
1134
|
-
`delta uploaded session ${shortId} (${deltaLines.length} new lines, open)`
|
|
1135
|
-
);
|
|
1136
|
-
} catch (err) {
|
|
1137
|
-
errors++;
|
|
1138
|
-
logger3.error(`Failed to upload session ${shortId}`, {
|
|
1139
|
-
err: err.message
|
|
1140
|
-
});
|
|
1141
|
-
}
|
|
635
|
+
const remoteMap = options.forceAll ? /* @__PURE__ */ new Map() : await getUploadedSessions(apiUrl, token);
|
|
636
|
+
const toUpload = [];
|
|
637
|
+
for (const session of localSessions) {
|
|
638
|
+
const remoteLine = remoteMap.get(session.sessionId);
|
|
639
|
+
if (remoteLine !== void 0 && remoteLine >= session.lineCount) {
|
|
640
|
+
skipped++;
|
|
641
|
+
} else {
|
|
642
|
+
toUpload.push(session);
|
|
1142
643
|
}
|
|
1143
644
|
}
|
|
1144
|
-
const
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
645
|
+
const batch = options.limit ? toUpload.slice(0, options.limit) : toUpload;
|
|
646
|
+
skipped += toUpload.length - batch.length;
|
|
647
|
+
logger.info(
|
|
648
|
+
`Uploading ${batch.length} session(s), skipping ${skipped} unchanged`
|
|
649
|
+
);
|
|
650
|
+
for (const session of batch) {
|
|
651
|
+
const shortId = session.sessionId.slice(0, 8);
|
|
652
|
+
try {
|
|
653
|
+
const rawContent = (0, import_fs.readFileSync)(session.filePath, "utf-8");
|
|
654
|
+
const scrubbed = edgeScrub(rawContent);
|
|
655
|
+
if (options.dryRun) {
|
|
656
|
+
logger.info(
|
|
657
|
+
`[dry-run] Would upload ${shortId} (${session.lineCount} lines)`
|
|
658
|
+
);
|
|
1158
659
|
uploaded++;
|
|
1159
|
-
|
|
1160
|
-
} catch (err) {
|
|
1161
|
-
errors++;
|
|
1162
|
-
logger3.error(`Failed to finalize session ${shortId}`, {
|
|
1163
|
-
err: err.message
|
|
1164
|
-
});
|
|
660
|
+
continue;
|
|
1165
661
|
}
|
|
662
|
+
await uploadSession(session.sessionId, scrubbed, apiUrl, token);
|
|
663
|
+
uploaded++;
|
|
664
|
+
logger.info(`Uploaded ${shortId} (${session.lineCount} lines)`);
|
|
665
|
+
} catch (err) {
|
|
666
|
+
errors++;
|
|
667
|
+
logger.error(`Failed to upload ${shortId}`, { err: err.message });
|
|
1166
668
|
}
|
|
1167
669
|
}
|
|
1168
|
-
|
|
1169
|
-
logger3.info("No new sessions to upload");
|
|
1170
|
-
}
|
|
1171
|
-
return { uploaded, skipped: scanResult.unchangedCount, errors };
|
|
670
|
+
return { uploaded, skipped, errors };
|
|
1172
671
|
}
|
|
1173
|
-
async function
|
|
672
|
+
async function main() {
|
|
1174
673
|
const forceAll = process.argv.includes("--force-all");
|
|
1175
674
|
const dryRun = process.argv.includes("--dry-run");
|
|
1176
|
-
const projectPath = process.env.CLAUDE_PROJECT_PATH || void 0;
|
|
1177
675
|
const limitIdx = process.argv.indexOf("--limit");
|
|
1178
676
|
const limit = limitIdx !== -1 ? parseInt(process.argv[limitIdx + 1], 10) : void 0;
|
|
1179
|
-
|
|
1180
|
-
forceAll,
|
|
1181
|
-
dryRun,
|
|
1182
|
-
projectPath: projectPath ?? "all"
|
|
1183
|
-
});
|
|
677
|
+
logger.info("Collector starting", { forceAll, dryRun });
|
|
1184
678
|
const { uploaded, skipped, errors } = await collect({
|
|
1185
679
|
forceAll,
|
|
1186
680
|
dryRun,
|
|
1187
|
-
limit
|
|
1188
|
-
projectPath
|
|
681
|
+
limit
|
|
1189
682
|
});
|
|
1190
683
|
console.log(
|
|
1191
684
|
`
|
|
@@ -1193,21 +686,22 @@ Collector done: ${uploaded} uploaded, ${skipped} unchanged, ${errors} errors`
|
|
|
1193
686
|
);
|
|
1194
687
|
if (errors > 0) process.exit(1);
|
|
1195
688
|
}
|
|
1196
|
-
var
|
|
689
|
+
var import_dotenv, import_path, import_fs, import_os, import_zlib, logger, DEFAULT_API_URL2, CREDENTIALS_FILE2, CLAUDE_PROJECTS_DIR, MIN_LINES, EDGE_SCRUB_PATTERNS;
|
|
1197
690
|
var init_collector = __esm({
|
|
1198
691
|
"../service-capture/claude-sessions/collector.ts"() {
|
|
1199
692
|
"use strict";
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
693
|
+
import_dotenv = __toESM(require_main());
|
|
694
|
+
import_path = require("path");
|
|
695
|
+
import_fs = require("fs");
|
|
696
|
+
import_os = require("os");
|
|
1204
697
|
import_zlib = require("zlib");
|
|
1205
|
-
init_ingester();
|
|
1206
698
|
init_logger();
|
|
1207
|
-
(0,
|
|
1208
|
-
|
|
699
|
+
(0, import_dotenv.config)({ path: (0, import_path.resolve)(__dirname, "../../../.env") });
|
|
700
|
+
logger = createLogger("claude-collector");
|
|
1209
701
|
DEFAULT_API_URL2 = "https://api.haansi.co";
|
|
1210
|
-
CREDENTIALS_FILE2 = (0,
|
|
702
|
+
CREDENTIALS_FILE2 = (0, import_path.join)((0, import_os.homedir)(), ".haansi", "credentials.json");
|
|
703
|
+
CLAUDE_PROJECTS_DIR = (0, import_path.join)((0, import_os.homedir)(), ".claude", "projects");
|
|
704
|
+
MIN_LINES = 4;
|
|
1211
705
|
EDGE_SCRUB_PATTERNS = [
|
|
1212
706
|
{
|
|
1213
707
|
pattern: /-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----[\s\S]*?-----END (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/g,
|
|
@@ -1248,8 +742,8 @@ var init_collector = __esm({
|
|
|
1248
742
|
}
|
|
1249
743
|
];
|
|
1250
744
|
if (require.main === module) {
|
|
1251
|
-
|
|
1252
|
-
|
|
745
|
+
main().catch((err) => {
|
|
746
|
+
logger.error("Collector failed", err);
|
|
1253
747
|
process.exit(1);
|
|
1254
748
|
});
|
|
1255
749
|
}
|
|
@@ -2170,49 +1664,49 @@ var require_node_cron = __commonJS({
|
|
|
2170
1664
|
var collector_daemon_exports = {};
|
|
2171
1665
|
async function run() {
|
|
2172
1666
|
const projectPath = process.env.CLAUDE_PROJECT_PATH || void 0;
|
|
2173
|
-
|
|
1667
|
+
logger2.info("Collect run starting", { projectPath: projectPath ?? "all" });
|
|
2174
1668
|
const start = Date.now();
|
|
2175
1669
|
try {
|
|
2176
1670
|
const { uploaded, skipped, errors } = await collect({ projectPath });
|
|
2177
1671
|
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
2178
|
-
|
|
1672
|
+
logger2.info(`Collect run complete in ${elapsed}s`, {
|
|
2179
1673
|
uploaded,
|
|
2180
1674
|
skipped,
|
|
2181
1675
|
errors
|
|
2182
1676
|
});
|
|
2183
1677
|
} catch (err) {
|
|
2184
|
-
|
|
1678
|
+
logger2.error("Collect run failed", { err: err.message });
|
|
2185
1679
|
}
|
|
2186
1680
|
}
|
|
2187
|
-
async function
|
|
2188
|
-
|
|
1681
|
+
async function main2() {
|
|
1682
|
+
logger2.info("Collector daemon starting", { schedule: SCHEDULE });
|
|
2189
1683
|
await run();
|
|
2190
1684
|
import_node_cron.default.schedule(SCHEDULE, async () => {
|
|
2191
|
-
|
|
1685
|
+
logger2.info("Scheduled collect run triggered");
|
|
2192
1686
|
await run();
|
|
2193
1687
|
});
|
|
2194
|
-
|
|
1688
|
+
logger2.info(`Daemon running. Next run scheduled per cron: "${SCHEDULE}"`);
|
|
2195
1689
|
const shutdown = async (signal) => {
|
|
2196
|
-
|
|
1690
|
+
logger2.info(`Received ${signal}, shutting down`);
|
|
2197
1691
|
process.exit(0);
|
|
2198
1692
|
};
|
|
2199
1693
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
2200
1694
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
2201
1695
|
}
|
|
2202
|
-
var
|
|
1696
|
+
var import_dotenv2, import_path2, import_node_cron, logger2, SCHEDULE;
|
|
2203
1697
|
var init_collector_daemon = __esm({
|
|
2204
1698
|
"../service-capture/claude-sessions/collector-daemon.ts"() {
|
|
2205
1699
|
"use strict";
|
|
2206
|
-
|
|
2207
|
-
|
|
1700
|
+
import_dotenv2 = __toESM(require_main());
|
|
1701
|
+
import_path2 = require("path");
|
|
2208
1702
|
import_node_cron = __toESM(require_node_cron());
|
|
2209
1703
|
init_collector();
|
|
2210
1704
|
init_logger();
|
|
2211
|
-
(0,
|
|
2212
|
-
|
|
1705
|
+
(0, import_dotenv2.config)({ path: (0, import_path2.resolve)(__dirname, "../../../.env") });
|
|
1706
|
+
logger2 = createLogger("collector-daemon");
|
|
2213
1707
|
SCHEDULE = process.env.CLAUDE_COLLECT_SCHEDULE ?? "*/30 * * * *";
|
|
2214
|
-
|
|
2215
|
-
|
|
1708
|
+
main2().catch((err) => {
|
|
1709
|
+
logger2.error("Daemon failed to start", err);
|
|
2216
1710
|
process.exit(1);
|
|
2217
1711
|
});
|
|
2218
1712
|
}
|
|
@@ -6215,7 +5709,7 @@ function $constructor(name, initializer3, params) {
|
|
|
6215
5709
|
Object.defineProperty(_, "name", { value: name });
|
|
6216
5710
|
return _;
|
|
6217
5711
|
}
|
|
6218
|
-
function
|
|
5712
|
+
function config3(newConfig) {
|
|
6219
5713
|
if (newConfig)
|
|
6220
5714
|
Object.assign(globalConfig, newConfig);
|
|
6221
5715
|
return globalConfig;
|
|
@@ -6740,10 +6234,10 @@ function prefixIssues(path, issues) {
|
|
|
6740
6234
|
function unwrapMessage(message) {
|
|
6741
6235
|
return typeof message === "string" ? message : message?.message;
|
|
6742
6236
|
}
|
|
6743
|
-
function finalizeIssue(iss, ctx,
|
|
6237
|
+
function finalizeIssue(iss, ctx, config5) {
|
|
6744
6238
|
const full = { ...iss, path: iss.path ?? [] };
|
|
6745
6239
|
if (!iss.message) {
|
|
6746
|
-
const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(
|
|
6240
|
+
const message = unwrapMessage(iss.inst?._zod.def?.error?.(iss)) ?? unwrapMessage(ctx?.error?.(iss)) ?? unwrapMessage(config5.customError?.(iss)) ?? unwrapMessage(config5.localeError?.(iss)) ?? "Invalid input";
|
|
6747
6241
|
full.message = message;
|
|
6748
6242
|
}
|
|
6749
6243
|
delete full.inst;
|
|
@@ -7013,7 +6507,7 @@ var init_parse2 = __esm({
|
|
|
7013
6507
|
throw new $ZodAsyncError();
|
|
7014
6508
|
}
|
|
7015
6509
|
if (result.issues.length) {
|
|
7016
|
-
const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
6510
|
+
const e = new (_params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config3())));
|
|
7017
6511
|
captureStackTrace(e, _params?.callee);
|
|
7018
6512
|
throw e;
|
|
7019
6513
|
}
|
|
@@ -7026,7 +6520,7 @@ var init_parse2 = __esm({
|
|
|
7026
6520
|
if (result instanceof Promise)
|
|
7027
6521
|
result = await result;
|
|
7028
6522
|
if (result.issues.length) {
|
|
7029
|
-
const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
6523
|
+
const e = new (params?.Err ?? _Err)(result.issues.map((iss) => finalizeIssue(iss, ctx, config3())));
|
|
7030
6524
|
captureStackTrace(e, params?.callee);
|
|
7031
6525
|
throw e;
|
|
7032
6526
|
}
|
|
@@ -7041,7 +6535,7 @@ var init_parse2 = __esm({
|
|
|
7041
6535
|
}
|
|
7042
6536
|
return result.issues.length ? {
|
|
7043
6537
|
success: false,
|
|
7044
|
-
error: new (_Err ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
6538
|
+
error: new (_Err ?? $ZodError)(result.issues.map((iss) => finalizeIssue(iss, ctx, config3())))
|
|
7045
6539
|
} : { success: true, data: result.value };
|
|
7046
6540
|
};
|
|
7047
6541
|
safeParse = /* @__PURE__ */ _safeParse($ZodRealError);
|
|
@@ -7052,7 +6546,7 @@ var init_parse2 = __esm({
|
|
|
7052
6546
|
result = await result;
|
|
7053
6547
|
return result.issues.length ? {
|
|
7054
6548
|
success: false,
|
|
7055
|
-
error: new _Err(result.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
6549
|
+
error: new _Err(result.issues.map((iss) => finalizeIssue(iss, ctx, config3())))
|
|
7056
6550
|
} : { success: true, data: result.value };
|
|
7057
6551
|
};
|
|
7058
6552
|
safeParseAsync = /* @__PURE__ */ _safeParseAsync($ZodRealError);
|
|
@@ -7986,7 +7480,7 @@ function handleUnionResults(results, final, inst, ctx) {
|
|
|
7986
7480
|
code: "invalid_union",
|
|
7987
7481
|
input: final.value,
|
|
7988
7482
|
inst,
|
|
7989
|
-
errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
7483
|
+
errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config3())))
|
|
7990
7484
|
});
|
|
7991
7485
|
return final;
|
|
7992
7486
|
}
|
|
@@ -8001,7 +7495,7 @@ function handleExclusiveUnionResults(results, final, inst, ctx) {
|
|
|
8001
7495
|
code: "invalid_union",
|
|
8002
7496
|
input: final.value,
|
|
8003
7497
|
inst,
|
|
8004
|
-
errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
7498
|
+
errors: results.map((result) => result.issues.map((iss) => finalizeIssue(iss, ctx, config3())))
|
|
8005
7499
|
});
|
|
8006
7500
|
} else {
|
|
8007
7501
|
final.issues.push({
|
|
@@ -8113,7 +7607,7 @@ function handleMapResult(keyResult, valueResult, final, key, input, inst, ctx) {
|
|
|
8113
7607
|
origin: "map",
|
|
8114
7608
|
input,
|
|
8115
7609
|
inst,
|
|
8116
|
-
issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
7610
|
+
issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config3()))
|
|
8117
7611
|
});
|
|
8118
7612
|
}
|
|
8119
7613
|
}
|
|
@@ -8127,7 +7621,7 @@ function handleMapResult(keyResult, valueResult, final, key, input, inst, ctx) {
|
|
|
8127
7621
|
input,
|
|
8128
7622
|
inst,
|
|
8129
7623
|
key,
|
|
8130
|
-
issues: valueResult.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
7624
|
+
issues: valueResult.issues.map((iss) => finalizeIssue(iss, ctx, config3()))
|
|
8131
7625
|
});
|
|
8132
7626
|
}
|
|
8133
7627
|
}
|
|
@@ -9289,7 +8783,7 @@ var init_schemas = __esm({
|
|
|
9289
8783
|
payload.issues.push({
|
|
9290
8784
|
code: "invalid_key",
|
|
9291
8785
|
origin: "record",
|
|
9292
|
-
issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
8786
|
+
issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config3())),
|
|
9293
8787
|
input: key,
|
|
9294
8788
|
path: [key],
|
|
9295
8789
|
inst
|
|
@@ -9585,7 +9079,7 @@ var init_schemas = __esm({
|
|
|
9585
9079
|
payload.value = def.catchValue({
|
|
9586
9080
|
...payload,
|
|
9587
9081
|
error: {
|
|
9588
|
-
issues: result2.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
9082
|
+
issues: result2.issues.map((iss) => finalizeIssue(iss, ctx, config3()))
|
|
9589
9083
|
},
|
|
9590
9084
|
input: payload.value
|
|
9591
9085
|
});
|
|
@@ -9599,7 +9093,7 @@ var init_schemas = __esm({
|
|
|
9599
9093
|
payload.value = def.catchValue({
|
|
9600
9094
|
...payload,
|
|
9601
9095
|
error: {
|
|
9602
|
-
issues: result.issues.map((iss) => finalizeIssue(iss, ctx,
|
|
9096
|
+
issues: result.issues.map((iss) => finalizeIssue(iss, ctx, config3()))
|
|
9603
9097
|
},
|
|
9604
9098
|
input: payload.value
|
|
9605
9099
|
});
|
|
@@ -13897,7 +13391,7 @@ var init_external3 = __esm({
|
|
|
13897
13391
|
init_iso2();
|
|
13898
13392
|
init_iso2();
|
|
13899
13393
|
init_coerce2();
|
|
13900
|
-
|
|
13394
|
+
config3(en_default2());
|
|
13901
13395
|
}
|
|
13902
13396
|
});
|
|
13903
13397
|
|
|
@@ -16924,13 +16418,13 @@ var init_zodToJsonSchema = __esm({
|
|
|
16924
16418
|
}, true) ?? parseAnyDef(refs)
|
|
16925
16419
|
}), {}) : void 0;
|
|
16926
16420
|
const name = typeof options === "string" ? options : options?.nameStrategy === "title" ? void 0 : options?.name;
|
|
16927
|
-
const
|
|
16421
|
+
const main4 = parseDef(schema._def, name === void 0 ? refs : {
|
|
16928
16422
|
...refs,
|
|
16929
16423
|
currentPath: [...refs.basePath, refs.definitionPath, name]
|
|
16930
16424
|
}, false) ?? parseAnyDef(refs);
|
|
16931
16425
|
const title = typeof options === "object" && options.name !== void 0 && options.nameStrategy === "title" ? options.name : void 0;
|
|
16932
16426
|
if (title !== void 0) {
|
|
16933
|
-
|
|
16427
|
+
main4.title = title;
|
|
16934
16428
|
}
|
|
16935
16429
|
if (refs.flags.hasReferencedOpenAiAnyType) {
|
|
16936
16430
|
if (!definitions) {
|
|
@@ -16951,9 +16445,9 @@ var init_zodToJsonSchema = __esm({
|
|
|
16951
16445
|
}
|
|
16952
16446
|
}
|
|
16953
16447
|
const combined = name === void 0 ? definitions ? {
|
|
16954
|
-
...
|
|
16448
|
+
...main4,
|
|
16955
16449
|
[refs.definitionPath]: definitions
|
|
16956
|
-
} :
|
|
16450
|
+
} : main4 : {
|
|
16957
16451
|
$ref: [
|
|
16958
16452
|
...refs.$refStrategy === "relative" ? [] : refs.basePath,
|
|
16959
16453
|
refs.definitionPath,
|
|
@@ -16961,7 +16455,7 @@ var init_zodToJsonSchema = __esm({
|
|
|
16961
16455
|
].join("/"),
|
|
16962
16456
|
[refs.definitionPath]: {
|
|
16963
16457
|
...definitions,
|
|
16964
|
-
[name]:
|
|
16458
|
+
[name]: main4
|
|
16965
16459
|
}
|
|
16966
16460
|
};
|
|
16967
16461
|
if (refs.target === "jsonSchema7") {
|
|
@@ -17591,7 +17085,7 @@ var init_protocol = __esm({
|
|
|
17591
17085
|
return;
|
|
17592
17086
|
}
|
|
17593
17087
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
17594
|
-
await new Promise((
|
|
17088
|
+
await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
|
|
17595
17089
|
options?.signal?.throwIfAborted();
|
|
17596
17090
|
}
|
|
17597
17091
|
} catch (error2) {
|
|
@@ -17608,7 +17102,7 @@ var init_protocol = __esm({
|
|
|
17608
17102
|
*/
|
|
17609
17103
|
request(request, resultSchema, options) {
|
|
17610
17104
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
17611
|
-
return new Promise((
|
|
17105
|
+
return new Promise((resolve4, reject) => {
|
|
17612
17106
|
const earlyReject = (error2) => {
|
|
17613
17107
|
reject(error2);
|
|
17614
17108
|
};
|
|
@@ -17686,7 +17180,7 @@ var init_protocol = __esm({
|
|
|
17686
17180
|
if (!parseResult.success) {
|
|
17687
17181
|
reject(parseResult.error);
|
|
17688
17182
|
} else {
|
|
17689
|
-
|
|
17183
|
+
resolve4(parseResult.data);
|
|
17690
17184
|
}
|
|
17691
17185
|
} catch (error2) {
|
|
17692
17186
|
reject(error2);
|
|
@@ -17947,12 +17441,12 @@ var init_protocol = __esm({
|
|
|
17947
17441
|
}
|
|
17948
17442
|
} catch {
|
|
17949
17443
|
}
|
|
17950
|
-
return new Promise((
|
|
17444
|
+
return new Promise((resolve4, reject) => {
|
|
17951
17445
|
if (signal.aborted) {
|
|
17952
17446
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
17953
17447
|
return;
|
|
17954
17448
|
}
|
|
17955
|
-
const timeoutId = setTimeout(
|
|
17449
|
+
const timeoutId = setTimeout(resolve4, interval);
|
|
17956
17450
|
signal.addEventListener("abort", () => {
|
|
17957
17451
|
clearTimeout(timeoutId);
|
|
17958
17452
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -20979,7 +20473,7 @@ var require_compile = __commonJS({
|
|
|
20979
20473
|
const schOrFunc = root.refs[ref];
|
|
20980
20474
|
if (schOrFunc)
|
|
20981
20475
|
return schOrFunc;
|
|
20982
|
-
let _sch =
|
|
20476
|
+
let _sch = resolve4.call(this, root, ref);
|
|
20983
20477
|
if (_sch === void 0) {
|
|
20984
20478
|
const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
20985
20479
|
const { schemaId } = this.opts;
|
|
@@ -21006,7 +20500,7 @@ var require_compile = __commonJS({
|
|
|
21006
20500
|
function sameSchemaEnv(s1, s2) {
|
|
21007
20501
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
21008
20502
|
}
|
|
21009
|
-
function
|
|
20503
|
+
function resolve4(root, ref) {
|
|
21010
20504
|
let sch;
|
|
21011
20505
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
21012
20506
|
ref = sch;
|
|
@@ -21581,7 +21075,7 @@ var require_fast_uri = __commonJS({
|
|
|
21581
21075
|
}
|
|
21582
21076
|
return uri;
|
|
21583
21077
|
}
|
|
21584
|
-
function
|
|
21078
|
+
function resolve4(baseURI, relativeURI, options) {
|
|
21585
21079
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
21586
21080
|
const resolved = resolveComponent(parse4(baseURI, schemelessOptions), parse4(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
21587
21081
|
schemelessOptions.skipEscape = true;
|
|
@@ -21808,7 +21302,7 @@ var require_fast_uri = __commonJS({
|
|
|
21808
21302
|
var fastUri = {
|
|
21809
21303
|
SCHEMES,
|
|
21810
21304
|
normalize,
|
|
21811
|
-
resolve:
|
|
21305
|
+
resolve: resolve4,
|
|
21812
21306
|
resolveComponent,
|
|
21813
21307
|
equal,
|
|
21814
21308
|
serialize,
|
|
@@ -22363,13 +21857,13 @@ var require_core = __commonJS({
|
|
|
22363
21857
|
}, warn() {
|
|
22364
21858
|
}, error() {
|
|
22365
21859
|
} };
|
|
22366
|
-
function getLogger(
|
|
22367
|
-
if (
|
|
21860
|
+
function getLogger(logger3) {
|
|
21861
|
+
if (logger3 === false)
|
|
22368
21862
|
return noLogs;
|
|
22369
|
-
if (
|
|
21863
|
+
if (logger3 === void 0)
|
|
22370
21864
|
return console;
|
|
22371
|
-
if (
|
|
22372
|
-
return
|
|
21865
|
+
if (logger3.log && logger3.warn && logger3.error)
|
|
21866
|
+
return logger3;
|
|
22373
21867
|
throw new Error("logger must implement log, warn and error methods");
|
|
22374
21868
|
}
|
|
22375
21869
|
var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
|
|
@@ -27599,7 +27093,7 @@ var require_compile2 = __commonJS({
|
|
|
27599
27093
|
const schOrFunc = root.refs[ref];
|
|
27600
27094
|
if (schOrFunc)
|
|
27601
27095
|
return schOrFunc;
|
|
27602
|
-
let _sch =
|
|
27096
|
+
let _sch = resolve4.call(this, root, ref);
|
|
27603
27097
|
if (_sch === void 0) {
|
|
27604
27098
|
const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
|
|
27605
27099
|
const { schemaId } = this.opts;
|
|
@@ -27626,7 +27120,7 @@ var require_compile2 = __commonJS({
|
|
|
27626
27120
|
function sameSchemaEnv(s1, s2) {
|
|
27627
27121
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
27628
27122
|
}
|
|
27629
|
-
function
|
|
27123
|
+
function resolve4(root, ref) {
|
|
27630
27124
|
let sch;
|
|
27631
27125
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
27632
27126
|
ref = sch;
|
|
@@ -28261,13 +27755,13 @@ var require_core3 = __commonJS({
|
|
|
28261
27755
|
}, warn() {
|
|
28262
27756
|
}, error() {
|
|
28263
27757
|
} };
|
|
28264
|
-
function getLogger(
|
|
28265
|
-
if (
|
|
27758
|
+
function getLogger(logger3) {
|
|
27759
|
+
if (logger3 === false)
|
|
28266
27760
|
return noLogs;
|
|
28267
|
-
if (
|
|
27761
|
+
if (logger3 === void 0)
|
|
28268
27762
|
return console;
|
|
28269
|
-
if (
|
|
28270
|
-
return
|
|
27763
|
+
if (logger3.log && logger3.warn && logger3.error)
|
|
27764
|
+
return logger3;
|
|
28271
27765
|
throw new Error("logger must implement log, warn and error methods");
|
|
28272
27766
|
}
|
|
28273
27767
|
var KEYWORD_NAME = /^[a-z_$][a-z0-9_$:-]*$/i;
|
|
@@ -31158,13 +30652,13 @@ var init_mcp_server = __esm({
|
|
|
31158
30652
|
constructor(_mcpServer) {
|
|
31159
30653
|
this._mcpServer = _mcpServer;
|
|
31160
30654
|
}
|
|
31161
|
-
registerToolTask(name,
|
|
31162
|
-
const execution = { taskSupport: "required", ...
|
|
30655
|
+
registerToolTask(name, config5, handler) {
|
|
30656
|
+
const execution = { taskSupport: "required", ...config5.execution };
|
|
31163
30657
|
if (execution.taskSupport === "forbidden") {
|
|
31164
30658
|
throw new Error(`Cannot register task-based tool '${name}' with taskSupport 'forbidden'. Use registerTool() instead.`);
|
|
31165
30659
|
}
|
|
31166
30660
|
const mcpServerInternal = this._mcpServer;
|
|
31167
|
-
return mcpServerInternal._createRegisteredTool(name,
|
|
30661
|
+
return mcpServerInternal._createRegisteredTool(name, config5.title, config5.description, config5.inputSchema, config5.outputSchema, config5.annotations, execution, config5._meta, handler);
|
|
31168
30662
|
}
|
|
31169
30663
|
};
|
|
31170
30664
|
}
|
|
@@ -31476,7 +30970,7 @@ var init_mcp = __esm({
|
|
|
31476
30970
|
let task = createTaskResult.task;
|
|
31477
30971
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
31478
30972
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
31479
|
-
await new Promise((
|
|
30973
|
+
await new Promise((resolve4) => setTimeout(resolve4, pollInterval));
|
|
31480
30974
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
31481
30975
|
if (!updatedTask) {
|
|
31482
30976
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -31677,12 +31171,12 @@ var init_mcp = __esm({
|
|
|
31677
31171
|
return registeredResourceTemplate;
|
|
31678
31172
|
}
|
|
31679
31173
|
}
|
|
31680
|
-
registerResource(name, uriOrTemplate,
|
|
31174
|
+
registerResource(name, uriOrTemplate, config5, readCallback) {
|
|
31681
31175
|
if (typeof uriOrTemplate === "string") {
|
|
31682
31176
|
if (this._registeredResources[uriOrTemplate]) {
|
|
31683
31177
|
throw new Error(`Resource ${uriOrTemplate} is already registered`);
|
|
31684
31178
|
}
|
|
31685
|
-
const registeredResource = this._createRegisteredResource(name,
|
|
31179
|
+
const registeredResource = this._createRegisteredResource(name, config5.title, uriOrTemplate, config5, readCallback);
|
|
31686
31180
|
this.setResourceRequestHandlers();
|
|
31687
31181
|
this.sendResourceListChanged();
|
|
31688
31182
|
return registeredResource;
|
|
@@ -31690,7 +31184,7 @@ var init_mcp = __esm({
|
|
|
31690
31184
|
if (this._registeredResourceTemplates[name]) {
|
|
31691
31185
|
throw new Error(`Resource template ${name} is already registered`);
|
|
31692
31186
|
}
|
|
31693
|
-
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name,
|
|
31187
|
+
const registeredResourceTemplate = this._createRegisteredResourceTemplate(name, config5.title, uriOrTemplate, config5, readCallback);
|
|
31694
31188
|
this.setResourceRequestHandlers();
|
|
31695
31189
|
this.sendResourceListChanged();
|
|
31696
31190
|
return registeredResourceTemplate;
|
|
@@ -31885,11 +31379,11 @@ var init_mcp = __esm({
|
|
|
31885
31379
|
/**
|
|
31886
31380
|
* Registers a tool with a config object and callback.
|
|
31887
31381
|
*/
|
|
31888
|
-
registerTool(name,
|
|
31382
|
+
registerTool(name, config5, cb) {
|
|
31889
31383
|
if (this._registeredTools[name]) {
|
|
31890
31384
|
throw new Error(`Tool ${name} is already registered`);
|
|
31891
31385
|
}
|
|
31892
|
-
const { title, description, inputSchema, outputSchema, annotations, _meta } =
|
|
31386
|
+
const { title, description, inputSchema, outputSchema, annotations, _meta } = config5;
|
|
31893
31387
|
return this._createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, { taskSupport: "forbidden" }, _meta, cb);
|
|
31894
31388
|
}
|
|
31895
31389
|
prompt(name, ...rest) {
|
|
@@ -31913,11 +31407,11 @@ var init_mcp = __esm({
|
|
|
31913
31407
|
/**
|
|
31914
31408
|
* Registers a prompt with a config object and callback.
|
|
31915
31409
|
*/
|
|
31916
|
-
registerPrompt(name,
|
|
31410
|
+
registerPrompt(name, config5, cb) {
|
|
31917
31411
|
if (this._registeredPrompts[name]) {
|
|
31918
31412
|
throw new Error(`Prompt ${name} is already registered`);
|
|
31919
31413
|
}
|
|
31920
|
-
const { title, description, argsSchema } =
|
|
31414
|
+
const { title, description, argsSchema } = config5;
|
|
31921
31415
|
const registeredPrompt = this._createRegisteredPrompt(name, title, description, argsSchema, cb);
|
|
31922
31416
|
this.setPromptRequestHandlers();
|
|
31923
31417
|
this.sendPromptListChanged();
|
|
@@ -32067,12 +31561,12 @@ var init_stdio2 = __esm({
|
|
|
32067
31561
|
this.onclose?.();
|
|
32068
31562
|
}
|
|
32069
31563
|
send(message) {
|
|
32070
|
-
return new Promise((
|
|
31564
|
+
return new Promise((resolve4) => {
|
|
32071
31565
|
const json2 = serializeMessage(message);
|
|
32072
31566
|
if (this._stdout.write(json2)) {
|
|
32073
|
-
|
|
31567
|
+
resolve4();
|
|
32074
31568
|
} else {
|
|
32075
|
-
this._stdout.once("drain",
|
|
31569
|
+
this._stdout.once("drain", resolve4);
|
|
32076
31570
|
}
|
|
32077
31571
|
});
|
|
32078
31572
|
}
|
|
@@ -32084,9 +31578,9 @@ var init_stdio2 = __esm({
|
|
|
32084
31578
|
var mcp_server_exports = {};
|
|
32085
31579
|
function resolveToken2() {
|
|
32086
31580
|
if (process.env.HAANSI_TOKEN) return process.env.HAANSI_TOKEN;
|
|
32087
|
-
if ((0,
|
|
31581
|
+
if ((0, import_fs2.existsSync)(CREDENTIALS_FILE3)) {
|
|
32088
31582
|
try {
|
|
32089
|
-
const creds = JSON.parse((0,
|
|
31583
|
+
const creds = JSON.parse((0, import_fs2.readFileSync)(CREDENTIALS_FILE3, "utf-8"));
|
|
32090
31584
|
if (creds.token) return creds.token;
|
|
32091
31585
|
} catch {
|
|
32092
31586
|
}
|
|
@@ -32112,7 +31606,7 @@ function maybeCollect() {
|
|
|
32112
31606
|
triggerCollectInBackground();
|
|
32113
31607
|
}
|
|
32114
31608
|
}
|
|
32115
|
-
async function
|
|
31609
|
+
async function apiGet2(path) {
|
|
32116
31610
|
const response = await fetch(`${API_URL}/api/v1${path}`, {
|
|
32117
31611
|
headers: { Authorization: `Bearer ${TOKEN}` }
|
|
32118
31612
|
});
|
|
@@ -32122,9 +31616,9 @@ async function apiGet(path) {
|
|
|
32122
31616
|
}
|
|
32123
31617
|
return response.json();
|
|
32124
31618
|
}
|
|
32125
|
-
async function
|
|
31619
|
+
async function main3() {
|
|
32126
31620
|
try {
|
|
32127
|
-
await
|
|
31621
|
+
await apiGet2("/sessions/recent?limit=1");
|
|
32128
31622
|
console.error("[mcp-server] API connection ok");
|
|
32129
31623
|
triggerCollectInBackground();
|
|
32130
31624
|
} catch (err) {
|
|
@@ -32137,21 +31631,21 @@ async function main4() {
|
|
|
32137
31631
|
await mcpServer.connect(transport);
|
|
32138
31632
|
console.error("[mcp-server] haansi-solutions MCP server running on stdio");
|
|
32139
31633
|
}
|
|
32140
|
-
var
|
|
31634
|
+
var import_dotenv3, import_path3, import_fs2, import_os2, DEFAULT_API_URL3, CREDENTIALS_FILE3, API_URL, TOKEN, isCollecting, lastCollectTime, COLLECT_COOLDOWN_MS, mcpServer, server;
|
|
32141
31635
|
var init_mcp_server2 = __esm({
|
|
32142
31636
|
"../service-capture/claude-sessions/mcp-server.ts"() {
|
|
32143
31637
|
"use strict";
|
|
32144
|
-
|
|
32145
|
-
|
|
32146
|
-
|
|
32147
|
-
|
|
31638
|
+
import_dotenv3 = __toESM(require_main());
|
|
31639
|
+
import_path3 = require("path");
|
|
31640
|
+
import_fs2 = require("fs");
|
|
31641
|
+
import_os2 = require("os");
|
|
32148
31642
|
init_collector();
|
|
32149
31643
|
init_mcp();
|
|
32150
31644
|
init_stdio2();
|
|
32151
31645
|
init_types2();
|
|
32152
|
-
(0,
|
|
31646
|
+
(0, import_dotenv3.config)({ path: (0, import_path3.resolve)(__dirname, "../../../.env") });
|
|
32153
31647
|
DEFAULT_API_URL3 = "https://api.haansi.co";
|
|
32154
|
-
CREDENTIALS_FILE3 = (0,
|
|
31648
|
+
CREDENTIALS_FILE3 = (0, import_path3.join)((0, import_os2.homedir)(), ".haansi", "credentials.json");
|
|
32155
31649
|
API_URL = process.env.HAANSI_API_URL ?? DEFAULT_API_URL3;
|
|
32156
31650
|
TOKEN = resolveToken2();
|
|
32157
31651
|
if (!TOKEN) {
|
|
@@ -32213,7 +31707,7 @@ var init_mcp_server2 = __esm({
|
|
|
32213
31707
|
case "search_solutions": {
|
|
32214
31708
|
const { query, top_k = 5 } = args;
|
|
32215
31709
|
const k = Math.min(Math.max(1, top_k), 20);
|
|
32216
|
-
const data = await
|
|
31710
|
+
const data = await apiGet2(
|
|
32217
31711
|
`/sessions/search?q=${encodeURIComponent(query)}&top_k=${k}`
|
|
32218
31712
|
);
|
|
32219
31713
|
if (!data.results || data.results.length === 0) {
|
|
@@ -32233,7 +31727,7 @@ var init_mcp_server2 = __esm({
|
|
|
32233
31727
|
case "list_recent_solutions": {
|
|
32234
31728
|
const { limit = 10 } = args;
|
|
32235
31729
|
const n = Math.min(Math.max(1, limit), 50);
|
|
32236
|
-
const data = await
|
|
31730
|
+
const data = await apiGet2(`/sessions/recent?limit=${n}`);
|
|
32237
31731
|
if (!data.results || data.results.length === 0) {
|
|
32238
31732
|
return {
|
|
32239
31733
|
content: [
|
|
@@ -32259,7 +31753,7 @@ var init_mcp_server2 = __esm({
|
|
|
32259
31753
|
};
|
|
32260
31754
|
}
|
|
32261
31755
|
});
|
|
32262
|
-
|
|
31756
|
+
main3().catch((err) => {
|
|
32263
31757
|
console.error("[mcp-server] Failed to start:", err);
|
|
32264
31758
|
process.exit(1);
|
|
32265
31759
|
});
|
|
@@ -32272,24 +31766,24 @@ __export(setup_mcp_exports, {
|
|
|
32272
31766
|
setupMcp: () => setupMcp
|
|
32273
31767
|
});
|
|
32274
31768
|
async function setupMcp() {
|
|
32275
|
-
let
|
|
31769
|
+
let config5 = {};
|
|
32276
31770
|
if ((0, import_node_fs2.existsSync)(CLAUDE_JSON)) {
|
|
32277
31771
|
try {
|
|
32278
|
-
|
|
31772
|
+
config5 = JSON.parse((0, import_node_fs2.readFileSync)(CLAUDE_JSON, "utf-8"));
|
|
32279
31773
|
} catch {
|
|
32280
31774
|
console.error(
|
|
32281
31775
|
`Warning: Could not parse ${CLAUDE_JSON} \u2014 will overwrite with new config.`
|
|
32282
31776
|
);
|
|
32283
31777
|
}
|
|
32284
31778
|
}
|
|
32285
|
-
if (!
|
|
32286
|
-
|
|
31779
|
+
if (!config5.mcpServers) {
|
|
31780
|
+
config5.mcpServers = {};
|
|
32287
31781
|
}
|
|
32288
|
-
|
|
31782
|
+
config5.mcpServers["haansi-solutions"] = {
|
|
32289
31783
|
command: "npx",
|
|
32290
31784
|
args: ["haansi", "mcp-server"]
|
|
32291
31785
|
};
|
|
32292
|
-
(0, import_node_fs2.writeFileSync)(CLAUDE_JSON, JSON.stringify(
|
|
31786
|
+
(0, import_node_fs2.writeFileSync)(CLAUDE_JSON, JSON.stringify(config5, null, 2) + "\n", "utf-8");
|
|
32293
31787
|
console.log(`MCP server configured in ${CLAUDE_JSON}`);
|
|
32294
31788
|
console.log(
|
|
32295
31789
|
"Restart Claude Code to activate the haansi-solutions MCP server."
|
|
@@ -32414,7 +31908,7 @@ function resolveToken3() {
|
|
|
32414
31908
|
}
|
|
32415
31909
|
throw new Error("No token found. Run `haansi init` first.");
|
|
32416
31910
|
}
|
|
32417
|
-
async function
|
|
31911
|
+
async function apiGet3(path, token, apiUrl) {
|
|
32418
31912
|
const response = await fetch(`${apiUrl}/api/v1${path}`, {
|
|
32419
31913
|
headers: { Authorization: `Bearer ${token}` }
|
|
32420
31914
|
});
|
|
@@ -32429,13 +31923,13 @@ async function list(options) {
|
|
|
32429
31923
|
const token = resolveToken3();
|
|
32430
31924
|
let data;
|
|
32431
31925
|
if (options.search) {
|
|
32432
|
-
data = await
|
|
31926
|
+
data = await apiGet3(
|
|
32433
31927
|
`/sessions/search?q=${encodeURIComponent(options.search)}&top_k=${options.limit}`,
|
|
32434
31928
|
token,
|
|
32435
31929
|
apiUrl
|
|
32436
31930
|
);
|
|
32437
31931
|
} else {
|
|
32438
|
-
data = await
|
|
31932
|
+
data = await apiGet3(
|
|
32439
31933
|
`/sessions/recent?limit=${options.limit}`,
|
|
32440
31934
|
token,
|
|
32441
31935
|
apiUrl
|