specrails-core 5.2.2 → 5.3.0
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/README.md +3 -0
- package/bin/specrails-core.mjs +5 -0
- package/dist/agent-runtime/cli-executor.d.ts +34 -0
- package/dist/agent-runtime/cli-executor.js +335 -0
- package/dist/agent-runtime/cli-executor.js.map +1 -0
- package/dist/agent-runtime/cli-process.d.ts +32 -0
- package/dist/agent-runtime/cli-process.js +159 -0
- package/dist/agent-runtime/cli-process.js.map +1 -0
- package/dist/agent-runtime/cli.d.ts +38 -0
- package/dist/agent-runtime/cli.js +168 -0
- package/dist/agent-runtime/cli.js.map +1 -0
- package/dist/agent-runtime/config.d.ts +7 -0
- package/dist/agent-runtime/config.js +162 -0
- package/dist/agent-runtime/config.js.map +1 -0
- package/dist/agent-runtime/core-host.d.ts +33 -0
- package/dist/agent-runtime/core-host.js +97 -0
- package/dist/agent-runtime/core-host.js.map +1 -0
- package/dist/agent-runtime/durable-store.d.ts +22 -0
- package/dist/agent-runtime/durable-store.js +205 -0
- package/dist/agent-runtime/durable-store.js.map +1 -0
- package/dist/agent-runtime/executor-types.d.ts +92 -0
- package/dist/agent-runtime/executor-types.js +29 -0
- package/dist/agent-runtime/executor-types.js.map +1 -0
- package/dist/agent-runtime/executors.d.ts +22 -0
- package/dist/agent-runtime/executors.js +44 -0
- package/dist/agent-runtime/executors.js.map +1 -0
- package/dist/agent-runtime/gemini-policy.d.ts +8 -0
- package/dist/agent-runtime/gemini-policy.js +36 -0
- package/dist/agent-runtime/gemini-policy.js.map +1 -0
- package/dist/agent-runtime/graph/artifacts.d.ts +40 -0
- package/dist/agent-runtime/graph/artifacts.js +179 -0
- package/dist/agent-runtime/graph/artifacts.js.map +1 -0
- package/dist/agent-runtime/graph/nodes.d.ts +36 -0
- package/dist/agent-runtime/graph/nodes.js +274 -0
- package/dist/agent-runtime/graph/nodes.js.map +1 -0
- package/dist/agent-runtime/graph/review-policy.d.ts +18 -0
- package/dist/agent-runtime/graph/review-policy.js +32 -0
- package/dist/agent-runtime/graph/review-policy.js.map +1 -0
- package/dist/agent-runtime/graph/roles.d.ts +44 -0
- package/dist/agent-runtime/graph/roles.js +126 -0
- package/dist/agent-runtime/graph/roles.js.map +1 -0
- package/dist/agent-runtime/graph/state.d.ts +75 -0
- package/dist/agent-runtime/graph/state.js +23 -0
- package/dist/agent-runtime/graph/state.js.map +1 -0
- package/dist/agent-runtime/graph-checkpointer.d.ts +61 -0
- package/dist/agent-runtime/graph-checkpointer.js +137 -0
- package/dist/agent-runtime/graph-checkpointer.js.map +1 -0
- package/dist/agent-runtime/index.d.ts +14 -0
- package/dist/agent-runtime/index.js +15 -0
- package/dist/agent-runtime/index.js.map +1 -0
- package/dist/agent-runtime/kimi-acp.d.ts +7 -0
- package/dist/agent-runtime/kimi-acp.js +179 -0
- package/dist/agent-runtime/kimi-acp.js.map +1 -0
- package/dist/agent-runtime/openai-executor.d.ts +18 -0
- package/dist/agent-runtime/openai-executor.js +174 -0
- package/dist/agent-runtime/openai-executor.js.map +1 -0
- package/dist/agent-runtime/prompts.d.ts +40 -0
- package/dist/agent-runtime/prompts.js +291 -0
- package/dist/agent-runtime/prompts.js.map +1 -0
- package/dist/agent-runtime/workflow-types.d.ts +210 -0
- package/dist/agent-runtime/workflow-types.js +2 -0
- package/dist/agent-runtime/workflow-types.js.map +1 -0
- package/dist/agent-runtime/workflow.d.ts +17 -0
- package/dist/agent-runtime/workflow.js +609 -0
- package/dist/agent-runtime/workflow.js.map +1 -0
- package/dist/agent-runtime/workspace-tools.d.ts +25 -0
- package/dist/agent-runtime/workspace-tools.js +120 -0
- package/dist/agent-runtime/workspace-tools.js.map +1 -0
- package/dist/installer/cli.d.ts +35 -0
- package/dist/installer/cli.js +3 -0
- package/dist/installer/cli.js.map +1 -1
- package/dist/installer/commands/doctor.d.ts +27 -0
- package/dist/installer/commands/framework.d.ts +85 -0
- package/dist/installer/commands/init.d.ts +147 -0
- package/dist/installer/commands/update.d.ts +56 -0
- package/dist/installer/commands/v5-migration.d.ts +32 -0
- package/dist/installer/phases/framework-lifecycle.d.ts +53 -0
- package/dist/installer/phases/install-config.d.ts +64 -0
- package/dist/installer/phases/manifest.d.ts +45 -0
- package/dist/installer/phases/prereqs.d.ts +51 -0
- package/dist/installer/phases/provider-detect.d.ts +89 -0
- package/dist/installer/phases/scaffold.d.ts +211 -0
- package/dist/installer/runtime/kimi.d.ts +84 -0
- package/dist/installer/runtime/pipeline-state.d.ts +210 -0
- package/dist/installer/runtime/pipeline-state.js +107 -38
- package/dist/installer/runtime/pipeline-state.js.map +1 -1
- package/dist/installer/util/errors.d.ts +46 -0
- package/dist/installer/util/exec.d.ts +41 -0
- package/dist/installer/util/fs.d.ts +153 -0
- package/dist/installer/util/git.d.ts +44 -0
- package/dist/installer/util/install-transaction.d.ts +29 -0
- package/dist/installer/util/logger.d.ts +31 -0
- package/dist/installer/util/paths.d.ts +34 -0
- package/dist/installer/util/prompts.d.ts +23 -0
- package/dist/installer/util/registry.d.ts +174 -0
- package/dist/installer/util/template.d.ts +23 -0
- package/docs/README.md +1 -0
- package/docs/agent-runtime.md +268 -0
- package/integration-contract.json +79 -7
- package/package.json +16 -1
- package/schemas/agent-runtime.schema.json +55 -0
- package/templates/runtime/provider-pipeline.md +16 -0
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ Specrails Core installs specification-driven development workflows into a projec
|
|
|
8
8
|
|
|
9
9
|
Use Core directly from Claude Code, Codex CLI, Gemini CLI or Kimi Code. For mission conversations, a shared project board, execution loops and delivery controls, use [Specrails Desktop](https://github.com/fjpulidop/specrails-desktop).
|
|
10
10
|
|
|
11
|
+
The current source also includes an opt-in [programmatic agent runtime](docs/agent-runtime.md): a shared LangGraph workflow with per-role providers, durable recovery and local OpenAI-compatible models. Its TypeScript API and CLI keep phase control in Core while the caller owns delivery.
|
|
12
|
+
|
|
11
13
|
This README describes the current source tree. `npx specrails-core@latest` installs the published package, not unmerged changes. Check the [release notes](https://github.com/fjpulidop/specrails-core/releases), or [build this checkout](#develop-from-source) when testing unreleased work.
|
|
12
14
|
|
|
13
15
|
## Quick start
|
|
@@ -158,6 +160,7 @@ Core's configuration, specs and run state are local files. Provider CLIs still s
|
|
|
158
160
|
|
|
159
161
|
- [CLI reference](docs/user-docs/cli-reference.md)
|
|
160
162
|
- [Provider pipeline contracts](docs/user-docs/provider-pipelines.md)
|
|
163
|
+
- [Programmatic agent runtime](docs/agent-runtime.md)
|
|
161
164
|
- [Core update consistency](docs/user-docs/core-updates.md)
|
|
162
165
|
- [Local tickets](docs/local-tickets.md)
|
|
163
166
|
- [Documentation index](docs/README.md)
|
package/bin/specrails-core.mjs
CHANGED
|
@@ -62,6 +62,7 @@ const KNOWN_SUBCOMMANDS = new Set([
|
|
|
62
62
|
'update',
|
|
63
63
|
'doctor',
|
|
64
64
|
'pipeline',
|
|
65
|
+
'runtime',
|
|
65
66
|
'install-framework',
|
|
66
67
|
'swap-current',
|
|
67
68
|
'assemble',
|
|
@@ -399,6 +400,10 @@ async function runNodeCli(argv) {
|
|
|
399
400
|
process.exit(1)
|
|
400
401
|
}
|
|
401
402
|
const code = await mod.main(argv)
|
|
403
|
+
// Let pipe writes drain before exiting; runtime status can exceed a pipe buffer.
|
|
404
|
+
await Promise.all([process.stdout, process.stderr].map(stream =>
|
|
405
|
+
new Promise((resolve, reject) => stream.write('', error => error ? reject(error) : resolve()))
|
|
406
|
+
))
|
|
402
407
|
process.exit(code)
|
|
403
408
|
}
|
|
404
409
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type AgentEvent, type AgentExecutor, type AgentLimits, type AgentRequest, type AgentResult, type CliProvider } from './executor-types.js';
|
|
2
|
+
import { type CliInvocation, type CliProcessRunner } from './cli-process.js';
|
|
3
|
+
export interface CliExecutorOptions {
|
|
4
|
+
runProcess?: CliProcessRunner;
|
|
5
|
+
env?: NodeJS.ProcessEnv;
|
|
6
|
+
}
|
|
7
|
+
export interface CliInvocationOptions {
|
|
8
|
+
kimiAgentFile?: string;
|
|
9
|
+
geminiPolicyFile?: string;
|
|
10
|
+
codexSchemaFile?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Build the exact argv for one role. Read-only roles use each CLI's native
|
|
14
|
+
* read-only boundary. The developer role gets the same autonomy the legacy
|
|
15
|
+
* Implement step had (edits and shell inside the CLI's own sandbox), because a
|
|
16
|
+
* developer that cannot run tests iterates blind and fails verification.
|
|
17
|
+
*/
|
|
18
|
+
export declare function buildCliInvocation(provider: CliProvider, request: AgentRequest, options?: CliInvocationOptions): CliInvocation;
|
|
19
|
+
interface ParsedCliResult extends AgentResult {
|
|
20
|
+
terminal: boolean;
|
|
21
|
+
failed: boolean;
|
|
22
|
+
turns: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function parseCliOutput(provider: CliProvider, stdout: string): ParsedCliResult;
|
|
25
|
+
/** Live tool activity for one streamed JSON line, or undefined when the line carries none. */
|
|
26
|
+
export declare function cliToolEvents(provider: CliProvider, event: Record<string, unknown>): AgentEvent[];
|
|
27
|
+
export declare class CliExecutor implements AgentExecutor {
|
|
28
|
+
private readonly provider;
|
|
29
|
+
private readonly options;
|
|
30
|
+
constructor(provider: CliProvider, options?: CliExecutorOptions);
|
|
31
|
+
validateLimits(limits: AgentLimits): void;
|
|
32
|
+
execute(request: AgentRequest): Promise<AgentResult>;
|
|
33
|
+
}
|
|
34
|
+
export {};
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { normalizeKimiCliModel } from '../installer/runtime/kimi.js';
|
|
5
|
+
import { AgentExecutionError, unknownUsage, validateAgentRequest } from './executor-types.js';
|
|
6
|
+
import { runCliProcess } from './cli-process.js';
|
|
7
|
+
import { canonicalWorkspace } from './workspace-tools.js';
|
|
8
|
+
import { parseStructuredText } from './openai-executor.js';
|
|
9
|
+
import { executeKimiReadonlyAcp } from './kimi-acp.js';
|
|
10
|
+
import { assertGeminiAdminPolicyAvailable, GEMINI_READONLY_POLICY } from './gemini-policy.js';
|
|
11
|
+
/** Tools a Claude developer may not use: nested agents and platform skills would start a second, unobserved workflow. */
|
|
12
|
+
const CLAUDE_DEVELOPER_DISALLOWED = 'Agent,Task,Skill';
|
|
13
|
+
/**
|
|
14
|
+
* Build the exact argv for one role. Read-only roles use each CLI's native
|
|
15
|
+
* read-only boundary. The developer role gets the same autonomy the legacy
|
|
16
|
+
* Implement step had (edits and shell inside the CLI's own sandbox), because a
|
|
17
|
+
* developer that cannot run tests iterates blind and fails verification.
|
|
18
|
+
*/
|
|
19
|
+
export function buildCliInvocation(provider, request, options = {}) {
|
|
20
|
+
const readOnly = request.role !== 'developer';
|
|
21
|
+
const model = request.model ? ['--model', request.model] : [];
|
|
22
|
+
const extraRoots = request.allowedRoots.filter(root => root !== request.cwd);
|
|
23
|
+
const resume = request.resumeSessionId;
|
|
24
|
+
switch (provider) {
|
|
25
|
+
case 'claude': return { command: 'claude', stdin: request.prompt, args: [
|
|
26
|
+
'-p', '--output-format', 'stream-json', '--verbose', '--max-turns', String(request.maxTurns ?? 24), ...model,
|
|
27
|
+
// Project instructions and rules stay visible; the user's global config,
|
|
28
|
+
// memory and plugins never leak into an autonomous role.
|
|
29
|
+
'--setting-sources', 'project,local',
|
|
30
|
+
...(readOnly
|
|
31
|
+
? ['--tools', 'Read,Grep,Glob', '--permission-mode', 'plan', '--strict-mcp-config']
|
|
32
|
+
: ['--tools', 'default', '--disallowedTools', CLAUDE_DEVELOPER_DISALLOWED, '--dangerously-skip-permissions']),
|
|
33
|
+
...(request.outputSchema ? ['--json-schema', JSON.stringify(request.outputSchema)] : []),
|
|
34
|
+
...(request.maxCostUsd === undefined ? [] : ['--max-budget-usd', String(request.maxCostUsd)]),
|
|
35
|
+
...extraRoots.flatMap(root => ['--add-dir', root]),
|
|
36
|
+
...(resume ? ['--resume', resume] : []),
|
|
37
|
+
] };
|
|
38
|
+
case 'codex': {
|
|
39
|
+
const sandbox = readOnly ? 'read-only' : 'workspace-write';
|
|
40
|
+
const common = ['--json', '--skip-git-repo-check', '-c', 'approval_policy="never"', ...model];
|
|
41
|
+
// `codex exec resume` has no --sandbox flag; the same policy travels as a config override.
|
|
42
|
+
if (resume)
|
|
43
|
+
return { command: 'codex', stdin: request.prompt, args: ['exec', 'resume', ...common, '-c', `sandbox_mode="${sandbox}"`, resume, '-'] };
|
|
44
|
+
return { command: 'codex', stdin: request.prompt, args: [
|
|
45
|
+
'exec', ...common, '--sandbox', sandbox,
|
|
46
|
+
...(options.codexSchemaFile ? ['--output-schema', options.codexSchemaFile] : []),
|
|
47
|
+
...extraRoots.flatMap(root => ['--add-dir', root]), '-',
|
|
48
|
+
] };
|
|
49
|
+
}
|
|
50
|
+
case 'gemini': {
|
|
51
|
+
if (readOnly && !options.geminiPolicyFile)
|
|
52
|
+
throw new AgentExecutionError('Gemini architect/reviewer roles require --admin-policy support for an enforced read-only tool allowlist. Upgrade Gemini CLI or select another provider for this role.', 'provider_capability_unsupported');
|
|
53
|
+
return { command: 'gemini', stdin: request.prompt, args: [
|
|
54
|
+
'-p', 'Execute the task supplied on stdin.', '--output-format', 'stream-json', ...model,
|
|
55
|
+
// Headless Gemini cannot answer shell approvals; the developer runs with
|
|
56
|
+
// auto-approval exactly as the legacy Implement step did.
|
|
57
|
+
...(readOnly ? ['--approval-mode', 'plan'] : ['--yolo']),
|
|
58
|
+
...(options.geminiPolicyFile ? ['--admin-policy', options.geminiPolicyFile] : []),
|
|
59
|
+
...extraRoots.flatMap(root => ['--include-directories', root]),
|
|
60
|
+
...(resume ? ['--resume', resume] : []),
|
|
61
|
+
] };
|
|
62
|
+
}
|
|
63
|
+
case 'kimi': {
|
|
64
|
+
if (readOnly && !options.kimiAgentFile)
|
|
65
|
+
throw new AgentExecutionError('Kimi read-only print mode requires --agent-file and enforced tool allowlists. Use CliExecutor to select the ACP read-only transport on older CLIs.', 'provider_capability_unsupported');
|
|
66
|
+
return { command: 'kimi', args: [
|
|
67
|
+
...(request.model ? ['-m', normalizeKimiCliModel(request.model)] : []),
|
|
68
|
+
...(resume ? [`--session=${resume}`] : []),
|
|
69
|
+
'-p', request.prompt, '--output-format', 'stream-json',
|
|
70
|
+
...(options.kimiAgentFile ? ['--agent-file', options.kimiAgentFile] : []),
|
|
71
|
+
...extraRoots.flatMap(root => ['--add-dir', root]),
|
|
72
|
+
] };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function object(value) { return value && typeof value === 'object' && !Array.isArray(value) ? value : {}; }
|
|
77
|
+
function number(value) { return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : null; }
|
|
78
|
+
function textBlocks(value) {
|
|
79
|
+
return Array.isArray(value) ? value.map(raw => object(raw)).filter(block => block.type === 'text' && typeof block.text === 'string').map(block => block.text).join('') : '';
|
|
80
|
+
}
|
|
81
|
+
function shorten(value, limit = 160) {
|
|
82
|
+
if (typeof value !== 'string' || !value.trim())
|
|
83
|
+
return undefined;
|
|
84
|
+
const single = value.replace(/\s+/g, ' ').trim();
|
|
85
|
+
return single.length > limit ? single.slice(0, limit - 1) + '…' : single;
|
|
86
|
+
}
|
|
87
|
+
/** Short, human-readable tool activity for host logs; never a transcript. */
|
|
88
|
+
function toolEvent(tool, input) {
|
|
89
|
+
const args = object(input);
|
|
90
|
+
const detail = shorten(args.file_path ?? args.path ?? args.command ?? args.pattern ?? args.query ?? args.notebook_path ?? args.url);
|
|
91
|
+
return { kind: 'tool-start', tool, ...(detail ? { detail } : {}) };
|
|
92
|
+
}
|
|
93
|
+
export function parseCliOutput(provider, stdout) {
|
|
94
|
+
let text = '', terminal = false, failed = false, sessionId, turns = 0;
|
|
95
|
+
let usage = unknownUsage();
|
|
96
|
+
let structured;
|
|
97
|
+
const claudeMessages = new Map();
|
|
98
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
99
|
+
let event;
|
|
100
|
+
try {
|
|
101
|
+
event = object(JSON.parse(line));
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (typeof event.session_id === 'string')
|
|
107
|
+
sessionId = event.session_id;
|
|
108
|
+
if (typeof event.thread_id === 'string')
|
|
109
|
+
sessionId = event.thread_id;
|
|
110
|
+
if (['error', 'turn.failed', 'system.error'].includes(String(event.type)))
|
|
111
|
+
failed = true;
|
|
112
|
+
if (provider === 'claude') {
|
|
113
|
+
if (event.type === 'assistant') {
|
|
114
|
+
const message = object(event.message), reported = object(message.usage);
|
|
115
|
+
const messageText = textBlocks(message.content);
|
|
116
|
+
if (messageText)
|
|
117
|
+
text = messageText;
|
|
118
|
+
const id = typeof message.id === 'string' ? message.id : `anonymous-${claudeMessages.size}`;
|
|
119
|
+
const input = number(reported.input_tokens);
|
|
120
|
+
claudeMessages.set(id, { inputTokens: input === null ? null : input + (number(reported.cache_read_input_tokens) ?? 0) + (number(reported.cache_creation_input_tokens) ?? 0), outputTokens: number(reported.output_tokens), costUsd: null });
|
|
121
|
+
turns = claudeMessages.size;
|
|
122
|
+
}
|
|
123
|
+
if (event.type === 'result' && object(event.origin).kind !== 'task-notification') {
|
|
124
|
+
terminal = true;
|
|
125
|
+
failed ||= event.is_error === true || (typeof event.subtype === 'string' && event.subtype !== 'success');
|
|
126
|
+
if (typeof event.result === 'string')
|
|
127
|
+
text = event.result;
|
|
128
|
+
// --json-schema returns the validated object separately from the text.
|
|
129
|
+
if (event.structured_output && typeof event.structured_output === 'object' && !Array.isArray(event.structured_output))
|
|
130
|
+
structured = event.structured_output;
|
|
131
|
+
const reported = object(event.usage), input = number(reported.input_tokens);
|
|
132
|
+
usage = { inputTokens: input === null ? null : input + (number(reported.cache_read_input_tokens) ?? 0) + (number(reported.cache_creation_input_tokens) ?? 0), outputTokens: number(reported.output_tokens), costUsd: number(event.total_cost_usd) };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
else if (provider === 'codex') {
|
|
136
|
+
if (event.type === 'item.completed') {
|
|
137
|
+
const item = object(event.item);
|
|
138
|
+
if (item.type === 'agent_message' && typeof item.text === 'string')
|
|
139
|
+
text = item.text;
|
|
140
|
+
if (item.type === 'agent_message' || ['command_execution', 'mcp_tool_call', 'function_call', 'local_shell_call'].includes(String(item.type)))
|
|
141
|
+
turns++;
|
|
142
|
+
}
|
|
143
|
+
if (event.type === 'turn.completed') {
|
|
144
|
+
terminal = true;
|
|
145
|
+
const reported = object(event.usage);
|
|
146
|
+
usage = { inputTokens: number(reported.input_tokens), outputTokens: number(reported.output_tokens), costUsd: null };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
else if (provider === 'gemini') {
|
|
150
|
+
if (event.type === 'message' && event.role === 'assistant' && typeof event.content === 'string') {
|
|
151
|
+
text += event.content;
|
|
152
|
+
if (!event.delta)
|
|
153
|
+
turns++;
|
|
154
|
+
}
|
|
155
|
+
// Assistant deltas before a tool belong to an intermediate turn. Keep
|
|
156
|
+
// streaming them to observers, but return only the final response.
|
|
157
|
+
if (event.type === 'tool_use') {
|
|
158
|
+
turns++;
|
|
159
|
+
text = '';
|
|
160
|
+
}
|
|
161
|
+
if (event.type === 'result') {
|
|
162
|
+
terminal = true;
|
|
163
|
+
failed ||= event.status !== 'success';
|
|
164
|
+
const reported = object(event.stats);
|
|
165
|
+
usage = { inputTokens: number(reported.input_tokens), outputTokens: number(reported.output_tokens), costUsd: null };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
if (event.role === 'assistant') {
|
|
170
|
+
turns++;
|
|
171
|
+
const content = typeof event.content === 'string' ? event.content : textBlocks(event.content);
|
|
172
|
+
if (content)
|
|
173
|
+
text = content;
|
|
174
|
+
}
|
|
175
|
+
if (event.type === 'session.resume_hint')
|
|
176
|
+
terminal = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (provider === 'claude' && !terminal && claudeMessages.size) {
|
|
180
|
+
const messages = [...claudeMessages.values()];
|
|
181
|
+
usage = { inputTokens: messages.some(message => message.inputTokens === null) ? null : messages.reduce((sum, message) => sum + (message.inputTokens ?? 0), 0), outputTokens: messages.some(message => message.outputTokens === null) ? null : messages.reduce((sum, message) => sum + (message.outputTokens ?? 0), 0), costUsd: null };
|
|
182
|
+
}
|
|
183
|
+
if (structured && !text.trim())
|
|
184
|
+
text = JSON.stringify(structured);
|
|
185
|
+
return { text, terminal, failed, turns, usage, sessionId, structured: structured ?? parseStructuredText(text) };
|
|
186
|
+
}
|
|
187
|
+
/** Live tool activity for one streamed JSON line, or undefined when the line carries none. */
|
|
188
|
+
export function cliToolEvents(provider, event) {
|
|
189
|
+
if (provider === 'claude' && event.type === 'assistant') {
|
|
190
|
+
const content = object(event.message).content;
|
|
191
|
+
return Array.isArray(content) ? content.map(object).filter(block => block.type === 'tool_use' && typeof block.name === 'string').map(block => toolEvent(String(block.name), block.input)) : [];
|
|
192
|
+
}
|
|
193
|
+
if (provider === 'codex' && event.type === 'item.started') {
|
|
194
|
+
const item = object(event.item);
|
|
195
|
+
if (['command_execution', 'local_shell_call'].includes(String(item.type)))
|
|
196
|
+
return [toolEvent('shell', { command: item.command })];
|
|
197
|
+
if (['mcp_tool_call', 'function_call'].includes(String(item.type)))
|
|
198
|
+
return [toolEvent(typeof item.name === 'string' ? item.name : 'tool', item.arguments)];
|
|
199
|
+
if (item.type === 'file_change')
|
|
200
|
+
return [toolEvent('edit', { path: Array.isArray(item.changes) ? object(item.changes[0]).path : undefined })];
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
if (provider === 'gemini' && event.type === 'tool_use')
|
|
204
|
+
return [toolEvent(typeof event.tool_name === 'string' ? event.tool_name : 'tool', event.parameters)];
|
|
205
|
+
if (provider === 'kimi' && event.role === 'assistant' && Array.isArray(event.tool_calls)) {
|
|
206
|
+
return event.tool_calls.map(object).map(call => { const fn = object(call.function); let input; try {
|
|
207
|
+
input = typeof fn.arguments === 'string' ? JSON.parse(fn.arguments) : fn.arguments;
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
input = undefined;
|
|
211
|
+
} return toolEvent(typeof fn.name === 'string' ? fn.name : 'tool', input); });
|
|
212
|
+
}
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
215
|
+
export class CliExecutor {
|
|
216
|
+
provider;
|
|
217
|
+
options;
|
|
218
|
+
constructor(provider, options = {}) {
|
|
219
|
+
this.provider = provider;
|
|
220
|
+
this.options = options;
|
|
221
|
+
}
|
|
222
|
+
validateLimits(limits) {
|
|
223
|
+
if (limits.maxCostUsd !== undefined && this.provider !== 'claude')
|
|
224
|
+
throw new AgentExecutionError(`A strict USD cap is unsupported by the ${this.provider} CLI. Use Claude's native cap or remove the dollar cap.`, 'cost_limit_unsupported');
|
|
225
|
+
if (limits.maxTokens !== undefined && this.provider === 'kimi')
|
|
226
|
+
throw new AgentExecutionError('Kimi does not report authoritative token usage. Remove the token cap or select another provider for this role.', 'usage_unavailable');
|
|
227
|
+
}
|
|
228
|
+
async execute(request) {
|
|
229
|
+
validateAgentRequest(request);
|
|
230
|
+
this.validateLimits(request);
|
|
231
|
+
const scope = canonicalWorkspace(request.cwd, request.allowedRoots);
|
|
232
|
+
const normalized = { ...request, cwd: scope.cwd, allowedRoots: scope.roots };
|
|
233
|
+
const runner = this.options.runProcess ?? runCliProcess;
|
|
234
|
+
let temporary, kimiAgentFile, geminiPolicyFile, codexSchemaFile, stream = '', turns = 0;
|
|
235
|
+
const assistantIds = new Set();
|
|
236
|
+
const scratch = () => temporary ??= mkdtempSync(path.join(tmpdir(), 'specrails-' + this.provider + '-role-'));
|
|
237
|
+
try {
|
|
238
|
+
if (this.provider === 'gemini' && request.role !== 'developer') {
|
|
239
|
+
assertGeminiAdminPolicyAvailable();
|
|
240
|
+
const help = await runner({ command: 'gemini', args: ['--help'] }, { cwd: scope.cwd, signal: request.signal, timeoutMs: 10_000, env: this.options.env });
|
|
241
|
+
if (help.exitCode !== 0 || !help.stdout.includes('--admin-policy'))
|
|
242
|
+
throw new AgentExecutionError('Gemini architect/reviewer roles require --admin-policy support for an enforced read-only tool allowlist. Upgrade Gemini CLI or select another provider for this role.', 'provider_capability_unsupported');
|
|
243
|
+
geminiPolicyFile = path.join(scratch(), 'readonly.toml');
|
|
244
|
+
writeFileSync(geminiPolicyFile, GEMINI_READONLY_POLICY, { mode: 0o600 });
|
|
245
|
+
}
|
|
246
|
+
if (this.provider === 'kimi' && request.role !== 'developer') {
|
|
247
|
+
const help = await runner({ command: 'kimi', args: ['--help'] }, { cwd: scope.cwd, signal: request.signal, timeoutMs: 10_000, env: this.options.env });
|
|
248
|
+
if (help.exitCode !== 0)
|
|
249
|
+
throw new AgentExecutionError('Cannot detect Kimi CLI capabilities', 'provider_capability_unsupported');
|
|
250
|
+
if (!help.stdout.includes('--agent-file'))
|
|
251
|
+
return await executeKimiReadonlyAcp(request, this.options);
|
|
252
|
+
kimiAgentFile = path.join(scratch(), 'readonly.md');
|
|
253
|
+
writeFileSync(kimiAgentFile, '---\nname: specrails-readonly\ndescription: Execute one read-only Specrails role\ntools:\n - Read\n - Grep\n - Glob\nsubagents: []\n---\nExecute the supplied task and return its complete result. All instructions are supplied in the task.\n', { mode: 0o600 });
|
|
254
|
+
}
|
|
255
|
+
if (this.provider === 'codex' && request.outputSchema && !request.resumeSessionId) {
|
|
256
|
+
codexSchemaFile = path.join(scratch(), 'output-schema.json');
|
|
257
|
+
writeFileSync(codexSchemaFile, JSON.stringify(request.outputSchema), { mode: 0o600 });
|
|
258
|
+
}
|
|
259
|
+
const result = await runner(buildCliInvocation(this.provider, normalized, { kimiAgentFile, geminiPolicyFile, codexSchemaFile }), {
|
|
260
|
+
cwd: scope.cwd, signal: request.signal, timeoutMs: request.timeoutMs ?? 15 * 60_000, env: this.options.env,
|
|
261
|
+
onLine: line => {
|
|
262
|
+
stream += line + '\n';
|
|
263
|
+
let event;
|
|
264
|
+
try {
|
|
265
|
+
event = object(JSON.parse(line));
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
let delta = '';
|
|
271
|
+
if (this.provider === 'claude' && event.type === 'assistant') {
|
|
272
|
+
const message = object(event.message);
|
|
273
|
+
const id = typeof message.id === 'string' ? message.id : `anonymous-${turns}`;
|
|
274
|
+
if (!assistantIds.has(id)) {
|
|
275
|
+
assistantIds.add(id);
|
|
276
|
+
turns++;
|
|
277
|
+
}
|
|
278
|
+
delta = textBlocks(message.content);
|
|
279
|
+
}
|
|
280
|
+
else if (this.provider === 'codex' && event.type === 'item.completed') {
|
|
281
|
+
const item = object(event.item);
|
|
282
|
+
if (item.type === 'agent_message' || ['command_execution', 'mcp_tool_call', 'function_call', 'local_shell_call'].includes(String(item.type)))
|
|
283
|
+
turns++;
|
|
284
|
+
if (item.type === 'agent_message' && typeof item.text === 'string')
|
|
285
|
+
delta = item.text;
|
|
286
|
+
}
|
|
287
|
+
else if (this.provider === 'gemini') {
|
|
288
|
+
if (event.type === 'tool_use')
|
|
289
|
+
turns++;
|
|
290
|
+
if (event.type === 'message' && event.role === 'assistant' && typeof event.content === 'string')
|
|
291
|
+
delta = event.content;
|
|
292
|
+
}
|
|
293
|
+
else if (this.provider === 'kimi' && event.role === 'assistant') {
|
|
294
|
+
turns++;
|
|
295
|
+
delta = typeof event.content === 'string' ? event.content : textBlocks(event.content);
|
|
296
|
+
}
|
|
297
|
+
if (turns > (request.maxTurns ?? 24))
|
|
298
|
+
throw new AgentExecutionError('CLI exceeded its configured turn/tool limit', 'max_turns', parseCliOutput(this.provider, stream).usage);
|
|
299
|
+
for (const tool of cliToolEvents(this.provider, event))
|
|
300
|
+
request.onEvent?.(tool);
|
|
301
|
+
if (delta)
|
|
302
|
+
request.onEvent?.({ kind: 'text', text: delta });
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
const parsed = parseCliOutput(this.provider, result.stdout);
|
|
306
|
+
request.onEvent?.({ kind: 'usage', usage: parsed.usage });
|
|
307
|
+
if (result.exitCode !== 0 || parsed.failed)
|
|
308
|
+
throw new AgentExecutionError(`${this.provider} execution failed${result.exitCode !== 0 ? ` (exit ${result.exitCode})` : ''}. Check provider authentication, model access and limits.`, 'provider_execution_error', parsed.usage);
|
|
309
|
+
if (!parsed.terminal || !parsed.text.trim())
|
|
310
|
+
throw new AgentExecutionError(`${this.provider} exited without a successful final result`, 'incomplete_response', parsed.usage);
|
|
311
|
+
if (request.maxTokens !== undefined) {
|
|
312
|
+
if (parsed.usage.inputTokens === null || parsed.usage.outputTokens === null)
|
|
313
|
+
throw new AgentExecutionError(`${this.provider} did not report usage needed for the token limit`, 'usage_unavailable', parsed.usage);
|
|
314
|
+
if (parsed.usage.inputTokens + parsed.usage.outputTokens > request.maxTokens)
|
|
315
|
+
throw new AgentExecutionError('Agent token budget exceeded', 'token_budget', parsed.usage);
|
|
316
|
+
}
|
|
317
|
+
// The final text was already streamed as it arrived; re-emitting it would
|
|
318
|
+
// print every summary twice in host logs.
|
|
319
|
+
return { text: parsed.text, usage: parsed.usage, sessionId: parsed.sessionId, structured: parsed.structured };
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
if (error instanceof AgentExecutionError) {
|
|
323
|
+
const usage = parseCliOutput(this.provider, stream).usage;
|
|
324
|
+
if (error.usage.inputTokens === null && usage.inputTokens !== null)
|
|
325
|
+
throw new AgentExecutionError(error.message, error.code, usage);
|
|
326
|
+
}
|
|
327
|
+
throw error;
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
if (temporary)
|
|
331
|
+
rmSync(temporary, { recursive: true, force: true });
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
//# sourceMappingURL=cli-executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-executor.js","sourceRoot":"","sources":["../../src/agent-runtime/cli-executor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface CliInvocation {
|
|
2
|
+
command: string;
|
|
3
|
+
args: string[];
|
|
4
|
+
stdin?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface CliDuplexControl {
|
|
7
|
+
send(line: string): void;
|
|
8
|
+
complete(): void;
|
|
9
|
+
}
|
|
10
|
+
export interface CliProcessOptions {
|
|
11
|
+
cwd: string;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
timeoutMs: number;
|
|
14
|
+
env?: NodeJS.ProcessEnv;
|
|
15
|
+
onLine?: (line: string) => void;
|
|
16
|
+
duplex?: (control: CliDuplexControl) => void;
|
|
17
|
+
}
|
|
18
|
+
export interface CliProcessResult {
|
|
19
|
+
stdout: string;
|
|
20
|
+
stderr: string;
|
|
21
|
+
exitCode: number;
|
|
22
|
+
}
|
|
23
|
+
export type CliProcessRunner = (invocation: CliInvocation, options: CliProcessOptions) => Promise<CliProcessResult>;
|
|
24
|
+
/** Kimi print mode requires -p. Standard npm shims can transport even large prompts via stdin without cmd.exe parsing them. */
|
|
25
|
+
export declare function windowsKimiInvocation(invocation: CliInvocation, options?: {
|
|
26
|
+
env?: NodeJS.ProcessEnv;
|
|
27
|
+
exists?: (file: string) => boolean;
|
|
28
|
+
read?: (file: string) => string;
|
|
29
|
+
node?: string;
|
|
30
|
+
}): CliInvocation;
|
|
31
|
+
export declare function cliProcessEnvironment(base: NodeJS.ProcessEnv, platform?: NodeJS.Platform): NodeJS.ProcessEnv;
|
|
32
|
+
export declare const runCliProcess: CliProcessRunner;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import crossSpawn from 'cross-spawn';
|
|
2
|
+
import { execFile } from 'node:child_process';
|
|
3
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { StringDecoder } from 'node:string_decoder';
|
|
6
|
+
import { AgentExecutionError } from './executor-types.js';
|
|
7
|
+
const PROMPT_MARKER = '__SPECRAILS_KIMI_STDIN__';
|
|
8
|
+
const BOOTSTRAP = "const fs=require('node:fs');const u=require('node:url');const entry=process.argv[1];const i=process.argv.lastIndexOf('-p')+1;if(i<2||process.argv[i]!=='__SPECRAILS_KIMI_STDIN__')throw Error('Missing prompt marker');process.argv[i]=fs.readFileSync(0,'utf8');import(u.pathToFileURL(entry).href).catch(()=>{process.exitCode=1});";
|
|
9
|
+
/** Kimi print mode requires -p. Standard npm shims can transport even large prompts via stdin without cmd.exe parsing them. */
|
|
10
|
+
export function windowsKimiInvocation(invocation, options = {}) {
|
|
11
|
+
const env = options.env ?? process.env, exists = options.exists ?? existsSync;
|
|
12
|
+
const search = Object.entries(env).reverse().find(([key]) => key.toLowerCase() === 'path')?.[1] ?? '';
|
|
13
|
+
let binary;
|
|
14
|
+
for (const entry of search.split(';').map(value => value.trim().replace(/^"(.*)"$/, '$1')).filter(Boolean)) {
|
|
15
|
+
binary = ['kimi.cmd', 'kimi.bat', 'kimi.exe', 'kimi.com'].map(name => path.win32.join(entry, name)).find(exists);
|
|
16
|
+
if (binary)
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
if (!binary)
|
|
20
|
+
throw new AgentExecutionError('Kimi executable was not found on Windows PATH', 'provider_not_found');
|
|
21
|
+
if (/\.(cmd|bat)$/i.test(binary)) {
|
|
22
|
+
const shim = (options.read ?? (file => readFileSync(file, 'utf8')))(binary);
|
|
23
|
+
const entry = /%dp0%[\\/]([^"\r\n]*?\.(?:mjs|cjs|js))["']?\s+%\*/i.exec(shim)?.[1];
|
|
24
|
+
if (!entry)
|
|
25
|
+
throw new AgentExecutionError('Unsupported Kimi Windows shim; install the standard npm distribution or a native executable', 'unsupported_windows_shim');
|
|
26
|
+
const localNode = path.win32.join(path.win32.dirname(binary), 'node.exe');
|
|
27
|
+
// process.execPath can be the Desktop pkg sidecar, not a Node interpreter.
|
|
28
|
+
const nodeBinary = options.node ?? (exists(localNode) ? localNode : 'node');
|
|
29
|
+
const args = [...invocation.args], index = args.indexOf('-p') + 1;
|
|
30
|
+
if (index === 0)
|
|
31
|
+
return { command: nodeBinary, args: [path.win32.join(path.win32.dirname(binary), entry), ...args], stdin: invocation.stdin };
|
|
32
|
+
const prompt = args[index];
|
|
33
|
+
args[index] = PROMPT_MARKER;
|
|
34
|
+
return { command: nodeBinary, args: ['-e', BOOTSTRAP, path.win32.join(path.win32.dirname(binary), entry), ...args], stdin: prompt };
|
|
35
|
+
}
|
|
36
|
+
if (JSON.stringify(invocation.args).length + binary.length > 28_000)
|
|
37
|
+
throw new AgentExecutionError('Kimi prompt exceeds the native Windows command line limit; use the standard npm Kimi installation', 'windows_argument_limit');
|
|
38
|
+
return { ...invocation, command: binary };
|
|
39
|
+
}
|
|
40
|
+
export function cliProcessEnvironment(base, platform = process.platform) {
|
|
41
|
+
const env = { ...base };
|
|
42
|
+
// A nested invocation must not accidentally inherit the surrounding workflow's transport.
|
|
43
|
+
delete env.CLAUDECODE;
|
|
44
|
+
delete env.SPECRAILS_INVOCATION_ID;
|
|
45
|
+
if (platform === 'win32') {
|
|
46
|
+
for (const name of ['SystemRoot', 'windir', 'ComSpec', 'PATH']) {
|
|
47
|
+
const aliases = Object.keys(env).filter(key => key.toLowerCase() === name.toLowerCase());
|
|
48
|
+
// Match cross-spawn's last-defined alias precedence, then supply one key
|
|
49
|
+
// to both executable lookup and the Windows child's environment block.
|
|
50
|
+
const value = aliases.length ? env[aliases[aliases.length - 1]] : undefined;
|
|
51
|
+
for (const key of aliases)
|
|
52
|
+
delete env[key];
|
|
53
|
+
if (value !== undefined)
|
|
54
|
+
env[name] = value;
|
|
55
|
+
}
|
|
56
|
+
env.SystemRoot ??= env.windir ?? 'C:\\Windows';
|
|
57
|
+
env.windir ??= env.SystemRoot;
|
|
58
|
+
env.ComSpec ??= path.win32.join(env.SystemRoot, 'System32', 'cmd.exe');
|
|
59
|
+
}
|
|
60
|
+
return env;
|
|
61
|
+
}
|
|
62
|
+
export const runCliProcess = async (raw, options) => {
|
|
63
|
+
if (options.signal?.aborted)
|
|
64
|
+
throw new AgentExecutionError('Agent cancelled', 'aborted');
|
|
65
|
+
const env = cliProcessEnvironment(options.env ?? process.env);
|
|
66
|
+
const invocation = process.platform === 'win32' && raw.command === 'kimi' ? windowsKimiInvocation(raw, { env }) : raw;
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const child = crossSpawn(invocation.command, invocation.args, { cwd: options.cwd, env, stdio: ['pipe', 'pipe', 'pipe'], shell: false, windowsHide: true, detached: process.platform !== 'win32' });
|
|
69
|
+
let stdout = '', stderr = '', pending = '', failure, completed = false;
|
|
70
|
+
const decoder = new StringDecoder('utf8');
|
|
71
|
+
let termination;
|
|
72
|
+
const terminate = () => {
|
|
73
|
+
if (termination || !child.pid)
|
|
74
|
+
return;
|
|
75
|
+
const pid = child.pid;
|
|
76
|
+
termination = process.platform === 'win32'
|
|
77
|
+
? new Promise(done => {
|
|
78
|
+
const taskkill = path.win32.join(env.SystemRoot ?? 'C:\\Windows', 'System32', 'taskkill.exe');
|
|
79
|
+
execFile(taskkill, ['/PID', String(pid), '/T', '/F'], { windowsHide: true, env, timeout: 10_000 }, () => { try {
|
|
80
|
+
child.kill('SIGKILL');
|
|
81
|
+
}
|
|
82
|
+
catch { /* exited */ } done(); });
|
|
83
|
+
})
|
|
84
|
+
: Promise.resolve().then(() => { try {
|
|
85
|
+
process.kill(-pid, 'SIGKILL');
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
try {
|
|
89
|
+
child.kill('SIGKILL');
|
|
90
|
+
}
|
|
91
|
+
catch { /* exited */ }
|
|
92
|
+
} });
|
|
93
|
+
};
|
|
94
|
+
const fail = (error) => { failure ??= error; terminate(); };
|
|
95
|
+
const abort = () => fail(new AgentExecutionError('Agent cancelled', 'aborted'));
|
|
96
|
+
const timer = setTimeout(() => fail(new AgentExecutionError('Agent timed out', 'timeout')), options.timeoutMs);
|
|
97
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
98
|
+
if (options.signal?.aborted)
|
|
99
|
+
abort();
|
|
100
|
+
child.stdout?.on('data', (chunk) => {
|
|
101
|
+
if (failure)
|
|
102
|
+
return;
|
|
103
|
+
const text = decoder.write(chunk);
|
|
104
|
+
stdout += text;
|
|
105
|
+
pending += text;
|
|
106
|
+
if (Buffer.byteLength(stdout) > 8 * 1024 * 1024) {
|
|
107
|
+
fail(new AgentExecutionError('CLI output exceeded 8 MiB', 'output_limit'));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
for (;;) {
|
|
111
|
+
const newline = pending.indexOf('\n');
|
|
112
|
+
if (newline < 0)
|
|
113
|
+
break;
|
|
114
|
+
const line = pending.slice(0, newline).replace(/\r$/, '');
|
|
115
|
+
pending = pending.slice(newline + 1);
|
|
116
|
+
try {
|
|
117
|
+
options.onLine?.(line);
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
fail(error instanceof Error ? error : new Error('CLI stream observer failed'));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
child.stderr?.on('data', (chunk) => { stderr = (stderr + chunk.toString('utf8')).slice(-32_768); });
|
|
125
|
+
child.stdin?.on('error', () => { });
|
|
126
|
+
child.once('error', error => { failure ??= new AgentExecutionError(`Cannot launch ${raw.command}: ${error.message}`, 'provider_spawn_error'); });
|
|
127
|
+
child.once('close', (code) => {
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
options.signal?.removeEventListener('abort', abort);
|
|
130
|
+
void (termination ?? Promise.resolve()).then(() => {
|
|
131
|
+
if (failure) {
|
|
132
|
+
reject(failure);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
try {
|
|
136
|
+
if (pending)
|
|
137
|
+
options.onLine?.(pending);
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
reject(error);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
resolve({ stdout, stderr, exitCode: completed ? 0 : code ?? -1 });
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
if (options.duplex) {
|
|
147
|
+
try {
|
|
148
|
+
options.duplex({ send: line => { if (!failure && !completed)
|
|
149
|
+
child.stdin?.write(line + '\n', 'utf8'); }, complete: () => { completed = true; terminate(); } });
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
fail(error instanceof Error ? error : new Error('Duplex initialization failed'));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else
|
|
156
|
+
child.stdin?.end(invocation.stdin ?? '', 'utf8');
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
//# sourceMappingURL=cli-process.js.map
|