pudu-ai 0.2.19 → 0.2.21
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.es.md +85 -0
- package/README.md +19 -2
- package/dist/cli/brew-ZRR7D4XL.js +80 -0
- package/dist/cli/chunk-6C5EPKRI.js +1597 -0
- package/dist/cli/chunk-QGEXKMQ5.js +501 -0
- package/dist/cli/chunk-SS7OXXQQ.js +82 -0
- package/dist/cli/index.js +141 -2942
- package/dist/cli/render-3UBPZNF4.js +812 -0
- package/dist/cli/repo-cli-4GSG5MD3.js +255 -0
- package/package.json +3 -2
- package/python/pudu_agent/__init__.py +1 -0
- package/python/pudu_agent/__main__.py +4 -0
- 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__/protocol.cpython-312.pyc +0 -0
- package/python/pudu_agent/__pycache__/search.cpython-312.pyc +0 -0
- package/python/pudu_agent/ast_search.py +153 -0
- package/python/pudu_agent/protocol.py +96 -0
- package/python/pudu_agent/search.py +342 -0
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
// src/i18n/en.ts
|
|
2
|
+
var en = {
|
|
3
|
+
appTitle: "PUDU-AI",
|
|
4
|
+
appSubtitle: "Local AI Hardware & Benchmark Lab",
|
|
5
|
+
byline: "by devjaime",
|
|
6
|
+
aboutDashboard: "Opens the live lab: your machine, installed models, and estimates. Use it to see what can run here before you benchmark or launch a tool.",
|
|
7
|
+
aboutHardware: "Detects CPU, GPU, and Unified Memory/RAM. Use it to know what this computer can host before pulling a model.",
|
|
8
|
+
aboutModels: "Lists installed models vs catalog fits. FIT/EST. SPEED are estimated (CanIRun.ai by midudev); MEASURED is llama-bench history. Use it to separate \u201Calready here\u201D from \u201Cmight run\u201D.",
|
|
9
|
+
aboutRecommend: "Suggests models by use case for this hardware. Estimates only, unless you already measured. Use it to pick a size class, not a quality score.",
|
|
10
|
+
aboutTasks: "Asks what you want (code, video, image, transcription, chat) and returns small OpenCode-style harnesses per model, in this language. Use it to try a model on one concrete job.",
|
|
11
|
+
aboutBenchmark: "Runs llama-bench on a local GGUF and records measured t/s plus system telemetry. Use it when you need real numbers, not estimates.",
|
|
12
|
+
aboutCompare: "Compares saved measured runs (speed, memory, power). Quality is never inferred from speed. Use it after two or more benchmarks.",
|
|
13
|
+
compareHow: "[C] Compare needs two measured llama-bench runs of different models. Press [B], pick a model, wait; repeat with another; then [C]. Quality is not a speed score.",
|
|
14
|
+
compareNeedBench: "Need 2+ measured models. Press [B] to benchmark first.",
|
|
15
|
+
reqOllama: "install: https://ollama.com/download or brew install ollama",
|
|
16
|
+
reqLmStudio: "install: https://lmstudio.ai",
|
|
17
|
+
reqDocker: "install: https://docs.docker.com/get-docker/ or brew install --cask docker",
|
|
18
|
+
dockerHint: "press 4 to run ollama/ollama in Docker",
|
|
19
|
+
dockerStarting: "Starting ollama/ollama container\u2026",
|
|
20
|
+
dockerRunOk: "Docker Ollama is up on port 11434 (name: pudu-ollama).",
|
|
21
|
+
dockerRunFail: "Docker daemon is not running. Open Docker Desktop, wait until it is idle, press 4 again.",
|
|
22
|
+
dockerBrewInstall: "Installing Docker with Homebrew (brew install --cask docker)\u2026",
|
|
23
|
+
dockerDesktopWait: "Starting Docker Desktop and waiting for the daemon\u2026",
|
|
24
|
+
brewInstalling: "Homebrew: installing {pkg}\u2026",
|
|
25
|
+
aboutHistory: "Shows local benchmark files in ~/.pudu-ai/benchmarks. Use it to replay what this machine actually did.",
|
|
26
|
+
aboutDoctor: "Checks Node, Apple Silicon/Metal, Ollama, llama-bench, and the CanIRun API. Use it to see what is missing before a bench or launch.",
|
|
27
|
+
aboutReport: "Writes the latest measured run as Markdown. Use it to paste results into GitHub or notes.",
|
|
28
|
+
aboutLaunch: "Explains Ollama integrations (OpenCode, OpenClaw, Hermes, Claude Code). Pull/launch only with --yes if the model grade is S\u2013B and speed is enough. Use it to wire a coding agent only when this hardware can carry it.",
|
|
29
|
+
aboutAddPath: "Adds a GGUF folder to scan (not the whole disk). Use it so llama-bench can find models outside Ollama.",
|
|
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
|
+
repoSearchTitle: "Pudu Agent Lab \u2014 repo search",
|
|
32
|
+
repoNeedQuery: "Pass a query or --structural PATTERN, e.g. npx pudu-ai repo search validate_user --json",
|
|
33
|
+
repoUnknownSubcommand: "Unknown repo subcommand: {sub}. Iteration 1 supports search only.",
|
|
34
|
+
repoPythonMissing: "python3 not found. Agent Lab search needs Python 3.10+ on PATH.",
|
|
35
|
+
repoToolMissing: "{tool} not found on PATH",
|
|
36
|
+
repoNoMatches: "No matches.",
|
|
37
|
+
repoHelp: `Pudu Agent Lab \u2014 repository search
|
|
38
|
+
|
|
39
|
+
Usage:
|
|
40
|
+
npx pudu-ai repo search QUERY
|
|
41
|
+
npx pudu-ai repo search QUERY --repo PATH --json
|
|
42
|
+
npx pudu-ai repo search --structural 'def $FUNC($$$ARGS): $$$BODY'
|
|
43
|
+
|
|
44
|
+
Flags:
|
|
45
|
+
--repo PATH Repository root (default: cwd)
|
|
46
|
+
--structural PAT ast-grep pattern
|
|
47
|
+
--intent INTENT TEXT | STRUCTURAL | RELATIONSHIP | IMPACT | SEMANTIC | UNKNOWN
|
|
48
|
+
--glob GLOB Repeatable include glob
|
|
49
|
+
--limit N Max matches (default 100)
|
|
50
|
+
--json Machine-readable SearchResult
|
|
51
|
+
`,
|
|
52
|
+
nav: "[S] Setup/Install [B] Benchmark [M] Models [R] Recommend [T] Tasks [H] Hardware [C] Compare [L] History [Q] Quit",
|
|
53
|
+
setupTitle: "SETUP \u2014 INSTALL MODELS & AGENTS",
|
|
54
|
+
setupIntro: "Pick a recommended model (grade S\u2013B), install it with Ollama, then link or install OpenCode, Hermes, or OpenClaw.",
|
|
55
|
+
setupStep1: "1. Recommended models (\u2191\u2193 to choose)",
|
|
56
|
+
setupStep2: "2. Install the selected model",
|
|
57
|
+
setupInstallModel: "Press Enter or I \u2192 ollama pull (only if grade is S, A, or B)",
|
|
58
|
+
setupStep3: "3. Coding agent \u2014 link if present, install if missing",
|
|
59
|
+
setupLinkNow: "installed \u2192 press 1/2/3 to link this model",
|
|
60
|
+
setupInstallAgent: "not installed \u2192 press 1/2/3 to install via ollama launch (if eligible)",
|
|
61
|
+
setupKeys: "Enter/I = pull model 1 OpenCode 2 Hermes 3 OpenClaw Esc = back",
|
|
62
|
+
setupCta: "[S] Setup: install model + OpenCode / Hermes / OpenClaw",
|
|
63
|
+
machine: "MACHINE",
|
|
64
|
+
runtimes: "LOCAL AI RUNTIMES",
|
|
65
|
+
installedModels: "INSTALLED MODELS",
|
|
66
|
+
compatible: "COMPATIBLE (catalog, estimated)",
|
|
67
|
+
recommended: "RECOMMENDED FOR THIS MACHINE",
|
|
68
|
+
recommendedHint: "Estimated values come from CanIRun.ai by midudev (canirun.ai \xB7 midu.dev) or a local fallback \u2014 not measured",
|
|
69
|
+
recommendKeys: "[\u2191\u2193] model [I] ollama pull if grade S\u2013B [O] OpenCode [E] Hermes [W] OpenClaw [Esc] back",
|
|
70
|
+
recommendCliHint: "Install: npx pudu-ai recommend --install --yes Link: npx pudu-ai recommend --link opencode --yes",
|
|
71
|
+
agentsTitle: "CODING AGENTS",
|
|
72
|
+
agentMissing: "not installed \u2014 select to install via ollama launch if the model is eligible",
|
|
73
|
+
installGradeBlock: "Grade {grade} is too low to pull. Need S, A, or B.",
|
|
74
|
+
linkingTool: "Linking {tool} with {model}\u2026",
|
|
75
|
+
modelPulled: "Pulled {model} with Ollama.",
|
|
76
|
+
installNone: "No recommended model has grade S\u2013B to install.",
|
|
77
|
+
noOllamaTag: "No Ollama library tag for {model}. Catalog names like Agents-A1 are not pullable. Use qwen3:8b, gemma3:4b, qwen3.5:4b, llama3.1:8b.",
|
|
78
|
+
credits: "Measured: llama-bench + OS telemetry. Estimated: CanIRun.ai by midudev (https://canirun.ai, https://github.com/midudev/canirun.ai, https://midu.dev). Labelled separately; never mixed.",
|
|
79
|
+
noModels: "No local models detected",
|
|
80
|
+
notTested: "Not tested",
|
|
81
|
+
detected: "detected",
|
|
82
|
+
notDetected: "not detected",
|
|
83
|
+
unknownMachine: "Unknown machine",
|
|
84
|
+
cpuNa: "CPU N/A",
|
|
85
|
+
selectBenchmark: "Select model to benchmark",
|
|
86
|
+
enterToRun: "Enter to run llama-bench \xB7 Esc back",
|
|
87
|
+
noGguf: "No GGUF-resolvable models to benchmark.",
|
|
88
|
+
performance: "PERFORMANCE",
|
|
89
|
+
system: "SYSTEM",
|
|
90
|
+
powerThermals: "POWER / THERMALS",
|
|
91
|
+
elapsed: "Elapsed",
|
|
92
|
+
measuredAfter: "Prompt / generation t/s appear after llama-bench finishes (measured).",
|
|
93
|
+
permissionsHint: "GPU % and power need extra permissions; shown as N/A when not measured.",
|
|
94
|
+
cancelHint: "Ctrl+C / Q cancels llama-bench and telemetry",
|
|
95
|
+
saved: "Saved",
|
|
96
|
+
hardwareHint: "Power, GPU %, and thermals require extra OS permissions and show N/A when unavailable.",
|
|
97
|
+
modelsHint: "FIT and EST. SPEED are estimated. MEASURED comes from local llama-bench history.",
|
|
98
|
+
historyEmpty: "No benchmark history in ~/.pudu-ai/benchmarks",
|
|
99
|
+
compareNeedTwo: "Need at least two measured benchmarks to compare.",
|
|
100
|
+
compareTitle: "LOCAL MODEL BENCHMARKS (measured)",
|
|
101
|
+
winner: "Winner",
|
|
102
|
+
qualityNote: "Quality (not derived from speed; see catalog metadata)",
|
|
103
|
+
doctorTitle: "Pudu-AI Doctor",
|
|
104
|
+
doctorReady: "Ready to benchmark {count} installed models.",
|
|
105
|
+
doctorNoBench: "llama-bench unavailable. Install llama.cpp, e.g. `brew install llama.cpp`. Pudu-AI will not install native dependencies.",
|
|
106
|
+
loading: "Inspecting hardware, runtimes, and local models\u2026",
|
|
107
|
+
addedPath: "Added model path {path}",
|
|
108
|
+
modelNotFound: "Model not found: {id}",
|
|
109
|
+
noHistory: "No benchmark history.",
|
|
110
|
+
jsonNeedModel: "Pass a model id for JSON mode, e.g. npx pudu-ai benchmark qwen3:8b --json",
|
|
111
|
+
useCaseCoding: "Coding",
|
|
112
|
+
useCaseGeneral: "General",
|
|
113
|
+
useCaseReasoning: "Reasoning",
|
|
114
|
+
useCaseLightweight: "Lightweight",
|
|
115
|
+
estimated: "Estimated",
|
|
116
|
+
measured: "Measured",
|
|
117
|
+
scoreLabel: "Pudu-AI Score",
|
|
118
|
+
reportTitle: "Pudu-AI Benchmark",
|
|
119
|
+
benchmarkTitle: "Pudu-AI Benchmark",
|
|
120
|
+
tasksTitle: "TASK HARNESSES",
|
|
121
|
+
tasksHint: "Small OpenCode-style tasks in your selected language. Installed models first. Catalog matches are Estimated (CanIRun.ai by midudev).",
|
|
122
|
+
tasksKinds: "Work types",
|
|
123
|
+
tasksScope: "Scope",
|
|
124
|
+
tasksPriority: "Priority",
|
|
125
|
+
tasksInstalled: "installed",
|
|
126
|
+
tasksNotInstalled: "not installed",
|
|
127
|
+
tasksEmpty: "No matching models for those work types on this machine.",
|
|
128
|
+
tasksQ1: "What do you want to do? (space to toggle, enter next)",
|
|
129
|
+
tasksQ2: "Use only installed models, or also catalog estimates?",
|
|
130
|
+
tasksQ3: "Priority?",
|
|
131
|
+
tasksOptCode: "Code",
|
|
132
|
+
tasksOptVideo: "Video",
|
|
133
|
+
tasksOptImage: "Image",
|
|
134
|
+
tasksOptTranscription: "Transcription",
|
|
135
|
+
tasksOptChat: "Tasks / chat",
|
|
136
|
+
tasksOptInstalled: "Installed only",
|
|
137
|
+
tasksOptAll: "Installed + catalog (estimated)",
|
|
138
|
+
tasksOptSpeed: "Speed",
|
|
139
|
+
tasksOptBalanced: "Balanced",
|
|
140
|
+
tasksOptQuality: "Quality",
|
|
141
|
+
taskCodeReviewTitle: "Review a local diff",
|
|
142
|
+
taskCodeReviewPrompt: "In OpenCode: open a git diff and ask this model to list bugs, missing tests, and a 5-line summary. Do not apply patches unless you ask.",
|
|
143
|
+
taskCodeTestsTitle: "Write one failing test",
|
|
144
|
+
taskCodeTestsPrompt: "Pick one function. Ask the model for a single Vitest/Jest test that fails on the current bug. Do not generate extra files.",
|
|
145
|
+
taskChatPlanTitle: "Turn a goal into a task list",
|
|
146
|
+
taskChatPlanPrompt: "Give the model one goal. Require a numbered plan of 5 harness-sized tasks. No implementation yet.",
|
|
147
|
+
taskChatAgentTitle: "Agent loop on one file",
|
|
148
|
+
taskChatAgentPrompt: "Point the model at one file and one acceptance check. It may edit only that file, then stop.",
|
|
149
|
+
taskImageCaptionTitle: "Caption a local image",
|
|
150
|
+
taskImageCaptionPrompt: "If this is a vision/image model, caption one local image in your UI language. If it is text-only, skip and say N/A.",
|
|
151
|
+
taskImageBriefTitle: "Image generation brief",
|
|
152
|
+
taskImageBriefPrompt: "Ask for a 6-line prompt brief (subject, lens, light, negative prompt) for one still. Do not invent that the model rendered the image unless it did.",
|
|
153
|
+
taskVideoBoardTitle: "8-shot storyboard",
|
|
154
|
+
taskVideoBoardPrompt: "Ask for 8 shots: duration, camera, action, on-screen text. Keep it local. Do not claim a render exists.",
|
|
155
|
+
taskVideoShotTitle: "Shot list from a script",
|
|
156
|
+
taskVideoShotPrompt: "Paste a short script. Ask for a shot list and estimated seconds. Text-only models may plan; they cannot encode video.",
|
|
157
|
+
taskTranscribeCleanTitle: "Clean a transcript",
|
|
158
|
+
taskTranscribeCleanPrompt: "Paste noisy speech-to-text. Ask the model to punctuate, remove fillers, and keep speaker labels. This is not ASR; it edits text.",
|
|
159
|
+
taskTranscribeActionsTitle: "Actions from a transcript",
|
|
160
|
+
taskTranscribeActionsPrompt: "From a meeting transcript, extract owners, due dates, and open questions as a checklist in your UI language.",
|
|
161
|
+
launchTitle: "OLLAMA INTEGRATIONS",
|
|
162
|
+
launchHint: "Explain-only by default. Pull/install/launch run only with --yes and only if a recommended model fits this hardware (grade S\u2013B; measured speed when available). Docs: OpenCode, OpenClaw, Hermes, Claude Code via Ollama.",
|
|
163
|
+
launchNeedOllama: "Ollama is not detected. Install Ollama first. Pudu-AI will not install it.",
|
|
164
|
+
launchNoModel: "No coding/chat model on this machine meets the hardware bar for this integration.",
|
|
165
|
+
launchGradeFail: "Grade {grade} is below the allowed set ({allowed}).",
|
|
166
|
+
launchSlowFail: "Measured {tps} t/s is below the {min} t/s agent floor.",
|
|
167
|
+
launchEstimateWeak: "Only a weak estimate is available; will not pull or launch.",
|
|
168
|
+
launchOk: "Eligible on this hardware for recommended tasks.",
|
|
169
|
+
launchModel: "Model",
|
|
170
|
+
launchRunHint: "To pull (if needed) and launch: npx pudu-ai launch {tool} --yes",
|
|
171
|
+
launchBlocked: "Blocked. No pull, install, or launch.",
|
|
172
|
+
launchNeedTool: "Pass a tool: opencode | openclaw | hermes | claude",
|
|
173
|
+
launchUnknown: "Unknown integration.",
|
|
174
|
+
launchPulling: "Pulling {model} with ollama pull\u2026",
|
|
175
|
+
launchPullFail: "ollama pull failed.",
|
|
176
|
+
launchExecFail: "ollama launch failed.",
|
|
177
|
+
help: `Pudu-AI \u2014 local AI hardware & benchmark lab
|
|
178
|
+
|
|
179
|
+
Usage:
|
|
180
|
+
npx pudu-ai
|
|
181
|
+
npx pudu-ai hardware
|
|
182
|
+
npx pudu-ai models
|
|
183
|
+
npx pudu-ai models add-path ~/Models
|
|
184
|
+
npx pudu-ai setup
|
|
185
|
+
npx pudu-ai recommend
|
|
186
|
+
npx pudu-ai recommend --install --yes
|
|
187
|
+
npx pudu-ai recommend --link opencode --yes
|
|
188
|
+
npx pudu-ai tasks
|
|
189
|
+
npx pudu-ai tasks --for code,image --scope all --priority speed
|
|
190
|
+
npx pudu-ai benchmark [model]
|
|
191
|
+
npx pudu-ai compare
|
|
192
|
+
npx pudu-ai history
|
|
193
|
+
npx pudu-ai doctor
|
|
194
|
+
npx pudu-ai report --markdown
|
|
195
|
+
npx pudu-ai launch
|
|
196
|
+
npx pudu-ai launch opencode
|
|
197
|
+
npx pudu-ai launch opencode --yes
|
|
198
|
+
npx pudu-ai repo search QUERY
|
|
199
|
+
npx pudu-ai repo search --structural 'def $FUNC($$$ARGS): $$$BODY'
|
|
200
|
+
|
|
201
|
+
Flags:
|
|
202
|
+
--json Machine-readable JSON (no TUI)
|
|
203
|
+
--csv CSV output
|
|
204
|
+
--no-network Skip CanIRun API; use cache/local estimates
|
|
205
|
+
--no-color Disable ANSI color
|
|
206
|
+
--verbose Debug logs to stderr
|
|
207
|
+
--preset quick | standard | stress
|
|
208
|
+
--lang en | es
|
|
209
|
+
--for code,video,image,transcription,chat
|
|
210
|
+
--scope installed | all
|
|
211
|
+
--priority speed | balanced | quality
|
|
212
|
+
--yes Execute pull/launch only if the model is eligible
|
|
213
|
+
--repo Agent Lab repository root
|
|
214
|
+
--structural ast-grep pattern for repo search
|
|
215
|
+
--intent TEXT | STRUCTURAL | RELATIONSHIP | IMPACT | SEMANTIC | UNKNOWN
|
|
216
|
+
--glob Repeatable include glob for repo search
|
|
217
|
+
--limit Max repo search matches
|
|
218
|
+
|
|
219
|
+
Credits:
|
|
220
|
+
Measured values come from llama-bench and OS telemetry.
|
|
221
|
+
Estimated values come from CanIRun.ai by midudev
|
|
222
|
+
(https://canirun.ai \xB7 https://github.com/midudev/canirun.ai \xB7 https://midu.dev)
|
|
223
|
+
or a local fallback, and are labelled as such.
|
|
224
|
+
`
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// src/i18n/es.ts
|
|
228
|
+
var es = {
|
|
229
|
+
appTitle: "PUDU-AI",
|
|
230
|
+
appSubtitle: "Laboratorio local de hardware y benchmarks de IA",
|
|
231
|
+
byline: "by devjaime",
|
|
232
|
+
aboutDashboard: "Abre el laboratorio en vivo: equipo, modelos instalados y estimaciones. Sirve para ver qu\xE9 puede correr aqu\xED antes de medir o lanzar una herramienta.",
|
|
233
|
+
aboutHardware: "Detecta CPU, GPU y memoria unificada/RAM. Sirve para saber qu\xE9 puede hospedar este equipo antes de descargar un modelo.",
|
|
234
|
+
aboutModels: "Lista instalados frente al cat\xE1logo. FIT/EST. SPEED son estimados (CanIRun.ai de midudev); MEASURED es historial de llama-bench. Sirve para separar \u201Cya est\xE1\u201D de \u201Cpodr\xEDa correr\u201D.",
|
|
235
|
+
aboutRecommend: "Sugiere modelos por caso de uso para este hardware. Solo estimaciones, salvo que ya hayas medido. Sirve para elegir un tama\xF1o, no una nota de calidad.",
|
|
236
|
+
aboutTasks: "Pregunta qu\xE9 quieres (c\xF3digo, v\xEDdeo, imagen, transcripci\xF3n, chat) y devuelve harnesses peque\xF1os estilo OpenCode por modelo, en este idioma. Sirve para probar un modelo en una tarea concreta.",
|
|
237
|
+
aboutBenchmark: "Ejecuta llama-bench sobre un GGUF local y guarda t/s medidos m\xE1s telemetr\xEDa. Sirve cuando necesitas n\xFAmeros reales, no estimaciones.",
|
|
238
|
+
aboutCompare: "Compara corridas medidas (velocidad, memoria, potencia). La calidad no se infiere de la velocidad. Sirve despu\xE9s de dos o m\xE1s benchmarks.",
|
|
239
|
+
compareHow: "[C] Comparar necesita dos corridas medidas de llama-bench en modelos distintos. Pulsa [B], elige un modelo, espera; repite con otro; luego [C]. La calidad no es un score de velocidad.",
|
|
240
|
+
compareNeedBench: "Hacen falta 2+ modelos medidos. Pulsa [B] para hacer un benchmark primero.",
|
|
241
|
+
reqOllama: "instalar: https://ollama.com/download o brew install ollama",
|
|
242
|
+
reqLmStudio: "instalar: https://lmstudio.ai",
|
|
243
|
+
reqDocker: "instalar: https://docs.docker.com/get-docker/ o brew install --cask docker",
|
|
244
|
+
dockerHint: "pulsa 4 para correr ollama/ollama en Docker",
|
|
245
|
+
dockerStarting: "Arrancando contenedor ollama/ollama\u2026",
|
|
246
|
+
dockerRunOk: "Ollama en Docker est\xE1 en el puerto 11434 (nombre: pudu-ollama).",
|
|
247
|
+
dockerRunFail: "El daemon de Docker no corre. Abre Docker Desktop, espera a que est\xE9 listo y pulsa 4 otra vez.",
|
|
248
|
+
dockerBrewInstall: "Instalando Docker con Homebrew (brew install --cask docker)\u2026",
|
|
249
|
+
dockerDesktopWait: "Arrancando Docker Desktop y esperando al daemon\u2026",
|
|
250
|
+
brewInstalling: "Homebrew: instalando {pkg}\u2026",
|
|
251
|
+
aboutHistory: "Muestra los JSON locales en ~/.pudu-ai/benchmarks. Sirve para ver qu\xE9 hizo realmente esta m\xE1quina.",
|
|
252
|
+
aboutDoctor: "Revisa Node, Apple Silicon/Metal, Ollama, llama-bench y la API de CanIRun. Sirve para ver qu\xE9 falta antes de un bench o un launch.",
|
|
253
|
+
aboutReport: "Exporta la \xFAltima corrida medida en Markdown. Sirve para pegar resultados en GitHub o notas.",
|
|
254
|
+
aboutLaunch: "Explica las integraciones de Ollama (OpenCode, OpenClaw, Hermes, Claude Code). Pull/launch solo con --yes si la nota es S\u2013B y hay velocidad suficiente. Sirve para conectar un agente de c\xF3digo solo cuando el hardware lo aguanta.",
|
|
255
|
+
aboutAddPath: "A\xF1ade una carpeta GGUF a escanear (no todo el disco). Sirve para que llama-bench encuentre modelos fuera de Ollama.",
|
|
256
|
+
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
|
+
repoSearchTitle: "Pudu Agent Lab \u2014 b\xFAsqueda de repo",
|
|
258
|
+
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}. La iteraci\xF3n 1 solo admite search.",
|
|
260
|
+
repoPythonMissing: "No se encontr\xF3 python3. La b\xFAsqueda de Agent Lab necesita Python 3.10+ en PATH.",
|
|
261
|
+
repoToolMissing: "{tool} no est\xE1 en PATH",
|
|
262
|
+
repoNoMatches: "Sin coincidencias.",
|
|
263
|
+
repoHelp: `Pudu Agent Lab \u2014 b\xFAsqueda de repositorio
|
|
264
|
+
|
|
265
|
+
Uso:
|
|
266
|
+
npx pudu-ai repo search CONSULTA
|
|
267
|
+
npx pudu-ai repo search CONSULTA --repo RUTA --json
|
|
268
|
+
npx pudu-ai repo search --structural 'def $FUNC($$$ARGS): $$$BODY'
|
|
269
|
+
|
|
270
|
+
Flags:
|
|
271
|
+
--repo PATH Ra\xEDz del repositorio (por defecto: cwd)
|
|
272
|
+
--structural PAT Patr\xF3n de ast-grep
|
|
273
|
+
--intent INTENT TEXT | STRUCTURAL | RELATIONSHIP | IMPACT | SEMANTIC | UNKNOWN
|
|
274
|
+
--glob GLOB Glob de inclusi\xF3n (repetible)
|
|
275
|
+
--limit N M\xE1ximo de coincidencias (100)
|
|
276
|
+
--json SearchResult legible por m\xE1quinas
|
|
277
|
+
`,
|
|
278
|
+
nav: "[S] Setup/Instalar [B] Benchmark [M] Modelos [R] Recomendaciones [T] Tareas [H] Hardware [C] Comparar [L] Historial [Q] Salir",
|
|
279
|
+
setupTitle: "SETUP \u2014 INSTALAR MODELOS Y AGENTES",
|
|
280
|
+
setupIntro: "Elige un modelo recomendado (nota S\u2013B), inst\xE1lalo con Ollama y luego vincula o instala OpenCode, Hermes u OpenClaw.",
|
|
281
|
+
setupStep1: "1. Modelos recomendados (\u2191\u2193 para elegir)",
|
|
282
|
+
setupStep2: "2. Instalar el modelo seleccionado",
|
|
283
|
+
setupInstallModel: "Enter o I \u2192 ollama pull (solo si la nota es S, A o B)",
|
|
284
|
+
setupStep3: "3. Agente de c\xF3digo \u2014 vincular si existe, instalar si falta",
|
|
285
|
+
setupLinkNow: "instalado \u2192 pulsa 1/2/3 para vincular este modelo",
|
|
286
|
+
setupInstallAgent: "no instalado \u2192 pulsa 1/2/3 para instalar con ollama launch (si es elegible)",
|
|
287
|
+
setupKeys: "Enter/I = descargar modelo 1 OpenCode 2 Hermes 3 OpenClaw Esc = atr\xE1s",
|
|
288
|
+
setupCta: "[S] Setup: instalar modelo + OpenCode / Hermes / OpenClaw",
|
|
289
|
+
machine: "EQUIPO",
|
|
290
|
+
runtimes: "RUNTIMES DE IA LOCAL",
|
|
291
|
+
installedModels: "MODELOS INSTALADOS",
|
|
292
|
+
compatible: "COMPATIBLES (cat\xE1logo, estimado)",
|
|
293
|
+
recommended: "RECOMENDADOS PARA ESTE EQUIPO",
|
|
294
|
+
recommendedHint: "Los valores estimados vienen de CanIRun.ai de midudev (canirun.ai \xB7 midu.dev) o de un fallback local \u2014 no son mediciones",
|
|
295
|
+
recommendKeys: "[\u2191\u2193] modelo [I] ollama pull si nota S\u2013B [O] OpenCode [E] Hermes [W] OpenClaw [Esc] atr\xE1s",
|
|
296
|
+
recommendCliHint: "Instalar: npx pudu-ai recommend --install --yes Vincular: npx pudu-ai recommend --link opencode --yes",
|
|
297
|
+
agentsTitle: "AGENTES DE C\xD3DIGO",
|
|
298
|
+
agentMissing: "no instalado \u2014 selecci\xF3nalo para instalar con ollama launch si el modelo es elegible",
|
|
299
|
+
installGradeBlock: "La nota {grade} es baja para hacer pull. Hace falta S, A o B.",
|
|
300
|
+
linkingTool: "Vinculando {tool} con {model}\u2026",
|
|
301
|
+
modelPulled: "Descargado {model} con Ollama.",
|
|
302
|
+
installNone: "Ning\xFAn modelo recomendado tiene nota S\u2013B para instalar.",
|
|
303
|
+
noOllamaTag: "No hay tag de Ollama para {model}. Nombres de cat\xE1logo como Agents-A1 no se pueden descargar. Usa qwen3:8b, gemma3:4b, qwen3.5:4b, llama3.1:8b.",
|
|
304
|
+
credits: "Medido: llama-bench + telemetr\xEDa del SO. Estimado: CanIRun.ai de midudev (https://canirun.ai, https://github.com/midudev/canirun.ai, https://midu.dev). Se etiquetan por separado; nunca se mezclan.",
|
|
305
|
+
noModels: "No se detectaron modelos locales",
|
|
306
|
+
notTested: "Sin probar",
|
|
307
|
+
detected: "detectado",
|
|
308
|
+
notDetected: "no detectado",
|
|
309
|
+
unknownMachine: "Equipo desconocido",
|
|
310
|
+
cpuNa: "CPU N/D",
|
|
311
|
+
selectBenchmark: "Selecciona un modelo para medir",
|
|
312
|
+
enterToRun: "Enter para ejecutar llama-bench \xB7 Esc atr\xE1s",
|
|
313
|
+
noGguf: "No hay modelos GGUF resolubles para medir.",
|
|
314
|
+
performance: "RENDIMIENTO",
|
|
315
|
+
system: "SISTEMA",
|
|
316
|
+
powerThermals: "ENERG\xCDA / T\xC9RMICAS",
|
|
317
|
+
elapsed: "Transcurrido",
|
|
318
|
+
measuredAfter: "Los t/s de prompt y generaci\xF3n aparecen al terminar llama-bench (medidos).",
|
|
319
|
+
permissionsHint: "El % de GPU y la potencia requieren permisos extra; se muestra N/D si no se puede medir.",
|
|
320
|
+
cancelHint: "Ctrl+C / Q cancela llama-bench y la telemetr\xEDa",
|
|
321
|
+
saved: "Guardado",
|
|
322
|
+
hardwareHint: "Potencia, % de GPU y t\xE9rmicas requieren permisos extra del SO; si no hay datos se muestra N/D.",
|
|
323
|
+
modelsHint: "FIT y EST. SPEED son estimados. MEASURED sale del historial local de llama-bench.",
|
|
324
|
+
historyEmpty: "No hay historial de benchmarks en ~/.pudu-ai/benchmarks",
|
|
325
|
+
compareNeedTwo: "Se necesitan al menos dos benchmarks medidos para comparar.",
|
|
326
|
+
compareTitle: "BENCHMARKS LOCALES (medidos)",
|
|
327
|
+
winner: "Ganador",
|
|
328
|
+
qualityNote: "Calidad (no se deriva de la velocidad; ver metadatos del cat\xE1logo)",
|
|
329
|
+
doctorTitle: "Pudu-AI Doctor",
|
|
330
|
+
doctorReady: "Listo para medir {count} modelos instalados.",
|
|
331
|
+
doctorNoBench: "llama-bench no est\xE1 disponible. Instala llama.cpp, p. ej. `brew install llama.cpp`. Pudu-AI no instala dependencias nativas.",
|
|
332
|
+
loading: "Inspeccionando hardware, runtimes y modelos locales\u2026",
|
|
333
|
+
addedPath: "Ruta de modelos a\xF1adida {path}",
|
|
334
|
+
modelNotFound: "Modelo no encontrado: {id}",
|
|
335
|
+
noHistory: "No hay historial de benchmarks.",
|
|
336
|
+
jsonNeedModel: "Pasa un id de modelo en modo JSON, p. ej. npx pudu-ai benchmark qwen3:8b --json",
|
|
337
|
+
useCaseCoding: "C\xF3digo",
|
|
338
|
+
useCaseGeneral: "General",
|
|
339
|
+
useCaseReasoning: "Razonamiento",
|
|
340
|
+
useCaseLightweight: "Ligero",
|
|
341
|
+
estimated: "Estimado",
|
|
342
|
+
measured: "Medido",
|
|
343
|
+
scoreLabel: "Puntuaci\xF3n Pudu-AI",
|
|
344
|
+
reportTitle: "Benchmark Pudu-AI",
|
|
345
|
+
benchmarkTitle: "Benchmark Pudu-AI",
|
|
346
|
+
tasksTitle: "HARNESSES DE TAREAS",
|
|
347
|
+
tasksHint: "Tareas peque\xF1as estilo OpenCode en tu idioma. Primero modelos instalados. El cat\xE1logo es Estimado (CanIRun.ai de midudev).",
|
|
348
|
+
tasksKinds: "Tipos de trabajo",
|
|
349
|
+
tasksScope: "Alcance",
|
|
350
|
+
tasksPriority: "Prioridad",
|
|
351
|
+
tasksInstalled: "instalado",
|
|
352
|
+
tasksNotInstalled: "no instalado",
|
|
353
|
+
tasksEmpty: "No hay modelos que coincidan con esos tipos de trabajo en este equipo.",
|
|
354
|
+
tasksQ1: "\xBFQu\xE9 quieres hacer? (espacio para marcar, enter para seguir)",
|
|
355
|
+
tasksQ2: "\xBFSolo modelos instalados, o tambi\xE9n estimaciones del cat\xE1logo?",
|
|
356
|
+
tasksQ3: "\xBFPrioridad?",
|
|
357
|
+
tasksOptCode: "C\xF3digo",
|
|
358
|
+
tasksOptVideo: "V\xEDdeo",
|
|
359
|
+
tasksOptImage: "Imagen",
|
|
360
|
+
tasksOptTranscription: "Transcripci\xF3n",
|
|
361
|
+
tasksOptChat: "Tareas / chat",
|
|
362
|
+
tasksOptInstalled: "Solo instalados",
|
|
363
|
+
tasksOptAll: "Instalados + cat\xE1logo (estimado)",
|
|
364
|
+
tasksOptSpeed: "Velocidad",
|
|
365
|
+
tasksOptBalanced: "Equilibrado",
|
|
366
|
+
tasksOptQuality: "Calidad",
|
|
367
|
+
taskCodeReviewTitle: "Revisar un diff local",
|
|
368
|
+
taskCodeReviewPrompt: "En OpenCode: abre un git diff y pide a este modelo bugs, tests faltantes y un resumen de 5 l\xEDneas. No apliques parches salvo que lo pidas.",
|
|
369
|
+
taskCodeTestsTitle: "Escribir un test que falle",
|
|
370
|
+
taskCodeTestsPrompt: "Elige una funci\xF3n. Pide un solo test Vitest/Jest que falle con el bug actual. No generes archivos extra.",
|
|
371
|
+
taskChatPlanTitle: "Convertir un objetivo en tareas",
|
|
372
|
+
taskChatPlanPrompt: "Dale un objetivo. Exige un plan numerado de 5 tareas tama\xF1o harness. Sin implementar todav\xEDa.",
|
|
373
|
+
taskChatAgentTitle: "Bucle de agente en un archivo",
|
|
374
|
+
taskChatAgentPrompt: "Se\xF1ala un archivo y un criterio de aceptaci\xF3n. Solo puede editar ese archivo y luego parar.",
|
|
375
|
+
taskImageCaptionTitle: "Describir una imagen local",
|
|
376
|
+
taskImageCaptionPrompt: "Si es un modelo de visi\xF3n/imagen, describe una imagen local en el idioma de la UI. Si es solo texto, indica N/D.",
|
|
377
|
+
taskImageBriefTitle: "Brief de generaci\xF3n de imagen",
|
|
378
|
+
taskImageBriefPrompt: "Pide un brief de 6 l\xEDneas (sujeto, lente, luz, negative prompt) para un still. No inventes que el modelo renderiz\xF3 la imagen.",
|
|
379
|
+
taskVideoBoardTitle: "Storyboard de 8 planos",
|
|
380
|
+
taskVideoBoardPrompt: "Pide 8 planos: duraci\xF3n, c\xE1mara, acci\xF3n, texto en pantalla. Local. No afirmes que existe un render.",
|
|
381
|
+
taskVideoShotTitle: "Lista de planos desde un guion",
|
|
382
|
+
taskVideoShotPrompt: "Pega un guion corto. Pide lista de planos y segundos estimados. Un modelo de texto puede planear; no puede encodear v\xEDdeo.",
|
|
383
|
+
taskTranscribeCleanTitle: "Limpiar una transcripci\xF3n",
|
|
384
|
+
taskTranscribeCleanPrompt: "Pega texto de speech-to-text ruidoso. Pide puntuaci\xF3n, quitar muletillas y mantener hablantes. Esto no es ASR; edita texto.",
|
|
385
|
+
taskTranscribeActionsTitle: "Acciones desde una transcripci\xF3n",
|
|
386
|
+
taskTranscribeActionsPrompt: "De una reuni\xF3n transcrita, extrae responsables, fechas y preguntas abiertas como checklist en el idioma de la UI.",
|
|
387
|
+
launchTitle: "INTEGRACIONES OLLAMA",
|
|
388
|
+
launchHint: "Por defecto solo explica. Pull/instalaci\xF3n/launch solo con --yes y solo si un modelo recomendado cabe en este hardware (nota S\u2013B; velocidad medida si existe). Docs: OpenCode, OpenClaw, Hermes, Claude Code v\xEDa Ollama.",
|
|
389
|
+
launchNeedOllama: "Ollama no est\xE1 detectado. Inst\xE1lalo primero. Pudu-AI no lo instala.",
|
|
390
|
+
launchNoModel: "Ning\xFAn modelo de c\xF3digo/chat en este equipo cumple el umbral de hardware para esta integraci\xF3n.",
|
|
391
|
+
launchGradeFail: "La nota {grade} est\xE1 por debajo del conjunto permitido ({allowed}).",
|
|
392
|
+
launchSlowFail: "Los {tps} t/s medidos est\xE1n por debajo del m\xEDnimo de agente ({min} t/s).",
|
|
393
|
+
launchEstimateWeak: "Solo hay una estimaci\xF3n d\xE9bil; no se har\xE1 pull ni launch.",
|
|
394
|
+
launchOk: "Elegible en este hardware para las tareas recomendadas.",
|
|
395
|
+
launchModel: "Modelo",
|
|
396
|
+
launchRunHint: "Para hacer pull (si hace falta) y lanzar: npx pudu-ai launch {tool} --yes",
|
|
397
|
+
launchBlocked: "Bloqueado. Sin pull, instalaci\xF3n ni launch.",
|
|
398
|
+
launchNeedTool: "Indica una herramienta: opencode | openclaw | hermes | claude",
|
|
399
|
+
launchUnknown: "Integraci\xF3n desconocida.",
|
|
400
|
+
launchPulling: "Descargando {model} con ollama pull\u2026",
|
|
401
|
+
launchPullFail: "Fall\xF3 ollama pull.",
|
|
402
|
+
launchExecFail: "Fall\xF3 ollama launch.",
|
|
403
|
+
help: `Pudu-AI \u2014 laboratorio local de hardware y benchmarks de IA
|
|
404
|
+
|
|
405
|
+
Uso:
|
|
406
|
+
npx pudu-ai
|
|
407
|
+
npx pudu-ai hardware
|
|
408
|
+
npx pudu-ai models
|
|
409
|
+
npx pudu-ai models add-path ~/Models
|
|
410
|
+
npx pudu-ai setup
|
|
411
|
+
npx pudu-ai recommend
|
|
412
|
+
npx pudu-ai recommend --install --yes
|
|
413
|
+
npx pudu-ai recommend --link opencode --yes
|
|
414
|
+
npx pudu-ai tasks
|
|
415
|
+
npx pudu-ai tasks --for code,image --scope all --priority speed
|
|
416
|
+
npx pudu-ai benchmark [model]
|
|
417
|
+
npx pudu-ai compare
|
|
418
|
+
npx pudu-ai history
|
|
419
|
+
npx pudu-ai doctor
|
|
420
|
+
npx pudu-ai report --markdown
|
|
421
|
+
npx pudu-ai launch
|
|
422
|
+
npx pudu-ai launch opencode
|
|
423
|
+
npx pudu-ai launch opencode --yes
|
|
424
|
+
npx pudu-ai repo search CONSULTA
|
|
425
|
+
npx pudu-ai repo search --structural 'def $FUNC($$$ARGS): $$$BODY'
|
|
426
|
+
|
|
427
|
+
Flags:
|
|
428
|
+
--json JSON legible por m\xE1quinas (sin TUI)
|
|
429
|
+
--csv Salida CSV
|
|
430
|
+
--no-network Omite la API de CanIRun; usa cach\xE9/estimaciones locales
|
|
431
|
+
--no-color Sin color ANSI
|
|
432
|
+
--verbose Logs de depuraci\xF3n en stderr
|
|
433
|
+
--preset quick | standard | stress
|
|
434
|
+
--lang en | es
|
|
435
|
+
--for code,video,image,transcription,chat
|
|
436
|
+
--scope installed | all
|
|
437
|
+
--priority speed | balanced | quality
|
|
438
|
+
--yes Ejecuta pull/launch solo si el modelo es elegible
|
|
439
|
+
--repo Ra\xEDz del repositorio para Agent Lab
|
|
440
|
+
--structural Patr\xF3n ast-grep para repo search
|
|
441
|
+
--intent TEXT | STRUCTURAL | RELATIONSHIP | IMPACT | SEMANTIC | UNKNOWN
|
|
442
|
+
--glob Glob de inclusi\xF3n (repetible)
|
|
443
|
+
--limit M\xE1ximo de coincidencias
|
|
444
|
+
|
|
445
|
+
Cr\xE9ditos:
|
|
446
|
+
Los valores medidos vienen de llama-bench y de la telemetr\xEDa del SO.
|
|
447
|
+
Los valores estimados vienen de CanIRun.ai de midudev
|
|
448
|
+
(https://canirun.ai \xB7 https://github.com/midudev/canirun.ai \xB7 https://midu.dev)
|
|
449
|
+
o de un fallback local, y se etiquetan como tal.
|
|
450
|
+
`
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// src/i18n/index.ts
|
|
454
|
+
var locales = { en, es };
|
|
455
|
+
var current = "en";
|
|
456
|
+
function resolveLocale(raw) {
|
|
457
|
+
const value = (raw ?? process.env.PUDU_AI_LANG ?? process.env.PUDU_LANG ?? process.env.LANG ?? "en").toLowerCase();
|
|
458
|
+
if (value.startsWith("es")) return "es";
|
|
459
|
+
return "en";
|
|
460
|
+
}
|
|
461
|
+
function setLocale(locale) {
|
|
462
|
+
current = locale in locales ? locale : "en";
|
|
463
|
+
}
|
|
464
|
+
function t(id, vars) {
|
|
465
|
+
const table = locales[current] ?? locales.en;
|
|
466
|
+
let text = table[id] ?? locales.en[id];
|
|
467
|
+
if (vars) {
|
|
468
|
+
for (const [key, value] of Object.entries(vars)) {
|
|
469
|
+
text = text.replaceAll(`{${key}}`, String(value));
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return text;
|
|
473
|
+
}
|
|
474
|
+
|
|
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
|
+
export {
|
|
494
|
+
resolveLocale,
|
|
495
|
+
setLocale,
|
|
496
|
+
t,
|
|
497
|
+
formatNumber,
|
|
498
|
+
formatPercent,
|
|
499
|
+
formatTokensPerSec,
|
|
500
|
+
na
|
|
501
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// src/shared/process.ts
|
|
2
|
+
import { execa } from "execa";
|
|
3
|
+
var children = /* @__PURE__ */ new Set();
|
|
4
|
+
var installed = false;
|
|
5
|
+
function installSignalHandlers() {
|
|
6
|
+
if (installed) return;
|
|
7
|
+
installed = true;
|
|
8
|
+
const stop = () => {
|
|
9
|
+
void killAll("SIGTERM");
|
|
10
|
+
};
|
|
11
|
+
process.once("SIGINT", stop);
|
|
12
|
+
process.once("SIGTERM", stop);
|
|
13
|
+
}
|
|
14
|
+
function spawnTracked(file, args = [], options = {}) {
|
|
15
|
+
installSignalHandlers();
|
|
16
|
+
const subprocess = execa(file, args, {
|
|
17
|
+
reject: false,
|
|
18
|
+
timeout: options.timeout === void 0 ? 3e4 : options.timeout === 0 ? void 0 : options.timeout,
|
|
19
|
+
...options
|
|
20
|
+
});
|
|
21
|
+
children.add(subprocess);
|
|
22
|
+
void subprocess.finally(() => {
|
|
23
|
+
children.delete(subprocess);
|
|
24
|
+
});
|
|
25
|
+
return subprocess;
|
|
26
|
+
}
|
|
27
|
+
function toText(value) {
|
|
28
|
+
if (typeof value === "string") return value;
|
|
29
|
+
if (value instanceof Uint8Array) return Buffer.from(value).toString("utf8");
|
|
30
|
+
if (Array.isArray(value)) return value.map(String).join("\n");
|
|
31
|
+
if (value == null) return "";
|
|
32
|
+
return String(value);
|
|
33
|
+
}
|
|
34
|
+
async function runCommand(file, args = [], options = {}) {
|
|
35
|
+
const result = await spawnTracked(file, args, options);
|
|
36
|
+
return {
|
|
37
|
+
stdout: toText(result.stdout),
|
|
38
|
+
stderr: toText(result.stderr),
|
|
39
|
+
exitCode: result.exitCode,
|
|
40
|
+
timedOut: Boolean(result.timedOut)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async function killAll(signal = "SIGTERM") {
|
|
44
|
+
for (const child of [...children]) {
|
|
45
|
+
child.kill(signal);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/shared/which.ts
|
|
50
|
+
import { access } from "node:fs/promises";
|
|
51
|
+
import { constants } from "node:fs";
|
|
52
|
+
import path from "node:path";
|
|
53
|
+
async function commandExists(name) {
|
|
54
|
+
if (name.includes("/") || name.includes("\\")) {
|
|
55
|
+
try {
|
|
56
|
+
await access(name, constants.X_OK);
|
|
57
|
+
return name;
|
|
58
|
+
} catch {
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const pathEnv = process.env.PATH ?? "";
|
|
63
|
+
const parts = pathEnv.split(path.delimiter);
|
|
64
|
+
for (const dir of parts) {
|
|
65
|
+
const candidate = path.join(dir, name);
|
|
66
|
+
try {
|
|
67
|
+
await access(candidate, constants.X_OK);
|
|
68
|
+
return candidate;
|
|
69
|
+
} catch {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return void 0;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export {
|
|
77
|
+
spawnTracked,
|
|
78
|
+
toText,
|
|
79
|
+
runCommand,
|
|
80
|
+
killAll,
|
|
81
|
+
commandExists
|
|
82
|
+
};
|