pudu-ai 0.2.21 → 0.2.23
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 +2 -0
- package/dist/cli/chunk-473MNTMS.js +24 -0
- package/dist/cli/chunk-47LK5I2X.js +39 -0
- package/dist/cli/{chunk-QGEXKMQ5.js → chunk-4VEDGI4L.js} +56 -31
- package/dist/cli/{chunk-6C5EPKRI.js → chunk-6BGYI7XY.js} +7 -38
- package/dist/cli/chunk-7KPNGK6A.js +61 -0
- package/dist/cli/chunk-XEEGLMVT.js +25 -0
- package/dist/cli/harness-launch-KEUKZZXH.js +118 -0
- package/dist/cli/index.js +17 -7
- package/dist/cli/{render-3UBPZNF4.js → render-Y4DNH7FW.js} +279 -26
- package/dist/cli/{repo-cli-4GSG5MD3.js → repo-cli-BK76EVBZ.js} +90 -71
- package/package.json +1 -1
- package/python/pudu_agent/__main__.py +7 -1
- package/python/pudu_agent/__pycache__/__init__.cpython-312.pyc +0 -0
- package/python/pudu_agent/__pycache__/__main__.cpython-312.pyc +0 -0
- package/python/pudu_agent/__pycache__/ast_search.cpython-312.pyc +0 -0
- package/python/pudu_agent/__pycache__/graph.cpython-312.pyc +0 -0
- package/python/pudu_agent/__pycache__/protocol.cpython-312.pyc +0 -0
- package/python/pudu_agent/__pycache__/search.cpython-312.pyc +0 -0
- package/python/pudu_agent/graph.py +398 -0
- package/python/pudu_agent/mcp_server.py +140 -0
- package/python/pudu_agent/search.py +61 -15
package/README.md
CHANGED
|
@@ -148,6 +148,8 @@ Deterministic repository search. Optional Python 3.10+, `rg`, and `ast-grep`. No
|
|
|
148
148
|
```bash
|
|
149
149
|
npx pudu-ai repo search validate_user --repo . --json
|
|
150
150
|
npx pudu-ai repo search --structural 'def $FUNC($$$ARGS): $$$BODY'
|
|
151
|
+
npx pudu-ai repo graph --repo .
|
|
152
|
+
npx pudu-ai repo harness --task "fix validate_user" --json
|
|
151
153
|
npx pudu-ai repo search validate_user --lang es
|
|
152
154
|
```
|
|
153
155
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// src/shared/format.ts
|
|
2
|
+
function formatNumber(value, digits = 1) {
|
|
3
|
+
if (value === void 0 || Number.isNaN(value)) return "N/A";
|
|
4
|
+
return value.toFixed(digits);
|
|
5
|
+
}
|
|
6
|
+
function formatPercent(value, digits = 0) {
|
|
7
|
+
if (value === void 0 || Number.isNaN(value)) return "N/A";
|
|
8
|
+
return `${value.toFixed(digits)} %`;
|
|
9
|
+
}
|
|
10
|
+
function formatTokensPerSec(value, digits = 1) {
|
|
11
|
+
if (value === void 0 || Number.isNaN(value)) return "N/A";
|
|
12
|
+
return `${value.toFixed(digits)} t/s`;
|
|
13
|
+
}
|
|
14
|
+
function na(value) {
|
|
15
|
+
if (value === void 0 || value === null || value === "") return "N/A";
|
|
16
|
+
return String(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
formatNumber,
|
|
21
|
+
formatPercent,
|
|
22
|
+
formatTokensPerSec,
|
|
23
|
+
na
|
|
24
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/integrations/catalog.ts
|
|
2
|
+
var INTEGRATIONS = {
|
|
3
|
+
opencode: {
|
|
4
|
+
id: "opencode",
|
|
5
|
+
docsUrl: "https://docs.ollama.com/integrations/opencode",
|
|
6
|
+
ollamaLaunch: "opencode",
|
|
7
|
+
minGenerationTps: 12,
|
|
8
|
+
allowedGrades: ["S", "A", "B"],
|
|
9
|
+
useCases: ["code", "chat", "reasoning"]
|
|
10
|
+
},
|
|
11
|
+
openclaw: {
|
|
12
|
+
id: "openclaw",
|
|
13
|
+
docsUrl: "https://docs.ollama.com/integrations/openclaw",
|
|
14
|
+
ollamaLaunch: "openclaw",
|
|
15
|
+
minGenerationTps: 12,
|
|
16
|
+
allowedGrades: ["S", "A", "B"],
|
|
17
|
+
useCases: ["code", "chat", "reasoning"]
|
|
18
|
+
},
|
|
19
|
+
hermes: {
|
|
20
|
+
id: "hermes",
|
|
21
|
+
docsUrl: "https://docs.ollama.com/integrations/hermes",
|
|
22
|
+
ollamaLaunch: "hermes",
|
|
23
|
+
minGenerationTps: 12,
|
|
24
|
+
allowedGrades: ["S", "A"],
|
|
25
|
+
useCases: ["code", "chat", "reasoning"]
|
|
26
|
+
},
|
|
27
|
+
claude: {
|
|
28
|
+
id: "claude",
|
|
29
|
+
docsUrl: "https://docs.ollama.com/integrations/claude-code",
|
|
30
|
+
ollamaLaunch: "claude",
|
|
31
|
+
minGenerationTps: 12,
|
|
32
|
+
allowedGrades: ["S", "A", "B"],
|
|
33
|
+
useCases: ["code", "chat", "reasoning"]
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
INTEGRATIONS
|
|
39
|
+
};
|
|
@@ -30,16 +30,36 @@ var en = {
|
|
|
30
30
|
aboutRepoSearch: "Deterministic repository search (Pudu Agent Lab). Literal strings use rg; syntactic patterns use ast-grep. No LLM. Missing tools are reported, never faked.",
|
|
31
31
|
repoSearchTitle: "Pudu Agent Lab \u2014 repo search",
|
|
32
32
|
repoNeedQuery: "Pass a query or --structural PATTERN, e.g. npx pudu-ai repo search validate_user --json",
|
|
33
|
-
repoUnknownSubcommand: "Unknown repo subcommand: {sub}.
|
|
33
|
+
repoUnknownSubcommand: "Unknown repo subcommand: {sub}. Supported: search, graph, harness.",
|
|
34
34
|
repoPythonMissing: "python3 not found. Agent Lab search needs Python 3.10+ on PATH.",
|
|
35
35
|
repoToolMissing: "{tool} not found on PATH",
|
|
36
36
|
repoNoMatches: "No matches.",
|
|
37
|
-
|
|
37
|
+
repoNeedTask: 'Pass a task, e.g. npx pudu-ai repo harness --task "fix validate_user" --repo .',
|
|
38
|
+
repoGraphTitle: "AGENT LAB \u2014 IMPLEMENTATION GRAPH",
|
|
39
|
+
aboutRepoGraph: "Python stdlib AST graph (imports, defs, calls, inherits). Optional Graphify file if present. Edges are EXTRACTED or RESOLVED. Never invented.",
|
|
40
|
+
repoGraphBuilding: "Building graph of this folder\u2026",
|
|
41
|
+
repoGraphEmpty: "No Python files with extractable structure.",
|
|
42
|
+
repoGraphFiles: "Files",
|
|
43
|
+
repoGraphNodes: "Nodes",
|
|
44
|
+
repoGraphEdges: "Edges",
|
|
45
|
+
repoTokens: "Token estimate",
|
|
46
|
+
repoEffortTitle: "TASK EFFORT (DERIVED)",
|
|
47
|
+
repoHarnessPrompt: "HARNESS TASK",
|
|
48
|
+
repoHarnessPromptHint: "Type the coding task, then Enter",
|
|
49
|
+
repoHarnessEnter: "Enter = pick local model from effort Esc = back",
|
|
50
|
+
repoHarnessNoPrompt: "(empty prompt \u2014 effort is still from the graph)",
|
|
51
|
+
repoHarnessModel: "BEST LOCAL MODEL FOR THIS TASK",
|
|
52
|
+
repoHarnessKeys: "1 OpenCode 2 Hermes 3 OpenClaw Enter=OpenCode Esc=back",
|
|
53
|
+
repoHarnessNoTag: "No Ollama tag for this model. N/A \u2014 will not invent a launch command.",
|
|
54
|
+
repoHarnessWroteConfig: "Wrote OpenCode config {file} (local model + pudu-ai MCP tools).",
|
|
55
|
+
repoHelp: `Pudu Agent Lab \u2014 repository search, graph, harness
|
|
38
56
|
|
|
39
57
|
Usage:
|
|
40
58
|
npx pudu-ai repo search QUERY
|
|
41
59
|
npx pudu-ai repo search QUERY --repo PATH --json
|
|
42
60
|
npx pudu-ai repo search --structural 'def $FUNC($$$ARGS): $$$BODY'
|
|
61
|
+
npx pudu-ai repo graph --repo PATH
|
|
62
|
+
npx pudu-ai repo harness --task "fix auth" --repo PATH --json
|
|
43
63
|
|
|
44
64
|
Flags:
|
|
45
65
|
--repo PATH Repository root (default: cwd)
|
|
@@ -47,7 +67,8 @@ Flags:
|
|
|
47
67
|
--intent INTENT TEXT | STRUCTURAL | RELATIONSHIP | IMPACT | SEMANTIC | UNKNOWN
|
|
48
68
|
--glob GLOB Repeatable include glob
|
|
49
69
|
--limit N Max matches (default 100)
|
|
50
|
-
--
|
|
70
|
+
--task TEXT Harness task prompt
|
|
71
|
+
--json Machine-readable JSON
|
|
51
72
|
`,
|
|
52
73
|
nav: "[S] Setup/Install [B] Benchmark [M] Models [R] Recommend [T] Tasks [H] Hardware [C] Compare [L] History [Q] Quit",
|
|
53
74
|
setupTitle: "SETUP \u2014 INSTALL MODELS & AGENTS",
|
|
@@ -197,6 +218,8 @@ Usage:
|
|
|
197
218
|
npx pudu-ai launch opencode --yes
|
|
198
219
|
npx pudu-ai repo search QUERY
|
|
199
220
|
npx pudu-ai repo search --structural 'def $FUNC($$$ARGS): $$$BODY'
|
|
221
|
+
npx pudu-ai repo graph
|
|
222
|
+
npx pudu-ai repo harness --task "fix auth"
|
|
200
223
|
|
|
201
224
|
Flags:
|
|
202
225
|
--json Machine-readable JSON (no TUI)
|
|
@@ -215,6 +238,7 @@ Flags:
|
|
|
215
238
|
--intent TEXT | STRUCTURAL | RELATIONSHIP | IMPACT | SEMANTIC | UNKNOWN
|
|
216
239
|
--glob Repeatable include glob for repo search
|
|
217
240
|
--limit Max repo search matches
|
|
241
|
+
--task Harness task prompt
|
|
218
242
|
|
|
219
243
|
Credits:
|
|
220
244
|
Measured values come from llama-bench and OS telemetry.
|
|
@@ -256,24 +280,45 @@ var es = {
|
|
|
256
280
|
aboutRepoSearch: "B\xFAsqueda determinista de repositorio (Pudu Agent Lab). Cadenas literales: rg; patrones sint\xE1cticos: ast-grep. Sin LLM. Si falta una herramienta se informa, no se inventa.",
|
|
257
281
|
repoSearchTitle: "Pudu Agent Lab \u2014 b\xFAsqueda de repo",
|
|
258
282
|
repoNeedQuery: "Pasa una consulta o --structural PATR\xD3N, p. ej. npx pudu-ai repo search validate_user --json",
|
|
259
|
-
repoUnknownSubcommand: "Subcomando de repo desconocido: {sub}.
|
|
283
|
+
repoUnknownSubcommand: "Subcomando de repo desconocido: {sub}. Soportados: search, graph, harness.",
|
|
260
284
|
repoPythonMissing: "No se encontr\xF3 python3. La b\xFAsqueda de Agent Lab necesita Python 3.10+ en PATH.",
|
|
261
285
|
repoToolMissing: "{tool} no est\xE1 en PATH",
|
|
262
286
|
repoNoMatches: "Sin coincidencias.",
|
|
263
|
-
|
|
287
|
+
repoNeedTask: 'Pasa una tarea, p. ej. npx pudu-ai repo harness --task "arreglar validate_user" --repo .',
|
|
288
|
+
repoGraphTitle: "AGENT LAB \u2014 GRAFO DE IMPLEMENTACI\xD3N",
|
|
289
|
+
aboutRepoGraph: "Grafo AST de Python (imports, defs, llamadas, herencias). Graphify opcional si existe el archivo. Aristas EXTRACTED o RESOLVED. Nunca inventadas.",
|
|
290
|
+
repoGraphBuilding: "Construyendo el grafo de esta carpeta\u2026",
|
|
291
|
+
repoGraphEmpty: "No hay Python con estructura extra\xEDble.",
|
|
292
|
+
repoGraphFiles: "Archivos",
|
|
293
|
+
repoGraphNodes: "Nodos",
|
|
294
|
+
repoGraphEdges: "Aristas",
|
|
295
|
+
repoTokens: "Estimaci\xF3n de tokens",
|
|
296
|
+
repoEffortTitle: "ESFUERZO DE TAREA (DERIVED)",
|
|
297
|
+
repoHarnessPrompt: "TAREA HARNESS",
|
|
298
|
+
repoHarnessPromptHint: "Escribe la tarea de c\xF3digo y pulsa Enter",
|
|
299
|
+
repoHarnessEnter: "Enter = elegir modelo local seg\xFAn esfuerzo Esc = atr\xE1s",
|
|
300
|
+
repoHarnessNoPrompt: "(prompt vac\xEDo \u2014 el esfuerzo sigue saliendo del grafo)",
|
|
301
|
+
repoHarnessModel: "MEJOR MODELO LOCAL PARA ESTA TAREA",
|
|
302
|
+
repoHarnessKeys: "1 OpenCode 2 Hermes 3 OpenClaw Enter=OpenCode Esc=atr\xE1s",
|
|
303
|
+
repoHarnessNoTag: "Este modelo no tiene tag Ollama. N/A \u2014 no se inventa el comando de launch.",
|
|
304
|
+
repoHarnessWroteConfig: "Config OpenCode escrita en {file} (modelo local + tools MCP pudu-ai).",
|
|
305
|
+
repoHelp: `Pudu Agent Lab \u2014 b\xFAsqueda, grafo y harness
|
|
264
306
|
|
|
265
307
|
Uso:
|
|
266
308
|
npx pudu-ai repo search CONSULTA
|
|
267
309
|
npx pudu-ai repo search CONSULTA --repo RUTA --json
|
|
268
310
|
npx pudu-ai repo search --structural 'def $FUNC($$$ARGS): $$$BODY'
|
|
311
|
+
npx pudu-ai repo graph --repo RUTA
|
|
312
|
+
npx pudu-ai repo harness --task "arreglar auth" --repo RUTA --json
|
|
269
313
|
|
|
270
314
|
Flags:
|
|
271
|
-
--repo PATH Ra\xEDz del repositorio (
|
|
272
|
-
--structural PAT Patr\xF3n
|
|
315
|
+
--repo PATH Ra\xEDz del repositorio (cwd)
|
|
316
|
+
--structural PAT Patr\xF3n ast-grep
|
|
273
317
|
--intent INTENT TEXT | STRUCTURAL | RELATIONSHIP | IMPACT | SEMANTIC | UNKNOWN
|
|
274
318
|
--glob GLOB Glob de inclusi\xF3n (repetible)
|
|
275
319
|
--limit N M\xE1ximo de coincidencias (100)
|
|
276
|
-
--
|
|
320
|
+
--task TEXT Prompt de la tarea harness
|
|
321
|
+
--json JSON
|
|
277
322
|
`,
|
|
278
323
|
nav: "[S] Setup/Instalar [B] Benchmark [M] Modelos [R] Recomendaciones [T] Tareas [H] Hardware [C] Comparar [L] Historial [Q] Salir",
|
|
279
324
|
setupTitle: "SETUP \u2014 INSTALAR MODELOS Y AGENTES",
|
|
@@ -423,6 +468,8 @@ Uso:
|
|
|
423
468
|
npx pudu-ai launch opencode --yes
|
|
424
469
|
npx pudu-ai repo search CONSULTA
|
|
425
470
|
npx pudu-ai repo search --structural 'def $FUNC($$$ARGS): $$$BODY'
|
|
471
|
+
npx pudu-ai repo graph
|
|
472
|
+
npx pudu-ai repo harness --task "arreglar auth"
|
|
426
473
|
|
|
427
474
|
Flags:
|
|
428
475
|
--json JSON legible por m\xE1quinas (sin TUI)
|
|
@@ -472,30 +519,8 @@ function t(id, vars) {
|
|
|
472
519
|
return text;
|
|
473
520
|
}
|
|
474
521
|
|
|
475
|
-
// src/shared/format.ts
|
|
476
|
-
function formatNumber(value, digits = 1) {
|
|
477
|
-
if (value === void 0 || Number.isNaN(value)) return "N/A";
|
|
478
|
-
return value.toFixed(digits);
|
|
479
|
-
}
|
|
480
|
-
function formatPercent(value, digits = 0) {
|
|
481
|
-
if (value === void 0 || Number.isNaN(value)) return "N/A";
|
|
482
|
-
return `${value.toFixed(digits)} %`;
|
|
483
|
-
}
|
|
484
|
-
function formatTokensPerSec(value, digits = 1) {
|
|
485
|
-
if (value === void 0 || Number.isNaN(value)) return "N/A";
|
|
486
|
-
return `${value.toFixed(digits)} t/s`;
|
|
487
|
-
}
|
|
488
|
-
function na(value) {
|
|
489
|
-
if (value === void 0 || value === null || value === "") return "N/A";
|
|
490
|
-
return String(value);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
522
|
export {
|
|
494
523
|
resolveLocale,
|
|
495
524
|
setLocale,
|
|
496
|
-
t
|
|
497
|
-
formatNumber,
|
|
498
|
-
formatPercent,
|
|
499
|
-
formatTokensPerSec,
|
|
500
|
-
na
|
|
525
|
+
t
|
|
501
526
|
};
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INTEGRATIONS
|
|
3
|
+
} from "./chunk-47LK5I2X.js";
|
|
1
4
|
import {
|
|
2
5
|
formatNumber,
|
|
3
6
|
formatTokensPerSec,
|
|
4
|
-
na
|
|
7
|
+
na
|
|
8
|
+
} from "./chunk-473MNTMS.js";
|
|
9
|
+
import {
|
|
5
10
|
t
|
|
6
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-4VEDGI4L.js";
|
|
7
12
|
import {
|
|
8
13
|
commandExists,
|
|
9
14
|
runCommand,
|
|
@@ -1053,42 +1058,6 @@ function tasksText(plans, answers) {
|
|
|
1053
1058
|
return [...header, ...blocks].join("\n\n");
|
|
1054
1059
|
}
|
|
1055
1060
|
|
|
1056
|
-
// src/integrations/catalog.ts
|
|
1057
|
-
var INTEGRATIONS = {
|
|
1058
|
-
opencode: {
|
|
1059
|
-
id: "opencode",
|
|
1060
|
-
docsUrl: "https://docs.ollama.com/integrations/opencode",
|
|
1061
|
-
ollamaLaunch: "opencode",
|
|
1062
|
-
minGenerationTps: 12,
|
|
1063
|
-
allowedGrades: ["S", "A", "B"],
|
|
1064
|
-
useCases: ["code", "chat", "reasoning"]
|
|
1065
|
-
},
|
|
1066
|
-
openclaw: {
|
|
1067
|
-
id: "openclaw",
|
|
1068
|
-
docsUrl: "https://docs.ollama.com/integrations/openclaw",
|
|
1069
|
-
ollamaLaunch: "openclaw",
|
|
1070
|
-
minGenerationTps: 12,
|
|
1071
|
-
allowedGrades: ["S", "A", "B"],
|
|
1072
|
-
useCases: ["code", "chat", "reasoning"]
|
|
1073
|
-
},
|
|
1074
|
-
hermes: {
|
|
1075
|
-
id: "hermes",
|
|
1076
|
-
docsUrl: "https://docs.ollama.com/integrations/hermes",
|
|
1077
|
-
ollamaLaunch: "hermes",
|
|
1078
|
-
minGenerationTps: 12,
|
|
1079
|
-
allowedGrades: ["S", "A"],
|
|
1080
|
-
useCases: ["code", "chat", "reasoning"]
|
|
1081
|
-
},
|
|
1082
|
-
claude: {
|
|
1083
|
-
id: "claude",
|
|
1084
|
-
docsUrl: "https://docs.ollama.com/integrations/claude-code",
|
|
1085
|
-
ollamaLaunch: "claude",
|
|
1086
|
-
minGenerationTps: 12,
|
|
1087
|
-
allowedGrades: ["S", "A", "B"],
|
|
1088
|
-
useCases: ["code", "chat", "reasoning"]
|
|
1089
|
-
}
|
|
1090
|
-
};
|
|
1091
|
-
|
|
1092
1061
|
// src/integrations/ollama-tags.ts
|
|
1093
1062
|
var RULES = [
|
|
1094
1063
|
{ test: /qwen\s*3\.5\s*[-:]?\s*8b/i, tag: "qwen3.5:8b" },
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
commandExists,
|
|
3
|
+
runCommand
|
|
4
|
+
} from "./chunk-SS7OXXQQ.js";
|
|
5
|
+
|
|
6
|
+
// src/agent-lab/python-bridge.ts
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
function hasEngine(dir) {
|
|
11
|
+
return existsSync(path.join(dir, "pudu_agent", "__main__.py"));
|
|
12
|
+
}
|
|
13
|
+
function pythonRoot() {
|
|
14
|
+
const starts = [path.dirname(fileURLToPath(import.meta.url)), process.cwd()];
|
|
15
|
+
for (const start of starts) {
|
|
16
|
+
let dir = start;
|
|
17
|
+
for (let i = 0; i < 8; i++) {
|
|
18
|
+
const candidate = path.join(dir, "python");
|
|
19
|
+
if (hasEngine(candidate)) return candidate;
|
|
20
|
+
const parent = path.dirname(dir);
|
|
21
|
+
if (parent === dir) break;
|
|
22
|
+
dir = parent;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "python");
|
|
26
|
+
}
|
|
27
|
+
async function findPython() {
|
|
28
|
+
return await commandExists("python3") ?? await commandExists("python");
|
|
29
|
+
}
|
|
30
|
+
async function invokePuduAgent(request, timeout = 3e4) {
|
|
31
|
+
const python = await findPython();
|
|
32
|
+
if (!python) {
|
|
33
|
+
throw new Error("python3 not found on PATH");
|
|
34
|
+
}
|
|
35
|
+
const root = pythonRoot();
|
|
36
|
+
const payload = `${JSON.stringify(request)}
|
|
37
|
+
`;
|
|
38
|
+
const result = await runCommand(python, ["-m", "pudu_agent"], {
|
|
39
|
+
cwd: root,
|
|
40
|
+
env: { ...process.env, PYTHONPATH: root, PYTHONUNBUFFERED: "1" },
|
|
41
|
+
input: payload,
|
|
42
|
+
timeout
|
|
43
|
+
});
|
|
44
|
+
return { ...result, python };
|
|
45
|
+
}
|
|
46
|
+
function parseJsonStdout(stdout) {
|
|
47
|
+
const text = stdout.trim();
|
|
48
|
+
if (!text) {
|
|
49
|
+
throw new Error("Python engine returned empty stdout");
|
|
50
|
+
}
|
|
51
|
+
const start = text.indexOf("{");
|
|
52
|
+
const slice = start >= 0 ? text.slice(start) : text;
|
|
53
|
+
return JSON.parse(slice);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export {
|
|
57
|
+
pythonRoot,
|
|
58
|
+
findPython,
|
|
59
|
+
invokePuduAgent,
|
|
60
|
+
parseJsonStdout
|
|
61
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
invokePuduAgent,
|
|
3
|
+
parseJsonStdout
|
|
4
|
+
} from "./chunk-7KPNGK6A.js";
|
|
5
|
+
|
|
6
|
+
// src/agent-lab/graph.ts
|
|
7
|
+
async function buildRepoGraph(repo, timeoutMs = 6e4) {
|
|
8
|
+
const invoked = await invokePuduAgent({ op: "graph", repo }, timeoutMs);
|
|
9
|
+
if (invoked.timedOut) throw new Error("Python graph engine timed out");
|
|
10
|
+
const parsed = parseJsonStdout(invoked.stdout);
|
|
11
|
+
if (!parsed || parsed.ok === false) {
|
|
12
|
+
throw new Error(parsed.error ?? "graph failed");
|
|
13
|
+
}
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
async function runRepoHarness(repo, task, timeoutMs = 6e4) {
|
|
17
|
+
const invoked = await invokePuduAgent({ op: "harness", repo, task }, timeoutMs);
|
|
18
|
+
if (invoked.timedOut) throw new Error("Python harness engine timed out");
|
|
19
|
+
return parseJsonStdout(invoked.stdout);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export {
|
|
23
|
+
buildRepoGraph,
|
|
24
|
+
runRepoHarness
|
|
25
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
INTEGRATIONS
|
|
3
|
+
} from "./chunk-47LK5I2X.js";
|
|
4
|
+
import {
|
|
5
|
+
findPython,
|
|
6
|
+
pythonRoot
|
|
7
|
+
} from "./chunk-7KPNGK6A.js";
|
|
8
|
+
import {
|
|
9
|
+
t
|
|
10
|
+
} from "./chunk-4VEDGI4L.js";
|
|
11
|
+
import {
|
|
12
|
+
commandExists,
|
|
13
|
+
runCommand
|
|
14
|
+
} from "./chunk-SS7OXXQQ.js";
|
|
15
|
+
|
|
16
|
+
// src/integrations/opencode-config.ts
|
|
17
|
+
import { readFile, writeFile, mkdir } from "node:fs/promises";
|
|
18
|
+
import path from "node:path";
|
|
19
|
+
function mergeOpenCodeHarness(existing, ollamaTag, pythonBin, engineRoot) {
|
|
20
|
+
const prev = existing ?? {};
|
|
21
|
+
const provider = prev.provider ?? {};
|
|
22
|
+
const ollama = provider.ollama ?? {};
|
|
23
|
+
const models = ollama.models ?? {};
|
|
24
|
+
const mcp = prev.mcp ?? {};
|
|
25
|
+
return {
|
|
26
|
+
...prev,
|
|
27
|
+
$schema: typeof prev.$schema === "string" ? prev.$schema : "https://opencode.ai/config.json",
|
|
28
|
+
model: `ollama/${ollamaTag}`,
|
|
29
|
+
provider: {
|
|
30
|
+
...provider,
|
|
31
|
+
ollama: {
|
|
32
|
+
...ollama,
|
|
33
|
+
npm: "@ai-sdk/openai-compatible",
|
|
34
|
+
name: typeof ollama.name === "string" ? ollama.name : "Ollama (local)",
|
|
35
|
+
options: {
|
|
36
|
+
...ollama.options ?? {},
|
|
37
|
+
baseURL: "http://127.0.0.1:11434/v1"
|
|
38
|
+
},
|
|
39
|
+
models: {
|
|
40
|
+
...models,
|
|
41
|
+
[ollamaTag]: {
|
|
42
|
+
...models[ollamaTag] ?? {},
|
|
43
|
+
name: ollamaTag
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
mcp: {
|
|
49
|
+
...mcp,
|
|
50
|
+
"pudu-ai": {
|
|
51
|
+
type: "local",
|
|
52
|
+
enabled: true,
|
|
53
|
+
timeout: 12e4,
|
|
54
|
+
command: [pythonBin, "-m", "pudu_agent", "mcp"],
|
|
55
|
+
environment: { PYTHONPATH: engineRoot, PYTHONUNBUFFERED: "1" }
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function writeOpenCodeHarnessConfig(input) {
|
|
61
|
+
const file = path.join(input.repo, "opencode.json");
|
|
62
|
+
let existing;
|
|
63
|
+
try {
|
|
64
|
+
const raw = await readFile(file, "utf8");
|
|
65
|
+
const parsed = JSON.parse(raw);
|
|
66
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
67
|
+
existing = parsed;
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
existing = void 0;
|
|
71
|
+
}
|
|
72
|
+
const next = mergeOpenCodeHarness(existing, input.ollamaTag, input.pythonBin, pythonRoot());
|
|
73
|
+
await mkdir(input.repo, { recursive: true });
|
|
74
|
+
await writeFile(file, `${JSON.stringify(next, null, 2)}
|
|
75
|
+
`, "utf8");
|
|
76
|
+
return file;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/integrations/harness-launch.ts
|
|
80
|
+
async function executeHarnessLaunch(req) {
|
|
81
|
+
const def = INTEGRATIONS[req.tool];
|
|
82
|
+
if (!def) return { ok: false, log: t("launchUnknown") };
|
|
83
|
+
const ollama = await commandExists("ollama");
|
|
84
|
+
if (!ollama) return { ok: false, log: t("launchNeedOllama") };
|
|
85
|
+
const lines = [];
|
|
86
|
+
if (req.tool === "opencode") {
|
|
87
|
+
const python = await findPython();
|
|
88
|
+
if (!python) return { ok: false, log: t("repoPythonMissing") };
|
|
89
|
+
const file = await writeOpenCodeHarnessConfig({
|
|
90
|
+
repo: req.repo,
|
|
91
|
+
ollamaTag: req.ollamaTag,
|
|
92
|
+
pythonBin: python
|
|
93
|
+
});
|
|
94
|
+
lines.push(t("repoHarnessWroteConfig", { file }));
|
|
95
|
+
const bin = await commandExists("opencode");
|
|
96
|
+
if (bin) {
|
|
97
|
+
lines.push(`${bin} (${req.ollamaTag})`);
|
|
98
|
+
const launched2 = await runCommand(bin, [], { cwd: req.repo, timeout: 0, stdio: "inherit" });
|
|
99
|
+
if (launched2.exitCode !== 0) {
|
|
100
|
+
return { ok: false, log: `${lines.join("\n")}
|
|
101
|
+
${launched2.stderr || t("launchExecFail")}` };
|
|
102
|
+
}
|
|
103
|
+
return { ok: true, log: lines.join("\n") };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const args = ["launch", def.ollamaLaunch, "--model", req.ollamaTag];
|
|
107
|
+
if (def.id === "openclaw" || def.id === "claude") args.push("--yes");
|
|
108
|
+
lines.push(`ollama ${args.join(" ")}`);
|
|
109
|
+
const launched = await runCommand("ollama", args, { timeout: 0, stdio: "inherit", cwd: req.repo });
|
|
110
|
+
if (launched.exitCode !== 0) {
|
|
111
|
+
return { ok: false, log: `${lines.join("\n")}
|
|
112
|
+
${launched.stderr || t("launchExecFail")}` };
|
|
113
|
+
}
|
|
114
|
+
return { ok: true, log: lines.join("\n") };
|
|
115
|
+
}
|
|
116
|
+
export {
|
|
117
|
+
executeHarnessLaunch
|
|
118
|
+
};
|
package/dist/cli/index.js
CHANGED
|
@@ -32,12 +32,14 @@ import {
|
|
|
32
32
|
runBenchmark,
|
|
33
33
|
runtimesText,
|
|
34
34
|
tasksText
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-6BGYI7XY.js";
|
|
36
|
+
import "./chunk-47LK5I2X.js";
|
|
37
|
+
import "./chunk-473MNTMS.js";
|
|
36
38
|
import {
|
|
37
39
|
resolveLocale,
|
|
38
40
|
setLocale,
|
|
39
41
|
t
|
|
40
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-4VEDGI4L.js";
|
|
41
43
|
import {
|
|
42
44
|
commandExists,
|
|
43
45
|
killAll,
|
|
@@ -57,7 +59,8 @@ var VALUE_FLAGS = /* @__PURE__ */ new Set([
|
|
|
57
59
|
"--repo",
|
|
58
60
|
"--intent",
|
|
59
61
|
"--limit",
|
|
60
|
-
"--glob"
|
|
62
|
+
"--glob",
|
|
63
|
+
"--task"
|
|
61
64
|
]);
|
|
62
65
|
function flagValue(args2, name) {
|
|
63
66
|
const index = args2.findIndex((a) => a === name);
|
|
@@ -131,7 +134,8 @@ function parseArgs(argv) {
|
|
|
131
134
|
repo: flagValue(args2, "--repo"),
|
|
132
135
|
intent: flagValue(args2, "--intent"),
|
|
133
136
|
limit,
|
|
134
|
-
globs: flagValues(args2, "--glob")
|
|
137
|
+
globs: flagValues(args2, "--glob"),
|
|
138
|
+
task: flagValue(args2, "--task")
|
|
135
139
|
};
|
|
136
140
|
}
|
|
137
141
|
|
|
@@ -726,8 +730,14 @@ ${modelsText(session)}`)}
|
|
|
726
730
|
);
|
|
727
731
|
return;
|
|
728
732
|
}
|
|
729
|
-
const { renderDashboard } = await import("./render-
|
|
730
|
-
await renderDashboard(session, preset, start);
|
|
733
|
+
const { renderDashboard } = await import("./render-Y4DNH7FW.js");
|
|
734
|
+
const pending = await renderDashboard(session, preset, start);
|
|
735
|
+
if (pending) {
|
|
736
|
+
const { executeHarnessLaunch } = await import("./harness-launch-KEUKZZXH.js");
|
|
737
|
+
const result = await executeHarnessLaunch(pending);
|
|
738
|
+
if (!result.ok) process.stderr.write(`${result.log}
|
|
739
|
+
`);
|
|
740
|
+
}
|
|
731
741
|
}
|
|
732
742
|
async function run(args2) {
|
|
733
743
|
setLocale(resolveLocale(args2.lang));
|
|
@@ -739,7 +749,7 @@ async function run(args2) {
|
|
|
739
749
|
if (args2.verbose) setLogLevel("verbose");
|
|
740
750
|
if (!args2.color) process.env.NO_COLOR = "1";
|
|
741
751
|
if (args2.command === "repo") {
|
|
742
|
-
const { runRepoCommand } = await import("./repo-cli-
|
|
752
|
+
const { runRepoCommand } = await import("./repo-cli-BK76EVBZ.js");
|
|
743
753
|
return runRepoCommand(args2);
|
|
744
754
|
}
|
|
745
755
|
const cleanup = () => {
|