cmdr-agent 2.5.5 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -2
- package/dist/bin/cmdr.js +64 -3
- package/dist/bin/cmdr.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/src/cli/args.d.ts +11 -2
- package/dist/src/cli/args.d.ts.map +1 -1
- package/dist/src/cli/args.js +51 -9
- package/dist/src/cli/args.js.map +1 -1
- package/dist/src/cli/buddy.d.ts +49 -0
- package/dist/src/cli/buddy.d.ts.map +1 -0
- package/dist/src/cli/buddy.js +187 -0
- package/dist/src/cli/buddy.js.map +1 -0
- package/dist/src/cli/commands.js +131 -11
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/daemon.d.ts +45 -0
- package/dist/src/cli/daemon.d.ts.map +1 -0
- package/dist/src/cli/daemon.js +157 -0
- package/dist/src/cli/daemon.js.map +1 -0
- package/dist/src/cli/ink/App.d.ts.map +1 -1
- package/dist/src/cli/ink/App.js +299 -3
- package/dist/src/cli/ink/App.js.map +1 -1
- package/dist/src/cli/repl.d.ts +5 -3
- package/dist/src/cli/repl.d.ts.map +1 -1
- package/dist/src/cli/repl.js +72 -20
- package/dist/src/cli/repl.js.map +1 -1
- package/dist/src/config/api-key-store.d.ts +42 -0
- package/dist/src/config/api-key-store.d.ts.map +1 -0
- package/dist/src/config/api-key-store.js +180 -0
- package/dist/src/config/api-key-store.js.map +1 -0
- package/dist/src/config/schema.d.ts +3 -3
- package/dist/src/config/schema.js +1 -1
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/core/agent.d.ts +2 -0
- package/dist/src/core/agent.d.ts.map +1 -1
- package/dist/src/core/agent.js +13 -0
- package/dist/src/core/agent.js.map +1 -1
- package/dist/src/core/types.d.ts +19 -4
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/core/types.js +6 -0
- package/dist/src/core/types.js.map +1 -1
- package/dist/src/index.d.ts +13 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/llm/anthropic.d.ts.map +1 -1
- package/dist/src/llm/anthropic.js +11 -0
- package/dist/src/llm/anthropic.js.map +1 -1
- package/dist/src/llm/model-registry.d.ts.map +1 -1
- package/dist/src/llm/model-registry.js +5 -0
- package/dist/src/llm/model-registry.js.map +1 -1
- package/dist/src/llm/ollama.d.ts.map +1 -1
- package/dist/src/llm/ollama.js +8 -1
- package/dist/src/llm/ollama.js.map +1 -1
- package/dist/src/llm/openai.d.ts.map +1 -1
- package/dist/src/llm/openai.js +21 -6
- package/dist/src/llm/openai.js.map +1 -1
- package/dist/src/llm/provider-factory.d.ts +4 -1
- package/dist/src/llm/provider-factory.d.ts.map +1 -1
- package/dist/src/llm/provider-factory.js +23 -0
- package/dist/src/llm/provider-factory.js.map +1 -1
- package/dist/src/memory/index-manager.d.ts +65 -0
- package/dist/src/memory/index-manager.d.ts.map +1 -0
- package/dist/src/memory/index-manager.js +241 -0
- package/dist/src/memory/index-manager.js.map +1 -0
- package/dist/src/server/index.d.ts +20 -0
- package/dist/src/server/index.d.ts.map +1 -0
- package/dist/src/server/index.js +283 -0
- package/dist/src/server/index.js.map +1 -0
- package/dist/src/session/branch-manager.d.ts +39 -0
- package/dist/src/session/branch-manager.d.ts.map +1 -0
- package/dist/src/session/branch-manager.js +142 -0
- package/dist/src/session/branch-manager.js.map +1 -0
- package/dist/src/session/checkpoint-manager.d.ts +28 -0
- package/dist/src/session/checkpoint-manager.d.ts.map +1 -0
- package/dist/src/session/checkpoint-manager.js +96 -0
- package/dist/src/session/checkpoint-manager.js.map +1 -0
- package/dist/src/tools/built-in/browser.d.ts +32 -0
- package/dist/src/tools/built-in/browser.d.ts.map +1 -0
- package/dist/src/tools/built-in/browser.js +168 -0
- package/dist/src/tools/built-in/browser.js.map +1 -0
- package/dist/src/tools/built-in/git-diff.d.ts +1 -0
- package/dist/src/tools/built-in/git-diff.d.ts.map +1 -1
- package/dist/src/tools/built-in/git-diff.js +7 -2
- package/dist/src/tools/built-in/git-diff.js.map +1 -1
- package/dist/src/tools/built-in/index.d.ts +7 -1
- package/dist/src/tools/built-in/index.d.ts.map +1 -1
- package/dist/src/tools/built-in/index.js +19 -1
- package/dist/src/tools/built-in/index.js.map +1 -1
- package/dist/src/tools/built-in/rag-search.d.ts +12 -0
- package/dist/src/tools/built-in/rag-search.d.ts.map +1 -0
- package/dist/src/tools/built-in/rag-search.js +37 -0
- package/dist/src/tools/built-in/rag-search.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RAG / Document Indexing — semantic search over project files.
|
|
3
|
+
*
|
|
4
|
+
* Uses Ollama /api/embed with nomic-embed-text for embeddings.
|
|
5
|
+
* Stores index as JSON (no SQLite dependency).
|
|
6
|
+
* 512-token chunks with 64-token overlap.
|
|
7
|
+
* Cosine similarity search.
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, readdir, writeFile, stat, mkdir } from 'node:fs/promises';
|
|
10
|
+
import { join, relative, extname } from 'node:path';
|
|
11
|
+
import { homedir } from 'node:os';
|
|
12
|
+
// ── Chunking ────────────────────────────────────────────────────────
|
|
13
|
+
const CHUNK_SIZE = 80; // lines (approximation of ~512 tokens)
|
|
14
|
+
const CHUNK_OVERLAP = 10; // lines (~64 tokens)
|
|
15
|
+
/** Text file extensions to index. */
|
|
16
|
+
const INDEXABLE_EXTENSIONS = new Set([
|
|
17
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
18
|
+
'.py', '.rb', '.go', '.rs', '.java', '.kt', '.swift', '.c', '.cpp', '.h', '.hpp',
|
|
19
|
+
'.cs', '.php', '.lua', '.sh', '.bash', '.zsh',
|
|
20
|
+
'.html', '.css', '.scss', '.less', '.vue', '.svelte',
|
|
21
|
+
'.json', '.yaml', '.yml', '.toml', '.xml',
|
|
22
|
+
'.md', '.mdx', '.txt', '.rst',
|
|
23
|
+
'.sql', '.graphql', '.proto',
|
|
24
|
+
'.env', '.gitignore', '.dockerignore',
|
|
25
|
+
'.dockerfile', 'Dockerfile',
|
|
26
|
+
]);
|
|
27
|
+
/** Directories to skip. */
|
|
28
|
+
const SKIP_DIRS = new Set([
|
|
29
|
+
'node_modules', '.git', '.next', 'dist', 'build', 'out', 'target',
|
|
30
|
+
'coverage', '.vscode', '.idea', '__pycache__', '.tox', 'venv', '.venv',
|
|
31
|
+
]);
|
|
32
|
+
function chunkText(text, file) {
|
|
33
|
+
const lines = text.split('\n');
|
|
34
|
+
const chunks = [];
|
|
35
|
+
let start = 0;
|
|
36
|
+
while (start < lines.length) {
|
|
37
|
+
const end = Math.min(start + CHUNK_SIZE, lines.length);
|
|
38
|
+
const chunkLines = lines.slice(start, end);
|
|
39
|
+
chunks.push({
|
|
40
|
+
file,
|
|
41
|
+
startLine: start + 1,
|
|
42
|
+
endLine: end,
|
|
43
|
+
text: chunkLines.join('\n'),
|
|
44
|
+
});
|
|
45
|
+
start += CHUNK_SIZE - CHUNK_OVERLAP;
|
|
46
|
+
if (start >= lines.length)
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
return chunks;
|
|
50
|
+
}
|
|
51
|
+
// ── Embedding ───────────────────────────────────────────────────────
|
|
52
|
+
async function getEmbedding(text, ollamaUrl, model = 'nomic-embed-text') {
|
|
53
|
+
const response = await fetch(`${ollamaUrl}/api/embed`, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: { 'Content-Type': 'application/json' },
|
|
56
|
+
body: JSON.stringify({ model, input: text }),
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
throw new Error(`Embedding failed: ${response.status} ${response.statusText}`);
|
|
60
|
+
}
|
|
61
|
+
const data = await response.json();
|
|
62
|
+
return data.embeddings[0];
|
|
63
|
+
}
|
|
64
|
+
// ── Similarity ──────────────────────────────────────────────────────
|
|
65
|
+
function cosineSimilarity(a, b) {
|
|
66
|
+
let dotProduct = 0;
|
|
67
|
+
let normA = 0;
|
|
68
|
+
let normB = 0;
|
|
69
|
+
for (let i = 0; i < a.length; i++) {
|
|
70
|
+
dotProduct += a[i] * b[i];
|
|
71
|
+
normA += a[i] * a[i];
|
|
72
|
+
normB += b[i] * b[i];
|
|
73
|
+
}
|
|
74
|
+
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
75
|
+
return denom === 0 ? 0 : dotProduct / denom;
|
|
76
|
+
}
|
|
77
|
+
// ── File discovery ──────────────────────────────────────────────────
|
|
78
|
+
async function discoverFiles(rootDir, paths) {
|
|
79
|
+
const files = [];
|
|
80
|
+
async function walkDir(dir) {
|
|
81
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
82
|
+
for (const entry of entries) {
|
|
83
|
+
if (entry.name.startsWith('.') && SKIP_DIRS.has(entry.name))
|
|
84
|
+
continue;
|
|
85
|
+
if (SKIP_DIRS.has(entry.name))
|
|
86
|
+
continue;
|
|
87
|
+
const fullPath = join(dir, entry.name);
|
|
88
|
+
if (entry.isDirectory()) {
|
|
89
|
+
await walkDir(fullPath);
|
|
90
|
+
}
|
|
91
|
+
else if (entry.isFile()) {
|
|
92
|
+
const ext = extname(entry.name).toLowerCase();
|
|
93
|
+
if (INDEXABLE_EXTENSIONS.has(ext) || INDEXABLE_EXTENSIONS.has(entry.name)) {
|
|
94
|
+
files.push(fullPath);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
for (const p of paths) {
|
|
100
|
+
const fullPath = join(rootDir, p);
|
|
101
|
+
const s = await stat(fullPath).catch(() => null);
|
|
102
|
+
if (!s)
|
|
103
|
+
continue;
|
|
104
|
+
if (s.isDirectory()) {
|
|
105
|
+
await walkDir(fullPath);
|
|
106
|
+
}
|
|
107
|
+
else if (s.isFile()) {
|
|
108
|
+
files.push(fullPath);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return files;
|
|
112
|
+
}
|
|
113
|
+
// ── IndexManager ────────────────────────────────────────────────────
|
|
114
|
+
export class IndexManager {
|
|
115
|
+
cwd;
|
|
116
|
+
ollamaUrl;
|
|
117
|
+
embedModel;
|
|
118
|
+
indexPath;
|
|
119
|
+
state = null;
|
|
120
|
+
constructor(cwd, ollamaUrl = 'http://localhost:11434', embedModel = 'nomic-embed-text') {
|
|
121
|
+
this.cwd = cwd;
|
|
122
|
+
this.ollamaUrl = ollamaUrl;
|
|
123
|
+
this.embedModel = embedModel;
|
|
124
|
+
const indexDir = join(homedir(), '.cmdr', 'index');
|
|
125
|
+
// Use cwd hash for unique index per project
|
|
126
|
+
const { createHash } = require('node:crypto');
|
|
127
|
+
const hash = createHash('sha256').update(cwd).digest('hex').slice(0, 12);
|
|
128
|
+
this.indexPath = join(indexDir, `${hash}.json`);
|
|
129
|
+
}
|
|
130
|
+
/** Load existing index from disk. */
|
|
131
|
+
async load() {
|
|
132
|
+
if (this.state)
|
|
133
|
+
return this.state;
|
|
134
|
+
try {
|
|
135
|
+
const raw = await readFile(this.indexPath, 'utf-8');
|
|
136
|
+
this.state = JSON.parse(raw);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
this.state = {
|
|
140
|
+
version: 1,
|
|
141
|
+
model: this.embedModel,
|
|
142
|
+
chunks: [],
|
|
143
|
+
indexedFiles: {},
|
|
144
|
+
createdAt: new Date().toISOString(),
|
|
145
|
+
updatedAt: new Date().toISOString(),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return this.state;
|
|
149
|
+
}
|
|
150
|
+
/** Save index to disk. */
|
|
151
|
+
async save() {
|
|
152
|
+
if (!this.state)
|
|
153
|
+
return;
|
|
154
|
+
this.state.updatedAt = new Date().toISOString();
|
|
155
|
+
const dir = join(homedir(), '.cmdr', 'index');
|
|
156
|
+
await mkdir(dir, { recursive: true });
|
|
157
|
+
await writeFile(this.indexPath, JSON.stringify(this.state));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Index files at the given paths (relative to cwd).
|
|
161
|
+
* Returns number of chunks indexed.
|
|
162
|
+
*/
|
|
163
|
+
async index(paths, onProgress) {
|
|
164
|
+
const state = await this.load();
|
|
165
|
+
const files = await discoverFiles(this.cwd, paths);
|
|
166
|
+
let totalNew = 0;
|
|
167
|
+
for (const file of files) {
|
|
168
|
+
const relPath = relative(this.cwd, file);
|
|
169
|
+
const fileStat = await stat(file);
|
|
170
|
+
const mtime = fileStat.mtimeMs;
|
|
171
|
+
// Skip if already indexed and not modified
|
|
172
|
+
const existing = state.indexedFiles[relPath];
|
|
173
|
+
if (existing && existing.mtime >= mtime)
|
|
174
|
+
continue;
|
|
175
|
+
onProgress?.(`Indexing: ${relPath}`);
|
|
176
|
+
// Remove old chunks for this file
|
|
177
|
+
state.chunks = state.chunks.filter(c => c.file !== relPath);
|
|
178
|
+
// Read and chunk
|
|
179
|
+
const content = await readFile(file, 'utf-8');
|
|
180
|
+
const textChunks = chunkText(content, relPath);
|
|
181
|
+
// Get embeddings
|
|
182
|
+
for (const chunk of textChunks) {
|
|
183
|
+
try {
|
|
184
|
+
const embedding = await getEmbedding(chunk.text, this.ollamaUrl, this.embedModel);
|
|
185
|
+
state.chunks.push({ ...chunk, embedding });
|
|
186
|
+
totalNew++;
|
|
187
|
+
}
|
|
188
|
+
catch (err) {
|
|
189
|
+
onProgress?.(` ⚠ Embedding failed for ${relPath}:${chunk.startLine}: ${err instanceof Error ? err.message : String(err)}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
state.indexedFiles[relPath] = { mtime, chunkCount: textChunks.length };
|
|
193
|
+
}
|
|
194
|
+
await this.save();
|
|
195
|
+
onProgress?.(`Done: ${totalNew} new chunks indexed (${state.chunks.length} total)`);
|
|
196
|
+
return totalNew;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Semantic search across indexed documents.
|
|
200
|
+
* Returns top-k results sorted by similarity.
|
|
201
|
+
*/
|
|
202
|
+
async search(query, topK = 5) {
|
|
203
|
+
const state = await this.load();
|
|
204
|
+
if (state.chunks.length === 0) {
|
|
205
|
+
return [];
|
|
206
|
+
}
|
|
207
|
+
const queryEmbedding = await getEmbedding(query, this.ollamaUrl, this.embedModel);
|
|
208
|
+
const scored = state.chunks.map(chunk => ({
|
|
209
|
+
file: chunk.file,
|
|
210
|
+
startLine: chunk.startLine,
|
|
211
|
+
endLine: chunk.endLine,
|
|
212
|
+
text: chunk.text,
|
|
213
|
+
score: cosineSimilarity(queryEmbedding, chunk.embedding),
|
|
214
|
+
}));
|
|
215
|
+
scored.sort((a, b) => b.score - a.score);
|
|
216
|
+
return scored.slice(0, topK);
|
|
217
|
+
}
|
|
218
|
+
/** Get index status/stats. */
|
|
219
|
+
async status() {
|
|
220
|
+
const state = await this.load();
|
|
221
|
+
return {
|
|
222
|
+
fileCount: Object.keys(state.indexedFiles).length,
|
|
223
|
+
chunkCount: state.chunks.length,
|
|
224
|
+
model: state.model,
|
|
225
|
+
updatedAt: state.updatedAt,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/** Clear the entire index. */
|
|
229
|
+
async clear() {
|
|
230
|
+
this.state = {
|
|
231
|
+
version: 1,
|
|
232
|
+
model: this.embedModel,
|
|
233
|
+
chunks: [],
|
|
234
|
+
indexedFiles: {},
|
|
235
|
+
createdAt: new Date().toISOString(),
|
|
236
|
+
updatedAt: new Date().toISOString(),
|
|
237
|
+
};
|
|
238
|
+
await this.save();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
//# sourceMappingURL=index-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-manager.js","sourceRoot":"","sources":["../../../src/memory/index-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cmdr serve — HTTP/SSE server exposing the agent API.
|
|
3
|
+
*
|
|
4
|
+
* Endpoints:
|
|
5
|
+
* POST /v1/chat — JSON request/response
|
|
6
|
+
* POST /v1/stream — SSE streaming response
|
|
7
|
+
* GET /health — health check
|
|
8
|
+
* GET /v1/models — list available models
|
|
9
|
+
*
|
|
10
|
+
* Uses Node.js built-in http module (no Express).
|
|
11
|
+
*/
|
|
12
|
+
export interface ServeOptions {
|
|
13
|
+
port: number;
|
|
14
|
+
host: string;
|
|
15
|
+
model: string;
|
|
16
|
+
ollamaUrl: string;
|
|
17
|
+
provider?: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function startServer(options: ServeOptions): Promise<void>;
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAgBH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAgDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAwPtE"}
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cmdr serve — HTTP/SSE server exposing the agent API.
|
|
3
|
+
*
|
|
4
|
+
* Endpoints:
|
|
5
|
+
* POST /v1/chat — JSON request/response
|
|
6
|
+
* POST /v1/stream — SSE streaming response
|
|
7
|
+
* GET /health — health check
|
|
8
|
+
* GET /v1/models — list available models
|
|
9
|
+
*
|
|
10
|
+
* Uses Node.js built-in http module (no Express).
|
|
11
|
+
*/
|
|
12
|
+
import { createServer } from 'node:http';
|
|
13
|
+
import { Agent } from '../core/agent.js';
|
|
14
|
+
import { createAdapter, detectProviderFromModel } from '../llm/provider-factory.js';
|
|
15
|
+
import { ToolRegistry } from '../tools/registry.js';
|
|
16
|
+
import { registerBuiltInTools } from '../tools/built-in/index.js';
|
|
17
|
+
import { PermissionManager } from '../core/permissions.js';
|
|
18
|
+
import { SOLO_CODER } from '../core/presets.js';
|
|
19
|
+
import { discoverProject } from '../session/project-context.js';
|
|
20
|
+
import { buildSystemPrompt } from '../session/prompt-builder.js';
|
|
21
|
+
import { discoverOllamaModels } from '../llm/model-registry.js';
|
|
22
|
+
import { MemoryManager } from '../memory/memory-manager.js';
|
|
23
|
+
/** Read request body as JSON, with a size limit. */
|
|
24
|
+
async function readBody(req, maxBytes = 10 * 1024 * 1024) {
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
const chunks = [];
|
|
27
|
+
let size = 0;
|
|
28
|
+
req.on('data', (chunk) => {
|
|
29
|
+
size += chunk.length;
|
|
30
|
+
if (size > maxBytes) {
|
|
31
|
+
req.destroy();
|
|
32
|
+
reject(new Error('Request body too large'));
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
chunks.push(chunk);
|
|
36
|
+
});
|
|
37
|
+
req.on('end', () => {
|
|
38
|
+
try {
|
|
39
|
+
const body = Buffer.concat(chunks).toString('utf-8');
|
|
40
|
+
resolve(JSON.parse(body));
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
reject(new Error('Invalid JSON'));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
req.on('error', reject);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function sendJson(res, status, data) {
|
|
50
|
+
const body = JSON.stringify(data);
|
|
51
|
+
res.writeHead(status, {
|
|
52
|
+
'Content-Type': 'application/json',
|
|
53
|
+
'Content-Length': Buffer.byteLength(body),
|
|
54
|
+
});
|
|
55
|
+
res.end(body);
|
|
56
|
+
}
|
|
57
|
+
function sendError(res, status, message) {
|
|
58
|
+
sendJson(res, status, { error: message });
|
|
59
|
+
}
|
|
60
|
+
/** Set CORS headers for development use. */
|
|
61
|
+
function setCorsHeaders(res) {
|
|
62
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
63
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
64
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
65
|
+
}
|
|
66
|
+
export async function startServer(options) {
|
|
67
|
+
const cwd = process.cwd();
|
|
68
|
+
// Resolve provider
|
|
69
|
+
const provider = options.provider
|
|
70
|
+
?? detectProviderFromModel(options.model)
|
|
71
|
+
?? 'ollama';
|
|
72
|
+
const adapter = createAdapter({ provider, ollamaUrl: options.ollamaUrl });
|
|
73
|
+
// Discover project context
|
|
74
|
+
const projectContext = await discoverProject(cwd);
|
|
75
|
+
// Memory
|
|
76
|
+
const memoryManager = new MemoryManager(cwd);
|
|
77
|
+
const memoryPrompt = await memoryManager.getMemoryPrompt();
|
|
78
|
+
// System prompt
|
|
79
|
+
const systemPrompt = buildSystemPrompt({
|
|
80
|
+
basePrompt: SOLO_CODER.systemPrompt,
|
|
81
|
+
projectContext,
|
|
82
|
+
model: options.model,
|
|
83
|
+
memoryPrompt: memoryPrompt || undefined,
|
|
84
|
+
});
|
|
85
|
+
// Tools
|
|
86
|
+
const toolRegistry = new ToolRegistry();
|
|
87
|
+
registerBuiltInTools(toolRegistry);
|
|
88
|
+
// Permission manager (yolo for server mode — approvals aren't interactive)
|
|
89
|
+
const permissionManager = new PermissionManager('yolo');
|
|
90
|
+
// Discover models for Ollama
|
|
91
|
+
if (provider === 'ollama') {
|
|
92
|
+
const ollamaAdapter = adapter;
|
|
93
|
+
const healthy = await ollamaAdapter.healthCheck();
|
|
94
|
+
if (!healthy) {
|
|
95
|
+
console.error(`Cannot connect to Ollama at ${options.ollamaUrl}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
await discoverOllamaModels(options.ollamaUrl);
|
|
99
|
+
}
|
|
100
|
+
/** Create a fresh agent for each request. */
|
|
101
|
+
function createAgent(model) {
|
|
102
|
+
const allowedToolNames = Array.from(new Set([
|
|
103
|
+
...(SOLO_CODER.tools ?? []),
|
|
104
|
+
...toolRegistry.list().map(t => t.name),
|
|
105
|
+
]));
|
|
106
|
+
return new Agent({
|
|
107
|
+
...SOLO_CODER,
|
|
108
|
+
model,
|
|
109
|
+
systemPrompt,
|
|
110
|
+
tools: allowedToolNames,
|
|
111
|
+
}, adapter, toolRegistry, cwd, permissionManager);
|
|
112
|
+
}
|
|
113
|
+
const server = createServer(async (req, res) => {
|
|
114
|
+
setCorsHeaders(res);
|
|
115
|
+
// Handle CORS preflight
|
|
116
|
+
if (req.method === 'OPTIONS') {
|
|
117
|
+
res.writeHead(204);
|
|
118
|
+
res.end();
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const url = req.url ?? '/';
|
|
122
|
+
try {
|
|
123
|
+
// GET /health
|
|
124
|
+
if (req.method === 'GET' && url === '/health') {
|
|
125
|
+
sendJson(res, 200, {
|
|
126
|
+
status: 'ok',
|
|
127
|
+
model: options.model,
|
|
128
|
+
provider,
|
|
129
|
+
uptime: process.uptime(),
|
|
130
|
+
});
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// GET /v1/models
|
|
134
|
+
if (req.method === 'GET' && url === '/v1/models') {
|
|
135
|
+
if (provider === 'ollama') {
|
|
136
|
+
const ollamaAdapter = adapter;
|
|
137
|
+
const models = await ollamaAdapter.listModels();
|
|
138
|
+
sendJson(res, 200, { models });
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
sendJson(res, 200, { models: [options.model] });
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// POST /v1/chat — non-streaming chat
|
|
146
|
+
if (req.method === 'POST' && url === '/v1/chat') {
|
|
147
|
+
const body = await readBody(req);
|
|
148
|
+
if (!body?.message) {
|
|
149
|
+
sendError(res, 400, 'Missing "message" field');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
const model = body.model ?? options.model;
|
|
153
|
+
const agent = createAgent(model);
|
|
154
|
+
const startTime = Date.now();
|
|
155
|
+
const toolsCalled = [];
|
|
156
|
+
let currentToolStart = 0;
|
|
157
|
+
let currentToolName = '';
|
|
158
|
+
let fullOutput = '';
|
|
159
|
+
for await (const event of agent.stream(body.message)) {
|
|
160
|
+
switch (event.type) {
|
|
161
|
+
case 'text':
|
|
162
|
+
fullOutput += event.data;
|
|
163
|
+
break;
|
|
164
|
+
case 'tool_use': {
|
|
165
|
+
const block = event.data;
|
|
166
|
+
currentToolName = block.name;
|
|
167
|
+
currentToolStart = Date.now();
|
|
168
|
+
break;
|
|
169
|
+
}
|
|
170
|
+
case 'tool_result': {
|
|
171
|
+
toolsCalled.push({ name: currentToolName, duration_ms: Date.now() - currentToolStart });
|
|
172
|
+
currentToolName = '';
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
const state = agent.getState();
|
|
178
|
+
const tokens = state.tokenUsage;
|
|
179
|
+
sendJson(res, 200, {
|
|
180
|
+
model,
|
|
181
|
+
response: fullOutput,
|
|
182
|
+
tools_called: toolsCalled,
|
|
183
|
+
tokens: { input: tokens.input_tokens, output: tokens.output_tokens },
|
|
184
|
+
duration_ms: Date.now() - startTime,
|
|
185
|
+
});
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// POST /v1/stream — SSE streaming
|
|
189
|
+
if (req.method === 'POST' && url === '/v1/stream') {
|
|
190
|
+
const body = await readBody(req);
|
|
191
|
+
if (!body?.message) {
|
|
192
|
+
sendError(res, 400, 'Missing "message" field');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const model = body.model ?? options.model;
|
|
196
|
+
const agent = createAgent(model);
|
|
197
|
+
const startTime = Date.now();
|
|
198
|
+
const toolsCalled = [];
|
|
199
|
+
let currentToolStart = 0;
|
|
200
|
+
let currentToolName = '';
|
|
201
|
+
res.writeHead(200, {
|
|
202
|
+
'Content-Type': 'text/event-stream',
|
|
203
|
+
'Cache-Control': 'no-cache',
|
|
204
|
+
'Connection': 'keep-alive',
|
|
205
|
+
'Access-Control-Allow-Origin': '*',
|
|
206
|
+
});
|
|
207
|
+
const sendEvent = (data) => {
|
|
208
|
+
res.write(`data: ${JSON.stringify(data)}\n\n`);
|
|
209
|
+
};
|
|
210
|
+
try {
|
|
211
|
+
for await (const event of agent.stream(body.message)) {
|
|
212
|
+
switch (event.type) {
|
|
213
|
+
case 'text':
|
|
214
|
+
sendEvent({ type: 'text', data: event.data, timestamp: Date.now() });
|
|
215
|
+
break;
|
|
216
|
+
case 'tool_use': {
|
|
217
|
+
const block = event.data;
|
|
218
|
+
currentToolName = block.name;
|
|
219
|
+
currentToolStart = Date.now();
|
|
220
|
+
sendEvent({ type: 'tool_use', tool: block.name, input: block.input, timestamp: Date.now() });
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
case 'tool_result': {
|
|
224
|
+
const block = event.data;
|
|
225
|
+
const duration_ms = Date.now() - currentToolStart;
|
|
226
|
+
toolsCalled.push({ name: currentToolName, duration_ms });
|
|
227
|
+
sendEvent({ type: 'tool_result', tool: currentToolName, output: block.content, is_error: block.is_error, duration_ms, timestamp: Date.now() });
|
|
228
|
+
currentToolName = '';
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
case 'error': {
|
|
232
|
+
const err = event.data;
|
|
233
|
+
sendEvent({ type: 'error', message: err.message, timestamp: Date.now() });
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
241
|
+
sendEvent({ type: 'error', message: msg, timestamp: Date.now() });
|
|
242
|
+
}
|
|
243
|
+
const state = agent.getState();
|
|
244
|
+
const tokens = state.tokenUsage;
|
|
245
|
+
sendEvent({
|
|
246
|
+
type: 'done',
|
|
247
|
+
tokens: { input: tokens.input_tokens, output: tokens.output_tokens },
|
|
248
|
+
duration_ms: Date.now() - startTime,
|
|
249
|
+
});
|
|
250
|
+
res.end();
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// 404 for anything else
|
|
254
|
+
sendError(res, 404, `Not found: ${url}`);
|
|
255
|
+
}
|
|
256
|
+
catch (err) {
|
|
257
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
258
|
+
console.error(`[cmdr serve] Error:`, msg);
|
|
259
|
+
if (!res.headersSent) {
|
|
260
|
+
sendError(res, 500, msg);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
server.listen(options.port, options.host, () => {
|
|
265
|
+
console.log(`cmdr serve running on http://${options.host}:${options.port}`);
|
|
266
|
+
console.log(` Model: ${options.model}`);
|
|
267
|
+
console.log(` Provider: ${provider}`);
|
|
268
|
+
console.log(` Endpoints:`);
|
|
269
|
+
console.log(` GET /health — health check`);
|
|
270
|
+
console.log(` GET /v1/models — list models`);
|
|
271
|
+
console.log(` POST /v1/chat — JSON chat`);
|
|
272
|
+
console.log(` POST /v1/stream — SSE streaming`);
|
|
273
|
+
});
|
|
274
|
+
// Graceful shutdown
|
|
275
|
+
const shutdown = () => {
|
|
276
|
+
console.log('\nShutting down cmdr serve...');
|
|
277
|
+
server.close();
|
|
278
|
+
process.exit(0);
|
|
279
|
+
};
|
|
280
|
+
process.on('SIGINT', shutdown);
|
|
281
|
+
process.on('SIGTERM', shutdown);
|
|
282
|
+
}
|
|
283
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BranchManager — fork, switch, and merge conversation branches.
|
|
3
|
+
*
|
|
4
|
+
* Branches are persisted as JSON files under:
|
|
5
|
+
* ~/.cmdr/sessions/{sessionId}/branches/{branch-id}.json
|
|
6
|
+
*
|
|
7
|
+
* Each branch stores a snapshot of the conversation at fork time,
|
|
8
|
+
* plus any messages added after the fork.
|
|
9
|
+
*/
|
|
10
|
+
import type { LLMMessage } from '../core/types.js';
|
|
11
|
+
export interface Branch {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly model: string;
|
|
15
|
+
readonly messages: LLMMessage[];
|
|
16
|
+
readonly messageCount: number;
|
|
17
|
+
readonly createdAt: string;
|
|
18
|
+
readonly parentBranch?: string;
|
|
19
|
+
readonly forkPoint: number;
|
|
20
|
+
}
|
|
21
|
+
export declare class BranchManager {
|
|
22
|
+
private sessionId;
|
|
23
|
+
private currentBranchId;
|
|
24
|
+
constructor(sessionId: string);
|
|
25
|
+
get activeBranch(): string | null;
|
|
26
|
+
/** Fork the conversation at the current point. */
|
|
27
|
+
fork(name: string, messages: LLMMessage[], model: string, parentBranch?: string): Promise<Branch>;
|
|
28
|
+
/** Switch to a branch by ID. Returns the branch data or null. */
|
|
29
|
+
switch(id: string): Promise<Branch | null>;
|
|
30
|
+
/** List all branches for this session, newest first. */
|
|
31
|
+
list(): Promise<Omit<Branch, 'messages'>[]>;
|
|
32
|
+
/** Merge branch messages onto a target (appends after fork point). */
|
|
33
|
+
merge(sourceId: string, targetMessages: LLMMessage[]): Promise<LLMMessage[] | null>;
|
|
34
|
+
/** Delete a branch by ID. */
|
|
35
|
+
delete(id: string): Promise<boolean>;
|
|
36
|
+
/** Save the current state of a branch (update messages). */
|
|
37
|
+
update(id: string, messages: LLMMessage[], model: string): Promise<boolean>;
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=branch-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branch-manager.d.ts","sourceRoot":"","sources":["../../../src/session/branch-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAKlD,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;IAC/B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;CAC3B;AAMD,qBAAa,aAAa;IACxB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,eAAe,CAAsB;gBAEjC,SAAS,EAAE,MAAM;IAI7B,IAAI,YAAY,IAAI,MAAM,GAAG,IAAI,CAEhC;IAED,kDAAkD;IAC5C,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAqBvG,iEAAiE;IAC3D,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAchD,wDAAwD;IAClD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC;IAmCjD,sEAAsE;IAChE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC;IASzF,6BAA6B;IACvB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAa1C,4DAA4D;IACtD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAmBlF"}
|