mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-06-28 15:25:27 +08:00
fix(opencode): pass workspace dir explicitly
The remote environment could start OpenCode runs under /opt/claudecodeui. That happened even when the selected project path was correct. The integration relied on child-process cwd alone. OpenCode run resolves its workspace through the explicit --dir contract. Pass --dir with the resolved working directory. Assert in the CLI test that launch args include the workspace dir.
This commit is contained in:
@@ -194,6 +194,10 @@ async function spawnOpenCode(command, options = {}, ws) {
|
|||||||
|
|
||||||
void providerModelsService.resolveResumeModel('opencode', sessionId, model).then((resolvedModel) => {
|
void providerModelsService.resolveResumeModel('opencode', sessionId, model).then((resolvedModel) => {
|
||||||
const args = ['run', '--format', 'json'];
|
const args = ['run', '--format', 'json'];
|
||||||
|
// OpenCode's `run` command owns workspace selection through `--dir`.
|
||||||
|
// Relying on the child-process cwd alone is not enough on Linux, where
|
||||||
|
// the CLI can still resolve the session under the server install dir.
|
||||||
|
args.push('--dir', workingDir);
|
||||||
if (sessionId) {
|
if (sessionId) {
|
||||||
args.push('--session', sessionId);
|
args.push('--session', sessionId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import assert from 'node:assert/strict';
|
import assert from 'node:assert/strict';
|
||||||
import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
import { chmod, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import test from 'node:test';
|
import test from 'node:test';
|
||||||
@@ -12,6 +12,11 @@ const findEnvKey = (name) =>
|
|||||||
async function createFakeOpenCodeExecutable(binDir) {
|
async function createFakeOpenCodeExecutable(binDir) {
|
||||||
const scriptPath = path.join(binDir, 'opencode.js');
|
const scriptPath = path.join(binDir, 'opencode.js');
|
||||||
await writeFile(scriptPath, `
|
await writeFile(scriptPath, `
|
||||||
|
const capturePath = process.env.OPENCODE_ARGS_CAPTURE;
|
||||||
|
if (capturePath) {
|
||||||
|
require('node:fs').writeFileSync(capturePath, JSON.stringify(process.argv.slice(2)));
|
||||||
|
}
|
||||||
|
|
||||||
const events = [
|
const events = [
|
||||||
{ type: 'text', sessionID: 'open-live-1', text: 'assistant response' },
|
{ type: 'text', sessionID: 'open-live-1', text: 'assistant response' },
|
||||||
{ type: 'step_finish', sessionID: 'open-live-1' },
|
{ type: 'step_finish', sessionID: 'open-live-1' },
|
||||||
@@ -35,10 +40,12 @@ for (const event of events) {
|
|||||||
|
|
||||||
test('spawnOpenCode emits session_created before normalized live messages for new sessions', async () => {
|
test('spawnOpenCode emits session_created before normalized live messages for new sessions', async () => {
|
||||||
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'opencode-cli-live-'));
|
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'opencode-cli-live-'));
|
||||||
|
const argsCapturePath = path.join(tempRoot, 'opencode-args.json');
|
||||||
const pathKey = findEnvKey('PATH');
|
const pathKey = findEnvKey('PATH');
|
||||||
const pathExtKey = findEnvKey('PATHEXT');
|
const pathExtKey = findEnvKey('PATHEXT');
|
||||||
const previousPath = process.env[pathKey];
|
const previousPath = process.env[pathKey];
|
||||||
const previousPathExt = process.env[pathExtKey];
|
const previousPathExt = process.env[pathExtKey];
|
||||||
|
const previousArgsCapture = process.env.OPENCODE_ARGS_CAPTURE;
|
||||||
const messages = [];
|
const messages = [];
|
||||||
const writer = {
|
const writer = {
|
||||||
userId: null,
|
userId: null,
|
||||||
@@ -54,6 +61,7 @@ test('spawnOpenCode emits session_created before normalized live messages for ne
|
|||||||
try {
|
try {
|
||||||
await createFakeOpenCodeExecutable(tempRoot);
|
await createFakeOpenCodeExecutable(tempRoot);
|
||||||
process.env[pathKey] = `${tempRoot}${path.delimiter}${previousPath || ''}`;
|
process.env[pathKey] = `${tempRoot}${path.delimiter}${previousPath || ''}`;
|
||||||
|
process.env.OPENCODE_ARGS_CAPTURE = argsCapturePath;
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
process.env[pathExtKey] = previousPathExt?.toUpperCase().includes('.CMD')
|
process.env[pathExtKey] = previousPathExt?.toUpperCase().includes('.CMD')
|
||||||
? previousPathExt
|
? previousPathExt
|
||||||
@@ -77,6 +85,11 @@ test('spawnOpenCode emits session_created before normalized live messages for ne
|
|||||||
assert.equal(streamEnd?.sessionId, 'open-live-1');
|
assert.equal(streamEnd?.sessionId, 'open-live-1');
|
||||||
assert.equal(complete?.sessionId, 'open-live-1');
|
assert.equal(complete?.sessionId, 'open-live-1');
|
||||||
assert.equal(messages.some((message) => message.kind === 'error'), false);
|
assert.equal(messages.some((message) => message.kind === 'error'), false);
|
||||||
|
|
||||||
|
const launchedArgs = JSON.parse(await readFile(argsCapturePath, 'utf8'));
|
||||||
|
assert.ok(Array.isArray(launchedArgs));
|
||||||
|
assert.deepEqual(launchedArgs.slice(0, 4), ['run', '--format', 'json', '--dir']);
|
||||||
|
assert.equal(launchedArgs[4], tempRoot);
|
||||||
} finally {
|
} finally {
|
||||||
if (previousPath === undefined) {
|
if (previousPath === undefined) {
|
||||||
delete process.env[pathKey];
|
delete process.env[pathKey];
|
||||||
@@ -90,6 +103,12 @@ test('spawnOpenCode emits session_created before normalized live messages for ne
|
|||||||
process.env[pathExtKey] = previousPathExt;
|
process.env[pathExtKey] = previousPathExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previousArgsCapture === undefined) {
|
||||||
|
delete process.env.OPENCODE_ARGS_CAPTURE;
|
||||||
|
} else {
|
||||||
|
process.env.OPENCODE_ARGS_CAPTURE = previousArgsCapture;
|
||||||
|
}
|
||||||
|
|
||||||
await rm(tempRoot, { recursive: true, force: true });
|
await rm(tempRoot, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user