@autokap/core 1.7.1 → 1.7.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.
- package/dist/fs-paths.d.ts +45 -0
- package/dist/fs-paths.js +138 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +5 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sanitize a single path segment (NOT a full path — slashes are stripped) into
|
|
3
|
+
* a safe, deterministic filename token.
|
|
4
|
+
*
|
|
5
|
+
* - Collapses any run of characters outside `[A-Za-z0-9._-]` to a single `-`.
|
|
6
|
+
* - Collapses repeated `-`.
|
|
7
|
+
* - Trims leading/trailing `.`/`-`/space (Windows rejects trailing dot/space;
|
|
8
|
+
* a leading dot would hide the file on POSIX).
|
|
9
|
+
* - Prefixes `_` when the result collides with a Windows reserved device name.
|
|
10
|
+
* - Caps length at 80 chars.
|
|
11
|
+
* - Falls back to `'artifact'` when nothing usable remains.
|
|
12
|
+
*/
|
|
13
|
+
export declare function sanitizePathToken(value: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve `fileName` inside `outputDir`, refusing any result that escapes the
|
|
16
|
+
* directory (via `..` segments or an absolute component). Returns the absolute
|
|
17
|
+
* contained path; throws otherwise.
|
|
18
|
+
*
|
|
19
|
+
* `fileName` should already be sanitized with `sanitizePathToken`; this is the
|
|
20
|
+
* defense-in-depth boundary check.
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveContainedPath(outputDir: string, fileName: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Map an asset `format` token (`png`, `webp`, `gif`, `mp4`, …) to a file
|
|
25
|
+
* extension (without the leading dot). `jpeg` normalizes to `jpg`. Unknown
|
|
26
|
+
* formats fall back to `bin`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function extFromFormat(format: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Map a MIME type to a file extension (without the leading dot). Mirrors the
|
|
31
|
+
* mime→ext ladder previously inline in `cli-runner-local.ts`. Unknown types
|
|
32
|
+
* fall back to `bin`.
|
|
33
|
+
*/
|
|
34
|
+
export declare function extFromMimeType(mimeType: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* Atomically write `data` to `dest`: write to a per-process tmp sibling, then
|
|
37
|
+
* rename over the destination. A bare `writeFile` can be interrupted (crash,
|
|
38
|
+
* OOM, signal) mid-write, leaving a truncated file in the user's repo;
|
|
39
|
+
* tmp+rename is a single `rename(2)` syscall, atomic on POSIX (same volume)
|
|
40
|
+
* and on Windows NTFS.
|
|
41
|
+
*
|
|
42
|
+
* Unlike `~/.autokap` writes, this does NOT chmod — the destination is the
|
|
43
|
+
* user's project directory and should inherit normal umask permissions.
|
|
44
|
+
*/
|
|
45
|
+
export declare function writeFileAtomic(dest: string, data: Buffer): Promise<void>;
|
package/dist/fs-paths.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import fs from 'node:fs/promises';
|
|
3
|
+
/**
|
|
4
|
+
* Shared filesystem-path helpers for the CLI and the MCP server.
|
|
5
|
+
*
|
|
6
|
+
* These were originally private to `src/cli-runner-local.ts` (CLI-only). The
|
|
7
|
+
* MCP download tools (`autokap_download_assets`) need the same path-safety and
|
|
8
|
+
* atomic-write guarantees when writing fetched assets into a user-chosen
|
|
9
|
+
* directory, so they live here in `@autokap/core` — imported by both surfaces.
|
|
10
|
+
*
|
|
11
|
+
* Cross-platform notes (CLAUDE.md §8):
|
|
12
|
+
* - Always go through `path.*` (join/resolve/relative); never string-concat
|
|
13
|
+
* with `/`.
|
|
14
|
+
* - `sanitizePathToken` additionally guards Windows reserved device names and
|
|
15
|
+
* trailing dots/spaces, which are illegal in NTFS filenames but harmless on
|
|
16
|
+
* POSIX.
|
|
17
|
+
* - `writeFileAtomic` deliberately does NOT chmod — downloads land in the
|
|
18
|
+
* user's project directory (a repo `docs/images` dir), not a secrets dir,
|
|
19
|
+
* so the restrictive 0o600 used for `~/.autokap/config.json` would be wrong.
|
|
20
|
+
*/
|
|
21
|
+
// Windows reserved device names (case-insensitive, with or without extension):
|
|
22
|
+
// CON, PRN, AUX, NUL, COM1-9, LPT1-9. A file literally named `CON.png` is
|
|
23
|
+
// rejected by the Win32 API, so we prefix an underscore to make it legal.
|
|
24
|
+
const WINDOWS_RESERVED_NAME_RE = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\..*)?$/i;
|
|
25
|
+
const MAX_PATH_TOKEN_LENGTH = 80;
|
|
26
|
+
/**
|
|
27
|
+
* Sanitize a single path segment (NOT a full path — slashes are stripped) into
|
|
28
|
+
* a safe, deterministic filename token.
|
|
29
|
+
*
|
|
30
|
+
* - Collapses any run of characters outside `[A-Za-z0-9._-]` to a single `-`.
|
|
31
|
+
* - Collapses repeated `-`.
|
|
32
|
+
* - Trims leading/trailing `.`/`-`/space (Windows rejects trailing dot/space;
|
|
33
|
+
* a leading dot would hide the file on POSIX).
|
|
34
|
+
* - Prefixes `_` when the result collides with a Windows reserved device name.
|
|
35
|
+
* - Caps length at 80 chars.
|
|
36
|
+
* - Falls back to `'artifact'` when nothing usable remains.
|
|
37
|
+
*/
|
|
38
|
+
export function sanitizePathToken(value) {
|
|
39
|
+
const cleaned = value
|
|
40
|
+
.trim()
|
|
41
|
+
.replace(/[^a-zA-Z0-9._-]+/g, '-')
|
|
42
|
+
.replace(/-+/g, '-')
|
|
43
|
+
// Strip leading/trailing dots, dashes and spaces. Leading `.` hides the
|
|
44
|
+
// file on POSIX; trailing `.`/space is silently dropped by Win32 (so two
|
|
45
|
+
// distinct tokens could collapse to the same on-disk name).
|
|
46
|
+
.replace(/^[.\-\s]+/, '')
|
|
47
|
+
.replace(/[.\-\s]+$/, '');
|
|
48
|
+
if (cleaned.length === 0) {
|
|
49
|
+
return 'artifact';
|
|
50
|
+
}
|
|
51
|
+
const capped = cleaned.slice(0, MAX_PATH_TOKEN_LENGTH);
|
|
52
|
+
// Re-trim after the slice — capping could have left a trailing `.`/`-`.
|
|
53
|
+
const trimmed = capped.replace(/[.\-\s]+$/, '');
|
|
54
|
+
const safe = trimmed.length > 0 ? trimmed : 'artifact';
|
|
55
|
+
if (WINDOWS_RESERVED_NAME_RE.test(safe)) {
|
|
56
|
+
return `_${safe}`;
|
|
57
|
+
}
|
|
58
|
+
return safe;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resolve `fileName` inside `outputDir`, refusing any result that escapes the
|
|
62
|
+
* directory (via `..` segments or an absolute component). Returns the absolute
|
|
63
|
+
* contained path; throws otherwise.
|
|
64
|
+
*
|
|
65
|
+
* `fileName` should already be sanitized with `sanitizePathToken`; this is the
|
|
66
|
+
* defense-in-depth boundary check.
|
|
67
|
+
*/
|
|
68
|
+
export function resolveContainedPath(outputDir, fileName) {
|
|
69
|
+
const resolved = path.resolve(outputDir, fileName);
|
|
70
|
+
const relative = path.relative(outputDir, resolved);
|
|
71
|
+
if (relative.startsWith('..') || path.isAbsolute(relative)) {
|
|
72
|
+
throw new Error(`Refusing to write outside output directory: ${fileName}`);
|
|
73
|
+
}
|
|
74
|
+
return resolved;
|
|
75
|
+
}
|
|
76
|
+
const FORMAT_EXTENSIONS = {
|
|
77
|
+
png: 'png',
|
|
78
|
+
jpg: 'jpg',
|
|
79
|
+
jpeg: 'jpg',
|
|
80
|
+
webp: 'webp',
|
|
81
|
+
gif: 'gif',
|
|
82
|
+
mp4: 'mp4',
|
|
83
|
+
webm: 'webm',
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Map an asset `format` token (`png`, `webp`, `gif`, `mp4`, …) to a file
|
|
87
|
+
* extension (without the leading dot). `jpeg` normalizes to `jpg`. Unknown
|
|
88
|
+
* formats fall back to `bin`.
|
|
89
|
+
*/
|
|
90
|
+
export function extFromFormat(format) {
|
|
91
|
+
return FORMAT_EXTENSIONS[format.trim().toLowerCase()] ?? 'bin';
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Map a MIME type to a file extension (without the leading dot). Mirrors the
|
|
95
|
+
* mime→ext ladder previously inline in `cli-runner-local.ts`. Unknown types
|
|
96
|
+
* fall back to `bin`.
|
|
97
|
+
*/
|
|
98
|
+
export function extFromMimeType(mimeType) {
|
|
99
|
+
const mime = mimeType.split(';', 1)[0].trim().toLowerCase();
|
|
100
|
+
switch (mime) {
|
|
101
|
+
case 'image/png':
|
|
102
|
+
return 'png';
|
|
103
|
+
case 'image/jpeg':
|
|
104
|
+
return 'jpg';
|
|
105
|
+
case 'image/webp':
|
|
106
|
+
return 'webp';
|
|
107
|
+
case 'image/gif':
|
|
108
|
+
return 'gif';
|
|
109
|
+
case 'video/mp4':
|
|
110
|
+
return 'mp4';
|
|
111
|
+
case 'video/webm':
|
|
112
|
+
return 'webm';
|
|
113
|
+
default:
|
|
114
|
+
return 'bin';
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Atomically write `data` to `dest`: write to a per-process tmp sibling, then
|
|
119
|
+
* rename over the destination. A bare `writeFile` can be interrupted (crash,
|
|
120
|
+
* OOM, signal) mid-write, leaving a truncated file in the user's repo;
|
|
121
|
+
* tmp+rename is a single `rename(2)` syscall, atomic on POSIX (same volume)
|
|
122
|
+
* and on Windows NTFS.
|
|
123
|
+
*
|
|
124
|
+
* Unlike `~/.autokap` writes, this does NOT chmod — the destination is the
|
|
125
|
+
* user's project directory and should inherit normal umask permissions.
|
|
126
|
+
*/
|
|
127
|
+
export async function writeFileAtomic(dest, data) {
|
|
128
|
+
const tmp = `${dest}.${process.pid}.${Date.now().toString(36)}.tmp`;
|
|
129
|
+
try {
|
|
130
|
+
await fs.writeFile(tmp, data);
|
|
131
|
+
await fs.rename(tmp, dest);
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
await fs.rm(tmp, { force: true }).catch(() => undefined);
|
|
135
|
+
throw err;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=fs-paths.js.map
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autokap/core",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.3",
|
|
4
4
|
"description": "Shared core library for AutoKap CLI and MCP server",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"author": "AutoKap",
|
|
@@ -33,6 +33,10 @@
|
|
|
33
33
|
"types": "./dist/endpoint-helpers.d.ts",
|
|
34
34
|
"default": "./dist/endpoint-helpers.js"
|
|
35
35
|
},
|
|
36
|
+
"./fs-paths": {
|
|
37
|
+
"types": "./dist/fs-paths.d.ts",
|
|
38
|
+
"default": "./dist/fs-paths.js"
|
|
39
|
+
},
|
|
36
40
|
"./types": {
|
|
37
41
|
"types": "./dist/types.d.ts",
|
|
38
42
|
"default": "./dist/types.js"
|