@diff-review-system/drs 1.0.0 → 2.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/.opencode/agent/describe/pr-describer.md +221 -0
- package/.opencode/agent/review/documentation.md +56 -0
- package/.opencode/agent/review/performance.md +32 -130
- package/.opencode/agent/review/quality.md +36 -104
- package/.opencode/agent/review/security.md +32 -94
- package/.opencode/agent/review/style.md +26 -10
- package/.opencode/agent/review/unified-reviewer.md +74 -0
- package/.opencode/opencode.jsonc +4 -41
- package/.opencode/tool/write_json_output.ts +24 -0
- package/README.md +215 -82
- package/dist/ci/runner.d.ts.map +1 -1
- package/dist/ci/runner.js +4 -4
- package/dist/ci/runner.js.map +1 -1
- package/dist/cli/describe-mr.d.ts +11 -0
- package/dist/cli/describe-mr.d.ts.map +1 -0
- package/dist/cli/describe-mr.js +104 -0
- package/dist/cli/describe-mr.js.map +1 -0
- package/dist/cli/describe-pr.d.ts +12 -0
- package/dist/cli/describe-pr.d.ts.map +1 -0
- package/dist/cli/describe-pr.js +105 -0
- package/dist/cli/describe-pr.js.map +1 -0
- package/dist/cli/index.js +234 -20
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.d.ts +1 -1
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +337 -120
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/post-comments.d.ts +15 -0
- package/dist/cli/post-comments.d.ts.map +1 -0
- package/dist/cli/post-comments.js +216 -0
- package/dist/cli/post-comments.js.map +1 -0
- package/dist/cli/review-local.d.ts +3 -0
- package/dist/cli/review-local.d.ts.map +1 -1
- package/dist/cli/review-local.js +46 -63
- package/dist/cli/review-local.js.map +1 -1
- package/dist/cli/review-mr.d.ts +7 -0
- package/dist/cli/review-mr.d.ts.map +1 -1
- package/dist/cli/review-mr.js +88 -117
- package/dist/cli/review-mr.js.map +1 -1
- package/dist/cli/review-pr.d.ts +6 -0
- package/dist/cli/review-pr.d.ts.map +1 -1
- package/dist/cli/review-pr.js +81 -114
- package/dist/cli/review-pr.js.map +1 -1
- package/dist/cli/show-changes.d.ts +15 -0
- package/dist/cli/show-changes.d.ts.map +1 -0
- package/dist/cli/show-changes.js +184 -0
- package/dist/cli/show-changes.js.map +1 -0
- package/dist/github/client.d.ts +199 -4
- package/dist/github/client.d.ts.map +1 -1
- package/dist/github/client.js +37 -2
- package/dist/github/client.js.map +1 -1
- package/dist/github/client.test.d.ts +2 -0
- package/dist/github/client.test.d.ts.map +1 -0
- package/dist/github/client.test.js +206 -0
- package/dist/github/client.test.js.map +1 -0
- package/dist/github/platform-adapter.d.ts +31 -0
- package/dist/github/platform-adapter.d.ts.map +1 -0
- package/dist/github/platform-adapter.js +129 -0
- package/dist/github/platform-adapter.js.map +1 -0
- package/dist/github/platform-adapter.test.d.ts +2 -0
- package/dist/github/platform-adapter.test.d.ts.map +1 -0
- package/dist/github/platform-adapter.test.js +40 -0
- package/dist/github/platform-adapter.test.js.map +1 -0
- package/dist/gitlab/client.d.ts +12 -0
- package/dist/gitlab/client.d.ts.map +1 -1
- package/dist/gitlab/client.js +19 -1
- package/dist/gitlab/client.js.map +1 -1
- package/dist/gitlab/diff-parser.test.d.ts +2 -0
- package/dist/gitlab/diff-parser.test.d.ts.map +1 -0
- package/dist/gitlab/diff-parser.test.js +315 -0
- package/dist/gitlab/diff-parser.test.js.map +1 -0
- package/dist/gitlab/platform-adapter.d.ts +27 -0
- package/dist/gitlab/platform-adapter.d.ts.map +1 -0
- package/dist/gitlab/platform-adapter.js +121 -0
- package/dist/gitlab/platform-adapter.js.map +1 -0
- package/dist/gitlab/platform-adapter.test.d.ts +2 -0
- package/dist/gitlab/platform-adapter.test.d.ts.map +1 -0
- package/dist/gitlab/platform-adapter.test.js +21 -0
- package/dist/gitlab/platform-adapter.test.js.map +1 -0
- package/dist/index.test.d.ts +2 -0
- package/dist/index.test.d.ts.map +1 -0
- package/dist/index.test.js +7 -0
- package/dist/index.test.js.map +1 -0
- package/dist/lib/change-summary.d.ts +8 -0
- package/dist/lib/change-summary.d.ts.map +1 -0
- package/dist/lib/change-summary.js +2 -0
- package/dist/lib/change-summary.js.map +1 -0
- package/dist/lib/code-quality-report.d.ts +44 -0
- package/dist/lib/code-quality-report.d.ts.map +1 -0
- package/dist/lib/code-quality-report.js +62 -0
- package/dist/lib/code-quality-report.js.map +1 -0
- package/dist/lib/code-quality-report.test.d.ts +2 -0
- package/dist/lib/code-quality-report.test.d.ts.map +1 -0
- package/dist/lib/code-quality-report.test.js +327 -0
- package/dist/lib/code-quality-report.test.js.map +1 -0
- package/dist/{gitlab → lib}/comment-formatter.d.ts +6 -3
- package/dist/lib/comment-formatter.d.ts.map +1 -0
- package/dist/{gitlab → lib}/comment-formatter.js +63 -16
- package/dist/lib/comment-formatter.js.map +1 -0
- package/dist/lib/comment-formatter.test.d.ts +2 -0
- package/dist/lib/comment-formatter.test.d.ts.map +1 -0
- package/dist/lib/comment-formatter.test.js +607 -0
- package/dist/lib/comment-formatter.test.js.map +1 -0
- package/dist/lib/comment-manager.d.ts +61 -0
- package/dist/lib/comment-manager.d.ts.map +1 -0
- package/dist/lib/comment-manager.js +91 -0
- package/dist/lib/comment-manager.js.map +1 -0
- package/dist/lib/comment-manager.test.d.ts +2 -0
- package/dist/lib/comment-manager.test.d.ts.map +1 -0
- package/dist/lib/comment-manager.test.js +618 -0
- package/dist/lib/comment-manager.test.js.map +1 -0
- package/dist/lib/comment-poster.d.ts +21 -0
- package/dist/lib/comment-poster.d.ts.map +1 -0
- package/dist/lib/comment-poster.js +96 -0
- package/dist/lib/comment-poster.js.map +1 -0
- package/dist/lib/comment-poster.test.d.ts +5 -0
- package/dist/lib/comment-poster.test.d.ts.map +1 -0
- package/dist/lib/comment-poster.test.js +215 -0
- package/dist/lib/comment-poster.test.js.map +1 -0
- package/dist/lib/config-model-overrides.test.d.ts +12 -0
- package/dist/lib/config-model-overrides.test.d.ts.map +1 -0
- package/dist/lib/config-model-overrides.test.js +254 -0
- package/dist/lib/config-model-overrides.test.js.map +1 -0
- package/dist/lib/config.d.ts +93 -8
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +178 -25
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/config.test.d.ts +2 -0
- package/dist/lib/config.test.d.ts.map +1 -0
- package/dist/lib/config.test.js +36 -0
- package/dist/lib/config.test.js.map +1 -0
- package/dist/lib/context-compression.d.ts +19 -0
- package/dist/lib/context-compression.d.ts.map +1 -0
- package/dist/lib/context-compression.js +170 -0
- package/dist/lib/context-compression.js.map +1 -0
- package/dist/lib/context-compression.test.d.ts +2 -0
- package/dist/lib/context-compression.test.d.ts.map +1 -0
- package/dist/lib/context-compression.test.js +33 -0
- package/dist/lib/context-compression.test.js.map +1 -0
- package/dist/lib/context-loader.d.ts +29 -0
- package/dist/lib/context-loader.d.ts.map +1 -0
- package/dist/lib/context-loader.js +75 -0
- package/dist/lib/context-loader.js.map +1 -0
- package/dist/lib/context-loader.test.d.ts +2 -0
- package/dist/lib/context-loader.test.d.ts.map +1 -0
- package/dist/lib/context-loader.test.js +207 -0
- package/dist/lib/context-loader.test.js.map +1 -0
- package/dist/lib/describe-core.d.ts +9 -0
- package/dist/lib/describe-core.d.ts.map +1 -0
- package/dist/lib/describe-core.js +71 -0
- package/dist/lib/describe-core.js.map +1 -0
- package/dist/lib/describe-core.test.d.ts +2 -0
- package/dist/lib/describe-core.test.d.ts.map +1 -0
- package/dist/lib/describe-core.test.js +208 -0
- package/dist/lib/describe-core.test.js.map +1 -0
- package/dist/lib/describe-output-path.test.d.ts +2 -0
- package/dist/lib/describe-output-path.test.d.ts.map +1 -0
- package/dist/lib/describe-output-path.test.js +51 -0
- package/dist/lib/describe-output-path.test.js.map +1 -0
- package/dist/lib/describe-parser.d.ts +3 -0
- package/dist/lib/describe-parser.d.ts.map +1 -0
- package/dist/lib/describe-parser.js +163 -0
- package/dist/lib/describe-parser.js.map +1 -0
- package/dist/lib/describe-parser.test.d.ts +2 -0
- package/dist/lib/describe-parser.test.d.ts.map +1 -0
- package/dist/lib/describe-parser.test.js +282 -0
- package/dist/lib/describe-parser.test.js.map +1 -0
- package/dist/lib/description-executor.d.ts +22 -0
- package/dist/lib/description-executor.d.ts.map +1 -0
- package/dist/lib/description-executor.js +72 -0
- package/dist/lib/description-executor.js.map +1 -0
- package/dist/lib/description-formatter.d.ts +37 -0
- package/dist/lib/description-formatter.d.ts.map +1 -0
- package/dist/lib/description-formatter.js +219 -0
- package/dist/lib/description-formatter.js.map +1 -0
- package/dist/{gitlab → lib}/diff-parser.d.ts +11 -0
- package/dist/lib/diff-parser.d.ts.map +1 -0
- package/dist/{gitlab → lib}/diff-parser.js +40 -3
- package/dist/lib/diff-parser.js.map +1 -0
- package/dist/lib/issue-parser.d.ts +29 -0
- package/dist/lib/issue-parser.d.ts.map +1 -0
- package/dist/lib/issue-parser.js +153 -0
- package/dist/lib/issue-parser.js.map +1 -0
- package/dist/lib/issue-parser.test.d.ts +2 -0
- package/dist/lib/issue-parser.test.d.ts.map +1 -0
- package/dist/lib/issue-parser.test.js +281 -0
- package/dist/lib/issue-parser.test.js.map +1 -0
- package/dist/lib/json-output-schema.d.ts +207 -0
- package/dist/lib/json-output-schema.d.ts.map +1 -0
- package/dist/lib/json-output-schema.js +124 -0
- package/dist/lib/json-output-schema.js.map +1 -0
- package/dist/lib/json-output-schema.test.d.ts +2 -0
- package/dist/lib/json-output-schema.test.d.ts.map +1 -0
- package/dist/lib/json-output-schema.test.js +92 -0
- package/dist/lib/json-output-schema.test.js.map +1 -0
- package/dist/lib/json-output.d.ts +43 -0
- package/dist/lib/json-output.d.ts.map +1 -0
- package/dist/lib/json-output.js +34 -0
- package/dist/lib/json-output.js.map +1 -0
- package/dist/lib/output-paths.d.ts +6 -0
- package/dist/lib/output-paths.d.ts.map +1 -0
- package/dist/lib/output-paths.js +5 -0
- package/dist/lib/output-paths.js.map +1 -0
- package/dist/lib/platform-client.d.ts +130 -0
- package/dist/lib/platform-client.d.ts.map +1 -0
- package/dist/lib/platform-client.js +8 -0
- package/dist/lib/platform-client.js.map +1 -0
- package/dist/lib/position-validator.d.ts +36 -0
- package/dist/lib/position-validator.d.ts.map +1 -0
- package/dist/lib/position-validator.js +43 -0
- package/dist/lib/position-validator.js.map +1 -0
- package/dist/lib/repository-validator.d.ts +52 -0
- package/dist/lib/repository-validator.d.ts.map +1 -0
- package/dist/lib/repository-validator.js +219 -0
- package/dist/lib/repository-validator.js.map +1 -0
- package/dist/lib/repository-validator.test.d.ts +5 -0
- package/dist/lib/repository-validator.test.d.ts.map +1 -0
- package/dist/lib/repository-validator.test.js +341 -0
- package/dist/lib/repository-validator.test.js.map +1 -0
- package/dist/lib/review-core.d.ts +66 -0
- package/dist/lib/review-core.d.ts.map +1 -0
- package/dist/lib/review-core.js +449 -0
- package/dist/lib/review-core.js.map +1 -0
- package/dist/lib/review-core.test.d.ts +2 -0
- package/dist/lib/review-core.test.d.ts.map +1 -0
- package/dist/lib/review-core.test.js +552 -0
- package/dist/lib/review-core.test.js.map +1 -0
- package/dist/lib/review-orchestrator.d.ts +77 -0
- package/dist/lib/review-orchestrator.d.ts.map +1 -0
- package/dist/lib/review-orchestrator.js +124 -0
- package/dist/lib/review-orchestrator.js.map +1 -0
- package/dist/lib/review-orchestrator.test.d.ts +2 -0
- package/dist/lib/review-orchestrator.test.d.ts.map +1 -0
- package/dist/lib/review-orchestrator.test.js +413 -0
- package/dist/lib/review-orchestrator.test.js.map +1 -0
- package/dist/lib/review-output-path.test.d.ts +2 -0
- package/dist/lib/review-output-path.test.d.ts.map +1 -0
- package/dist/lib/review-output-path.test.js +83 -0
- package/dist/lib/review-output-path.test.js.map +1 -0
- package/dist/lib/review-parser.d.ts +2 -0
- package/dist/lib/review-parser.d.ts.map +1 -0
- package/dist/lib/review-parser.js +100 -0
- package/dist/lib/review-parser.js.map +1 -0
- package/dist/lib/unified-review-executor.d.ts +49 -0
- package/dist/lib/unified-review-executor.d.ts.map +1 -0
- package/dist/lib/unified-review-executor.js +158 -0
- package/dist/lib/unified-review-executor.js.map +1 -0
- package/dist/lib/unified-review-executor.test.d.ts +5 -0
- package/dist/lib/unified-review-executor.test.d.ts.map +1 -0
- package/dist/lib/unified-review-executor.test.js +344 -0
- package/dist/lib/unified-review-executor.test.js.map +1 -0
- package/dist/lib/write-json-output.d.ts +13 -0
- package/dist/lib/write-json-output.d.ts.map +1 -0
- package/dist/lib/write-json-output.js +37 -0
- package/dist/lib/write-json-output.js.map +1 -0
- package/dist/opencode/agent-loader.d.ts +3 -4
- package/dist/opencode/agent-loader.d.ts.map +1 -1
- package/dist/opencode/agent-loader.js +51 -42
- package/dist/opencode/agent-loader.js.map +1 -1
- package/dist/opencode/agent-skill-overlay.d.ts +11 -0
- package/dist/opencode/agent-skill-overlay.d.ts.map +1 -0
- package/dist/opencode/agent-skill-overlay.js +164 -0
- package/dist/opencode/agent-skill-overlay.js.map +1 -0
- package/dist/opencode/client.d.ts +14 -5
- package/dist/opencode/client.d.ts.map +1 -1
- package/dist/opencode/client.js +311 -32
- package/dist/opencode/client.js.map +1 -1
- package/dist/opencode/client.test.d.ts +2 -0
- package/dist/opencode/client.test.d.ts.map +1 -0
- package/dist/opencode/client.test.js +317 -0
- package/dist/opencode/client.test.js.map +1 -0
- package/dist/opencode/opencode-paths.d.ts +2 -0
- package/dist/opencode/opencode-paths.d.ts.map +1 -0
- package/dist/opencode/opencode-paths.js +7 -0
- package/dist/opencode/opencode-paths.js.map +1 -0
- package/dist/opencode/skill-loader.d.ts +6 -0
- package/dist/opencode/skill-loader.d.ts.map +1 -0
- package/dist/opencode/skill-loader.js +36 -0
- package/dist/opencode/skill-loader.js.map +1 -0
- package/package.json +29 -20
- package/.opencode/agent/github-reviewer.md +0 -62
- package/.opencode/agent/gitlab-reviewer.md +0 -62
- package/.opencode/agent/local-reviewer.md +0 -71
- package/dist/gitlab/comment-formatter.d.ts.map +0 -1
- package/dist/gitlab/comment-formatter.js.map +0 -1
- package/dist/gitlab/diff-parser.d.ts.map +0 -1
- package/dist/gitlab/diff-parser.js.map +0 -1
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, readFile, readdir, rm, writeFile } from 'fs/promises';
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { dirname, join, relative } from 'path';
|
|
4
|
+
import { tmpdir } from 'os';
|
|
5
|
+
import * as yaml from 'yaml';
|
|
6
|
+
import { getDefaultSkills, normalizeAgentConfig } from '../lib/config.js';
|
|
7
|
+
import { builtInAgentPath } from './opencode-paths.js';
|
|
8
|
+
import { loadProjectSkills } from './skill-loader.js';
|
|
9
|
+
const SKILL_FILE_NAME = 'SKILL.md';
|
|
10
|
+
function normalizeSkillList(skills) {
|
|
11
|
+
if (!Array.isArray(skills)) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return skills.map(String).filter((skill) => skill.length > 0);
|
|
15
|
+
}
|
|
16
|
+
function resolveConfiguredSkills(skillConfig, agentName) {
|
|
17
|
+
if (!skillConfig || skillConfig.agents.length === 0) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
const normalizedName = agentName.startsWith('review/') ? agentName.slice(7) : agentName;
|
|
21
|
+
const entry = skillConfig.agents.find((agent) => agent.name === normalizedName) ??
|
|
22
|
+
skillConfig.agents.find((agent) => agent.name === agentName);
|
|
23
|
+
const defaultSkills = normalizeSkillList(skillConfig.defaultSkills);
|
|
24
|
+
const agentSkills = entry ? normalizeSkillList(entry.skills) : [];
|
|
25
|
+
const mergedSkills = new Set([...defaultSkills, ...agentSkills]);
|
|
26
|
+
if (mergedSkills.size === 0) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
return Array.from(mergedSkills);
|
|
30
|
+
}
|
|
31
|
+
function upsertAgentSkills(content, skills) {
|
|
32
|
+
if (skills.length === 0) {
|
|
33
|
+
return content;
|
|
34
|
+
}
|
|
35
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n?/);
|
|
36
|
+
const parsed = frontmatterMatch ? yaml.parse(frontmatterMatch[1]) : {};
|
|
37
|
+
const frontmatter = parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
38
|
+
// Enable the skill tool and set permissions for configured skills
|
|
39
|
+
// OpenCode uses on-demand skill loading via the 'skill' tool, not preloaded skills
|
|
40
|
+
const updatedFrontmatter = {
|
|
41
|
+
...frontmatter,
|
|
42
|
+
tools: {
|
|
43
|
+
...(frontmatter.tools || {}),
|
|
44
|
+
skill: true, // Enable the skill tool
|
|
45
|
+
},
|
|
46
|
+
permission: {
|
|
47
|
+
...(frontmatter.permission || {}),
|
|
48
|
+
skill: {
|
|
49
|
+
// Allow all skills by default - could be made more granular if needed
|
|
50
|
+
'*': 'allow',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const frontmatterText = yaml.stringify(updatedFrontmatter).trimEnd();
|
|
55
|
+
const newFrontmatter = `---\n${frontmatterText}\n---\n`;
|
|
56
|
+
if (frontmatterMatch) {
|
|
57
|
+
return `${newFrontmatter}${content.slice(frontmatterMatch[0].length)}`;
|
|
58
|
+
}
|
|
59
|
+
return `${newFrontmatter}\n${content}`;
|
|
60
|
+
}
|
|
61
|
+
async function writeFileWithSkills(sourcePath, targetPath, agentName, skillConfig) {
|
|
62
|
+
const content = await readFile(sourcePath, 'utf-8');
|
|
63
|
+
const skills = resolveConfiguredSkills(skillConfig, agentName);
|
|
64
|
+
const updatedContent = upsertAgentSkills(content, skills);
|
|
65
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
66
|
+
await writeFile(targetPath, updatedContent);
|
|
67
|
+
}
|
|
68
|
+
async function traverseDirectory(currentPath, fileFilter) {
|
|
69
|
+
const files = [];
|
|
70
|
+
if (!existsSync(currentPath)) {
|
|
71
|
+
return files;
|
|
72
|
+
}
|
|
73
|
+
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
74
|
+
for (const entry of entries) {
|
|
75
|
+
const fullPath = join(currentPath, entry.name);
|
|
76
|
+
if (entry.isDirectory()) {
|
|
77
|
+
files.push(...(await traverseDirectory(fullPath, fileFilter)));
|
|
78
|
+
}
|
|
79
|
+
else if (entry.isFile() && fileFilter(entry.name, fullPath)) {
|
|
80
|
+
files.push(fullPath);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return files;
|
|
84
|
+
}
|
|
85
|
+
function resolveOverrideAgentName(overrideRoot, filePath) {
|
|
86
|
+
const relativePath = relative(overrideRoot, filePath).replace(/\\/g, '/');
|
|
87
|
+
const stripped = relativePath.replace(/\/agent\.md$/, '');
|
|
88
|
+
return stripped.startsWith('review/') ? stripped : `review/${stripped}`;
|
|
89
|
+
}
|
|
90
|
+
async function copyBuiltInAgents(destinationRoot, skillConfig) {
|
|
91
|
+
const files = await traverseDirectory(builtInAgentPath, (entry) => entry.endsWith('.md'));
|
|
92
|
+
const results = await Promise.allSettled(files.map(async (filePath) => {
|
|
93
|
+
const relativePath = relative(builtInAgentPath, filePath).replace(/\\/g, '/');
|
|
94
|
+
const agentName = relativePath.replace(/\.md$/, '').replace(/\\/g, '/');
|
|
95
|
+
const targetPath = join(destinationRoot, relativePath);
|
|
96
|
+
await writeFileWithSkills(filePath, targetPath, agentName, skillConfig);
|
|
97
|
+
}));
|
|
98
|
+
const failures = results.filter((result) => result.status === 'rejected');
|
|
99
|
+
if (failures.length > 0) {
|
|
100
|
+
throw new Error(`Failed to prepare ${failures.length} built-in agent(s)`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function copyOverrideAgents(projectPath, destinationRoot, skillConfig) {
|
|
104
|
+
const overrideRoot = join(projectPath, '.drs', 'agents');
|
|
105
|
+
const files = await traverseDirectory(overrideRoot, (entry) => entry === 'agent.md');
|
|
106
|
+
const results = await Promise.allSettled(files.map(async (filePath) => {
|
|
107
|
+
const agentName = resolveOverrideAgentName(overrideRoot, filePath);
|
|
108
|
+
const targetPath = join(destinationRoot, `${agentName}.md`);
|
|
109
|
+
await writeFileWithSkills(filePath, targetPath, agentName, skillConfig);
|
|
110
|
+
}));
|
|
111
|
+
const failures = results.filter((result) => result.status === 'rejected');
|
|
112
|
+
if (failures.length > 0) {
|
|
113
|
+
throw new Error(`Failed to prepare ${failures.length} override agent(s)`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function copyProjectSkills(projectPath, destinationRoot) {
|
|
117
|
+
const skills = loadProjectSkills(projectPath);
|
|
118
|
+
const results = await Promise.allSettled(skills.map(async (skill) => {
|
|
119
|
+
const targetPath = join(destinationRoot, skill.name, SKILL_FILE_NAME);
|
|
120
|
+
const content = await readFile(skill.path, 'utf-8');
|
|
121
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
122
|
+
await writeFile(targetPath, content);
|
|
123
|
+
}));
|
|
124
|
+
const failures = results.filter((result) => result.status === 'rejected');
|
|
125
|
+
if (failures.length > 0) {
|
|
126
|
+
throw new Error(`Failed to prepare ${failures.length} skill file(s)`);
|
|
127
|
+
}
|
|
128
|
+
return skills;
|
|
129
|
+
}
|
|
130
|
+
export async function createAgentSkillOverlay(projectPath, config) {
|
|
131
|
+
const normalizedAgents = normalizeAgentConfig(config.review.agents);
|
|
132
|
+
const defaultSkills = getDefaultSkills(config);
|
|
133
|
+
const hasSkillConfig = defaultSkills.length > 0 || normalizedAgents.some((agent) => (agent.skills ?? []).length > 0);
|
|
134
|
+
const skills = loadProjectSkills(projectPath);
|
|
135
|
+
if (!hasSkillConfig && skills.length === 0) {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
const overlayRoot = await mkdtemp(join(tmpdir(), 'drs-opencode-'));
|
|
139
|
+
const opencodeRoot = join(overlayRoot, '.opencode');
|
|
140
|
+
const agentRoot = join(opencodeRoot, 'agent');
|
|
141
|
+
const skillRoot = join(opencodeRoot, 'skills');
|
|
142
|
+
try {
|
|
143
|
+
await mkdir(agentRoot, { recursive: true });
|
|
144
|
+
await mkdir(skillRoot, { recursive: true });
|
|
145
|
+
const skillConfig = {
|
|
146
|
+
defaultSkills,
|
|
147
|
+
agents: normalizedAgents,
|
|
148
|
+
};
|
|
149
|
+
await copyBuiltInAgents(agentRoot, skillConfig);
|
|
150
|
+
await copyOverrideAgents(projectPath, agentRoot, skillConfig);
|
|
151
|
+
await copyProjectSkills(projectPath, skillRoot);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
await rm(overlayRoot, { recursive: true, force: true });
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
root: overlayRoot,
|
|
159
|
+
cleanup: async () => {
|
|
160
|
+
await rm(overlayRoot, { recursive: true, force: true });
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=agent-skill-overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-skill-overlay.js","sourceRoot":"","sources":["../../src/opencode/agent-skill-overlay.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,16 +5,19 @@
|
|
|
5
5
|
* 1. Connect to an existing remote OpenCode server (when baseUrl is provided)
|
|
6
6
|
* 2. Start an OpenCode server in-process (when baseUrl is not provided)
|
|
7
7
|
*/
|
|
8
|
+
import type { CustomProvider, DRSConfig } from '../lib/config.js';
|
|
8
9
|
export interface OpencodeConfig {
|
|
9
10
|
baseUrl?: string;
|
|
10
11
|
directory?: string;
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
modelOverrides?: Record<string, string>;
|
|
13
|
+
provider?: Record<string, CustomProvider>;
|
|
14
|
+
debug?: boolean;
|
|
15
|
+
config?: DRSConfig;
|
|
13
16
|
}
|
|
14
17
|
export interface SessionCreateOptions {
|
|
15
18
|
agent: string;
|
|
16
19
|
message: string;
|
|
17
|
-
context?: Record<string,
|
|
20
|
+
context?: Record<string, unknown>;
|
|
18
21
|
}
|
|
19
22
|
export interface SessionMessage {
|
|
20
23
|
id: string;
|
|
@@ -36,17 +39,19 @@ export declare class OpencodeClient {
|
|
|
36
39
|
private inProcessServer?;
|
|
37
40
|
private client?;
|
|
38
41
|
private config;
|
|
42
|
+
private overlay?;
|
|
43
|
+
private projectRootEnv?;
|
|
39
44
|
constructor(config: OpencodeConfig);
|
|
40
45
|
/**
|
|
41
46
|
* Initialize - either connect to remote server or start in-process
|
|
42
47
|
*/
|
|
43
48
|
initialize(): Promise<void>;
|
|
44
49
|
/**
|
|
45
|
-
* Create a new session with an agent
|
|
50
|
+
* Create a new session with an agent and send initial message
|
|
46
51
|
*/
|
|
47
52
|
createSession(options: SessionCreateOptions): Promise<Session>;
|
|
48
53
|
/**
|
|
49
|
-
* Stream messages from a session
|
|
54
|
+
* Stream messages from a session (polls until agent completes)
|
|
50
55
|
*/
|
|
51
56
|
streamMessages(sessionId: string): AsyncGenerator<SessionMessage>;
|
|
52
57
|
/**
|
|
@@ -69,6 +74,10 @@ export declare class OpencodeClient {
|
|
|
69
74
|
* Get server URL
|
|
70
75
|
*/
|
|
71
76
|
getServerUrl(): string;
|
|
77
|
+
/**
|
|
78
|
+
* Resolve {env:VAR_NAME} references to actual environment variable values
|
|
79
|
+
*/
|
|
80
|
+
private resolveEnvReferences;
|
|
72
81
|
}
|
|
73
82
|
/**
|
|
74
83
|
* Create an OpenCode client with the given configuration
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/opencode/client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/opencode/client.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAIlE,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAID,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAC,CAAS;IACzB,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,eAAe,CAAC,CAA6C;IACrE,OAAO,CAAC,MAAM,CAAC,CAAqC;IACpD,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,OAAO,CAAC,CAAsD;IACtE,OAAO,CAAC,cAAc,CAAC,CAAS;gBAEpB,MAAM,EAAE,cAAc;IAMlC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA8LjC;;OAEG;IACG,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,OAAO,CAAC;IAmDpE;;OAEG;IACI,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,CAAC,cAAc,CAAC;IAyExE;;OAEG;IACG,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAQrE;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBpE;;OAEG;IACG,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBpD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB/B;;OAEG;IACH,YAAY,IAAI,MAAM;IAOtB;;OAEG;IACH,OAAO,CAAC,oBAAoB;CA8B7B;AAgDD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAE3E;AAED;;GAEG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC,cAAc,CAAC,CAIzB"}
|
package/dist/opencode/client.js
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
* 2. Start an OpenCode server in-process (when baseUrl is not provided)
|
|
7
7
|
*/
|
|
8
8
|
import { createOpencode, createOpencodeClient as createSDKClient } from '@opencode-ai/sdk';
|
|
9
|
+
import net from 'net';
|
|
10
|
+
import { getDefaultSkills, normalizeAgentConfig } from '../lib/config.js';
|
|
11
|
+
import { createAgentSkillOverlay } from './agent-skill-overlay.js';
|
|
12
|
+
const SERVER_START_TIMEOUT_MS = 10000;
|
|
9
13
|
/**
|
|
10
14
|
* OpenCode client that can start a server in-process or connect to remote
|
|
11
15
|
*/
|
|
@@ -15,6 +19,8 @@ export class OpencodeClient {
|
|
|
15
19
|
inProcessServer;
|
|
16
20
|
client;
|
|
17
21
|
config;
|
|
22
|
+
overlay;
|
|
23
|
+
projectRootEnv;
|
|
18
24
|
constructor(config) {
|
|
19
25
|
this.baseUrl = config.baseUrl;
|
|
20
26
|
this.directory = config.directory;
|
|
@@ -33,70 +39,261 @@ export class OpencodeClient {
|
|
|
33
39
|
}
|
|
34
40
|
else {
|
|
35
41
|
// Start server in-process
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
// Build OpenCode config programmatically from DRS config
|
|
43
|
+
const opencodeConfig = {
|
|
44
|
+
// Tools available to DRS review agents
|
|
45
|
+
tools: {
|
|
46
|
+
Read: true,
|
|
47
|
+
Glob: true,
|
|
48
|
+
Grep: true,
|
|
49
|
+
Bash: true,
|
|
50
|
+
write_json_output: true,
|
|
51
|
+
Write: false,
|
|
52
|
+
Edit: false,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
// Set log level to DEBUG when --debug flag is used
|
|
56
|
+
// This shows full system prompts, tools, API calls, etc. from OpenCode
|
|
57
|
+
if (this.config.debug) {
|
|
58
|
+
opencodeConfig.logLevel = 'DEBUG';
|
|
59
|
+
console.log('🔍 OpenCode debug logging enabled');
|
|
60
|
+
}
|
|
61
|
+
// Add custom provider if configured in DRS config
|
|
62
|
+
if (this.config.provider && Object.keys(this.config.provider).length > 0) {
|
|
63
|
+
// Deep clone and resolve environment variable references
|
|
64
|
+
opencodeConfig.provider = this.resolveEnvReferences(this.config.provider);
|
|
65
|
+
const providerNames = Object.keys(this.config.provider);
|
|
66
|
+
console.log(`📦 Custom provider configured: ${providerNames.join(', ')}`);
|
|
67
|
+
}
|
|
68
|
+
// Apply model overrides from DRS config
|
|
69
|
+
if (this.config.modelOverrides && Object.keys(this.config.modelOverrides).length > 0) {
|
|
70
|
+
const agentConfig = {};
|
|
71
|
+
console.log('📋 Agent model configuration:');
|
|
72
|
+
// Merge model overrides into agent configuration
|
|
73
|
+
for (const [agentName, model] of Object.entries(this.config.modelOverrides)) {
|
|
74
|
+
agentConfig[agentName] = { model };
|
|
75
|
+
console.log(` • ${agentName}: ${model}`);
|
|
76
|
+
}
|
|
77
|
+
opencodeConfig.agent = agentConfig;
|
|
78
|
+
console.log('');
|
|
79
|
+
}
|
|
80
|
+
if (this.config.config) {
|
|
81
|
+
const normalizedAgents = normalizeAgentConfig(this.config.config.review.agents);
|
|
82
|
+
const defaultSkills = getDefaultSkills(this.config.config);
|
|
83
|
+
const agentSkills = normalizedAgents
|
|
84
|
+
.map((agent) => {
|
|
85
|
+
const combined = new Set([
|
|
86
|
+
...defaultSkills,
|
|
87
|
+
...(agent.skills ? agent.skills.map(String) : []),
|
|
88
|
+
]);
|
|
89
|
+
return {
|
|
90
|
+
name: agent.name,
|
|
91
|
+
skills: Array.from(combined).filter((skill) => skill.length > 0),
|
|
92
|
+
};
|
|
93
|
+
})
|
|
94
|
+
.filter((agent) => agent.skills.length > 0);
|
|
95
|
+
if (agentSkills.length > 0) {
|
|
96
|
+
console.log('🧩 Agent skill configuration:');
|
|
97
|
+
for (const agent of agentSkills) {
|
|
98
|
+
console.log(` • review/${agent.name}: ${agent.skills.join(', ')}`);
|
|
99
|
+
}
|
|
100
|
+
console.log('');
|
|
101
|
+
}
|
|
40
102
|
}
|
|
41
|
-
//
|
|
103
|
+
// Debug: Print final OpenCode config
|
|
104
|
+
if (this.config.debug) {
|
|
105
|
+
console.log('🔧 DEBUG: Final OpenCode configuration (after env resolution):');
|
|
106
|
+
console.log('─'.repeat(50));
|
|
107
|
+
// Show environment variable status for custom providers
|
|
108
|
+
if (this.config.provider) {
|
|
109
|
+
console.log('\n📍 Environment variable status:');
|
|
110
|
+
for (const [providerName, provider] of Object.entries(this.config.provider)) {
|
|
111
|
+
const apiKeyConfig = provider.options?.apiKey;
|
|
112
|
+
if (apiKeyConfig && typeof apiKeyConfig === 'string') {
|
|
113
|
+
const envMatch = apiKeyConfig.match(/^\{env:([^}]+)\}$/);
|
|
114
|
+
if (envMatch) {
|
|
115
|
+
const envVarName = envMatch[1];
|
|
116
|
+
const envValue = process.env[envVarName];
|
|
117
|
+
if (envValue) {
|
|
118
|
+
console.log(` ✓ ${envVarName}: SET (${envValue.substring(0, 8)}...)`);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
console.log(` ✗ ${envVarName}: NOT SET`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(` • ${providerName}: API key is hardcoded (not env var)`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
console.log('');
|
|
130
|
+
}
|
|
131
|
+
// Sanitize config to hide API keys
|
|
132
|
+
const sanitizedConfig = JSON.parse(JSON.stringify(opencodeConfig));
|
|
133
|
+
if (sanitizedConfig.provider) {
|
|
134
|
+
for (const providerName of Object.keys(sanitizedConfig.provider)) {
|
|
135
|
+
if (sanitizedConfig.provider[providerName]?.options?.apiKey) {
|
|
136
|
+
const apiKey = sanitizedConfig.provider[providerName].options.apiKey;
|
|
137
|
+
// Always redact since we've resolved env vars
|
|
138
|
+
if (apiKey && apiKey.length > 0) {
|
|
139
|
+
sanitizedConfig.provider[providerName].options.apiKey =
|
|
140
|
+
`***REDACTED (${apiKey.length} chars)***`;
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
sanitizedConfig.provider[providerName].options.apiKey = '***EMPTY***';
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
console.log('Config being passed to OpenCode:');
|
|
149
|
+
console.log(JSON.stringify(sanitizedConfig, null, 2));
|
|
150
|
+
if (this.config.config) {
|
|
151
|
+
const normalizedAgents = normalizeAgentConfig(this.config.config.review.agents);
|
|
152
|
+
const defaultSkills = getDefaultSkills(this.config.config);
|
|
153
|
+
const agentSkills = normalizedAgents
|
|
154
|
+
.map((agent) => {
|
|
155
|
+
const combined = new Set([
|
|
156
|
+
...defaultSkills,
|
|
157
|
+
...(agent.skills ? agent.skills.map(String) : []),
|
|
158
|
+
]);
|
|
159
|
+
return {
|
|
160
|
+
name: `review/${agent.name}`,
|
|
161
|
+
skills: Array.from(combined).filter((skill) => skill.length > 0),
|
|
162
|
+
};
|
|
163
|
+
})
|
|
164
|
+
.filter((agent) => agent.skills.length > 0);
|
|
165
|
+
if (agentSkills.length > 0) {
|
|
166
|
+
console.log('Agent skills (applied via overlay frontmatter):');
|
|
167
|
+
console.log(JSON.stringify(agentSkills, null, 2));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
console.log('─'.repeat(50));
|
|
171
|
+
console.log('');
|
|
172
|
+
}
|
|
173
|
+
// Change to project directory so OpenCode can discover agents
|
|
174
|
+
const originalCwd = process.cwd();
|
|
175
|
+
const projectDir = this.directory || originalCwd;
|
|
176
|
+
this.projectRootEnv = process.env.DRS_PROJECT_ROOT;
|
|
177
|
+
process.env.DRS_PROJECT_ROOT = projectDir;
|
|
178
|
+
if (this.config.config) {
|
|
179
|
+
this.overlay = await createAgentSkillOverlay(projectDir, this.config.config);
|
|
180
|
+
}
|
|
181
|
+
const discoveryRoot = this.overlay?.root ?? projectDir;
|
|
182
|
+
if (discoveryRoot !== originalCwd) {
|
|
183
|
+
process.chdir(discoveryRoot);
|
|
184
|
+
}
|
|
185
|
+
// OpenCode SDK reads provider-specific API keys from environment automatically
|
|
186
|
+
// (ANTHROPIC_API_KEY, ZHIPU_API_KEY, OPENAI_API_KEY, etc.)
|
|
42
187
|
this.inProcessServer = await createOpencode({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
timeout: 10000,
|
|
46
|
-
config: {
|
|
47
|
-
model: 'anthropic/claude-opus-4-20250514',
|
|
48
|
-
},
|
|
188
|
+
timeout: SERVER_START_TIMEOUT_MS,
|
|
189
|
+
config: opencodeConfig,
|
|
49
190
|
});
|
|
191
|
+
// Restore original working directory
|
|
192
|
+
if (discoveryRoot !== originalCwd) {
|
|
193
|
+
process.chdir(originalCwd);
|
|
194
|
+
}
|
|
50
195
|
this.client = this.inProcessServer.client;
|
|
51
196
|
this.baseUrl = this.inProcessServer.server.url;
|
|
52
|
-
|
|
197
|
+
const ready = await waitForServerReady(this.baseUrl);
|
|
198
|
+
if (!ready) {
|
|
199
|
+
console.warn(`⚠️ OpenCode server did not become ready at ${this.baseUrl}. Review requests may fail.`);
|
|
200
|
+
}
|
|
53
201
|
}
|
|
54
202
|
}
|
|
55
203
|
/**
|
|
56
|
-
* Create a new session with an agent
|
|
204
|
+
* Create a new session with an agent and send initial message
|
|
57
205
|
*/
|
|
58
206
|
async createSession(options) {
|
|
59
207
|
if (!this.client) {
|
|
60
208
|
throw new Error('OpenCode client not initialized. Call initialize() first.');
|
|
61
209
|
}
|
|
62
210
|
try {
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
|
|
211
|
+
// Step 1: Create empty session
|
|
212
|
+
const createResponse = (await this.client.session.create({
|
|
213
|
+
query: {
|
|
214
|
+
directory: this.directory,
|
|
215
|
+
},
|
|
216
|
+
}));
|
|
217
|
+
const sessionId = createResponse.data?.id;
|
|
218
|
+
if (!sessionId) {
|
|
219
|
+
throw new Error('Failed to get session ID from create response');
|
|
220
|
+
}
|
|
221
|
+
// Step 2: Send initial message to start the agent
|
|
222
|
+
await this.client.session.prompt({
|
|
223
|
+
path: { id: sessionId },
|
|
224
|
+
query: {
|
|
225
|
+
directory: this.directory,
|
|
226
|
+
},
|
|
66
227
|
body: {
|
|
67
228
|
agent: options.agent,
|
|
68
|
-
|
|
229
|
+
parts: [
|
|
230
|
+
{
|
|
231
|
+
type: 'text',
|
|
232
|
+
text: options.message,
|
|
233
|
+
},
|
|
234
|
+
],
|
|
69
235
|
},
|
|
70
236
|
});
|
|
71
237
|
return {
|
|
72
|
-
id:
|
|
238
|
+
id: sessionId,
|
|
73
239
|
agent: options.agent,
|
|
74
240
|
createdAt: new Date(),
|
|
75
241
|
};
|
|
76
242
|
}
|
|
77
243
|
catch (error) {
|
|
78
|
-
|
|
244
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
245
|
+
const connectionHint = message.includes('fetch failed') || message.includes('ECONNREFUSED')
|
|
246
|
+
? ` Check the OpenCode server URL (${this.baseUrl ?? 'in-process'}) and ensure it is reachable.`
|
|
247
|
+
: '';
|
|
248
|
+
throw new Error(`Failed to create session: ${message}${connectionHint}`);
|
|
79
249
|
}
|
|
80
250
|
}
|
|
81
251
|
/**
|
|
82
|
-
* Stream messages from a session
|
|
252
|
+
* Stream messages from a session (polls until agent completes)
|
|
83
253
|
*/
|
|
84
254
|
async *streamMessages(sessionId) {
|
|
85
255
|
if (!this.client) {
|
|
86
256
|
throw new Error('OpenCode client not initialized. Call initialize() first.');
|
|
87
257
|
}
|
|
88
258
|
try {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
259
|
+
// Poll messages until agent completes
|
|
260
|
+
let lastMessageCount = 0;
|
|
261
|
+
let attempts = 0;
|
|
262
|
+
const maxAttempts = 60; // 60 attempts * 2s = 2 minutes max
|
|
263
|
+
while (attempts < maxAttempts) {
|
|
264
|
+
attempts++;
|
|
265
|
+
const messagesResponse = (await this.client.session.messages({
|
|
266
|
+
path: { id: sessionId },
|
|
267
|
+
}));
|
|
268
|
+
const messages = messagesResponse.data ?? [];
|
|
269
|
+
// Yield any new messages
|
|
270
|
+
for (let i = lastMessageCount; i < messages.length; i++) {
|
|
271
|
+
const msg = messages[i];
|
|
272
|
+
yield {
|
|
273
|
+
id: msg.info?.id ?? 'msg-' + Date.now(),
|
|
274
|
+
role: (msg.info?.role ?? 'assistant'),
|
|
275
|
+
content: msg.parts?.map((p) => p.text ?? '').join('') ?? '',
|
|
276
|
+
timestamp: new Date(),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
lastMessageCount = messages.length;
|
|
280
|
+
// Check if the last assistant message has completed
|
|
281
|
+
const lastAssistantMsg = [...messages].reverse().find((m) => m.info?.role === 'assistant');
|
|
282
|
+
if (lastAssistantMsg) {
|
|
283
|
+
const isComplete = lastAssistantMsg.info?.time?.completed !== undefined;
|
|
284
|
+
const hasError = lastAssistantMsg.info?.error !== undefined;
|
|
285
|
+
if (hasError) {
|
|
286
|
+
throw new Error(`Agent error: ${JSON.stringify(lastAssistantMsg.info?.error)}`);
|
|
287
|
+
}
|
|
288
|
+
if (isComplete) {
|
|
289
|
+
break;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
// Wait before polling again
|
|
293
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
294
|
+
}
|
|
295
|
+
if (attempts >= maxAttempts) {
|
|
296
|
+
throw new Error(`Session ${sessionId} timed out after ${maxAttempts * 2} seconds`);
|
|
100
297
|
}
|
|
101
298
|
}
|
|
102
299
|
catch (error) {
|
|
@@ -152,8 +349,20 @@ export class OpencodeClient {
|
|
|
152
349
|
*/
|
|
153
350
|
async shutdown() {
|
|
154
351
|
if (this.inProcessServer) {
|
|
352
|
+
// Close the OpenCode server
|
|
155
353
|
this.inProcessServer.server.close();
|
|
156
|
-
|
|
354
|
+
// Give server time to clean up connections
|
|
355
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
356
|
+
}
|
|
357
|
+
if (this.overlay) {
|
|
358
|
+
await this.overlay.cleanup();
|
|
359
|
+
this.overlay = undefined;
|
|
360
|
+
}
|
|
361
|
+
if (this.projectRootEnv === undefined) {
|
|
362
|
+
delete process.env.DRS_PROJECT_ROOT;
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
process.env.DRS_PROJECT_ROOT = this.projectRootEnv;
|
|
157
366
|
}
|
|
158
367
|
}
|
|
159
368
|
/**
|
|
@@ -165,6 +374,76 @@ export class OpencodeClient {
|
|
|
165
374
|
}
|
|
166
375
|
return this.baseUrl;
|
|
167
376
|
}
|
|
377
|
+
/**
|
|
378
|
+
* Resolve {env:VAR_NAME} references to actual environment variable values
|
|
379
|
+
*/
|
|
380
|
+
resolveEnvReferences(obj) {
|
|
381
|
+
if (typeof obj === 'string') {
|
|
382
|
+
// Check for {env:VAR_NAME} pattern
|
|
383
|
+
const envMatch = obj.match(/^\{env:([^}]+)\}$/);
|
|
384
|
+
if (envMatch) {
|
|
385
|
+
const envVarName = envMatch[1];
|
|
386
|
+
const envValue = process.env[envVarName];
|
|
387
|
+
if (!envValue) {
|
|
388
|
+
console.warn(`⚠️ Environment variable ${envVarName} is not set`);
|
|
389
|
+
return '';
|
|
390
|
+
}
|
|
391
|
+
return envValue;
|
|
392
|
+
}
|
|
393
|
+
return obj;
|
|
394
|
+
}
|
|
395
|
+
if (Array.isArray(obj)) {
|
|
396
|
+
return obj.map((item) => this.resolveEnvReferences(item));
|
|
397
|
+
}
|
|
398
|
+
if (obj !== null && typeof obj === 'object') {
|
|
399
|
+
const resolved = {};
|
|
400
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
401
|
+
resolved[key] = this.resolveEnvReferences(value);
|
|
402
|
+
}
|
|
403
|
+
return resolved;
|
|
404
|
+
}
|
|
405
|
+
return obj;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function parseServerEndpoint(baseUrl) {
|
|
409
|
+
try {
|
|
410
|
+
const url = new URL(baseUrl);
|
|
411
|
+
const port = url.port !== '' ? Number(url.port) : url.protocol === 'https:' ? 443 : 80;
|
|
412
|
+
if (!url.hostname || Number.isNaN(port)) {
|
|
413
|
+
return null;
|
|
414
|
+
}
|
|
415
|
+
return { host: url.hostname, port };
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
async function isServerReachable(baseUrl) {
|
|
422
|
+
const endpoint = parseServerEndpoint(baseUrl);
|
|
423
|
+
if (!endpoint) {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
426
|
+
return new Promise((resolve) => {
|
|
427
|
+
const socket = net.createConnection(endpoint, () => {
|
|
428
|
+
socket.end();
|
|
429
|
+
resolve(true);
|
|
430
|
+
});
|
|
431
|
+
socket.setTimeout(1000);
|
|
432
|
+
socket.on('timeout', () => {
|
|
433
|
+
socket.destroy();
|
|
434
|
+
resolve(false);
|
|
435
|
+
});
|
|
436
|
+
socket.on('error', () => resolve(false));
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
async function waitForServerReady(baseUrl, attempts = 10, delayMs = 200) {
|
|
440
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
441
|
+
if (await isServerReachable(baseUrl)) {
|
|
442
|
+
return true;
|
|
443
|
+
}
|
|
444
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
445
|
+
}
|
|
446
|
+
return false;
|
|
168
447
|
}
|
|
169
448
|
/**
|
|
170
449
|
* Create an OpenCode client with the given configuration
|