@goobits/sherpa 1.0.1
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/chunk-3CILH2TO.js +387 -0
- package/dist/chunk-3CILH2TO.js.map +7 -0
- package/dist/chunk-5NF3BSD6.js +512 -0
- package/dist/chunk-5NF3BSD6.js.map +7 -0
- package/dist/chunk-IIU6U7TE.js +307 -0
- package/dist/chunk-IIU6U7TE.js.map +7 -0
- package/dist/chunk-LQZTKH3U.js +307 -0
- package/dist/chunk-LQZTKH3U.js.map +7 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +84 -0
- package/dist/cli.js.map +7 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +333 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/post.d.ts +20 -0
- package/dist/commands/post.d.ts.map +1 -0
- package/dist/commands/post.js +183 -0
- package/dist/commands/post.js.map +1 -0
- package/dist/commands/pre.d.ts +18 -0
- package/dist/commands/pre.d.ts.map +1 -0
- package/dist/commands/pre.js +102 -0
- package/dist/commands/pre.js.map +1 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +48 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/daemon-V2QDZTUB.js +89 -0
- package/dist/daemon-V2QDZTUB.js.map +7 -0
- package/dist/daemon.d.ts +9 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +112 -0
- package/dist/daemon.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/index.js.map +7 -0
- package/dist/parser.d.ts +21 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +152 -0
- package/dist/parser.js.map +1 -0
- package/dist/reviewer/index.js +544 -0
- package/dist/reviewer/index.js.map +7 -0
- package/dist/rules.d.ts +21 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +165 -0
- package/dist/rules.js.map +1 -0
- package/dist/status-Q6Z4TFJZ.js +52 -0
- package/dist/status-Q6Z4TFJZ.js.map +7 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sherpa post - PostToolUse hook that offloads large outputs to scratch files
|
|
3
|
+
*/
|
|
4
|
+
import { countTokens, loadConfig, readHookInput, writeHookOutput } from '@goobits/sherpa-core';
|
|
5
|
+
import { createHash } from 'crypto';
|
|
6
|
+
import { existsSync, mkdirSync, readdirSync, statSync, unlinkSync, writeFileSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { DEFAULT_CONFIG } from '../types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Load guard config from .claude/guard.json or fallback locations
|
|
11
|
+
*/
|
|
12
|
+
export function loadGuardConfig() {
|
|
13
|
+
const searchPaths = [
|
|
14
|
+
join(process.cwd(), '.claude', 'guard.json'),
|
|
15
|
+
join(process.cwd(), 'guard.json')
|
|
16
|
+
];
|
|
17
|
+
return loadConfig('guard.json', DEFAULT_CONFIG, searchPaths);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Clean up scratch files by age and size
|
|
21
|
+
*/
|
|
22
|
+
function cleanupScratch(scratchDir, maxAgeMinutes, maxSizeMB) {
|
|
23
|
+
try {
|
|
24
|
+
if (!existsSync(scratchDir)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const files = readdirSync(scratchDir);
|
|
28
|
+
const now = Date.now();
|
|
29
|
+
const maxAge = maxAgeMinutes * 60 * 1000;
|
|
30
|
+
const maxBytes = maxSizeMB * 1024 * 1024;
|
|
31
|
+
// Collect file info
|
|
32
|
+
const fileInfos = [];
|
|
33
|
+
let totalSize = 0;
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
if (!file.startsWith('out_')) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const filepath = join(scratchDir, file);
|
|
39
|
+
try {
|
|
40
|
+
const stat = statSync(filepath);
|
|
41
|
+
fileInfos.push({
|
|
42
|
+
path: filepath,
|
|
43
|
+
size: stat.size,
|
|
44
|
+
mtime: stat.mtimeMs
|
|
45
|
+
});
|
|
46
|
+
totalSize += stat.size;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// Ignore errors on individual files
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Delete files older than maxAge
|
|
53
|
+
for (const info of fileInfos) {
|
|
54
|
+
if (now - info.mtime > maxAge) {
|
|
55
|
+
try {
|
|
56
|
+
unlinkSync(info.path);
|
|
57
|
+
totalSize -= info.size;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Ignore
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
// If still over size limit, delete oldest files (LRU)
|
|
65
|
+
if (totalSize > maxBytes) {
|
|
66
|
+
const remaining = fileInfos
|
|
67
|
+
.filter(f => existsSync(f.path))
|
|
68
|
+
.sort((a, b) => a.mtime - b.mtime); // Oldest first
|
|
69
|
+
for (const info of remaining) {
|
|
70
|
+
if (totalSize <= maxBytes) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
unlinkSync(info.path);
|
|
75
|
+
totalSize -= info.size;
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Ignore
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
// Ignore if directory doesn't exist yet
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Generate a short hash for the output
|
|
89
|
+
*/
|
|
90
|
+
function hashOutput(content) {
|
|
91
|
+
return createHash('md5').update(content).digest('hex').slice(0, 8);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Offload large output to a scratch file
|
|
95
|
+
*/
|
|
96
|
+
export function offloadOutput(output, exitCode, config) {
|
|
97
|
+
const tokens = countTokens(output);
|
|
98
|
+
const lines = output.split('\n');
|
|
99
|
+
// Small output: pass through
|
|
100
|
+
if (tokens <= config.maxTokens) {
|
|
101
|
+
return { modified: false, result: output };
|
|
102
|
+
}
|
|
103
|
+
// Create scratch directory
|
|
104
|
+
const scratchDir = join(process.cwd(), config.scratchDir);
|
|
105
|
+
mkdirSync(scratchDir, { recursive: true });
|
|
106
|
+
// Clean up old files and enforce size limit
|
|
107
|
+
cleanupScratch(scratchDir, config.maxAgeMinutes, config.maxScratchSizeMB);
|
|
108
|
+
// Save to scratch file
|
|
109
|
+
const hash = hashOutput(output);
|
|
110
|
+
const filename = `out_${hash}_exit${exitCode}.txt`;
|
|
111
|
+
const filepath = join(scratchDir, filename);
|
|
112
|
+
writeFileSync(filepath, output);
|
|
113
|
+
// Create preview (last N tokens worth of lines)
|
|
114
|
+
// Estimate ~4 chars per token to avoid expensive per-line token counting
|
|
115
|
+
const charsPerToken = 4;
|
|
116
|
+
const targetChars = config.previewTokens * charsPerToken;
|
|
117
|
+
const previewLines = [];
|
|
118
|
+
let charCount = 0;
|
|
119
|
+
for (let i = lines.length - 1; i >= 0 && charCount < targetChars; i--) {
|
|
120
|
+
previewLines.unshift(lines[i]);
|
|
121
|
+
charCount += lines[i].length + 1; // +1 for newline
|
|
122
|
+
}
|
|
123
|
+
const preview = previewLines.join('\n');
|
|
124
|
+
// Build pointer message
|
|
125
|
+
const sizeKB = (output.length / 1024).toFixed(1);
|
|
126
|
+
const result = [
|
|
127
|
+
`┌─ Output offloaded (${lines.length} lines, ${sizeKB}KB, ~${tokens} tokens)`,
|
|
128
|
+
`│ File: ${filepath}`,
|
|
129
|
+
`│ Hint: grep <pattern> ${filepath}`,
|
|
130
|
+
`└─ Last ${previewLines.length} lines:`,
|
|
131
|
+
preview
|
|
132
|
+
].join('\n');
|
|
133
|
+
return { modified: true, result };
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Main entry point for PostToolUse hook
|
|
137
|
+
*/
|
|
138
|
+
export function runPost() {
|
|
139
|
+
try {
|
|
140
|
+
const data = readHookInput();
|
|
141
|
+
// Only handle Bash tool
|
|
142
|
+
if (data.tool_name !== 'Bash') {
|
|
143
|
+
writeHookOutput(data);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const stdout = data.tool_result?.stdout || '';
|
|
147
|
+
const stderr = data.tool_result?.stderr || '';
|
|
148
|
+
const exitCode = data.tool_result?.exit_code || 0;
|
|
149
|
+
// Load config from .claude/guard.json or defaults
|
|
150
|
+
const config = loadGuardConfig();
|
|
151
|
+
// Check stdout
|
|
152
|
+
const stdoutResult = offloadOutput(stdout, exitCode, config);
|
|
153
|
+
// Check stderr (usually smaller, but handle anyway)
|
|
154
|
+
const stderrResult = offloadOutput(stderr, exitCode, config);
|
|
155
|
+
// If nothing was modified, pass through
|
|
156
|
+
if (!stdoutResult.modified && !stderrResult.modified) {
|
|
157
|
+
writeHookOutput(data);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// Return modified result
|
|
161
|
+
const result = {
|
|
162
|
+
...data,
|
|
163
|
+
tool_result: {
|
|
164
|
+
...data.tool_result,
|
|
165
|
+
stdout: stdoutResult.result,
|
|
166
|
+
stderr: stderrResult.modified ? stderrResult.result : stderr
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
writeHookOutput(result);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
// On error, try to pass through original
|
|
173
|
+
console.error('sherpa post error:', error.message);
|
|
174
|
+
try {
|
|
175
|
+
const data = readHookInput();
|
|
176
|
+
writeHookOutput(data);
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Nothing we can do
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=post.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post.js","sourceRoot":"","sources":["../../src/commands/post.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sherpa pre - PreToolUse hook that blocks dangerous bash commands
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Run pre-guard check on a command
|
|
6
|
+
*/
|
|
7
|
+
export declare function checkBashCommand(command: string): {
|
|
8
|
+
blocked: boolean;
|
|
9
|
+
rule?: {
|
|
10
|
+
name: string;
|
|
11
|
+
reason: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Main entry point for PreToolUse hook
|
|
16
|
+
*/
|
|
17
|
+
export declare function runPre(): void;
|
|
18
|
+
//# sourceMappingURL=pre.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre.d.ts","sourceRoot":"","sources":["../../src/commands/pre.ts"],"names":[],"mappings":"AAAA;;GAEG;AAsDH;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAgC/G;AAED;;GAEG;AACH,wBAAgB,MAAM,IAAI,IAAI,CAyB7B"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sherpa pre - PreToolUse hook that blocks dangerous bash commands
|
|
3
|
+
*/
|
|
4
|
+
import { EXIT, readHookInput } from '@goobits/sherpa-core';
|
|
5
|
+
// @ts-expect-error - bash-parser has no types
|
|
6
|
+
import parse from 'bash-parser';
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
import { dirname, join } from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { extractCommands } from '../parser.js';
|
|
11
|
+
import { checkCommand, checkPipeline } from '../rules.js';
|
|
12
|
+
// Load rules from JSON config
|
|
13
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
const rulesPath = join(__dirname, '..', '..', 'rules.json');
|
|
15
|
+
const rules = JSON.parse(readFileSync(rulesPath, 'utf-8'));
|
|
16
|
+
// Fast-path: commands that are always safe (skip expensive parse)
|
|
17
|
+
const SAFE_COMMAND_PREFIXES = [
|
|
18
|
+
'echo ', 'echo\t', 'printf ',
|
|
19
|
+
'ls ', 'ls\t', 'ls\n', 'ls',
|
|
20
|
+
'pwd', 'date', 'whoami', 'id',
|
|
21
|
+
'cat ', 'head ', 'tail ', 'wc ',
|
|
22
|
+
'grep ', 'awk ', 'sed ',
|
|
23
|
+
'cd ', 'cd\t',
|
|
24
|
+
'true', 'false', ':'
|
|
25
|
+
];
|
|
26
|
+
function isFastPathSafe(command) {
|
|
27
|
+
const trimmed = command.trim();
|
|
28
|
+
// Check if command starts with a known-safe prefix
|
|
29
|
+
for (const prefix of SAFE_COMMAND_PREFIXES) {
|
|
30
|
+
if (trimmed === prefix.trim() || trimmed.startsWith(prefix)) {
|
|
31
|
+
// Quick check: no compound commands, pipes, or dangerous chars
|
|
32
|
+
if (!trimmed.includes('|') &&
|
|
33
|
+
!trimmed.includes('&&') &&
|
|
34
|
+
!trimmed.includes('||') &&
|
|
35
|
+
!trimmed.includes(';') &&
|
|
36
|
+
!trimmed.includes('$(') &&
|
|
37
|
+
!trimmed.includes('`')) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Run pre-guard check on a command
|
|
46
|
+
*/
|
|
47
|
+
export function checkBashCommand(command) {
|
|
48
|
+
// Fast path: skip parsing for known-safe commands
|
|
49
|
+
if (isFastPathSafe(command)) {
|
|
50
|
+
return { blocked: false };
|
|
51
|
+
}
|
|
52
|
+
// Parse command into AST
|
|
53
|
+
let ast;
|
|
54
|
+
try {
|
|
55
|
+
ast = parse(command);
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// If parsing fails, allow (fail open)
|
|
59
|
+
return { blocked: false };
|
|
60
|
+
}
|
|
61
|
+
// Check pipeline patterns first (curl | bash)
|
|
62
|
+
const pipeRule = checkPipeline(ast, rules);
|
|
63
|
+
if (pipeRule) {
|
|
64
|
+
return { blocked: true, rule: { name: pipeRule.name, reason: pipeRule.reason } };
|
|
65
|
+
}
|
|
66
|
+
// Extract and check each command
|
|
67
|
+
const commands = extractCommands(ast);
|
|
68
|
+
for (const cmdInfo of commands) {
|
|
69
|
+
const result = checkCommand(cmdInfo, rules);
|
|
70
|
+
if (result.blocked && result.rule) {
|
|
71
|
+
return { blocked: true, rule: { name: result.rule.name, reason: result.rule.reason } };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { blocked: false };
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Main entry point for PreToolUse hook
|
|
78
|
+
*/
|
|
79
|
+
export function runPre() {
|
|
80
|
+
try {
|
|
81
|
+
const data = readHookInput();
|
|
82
|
+
const command = data.tool_input?.command;
|
|
83
|
+
if (!command) {
|
|
84
|
+
process.exit(EXIT.ALLOW);
|
|
85
|
+
}
|
|
86
|
+
const result = checkBashCommand(command);
|
|
87
|
+
if (result.blocked && result.rule) {
|
|
88
|
+
console.error('BLOCKED by sherpa');
|
|
89
|
+
console.error(` Rule: ${result.rule.name}`);
|
|
90
|
+
console.error(` Reason: ${result.rule.reason}`);
|
|
91
|
+
console.error(` Command: ${command}`);
|
|
92
|
+
process.exit(EXIT.BLOCK);
|
|
93
|
+
}
|
|
94
|
+
process.exit(EXIT.ALLOW);
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
// Graceful degradation: allow on error
|
|
98
|
+
console.error('sherpa pre error:', error.message);
|
|
99
|
+
process.exit(EXIT.ALLOW);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=pre.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pre.js","sourceRoot":"","sources":["../../src/commands/pre.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,IAAI,EAEJ,aAAa,EACb,MAAM,sBAAsB,CAAA;AAC7B,8CAA8C;AAC9C,OAAO,KAAK,MAAM,aAAa,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAA;AACjC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAA;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAGzD,8BAA8B;AAC9B,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAA;AACzD,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,CAAC,CAAA;AAC3D,MAAM,KAAK,GAAgB,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAA;AAEvE,kEAAkE;AAClE,MAAM,qBAAqB,GAAG;IAC7B,OAAO,EAAE,QAAQ,EAAE,SAAS;IAC5B,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI;IAC3B,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI;IAC7B,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK;IAC/B,OAAO,EAAE,MAAM,EAAE,MAAM;IACvB,KAAK,EAAE,MAAM;IACb,MAAM,EAAE,OAAO,EAAE,GAAG;CACpB,CAAA;AAED,SAAS,cAAc,CAAC,OAAe;IACtC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;IAC9B,mDAAmD;IACnD,KAAK,MAAM,MAAM,IAAI,qBAAqB,EAAE,CAAC;QAC5C,IAAI,OAAO,KAAK,MAAM,CAAC,IAAI,EAAE,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7D,+DAA+D;YAC/D,IACC,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;gBACtB,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACvB,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACvB,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;gBACtB,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;gBACvB,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EACrB,CAAC;gBACF,OAAO,IAAI,CAAA;YACZ,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAA;AACb,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAe;IAC/C,kDAAkD;IAClD,IAAI,cAAc,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;IAC1B,CAAC;IAED,yBAAyB;IACzB,IAAI,GAAY,CAAA;IAChB,IAAI,CAAC;QACJ,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,CAAA;IACrB,CAAC;IAAC,MAAM,CAAC;QACR,sCAAsC;QACtC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;IAC1B,CAAC;IAED,8CAA8C;IAC9C,MAAM,QAAQ,GAAG,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;IAC1C,IAAI,QAAQ,EAAE,CAAC;QACd,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAA;IACjF,CAAC;IAED,iCAAiC;IACjC,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAA;IAErC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC3C,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YACnC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAA;QACvF,CAAC;IACF,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,MAAM;IACrB,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,aAAa,EAAgB,CAAA;QAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,OAAO,CAAA;QAExC,IAAI,CAAC,OAAO,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;QAED,MAAM,MAAM,GAAG,gBAAgB,CAAC,OAAO,CAAC,CAAA;QAExC,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;YACnC,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAA;YAClC,OAAO,CAAC,KAAK,CAAC,WAAY,MAAM,CAAC,IAAI,CAAC,IAAK,EAAE,CAAC,CAAA;YAC9C,OAAO,CAAC,KAAK,CAAC,aAAc,MAAM,CAAC,IAAI,CAAC,MAAO,EAAE,CAAC,CAAA;YAClD,OAAO,CAAC,KAAK,CAAC,cAAe,OAAQ,EAAE,CAAC,CAAA;YACxC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACzB,CAAC;QAED,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;IAAC,OAAM,KAAK,EAAE,CAAC;QACf,uCAAuC;QACvC,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAG,KAAe,CAAC,OAAO,CAAC,CAAA;QAC5D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IACzB,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAmD/C"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sherpa status - Show LLM provider status and rate limits
|
|
3
|
+
*/
|
|
4
|
+
import { checkProviderStatus, getAvailableProviders } from '@goobits/sherpa-core';
|
|
5
|
+
export async function runStatus() {
|
|
6
|
+
console.log('Checking LLM providers...\n');
|
|
7
|
+
const available = getAvailableProviders();
|
|
8
|
+
if (available.length === 0) {
|
|
9
|
+
console.log('No providers configured.');
|
|
10
|
+
console.log('Set CEREBRAS_API_KEY, GROQ_API_KEY, or OPENAI_API_KEY');
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
console.log(`Configured: ${available.join(', ')}\n`);
|
|
14
|
+
const statuses = await checkProviderStatus();
|
|
15
|
+
for (const status of statuses) {
|
|
16
|
+
const icon = status.available ? '✓' : '✗';
|
|
17
|
+
console.log(`${icon} ${status.provider.toUpperCase()}`);
|
|
18
|
+
if (status.error) {
|
|
19
|
+
console.log(` Error: ${status.error}`);
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (status.limits) {
|
|
23
|
+
const l = status.limits;
|
|
24
|
+
// Show requests
|
|
25
|
+
if (l.requestsPerMinute && l.requestsRemainingMinute) {
|
|
26
|
+
const pct = Math.round((l.requestsRemainingMinute / l.requestsPerMinute) * 100);
|
|
27
|
+
console.log(` Requests/min: ${l.requestsRemainingMinute.toLocaleString()}/${l.requestsPerMinute.toLocaleString()} (${pct}%)`);
|
|
28
|
+
}
|
|
29
|
+
if (l.requestsRemainingDay) {
|
|
30
|
+
const limit = l.requestsPerDay || 14400; // Cerebras free tier default
|
|
31
|
+
const used = limit - l.requestsRemainingDay;
|
|
32
|
+
console.log(` Requests/day: ${l.requestsRemainingDay.toLocaleString()} remaining (${used.toLocaleString()} used)`);
|
|
33
|
+
}
|
|
34
|
+
// Show tokens
|
|
35
|
+
if (l.tokensPerMinute && l.tokensRemainingMinute) {
|
|
36
|
+
const pct = Math.round((l.tokensRemainingMinute / l.tokensPerMinute) * 100);
|
|
37
|
+
console.log(` Tokens/min: ${l.tokensRemainingMinute.toLocaleString()}/${l.tokensPerMinute.toLocaleString()} (${pct}%)`);
|
|
38
|
+
}
|
|
39
|
+
if (l.tokensRemainingDay) {
|
|
40
|
+
const limit = l.tokensPerDay || 1000000; // Cerebras free tier default
|
|
41
|
+
const used = limit - l.tokensRemainingDay;
|
|
42
|
+
console.log(` Tokens/day: ${l.tokensRemainingDay.toLocaleString()} remaining (${used.toLocaleString()} used)`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
console.log('');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=status.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_CONFIG,
|
|
4
|
+
checkBashCommand,
|
|
5
|
+
offloadOutput
|
|
6
|
+
} from "./chunk-5NF3BSD6.js";
|
|
7
|
+
import {
|
|
8
|
+
loadConfig
|
|
9
|
+
} from "./chunk-3CILH2TO.js";
|
|
10
|
+
|
|
11
|
+
// src/daemon.ts
|
|
12
|
+
import { existsSync, unlinkSync } from "fs";
|
|
13
|
+
import { createServer } from "net";
|
|
14
|
+
var config = loadConfig("config.json", DEFAULT_CONFIG);
|
|
15
|
+
var socketPath = config.socketPath;
|
|
16
|
+
function handleConnection(socket) {
|
|
17
|
+
let buffer = "";
|
|
18
|
+
socket.on("data", (chunk) => {
|
|
19
|
+
buffer += chunk.toString();
|
|
20
|
+
if (buffer.length > 10 * 1024 * 1024) {
|
|
21
|
+
socket.write(JSON.stringify({ error: "Request too large" }));
|
|
22
|
+
socket.end();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const request = JSON.parse(buffer);
|
|
27
|
+
buffer = "";
|
|
28
|
+
let response;
|
|
29
|
+
if (request.type === "pre") {
|
|
30
|
+
const { command } = request.data;
|
|
31
|
+
const result = checkBashCommand(command);
|
|
32
|
+
response = result;
|
|
33
|
+
} else if (request.type === "post") {
|
|
34
|
+
const { stdout, stderr, exit_code } = request.data;
|
|
35
|
+
const stdoutResult = offloadOutput(stdout, exit_code, config);
|
|
36
|
+
const stderrResult = offloadOutput(stderr, exit_code, config);
|
|
37
|
+
response = {
|
|
38
|
+
stdout: stdoutResult.result,
|
|
39
|
+
stderr: stderrResult.result,
|
|
40
|
+
modified: stdoutResult.modified || stderrResult.modified
|
|
41
|
+
};
|
|
42
|
+
} else {
|
|
43
|
+
response = { error: "Unknown request type" };
|
|
44
|
+
}
|
|
45
|
+
socket.write(JSON.stringify(response));
|
|
46
|
+
socket.end();
|
|
47
|
+
} catch (err) {
|
|
48
|
+
if (err instanceof SyntaxError && !err.message.includes("end of JSON")) {
|
|
49
|
+
buffer = "";
|
|
50
|
+
socket.write(JSON.stringify({ error: "Invalid JSON" }));
|
|
51
|
+
socket.end();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
socket.on("error", (err) => {
|
|
56
|
+
console.error("Socket error:", err.message);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function cleanup() {
|
|
60
|
+
try {
|
|
61
|
+
if (existsSync(socketPath)) {
|
|
62
|
+
unlinkSync(socketPath);
|
|
63
|
+
}
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
function main() {
|
|
69
|
+
if (existsSync(socketPath)) {
|
|
70
|
+
unlinkSync(socketPath);
|
|
71
|
+
}
|
|
72
|
+
const server = createServer(handleConnection);
|
|
73
|
+
process.on("SIGTERM", cleanup);
|
|
74
|
+
process.on("SIGINT", cleanup);
|
|
75
|
+
process.on("SIGHUP", cleanup);
|
|
76
|
+
process.on("uncaughtException", (err) => {
|
|
77
|
+
console.error("Uncaught exception:", err);
|
|
78
|
+
cleanup();
|
|
79
|
+
});
|
|
80
|
+
server.listen(socketPath, () => {
|
|
81
|
+
console.error(`Guard daemon listening on ${socketPath}`);
|
|
82
|
+
});
|
|
83
|
+
server.on("error", (err) => {
|
|
84
|
+
console.error("Server error:", err);
|
|
85
|
+
cleanup();
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
main();
|
|
89
|
+
//# sourceMappingURL=daemon-V2QDZTUB.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/daemon.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\n/**\n * Guard daemon - persistent server for fast hook responses\n *\n * Keeps bash-parser and rules loaded in memory.\n * Communicates via Unix socket for sub-5ms response times.\n */\n\nimport { loadConfig } from '@goobits/sherpa-core'\nimport { existsSync, unlinkSync } from 'fs'\nimport { createServer, type Socket } from 'net'\n\nimport { offloadOutput } from './commands/post.js'\nimport { checkBashCommand } from './commands/pre.js'\nimport { DEFAULT_CONFIG, type GuardConfig } from './types.js'\n\n// Load config\nconst config = loadConfig<GuardConfig>('config.json', DEFAULT_CONFIG)\nconst socketPath = config.socketPath\n\ninterface DaemonRequest {\n\ttype: 'pre' | 'post';\n\tdata: unknown;\n}\n\ninterface PreRequest {\n\tcommand: string;\n}\n\ninterface PostRequest {\n\tstdout: string;\n\tstderr: string;\n\texit_code: number;\n}\n\n/**\n * Handle a client connection\n */\nfunction handleConnection(socket: Socket): void {\n\tlet buffer = ''\n\n\tsocket.on('data', chunk => {\n\t\tbuffer += chunk.toString()\n\n\t\t// Limit buffer size to prevent DoS\n\t\tif (buffer.length > 10 * 1024 * 1024) {\n\t\t\tsocket.write(JSON.stringify({ error: 'Request too large' }))\n\t\t\tsocket.end()\n\t\t\treturn\n\t\t}\n\n\t\t// Try to parse complete JSON\n\t\ttry {\n\t\t\tconst request: DaemonRequest = JSON.parse(buffer)\n\t\t\tbuffer = ''\n\n\t\t\tlet response: unknown\n\n\t\t\tif (request.type === 'pre') {\n\t\t\t\tconst { command } = request.data as PreRequest\n\t\t\t\tconst result = checkBashCommand(command)\n\t\t\t\tresponse = result\n\t\t\t} else if (request.type === 'post') {\n\t\t\t\tconst { stdout, stderr, exit_code } = request.data as PostRequest\n\t\t\t\tconst stdoutResult = offloadOutput(stdout, exit_code, config)\n\t\t\t\tconst stderrResult = offloadOutput(stderr, exit_code, config)\n\t\t\t\tresponse = {\n\t\t\t\t\tstdout: stdoutResult.result,\n\t\t\t\t\tstderr: stderrResult.result,\n\t\t\t\t\tmodified: stdoutResult.modified || stderrResult.modified\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tresponse = { error: 'Unknown request type' }\n\t\t\t}\n\n\t\t\tsocket.write(JSON.stringify(response))\n\t\t\tsocket.end()\n\t\t} catch(err) {\n\t\t\t// Check if it's a syntax error (invalid JSON) vs incomplete JSON\n\t\t\tif (err instanceof SyntaxError && !err.message.includes('end of JSON')) {\n\t\t\t\t// Invalid JSON structure - reset buffer and report error\n\t\t\t\tbuffer = ''\n\t\t\t\tsocket.write(JSON.stringify({ error: 'Invalid JSON' }))\n\t\t\t\tsocket.end()\n\t\t\t}\n\t\t\t// Otherwise incomplete JSON, wait for more data\n\t\t}\n\t})\n\n\tsocket.on('error', err => {\n\t\tconsole.error('Socket error:', err.message)\n\t})\n}\n\n/**\n * Cleanup socket file on exit\n */\nfunction cleanup(): void {\n\ttry {\n\t\tif (existsSync(socketPath)) {\n\t\t\tunlinkSync(socketPath)\n\t\t}\n\t} catch {\n\t\t// Ignore cleanup errors\n\t}\n\tprocess.exit(0)\n}\n\n/**\n * Start the daemon\n */\nfunction main(): void {\n\t// Remove stale socket file\n\tif (existsSync(socketPath)) {\n\t\tunlinkSync(socketPath)\n\t}\n\n\tconst server = createServer(handleConnection)\n\n\t// Handle shutdown signals\n\tprocess.on('SIGTERM', cleanup)\n\tprocess.on('SIGINT', cleanup)\n\tprocess.on('SIGHUP', cleanup)\n\n\t// Handle uncaught errors\n\tprocess.on('uncaughtException', err => {\n\t\tconsole.error('Uncaught exception:', err)\n\t\tcleanup()\n\t})\n\n\tserver.listen(socketPath, () => {\n\t\tconsole.error(`Guard daemon listening on ${ socketPath }`)\n\t})\n\n\tserver.on('error', err => {\n\t\tconsole.error('Server error:', err)\n\t\tcleanup()\n\t})\n}\n\nmain()\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;AASA,SAAS,YAAY,kBAAkB;AACvC,SAAS,oBAAiC;AAO1C,IAAM,SAAS,WAAwB,eAAe,cAAc;AACpE,IAAM,aAAa,OAAO;AAoB1B,SAAS,iBAAiB,QAAsB;AAC/C,MAAI,SAAS;AAEb,SAAO,GAAG,QAAQ,WAAS;AAC1B,cAAU,MAAM,SAAS;AAGzB,QAAI,OAAO,SAAS,KAAK,OAAO,MAAM;AACrC,aAAO,MAAM,KAAK,UAAU,EAAE,OAAO,oBAAoB,CAAC,CAAC;AAC3D,aAAO,IAAI;AACX;AAAA,IACD;AAGA,QAAI;AACH,YAAM,UAAyB,KAAK,MAAM,MAAM;AAChD,eAAS;AAET,UAAI;AAEJ,UAAI,QAAQ,SAAS,OAAO;AAC3B,cAAM,EAAE,QAAQ,IAAI,QAAQ;AAC5B,cAAM,SAAS,iBAAiB,OAAO;AACvC,mBAAW;AAAA,MACZ,WAAW,QAAQ,SAAS,QAAQ;AACnC,cAAM,EAAE,QAAQ,QAAQ,UAAU,IAAI,QAAQ;AAC9C,cAAM,eAAe,cAAc,QAAQ,WAAW,MAAM;AAC5D,cAAM,eAAe,cAAc,QAAQ,WAAW,MAAM;AAC5D,mBAAW;AAAA,UACV,QAAQ,aAAa;AAAA,UACrB,QAAQ,aAAa;AAAA,UACrB,UAAU,aAAa,YAAY,aAAa;AAAA,QACjD;AAAA,MACD,OAAO;AACN,mBAAW,EAAE,OAAO,uBAAuB;AAAA,MAC5C;AAEA,aAAO,MAAM,KAAK,UAAU,QAAQ,CAAC;AACrC,aAAO,IAAI;AAAA,IACZ,SAAQ,KAAK;AAEZ,UAAI,eAAe,eAAe,CAAC,IAAI,QAAQ,SAAS,aAAa,GAAG;AAEvE,iBAAS;AACT,eAAO,MAAM,KAAK,UAAU,EAAE,OAAO,eAAe,CAAC,CAAC;AACtD,eAAO,IAAI;AAAA,MACZ;AAAA,IAED;AAAA,EACD,CAAC;AAED,SAAO,GAAG,SAAS,SAAO;AACzB,YAAQ,MAAM,iBAAiB,IAAI,OAAO;AAAA,EAC3C,CAAC;AACF;AAKA,SAAS,UAAgB;AACxB,MAAI;AACH,QAAI,WAAW,UAAU,GAAG;AAC3B,iBAAW,UAAU;AAAA,IACtB;AAAA,EACD,QAAQ;AAAA,EAER;AACA,UAAQ,KAAK,CAAC;AACf;AAKA,SAAS,OAAa;AAErB,MAAI,WAAW,UAAU,GAAG;AAC3B,eAAW,UAAU;AAAA,EACtB;AAEA,QAAM,SAAS,aAAa,gBAAgB;AAG5C,UAAQ,GAAG,WAAW,OAAO;AAC7B,UAAQ,GAAG,UAAU,OAAO;AAC5B,UAAQ,GAAG,UAAU,OAAO;AAG5B,UAAQ,GAAG,qBAAqB,SAAO;AACtC,YAAQ,MAAM,uBAAuB,GAAG;AACxC,YAAQ;AAAA,EACT,CAAC;AAED,SAAO,OAAO,YAAY,MAAM;AAC/B,YAAQ,MAAM,6BAA8B,UAAW,EAAE;AAAA,EAC1D,CAAC;AAED,SAAO,GAAG,SAAS,SAAO;AACzB,YAAQ,MAAM,iBAAiB,GAAG;AAClC,YAAQ;AAAA,EACT,CAAC;AACF;AAEA,KAAK;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":";AACA;;;;;GAKG"}
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Guard daemon - persistent server for fast hook responses
|
|
4
|
+
*
|
|
5
|
+
* Keeps bash-parser and rules loaded in memory.
|
|
6
|
+
* Communicates via Unix socket for sub-5ms response times.
|
|
7
|
+
*/
|
|
8
|
+
import { loadConfig } from '@goobits/sherpa-core';
|
|
9
|
+
import { existsSync, unlinkSync } from 'fs';
|
|
10
|
+
import { createServer } from 'net';
|
|
11
|
+
import { offloadOutput } from './commands/post.js';
|
|
12
|
+
import { checkBashCommand } from './commands/pre.js';
|
|
13
|
+
import { DEFAULT_CONFIG } from './types.js';
|
|
14
|
+
// Load config
|
|
15
|
+
const config = loadConfig('config.json', DEFAULT_CONFIG);
|
|
16
|
+
const socketPath = config.socketPath;
|
|
17
|
+
/**
|
|
18
|
+
* Handle a client connection
|
|
19
|
+
*/
|
|
20
|
+
function handleConnection(socket) {
|
|
21
|
+
let buffer = '';
|
|
22
|
+
socket.on('data', chunk => {
|
|
23
|
+
buffer += chunk.toString();
|
|
24
|
+
// Limit buffer size to prevent DoS
|
|
25
|
+
if (buffer.length > 10 * 1024 * 1024) {
|
|
26
|
+
socket.write(JSON.stringify({ error: 'Request too large' }));
|
|
27
|
+
socket.end();
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Try to parse complete JSON
|
|
31
|
+
try {
|
|
32
|
+
const request = JSON.parse(buffer);
|
|
33
|
+
buffer = '';
|
|
34
|
+
let response;
|
|
35
|
+
if (request.type === 'pre') {
|
|
36
|
+
const { command } = request.data;
|
|
37
|
+
const result = checkBashCommand(command);
|
|
38
|
+
response = result;
|
|
39
|
+
}
|
|
40
|
+
else if (request.type === 'post') {
|
|
41
|
+
const { stdout, stderr, exit_code } = request.data;
|
|
42
|
+
const stdoutResult = offloadOutput(stdout, exit_code, config);
|
|
43
|
+
const stderrResult = offloadOutput(stderr, exit_code, config);
|
|
44
|
+
response = {
|
|
45
|
+
stdout: stdoutResult.result,
|
|
46
|
+
stderr: stderrResult.result,
|
|
47
|
+
modified: stdoutResult.modified || stderrResult.modified
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
response = { error: 'Unknown request type' };
|
|
52
|
+
}
|
|
53
|
+
socket.write(JSON.stringify(response));
|
|
54
|
+
socket.end();
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
// Check if it's a syntax error (invalid JSON) vs incomplete JSON
|
|
58
|
+
if (err instanceof SyntaxError && !err.message.includes('end of JSON')) {
|
|
59
|
+
// Invalid JSON structure - reset buffer and report error
|
|
60
|
+
buffer = '';
|
|
61
|
+
socket.write(JSON.stringify({ error: 'Invalid JSON' }));
|
|
62
|
+
socket.end();
|
|
63
|
+
}
|
|
64
|
+
// Otherwise incomplete JSON, wait for more data
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
socket.on('error', err => {
|
|
68
|
+
console.error('Socket error:', err.message);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Cleanup socket file on exit
|
|
73
|
+
*/
|
|
74
|
+
function cleanup() {
|
|
75
|
+
try {
|
|
76
|
+
if (existsSync(socketPath)) {
|
|
77
|
+
unlinkSync(socketPath);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Ignore cleanup errors
|
|
82
|
+
}
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Start the daemon
|
|
87
|
+
*/
|
|
88
|
+
function main() {
|
|
89
|
+
// Remove stale socket file
|
|
90
|
+
if (existsSync(socketPath)) {
|
|
91
|
+
unlinkSync(socketPath);
|
|
92
|
+
}
|
|
93
|
+
const server = createServer(handleConnection);
|
|
94
|
+
// Handle shutdown signals
|
|
95
|
+
process.on('SIGTERM', cleanup);
|
|
96
|
+
process.on('SIGINT', cleanup);
|
|
97
|
+
process.on('SIGHUP', cleanup);
|
|
98
|
+
// Handle uncaught errors
|
|
99
|
+
process.on('uncaughtException', err => {
|
|
100
|
+
console.error('Uncaught exception:', err);
|
|
101
|
+
cleanup();
|
|
102
|
+
});
|
|
103
|
+
server.listen(socketPath, () => {
|
|
104
|
+
console.error(`Guard daemon listening on ${socketPath}`);
|
|
105
|
+
});
|
|
106
|
+
server.on('error', err => {
|
|
107
|
+
console.error('Server error:', err);
|
|
108
|
+
cleanup();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
main();
|
|
112
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @goobits/sherpa - MCP hooks and repo setup for safer AI coding
|
|
3
|
+
*
|
|
4
|
+
* CLI commands:
|
|
5
|
+
* sherpa init - Set up repo (husky, lint-staged, gitleaks, claude hooks)
|
|
6
|
+
* sherpa pre - PreToolUse hook (blocks dangerous commands)
|
|
7
|
+
* sherpa post - PostToolUse hook (offloads large output)
|
|
8
|
+
*/
|
|
9
|
+
export { runInit } from './commands/init.js';
|
|
10
|
+
export { offloadOutput, runPost } from './commands/post.js';
|
|
11
|
+
export { checkBashCommand, runPre } from './commands/pre.js';
|
|
12
|
+
export * from './parser.js';
|
|
13
|
+
export * from './rules.js';
|
|
14
|
+
export * from './types.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC5C,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAC5D,cAAc,aAAa,CAAA;AAC3B,cAAc,YAAY,CAAA;AAC1B,cAAc,YAAY,CAAA"}
|