@pentoshi/clai 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -0
- package/bin/clai.mjs +2 -0
- package/dist/agent/runner.d.ts +12 -0
- package/dist/agent/runner.js +249 -0
- package/dist/agent/runner.js.map +1 -0
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.js +29 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/providers.d.ts +13 -0
- package/dist/commands/providers.js +137 -0
- package/dist/commands/providers.js.map +1 -0
- package/dist/commands/update.d.ts +5 -0
- package/dist/commands/update.js +123 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/anthropic.d.ts +2 -0
- package/dist/llm/anthropic.js +127 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/gemini.d.ts +2 -0
- package/dist/llm/gemini.js +109 -0
- package/dist/llm/gemini.js.map +1 -0
- package/dist/llm/groq.d.ts +2 -0
- package/dist/llm/groq.js +49 -0
- package/dist/llm/groq.js.map +1 -0
- package/dist/llm/http.d.ts +35 -0
- package/dist/llm/http.js +112 -0
- package/dist/llm/http.js.map +1 -0
- package/dist/llm/ollama.d.ts +2 -0
- package/dist/llm/ollama.js +95 -0
- package/dist/llm/ollama.js.map +1 -0
- package/dist/llm/openai.d.ts +2 -0
- package/dist/llm/openai.js +49 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/llm/openrouter.d.ts +2 -0
- package/dist/llm/openrouter.js +55 -0
- package/dist/llm/openrouter.js.map +1 -0
- package/dist/llm/provider.d.ts +23 -0
- package/dist/llm/provider.js +58 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/llm/router.d.ts +8 -0
- package/dist/llm/router.js +103 -0
- package/dist/llm/router.js.map +1 -0
- package/dist/modes/agent.d.ts +17 -0
- package/dist/modes/agent.js +6 -0
- package/dist/modes/agent.js.map +1 -0
- package/dist/modes/ask.d.ts +8 -0
- package/dist/modes/ask.js +46 -0
- package/dist/modes/ask.js.map +1 -0
- package/dist/os/detect.d.ts +10 -0
- package/dist/os/detect.js +17 -0
- package/dist/os/detect.js.map +1 -0
- package/dist/os/pkgmgr.d.ts +6 -0
- package/dist/os/pkgmgr.js +32 -0
- package/dist/os/pkgmgr.js.map +1 -0
- package/dist/prompts/index.d.ts +2 -0
- package/dist/prompts/index.js +60 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/repl.d.ts +7 -0
- package/dist/repl.js +216 -0
- package/dist/repl.js.map +1 -0
- package/dist/safety/classifier.d.ts +8 -0
- package/dist/safety/classifier.js +118 -0
- package/dist/safety/classifier.js.map +1 -0
- package/dist/safety/patterns.d.ts +5 -0
- package/dist/safety/patterns.js +45 -0
- package/dist/safety/patterns.js.map +1 -0
- package/dist/store/config.d.ts +20 -0
- package/dist/store/config.js +46 -0
- package/dist/store/config.js.map +1 -0
- package/dist/store/history.d.ts +24 -0
- package/dist/store/history.js +145 -0
- package/dist/store/history.js.map +1 -0
- package/dist/store/keys.d.ts +10 -0
- package/dist/store/keys.js +115 -0
- package/dist/store/keys.js.map +1 -0
- package/dist/store/logs.d.ts +2 -0
- package/dist/store/logs.js +31 -0
- package/dist/store/logs.js.map +1 -0
- package/dist/store/project.d.ts +2 -0
- package/dist/store/project.js +14 -0
- package/dist/store/project.js.map +1 -0
- package/dist/tools/fs.d.ts +5 -0
- package/dist/tools/fs.js +82 -0
- package/dist/tools/fs.js.map +1 -0
- package/dist/tools/http.d.ts +6 -0
- package/dist/tools/http.js +14 -0
- package/dist/tools/http.js.map +1 -0
- package/dist/tools/registry.d.ts +5 -0
- package/dist/tools/registry.js +79 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/shell.d.ts +7 -0
- package/dist/tools/shell.js +16 -0
- package/dist/tools/shell.js.map +1 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/ui/banner.d.ts +12 -0
- package/dist/ui/banner.js +55 -0
- package/dist/ui/banner.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import net from "node:net";
|
|
2
|
+
import { destructiveCommandPatterns, exfiltrationPatterns, networkScanTools, readOnlyShellCommands, } from "./patterns.js";
|
|
3
|
+
function stringArg(args, key) {
|
|
4
|
+
const value = args[key];
|
|
5
|
+
return typeof value === "string" ? value : undefined;
|
|
6
|
+
}
|
|
7
|
+
export function isPrivateIpv4(value) {
|
|
8
|
+
const candidate = value.split("/")[0] ?? value;
|
|
9
|
+
if (net.isIP(candidate) !== 4)
|
|
10
|
+
return false;
|
|
11
|
+
const parts = candidate.split(".").map((part) => Number(part));
|
|
12
|
+
const [a, b] = parts;
|
|
13
|
+
if (a === 10)
|
|
14
|
+
return true;
|
|
15
|
+
if (a === 172 && b !== undefined && b >= 16 && b <= 31)
|
|
16
|
+
return true;
|
|
17
|
+
if (a === 192 && b === 168)
|
|
18
|
+
return true;
|
|
19
|
+
if (a === 127)
|
|
20
|
+
return true;
|
|
21
|
+
if (a === 169 && b === 254)
|
|
22
|
+
return true;
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
function commandContainsNetworkScanner(command) {
|
|
26
|
+
return networkScanTools.some((tool) => new RegExp(`(^|\\s)${tool}(\\s|$)`, "i").test(command));
|
|
27
|
+
}
|
|
28
|
+
function containsPublicTarget(command) {
|
|
29
|
+
const ips = command.match(/\b(?:\d{1,3}\.){3}\d{1,3}(?:\/\d{1,2})?\b/g) ?? [];
|
|
30
|
+
return ips.some((ip) => !isPrivateIpv4(ip));
|
|
31
|
+
}
|
|
32
|
+
export function isPentestToolCall(call) {
|
|
33
|
+
if (call.name === "net.scan" || call.name === "pentest.recon")
|
|
34
|
+
return true;
|
|
35
|
+
if (call.name !== "shell.exec")
|
|
36
|
+
return false;
|
|
37
|
+
const command = stringArg(call.args, "command") ?? "";
|
|
38
|
+
return commandContainsNetworkScanner(command);
|
|
39
|
+
}
|
|
40
|
+
export function classifyToolCall(call) {
|
|
41
|
+
if (call.name === "sysinfo" ||
|
|
42
|
+
call.name === "fs.read" ||
|
|
43
|
+
call.name === "fs.list" ||
|
|
44
|
+
call.name === "fs.search") {
|
|
45
|
+
return { level: "safe", reason: "Read-only operation" };
|
|
46
|
+
}
|
|
47
|
+
if (call.name === "http.fetch") {
|
|
48
|
+
return {
|
|
49
|
+
level: "safe",
|
|
50
|
+
reason: "HTTP fetch is read-only with response size limits",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (call.name === "shell.exec") {
|
|
54
|
+
const command = stringArg(call.args, "command") ?? "";
|
|
55
|
+
if (destructiveCommandPatterns.some((pattern) => pattern.test(command))) {
|
|
56
|
+
return {
|
|
57
|
+
level: "block",
|
|
58
|
+
reason: "Command matches destructive safety pattern",
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (exfiltrationPatterns.some((pattern) => pattern.test(command))) {
|
|
62
|
+
return {
|
|
63
|
+
level: "block",
|
|
64
|
+
reason: "Command resembles secret or data exfiltration",
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (commandContainsNetworkScanner(command) &&
|
|
68
|
+
containsPublicTarget(command) &&
|
|
69
|
+
!command.includes("--i-own-this")) {
|
|
70
|
+
return {
|
|
71
|
+
level: "block",
|
|
72
|
+
reason: "Public target scanning requires explicit --i-own-this authorization flag",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Read-only / info commands are safe to auto-execute
|
|
76
|
+
const base = command.trim().split(/\s+/)[0]?.replace(/^.*\//, "") ?? "";
|
|
77
|
+
if (readOnlyShellCommands.has(base)) {
|
|
78
|
+
return { level: "safe", reason: "Read-only command" };
|
|
79
|
+
}
|
|
80
|
+
return { level: "confirm", reason: "Shell commands require confirmation" };
|
|
81
|
+
}
|
|
82
|
+
if (call.name === "net.scan") {
|
|
83
|
+
const target = stringArg(call.args, "target") ?? "";
|
|
84
|
+
const ownsTarget = call.args.iOwnThis === true || call.args.own === true;
|
|
85
|
+
if (target && !isPrivateIpv4(target) && !ownsTarget) {
|
|
86
|
+
return {
|
|
87
|
+
level: "block",
|
|
88
|
+
reason: "Public IP scan requires ownership confirmation",
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { level: "confirm", reason: "Network scans require confirmation" };
|
|
92
|
+
}
|
|
93
|
+
if (call.name === "pentest.recon") {
|
|
94
|
+
const target = stringArg(call.args, "target") ?? "";
|
|
95
|
+
const ownsTarget = call.args.iOwnThis === true || call.args.own === true;
|
|
96
|
+
if (target &&
|
|
97
|
+
net.isIP(target.split("/")[0] ?? target) &&
|
|
98
|
+
!isPrivateIpv4(target) &&
|
|
99
|
+
!ownsTarget) {
|
|
100
|
+
return {
|
|
101
|
+
level: "block",
|
|
102
|
+
reason: "Public target recon requires ownership confirmation",
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
level: "confirm",
|
|
107
|
+
reason: "Pentest recon requires confirmation and authorization acknowledgement",
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
if (call.name === "fs.write" || call.name === "pkg.install") {
|
|
111
|
+
return {
|
|
112
|
+
level: "confirm",
|
|
113
|
+
reason: "Mutating operation requires confirmation",
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
return { level: "confirm", reason: "Unknown tool requires confirmation" };
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=classifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classifier.js","sourceRoot":"","sources":["../../src/safety/classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const destructiveCommandPatterns: RegExp[];
|
|
2
|
+
export declare const exfiltrationPatterns: RegExp[];
|
|
3
|
+
export declare const networkScanTools: string[];
|
|
4
|
+
/** Read-only commands that are safe to auto-execute without user confirmation */
|
|
5
|
+
export declare const readOnlyShellCommands: Set<string>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export const destructiveCommandPatterns = [
|
|
2
|
+
/\brm\s+-[rfRf]*\s+\//,
|
|
3
|
+
/\brm\s+-[rfRf]*\s+~\b/,
|
|
4
|
+
/\bdel\s+\/f\s+\/s\s+\/q\s+[A-Z]:\\/i,
|
|
5
|
+
/\bformat\s+[A-Z]:/i,
|
|
6
|
+
/\bdd\s+if=.*\s+of=\/dev\//,
|
|
7
|
+
/mkfs\.[a-z0-9]+\s+\/dev\//i,
|
|
8
|
+
/:\(\)\s*\{\s*:\|:\s*&\s*}\s*;/,
|
|
9
|
+
/\bshutdown\b.*\b(now|\/s|\/r)\b/i,
|
|
10
|
+
];
|
|
11
|
+
export const exfiltrationPatterns = [
|
|
12
|
+
/curl\s+.*\|\s*sh/i,
|
|
13
|
+
/wget\s+.*\|\s*sh/i,
|
|
14
|
+
/tar\s+.*\|\s*(curl|nc|netcat)/i,
|
|
15
|
+
/\bscp\b.*(~\/|\.ssh|\.env)/i,
|
|
16
|
+
];
|
|
17
|
+
export const networkScanTools = ['nmap', 'masscan', 'nikto', 'sqlmap', 'gobuster', 'ffuf', 'hydra'];
|
|
18
|
+
/** Read-only commands that are safe to auto-execute without user confirmation */
|
|
19
|
+
export const readOnlyShellCommands = new Set([
|
|
20
|
+
// system info
|
|
21
|
+
'whoami', 'hostname', 'uname', 'uptime', 'date', 'id', 'arch', 'sw_vers',
|
|
22
|
+
'lsb_release', 'hostnamectl', 'printenv', 'env', 'locale', 'ulimit',
|
|
23
|
+
'pwd', 'cd', 'test', 'true', 'false', 'basename', 'dirname',
|
|
24
|
+
// file inspection (read-only)
|
|
25
|
+
'ls', 'dir', 'cat', 'head', 'tail', 'less', 'more', 'wc', 'file', 'stat',
|
|
26
|
+
'find', 'which', 'where', 'whereis', 'type', 'readlink', 'realpath',
|
|
27
|
+
'tree', 'du', 'df', 'lsof', 'md5', 'md5sum', 'sha256sum', 'shasum',
|
|
28
|
+
// networking info
|
|
29
|
+
'ifconfig', 'ipconfig', 'ip', 'ping', 'traceroute', 'tracert', 'dig',
|
|
30
|
+
'nslookup', 'host', 'whois', 'curl', 'wget', 'netstat', 'ss', 'route',
|
|
31
|
+
'arp', 'iwconfig', 'nmcli',
|
|
32
|
+
// process info
|
|
33
|
+
'ps', 'top', 'htop', 'pgrep', 'lscpu', 'free', 'vmstat', 'iostat',
|
|
34
|
+
// text processing
|
|
35
|
+
'echo', 'printf', 'grep', 'egrep', 'fgrep', 'rg', 'ag', 'awk', 'sed',
|
|
36
|
+
'sort', 'uniq', 'cut', 'tr', 'diff', 'comm', 'tee', 'xargs', 'jq',
|
|
37
|
+
// git (read-only)
|
|
38
|
+
'git',
|
|
39
|
+
// package query
|
|
40
|
+
'dpkg', 'rpm', 'brew', 'pip', 'npm', 'node', 'python', 'python3', 'ruby',
|
|
41
|
+
// recon / scanning (read-only, pentest auth covers ethics)
|
|
42
|
+
'gobuster', 'dirb', 'ffuf', 'nikto', 'whatweb', 'wpscan',
|
|
43
|
+
'sublist3r', 'amass', 'subfinder', 'httpx', 'nuclei',
|
|
44
|
+
]);
|
|
45
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/safety/patterns.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,sBAAsB;IACtB,uBAAuB;IACvB,qCAAqC;IACrC,oBAAoB;IACpB,2BAA2B;IAC3B,4BAA4B;IAC5B,+BAA+B;IAC/B,kCAAkC;CACnC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,mBAAmB;IACnB,mBAAmB;IACnB,gCAAgC;IAChC,6BAA6B;CAC9B,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpG,iFAAiF;AACjF,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IAC3C,cAAc;IACd,QAAQ,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS;IACxE,aAAa,EAAE,aAAa,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ;IACnE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,SAAS;IAC3D,8BAA8B;IAC9B,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM;IACxE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU;IACnE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ;IAClE,kBAAkB;IAClB,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK;IACpE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO;IACrE,KAAK,EAAE,UAAU,EAAE,OAAO;IAC1B,eAAe;IACf,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IACjE,kBAAkB;IAClB,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK;IACpE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI;IACjE,kBAAkB;IAClB,KAAK;IACL,gBAAgB;IAChB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM;IACxE,2DAA2D;IAC3D,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ;IACxD,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ;CACrD,CAAC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Mode, ProviderId } from '../types.js';
|
|
2
|
+
export interface ClaiConfig {
|
|
3
|
+
defaultProvider: ProviderId;
|
|
4
|
+
defaultModel: string;
|
|
5
|
+
defaultMode: Mode;
|
|
6
|
+
providerModels: Partial<Record<ProviderId, string>>;
|
|
7
|
+
allowAlwaysTools: string[];
|
|
8
|
+
pentestAuthorized: boolean;
|
|
9
|
+
sandboxRoots: string[];
|
|
10
|
+
ollamaHost: string;
|
|
11
|
+
telemetry: boolean;
|
|
12
|
+
lastUpdateCheck: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function getConfig(): ClaiConfig;
|
|
15
|
+
export declare function updateConfig(patch: Partial<ClaiConfig>): ClaiConfig;
|
|
16
|
+
export declare function setDefaultProvider(provider: ProviderId): ClaiConfig;
|
|
17
|
+
export declare function setDefaultMode(mode: Mode): ClaiConfig;
|
|
18
|
+
export declare function setProviderModel(provider: ProviderId, model: string): ClaiConfig;
|
|
19
|
+
export declare function getProviderModel(provider: ProviderId): string;
|
|
20
|
+
export declare function getConfigPath(): string;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
import { defaultModels } from '../llm/provider.js';
|
|
3
|
+
const defaults = {
|
|
4
|
+
defaultProvider: 'groq',
|
|
5
|
+
defaultModel: defaultModels.groq,
|
|
6
|
+
defaultMode: 'ask',
|
|
7
|
+
providerModels: {},
|
|
8
|
+
allowAlwaysTools: [],
|
|
9
|
+
pentestAuthorized: false,
|
|
10
|
+
sandboxRoots: [process.cwd()],
|
|
11
|
+
ollamaHost: 'http://localhost:11434',
|
|
12
|
+
telemetry: false,
|
|
13
|
+
lastUpdateCheck: 0,
|
|
14
|
+
};
|
|
15
|
+
const store = new Conf({
|
|
16
|
+
projectName: 'clai',
|
|
17
|
+
defaults,
|
|
18
|
+
});
|
|
19
|
+
export function getConfig() {
|
|
20
|
+
return store.store;
|
|
21
|
+
}
|
|
22
|
+
export function updateConfig(patch) {
|
|
23
|
+
const next = { ...getConfig(), ...patch };
|
|
24
|
+
store.store = next;
|
|
25
|
+
return next;
|
|
26
|
+
}
|
|
27
|
+
export function setDefaultProvider(provider) {
|
|
28
|
+
const model = getProviderModel(provider);
|
|
29
|
+
return updateConfig({ defaultProvider: provider, defaultModel: model });
|
|
30
|
+
}
|
|
31
|
+
export function setDefaultMode(mode) {
|
|
32
|
+
return updateConfig({ defaultMode: mode });
|
|
33
|
+
}
|
|
34
|
+
export function setProviderModel(provider, model) {
|
|
35
|
+
const current = getConfig();
|
|
36
|
+
const providerModels = { ...current.providerModels, [provider]: model };
|
|
37
|
+
return updateConfig({ providerModels, defaultModel: model });
|
|
38
|
+
}
|
|
39
|
+
export function getProviderModel(provider) {
|
|
40
|
+
const configured = getConfig().providerModels[provider];
|
|
41
|
+
return configured ?? defaultModels[provider];
|
|
42
|
+
}
|
|
43
|
+
export function getConfigPath() {
|
|
44
|
+
return store.path;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/store/config.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAenD,MAAM,QAAQ,GAAe;IAC3B,eAAe,EAAE,MAAM;IACvB,YAAY,EAAE,aAAa,CAAC,IAAI;IAChC,WAAW,EAAE,KAAK;IAClB,cAAc,EAAE,EAAE;IAClB,gBAAgB,EAAE,EAAE;IACpB,iBAAiB,EAAE,KAAK;IACxB,YAAY,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IAC7B,UAAU,EAAE,wBAAwB;IACpC,SAAS,EAAE,KAAK;IAChB,eAAe,EAAE,CAAC;CACnB,CAAC;AAEF,MAAM,KAAK,GAAG,IAAI,IAAI,CAAa;IACjC,WAAW,EAAE,MAAM;IACnB,QAAQ;CACT,CAAC,CAAC;AAEH,MAAM,UAAU,SAAS;IACvB,OAAO,KAAK,CAAC,KAAK,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAA0B;IACrD,MAAM,IAAI,GAAG,EAAE,GAAG,SAAS,EAAE,EAAE,GAAG,KAAK,EAAuB,CAAC;IAC/D,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;IACnB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAoB;IACrD,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACzC,OAAO,YAAY,CAAC,EAAE,eAAe,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAU;IACvC,OAAO,YAAY,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAoB,EAAE,KAAa;IAClE,MAAM,OAAO,GAAG,SAAS,EAAE,CAAC;IAC5B,MAAM,cAAc,GAAG,EAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,QAAQ,CAAC,EAAE,KAAK,EAAE,CAAC;IACxE,OAAO,YAAY,CAAC,EAAE,cAAc,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,QAAoB;IACnD,MAAM,UAAU,GAAG,SAAS,EAAE,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;IACxD,OAAO,UAAU,IAAI,aAAa,CAAC,QAAQ,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,KAAK,CAAC,IAAI,CAAC;AACpB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ChatMessage, ToolCall, ToolResult } from "../types.js";
|
|
2
|
+
export interface HistoryRecord {
|
|
3
|
+
id: string;
|
|
4
|
+
name?: string | undefined;
|
|
5
|
+
createdAt: string;
|
|
6
|
+
updatedAt: string;
|
|
7
|
+
cwd: string;
|
|
8
|
+
messages: ChatMessage[];
|
|
9
|
+
}
|
|
10
|
+
export interface ToolCallRecord {
|
|
11
|
+
id: string;
|
|
12
|
+
sessionId: string;
|
|
13
|
+
createdAt: string;
|
|
14
|
+
name: string;
|
|
15
|
+
args: Record<string, unknown>;
|
|
16
|
+
ok: boolean;
|
|
17
|
+
exitCode?: number | undefined;
|
|
18
|
+
output: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function saveSession(messages: ChatMessage[], name?: string | undefined): Promise<HistoryRecord>;
|
|
21
|
+
export declare function saveToolCall(sessionId: string, call: ToolCall, result: ToolResult): Promise<ToolCallRecord>;
|
|
22
|
+
export declare function listSessions(limit?: number): Promise<HistoryRecord[]>;
|
|
23
|
+
export declare function getSession(sessionId: string): Promise<HistoryRecord | undefined>;
|
|
24
|
+
export declare function getHistoryPath(): string;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { mkdir, appendFile, readFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { redactSecrets } from "../llm/provider.js";
|
|
6
|
+
const historyDir = join(homedir(), ".clai");
|
|
7
|
+
const dbFile = join(historyDir, "history.db");
|
|
8
|
+
const jsonlFile = join(historyDir, "history.jsonl");
|
|
9
|
+
const sqliteModuleName = "better-sqlite3";
|
|
10
|
+
let cachedDb;
|
|
11
|
+
let sqliteUnavailable = false;
|
|
12
|
+
async function loadDatabase() {
|
|
13
|
+
if (cachedDb)
|
|
14
|
+
return cachedDb;
|
|
15
|
+
if (sqliteUnavailable)
|
|
16
|
+
return undefined;
|
|
17
|
+
try {
|
|
18
|
+
await mkdir(historyDir, { recursive: true });
|
|
19
|
+
const imported = (await import(sqliteModuleName));
|
|
20
|
+
const Ctor = imported.default ?? imported;
|
|
21
|
+
cachedDb = new Ctor(dbFile);
|
|
22
|
+
cachedDb.exec(`
|
|
23
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
24
|
+
id TEXT PRIMARY KEY,
|
|
25
|
+
name TEXT,
|
|
26
|
+
created_at TEXT NOT NULL,
|
|
27
|
+
updated_at TEXT NOT NULL,
|
|
28
|
+
cwd TEXT NOT NULL,
|
|
29
|
+
messages_json TEXT NOT NULL
|
|
30
|
+
);
|
|
31
|
+
CREATE TABLE IF NOT EXISTS tool_calls (
|
|
32
|
+
id TEXT PRIMARY KEY,
|
|
33
|
+
session_id TEXT NOT NULL,
|
|
34
|
+
created_at TEXT NOT NULL,
|
|
35
|
+
name TEXT NOT NULL,
|
|
36
|
+
args_json TEXT NOT NULL,
|
|
37
|
+
ok INTEGER NOT NULL,
|
|
38
|
+
exit_code INTEGER,
|
|
39
|
+
output TEXT NOT NULL,
|
|
40
|
+
FOREIGN KEY(session_id) REFERENCES sessions(id)
|
|
41
|
+
);
|
|
42
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_updated_at ON sessions(updated_at);
|
|
43
|
+
CREATE INDEX IF NOT EXISTS idx_tool_calls_session_id ON tool_calls(session_id);
|
|
44
|
+
`);
|
|
45
|
+
return cachedDb;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
sqliteUnavailable = true;
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function newId() {
|
|
53
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
54
|
+
}
|
|
55
|
+
function scrubMessages(messages) {
|
|
56
|
+
return messages.map((message) => ({
|
|
57
|
+
...message,
|
|
58
|
+
content: redactSecrets(message.content),
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
async function appendJsonl(record) {
|
|
62
|
+
await mkdir(historyDir, { recursive: true });
|
|
63
|
+
await appendFile(jsonlFile, `${JSON.stringify(record)}\n`, "utf8");
|
|
64
|
+
}
|
|
65
|
+
export async function saveSession(messages, name) {
|
|
66
|
+
const now = new Date().toISOString();
|
|
67
|
+
const record = {
|
|
68
|
+
id: newId(),
|
|
69
|
+
name,
|
|
70
|
+
createdAt: now,
|
|
71
|
+
updatedAt: now,
|
|
72
|
+
cwd: process.cwd(),
|
|
73
|
+
messages: scrubMessages(messages),
|
|
74
|
+
};
|
|
75
|
+
const db = await loadDatabase();
|
|
76
|
+
if (db) {
|
|
77
|
+
db.prepare("INSERT INTO sessions (id, name, created_at, updated_at, cwd, messages_json) VALUES (?, ?, ?, ?, ?, ?)").run(record.id, record.name ?? null, record.createdAt, record.updatedAt, record.cwd, JSON.stringify(record.messages));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
await appendJsonl(record);
|
|
81
|
+
}
|
|
82
|
+
return record;
|
|
83
|
+
}
|
|
84
|
+
export async function saveToolCall(sessionId, call, result) {
|
|
85
|
+
const record = {
|
|
86
|
+
id: newId(),
|
|
87
|
+
sessionId,
|
|
88
|
+
createdAt: new Date().toISOString(),
|
|
89
|
+
name: call.name,
|
|
90
|
+
args: call.args,
|
|
91
|
+
ok: result.ok,
|
|
92
|
+
exitCode: result.exitCode,
|
|
93
|
+
output: redactSecrets(result.output),
|
|
94
|
+
};
|
|
95
|
+
const db = await loadDatabase();
|
|
96
|
+
if (db) {
|
|
97
|
+
db.prepare("INSERT INTO tool_calls (id, session_id, created_at, name, args_json, ok, exit_code, output) VALUES (?, ?, ?, ?, ?, ?, ?, ?)").run(record.id, record.sessionId, record.createdAt, record.name, JSON.stringify(record.args), record.ok ? 1 : 0, record.exitCode ?? null, record.output);
|
|
98
|
+
}
|
|
99
|
+
return record;
|
|
100
|
+
}
|
|
101
|
+
function rowToSession(row) {
|
|
102
|
+
const data = row;
|
|
103
|
+
return {
|
|
104
|
+
id: data.id,
|
|
105
|
+
name: data.name ?? undefined,
|
|
106
|
+
createdAt: data.created_at,
|
|
107
|
+
updatedAt: data.updated_at,
|
|
108
|
+
cwd: data.cwd,
|
|
109
|
+
messages: JSON.parse(data.messages_json),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
async function listJsonlSessions(limit) {
|
|
113
|
+
if (!existsSync(jsonlFile))
|
|
114
|
+
return [];
|
|
115
|
+
const raw = await readFile(jsonlFile, "utf8");
|
|
116
|
+
return raw
|
|
117
|
+
.split("\n")
|
|
118
|
+
.filter(Boolean)
|
|
119
|
+
.map((line) => JSON.parse(line))
|
|
120
|
+
.slice(-limit)
|
|
121
|
+
.reverse();
|
|
122
|
+
}
|
|
123
|
+
export async function listSessions(limit = 20) {
|
|
124
|
+
const db = await loadDatabase();
|
|
125
|
+
if (!db)
|
|
126
|
+
return listJsonlSessions(limit);
|
|
127
|
+
const rows = db
|
|
128
|
+
.prepare("SELECT id, name, created_at, updated_at, cwd, messages_json FROM sessions ORDER BY updated_at DESC LIMIT ?")
|
|
129
|
+
.all(limit);
|
|
130
|
+
return rows.map(rowToSession);
|
|
131
|
+
}
|
|
132
|
+
export async function getSession(sessionId) {
|
|
133
|
+
const db = await loadDatabase();
|
|
134
|
+
if (!db) {
|
|
135
|
+
return (await listJsonlSessions(Number.MAX_SAFE_INTEGER)).find((session) => session.id === sessionId);
|
|
136
|
+
}
|
|
137
|
+
const row = db
|
|
138
|
+
.prepare("SELECT id, name, created_at, updated_at, cwd, messages_json FROM sessions WHERE id = ?")
|
|
139
|
+
.get(sessionId);
|
|
140
|
+
return row ? rowToSession(row) : undefined;
|
|
141
|
+
}
|
|
142
|
+
export function getHistoryPath() {
|
|
143
|
+
return sqliteUnavailable ? jsonlFile : dbFile;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/store/history.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ProviderId, ProviderStatus } from '../types.js';
|
|
2
|
+
export declare function getFallbackKeysPath(): string;
|
|
3
|
+
export declare function envValue(provider: ProviderId): string | undefined;
|
|
4
|
+
export declare function getProviderSecret(provider: ProviderId): Promise<{
|
|
5
|
+
value?: string;
|
|
6
|
+
source: ProviderStatus['source'];
|
|
7
|
+
}>;
|
|
8
|
+
export declare function setProviderSecret(provider: ProviderId, secret: string): Promise<'keychain' | 'fallback'>;
|
|
9
|
+
export declare function unsetProviderSecret(provider: ProviderId): Promise<void>;
|
|
10
|
+
export declare function listProviderStatuses(activeProvider: ProviderId): Promise<ProviderStatus[]>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { providerIds } from '../types.js';
|
|
6
|
+
import { envVars, getDefaultModel, maskSecret } from '../llm/provider.js';
|
|
7
|
+
import { getConfig } from './config.js';
|
|
8
|
+
const serviceName = 'clai';
|
|
9
|
+
const keychainModuleName = 'keytar';
|
|
10
|
+
const keysFile = join(homedir(), '.clai', 'keys.json');
|
|
11
|
+
async function loadKeytar() {
|
|
12
|
+
try {
|
|
13
|
+
const imported = (await import(keychainModuleName));
|
|
14
|
+
return imported.default ?? imported;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function readFallback() {
|
|
21
|
+
if (!existsSync(keysFile)) {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
const raw = await readFile(keysFile, 'utf8');
|
|
25
|
+
return JSON.parse(raw);
|
|
26
|
+
}
|
|
27
|
+
async function writeFallback(keys) {
|
|
28
|
+
await mkdir(dirname(keysFile), { recursive: true });
|
|
29
|
+
await writeFile(keysFile, `${JSON.stringify(keys, null, 2)}\n`, { mode: 0o600 });
|
|
30
|
+
if (process.platform !== 'win32') {
|
|
31
|
+
await chmod(keysFile, 0o600);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function getFallbackKeysPath() {
|
|
35
|
+
return keysFile;
|
|
36
|
+
}
|
|
37
|
+
export function envValue(provider) {
|
|
38
|
+
const envVar = envVars[provider];
|
|
39
|
+
if (!envVar) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const value = process.env[envVar];
|
|
43
|
+
return value && value.length > 0 ? value : undefined;
|
|
44
|
+
}
|
|
45
|
+
export async function getProviderSecret(provider) {
|
|
46
|
+
const env = envValue(provider);
|
|
47
|
+
if (env) {
|
|
48
|
+
return { value: env, source: provider === 'ollama' ? 'local' : 'env' };
|
|
49
|
+
}
|
|
50
|
+
if (provider === 'ollama') {
|
|
51
|
+
return { value: getConfig().ollamaHost, source: 'local' };
|
|
52
|
+
}
|
|
53
|
+
const keytar = await loadKeytar();
|
|
54
|
+
if (keytar) {
|
|
55
|
+
const fromKeychain = await keytar.getPassword(serviceName, provider);
|
|
56
|
+
if (fromKeychain) {
|
|
57
|
+
return { value: fromKeychain, source: 'keychain' };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const fallback = await readFallback();
|
|
61
|
+
const fromFallback = fallback[provider];
|
|
62
|
+
if (fromFallback) {
|
|
63
|
+
return { value: fromFallback, source: 'fallback' };
|
|
64
|
+
}
|
|
65
|
+
return { source: 'missing' };
|
|
66
|
+
}
|
|
67
|
+
export async function setProviderSecret(provider, secret) {
|
|
68
|
+
if (provider === 'ollama') {
|
|
69
|
+
return 'fallback';
|
|
70
|
+
}
|
|
71
|
+
const keytar = await loadKeytar();
|
|
72
|
+
if (keytar) {
|
|
73
|
+
await keytar.setPassword(serviceName, provider, secret);
|
|
74
|
+
return 'keychain';
|
|
75
|
+
}
|
|
76
|
+
const fallback = await readFallback();
|
|
77
|
+
fallback[provider] = secret;
|
|
78
|
+
await writeFallback(fallback);
|
|
79
|
+
return 'fallback';
|
|
80
|
+
}
|
|
81
|
+
export async function unsetProviderSecret(provider) {
|
|
82
|
+
const keytar = await loadKeytar();
|
|
83
|
+
if (keytar) {
|
|
84
|
+
await keytar.deletePassword(serviceName, provider);
|
|
85
|
+
}
|
|
86
|
+
if (existsSync(keysFile)) {
|
|
87
|
+
const fallback = await readFallback();
|
|
88
|
+
delete fallback[provider];
|
|
89
|
+
if (Object.keys(fallback).length === 0) {
|
|
90
|
+
await rm(keysFile, { force: true });
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
await writeFallback(fallback);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
export async function listProviderStatuses(activeProvider) {
|
|
98
|
+
const statuses = [];
|
|
99
|
+
for (const provider of providerIds) {
|
|
100
|
+
const secret = await getProviderSecret(provider);
|
|
101
|
+
const configured = Boolean(secret.value) || provider === 'ollama';
|
|
102
|
+
statuses.push({
|
|
103
|
+
provider,
|
|
104
|
+
label: provider,
|
|
105
|
+
active: provider === activeProvider,
|
|
106
|
+
configured,
|
|
107
|
+
source: secret.value ? secret.source : 'missing',
|
|
108
|
+
maskedKey: secret.value && provider !== 'ollama' ? maskSecret(secret.value) : undefined,
|
|
109
|
+
model: getDefaultModel(provider),
|
|
110
|
+
note: provider === 'ollama' ? secret.value : undefined,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
return statuses;
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../src/store/keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { mkdir, readdir, rename, stat, appendFile } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { redactSecrets } from '../llm/provider.js';
|
|
6
|
+
const logsDir = join(homedir(), '.clai', 'logs');
|
|
7
|
+
const maxLogBytes = 10 * 1024 * 1024;
|
|
8
|
+
function today() {
|
|
9
|
+
return new Date().toISOString().slice(0, 10).replaceAll('-', '');
|
|
10
|
+
}
|
|
11
|
+
export function getLogPath() {
|
|
12
|
+
return join(logsDir, `clai-${today()}.log`);
|
|
13
|
+
}
|
|
14
|
+
async function rotateIfNeeded(path) {
|
|
15
|
+
if (!existsSync(path))
|
|
16
|
+
return;
|
|
17
|
+
const info = await stat(path);
|
|
18
|
+
if (info.size < maxLogBytes)
|
|
19
|
+
return;
|
|
20
|
+
const siblings = await readdir(logsDir).catch(() => []);
|
|
21
|
+
const count = siblings.filter((name) => name.startsWith(`clai-${today()}.log.`)).length + 1;
|
|
22
|
+
await rename(path, `${path}.${count}`);
|
|
23
|
+
}
|
|
24
|
+
export async function auditLog(event, payload = {}) {
|
|
25
|
+
await mkdir(logsDir, { recursive: true });
|
|
26
|
+
const path = getLogPath();
|
|
27
|
+
await rotateIfNeeded(path);
|
|
28
|
+
const entry = redactSecrets(JSON.stringify({ at: new Date().toISOString(), event, payload }));
|
|
29
|
+
await appendFile(path, `${entry}\n`, 'utf8');
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=logs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/store/logs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AACjD,MAAM,WAAW,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAErC,SAAS,KAAK;IACZ,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,IAAI,CAAC,OAAO,EAAE,QAAQ,KAAK,EAAE,MAAM,CAAC,CAAC;AAC9C,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,IAAY;IACxC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO;IAC9B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,IAAI,CAAC,IAAI,GAAG,WAAW;QAAE,OAAO;IACpC,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAC5F,MAAM,MAAM,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,KAAa,EAAE,UAAmB,EAAE;IACjE,MAAM,KAAK,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAC1B,MAAM,cAAc,CAAC,IAAI,CAAC,CAAC;IAC3B,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;IAC9F,MAAM,UAAU,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,EAAE,MAAM,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const contextFile = join(process.cwd(), '.clai', 'context.md');
|
|
5
|
+
export async function loadProjectContext() {
|
|
6
|
+
if (!existsSync(contextFile))
|
|
7
|
+
return undefined;
|
|
8
|
+
const content = await readFile(contextFile, 'utf8');
|
|
9
|
+
return content.trim().length > 0 ? content.trim() : undefined;
|
|
10
|
+
}
|
|
11
|
+
export function getProjectContextPath() {
|
|
12
|
+
return contextFile;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/store/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC;AAE/D,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/C,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACpD,OAAO,OAAO,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAChE,CAAC;AAED,MAAM,UAAU,qBAAqB;IACnC,OAAO,WAAW,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ToolResult } from "../types.js";
|
|
2
|
+
export declare function fsRead(path: string): Promise<ToolResult>;
|
|
3
|
+
export declare function fsWrite(path: string, content: string): Promise<ToolResult>;
|
|
4
|
+
export declare function fsList(path: string): Promise<ToolResult>;
|
|
5
|
+
export declare function fsSearch(pattern: string, path?: string): Promise<ToolResult>;
|