Files
lyuksovannyy d1e134196d dsh-remote 0.2.0: agent tools to drive slaves from the master
- remote_devices / remote_exec / remote_file / remote_task tools on the master
  (exec: bash -lc with timeout + output clipping; files: read/write/list;
  task: delegate a prompt to the device's own AI, wait for turn end, return
  its answer; optional permission preset, status/cancel)
- only the master may control devices (allowPeerControl to relay between slaves),
  agentTools.enabled=false opts a device out
- FORGE_CREDENTIALS opt-in pseudo-secret: ships the master's tea logins to a
  slave (tea config + git credential store + identity)
- /dsh-remote/debug/inspect + deploy/session-log.mjs for transcripts
- tests for exec/files/task/forge install
2026-10-11 10:08:04 +00:00

32 lines
1.9 KiB
JavaScript

#!/usr/bin/env node
// Print a compact transcript of a local DSH session (tool calls, results, answers).
// node deploy/session-log.mjs <sessionId> [sinceSeq]
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import { mintCookie } from '../lib/auth-cookie.mjs'
const [sid, since = '-1'] = process.argv.slice(2)
const port = process.env.DSH_PORT || '3081'
const home = process.env.DSH_HOME || path.join(os.homedir(), '.dsh')
const secret = fs.readFileSync(path.join(home, '.credentials.yaml'), 'utf8').match(/browser-session:[\s\S]*?secret:\s*([A-Za-z0-9_-]{43})/)[1]
const authority = `127.0.0.1:${port}`
const device = process.env.DEVICE ? `&device=${encodeURIComponent(process.env.DEVICE)}` : ''
const res = await fetch(`http://${authority}/dsh-remote/debug/inspect?sessionId=${encodeURIComponent(sid)}${device}`, { headers: { cookie: mintCookie(authority, secret, 1) } })
const j = await res.json()
if (!res.ok) { console.error(res.status, j); process.exit(1) }
const short = (s, n = 300) => { const t = String(s ?? '').replace(/\s+/g, ' '); return t.length > n ? t.slice(0, n) + '…' : t }
for (const e of j.events) {
if (e.seq <= Number(since)) continue
const d = e.data || {}
if (e.type === 'user/message') console.log(`#${e.seq} USER: ${short(d.content?.map((c) => c.text).join(' '), 200)}`)
else if (e.type === 'tool/call') console.log(`#${e.seq} CALL ${d.name}: ${short(JSON.stringify(d.arguments), 260)}`)
else if (e.type === 'tool/result') {
const c = d.message?.content || []
console.log(`#${e.seq} → ${short(c.map((x) => x.text ?? JSON.stringify(x)).join(' '), 360)}`)
} else if (e.type === 'assistant/message') {
const t = (d.message?.content || []).filter((c) => c.type === 'text').map((c) => c.text).join(' ')
if (t) console.log(`#${e.seq} AI: ${short(t, 1200)}`)
} else if (e.type === 'turn/end') console.log(`#${e.seq} TURN END ${JSON.stringify(d.reason)}`)
}