@serjm/deepseek-code 0.3.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/CONTRIBUTING.md +73 -0
- package/README.md +194 -0
- package/README.ru.md +194 -0
- package/dist/api/index.d.ts +77 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +263 -0
- package/dist/api/index.js.map +1 -0
- package/dist/cli/headless.d.ts +22 -0
- package/dist/cli/headless.d.ts.map +1 -0
- package/dist/cli/headless.js +122 -0
- package/dist/cli/headless.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +90 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/interactive.d.ts +18 -0
- package/dist/cli/interactive.d.ts.map +1 -0
- package/dist/cli/interactive.js +75 -0
- package/dist/cli/interactive.js.map +1 -0
- package/dist/commands/index.d.ts +30 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +964 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/config/defaults.d.ts +37 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +39 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/loader.d.ts +4 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +76 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/core/agent-loop.d.ts +111 -0
- package/dist/core/agent-loop.d.ts.map +1 -0
- package/dist/core/agent-loop.js +485 -0
- package/dist/core/agent-loop.js.map +1 -0
- package/dist/core/checkpoint.d.ts +10 -0
- package/dist/core/checkpoint.d.ts.map +1 -0
- package/dist/core/checkpoint.js +83 -0
- package/dist/core/checkpoint.js.map +1 -0
- package/dist/core/extensions.d.ts +55 -0
- package/dist/core/extensions.d.ts.map +1 -0
- package/dist/core/extensions.js +113 -0
- package/dist/core/extensions.js.map +1 -0
- package/dist/core/git.d.ts +68 -0
- package/dist/core/git.d.ts.map +1 -0
- package/dist/core/git.js +148 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/hooks.d.ts +37 -0
- package/dist/core/hooks.d.ts.map +1 -0
- package/dist/core/hooks.js +77 -0
- package/dist/core/hooks.js.map +1 -0
- package/dist/core/i18n.d.ts +90 -0
- package/dist/core/i18n.d.ts.map +1 -0
- package/dist/core/i18n.js +253 -0
- package/dist/core/i18n.js.map +1 -0
- package/dist/core/lsp.d.ts +74 -0
- package/dist/core/lsp.d.ts.map +1 -0
- package/dist/core/lsp.js +239 -0
- package/dist/core/lsp.js.map +1 -0
- package/dist/core/mcp.d.ts +49 -0
- package/dist/core/mcp.d.ts.map +1 -0
- package/dist/core/mcp.js +195 -0
- package/dist/core/mcp.js.map +1 -0
- package/dist/core/memory.d.ts +38 -0
- package/dist/core/memory.d.ts.map +1 -0
- package/dist/core/memory.js +231 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/metrics.d.ts +36 -0
- package/dist/core/metrics.d.ts.map +1 -0
- package/dist/core/metrics.js +111 -0
- package/dist/core/metrics.js.map +1 -0
- package/dist/core/review.d.ts +27 -0
- package/dist/core/review.d.ts.map +1 -0
- package/dist/core/review.js +201 -0
- package/dist/core/review.js.map +1 -0
- package/dist/core/sandbox.d.ts +52 -0
- package/dist/core/sandbox.d.ts.map +1 -0
- package/dist/core/sandbox.js +140 -0
- package/dist/core/sandbox.js.map +1 -0
- package/dist/core/scheduler.d.ts +56 -0
- package/dist/core/scheduler.d.ts.map +1 -0
- package/dist/core/scheduler.js +167 -0
- package/dist/core/scheduler.js.map +1 -0
- package/dist/core/session.d.ts +49 -0
- package/dist/core/session.d.ts.map +1 -0
- package/dist/core/session.js +127 -0
- package/dist/core/session.js.map +1 -0
- package/dist/core/skills.d.ts +36 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +90 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/subagent.d.ts +45 -0
- package/dist/core/subagent.d.ts.map +1 -0
- package/dist/core/subagent.js +130 -0
- package/dist/core/subagent.js.map +1 -0
- package/dist/core/themes.d.ts +35 -0
- package/dist/core/themes.d.ts.map +1 -0
- package/dist/core/themes.js +188 -0
- package/dist/core/themes.js.map +1 -0
- package/dist/tools/bash.d.ts +3 -0
- package/dist/tools/bash.d.ts.map +1 -0
- package/dist/tools/bash.js +92 -0
- package/dist/tools/bash.js.map +1 -0
- package/dist/tools/chrome-manager.d.ts +35 -0
- package/dist/tools/chrome-manager.d.ts.map +1 -0
- package/dist/tools/chrome-manager.js +163 -0
- package/dist/tools/chrome-manager.js.map +1 -0
- package/dist/tools/chrome.d.ts +78 -0
- package/dist/tools/chrome.d.ts.map +1 -0
- package/dist/tools/chrome.js +1058 -0
- package/dist/tools/chrome.js.map +1 -0
- package/dist/tools/edit.d.ts +3 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +81 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/glob.d.ts +3 -0
- package/dist/tools/glob.d.ts.map +1 -0
- package/dist/tools/glob.js +41 -0
- package/dist/tools/glob.js.map +1 -0
- package/dist/tools/grep.d.ts +3 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +74 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/path-safety.d.ts +3 -0
- package/dist/tools/path-safety.d.ts.map +1 -0
- package/dist/tools/path-safety.js +19 -0
- package/dist/tools/path-safety.js.map +1 -0
- package/dist/tools/read.d.ts +3 -0
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +58 -0
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/registry.d.ts +4 -0
- package/dist/tools/registry.d.ts.map +1 -0
- package/dist/tools/registry.js +43 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/types.d.ts +47 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +90 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/tools/write.d.ts +3 -0
- package/dist/tools/write.d.ts.map +1 -0
- package/dist/tools/write.js +51 -0
- package/dist/tools/write.js.map +1 -0
- package/dist/ui/activity-cards.d.ts +50 -0
- package/dist/ui/activity-cards.d.ts.map +1 -0
- package/dist/ui/activity-cards.js +185 -0
- package/dist/ui/activity-cards.js.map +1 -0
- package/dist/ui/app.d.ts +9 -0
- package/dist/ui/app.d.ts.map +1 -0
- package/dist/ui/app.js +852 -0
- package/dist/ui/app.js.map +1 -0
- package/dist/ui/chat-view.d.ts +10 -0
- package/dist/ui/chat-view.d.ts.map +1 -0
- package/dist/ui/chat-view.js +94 -0
- package/dist/ui/chat-view.js.map +1 -0
- package/dist/ui/error-boundary.d.ts +13 -0
- package/dist/ui/error-boundary.d.ts.map +1 -0
- package/dist/ui/error-boundary.js +16 -0
- package/dist/ui/error-boundary.js.map +1 -0
- package/dist/ui/fade-in.d.ts +8 -0
- package/dist/ui/fade-in.d.ts.map +1 -0
- package/dist/ui/fade-in.js +14 -0
- package/dist/ui/fade-in.js.map +1 -0
- package/dist/ui/input-bar.d.ts +16 -0
- package/dist/ui/input-bar.d.ts.map +1 -0
- package/dist/ui/input-bar.js +269 -0
- package/dist/ui/input-bar.js.map +1 -0
- package/dist/ui/markdown-view.d.ts +9 -0
- package/dist/ui/markdown-view.d.ts.map +1 -0
- package/dist/ui/markdown-view.js +240 -0
- package/dist/ui/markdown-view.js.map +1 -0
- package/dist/ui/matrix-rain.d.ts +2 -0
- package/dist/ui/matrix-rain.d.ts.map +1 -0
- package/dist/ui/matrix-rain.js +134 -0
- package/dist/ui/matrix-rain.js.map +1 -0
- package/dist/ui/reasoning-view.d.ts +12 -0
- package/dist/ui/reasoning-view.d.ts.map +1 -0
- package/dist/ui/reasoning-view.js +34 -0
- package/dist/ui/reasoning-view.js.map +1 -0
- package/dist/ui/results-panel.d.ts +11 -0
- package/dist/ui/results-panel.d.ts.map +1 -0
- package/dist/ui/results-panel.js +17 -0
- package/dist/ui/results-panel.js.map +1 -0
- package/dist/ui/setup-wizard.d.ts +30 -0
- package/dist/ui/setup-wizard.d.ts.map +1 -0
- package/dist/ui/setup-wizard.js +166 -0
- package/dist/ui/setup-wizard.js.map +1 -0
- package/dist/ui/status-bar.d.ts +14 -0
- package/dist/ui/status-bar.d.ts.map +1 -0
- package/dist/ui/status-bar.js +63 -0
- package/dist/ui/status-bar.js.map +1 -0
- package/dist/ui/tool-activity-card.d.ts +9 -0
- package/dist/ui/tool-activity-card.d.ts.map +1 -0
- package/dist/ui/tool-activity-card.js +172 -0
- package/dist/ui/tool-activity-card.js.map +1 -0
- package/dist/ui/tool-call-view.d.ts +9 -0
- package/dist/ui/tool-call-view.d.ts.map +1 -0
- package/dist/ui/tool-call-view.js +149 -0
- package/dist/ui/tool-call-view.js.map +1 -0
- package/dist/utils/clipboard.d.ts +6 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.js +56 -0
- package/dist/utils/clipboard.js.map +1 -0
- package/dist/utils/ignore.d.ts +6 -0
- package/dist/utils/ignore.d.ts.map +1 -0
- package/dist/utils/ignore.js +40 -0
- package/dist/utils/ignore.js.map +1 -0
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +13 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/string-width.d.ts +6 -0
- package/dist/utils/string-width.d.ts.map +1 -0
- package/dist/utils/string-width.js +37 -0
- package/dist/utils/string-width.js.map +1 -0
- package/package.json +68 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { DeepSeekConfig } from '../config/defaults.js';
|
|
2
|
+
export interface ReviewOptions {
|
|
3
|
+
files?: string[];
|
|
4
|
+
gitRef?: string;
|
|
5
|
+
runLinters?: boolean;
|
|
6
|
+
autoFix?: boolean;
|
|
7
|
+
prNumber?: number;
|
|
8
|
+
prUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ReviewIssue {
|
|
11
|
+
file: string;
|
|
12
|
+
line: number;
|
|
13
|
+
severity: 'critical' | 'major' | 'minor' | 'info';
|
|
14
|
+
category: 'correctness' | 'security' | 'quality' | 'performance';
|
|
15
|
+
message: string;
|
|
16
|
+
suggestion?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface ReviewResult {
|
|
19
|
+
issues: ReviewIssue[];
|
|
20
|
+
summary: string;
|
|
21
|
+
score: number;
|
|
22
|
+
linterOutput?: string;
|
|
23
|
+
durationMs: number;
|
|
24
|
+
}
|
|
25
|
+
export declare function reviewCode(config: DeepSeekConfig, options: ReviewOptions): Promise<ReviewResult>;
|
|
26
|
+
export declare function formatReviewReport(result: ReviewResult): string;
|
|
27
|
+
//# sourceMappingURL=review.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.d.ts","sourceRoot":"","sources":["../../src/core/review.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAE3D,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;IAClD,QAAQ,EAAE,aAAa,GAAG,UAAU,GAAG,SAAS,GAAG,aAAa,CAAC;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAcD,wBAAsB,UAAU,CAC9B,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,aAAa,GACrB,OAAO,CAAC,YAAY,CAAC,CA2FvB;AAED,wBAAgB,kBAAkB,CAAE,MAAM,EAAE,YAAY,GAAG,MAAM,CAWhE"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { DeepSeekAPI } from '../api/index.js';
|
|
5
|
+
const LINTER_COMMANDS = {
|
|
6
|
+
ts: 'npx tsc --noEmit 2>&1 || true',
|
|
7
|
+
eslint: 'npx eslint . --format compact 2>&1 || true',
|
|
8
|
+
};
|
|
9
|
+
const severityWeight = {
|
|
10
|
+
critical: 0,
|
|
11
|
+
major: 1,
|
|
12
|
+
minor: 2,
|
|
13
|
+
info: 3,
|
|
14
|
+
};
|
|
15
|
+
export async function reviewCode(config, options) {
|
|
16
|
+
const startTime = Date.now();
|
|
17
|
+
const api = new DeepSeekAPI(config);
|
|
18
|
+
let linterOutput = '';
|
|
19
|
+
const filesToReview = await determineScope(options);
|
|
20
|
+
if (filesToReview.length === 0) {
|
|
21
|
+
return {
|
|
22
|
+
issues: [],
|
|
23
|
+
summary: 'No files to review.',
|
|
24
|
+
score: 100,
|
|
25
|
+
durationMs: Date.now() - startTime,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (options.runLinters !== false) {
|
|
29
|
+
linterOutput = await runLinters(filesToReview);
|
|
30
|
+
}
|
|
31
|
+
const fileContents = [];
|
|
32
|
+
for (const file of filesToReview.slice(0, 10)) {
|
|
33
|
+
try {
|
|
34
|
+
const content = await readFile(file, 'utf-8');
|
|
35
|
+
fileContents.push(`=== ${file} ===\n${content.slice(0, 5000)}`);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Skip unreadable files.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const reviewPrompt = `Review the following code for issues. Focus on:
|
|
42
|
+
1. Correctness
|
|
43
|
+
2. Security
|
|
44
|
+
3. Quality
|
|
45
|
+
4. Performance
|
|
46
|
+
|
|
47
|
+
Return strict JSON only:
|
|
48
|
+
{
|
|
49
|
+
"issues": [
|
|
50
|
+
{
|
|
51
|
+
"file": "path",
|
|
52
|
+
"line": 1,
|
|
53
|
+
"severity": "critical|major|minor|info",
|
|
54
|
+
"category": "correctness|security|quality|performance",
|
|
55
|
+
"message": "short finding",
|
|
56
|
+
"suggestion": "optional fix"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"summary": "overall assessment",
|
|
60
|
+
"score": 0
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Files:
|
|
64
|
+
${fileContents.join('\n\n')}
|
|
65
|
+
|
|
66
|
+
${linterOutput ? `\nLinter output:\n${linterOutput}` : ''}`;
|
|
67
|
+
let issues = [];
|
|
68
|
+
let summary = `Reviewed ${filesToReview.length} file(s).`;
|
|
69
|
+
let score = 100;
|
|
70
|
+
try {
|
|
71
|
+
const response = await api.chat([
|
|
72
|
+
{ role: 'system', content: 'You are a code review expert. Return strict JSON only.' },
|
|
73
|
+
{ role: 'user', content: reviewPrompt },
|
|
74
|
+
]);
|
|
75
|
+
const parsed = parseReviewResponse(response.content);
|
|
76
|
+
if (parsed) {
|
|
77
|
+
issues = normalizeIssues(parsed.issues ?? []);
|
|
78
|
+
summary = parsed.summary ?? summary;
|
|
79
|
+
if (typeof parsed.score === 'number') {
|
|
80
|
+
score = clampScore(parsed.score);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Keep deterministic fallback summary below.
|
|
86
|
+
}
|
|
87
|
+
if (issues.length === 0) {
|
|
88
|
+
score = Math.max(score, linterOutput.trim() ? 90 : 100);
|
|
89
|
+
}
|
|
90
|
+
else if (score === 100) {
|
|
91
|
+
score = Math.max(0, 100 - issues.length * 5);
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
issues,
|
|
95
|
+
summary,
|
|
96
|
+
score,
|
|
97
|
+
linterOutput,
|
|
98
|
+
durationMs: Date.now() - startTime,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export function formatReviewReport(result) {
|
|
102
|
+
if (result.issues.length === 0) {
|
|
103
|
+
return `**Findings**\n\nNo issues found.\n\n**Summary**\n\nScore: **${result.score}/100**\nDuration: ${(result.durationMs / 1000).toFixed(1)}s\n\n${result.summary}`;
|
|
104
|
+
}
|
|
105
|
+
const findings = result.issues.slice(0, 20).map(issue => {
|
|
106
|
+
const suggestion = issue.suggestion ? `\n Suggestion: ${issue.suggestion}` : '';
|
|
107
|
+
return `- [${issue.severity.toUpperCase()}] ${issue.file}:${issue.line} (${issue.category}) — ${issue.message}${suggestion}`;
|
|
108
|
+
}).join('\n');
|
|
109
|
+
return `**Findings**\n\n${findings}\n\n**Summary**\n\nScore: **${result.score}/100**\nIssues: ${result.issues.length}\nDuration: ${(result.durationMs / 1000).toFixed(1)}s\n\n${result.summary}`;
|
|
110
|
+
}
|
|
111
|
+
async function determineScope(options) {
|
|
112
|
+
if (options.files && options.files.length > 0) {
|
|
113
|
+
return options.files.map(file => file.startsWith(process.cwd()) ? file : join(process.cwd(), file));
|
|
114
|
+
}
|
|
115
|
+
if (options.prUrl) {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const ref = options.gitRef ?? 'HEAD';
|
|
120
|
+
const output = execSync(`git diff --name-only ${ref}`, {
|
|
121
|
+
encoding: 'utf-8',
|
|
122
|
+
windowsHide: true,
|
|
123
|
+
});
|
|
124
|
+
return output.split('\n').filter(Boolean).map(file => join(process.cwd(), file));
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function runLinters(files) {
|
|
131
|
+
const output = [];
|
|
132
|
+
const hasTsFiles = files.some(file => file.endsWith('.ts') || file.endsWith('.tsx'));
|
|
133
|
+
if (hasTsFiles) {
|
|
134
|
+
try {
|
|
135
|
+
const result = execSync(LINTER_COMMANDS.ts, {
|
|
136
|
+
encoding: 'utf-8',
|
|
137
|
+
timeout: 60000,
|
|
138
|
+
windowsHide: true,
|
|
139
|
+
});
|
|
140
|
+
if (result.trim())
|
|
141
|
+
output.push(`[tsc]\n${result}`);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Ignore linter process failures, capture only output.
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const result = execSync(LINTER_COMMANDS.eslint, {
|
|
148
|
+
encoding: 'utf-8',
|
|
149
|
+
timeout: 60000,
|
|
150
|
+
windowsHide: true,
|
|
151
|
+
});
|
|
152
|
+
if (result.trim())
|
|
153
|
+
output.push(`[eslint]\n${result}`);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
// Ignore linter process failures, capture only output.
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return output.join('\n\n');
|
|
160
|
+
}
|
|
161
|
+
function parseReviewResponse(content) {
|
|
162
|
+
const fencedMatch = content.match(/```json\n([\s\S]*?)\n```/);
|
|
163
|
+
const raw = fencedMatch?.[1] ?? content.trim();
|
|
164
|
+
try {
|
|
165
|
+
return JSON.parse(raw);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function normalizeIssues(issues) {
|
|
172
|
+
const normalized = [];
|
|
173
|
+
for (const issue of issues) {
|
|
174
|
+
if (!issue || typeof issue !== 'object')
|
|
175
|
+
continue;
|
|
176
|
+
const candidate = issue;
|
|
177
|
+
if (!candidate.file || !candidate.message)
|
|
178
|
+
continue;
|
|
179
|
+
normalized.push({
|
|
180
|
+
file: candidate.file,
|
|
181
|
+
line: typeof candidate.line === 'number' ? candidate.line : 1,
|
|
182
|
+
severity: isSeverity(candidate.severity) ? candidate.severity : 'minor',
|
|
183
|
+
category: isCategory(candidate.category) ? candidate.category : 'quality',
|
|
184
|
+
message: candidate.message,
|
|
185
|
+
suggestion: candidate.suggestion,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
return normalized.sort((a, b) => severityWeight[a.severity] - severityWeight[b.severity] ||
|
|
189
|
+
a.file.localeCompare(b.file) ||
|
|
190
|
+
a.line - b.line);
|
|
191
|
+
}
|
|
192
|
+
function isSeverity(value) {
|
|
193
|
+
return value === 'critical' || value === 'major' || value === 'minor' || value === 'info';
|
|
194
|
+
}
|
|
195
|
+
function isCategory(value) {
|
|
196
|
+
return value === 'correctness' || value === 'security' || value === 'quality' || value === 'performance';
|
|
197
|
+
}
|
|
198
|
+
function clampScore(score) {
|
|
199
|
+
return Math.max(0, Math.min(100, Math.round(score)));
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=review.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review.js","sourceRoot":"","sources":["../../src/core/review.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export interface SandboxOptions {
|
|
2
|
+
/** Docker image to use */
|
|
3
|
+
image?: string;
|
|
4
|
+
/** Timeout in ms */
|
|
5
|
+
timeout?: number;
|
|
6
|
+
/** Network access: none, isolated, full */
|
|
7
|
+
network?: 'none' | 'isolated' | 'full';
|
|
8
|
+
/** Mount current project directory */
|
|
9
|
+
mountProject?: boolean;
|
|
10
|
+
/** Additional directories to mount */
|
|
11
|
+
mounts?: Array<{
|
|
12
|
+
host: string;
|
|
13
|
+
container: string;
|
|
14
|
+
}>;
|
|
15
|
+
/** Environment variables */
|
|
16
|
+
env?: Record<string, string>;
|
|
17
|
+
}
|
|
18
|
+
export interface SandboxResult {
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
exitCode: number;
|
|
22
|
+
durationMs: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Sandbox for isolated command execution using Docker.
|
|
26
|
+
* Falls back to direct execution if Docker is not available.
|
|
27
|
+
*/
|
|
28
|
+
export declare class Sandbox {
|
|
29
|
+
private dockerAvailable;
|
|
30
|
+
/**
|
|
31
|
+
* Check if sandbox is supported on the current platform.
|
|
32
|
+
* On Windows, sandbox requires Docker/WSL which may not be available.
|
|
33
|
+
*/
|
|
34
|
+
isSupported(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Check if Docker-based sandbox (with isolation) is available.
|
|
37
|
+
*/
|
|
38
|
+
isDockerAvailable(): Promise<boolean>;
|
|
39
|
+
/**
|
|
40
|
+
* Get a human-readable reason why sandbox might be limited on this platform.
|
|
41
|
+
*/
|
|
42
|
+
getCapabilityInfo(): {
|
|
43
|
+
supported: boolean;
|
|
44
|
+
reason?: string;
|
|
45
|
+
action?: string;
|
|
46
|
+
};
|
|
47
|
+
execute(command: string, options?: SandboxOptions): Promise<SandboxResult>;
|
|
48
|
+
private executeInDocker;
|
|
49
|
+
private executeDirect;
|
|
50
|
+
}
|
|
51
|
+
export declare const sandbox: Sandbox;
|
|
52
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/core/sandbox.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACvC,sCAAsC;IACtC,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,sCAAsC;IACtC,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpD,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAID;;;GAGG;AACH,qBAAa,OAAO;IAClB,OAAO,CAAC,eAAe,CAAuB;IAE9C;;;OAGG;IACH,WAAW,IAAK,OAAO;IAMvB;;OAEG;IACG,iBAAiB,IAAK,OAAO,CAAC,OAAO,CAAC;IAa5C;;OAEG;IACH,iBAAiB,IAAK;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;IAYxE,OAAO,CAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;YAWvE,eAAe;YAwDf,aAAa;CA2B5B;AAGD,eAAO,MAAM,OAAO,SAAgB,CAAA"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { sep } from 'node:path';
|
|
4
|
+
import { platform } from 'node:os';
|
|
5
|
+
/** Convert Windows backslash paths to forward-slash paths for Docker mount syntax */
|
|
6
|
+
function toDockerPath(p) {
|
|
7
|
+
return p.split(sep).join('/');
|
|
8
|
+
}
|
|
9
|
+
const DEFAULT_IMAGE = 'node:20-alpine';
|
|
10
|
+
/**
|
|
11
|
+
* Sandbox for isolated command execution using Docker.
|
|
12
|
+
* Falls back to direct execution if Docker is not available.
|
|
13
|
+
*/
|
|
14
|
+
export class Sandbox {
|
|
15
|
+
dockerAvailable = null;
|
|
16
|
+
/**
|
|
17
|
+
* Check if sandbox is supported on the current platform.
|
|
18
|
+
* On Windows, sandbox requires Docker/WSL which may not be available.
|
|
19
|
+
*/
|
|
20
|
+
isSupported() {
|
|
21
|
+
// Direct execution fallback works on all platforms,
|
|
22
|
+
// but Docker-based isolation is only meaningful with Docker/WSL.
|
|
23
|
+
return true; // direct execution always works
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if Docker-based sandbox (with isolation) is available.
|
|
27
|
+
*/
|
|
28
|
+
async isDockerAvailable() {
|
|
29
|
+
if (this.dockerAvailable !== null)
|
|
30
|
+
return this.dockerAvailable;
|
|
31
|
+
try {
|
|
32
|
+
execSync('docker --version', { encoding: 'utf-8', stdio: 'pipe', windowsHide: true });
|
|
33
|
+
this.dockerAvailable = true;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
this.dockerAvailable = false;
|
|
37
|
+
}
|
|
38
|
+
return this.dockerAvailable;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get a human-readable reason why sandbox might be limited on this platform.
|
|
42
|
+
*/
|
|
43
|
+
getCapabilityInfo() {
|
|
44
|
+
const os = platform();
|
|
45
|
+
if (os === 'win32') {
|
|
46
|
+
return {
|
|
47
|
+
supported: false,
|
|
48
|
+
reason: 'current sandbox implementation requires Docker/WSL/Linux runtime',
|
|
49
|
+
action: 'use WSL/Podman or disable sandbox command',
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { supported: true };
|
|
53
|
+
}
|
|
54
|
+
async execute(command, options = {}) {
|
|
55
|
+
const startTime = Date.now();
|
|
56
|
+
if (await this.isDockerAvailable()) {
|
|
57
|
+
return this.executeInDocker(command, options, startTime);
|
|
58
|
+
}
|
|
59
|
+
// Fallback: direct execution
|
|
60
|
+
return this.executeDirect(command, options, startTime);
|
|
61
|
+
}
|
|
62
|
+
async executeInDocker(command, options, startTime) {
|
|
63
|
+
const containerName = `dsc-sandbox-${randomUUID().slice(0, 8)}`;
|
|
64
|
+
const image = options.image ?? DEFAULT_IMAGE;
|
|
65
|
+
const timeout = options.timeout ?? 120000;
|
|
66
|
+
const dockerArgs = [
|
|
67
|
+
'run',
|
|
68
|
+
'--rm',
|
|
69
|
+
'--name', containerName,
|
|
70
|
+
'--network', options.network === 'full' ? 'bridge' : 'none',
|
|
71
|
+
'-w', '/workspace',
|
|
72
|
+
];
|
|
73
|
+
if (options.mountProject !== false) {
|
|
74
|
+
dockerArgs.push('-v', `${toDockerPath(process.cwd())}:/workspace`);
|
|
75
|
+
}
|
|
76
|
+
if (options.mounts) {
|
|
77
|
+
for (const m of options.mounts) {
|
|
78
|
+
dockerArgs.push('-v', `${toDockerPath(m.host)}:${m.container}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (options.env) {
|
|
82
|
+
for (const [key, value] of Object.entries(options.env)) {
|
|
83
|
+
dockerArgs.push('-e', `${key}=${value}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
dockerArgs.push(image, 'sh', '-c', command);
|
|
87
|
+
try {
|
|
88
|
+
const output = execSync(dockerArgs.join(' '), {
|
|
89
|
+
encoding: 'utf-8',
|
|
90
|
+
timeout,
|
|
91
|
+
windowsHide: true,
|
|
92
|
+
stdio: 'pipe',
|
|
93
|
+
});
|
|
94
|
+
return {
|
|
95
|
+
stdout: output,
|
|
96
|
+
stderr: '',
|
|
97
|
+
exitCode: 0,
|
|
98
|
+
durationMs: Date.now() - startTime,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
const error = err;
|
|
103
|
+
return {
|
|
104
|
+
stdout: error.stdout ?? '',
|
|
105
|
+
stderr: error.stderr ?? error.message,
|
|
106
|
+
exitCode: error.status ?? 1,
|
|
107
|
+
durationMs: Date.now() - startTime,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
async executeDirect(command, options, startTime) {
|
|
112
|
+
const timeout = options.timeout ?? 120000;
|
|
113
|
+
try {
|
|
114
|
+
const output = execSync(command, {
|
|
115
|
+
encoding: 'utf-8',
|
|
116
|
+
timeout,
|
|
117
|
+
windowsHide: true,
|
|
118
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
119
|
+
});
|
|
120
|
+
return {
|
|
121
|
+
stdout: output,
|
|
122
|
+
stderr: '',
|
|
123
|
+
exitCode: 0,
|
|
124
|
+
durationMs: Date.now() - startTime,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
const error = err;
|
|
129
|
+
return {
|
|
130
|
+
stdout: error.stdout ?? '',
|
|
131
|
+
stderr: error.stderr ?? error.message,
|
|
132
|
+
exitCode: error.status ?? 1,
|
|
133
|
+
durationMs: Date.now() - startTime,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Singleton
|
|
139
|
+
export const sandbox = new Sandbox();
|
|
140
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/core/sandbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
export interface ScheduledTask {
|
|
3
|
+
id: string;
|
|
4
|
+
prompt: string;
|
|
5
|
+
interval: number;
|
|
6
|
+
lastRun: number;
|
|
7
|
+
nextRun: number;
|
|
8
|
+
runCount: number;
|
|
9
|
+
maxRuns?: number;
|
|
10
|
+
createdAt: number;
|
|
11
|
+
}
|
|
12
|
+
export type TaskCallback = (prompt: string) => Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Simple in-memory scheduler for recurring tasks (/loop command).
|
|
15
|
+
* Tasks run within the CLI session and are lost on exit.
|
|
16
|
+
*/
|
|
17
|
+
export declare class Scheduler extends EventEmitter {
|
|
18
|
+
private tasks;
|
|
19
|
+
private timers;
|
|
20
|
+
private callback;
|
|
21
|
+
setCallback(cb: TaskCallback): void;
|
|
22
|
+
/**
|
|
23
|
+
* Add a recurring task
|
|
24
|
+
*/
|
|
25
|
+
addTask(prompt: string, intervalMs: number, maxRuns?: number): ScheduledTask;
|
|
26
|
+
/**
|
|
27
|
+
* Parse interval string (e.g., "5m", "1h", "30s") to milliseconds
|
|
28
|
+
*/
|
|
29
|
+
static parseInterval(str: string): number;
|
|
30
|
+
/**
|
|
31
|
+
* Remove a task
|
|
32
|
+
*/
|
|
33
|
+
removeTask(id: string): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* List all active tasks
|
|
36
|
+
*/
|
|
37
|
+
listTasks(): ScheduledTask[];
|
|
38
|
+
/**
|
|
39
|
+
* Clear all tasks
|
|
40
|
+
*/
|
|
41
|
+
clearAll(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Get task count
|
|
44
|
+
*/
|
|
45
|
+
get count(): number;
|
|
46
|
+
/**
|
|
47
|
+
* Save tasks to disk for persistence across restarts.
|
|
48
|
+
*/
|
|
49
|
+
save(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Load tasks from disk and re-activate them.
|
|
52
|
+
*/
|
|
53
|
+
load(callback?: TaskCallback): Promise<void>;
|
|
54
|
+
}
|
|
55
|
+
export declare const scheduler: Scheduler;
|
|
56
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","sourceRoot":"","sources":["../../src/core/scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAM1C,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,YAAY,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;AAM5D;;;GAGG;AACH,qBAAa,SAAU,SAAQ,YAAY;IACzC,OAAO,CAAC,KAAK,CAAwC;IACrD,OAAO,CAAC,MAAM,CAAyD;IACvE,OAAO,CAAC,QAAQ,CAA4B;IAE5C,WAAW,CAAE,EAAE,EAAE,YAAY,GAAG,IAAI;IAIpC;;OAEG;IACH,OAAO,CAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa;IA8C7E;;OAEG;IACH,MAAM,CAAC,aAAa,CAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAgB1C;;OAEG;IACH,UAAU,CAAE,EAAE,EAAE,MAAM,GAAG,OAAO;IAWhC;;OAEG;IACH,SAAS,IAAK,aAAa,EAAE;IAK7B;;OAEG;IACH,QAAQ,IAAK,IAAI;IASjB;;OAEG;IACH,IAAI,KAAK,IAAK,MAAM,CAEnB;IAED;;OAEG;IACG,IAAI,IAAK,OAAO,CAAC,IAAI,CAAC;IAY5B;;OAEG;IACG,IAAI,CAAE,QAAQ,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAiCpD;AAGD,eAAO,MAAM,SAAS,WAAkB,CAAA"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
function getSchedulerFile() {
|
|
7
|
+
return join(homedir(), '.deepseek-code', 'scheduler-tasks.json');
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Simple in-memory scheduler for recurring tasks (/loop command).
|
|
11
|
+
* Tasks run within the CLI session and are lost on exit.
|
|
12
|
+
*/
|
|
13
|
+
export class Scheduler extends EventEmitter {
|
|
14
|
+
tasks = new Map();
|
|
15
|
+
timers = new Map();
|
|
16
|
+
callback = null;
|
|
17
|
+
setCallback(cb) {
|
|
18
|
+
this.callback = cb;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Add a recurring task
|
|
22
|
+
*/
|
|
23
|
+
addTask(prompt, intervalMs, maxRuns) {
|
|
24
|
+
const id = `task_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const task = {
|
|
27
|
+
id,
|
|
28
|
+
prompt,
|
|
29
|
+
interval: intervalMs,
|
|
30
|
+
lastRun: 0,
|
|
31
|
+
nextRun: now + intervalMs,
|
|
32
|
+
runCount: 0,
|
|
33
|
+
maxRuns,
|
|
34
|
+
createdAt: now,
|
|
35
|
+
};
|
|
36
|
+
this.tasks.set(id, task);
|
|
37
|
+
const timer = setInterval(async () => {
|
|
38
|
+
const current = this.tasks.get(id);
|
|
39
|
+
if (!current) {
|
|
40
|
+
clearInterval(timer);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// Check max runs
|
|
44
|
+
if (current.maxRuns && current.runCount >= current.maxRuns) {
|
|
45
|
+
this.removeTask(id);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
current.lastRun = Date.now();
|
|
49
|
+
current.nextRun = current.lastRun + current.interval;
|
|
50
|
+
current.runCount++;
|
|
51
|
+
this.emit('taskRun', current);
|
|
52
|
+
if (this.callback) {
|
|
53
|
+
await this.callback(current.prompt);
|
|
54
|
+
}
|
|
55
|
+
}, intervalMs);
|
|
56
|
+
this.timers.set(id, timer);
|
|
57
|
+
return task;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Parse interval string (e.g., "5m", "1h", "30s") to milliseconds
|
|
61
|
+
*/
|
|
62
|
+
static parseInterval(str) {
|
|
63
|
+
const match = str.match(/^(\d+)\s*(s|m|h|min)?$/);
|
|
64
|
+
if (!match)
|
|
65
|
+
return 10 * 60 * 1000; // default 10m
|
|
66
|
+
const value = parseInt(match[1], 10);
|
|
67
|
+
const unit = match[2] ?? 'm';
|
|
68
|
+
switch (unit) {
|
|
69
|
+
case 's': return value * 1000;
|
|
70
|
+
case 'm':
|
|
71
|
+
case 'min': return value * 60 * 1000;
|
|
72
|
+
case 'h': return value * 3600 * 1000;
|
|
73
|
+
default: return value * 60 * 1000;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Remove a task
|
|
78
|
+
*/
|
|
79
|
+
removeTask(id) {
|
|
80
|
+
const timer = this.timers.get(id);
|
|
81
|
+
if (timer) {
|
|
82
|
+
clearInterval(timer);
|
|
83
|
+
this.timers.delete(id);
|
|
84
|
+
}
|
|
85
|
+
const removed = this.tasks.delete(id);
|
|
86
|
+
if (removed)
|
|
87
|
+
this.save().catch(() => { });
|
|
88
|
+
return removed;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* List all active tasks
|
|
92
|
+
*/
|
|
93
|
+
listTasks() {
|
|
94
|
+
return Array.from(this.tasks.values())
|
|
95
|
+
.sort((a, b) => a.nextRun - b.nextRun);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Clear all tasks
|
|
99
|
+
*/
|
|
100
|
+
clearAll() {
|
|
101
|
+
for (const [id, timer] of this.timers) {
|
|
102
|
+
clearInterval(timer);
|
|
103
|
+
this.timers.delete(id);
|
|
104
|
+
}
|
|
105
|
+
this.tasks.clear();
|
|
106
|
+
this.save().catch(() => { });
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Get task count
|
|
110
|
+
*/
|
|
111
|
+
get count() {
|
|
112
|
+
return this.tasks.size;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Save tasks to disk for persistence across restarts.
|
|
116
|
+
*/
|
|
117
|
+
async save() {
|
|
118
|
+
const dir = join(homedir(), '.deepseek-code');
|
|
119
|
+
if (!existsSync(dir)) {
|
|
120
|
+
await mkdir(dir, { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
const tasks = Array.from(this.tasks.values()).map(t => ({
|
|
123
|
+
...t,
|
|
124
|
+
// Don't save timer references
|
|
125
|
+
}));
|
|
126
|
+
await writeFile(getSchedulerFile(), JSON.stringify(tasks, null, 2), 'utf-8');
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Load tasks from disk and re-activate them.
|
|
130
|
+
*/
|
|
131
|
+
async load(callback) {
|
|
132
|
+
if (callback)
|
|
133
|
+
this.setCallback(callback);
|
|
134
|
+
const schedulerFile = getSchedulerFile();
|
|
135
|
+
if (!existsSync(schedulerFile))
|
|
136
|
+
return;
|
|
137
|
+
try {
|
|
138
|
+
const content = await readFile(schedulerFile, 'utf-8');
|
|
139
|
+
const tasks = JSON.parse(content);
|
|
140
|
+
for (const task of tasks) {
|
|
141
|
+
// Re-activate tasks that haven't expired
|
|
142
|
+
if (task.maxRuns && task.runCount >= task.maxRuns)
|
|
143
|
+
continue;
|
|
144
|
+
// Calculate remaining interval
|
|
145
|
+
const now = Date.now();
|
|
146
|
+
const elapsed = now - task.lastRun;
|
|
147
|
+
const remaining = Math.max(0, task.interval - elapsed);
|
|
148
|
+
if (remaining > 0) {
|
|
149
|
+
// Re-schedule with remaining time
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
this.addTask(task.prompt, task.interval, task.maxRuns);
|
|
152
|
+
}, remaining);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
// Already past due, schedule immediately
|
|
156
|
+
this.addTask(task.prompt, task.interval, task.maxRuns);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Ignore load errors
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Singleton
|
|
166
|
+
export const scheduler = new Scheduler();
|
|
167
|
+
//# sourceMappingURL=scheduler.js.map
|