dsh-file-convert 0.4.1 → 0.4.3
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.
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Write `data` to `target` atomically: the bytes land in a sibling temp file
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* (same volume, so rename never hits EXDEV) and are renamed over the target
|
|
4
|
+
* only when complete. Windows AV scanners can hold the target briefly - those
|
|
5
|
+
* lock errors are retried a few times; a persistent failure throws loudly
|
|
6
|
+
* rather than degrading to a non-atomic copy.
|
|
5
7
|
*/
|
|
6
8
|
export declare function writeFileAtomic(target: string, data: Buffer | string): Promise<void>;
|
|
@@ -2,20 +2,26 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
/**
|
|
4
4
|
* Write `data` to `target` atomically: the bytes land in a sibling temp file
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* (same volume, so rename never hits EXDEV) and are renamed over the target
|
|
6
|
+
* only when complete. Windows AV scanners can hold the target briefly - those
|
|
7
|
+
* lock errors are retried a few times; a persistent failure throws loudly
|
|
8
|
+
* rather than degrading to a non-atomic copy.
|
|
7
9
|
*/
|
|
8
10
|
export async function writeFileAtomic(target, data) {
|
|
9
11
|
const tmp = path.join(path.dirname(target), `.${path.basename(target)}.${Date.now()}-${Math.random().toString(36).slice(2)}.tmp`);
|
|
10
12
|
try {
|
|
11
13
|
await fs.writeFile(tmp, data);
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
for (let attempt = 0;; attempt++) {
|
|
15
|
+
try {
|
|
16
|
+
await fs.rename(tmp, target);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
catch (err) {
|
|
20
|
+
const code = err.code;
|
|
21
|
+
if (attempt >= 2 || (code !== 'EPERM' && code !== 'EACCES' && code !== 'EBUSY'))
|
|
22
|
+
throw err;
|
|
23
|
+
await new Promise((resolve) => setTimeout(resolve, 75 * (attempt + 1)));
|
|
24
|
+
}
|
|
19
25
|
}
|
|
20
26
|
}
|
|
21
27
|
catch (err) {
|
|
@@ -3,7 +3,7 @@ import path from 'node:path';
|
|
|
3
3
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
4
4
|
import { DetectError, optimizeFile, resolveBinary } from '../core/index.js';
|
|
5
5
|
import { canonicalExtension } from '../core/index.js';
|
|
6
|
-
import { isInsideAnyRoot } from '../core/index.js';
|
|
6
|
+
import { isInsideAnyRoot, isSameFile } from '../core/index.js';
|
|
7
7
|
import { formatBytes, formatDuration, formatFailure } from '../format.js';
|
|
8
8
|
export function createOptimizeFileTool(router, config, logger) {
|
|
9
9
|
return defineTool({
|
|
@@ -47,7 +47,7 @@ export function createOptimizeFileTool(router, config, logger) {
|
|
|
47
47
|
const outExt = format === 'mov' ? '.mp4' : canonicalExtension(format);
|
|
48
48
|
const output = args.output ??
|
|
49
49
|
path.join(path.dirname(args.input), `${path.basename(args.input, path.extname(args.input))}-min${outExt}`);
|
|
50
|
-
if (
|
|
50
|
+
if (await isSameFile(output, args.input)) {
|
|
51
51
|
throw new Error('Output path equals the input path; optimizing would destroy the source. Use the default -min output name or pick another path.');
|
|
52
52
|
}
|
|
53
53
|
if (args.output !== undefined && config.outputRoots.length > 0 && !(await isInsideAnyRoot(args.output, config.outputRoots))) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-file-convert",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Local-first file conversion for DeepSeek Harness - images, PDF, data, audio/video and office documents. No API keys, no uploads, no token cost.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|