@plaited/acp-harness 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/headless-cli.ts +7 -4
- package/src/headless-session-manager.ts +32 -10
package/package.json
CHANGED
package/src/headless-cli.ts
CHANGED
|
@@ -235,9 +235,10 @@ const mapToACPUpdate = (update: { type: string; content?: string; title?: string
|
|
|
235
235
|
* Runs the headless adapter main loop.
|
|
236
236
|
*
|
|
237
237
|
* @param schema - Headless adapter configuration
|
|
238
|
+
* @param verbose - Whether to show debug output
|
|
238
239
|
*/
|
|
239
|
-
const runAdapter = async (schema: HeadlessAdapterConfig): Promise<void> => {
|
|
240
|
-
const sessions = createSessionManager({ schema })
|
|
240
|
+
const runAdapter = async (schema: HeadlessAdapterConfig, verbose = false): Promise<void> => {
|
|
241
|
+
const sessions = createSessionManager({ schema, verbose })
|
|
241
242
|
const handlers = createHandlers(schema, sessions)
|
|
242
243
|
|
|
243
244
|
// Method handlers (requests expect responses)
|
|
@@ -349,6 +350,7 @@ export const headless = async (args: string[]): Promise<void> => {
|
|
|
349
350
|
args,
|
|
350
351
|
options: {
|
|
351
352
|
schema: { type: 'string', short: 's' },
|
|
353
|
+
verbose: { type: 'boolean', short: 'v' },
|
|
352
354
|
help: { type: 'boolean', short: 'h' },
|
|
353
355
|
},
|
|
354
356
|
allowPositionals: false,
|
|
@@ -357,10 +359,11 @@ export const headless = async (args: string[]): Promise<void> => {
|
|
|
357
359
|
if (values.help) {
|
|
358
360
|
// biome-ignore lint/suspicious/noConsole: CLI help output
|
|
359
361
|
console.log(`
|
|
360
|
-
Usage: acp-harness headless --schema <path>
|
|
362
|
+
Usage: acp-harness headless --schema <path> [--verbose]
|
|
361
363
|
|
|
362
364
|
Arguments:
|
|
363
365
|
-s, --schema Path to headless adapter schema (JSON)
|
|
366
|
+
-v, --verbose Show constructed commands (for debugging)
|
|
364
367
|
-h, --help Show this help message
|
|
365
368
|
|
|
366
369
|
Description:
|
|
@@ -421,7 +424,7 @@ Examples:
|
|
|
421
424
|
}
|
|
422
425
|
|
|
423
426
|
// Run the adapter
|
|
424
|
-
await runAdapter(schema)
|
|
427
|
+
await runAdapter(schema, values.verbose ?? false)
|
|
425
428
|
}
|
|
426
429
|
|
|
427
430
|
// Allow direct execution
|
|
@@ -57,6 +57,8 @@ export type SessionManagerConfig = {
|
|
|
57
57
|
schema: HeadlessAdapterConfig
|
|
58
58
|
/** Default timeout for operations in ms */
|
|
59
59
|
timeout?: number
|
|
60
|
+
/** Whether to show debug output (constructed commands) */
|
|
61
|
+
verbose?: boolean
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
// ============================================================================
|
|
@@ -84,7 +86,7 @@ export type SessionManagerConfig = {
|
|
|
84
86
|
* @returns Session manager with create, prompt, and cancel methods
|
|
85
87
|
*/
|
|
86
88
|
export const createSessionManager = (config: SessionManagerConfig) => {
|
|
87
|
-
const { schema, timeout = 60000 } = config
|
|
89
|
+
const { schema, timeout = 60000, verbose = false } = config
|
|
88
90
|
const sessions = new Map<string, Session>()
|
|
89
91
|
const outputParser = createOutputParser(schema)
|
|
90
92
|
|
|
@@ -164,9 +166,10 @@ export const createSessionManager = (config: SessionManagerConfig) => {
|
|
|
164
166
|
stderr: 'inherit',
|
|
165
167
|
})
|
|
166
168
|
|
|
167
|
-
// If using stdin, write the prompt
|
|
169
|
+
// If using stdin, write the prompt and close stdin
|
|
170
|
+
// (stream mode spawns new process per turn, so stdin should close after writing)
|
|
168
171
|
if (schema.prompt.stdin && session.process) {
|
|
169
|
-
writePromptToStdin(session.process, promptText)
|
|
172
|
+
writePromptToStdin(session.process, promptText, true)
|
|
170
173
|
}
|
|
171
174
|
} else {
|
|
172
175
|
// Subsequent turns: spawn new process with resume flag
|
|
@@ -180,9 +183,10 @@ export const createSessionManager = (config: SessionManagerConfig) => {
|
|
|
180
183
|
stderr: 'inherit',
|
|
181
184
|
})
|
|
182
185
|
|
|
183
|
-
// If using stdin, write the prompt
|
|
186
|
+
// If using stdin, write the prompt and close stdin
|
|
187
|
+
// (stream mode spawns new process per turn, so stdin should close after writing)
|
|
184
188
|
if (schema.prompt.stdin && session.process) {
|
|
185
|
-
writePromptToStdin(session.process, promptText)
|
|
189
|
+
writePromptToStdin(session.process, promptText, true)
|
|
186
190
|
}
|
|
187
191
|
}
|
|
188
192
|
|
|
@@ -211,9 +215,10 @@ export const createSessionManager = (config: SessionManagerConfig) => {
|
|
|
211
215
|
stderr: 'inherit',
|
|
212
216
|
})
|
|
213
217
|
|
|
214
|
-
// If using stdin, write the prompt
|
|
218
|
+
// If using stdin, write the prompt and close stdin
|
|
219
|
+
// (iterative mode spawns new process per turn, so stdin should close after writing)
|
|
215
220
|
if (schema.prompt.stdin && session.process) {
|
|
216
|
-
writePromptToStdin(session.process, fullPrompt)
|
|
221
|
+
writePromptToStdin(session.process, fullPrompt, true)
|
|
217
222
|
}
|
|
218
223
|
|
|
219
224
|
const result = await collectOutput(session, outputParser, onUpdate, timeout)
|
|
@@ -233,8 +238,10 @@ export const createSessionManager = (config: SessionManagerConfig) => {
|
|
|
233
238
|
const buildCommand = (session: Session, promptText: string): string[] => {
|
|
234
239
|
const args = [...schema.command]
|
|
235
240
|
|
|
236
|
-
// Add output format flags
|
|
237
|
-
|
|
241
|
+
// Add output format flags (only if non-empty)
|
|
242
|
+
if (schema.output.flag) {
|
|
243
|
+
args.push(schema.output.flag, schema.output.value)
|
|
244
|
+
}
|
|
238
245
|
|
|
239
246
|
// Add auto-approve flags
|
|
240
247
|
if (schema.autoApprove) {
|
|
@@ -261,6 +268,12 @@ export const createSessionManager = (config: SessionManagerConfig) => {
|
|
|
261
268
|
}
|
|
262
269
|
}
|
|
263
270
|
|
|
271
|
+
// Debug output: show constructed command
|
|
272
|
+
if (verbose) {
|
|
273
|
+
const stdinNote = schema.prompt.stdin ? ' (+ stdin)' : ''
|
|
274
|
+
console.error(`[headless] Command: ${args.join(' ')}${stdinNote}`)
|
|
275
|
+
}
|
|
276
|
+
|
|
264
277
|
return args
|
|
265
278
|
}
|
|
266
279
|
|
|
@@ -334,15 +347,24 @@ const generateSessionId = (): string => {
|
|
|
334
347
|
* - `'ignore'` → null (not writable)
|
|
335
348
|
* - number → file descriptor (not a FileSink)
|
|
336
349
|
*
|
|
350
|
+
* **Closing stdin:** When `closeAfterWrite` is true, the stdin stream is
|
|
351
|
+
* closed after writing. This is required for CLIs that read from stdin
|
|
352
|
+
* with `-` and wait for EOF before processing (e.g., Codex). For stream
|
|
353
|
+
* mode sessions where stdin stays open for subsequent prompts, pass false.
|
|
354
|
+
*
|
|
337
355
|
* @param process - Subprocess with stdin stream
|
|
338
356
|
* @param prompt - Prompt text to write
|
|
357
|
+
* @param closeAfterWrite - Whether to close stdin after writing (default: false)
|
|
339
358
|
*
|
|
340
359
|
* @internal
|
|
341
360
|
*/
|
|
342
|
-
const writePromptToStdin = (process: Subprocess, prompt: string): void => {
|
|
361
|
+
const writePromptToStdin = (process: Subprocess, prompt: string, closeAfterWrite = false): void => {
|
|
343
362
|
if (process.stdin && typeof process.stdin !== 'number') {
|
|
344
363
|
process.stdin.write(`${prompt}\n`)
|
|
345
364
|
process.stdin.flush()
|
|
365
|
+
if (closeAfterWrite) {
|
|
366
|
+
process.stdin.end()
|
|
367
|
+
}
|
|
346
368
|
}
|
|
347
369
|
}
|
|
348
370
|
|