vibecheck-agent 1.0.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/dist/agent.d.ts +32 -0
- package/dist/agent.js +431 -0
- package/dist/agent.js.map +1 -0
- package/dist/claude.d.ts +52 -0
- package/dist/claude.js +246 -0
- package/dist/claude.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/images.d.ts +19 -0
- package/dist/images.js +101 -0
- package/dist/images.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +54 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +163 -0
- package/dist/protocol.js +3 -0
- package/dist/protocol.js.map +1 -0
- package/dist/scheduler.d.ts +33 -0
- package/dist/scheduler.js +132 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/screenshot.d.ts +28 -0
- package/dist/screenshot.js +356 -0
- package/dist/screenshot.js.map +1 -0
- package/dist/security.d.ts +25 -0
- package/dist/security.js +117 -0
- package/dist/security.js.map +1 -0
- package/dist/session.d.ts +4 -0
- package/dist/session.js +55 -0
- package/dist/session.js.map +1 -0
- package/dist/skills.d.ts +25 -0
- package/dist/skills.js +91 -0
- package/dist/skills.js.map +1 -0
- package/package.json +43 -0
- package/src/agent.ts +557 -0
- package/src/claude.ts +322 -0
- package/src/config.ts +61 -0
- package/src/images.ts +122 -0
- package/src/index.ts +73 -0
- package/src/protocol.ts +229 -0
- package/src/scheduler.ts +173 -0
- package/src/screenshot.ts +387 -0
- package/src/security.ts +158 -0
- package/src/session.ts +68 -0
- package/src/skills.ts +122 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { execSync, spawn } from "node:child_process";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import net from "node:net";
|
|
6
|
+
/**
|
|
7
|
+
* Detect project type from directory contents.
|
|
8
|
+
*/
|
|
9
|
+
export function detectProjectType(projectDir) {
|
|
10
|
+
// Check package.json for Node.js projects
|
|
11
|
+
const pkgPath = path.join(projectDir, "package.json");
|
|
12
|
+
if (existsSync(pkgPath)) {
|
|
13
|
+
try {
|
|
14
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
15
|
+
const deps = {
|
|
16
|
+
...pkg.dependencies,
|
|
17
|
+
...pkg.devDependencies,
|
|
18
|
+
};
|
|
19
|
+
if (deps["next"]) {
|
|
20
|
+
return {
|
|
21
|
+
type: "node",
|
|
22
|
+
framework: "Next.js",
|
|
23
|
+
cmd: "npx next dev",
|
|
24
|
+
port: 3000,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (deps["vite"]) {
|
|
28
|
+
return {
|
|
29
|
+
type: "node",
|
|
30
|
+
framework: "Vite",
|
|
31
|
+
cmd: "npx vite",
|
|
32
|
+
port: 5173,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (deps["react-scripts"]) {
|
|
36
|
+
return {
|
|
37
|
+
type: "node",
|
|
38
|
+
framework: "Create React App",
|
|
39
|
+
cmd: "npx react-scripts start",
|
|
40
|
+
port: 3000,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
if (deps["@vue/cli-service"]) {
|
|
44
|
+
return {
|
|
45
|
+
type: "node",
|
|
46
|
+
framework: "Vue CLI",
|
|
47
|
+
cmd: "npx vue-cli-service serve",
|
|
48
|
+
port: 8080,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// Generic Node.js with start script
|
|
52
|
+
if (pkg.scripts?.dev) {
|
|
53
|
+
return {
|
|
54
|
+
type: "node",
|
|
55
|
+
framework: "Node.js",
|
|
56
|
+
cmd: "npm run dev",
|
|
57
|
+
port: 3000,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
if (pkg.scripts?.start) {
|
|
61
|
+
return {
|
|
62
|
+
type: "node",
|
|
63
|
+
framework: "Node.js",
|
|
64
|
+
cmd: "npm start",
|
|
65
|
+
port: 3000,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// ignore parse errors
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Check for Python projects
|
|
74
|
+
const requirementsPath = path.join(projectDir, "requirements.txt");
|
|
75
|
+
const pyprojectPath = path.join(projectDir, "pyproject.toml");
|
|
76
|
+
if (existsSync(requirementsPath) || existsSync(pyprojectPath)) {
|
|
77
|
+
// Check for Streamlit
|
|
78
|
+
for (const name of [
|
|
79
|
+
"app.py",
|
|
80
|
+
"main.py",
|
|
81
|
+
"streamlit_app.py",
|
|
82
|
+
]) {
|
|
83
|
+
const fp = path.join(projectDir, name);
|
|
84
|
+
if (existsSync(fp)) {
|
|
85
|
+
try {
|
|
86
|
+
const content = readFileSync(fp, "utf-8");
|
|
87
|
+
if (content.includes("streamlit")) {
|
|
88
|
+
return {
|
|
89
|
+
type: "python",
|
|
90
|
+
framework: "Streamlit",
|
|
91
|
+
cmd: `streamlit run ${name}`,
|
|
92
|
+
entry: name,
|
|
93
|
+
port: 8501,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// ignore
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Check for FastAPI
|
|
103
|
+
const mainPy = path.join(projectDir, "main.py");
|
|
104
|
+
if (existsSync(mainPy)) {
|
|
105
|
+
try {
|
|
106
|
+
const content = readFileSync(mainPy, "utf-8");
|
|
107
|
+
if (content.includes("fastapi") || content.includes("FastAPI")) {
|
|
108
|
+
return {
|
|
109
|
+
type: "python",
|
|
110
|
+
framework: "FastAPI",
|
|
111
|
+
cmd: "uvicorn main:app --reload",
|
|
112
|
+
entry: "main.py",
|
|
113
|
+
port: 8000,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// ignore
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Check for Flask
|
|
122
|
+
const appPy = path.join(projectDir, "app.py");
|
|
123
|
+
if (existsSync(appPy)) {
|
|
124
|
+
try {
|
|
125
|
+
const content = readFileSync(appPy, "utf-8");
|
|
126
|
+
if (content.includes("flask") || content.includes("Flask")) {
|
|
127
|
+
return {
|
|
128
|
+
type: "python",
|
|
129
|
+
framework: "Flask",
|
|
130
|
+
cmd: "flask run",
|
|
131
|
+
entry: "app.py",
|
|
132
|
+
port: 5000,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// ignore
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Check for static HTML
|
|
142
|
+
const indexHtml = path.join(projectDir, "index.html");
|
|
143
|
+
if (existsSync(indexHtml)) {
|
|
144
|
+
return {
|
|
145
|
+
type: "static",
|
|
146
|
+
framework: null,
|
|
147
|
+
entry: "index.html",
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
// Look for any HTML file
|
|
151
|
+
try {
|
|
152
|
+
const files = readdirSync(projectDir);
|
|
153
|
+
const htmlFile = files.find((f) => f.endsWith(".html"));
|
|
154
|
+
if (htmlFile) {
|
|
155
|
+
return {
|
|
156
|
+
type: "static",
|
|
157
|
+
framework: null,
|
|
158
|
+
entry: htmlFile,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// ignore
|
|
164
|
+
}
|
|
165
|
+
return { type: "unknown", framework: null };
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Wait for a port to become available.
|
|
169
|
+
*/
|
|
170
|
+
function waitForPort(port, timeoutMs) {
|
|
171
|
+
return new Promise((resolve) => {
|
|
172
|
+
const start = Date.now();
|
|
173
|
+
const check = () => {
|
|
174
|
+
const socket = new net.Socket();
|
|
175
|
+
socket.setTimeout(500);
|
|
176
|
+
socket.once("connect", () => {
|
|
177
|
+
socket.destroy();
|
|
178
|
+
resolve(true);
|
|
179
|
+
});
|
|
180
|
+
socket.once("error", () => {
|
|
181
|
+
socket.destroy();
|
|
182
|
+
if (Date.now() - start > timeoutMs) {
|
|
183
|
+
resolve(false);
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
setTimeout(check, 500);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
socket.once("timeout", () => {
|
|
190
|
+
socket.destroy();
|
|
191
|
+
if (Date.now() - start > timeoutMs) {
|
|
192
|
+
resolve(false);
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
setTimeout(check, 500);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
socket.connect(port, "127.0.0.1");
|
|
199
|
+
};
|
|
200
|
+
check();
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Take a screenshot of an HTML file using Playwright.
|
|
205
|
+
*/
|
|
206
|
+
export async function htmlFileToScreenshot(htmlPath, outputPath) {
|
|
207
|
+
try {
|
|
208
|
+
const { chromium } = await import("playwright");
|
|
209
|
+
const browser = await chromium.launch({ headless: true });
|
|
210
|
+
const page = await browser.newPage({
|
|
211
|
+
viewport: { width: 1200, height: 800 },
|
|
212
|
+
});
|
|
213
|
+
await page.goto(`file://${path.resolve(htmlPath)}`);
|
|
214
|
+
await page.waitForTimeout(500);
|
|
215
|
+
await page.screenshot({ path: outputPath, fullPage: true });
|
|
216
|
+
await browser.close();
|
|
217
|
+
return outputPath;
|
|
218
|
+
}
|
|
219
|
+
catch (e) {
|
|
220
|
+
console.error("[screenshot] HTML screenshot failed:", e);
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Take a screenshot of a project by starting its dev server.
|
|
226
|
+
*/
|
|
227
|
+
export async function screenshotProject(projectDir, outputPath) {
|
|
228
|
+
const info = detectProjectType(projectDir);
|
|
229
|
+
// Static HTML: screenshot directly
|
|
230
|
+
if (info.type === "static" && info.entry) {
|
|
231
|
+
const htmlPath = path.join(projectDir, info.entry);
|
|
232
|
+
return htmlFileToScreenshot(htmlPath, outputPath);
|
|
233
|
+
}
|
|
234
|
+
// Dev server projects: start server, screenshot, kill
|
|
235
|
+
if (!info.cmd || !info.port) {
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
let serverProcess = null;
|
|
239
|
+
try {
|
|
240
|
+
// Ensure npm dependencies are installed for Node.js projects
|
|
241
|
+
if (info.type === "node" &&
|
|
242
|
+
!existsSync(path.join(projectDir, "node_modules"))) {
|
|
243
|
+
console.log("[screenshot] Running npm install...");
|
|
244
|
+
execSync("npm install", {
|
|
245
|
+
cwd: projectDir,
|
|
246
|
+
timeout: 60_000,
|
|
247
|
+
stdio: "ignore",
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
// Start dev server
|
|
251
|
+
const [cmd, ...args] = info.cmd.split(" ");
|
|
252
|
+
serverProcess = spawn(cmd, args, {
|
|
253
|
+
cwd: projectDir,
|
|
254
|
+
stdio: "ignore",
|
|
255
|
+
detached: true,
|
|
256
|
+
env: { ...process.env, PORT: String(info.port), BROWSER: "none" },
|
|
257
|
+
});
|
|
258
|
+
// Wait for port to open
|
|
259
|
+
const portReady = await waitForPort(info.port, 30_000);
|
|
260
|
+
if (!portReady) {
|
|
261
|
+
console.warn("[screenshot] Server start timeout (30s)");
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
// Take screenshot
|
|
265
|
+
try {
|
|
266
|
+
const { chromium } = await import("playwright");
|
|
267
|
+
const browser = await chromium.launch({ headless: true });
|
|
268
|
+
const page = await browser.newPage({
|
|
269
|
+
viewport: { width: 1200, height: 800 },
|
|
270
|
+
});
|
|
271
|
+
await page.goto(`http://localhost:${info.port}`, {
|
|
272
|
+
waitUntil: "networkidle",
|
|
273
|
+
timeout: 15_000,
|
|
274
|
+
});
|
|
275
|
+
await page.screenshot({ path: outputPath, fullPage: true });
|
|
276
|
+
await browser.close();
|
|
277
|
+
return outputPath;
|
|
278
|
+
}
|
|
279
|
+
catch (e) {
|
|
280
|
+
console.error("[screenshot] Screenshot capture failed:", e);
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch (e) {
|
|
285
|
+
console.error("[screenshot] Project screenshot failed:", e);
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
finally {
|
|
289
|
+
// Kill dev server process group
|
|
290
|
+
if (serverProcess && serverProcess.pid) {
|
|
291
|
+
try {
|
|
292
|
+
process.kill(-serverProcess.pid, "SIGTERM");
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
try {
|
|
296
|
+
serverProcess.kill("SIGKILL");
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
// ignore
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Find the project directory from a message or response text.
|
|
307
|
+
* Looks for paths that contain typical project indicators.
|
|
308
|
+
*/
|
|
309
|
+
export function findProjectDir(text, workDir) {
|
|
310
|
+
// Check if workDir itself is a project
|
|
311
|
+
const info = detectProjectType(workDir);
|
|
312
|
+
if (info.type !== "unknown") {
|
|
313
|
+
return workDir;
|
|
314
|
+
}
|
|
315
|
+
// Look for directory paths in the text
|
|
316
|
+
const dirRe = /(\/[^\s:*?"<>|]+)/g;
|
|
317
|
+
let match;
|
|
318
|
+
while ((match = dirRe.exec(text)) !== null) {
|
|
319
|
+
const dir = match[1];
|
|
320
|
+
try {
|
|
321
|
+
const projInfo = detectProjectType(dir);
|
|
322
|
+
if (projInfo.type !== "unknown") {
|
|
323
|
+
return dir;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
catch {
|
|
327
|
+
// not a valid directory
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
// Check subdirectories of workDir (1 level deep)
|
|
331
|
+
try {
|
|
332
|
+
const entries = readdirSync(workDir, { withFileTypes: true });
|
|
333
|
+
for (const entry of entries) {
|
|
334
|
+
if (entry.isDirectory() &&
|
|
335
|
+
!entry.name.startsWith(".") &&
|
|
336
|
+
entry.name !== "node_modules") {
|
|
337
|
+
const subDir = path.join(workDir, entry.name);
|
|
338
|
+
const subInfo = detectProjectType(subDir);
|
|
339
|
+
if (subInfo.type !== "unknown") {
|
|
340
|
+
return subDir;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
// ignore
|
|
347
|
+
}
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Get screenshot output path.
|
|
352
|
+
*/
|
|
353
|
+
export function getScreenshotPath() {
|
|
354
|
+
return path.join(os.tmpdir(), "vibecheck_screenshot.png");
|
|
355
|
+
}
|
|
356
|
+
//# sourceMappingURL=screenshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../src/screenshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PermissionResult } from "@anthropic-ai/claude-agent-sdk";
|
|
2
|
+
export declare class SecurityManager {
|
|
3
|
+
private trustedPaths;
|
|
4
|
+
private pendingApproval;
|
|
5
|
+
/** Callback to send approval_required to WebSocket */
|
|
6
|
+
onApprovalNeeded?: (paths: string[], toolName: string, input: Record<string, unknown>) => void;
|
|
7
|
+
constructor(workDir: string);
|
|
8
|
+
addTrustedPath(p: string): void;
|
|
9
|
+
isPathTrusted(p: string): boolean;
|
|
10
|
+
isSafeCommand(command: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* SDK canUseTool callback.
|
|
13
|
+
* Matches the exact CanUseTool signature from @anthropic-ai/claude-agent-sdk.
|
|
14
|
+
*/
|
|
15
|
+
canUseTool: (toolName: string, input: Record<string, unknown>, options: {
|
|
16
|
+
signal: AbortSignal;
|
|
17
|
+
}) => Promise<PermissionResult>;
|
|
18
|
+
/**
|
|
19
|
+
* Called by agent when approval/denial arrives from server.
|
|
20
|
+
*/
|
|
21
|
+
resolveApproval(approved: boolean, permanent: boolean): void;
|
|
22
|
+
private extractPathsFromToolInput;
|
|
23
|
+
}
|
|
24
|
+
/** Extract absolute paths from text (for Bash commands, user messages, etc.) */
|
|
25
|
+
export declare function extractPathsFromText(text: string): string[];
|
package/dist/security.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { SAFE_SYSTEM_COMMANDS } from "./config.js";
|
|
3
|
+
// Regex patterns to extract paths from text
|
|
4
|
+
const ABSOLUTE_PATH_RE = /(?:^|\s)(\/[^\s:*?"<>|]+)/g;
|
|
5
|
+
const RELATIVE_PATH_RE = /(?:^|\s)(\.\.?\/[^\s:*?"<>|]+)/g;
|
|
6
|
+
export class SecurityManager {
|
|
7
|
+
trustedPaths;
|
|
8
|
+
pendingApproval = null;
|
|
9
|
+
/** Callback to send approval_required to WebSocket */
|
|
10
|
+
onApprovalNeeded;
|
|
11
|
+
constructor(workDir) {
|
|
12
|
+
this.trustedPaths = new Set([path.resolve(workDir)]);
|
|
13
|
+
}
|
|
14
|
+
addTrustedPath(p) {
|
|
15
|
+
const normalized = path.resolve(p);
|
|
16
|
+
this.trustedPaths.add(normalized);
|
|
17
|
+
console.log(`[security] Trusted path added: ${normalized}`);
|
|
18
|
+
}
|
|
19
|
+
isPathTrusted(p) {
|
|
20
|
+
const normalized = path.resolve(p);
|
|
21
|
+
for (const trusted of this.trustedPaths) {
|
|
22
|
+
if (normalized === trusted ||
|
|
23
|
+
normalized.startsWith(trusted + path.sep)) {
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
isSafeCommand(command) {
|
|
30
|
+
const trimmed = command.trim();
|
|
31
|
+
for (const safe of SAFE_SYSTEM_COMMANDS) {
|
|
32
|
+
if (trimmed === safe || trimmed.startsWith(safe + " ")) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* SDK canUseTool callback.
|
|
40
|
+
* Matches the exact CanUseTool signature from @anthropic-ai/claude-agent-sdk.
|
|
41
|
+
*/
|
|
42
|
+
canUseTool = async (toolName, input, options) => {
|
|
43
|
+
const paths = this.extractPathsFromToolInput(toolName, input);
|
|
44
|
+
const untrusted = paths.filter((p) => !this.isPathTrusted(p));
|
|
45
|
+
// Trusted paths or safe commands → allow immediately
|
|
46
|
+
if (untrusted.length === 0) {
|
|
47
|
+
return { behavior: "allow", updatedInput: input };
|
|
48
|
+
}
|
|
49
|
+
if (toolName === "Bash" &&
|
|
50
|
+
typeof input.command === "string" &&
|
|
51
|
+
this.isSafeCommand(input.command)) {
|
|
52
|
+
return { behavior: "allow", updatedInput: input };
|
|
53
|
+
}
|
|
54
|
+
// Request approval from web UI via WebSocket
|
|
55
|
+
this.onApprovalNeeded?.(untrusted, toolName, input);
|
|
56
|
+
// Wait for user response (Promise resolved by resolveApproval)
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
this.pendingApproval = { resolve, toolName, input };
|
|
59
|
+
// Handle abort signal
|
|
60
|
+
options.signal.addEventListener("abort", () => {
|
|
61
|
+
this.pendingApproval = null;
|
|
62
|
+
resolve({ behavior: "deny", message: "Operation aborted" });
|
|
63
|
+
}, { once: true });
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Called by agent when approval/denial arrives from server.
|
|
68
|
+
*/
|
|
69
|
+
resolveApproval(approved, permanent) {
|
|
70
|
+
if (!this.pendingApproval)
|
|
71
|
+
return;
|
|
72
|
+
const { resolve, toolName, input } = this.pendingApproval;
|
|
73
|
+
if (approved) {
|
|
74
|
+
if (permanent) {
|
|
75
|
+
const paths = this.extractPathsFromToolInput(toolName, input);
|
|
76
|
+
paths.forEach((p) => this.addTrustedPath(p));
|
|
77
|
+
}
|
|
78
|
+
resolve({ behavior: "allow", updatedInput: input });
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
resolve({ behavior: "deny", message: "User denied the request" });
|
|
82
|
+
}
|
|
83
|
+
this.pendingApproval = null;
|
|
84
|
+
}
|
|
85
|
+
extractPathsFromToolInput(toolName, input) {
|
|
86
|
+
switch (toolName) {
|
|
87
|
+
case "Read":
|
|
88
|
+
case "Write":
|
|
89
|
+
case "Edit":
|
|
90
|
+
return typeof input.file_path === "string" ? [input.file_path] : [];
|
|
91
|
+
case "Bash":
|
|
92
|
+
return typeof input.command === "string"
|
|
93
|
+
? extractPathsFromText(input.command)
|
|
94
|
+
: [];
|
|
95
|
+
case "Glob":
|
|
96
|
+
case "Grep":
|
|
97
|
+
return typeof input.path === "string" ? [input.path] : [];
|
|
98
|
+
default:
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** Extract absolute paths from text (for Bash commands, user messages, etc.) */
|
|
104
|
+
export function extractPathsFromText(text) {
|
|
105
|
+
const paths = [];
|
|
106
|
+
let match;
|
|
107
|
+
ABSOLUTE_PATH_RE.lastIndex = 0;
|
|
108
|
+
while ((match = ABSOLUTE_PATH_RE.exec(text)) !== null) {
|
|
109
|
+
paths.push(match[1]);
|
|
110
|
+
}
|
|
111
|
+
RELATIVE_PATH_RE.lastIndex = 0;
|
|
112
|
+
while ((match = RELATIVE_PATH_RE.exec(text)) !== null) {
|
|
113
|
+
paths.push(match[1]);
|
|
114
|
+
}
|
|
115
|
+
return paths;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security.js","sourceRoot":"","sources":["../src/security.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function getSessionFilePath(workDir: string): string;
|
|
2
|
+
export declare function saveSessionId(workDir: string, sessionId: string): void;
|
|
3
|
+
export declare function loadSessionId(workDir: string): string | null;
|
|
4
|
+
export declare function clearSessionId(workDir: string): void;
|
package/dist/session.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync, } from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { SESSION_DIR } from "./config.js";
|
|
5
|
+
export function getSessionFilePath(workDir) {
|
|
6
|
+
const hash = createHash("md5").update(workDir).digest("hex").slice(0, 12);
|
|
7
|
+
return path.join(SESSION_DIR, `session_${hash}.json`);
|
|
8
|
+
}
|
|
9
|
+
export function saveSessionId(workDir, sessionId) {
|
|
10
|
+
try {
|
|
11
|
+
if (!existsSync(SESSION_DIR)) {
|
|
12
|
+
mkdirSync(SESSION_DIR, { recursive: true });
|
|
13
|
+
}
|
|
14
|
+
const data = {
|
|
15
|
+
work_dir: workDir,
|
|
16
|
+
session_id: sessionId,
|
|
17
|
+
updated_at: new Date().toISOString(),
|
|
18
|
+
};
|
|
19
|
+
writeFileSync(getSessionFilePath(workDir), JSON.stringify(data, null, 2));
|
|
20
|
+
console.log(`[session] Session ID saved: ${sessionId.slice(0, 20)}...`);
|
|
21
|
+
}
|
|
22
|
+
catch (e) {
|
|
23
|
+
console.error(`[session] Failed to save session ID:`, e);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export function loadSessionId(workDir) {
|
|
27
|
+
try {
|
|
28
|
+
const filePath = getSessionFilePath(workDir);
|
|
29
|
+
if (!existsSync(filePath))
|
|
30
|
+
return null;
|
|
31
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
32
|
+
const data = JSON.parse(raw);
|
|
33
|
+
if (data.session_id && path.resolve(data.work_dir) === path.resolve(workDir)) {
|
|
34
|
+
console.log(`[session] Session ID loaded: ${data.session_id.slice(0, 20)}...`);
|
|
35
|
+
return data.session_id;
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function clearSessionId(workDir) {
|
|
44
|
+
try {
|
|
45
|
+
const filePath = getSessionFilePath(workDir);
|
|
46
|
+
if (existsSync(filePath)) {
|
|
47
|
+
unlinkSync(filePath);
|
|
48
|
+
console.log("[session] Session ID cleared");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// ignore
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EACL,UAAU,EACV,SAAS,EACT,YAAY,EACZ,aAAa,EACb,UAAU,GACX,MAAM,SAAS,CAAC;AACjB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAQ1C,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,IAAI,OAAO,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe,EAAE,SAAiB;IAC9D,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YAC7B,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9C,CAAC;QACD,MAAM,IAAI,GAAgB;YACxB,QAAQ,EAAE,OAAO;YACjB,UAAU,EAAE,SAAS;YACrB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACrC,CAAC;QACF,aAAa,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,+BAA+B,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC;IAC1E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,sCAAsC,EAAE,CAAC,CAAC,CAAC;IAC3D,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAgB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC1C,IAAI,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7E,OAAO,CAAC,GAAG,CACT,gCAAgC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAClE,CAAC;YACF,OAAO,IAAI,CAAC,UAAU,CAAC;QACzB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,UAAU,CAAC,QAAQ,CAAC,CAAC;YACrB,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;QAC9C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;AACH,CAAC"}
|
package/dist/skills.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skills System
|
|
3
|
+
*
|
|
4
|
+
* A skill is a named preset that combines:
|
|
5
|
+
* - systemPrompt override → specialises Claude's behaviour
|
|
6
|
+
* - allowedTools override → restricts/expands what Claude may do
|
|
7
|
+
*
|
|
8
|
+
* Agent types (read-only research vs. full coding) are expressed as skills.
|
|
9
|
+
*/
|
|
10
|
+
export interface Skill {
|
|
11
|
+
id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
icon: string;
|
|
14
|
+
description: string;
|
|
15
|
+
/** Prepended / appended to Claude's default system prompt */
|
|
16
|
+
systemPrompt?: string;
|
|
17
|
+
/**
|
|
18
|
+
* If set, only these tools are available for this skill.
|
|
19
|
+
* Leave undefined to inherit the global allowedTools list.
|
|
20
|
+
*/
|
|
21
|
+
allowedTools?: string[];
|
|
22
|
+
}
|
|
23
|
+
export declare const DEFAULT_SKILLS: Skill[];
|
|
24
|
+
export declare function getSkill(id: string): Skill | undefined;
|
|
25
|
+
export declare function getAllSkills(): Skill[];
|