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
This commit is contained in:
1 parent
8872e73e76
commit
d1e134196d
7 files changed
+625
-8
No files matched your search
@@ -0,0 +1,31 @@
|
||||
#!/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)}`)
|
||||
}
|
||||
Reference in new issue
Block a user