mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
* chore: bump vela cli to 0.0.4-test.0 * chore: refresh lockfile for vela cli 0.0.4-test.0 * chore(nix): refresh pnpm deps hash * fix: materialize electron before mac release checks * fix: rebuild electron when mac framework links are invalid * revert: drop release workflow experiments * chore(nix): refresh pnpm deps hash * fix: stop blocking beta mac release on electron symlink preflight * fix: stop using custom electron dist for beta mac packaging * fix: guard oversized chat images and opencode overflow * chore: bump vela cli to 0.0.4 * chore(nix): refresh pnpm deps hash * fix(daemon): surface prompt-image stat failures instead of dropping them resolveSafePromptImagePaths only swallowed unresolvable path input; once a path was confirmed inside UPLOAD_DIR and existed, a statSync failure (EACCES/EPERM, a file vanishing mid-run) silently dropped the image and let the run continue without that prompt context. Since this helper is now also the 1 MB enforcement point, that turned an infra/validation failure into a 'successful' run with missing required context. Collect those into a new failedImages bucket and fail the run with INTERNAL_ERROR at the call site, mirroring the oversized-image guard. Add a unit test covering statSync throwing. --------- Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com> Co-authored-by: lefarcen <935902669@qq.com>
77 lines
2.3 KiB
TypeScript
77 lines
2.3 KiB
TypeScript
import { expect, test } from 'vitest';
|
|
|
|
import { resolveSafePromptImagePaths, selectPromptImagePaths } from '../src/server.js';
|
|
|
|
test('selectPromptImagePaths uses staged AMR paths in prompt text', () => {
|
|
expect(
|
|
selectPromptImagePaths(
|
|
'amr',
|
|
['/tmp/od-uploads/original.png'],
|
|
['/project/.amr-attachments/staged.png'],
|
|
),
|
|
).toEqual(['/project/.amr-attachments/staged.png']);
|
|
});
|
|
|
|
test('selectPromptImagePaths keeps original paths for non-AMR agents', () => {
|
|
expect(
|
|
selectPromptImagePaths(
|
|
'opencode',
|
|
['/tmp/od-uploads/original.png'],
|
|
['/project/.amr-attachments/staged.png'],
|
|
),
|
|
).toEqual(['/tmp/od-uploads/original.png']);
|
|
});
|
|
|
|
test('resolveSafePromptImagePaths rejects images larger than 1 MB', () => {
|
|
const result = resolveSafePromptImagePaths(
|
|
['/tmp/od-uploads/too-large.png', '/tmp/od-uploads/ok.png'],
|
|
{
|
|
uploadDir: '/tmp/od-uploads',
|
|
existsSync: () => true,
|
|
statSync: (inputPath: string) => ({
|
|
isFile: () => true,
|
|
size: inputPath.endsWith('too-large.png') ? 1024 * 1024 + 1 : 1024,
|
|
}),
|
|
},
|
|
);
|
|
|
|
expect(result.safeImages).toEqual(['/tmp/od-uploads/ok.png']);
|
|
expect(result.oversizedImages).toEqual([
|
|
{ path: '/tmp/od-uploads/too-large.png', sizeBytes: 1024 * 1024 + 1 },
|
|
]);
|
|
});
|
|
|
|
test('resolveSafePromptImagePaths keeps images at or below 1 MB', () => {
|
|
const result = resolveSafePromptImagePaths(
|
|
['/tmp/od-uploads/exactly-1mb.png'],
|
|
{
|
|
uploadDir: '/tmp/od-uploads',
|
|
existsSync: () => true,
|
|
statSync: () => ({
|
|
isFile: () => true,
|
|
size: 1024 * 1024,
|
|
}),
|
|
},
|
|
);
|
|
|
|
expect(result.safeImages).toEqual(['/tmp/od-uploads/exactly-1mb.png']);
|
|
expect(result.oversizedImages).toEqual([]);
|
|
});
|
|
|
|
test('resolveSafePromptImagePaths surfaces stat failures instead of dropping the image', () => {
|
|
const result = resolveSafePromptImagePaths(['/tmp/od-uploads/unreadable.png'], {
|
|
uploadDir: '/tmp/od-uploads',
|
|
existsSync: () => true,
|
|
statSync: () => {
|
|
throw Object.assign(new Error('EACCES: permission denied'), {
|
|
code: 'EACCES',
|
|
});
|
|
},
|
|
});
|
|
|
|
expect(result.safeImages).toEqual([]);
|
|
expect(result.oversizedImages).toEqual([]);
|
|
expect(result.failedImages).toEqual([
|
|
{ path: '/tmp/od-uploads/unreadable.png', error: 'EACCES: permission denied' },
|
|
]);
|
|
});
|