duckhunt-runner 0.2.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/dist/daemon.js ADDED
@@ -0,0 +1,356 @@
1
+ // loop del daemon (contrato 42): reclama runs por poll, lanza `claude -p` headless en el
2
+ // checkout del repo (o en el scratch dir si no hay repo) con el mcp de duckhunt adjunto y el
3
+ // perfil de tools del claim, y reporta el cierre. un run a la vez: el cómputo es la máquina del
4
+ // usuario. el server nunca ve paths ni perfiles — el mapa repo→checkout y cuenta→perfil vive
5
+ // en la config local.
6
+ import { execFile, spawn } from 'node:child_process';
7
+ import crypto from 'node:crypto';
8
+ import fs from 'node:fs';
9
+ import os from 'node:os';
10
+ import path from 'node:path';
11
+ import readline from 'node:readline';
12
+ import { promisify } from 'node:util';
13
+ import { refreshAccess } from './oauth.js';
14
+ import { logsDir, scratchDir } from './config.js';
15
+ import { consumeStreamLine, detectClaude, newStreamState } from './claude.js';
16
+ const execFileP = promisify(execFile);
17
+ const CLAIM_POLL_MS = 5_000;
18
+ // margen para refrescar el access token del daemon antes de que caduque.
19
+ const TOKEN_REFRESH_MARGIN_MS = 5 * 60_000;
20
+ // SIGTERM → SIGKILL si el proceso no muere en este margen.
21
+ const KILL_GRACE_MS = 10_000;
22
+ // throttle del reporte de tool calls al server.
23
+ const PROGRESS_MIN_INTERVAL_MS = 5_000;
24
+ const STDERR_TAIL_LINES = 20;
25
+ const STDERR_TAIL_MAX_CHARS = 4000;
26
+ export class RunnerDaemon {
27
+ cfg;
28
+ opts;
29
+ access = null;
30
+ claude = { version: null, supported: new Set() };
31
+ hasAws = false;
32
+ constructor(cfg, opts = {}) {
33
+ this.cfg = cfg;
34
+ this.opts = opts;
35
+ }
36
+ async accessToken() {
37
+ if (!this.access || this.access.expiresAt - Date.now() < TOKEN_REFRESH_MARGIN_MS) {
38
+ this.access = await refreshAccess(this.cfg);
39
+ }
40
+ return this.access.token;
41
+ }
42
+ // fetch autenticado contra /api/runner con un reintento tras refresh en 401.
43
+ async api(pathname, body) {
44
+ const call = async () => fetch(`${this.cfg.baseUrl}/api/runner${pathname}`, {
45
+ method: 'POST',
46
+ headers: {
47
+ Authorization: `Bearer ${await this.accessToken()}`,
48
+ 'Content-Type': 'application/json',
49
+ },
50
+ body: body === undefined ? '{}' : JSON.stringify(body),
51
+ });
52
+ const first = await call();
53
+ if (first.status !== 401)
54
+ return first;
55
+ this.access = null;
56
+ return call();
57
+ }
58
+ label() {
59
+ return this.cfg.label ?? os.hostname();
60
+ }
61
+ /** loop principal: claim → ejecutar → repetir. nunca retorna (ctrl-c para salir). */
62
+ async run() {
63
+ this.claude = await detectClaude();
64
+ if (!this.claude.version) {
65
+ throw new Error('no se encuentra `claude` en el PATH: instala claude code y haz login antes de arrancar el daemon');
66
+ }
67
+ this.hasAws = await execFileP('aws', ['--version']).then(() => true).catch(() => false);
68
+ console.log(`[runner] ${this.label()} → ${this.cfg.baseUrl} (claude ${this.claude.version}, aws cli ${this.hasAws ? 'sí' : 'no'}, ${Object.keys(this.cfg.repos).length} repos, ${Object.keys(this.cfg.aws).length} cuentas aws, poll ${CLAIM_POLL_MS / 1000}s)`);
69
+ for (;;) {
70
+ let claim = null;
71
+ try {
72
+ const res = await this.api('/claim', {
73
+ runnerLabel: this.label(),
74
+ repos: Object.keys(this.cfg.repos),
75
+ awsAccounts: Object.keys(this.cfg.aws),
76
+ capabilities: this.hasAws ? ['aws'] : [],
77
+ });
78
+ if (res.status === 200)
79
+ claim = (await res.json());
80
+ else if (res.status !== 204) {
81
+ console.error(`[runner] claim falló: http ${res.status} ${await res.text().catch(() => '')}`);
82
+ }
83
+ }
84
+ catch (err) {
85
+ console.error(`[runner] claim inaccesible: ${err.message}`);
86
+ }
87
+ if (claim) {
88
+ await this.executeRun(claim).catch((err) => {
89
+ console.error(`[runner] run ${claim.run.id} reventó: ${err.message}`);
90
+ });
91
+ }
92
+ else {
93
+ await new Promise((r) => setTimeout(r, CLAIM_POLL_MS));
94
+ }
95
+ }
96
+ }
97
+ // --- cwd del run: checkout mapeado (worktree opcional) o scratch fijo ---
98
+ async prepareWorkdir(run) {
99
+ const repoCfg = run.repo ? this.cfg.repos[run.repo] ?? null : null;
100
+ if (!repoCfg) {
101
+ const note = run.repo ? `repo ${run.repo} no mapeado en este runner: corriendo en scratch` : null;
102
+ return { workdir: scratchDir(), repoCfg: null, worktree: null, note };
103
+ }
104
+ if (!fs.existsSync(repoCfg.path)) {
105
+ return { workdir: scratchDir(), repoCfg: null, worktree: null, note: `checkout de ${run.repo} no existe (${repoCfg.path}): corriendo en scratch` };
106
+ }
107
+ if (repoCfg.worktree === false)
108
+ return { workdir: repoCfg.path, repoCfg, worktree: null, note: null };
109
+ // aislamiento: worktree git detached por run. la branch del run si existe (local u origin);
110
+ // si no, HEAD. en done se borra; en failed se conserva para autopsia.
111
+ const worktree = path.join(repoCfg.path, '.duckhunt', 'worktrees', `run-${run.id}`);
112
+ const refs = run.branch ? [run.branch, `origin/${run.branch}`, 'HEAD'] : ['HEAD'];
113
+ let lastErr = null;
114
+ for (const ref of refs) {
115
+ try {
116
+ await execFileP('git', ['worktree', 'add', '--detach', worktree, ref], { cwd: repoCfg.path });
117
+ const note = run.branch && ref === 'HEAD' ? `branch ${run.branch} no existe localmente: worktree sobre HEAD` : null;
118
+ return { workdir: worktree, repoCfg, worktree, note };
119
+ }
120
+ catch (err) {
121
+ lastErr = err;
122
+ }
123
+ }
124
+ throw new Error(`git worktree add falló: ${lastErr?.message ?? 'desconocido'}`);
125
+ }
126
+ async removeWorktree(repoPath, worktree) {
127
+ await execFileP('git', ['worktree', 'remove', '--force', worktree], { cwd: repoPath }).catch((err) => {
128
+ console.error(`[runner] limpieza del worktree falló: ${err.message}`);
129
+ });
130
+ }
131
+ // --- env del run: el del usuario + perfil aws mapeado ---
132
+ envFor(run) {
133
+ const env = { ...process.env, AWS_PAGER: '' };
134
+ const account = run.aws?.accountId ? this.cfg.aws[run.aws.accountId] : undefined;
135
+ if (account) {
136
+ env.AWS_PROFILE = account.profile;
137
+ const region = account.region ?? run.aws?.region ?? undefined;
138
+ if (region)
139
+ env.AWS_DEFAULT_REGION = region;
140
+ }
141
+ else if (run.aws?.region) {
142
+ env.AWS_DEFAULT_REGION = run.aws.region;
143
+ }
144
+ return env;
145
+ }
146
+ // --- args de claude ---
147
+ claudeArgs(claim, prompt, mcpFile, repoCfg, resumeSessionId) {
148
+ const has = (f) => this.claude.supported.has(f);
149
+ const args = ['-p', prompt, '--output-format', 'stream-json'];
150
+ if (has('--verbose'))
151
+ args.push('--verbose');
152
+ args.push('--mcp-config', mcpFile);
153
+ if (has('--strict-mcp-config'))
154
+ args.push('--strict-mcp-config');
155
+ if (claim.tools.allowed.length > 0)
156
+ args.push('--allowedTools', claim.tools.allowed.join(','));
157
+ if (has('--disallowedTools') && claim.tools.disallowed.length > 0)
158
+ args.push('--disallowedTools', claim.tools.disallowed.join(','));
159
+ if (has('--permission-mode') && claim.permissionMode)
160
+ args.push('--permission-mode', claim.permissionMode);
161
+ if (has('--max-turns') && claim.maxTurns > 0)
162
+ args.push('--max-turns', String(claim.maxTurns));
163
+ // presupuesto: solo tiene sentido cuando el coste es REAL (api key). con login de
164
+ // suscripción el cli lo aplicaría sobre un coste nominal que nadie paga y mata runs
165
+ // legítimos (el guard contra loops es el timeout). config manda: número = forzar, 0 = nunca.
166
+ const payingWithApiKey = !!process.env.ANTHROPIC_API_KEY;
167
+ const budget = this.cfg.defaults.maxBudgetUsd ?? (payingWithApiKey ? claim.maxBudgetUsd : 0);
168
+ if (has('--max-budget-usd') && budget > 0)
169
+ args.push('--max-budget-usd', String(budget));
170
+ if (this.cfg.defaults.model)
171
+ args.push('--model', this.cfg.defaults.model);
172
+ if (repoCfg?.dangerouslySkipPermissions)
173
+ args.push('--dangerously-skip-permissions');
174
+ if (resumeSessionId)
175
+ args.push('--resume', resumeSessionId);
176
+ return args;
177
+ }
178
+ // --- ejecución ---
179
+ async executeRun(claim) {
180
+ const { run } = claim;
181
+ console.log(`[runner] run ${run.id} reclamado (${run.kind}, entry ${run.entryId}${claim.tier ? `, tier ${claim.tier}` : ''}${run.repo ? `, repo ${run.repo}` : ''}${run.sessionId ? `, sesión ${run.sessionId}` : ''})`);
182
+ let prepared;
183
+ try {
184
+ prepared = await this.prepareWorkdir(run);
185
+ }
186
+ catch (err) {
187
+ await this.postStatus(run.id, { status: 'failed', error: err.message });
188
+ return;
189
+ }
190
+ if (prepared.note)
191
+ console.log(`[runner] run ${run.id}: ${prepared.note}`);
192
+ // mcp-config temporal con el token per-run (0600; se borra al terminar).
193
+ const mcpFile = path.join(os.tmpdir(), `duckhunt-runner-${run.id}-${crypto.randomBytes(4).toString('hex')}.json`);
194
+ fs.writeFileSync(mcpFile, JSON.stringify({
195
+ mcpServers: {
196
+ [claim.mcp.serverName]: { type: 'http', url: claim.mcp.url, headers: { Authorization: `Bearer ${claim.mcp.token}` } },
197
+ },
198
+ }), { mode: 0o600 });
199
+ const env = this.envFor(run);
200
+ let report;
201
+ try {
202
+ // reanudar o no lo decide el server: manda sessionId solo cuando hay sesión que retomar
203
+ // (el run preguntó y ya le respondieron, o es la instrucción sobre un resultado anterior).
204
+ const resume = run.sessionId;
205
+ let attempt = await this.spawnClaude(claim, this.claudeArgs(claim, claim.prompt, mcpFile, prepared.repoCfg, resume), prepared.workdir, env);
206
+ let resumed = resume ? true : undefined;
207
+ // reanudación fallida antes de que el asistente hablara (sesión inexistente en esta
208
+ // máquina, borrada, de otro daemon): un solo reintento desde cero con el prompt de respaldo.
209
+ if (resume && attempt.killedBy === null && attempt.exitCode !== 0 && !attempt.stream.sawAssistant && claim.fallbackPrompt) {
210
+ console.log(`[runner] run ${run.id}: la sesión ${resume} no se pudo reanudar; reintento sin --resume`);
211
+ attempt = await this.spawnClaude(claim, this.claudeArgs(claim, claim.fallbackPrompt, mcpFile, prepared.repoCfg, null), prepared.workdir, env);
212
+ resumed = false;
213
+ }
214
+ report = this.reportFor(attempt, resumed);
215
+ }
216
+ finally {
217
+ fs.rmSync(mcpFile, { force: true });
218
+ }
219
+ await this.postStatus(run.id, report);
220
+ if (prepared.worktree && prepared.repoCfg) {
221
+ if (report.status === 'done')
222
+ await this.removeWorktree(prepared.repoCfg.path, prepared.worktree);
223
+ else
224
+ console.log(`[runner] worktree conservado para autopsia: ${prepared.worktree}`);
225
+ }
226
+ console.log(`[runner] run ${run.id} terminado (${report.status}${report.error ? `: ${report.error}` : ''})`);
227
+ }
228
+ reportFor(attempt, resumed) {
229
+ const s = attempt.stream;
230
+ const base = {
231
+ status: 'failed',
232
+ toolCalls: s.toolCalls,
233
+ ...(s.result?.costUsd !== null && s.result?.costUsd !== undefined ? { costUsd: s.result.costUsd } : {}),
234
+ ...(s.result?.numTurns !== null && s.result?.numTurns !== undefined ? { numTurns: s.result.numTurns } : {}),
235
+ ...(s.result?.sessionId ?? s.sessionId ? { sessionId: (s.result?.sessionId ?? s.sessionId) } : {}),
236
+ ...(s.result?.model ? { model: s.result.model } : {}),
237
+ ...(resumed !== undefined ? { resumed } : {}),
238
+ ...(attempt.stderrTail ? { stderrTail: attempt.stderrTail } : {}),
239
+ };
240
+ if (attempt.killedBy === 'cancel')
241
+ return { ...base, status: 'canceled', error: 'cancelado desde el server' };
242
+ if (attempt.killedBy === 'timeout')
243
+ return { ...base, status: 'failed', error: 'timeout: el run superó el tiempo máximo' };
244
+ if (attempt.exitCode === 0 && s.result && !s.result.isError) {
245
+ return { ...base, status: 'done', ...(s.result.result ? { result: s.result.result } : {}) };
246
+ }
247
+ const budgetHit = s.result?.subtype === 'error_max_budget_usd';
248
+ const reason = s.result?.isError
249
+ ? `claude terminó con error (${s.result.subtype ?? 'error'})${budgetHit ? ' — presupuesto nominal agotado: sube defaults.maxBudgetUsd en ~/.duckhunt-runner.json (0 = sin límite)' : ''}${s.result.result ? `: ${s.result.result.slice(0, 500)}` : ''}`
250
+ : `claude terminó con exit code ${attempt.exitCode}`;
251
+ return { ...base, status: 'failed', error: reason };
252
+ }
253
+ // spawnea claude, consume stdout (stream-json) y stderr (tail), bombea heartbeat (soft-cancel)
254
+ // y progreso, y aplica el timeout wall-clock. resuelve siempre (nunca rechaza).
255
+ spawnClaude(claim, args, cwd, env) {
256
+ const runId = claim.run.id;
257
+ return new Promise((resolve) => {
258
+ const stream = newStreamState();
259
+ const stderrTail = [];
260
+ let killedBy = null;
261
+ let killTimer = null;
262
+ let logFile = null;
263
+ if (this.opts.verboseLog) {
264
+ try {
265
+ logFile = fs.openSync(path.join(logsDir(), `${runId}.jsonl`), 'a');
266
+ }
267
+ catch (err) {
268
+ console.error(`[runner] no se pudo abrir el log del run: ${err.message}`);
269
+ }
270
+ }
271
+ let child;
272
+ try {
273
+ child = spawn('claude', args, { cwd, env, stdio: ['ignore', 'pipe', 'pipe'] });
274
+ }
275
+ catch (err) {
276
+ resolve({ exitCode: null, killedBy: null, stream, stderrTail: `no se pudo lanzar claude: ${err.message}` });
277
+ return;
278
+ }
279
+ const kill = (reason) => {
280
+ if (killedBy)
281
+ return;
282
+ killedBy = reason;
283
+ child.kill('SIGTERM');
284
+ killTimer = setTimeout(() => {
285
+ if (child.exitCode === null && child.signalCode === null)
286
+ child.kill('SIGKILL');
287
+ }, KILL_GRACE_MS);
288
+ };
289
+ readline.createInterface({ input: child.stdout }).on('line', (line) => {
290
+ consumeStreamLine(stream, line);
291
+ if (logFile !== null)
292
+ fs.writeSync(logFile, `${line}\n`);
293
+ });
294
+ readline.createInterface({ input: child.stderr }).on('line', (line) => {
295
+ stderrTail.push(line);
296
+ if (stderrTail.length > STDERR_TAIL_LINES)
297
+ stderrTail.shift();
298
+ if (logFile !== null)
299
+ fs.writeSync(logFile, `${JSON.stringify({ type: 'stderr', line })}\n`);
300
+ });
301
+ // heartbeat: transporta el soft-cancel del server.
302
+ const heartbeat = setInterval(() => {
303
+ void this.api(`/runs/${runId}/heartbeat`)
304
+ .then(async (res) => {
305
+ if (!res.ok)
306
+ return;
307
+ const body = (await res.json().catch(() => null));
308
+ if (body?.cancel) {
309
+ console.log(`[runner] run ${runId} cancelado desde el server; deteniendo claude`);
310
+ kill('cancel');
311
+ }
312
+ })
313
+ .catch((err) => console.error(`[runner] heartbeat falló: ${err.message}`));
314
+ }, Math.max(5_000, claim.heartbeatMs));
315
+ // progreso: tool calls al server, throttled.
316
+ let reportedToolCalls = 0;
317
+ let lastProgressAt = 0;
318
+ const progress = setInterval(() => {
319
+ const now = Date.now();
320
+ if (stream.toolCalls === reportedToolCalls || now - lastProgressAt < PROGRESS_MIN_INTERVAL_MS)
321
+ return;
322
+ reportedToolCalls = stream.toolCalls;
323
+ lastProgressAt = now;
324
+ void this.api(`/runs/${runId}/progress`, { toolCalls: reportedToolCalls }).catch((err) => console.error(`[runner] progress falló: ${err.message}`));
325
+ }, 1_000);
326
+ const timeout = setTimeout(() => {
327
+ console.log(`[runner] run ${runId} superó ${Math.round(claim.timeoutMs / 1000)}s; deteniendo claude`);
328
+ kill('timeout');
329
+ }, Math.max(60_000, claim.timeoutMs));
330
+ child.on('error', (err) => {
331
+ stderrTail.push(`[runner] no se pudo lanzar claude: ${err.message}`);
332
+ });
333
+ child.on('close', (code) => {
334
+ clearInterval(heartbeat);
335
+ clearInterval(progress);
336
+ clearTimeout(timeout);
337
+ if (killTimer)
338
+ clearTimeout(killTimer);
339
+ if (logFile !== null)
340
+ fs.closeSync(logFile);
341
+ resolve({ exitCode: code, killedBy, stream, stderrTail: stderrTail.join('\n').slice(-STDERR_TAIL_MAX_CHARS) });
342
+ });
343
+ });
344
+ }
345
+ async postStatus(runId, report) {
346
+ const res = await this.api(`/runs/${runId}/status`, report).catch((err) => {
347
+ console.error(`[runner] status post falló: ${err.message}`);
348
+ return null;
349
+ });
350
+ // 409 = el server ya cerró el run (cancel del usuario, sweeper): benigno.
351
+ if (res && !res.ok && res.status !== 409) {
352
+ console.error(`[runner] status post rechazado: http ${res.status} ${await res.text().catch(() => '')}`);
353
+ }
354
+ }
355
+ }
356
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ // cli del daemon de duckhunt (contrato 42): `login` conecta la credencial oauth (audiencia
3
+ // /api/runner), `repos`/`aws` mantienen los mapas locales, `status` diagnostica el kit de la
4
+ // máquina y el modo por defecto (`start`) arranca el loop de claim.
5
+ import { loadConfig } from './config.js';
6
+ import { login } from './oauth.js';
7
+ import { RunnerDaemon } from './daemon.js';
8
+ import { reposCommand } from './repos.js';
9
+ import { awsCommand } from './aws.js';
10
+ import { statusCommand } from './status.js';
11
+ const USAGE = `duckhunt-runner — daemon local de agent runs
12
+
13
+ uso:
14
+ duckhunt-runner login <base-url> [label] conecta el runner (pega el código del browser)
15
+ duckhunt-runner status kit de esta máquina: claude, credencial, aws, repos
16
+ duckhunt-runner start [--verbose] arranca el daemon (config en ~/.duckhunt-runner.json)
17
+ duckhunt-runner repos list|add|remove|discover
18
+ duckhunt-runner aws list|add|remove
19
+ `;
20
+ async function main() {
21
+ const [cmd, ...rest] = process.argv.slice(2);
22
+ if (cmd === 'login') {
23
+ const [baseUrl, label] = rest;
24
+ if (!baseUrl) {
25
+ console.error(USAGE);
26
+ process.exitCode = 1;
27
+ return;
28
+ }
29
+ await login(baseUrl, label);
30
+ return;
31
+ }
32
+ if (cmd === 'status') {
33
+ await statusCommand();
34
+ return;
35
+ }
36
+ if (cmd === 'repos') {
37
+ reposCommand(rest);
38
+ return;
39
+ }
40
+ if (cmd === 'aws') {
41
+ awsCommand(rest);
42
+ return;
43
+ }
44
+ if (cmd !== undefined && cmd !== 'start' && cmd !== 'run') {
45
+ console.error(USAGE);
46
+ process.exitCode = 1;
47
+ return;
48
+ }
49
+ const cfg = loadConfig();
50
+ if (!cfg) {
51
+ console.error('sin config: ejecuta `duckhunt-runner login <base-url>` primero');
52
+ process.exitCode = 1;
53
+ return;
54
+ }
55
+ await new RunnerDaemon(cfg, { verboseLog: rest.includes('--verbose') }).run();
56
+ }
57
+ main().catch((err) => {
58
+ console.error(`[runner] ${err.message}`);
59
+ process.exitCode = 1;
60
+ });
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,2FAA2F;AAC3F,6FAA6F;AAC7F,oEAAoE;AAEpE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,KAAK,GAAG;;;;;;;;CAQb,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC7C,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACrB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC5B,OAAO;IACT,CAAC;IACD,IAAI,GAAG,KAAK,QAAQ,EAAE,CAAC;QACrB,MAAM,aAAa,EAAE,CAAC;QACtB,OAAO;IACT,CAAC;IACD,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;QACpB,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,OAAO;IACT,CAAC;IACD,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QAClB,UAAU,CAAC,IAAI,CAAC,CAAC;QACjB,OAAO;IACT,CAAC;IACD,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,KAAK,OAAO,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;QAC1D,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACrB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;QAChF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,OAAO;IACT,CAAC;IACD,MAAM,IAAI,YAAY,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC;AAChF,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,YAAa,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { type RunnerConfig } from './config.js';
2
+ /** login interactivo: registra el cliente, imprime la url de autorización, espera el code
3
+ * pegado por el usuario y persiste clientId + refresh token en la config. */
4
+ export declare function login(baseUrl: string, label?: string): Promise<void>;
5
+ export interface AccessState {
6
+ token: string;
7
+ expiresAt: number;
8
+ }
9
+ /** refresca el access token del daemon. la rotación persiste el refresh nuevo AL INSTANTE
10
+ * (perder el refresh rotado invalida la cadena entera). */
11
+ export declare function refreshAccess(cfg: RunnerConfig): Promise<AccessState>;
12
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAOA,OAAO,EAA0B,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AA8BxE;8EAC8E;AAC9E,wBAAsB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmD1E;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;4DAC4D;AAC5D,wBAAsB,aAAa,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAS3E"}
package/dist/oauth.js ADDED
@@ -0,0 +1,90 @@
1
+ // flujo oauth del daemon contra el authorization server mcp de duckhunt: dcr + pkce + code
2
+ // out-of-band (el usuario aprueba en su browser y pega el code — funciona en máquinas headless
3
+ // donde un loopback redirect no llega). audiencia dedicada /api/runner (rfc 8707): estos tokens
4
+ // no valen en /mcp ni viceversa.
5
+ import crypto from 'node:crypto';
6
+ import readline from 'node:readline/promises';
7
+ import { loadConfig, saveConfig } from './config.js';
8
+ // path del recurso runner en el server (espejo de canonicalRunnerResourceUrl).
9
+ const RUNNER_RESOURCE_PATH = '/api/runner';
10
+ // redirect out-of-band: página del server que muestra el code para pegar.
11
+ const OOB_REDIRECT_PATH = '/oauth/authorized';
12
+ function b64url(buf) {
13
+ return buf.toString('base64url');
14
+ }
15
+ async function postToken(baseUrl, form) {
16
+ const res = await fetch(`${baseUrl}/oauth/token`, {
17
+ method: 'POST',
18
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
19
+ body: new URLSearchParams(form).toString(),
20
+ });
21
+ const body = (await res.json().catch(() => ({})));
22
+ if (!res.ok || !body.access_token || !body.refresh_token) {
23
+ throw new Error(`token endpoint: ${body.error_description ?? body.error ?? `http ${res.status}`}`);
24
+ }
25
+ return body;
26
+ }
27
+ /** login interactivo: registra el cliente, imprime la url de autorización, espera el code
28
+ * pegado por el usuario y persiste clientId + refresh token en la config. */
29
+ export async function login(baseUrl, label) {
30
+ const base = baseUrl.replace(/\/+$/, '');
31
+ const redirectUri = `${base}${OOB_REDIRECT_PATH}`;
32
+ const resource = `${base}${RUNNER_RESOURCE_PATH}`;
33
+ const reg = await fetch(`${base}/oauth/register`, {
34
+ method: 'POST',
35
+ headers: { 'Content-Type': 'application/json' },
36
+ body: JSON.stringify({ client_name: `duckhunt-runner (${label ?? 'local'})`, redirect_uris: [redirectUri] }),
37
+ });
38
+ const client = (await reg.json().catch(() => ({})));
39
+ if (!reg.ok || !client.client_id)
40
+ throw new Error(`dcr falló: http ${reg.status}`);
41
+ const verifier = b64url(crypto.randomBytes(32));
42
+ const challenge = b64url(crypto.createHash('sha256').update(verifier).digest());
43
+ const authorizeUrl = new URL(`${base}/oauth/authorize`);
44
+ authorizeUrl.searchParams.set('response_type', 'code');
45
+ authorizeUrl.searchParams.set('client_id', client.client_id);
46
+ authorizeUrl.searchParams.set('redirect_uri', redirectUri);
47
+ authorizeUrl.searchParams.set('code_challenge', challenge);
48
+ authorizeUrl.searchParams.set('code_challenge_method', 'S256');
49
+ authorizeUrl.searchParams.set('resource', resource);
50
+ console.log('\nabre esta url en tu navegador, autoriza y pega aquí el código:\n');
51
+ console.log(` ${authorizeUrl.toString()}\n`);
52
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
53
+ const code = (await rl.question('código: ')).trim();
54
+ rl.close();
55
+ if (!code)
56
+ throw new Error('código vacío');
57
+ const tokens = await postToken(base, {
58
+ grant_type: 'authorization_code',
59
+ code,
60
+ code_verifier: verifier,
61
+ redirect_uri: redirectUri,
62
+ client_id: client.client_id,
63
+ resource,
64
+ });
65
+ const existing = loadConfig();
66
+ const cfg = {
67
+ baseUrl: base,
68
+ clientId: client.client_id,
69
+ refreshToken: tokens.refresh_token,
70
+ label: label ?? existing?.label,
71
+ repos: existing?.repos ?? {},
72
+ aws: existing?.aws ?? {},
73
+ defaults: existing?.defaults ?? {},
74
+ };
75
+ saveConfig(cfg);
76
+ console.log('\nconectado. config en ~/.duckhunt-runner.json — mapea tus repos (`repos discover ~/dev`) y cuentas aws (`aws add`).');
77
+ }
78
+ /** refresca el access token del daemon. la rotación persiste el refresh nuevo AL INSTANTE
79
+ * (perder el refresh rotado invalida la cadena entera). */
80
+ export async function refreshAccess(cfg) {
81
+ const tokens = await postToken(cfg.baseUrl, {
82
+ grant_type: 'refresh_token',
83
+ refresh_token: cfg.refreshToken,
84
+ client_id: cfg.clientId,
85
+ });
86
+ cfg.refreshToken = tokens.refresh_token;
87
+ saveConfig(cfg);
88
+ return { token: tokens.access_token, expiresAt: Date.now() + tokens.expires_in * 1000 };
89
+ }
90
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export declare function repoKeyFromRemote(url: string): string | null;
2
+ export declare function reposCommand(args: string[]): void;
3
+ //# sourceMappingURL=repos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repos.d.ts","sourceRoot":"","sources":["../src/repos.ts"],"names":[],"mappings":"AAuBA,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAiB5D;AAmCD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CA0HjD"}