- 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
32 lines
1.9 KiB
JavaScript
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)}`)
|
|
}
|