feat: AcFun tools plugin for DeepSeek Harness
Eight tools (search, video, comments, danmaku, rank, playurl, interact, post_comment) over acfun.cn's public web API, a model-facing skill, a bundle patch with a skill-filesystem row, and offline/live checks. Reads are anonymous; writes require allowWriteActions plus a login cookie.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Live post-install smoke test for the AcFun read tools.
|
||||
*
|
||||
* Exercises the same tool definitions the Harness registers, against the live
|
||||
* acfun.cn API, validates every canonical value against the tool's declared
|
||||
* output schema, and renders it. Write tools are expected to fail closed.
|
||||
*
|
||||
* Run with: node scripts/live-check.mjs
|
||||
*/
|
||||
|
||||
import { AcfunProvider } from '../lib/provider.js'
|
||||
import { resolveConfig } from '../lib/config.js'
|
||||
import { createAcfunTools } from '../lib/tools.js'
|
||||
import { validateValue } from './schema-check.mjs'
|
||||
|
||||
const provider = new AcfunProvider({ requestTimeoutMs: 30_000 })
|
||||
const config = resolveConfig({})
|
||||
const tools = new Map(createAcfunTools(provider, config).map((tool) => [tool.name, tool]))
|
||||
const exec = { signal: new AbortController().signal }
|
||||
const failures = []
|
||||
|
||||
/** Execute one tool, validate its canonical output and render it. */
|
||||
async function run(toolName, args, { expectError } = {}) {
|
||||
const tool = tools.get(toolName)
|
||||
if (tool === undefined) {
|
||||
failures.push(`${toolName}: not registered`)
|
||||
return undefined
|
||||
}
|
||||
console.log(`\n=== ${toolName} ${JSON.stringify(args)}`)
|
||||
const started = Date.now()
|
||||
let value
|
||||
try {
|
||||
value = await tool.execute(args, exec)
|
||||
} catch (error) {
|
||||
if (expectError !== undefined) {
|
||||
if (error?.code === expectError) {
|
||||
console.log(` ok failed closed with ${error.code}: ${error.message}`)
|
||||
return undefined
|
||||
}
|
||||
failures.push(`${toolName}: expected ${expectError}, got ${error?.code ?? error?.name}: ${error?.message}`)
|
||||
return undefined
|
||||
}
|
||||
failures.push(`${toolName}: threw ${error?.name}: ${error?.message}`)
|
||||
return undefined
|
||||
}
|
||||
if (expectError !== undefined) {
|
||||
failures.push(`${toolName}: expected ${expectError} but the call succeeded`)
|
||||
return value
|
||||
}
|
||||
const schemaErrors = validateValue(value, tool.output.schema)
|
||||
if (schemaErrors.length > 0) failures.push(`${toolName}: output schema violations: ${schemaErrors.join('; ')}`)
|
||||
else console.log(' ok canonical value matches output schema')
|
||||
let rendered
|
||||
try {
|
||||
rendered = tool.output.render(args, value)
|
||||
} catch (error) {
|
||||
failures.push(`${toolName}: render threw ${error?.message}`)
|
||||
return value
|
||||
}
|
||||
const text = rendered.map((block) => block.text).join('\n')
|
||||
console.log(` ok rendered ${text.length} chars in ${Date.now() - started} ms`)
|
||||
console.log(
|
||||
text
|
||||
.split('\n')
|
||||
.slice(0, 12)
|
||||
.map((line) => ` | ${line}`)
|
||||
.join('\n'),
|
||||
)
|
||||
return value
|
||||
}
|
||||
|
||||
const search = await run('acfun_search', { keyword: '原神' })
|
||||
await run('acfun_search', { keyword: '原神', sort: 'views' })
|
||||
const acId = search?.items?.[0]?.acId ?? '42588725'
|
||||
console.log(`\n(using acId ${acId} for the follow-up calls)`)
|
||||
|
||||
const video = await run('acfun_video', { acId })
|
||||
await run('acfun_comments', { acId, page: 1 })
|
||||
await run('acfun_danmaku', { acId, limit: 50 })
|
||||
await run('acfun_rank', { period: 'day', limit: 5 })
|
||||
await run('acfun_playurl', { acId })
|
||||
if (video !== undefined && video.title.length === 0) failures.push('acfun_video: empty title')
|
||||
|
||||
// Interactions must fail closed without allowWriteActions + cookie.
|
||||
await run('acfun_interact', { action: 'like', acId }, { expectError: 'WRITE_DISABLED' })
|
||||
await run('acfun_post_comment', { acId, content: 'live-check should not post this' }, { expectError: 'WRITE_DISABLED' })
|
||||
|
||||
if (failures.length > 0) {
|
||||
console.error(`\n${failures.length} live check(s) failed`)
|
||||
for (const failure of failures) console.error(`- ${failure}`)
|
||||
process.exitCode = 1
|
||||
} else {
|
||||
console.log('\nall live read checks passed; write tools failed closed as designed')
|
||||
}
|
||||
Reference in New Issue
Block a user