// Device-side agent ops: exec, file read/write/list, task delegation (with a fake // session controller), forge login install into a temp HOME. import test from 'node:test' import assert from 'node:assert/strict' import fs from 'node:fs' import os from 'node:os' import path from 'node:path' import * as ops from '../lib/agent-ops.mjs' test('exec: exit code, stdout/stderr, cwd, timeout', async () => { const r = await ops.execCommand({ command: 'echo out; echo err >&2; pwd; exit 3', cwd: '/tmp' }) assert.equal(r.exitCode, 3) assert.match(r.stdout, /^out\n\/tmp\n$/) assert.equal(r.stderr, 'err\n') const t = await ops.execCommand({ command: 'sleep 5', timeoutMs: 1000 }) assert.equal(t.timedOut, true) assert.notEqual(t.exitCode, 0) await assert.rejects(ops.execCommand({ command: 'true', cwd: '/nonexistent/dir' }), /cwd does not exist/) }) test('files: write, append, read (text + binary), list', () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'dshr-ops-')) ops.writeFileOp({ path: 'a/b.txt', cwd: dir, content: 'hello' }) ops.writeFileOp({ path: 'a/b.txt', cwd: dir, content: ' world', append: true }) assert.equal(ops.readFileOp({ path: path.join(dir, 'a/b.txt') }).content, 'hello world') ops.writeFileOp({ path: 'bin', cwd: dir, content: Buffer.from([0, 1, 2]).toString('base64'), encoding: 'base64' }) const b = ops.readFileOp({ path: 'bin', cwd: dir }) assert.equal(b.encoding, 'base64') assert.equal(b.content, 'AAEC') const l = ops.listDirOp({ path: dir }) assert.deepEqual(l.entries.map((e) => `${e.type}:${e.name}`).sort(), ['dir:a', 'file:bin']) fs.rmSync(dir, { recursive: true, force: true }) }) test('task: creates a session, prompts, waits for turn/end, returns last answer', async () => { const events = [{ seq: 0, type: 'session/start' }] let prompted = null const sc = { async create(req) { assert.ok(req.cwd || req.workspaceId); return { sessionId: 'session-x' } }, async inspect() { return { events: [...events] } }, async prompt(req) { prompted = req setTimeout(() => { events.push({ seq: 1, type: 'turn/start', data: { turn: 1 } }) events.push({ seq: 2, type: 'tool/call', data: { name: 'bash' } }) events.push({ seq: 3, type: 'assistant/message', data: { message: { content: [{ type: 'text', text: 'all 12 tests passed' }] } } }) events.push({ seq: 4, type: 'turn/end', data: { reason: { kind: 'completed' } } }) }, 300) return { accepted: true } }, } const r = await ops.runTask(sc, null, { prompt: 'run the tests', cwd: os.tmpdir(), timeoutMs: 15000 }) assert.equal(prompted.content[0].text, 'run the tests') assert.equal(r.sessionId, 'session-x') assert.equal(r.status, 'completed') assert.equal(r.answer, 'all 12 tests passed') assert.deepEqual(r.tools, { bash: 1 }) const s = await ops.taskStatus(sc, { sessionId: 'session-x' }) assert.equal(s.status, 'completed') assert.equal(s.answer, 'all 12 tests passed') }) test('forge logins: tea config + git credential store in a temp HOME', async () => { const home = fs.mkdtempSync(path.join(os.tmpdir(), 'dshr-home-')) const prev = { HOME: process.env.HOME, XDG: process.env.XDG_CONFIG_HOME } process.env.HOME = home delete process.env.XDG_CONFIG_HOME const origHomedir = os.homedir os.homedir = () => home try { const r = await ops.installForgeLogins([{ name: 'Agent', url: 'https://git.example.com', user: 'Agent', token: 'tok123' }]) assert.deepEqual(r.installed, ['Agent@git.example.com']) const tea = fs.readFileSync(path.join(home, '.config/tea/config.yml'), 'utf8') assert.match(tea, /token: tok123/) assert.equal(fs.statSync(path.join(home, '.git-credentials')).mode & 0o777, 0o600) assert.match(fs.readFileSync(path.join(home, '.git-credentials'), 'utf8'), /^https:\/\/Agent:tok123@git\.example\.com$/m) const helper = await ops.execCommand({ command: 'HOME=' + home + ' git config --global credential.helper' }) assert.equal(helper.stdout.trim(), 'store') } finally { os.homedir = origHomedir process.env.HOME = prev.HOME if (prev.XDG) process.env.XDG_CONFIG_HOME = prev.XDG fs.rmSync(home, { recursive: true, force: true }) } })