mirror of
https://github.com/fancive/obsidian-parallel-reader.git
synced 2026-07-22 06:53:43 +00:00
fix: remove unsupported --max-tokens flag from claude CLI args
The Claude Code CLI doesn't support --max-tokens. This caused the CLI backend to always fail with exit code 1. Also adds args validation tests for both claude and codex CLI backends. Change-Id: I57c5cc672ed08de291e3941511ce68ba56e5b4ff
This commit is contained in:
parent
ea5f440ca5
commit
df48dc2cc4
4 changed files with 148 additions and 21 deletions
30
main.js
30
main.js
File diff suppressed because one or more lines are too long
|
|
@ -164,21 +164,19 @@ export async function summarizeViaClaudeCode(
|
|||
user: string,
|
||||
settings: PluginSettings,
|
||||
job?: GenerationJob,
|
||||
spawnImpl?: typeof spawn,
|
||||
): Promise<RawCard[]> {
|
||||
const cmd = resolveCliPath('claude', settings.cliPath);
|
||||
const maxTokens = Number(settings.apiMaxTokens) || 4096;
|
||||
const args = [
|
||||
'-p',
|
||||
'--output-format',
|
||||
'json',
|
||||
'--max-tokens',
|
||||
String(maxTokens),
|
||||
'--append-system-prompt',
|
||||
system,
|
||||
'--disallowed-tools',
|
||||
'Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch,TodoWrite,Task',
|
||||
];
|
||||
const { stdout } = await runCli(cmd, args, user, settings.cliTimeoutMs, job);
|
||||
const { stdout } = await runCli(cmd, args, user, settings.cliTimeoutMs, job, spawnImpl);
|
||||
|
||||
// --output-format json produces a JSON array of event objects.
|
||||
// Find the "result" entry and extract its .result text field.
|
||||
|
|
@ -211,10 +209,11 @@ export async function summarizeViaCodex(
|
|||
user: string,
|
||||
settings: PluginSettings,
|
||||
job?: GenerationJob,
|
||||
spawnImpl?: typeof spawn,
|
||||
): Promise<RawCard[]> {
|
||||
const cmd = resolveCliPath('codex', settings.cliPath);
|
||||
const combined = `<<SYSTEM>>\n${system}\n<<USER>>\n${user}\n\nOutput JSON directly with no explanation.`;
|
||||
const args = ['exec', '--skip-git-repo-check', '-'];
|
||||
const { stdout } = await runCli(cmd, args, combined, settings.cliTimeoutMs, job);
|
||||
const { stdout } = await runCli(cmd, args, combined, settings.cliTimeoutMs, job, spawnImpl);
|
||||
return parseCardsJson(stdout, settings);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ export {
|
|||
export { serializeCacheFile, shouldConfirmRegenerate, touchCacheEntry } from './cache';
|
||||
export { CacheManager } from './cache-manager';
|
||||
export { activeIndexAfterCardDelete, removeCardAt, updateCardAt } from './cards';
|
||||
export { resolveCliPath, runCli } from './cli';
|
||||
export { resolveCliPath, runCli, summarizeViaClaudeCode, summarizeViaCodex } from './cli';
|
||||
export { cancellationNoticeKey, summarizeDocument } from './generation';
|
||||
export {
|
||||
classifyGenerationError,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,132 @@ async function testRunCliEdgeCases() {
|
|||
);
|
||||
}
|
||||
|
||||
// ── summarizeViaClaudeCode args validation ──
|
||||
|
||||
// Known flags supported by the Claude Code CLI (from `claude --help`).
|
||||
const VALID_CLAUDE_FLAGS = new Set([
|
||||
'--add-dir', '--agent', '--agents', '--allow-dangerously-skip-permissions',
|
||||
'--allowedTools', '--allowed-tools', '--append-system-prompt', '--bare',
|
||||
'--betas', '--brief', '--chrome', '--dangerously-skip-permissions',
|
||||
'--debug-file', '--disable-slash-commands', '--disallowedTools',
|
||||
'--disallowed-tools', '--effort', '--exclude-dynamic-system-prompt-sections',
|
||||
'--fallback-model', '--file', '--fork-session', '--from-pr', '--ide',
|
||||
'--include-hook-events', '--include-partial-messages', '--input-format',
|
||||
'--json-schema', '--max-budget-usd', '--mcp-config', '--mcp-debug',
|
||||
'--model', '--no-chrome', '--no-session-persistence', '--output-format',
|
||||
'--permission-mode', '--plugin-dir', '--remote-control-session-name-prefix',
|
||||
'--replay-user-messages', '--session-id', '--setting-sources', '--settings',
|
||||
'--strict-mcp-config', '--system-prompt', '--tmux', '--tools', '--verbose',
|
||||
'-p', '-c', '-d', '-h', '-n', '-r', '-v', '-w',
|
||||
]);
|
||||
|
||||
async function testClaudeCodeArgs() {
|
||||
let capturedArgs = null;
|
||||
const resultJson = JSON.stringify([{ type: 'result', result: '{"cards":[{"title":"T","anchor":"hello world test anchor","gist":"G","bullets":["B"]}]}' }]);
|
||||
const fakeSpawn = (_cmd, args) => {
|
||||
capturedArgs = args;
|
||||
const child = createFakeChild();
|
||||
process.nextTick(() => {
|
||||
child.stdout.emit('data', Buffer.from(resultJson));
|
||||
child.emit('close', 0);
|
||||
});
|
||||
return child;
|
||||
};
|
||||
|
||||
const settings = {
|
||||
backend: 'claude-code',
|
||||
cliPath: '/usr/bin/claude',
|
||||
apiMaxTokens: 8192,
|
||||
cliTimeoutMs: 5000,
|
||||
promptLanguage: 'zh',
|
||||
minCards: 5,
|
||||
maxCards: 15,
|
||||
maxDocChars: 100000,
|
||||
};
|
||||
|
||||
await t.summarizeViaClaudeCode('system prompt', 'user content', settings, undefined, fakeSpawn);
|
||||
|
||||
assert.ok(capturedArgs, 'summarizeViaClaudeCode should have called spawn');
|
||||
|
||||
// Verify every --flag in the args is a valid Claude CLI flag
|
||||
const flags = capturedArgs.filter(a => a.startsWith('-'));
|
||||
for (const flag of flags) {
|
||||
assert.ok(
|
||||
VALID_CLAUDE_FLAGS.has(flag),
|
||||
`summarizeViaClaudeCode passes unsupported flag "${flag}" to claude CLI`,
|
||||
);
|
||||
}
|
||||
|
||||
// Verify key expected flags are present
|
||||
assert.ok(capturedArgs.includes('-p'), 'should include -p (print mode)');
|
||||
assert.ok(capturedArgs.includes('--output-format'), 'should include --output-format');
|
||||
assert.ok(capturedArgs.includes('--append-system-prompt'), 'should include --append-system-prompt');
|
||||
assert.ok(capturedArgs.includes('--disallowed-tools'), 'should include --disallowed-tools');
|
||||
|
||||
// Verify --max-tokens is NOT present (it's not a valid claude CLI flag)
|
||||
assert.ok(!capturedArgs.includes('--max-tokens'), 'should NOT include --max-tokens');
|
||||
}
|
||||
|
||||
// ── summarizeViaCodex args validation ──
|
||||
|
||||
// Known flags/subcommands supported by `codex exec` (from `codex exec --help`).
|
||||
const VALID_CODEX_EXEC_FLAGS = new Set([
|
||||
'-c', '--config', '--enable', '--disable', '-i', '--image',
|
||||
'-m', '--model', '--oss', '--local-provider', '-p', '--profile',
|
||||
'-s', '--sandbox', '--full-auto', '--dangerously-bypass-approvals-and-sandbox',
|
||||
'-C', '--cd', '--add-dir', '--skip-git-repo-check', '--ephemeral',
|
||||
'--ignore-user-config', '--ignore-rules', '--output-schema', '--color',
|
||||
'--json', '-o', '--output-last-message', '-h', '--help', '-V', '--version',
|
||||
]);
|
||||
|
||||
async function testCodexArgs() {
|
||||
let capturedArgs = null;
|
||||
const resultJson = '{"cards":[{"title":"T","anchor":"hello world test anchor","gist":"G","bullets":["B"]}]}';
|
||||
const fakeSpawn = (_cmd, args) => {
|
||||
capturedArgs = args;
|
||||
const child = createFakeChild();
|
||||
process.nextTick(() => {
|
||||
child.stdout.emit('data', Buffer.from(resultJson));
|
||||
child.emit('close', 0);
|
||||
});
|
||||
return child;
|
||||
};
|
||||
|
||||
const settings = {
|
||||
backend: 'codex',
|
||||
cliPath: '/usr/bin/codex',
|
||||
cliTimeoutMs: 5000,
|
||||
promptLanguage: 'zh',
|
||||
minCards: 5,
|
||||
maxCards: 15,
|
||||
maxDocChars: 100000,
|
||||
};
|
||||
|
||||
await t.summarizeViaCodex('system prompt', 'user content', settings, undefined, fakeSpawn);
|
||||
|
||||
assert.ok(capturedArgs, 'summarizeViaCodex should have called spawn');
|
||||
|
||||
// First arg must be the 'exec' subcommand
|
||||
assert.strictEqual(capturedArgs[0], 'exec', 'first arg should be exec subcommand');
|
||||
|
||||
// Verify every --flag (after 'exec') is valid for `codex exec`
|
||||
const flags = capturedArgs.slice(1).filter(a => a.startsWith('-') && a !== '-');
|
||||
for (const flag of flags) {
|
||||
assert.ok(
|
||||
VALID_CODEX_EXEC_FLAGS.has(flag),
|
||||
`summarizeViaCodex passes unsupported flag "${flag}" to codex exec`,
|
||||
);
|
||||
}
|
||||
|
||||
// Verify key expected flags are present
|
||||
assert.ok(capturedArgs.includes('--skip-git-repo-check'), 'should include --skip-git-repo-check');
|
||||
// '-' means read prompt from stdin
|
||||
assert.ok(capturedArgs.includes('-'), 'should include - (read from stdin)');
|
||||
|
||||
// Verify stdin content combines system and user prompts
|
||||
assert.ok(capturedArgs.includes('exec'), 'should include exec subcommand');
|
||||
}
|
||||
|
||||
// ── classifyGenerationError ──
|
||||
|
||||
assert.strictEqual(t.classifyGenerationError(new Error('API key 未设置')), 'auth');
|
||||
|
|
@ -92,6 +218,8 @@ assert.strictEqual(t.classifyGenerationError('string error'), 'unknown', 'string
|
|||
|
||||
(async () => {
|
||||
await testRunCliEdgeCases();
|
||||
await testClaudeCodeArgs();
|
||||
await testCodexArgs();
|
||||
console.log('cli tests passed');
|
||||
})().catch((e) => {
|
||||
console.error(e);
|
||||
|
|
|
|||
Loading…
Reference in a new issue