|
|
@@ -0,0 +1,67 @@
|
|
|
+#!/usr/bin/env node
|
|
|
+/**
|
|
|
+ * Test driver: boot the tool-pwsh Loader composition, execute one real
|
|
|
+ * foreground and one real background pwsh command through the tool registry,
|
|
|
+ * and persist the observed model-visible output to `./pwsh-loader-report.json`
|
|
|
+ * for the package spec's inspect step.
|
|
|
+ */
|
|
|
+
|
|
|
+import { writeFile } from 'node:fs/promises'
|
|
|
+import { boot, resolveConfigPath } from '@deepseek-ai/dsh-app-boot'
|
|
|
+import { CallId } from '@deepseek-ai/dsh-llm'
|
|
|
+
|
|
|
+const configPath = process.argv[2]
|
|
|
+if (configPath === undefined) throw new Error('tool-pwsh driver requires a config path')
|
|
|
+
|
|
|
+const ctx = await boot('tool-pwsh-loader-smoke', resolveConfigPath(configPath, undefined))
|
|
|
+try {
|
|
|
+ const schema = ctx.tools.schemas().find(tool => tool.name === 'pwsh')
|
|
|
+ if (schema === undefined) throw new Error('pwsh tool not registered by the composition')
|
|
|
+ const prompt = (await ctx.systemPrompt.assemble()).sections.find(section => section.name === 'tool:pwsh')
|
|
|
+
|
|
|
+ const foreground = await ctx.tools.execute({
|
|
|
+ signal: new AbortController().signal,
|
|
|
+ callId: CallId('loader-fg'),
|
|
|
+ name: 'pwsh',
|
|
|
+ arguments: { command: 'Write-Output loader-ok', description: 'loader foreground' },
|
|
|
+ })
|
|
|
+ const foregroundText = foreground.content.filter(block => block.type === 'text').map(block => block.text).join('')
|
|
|
+
|
|
|
+ const background = await ctx.tools.execute({
|
|
|
+ signal: new AbortController().signal,
|
|
|
+ callId: CallId('loader-bg'),
|
|
|
+ name: 'pwsh',
|
|
|
+ arguments: {
|
|
|
+ command: 'Start-Sleep -Milliseconds 200; Write-Output loader-bg-ok',
|
|
|
+ description: 'loader background',
|
|
|
+ run_in_background: true,
|
|
|
+ },
|
|
|
+ })
|
|
|
+ const taskId = (background.value as { taskId: string }).taskId
|
|
|
+
|
|
|
+ // The output delta and the terminal status can land in separate reads
|
|
|
+ // (Windows flushes the child pipe at exit), so accumulate both.
|
|
|
+ let backgroundText = ''
|
|
|
+ const deadline = Date.now() + 10_000
|
|
|
+ while (Date.now() < deadline) {
|
|
|
+ const read = await ctx.tools.execute({
|
|
|
+ signal: new AbortController().signal,
|
|
|
+ callId: CallId('loader-bg-read'),
|
|
|
+ name: 'task_output',
|
|
|
+ arguments: { task_id: taskId },
|
|
|
+ })
|
|
|
+ backgroundText += read.content.filter(block => block.type === 'text').map(block => block.text).join('')
|
|
|
+ if (backgroundText.includes('loader-bg-ok') && backgroundText.includes('[status: completed')) break
|
|
|
+ await new Promise(resolve => setTimeout(resolve, 50))
|
|
|
+ }
|
|
|
+
|
|
|
+ await writeFile('./pwsh-loader-report.json', JSON.stringify({
|
|
|
+ schemaHasRunInBackground: Object.hasOwn(schema.parameters.properties as object, 'run_in_background'),
|
|
|
+ promptHasMarkerSection: prompt?.text.includes('Non-zero exits are reported as `[exit code: N]` markers') === true,
|
|
|
+ // Normalize PowerShell's platform line endings (CRLF on Windows, LF elsewhere).
|
|
|
+ foregroundText: foregroundText.replace(/\r\n/g, '\n'),
|
|
|
+ backgroundText: backgroundText.replace(/\r\n/g, '\n'),
|
|
|
+ }))
|
|
|
+} finally {
|
|
|
+ await ctx.fiber.dispose()
|
|
|
+}
|