@amirdaraee/namewise 1.0.1 → 1.2.0
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/CHANGELOG.md +94 -0
- package/README.md +8 -8
- package/dist/cli/apply.d.ts.map +1 -1
- package/dist/cli/apply.js +33 -6
- package/dist/cli/apply.js.map +1 -1
- package/dist/cli/clean-empty.d.ts.map +1 -1
- package/dist/cli/clean-empty.js +2 -3
- package/dist/cli/clean-empty.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +18 -37
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/config-cmd.d.ts.map +1 -1
- package/dist/cli/config-cmd.js +7 -4
- package/dist/cli/config-cmd.js.map +1 -1
- package/dist/cli/dedup.d.ts.map +1 -1
- package/dist/cli/dedup.js +3 -11
- package/dist/cli/dedup.js.map +1 -1
- package/dist/cli/diff.d.ts.map +1 -1
- package/dist/cli/diff.js +3 -7
- package/dist/cli/diff.js.map +1 -1
- package/dist/cli/find.d.ts.map +1 -1
- package/dist/cli/find.js +2 -3
- package/dist/cli/find.js.map +1 -1
- package/dist/cli/flatten.d.ts.map +1 -1
- package/dist/cli/flatten.js +4 -11
- package/dist/cli/flatten.js.map +1 -1
- package/dist/cli/handle-cli-error.d.ts +7 -0
- package/dist/cli/handle-cli-error.d.ts.map +1 -0
- package/dist/cli/handle-cli-error.js +27 -0
- package/dist/cli/handle-cli-error.js.map +1 -0
- package/dist/cli/init.d.ts.map +1 -1
- package/dist/cli/init.js +8 -6
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/organize.d.ts.map +1 -1
- package/dist/cli/organize.js +4 -11
- package/dist/cli/organize.js.map +1 -1
- package/dist/cli/rename.d.ts +4 -2
- package/dist/cli/rename.d.ts.map +1 -1
- package/dist/cli/rename.js +171 -227
- package/dist/cli/rename.js.map +1 -1
- package/dist/cli/sanitize.d.ts.map +1 -1
- package/dist/cli/sanitize.js +4 -12
- package/dist/cli/sanitize.js.map +1 -1
- package/dist/cli/shared-config.d.ts +60 -0
- package/dist/cli/shared-config.d.ts.map +1 -0
- package/dist/cli/shared-config.js +64 -0
- package/dist/cli/shared-config.js.map +1 -0
- package/dist/cli/stats.d.ts.map +1 -1
- package/dist/cli/stats.js +2 -4
- package/dist/cli/stats.js.map +1 -1
- package/dist/cli/tree.d.ts.map +1 -1
- package/dist/cli/tree.js +2 -3
- package/dist/cli/tree.js.map +1 -1
- package/dist/cli/watch.d.ts +3 -1
- package/dist/cli/watch.d.ts.map +1 -1
- package/dist/cli/watch.js +19 -63
- package/dist/cli/watch.js.map +1 -1
- package/dist/parsers/pdf-parser.d.ts.map +1 -1
- package/dist/parsers/pdf-parser.js +44 -27
- package/dist/parsers/pdf-parser.js.map +1 -1
- package/dist/services/base-cloud-service.d.ts +32 -0
- package/dist/services/base-cloud-service.d.ts.map +1 -0
- package/dist/services/base-cloud-service.js +73 -0
- package/dist/services/base-cloud-service.js.map +1 -0
- package/dist/services/base-local-service.d.ts +18 -0
- package/dist/services/base-local-service.d.ts.map +1 -0
- package/dist/services/base-local-service.js +81 -0
- package/dist/services/base-local-service.js.map +1 -0
- package/dist/services/claude-service.d.ts +8 -6
- package/dist/services/claude-service.d.ts.map +1 -1
- package/dist/services/claude-service.js +40 -98
- package/dist/services/claude-service.js.map +1 -1
- package/dist/services/file-renamer.d.ts +7 -0
- package/dist/services/file-renamer.d.ts.map +1 -1
- package/dist/services/file-renamer.js +63 -12
- package/dist/services/file-renamer.js.map +1 -1
- package/dist/services/lmstudio-service.d.ts +35 -9
- package/dist/services/lmstudio-service.d.ts.map +1 -1
- package/dist/services/lmstudio-service.js +23 -101
- package/dist/services/lmstudio-service.js.map +1 -1
- package/dist/services/ollama-service.d.ts +23 -9
- package/dist/services/ollama-service.d.ts.map +1 -1
- package/dist/services/ollama-service.js +16 -87
- package/dist/services/ollama-service.js.map +1 -1
- package/dist/services/openai-service.d.ts +8 -6
- package/dist/services/openai-service.d.ts.map +1 -1
- package/dist/services/openai-service.js +36 -94
- package/dist/services/openai-service.js.map +1 -1
- package/dist/utils/ai-name-sanitizer.d.ts +14 -0
- package/dist/utils/ai-name-sanitizer.d.ts.map +1 -0
- package/dist/utils/ai-name-sanitizer.js +55 -0
- package/dist/utils/ai-name-sanitizer.js.map +1 -0
- package/dist/utils/ai-prompts.d.ts.map +1 -1
- package/dist/utils/ai-prompts.js +2 -1
- package/dist/utils/ai-prompts.js.map +1 -1
- package/dist/utils/assert-directory.d.ts +9 -0
- package/dist/utils/assert-directory.d.ts.map +1 -0
- package/dist/utils/assert-directory.js +15 -0
- package/dist/utils/assert-directory.js.map +1 -0
- package/dist/utils/file-info.d.ts +8 -0
- package/dist/utils/file-info.d.ts.map +1 -0
- package/dist/utils/file-info.js +25 -0
- package/dist/utils/file-info.js.map +1 -0
- package/dist/utils/file-templates.d.ts.map +1 -1
- package/dist/utils/file-templates.js +31 -1
- package/dist/utils/file-templates.js.map +1 -1
- package/dist/utils/image-compressor.d.ts +1 -1
- package/dist/utils/image-compressor.d.ts.map +1 -1
- package/dist/utils/image-compressor.js +4 -2
- package/dist/utils/image-compressor.js.map +1 -1
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +4 -0
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/record-session.d.ts +18 -0
- package/dist/utils/record-session.d.ts.map +1 -0
- package/dist/utils/record-session.js +24 -0
- package/dist/utils/record-session.js.map +1 -0
- package/dist/utils/ui.d.ts.map +1 -1
- package/dist/utils/ui.js +1 -7
- package/dist/utils/ui.js.map +1 -1
- package/package.json +17 -18
|
@@ -1,108 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export class LMStudioService {
|
|
4
|
-
name = 'LMStudio';
|
|
5
|
-
baseUrl;
|
|
6
|
-
model;
|
|
1
|
+
import { AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
|
|
2
|
+
import { BaseLocalService } from './base-local-service.js';
|
|
3
|
+
export class LMStudioService extends BaseLocalService {
|
|
7
4
|
constructor(baseUrl = 'http://localhost:1234', model = 'local-model') {
|
|
8
|
-
|
|
9
|
-
this.model = model;
|
|
5
|
+
super('LMStudio', baseUrl, model);
|
|
10
6
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const hostname = parsed.hostname.toLowerCase();
|
|
20
|
-
const allowed = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '[::1]';
|
|
21
|
-
if (!allowed) {
|
|
22
|
-
throw new ConfigError(`LMStudio base URL must point to localhost (got: ${hostname})`);
|
|
23
|
-
}
|
|
24
|
-
return url;
|
|
25
|
-
}
|
|
26
|
-
async generateFileName(content, originalName, namingConvention = 'kebab-case', category = 'general', fileInfo, language, context, imageData) {
|
|
27
|
-
try {
|
|
28
|
-
if (imageData && (!imageData.startsWith('data:image/') || !imageData.includes(','))) {
|
|
29
|
-
throw new NetworkError('Invalid image data format');
|
|
30
|
-
}
|
|
31
|
-
const prompt = buildFileNamePrompt({
|
|
32
|
-
content: imageData
|
|
33
|
-
? 'Analyze this image and generate an appropriate filename based on what you see.'
|
|
34
|
-
: content,
|
|
35
|
-
originalName,
|
|
36
|
-
namingConvention: namingConvention,
|
|
37
|
-
category: category,
|
|
38
|
-
fileInfo,
|
|
39
|
-
language,
|
|
40
|
-
context
|
|
41
|
-
});
|
|
42
|
-
const userMessage = imageData
|
|
43
|
-
? {
|
|
44
|
-
role: 'user',
|
|
45
|
-
content: [
|
|
46
|
-
{ type: 'text', text: prompt },
|
|
47
|
-
{ type: 'image_url', image_url: { url: imageData } }
|
|
48
|
-
]
|
|
49
|
-
}
|
|
50
|
-
: { role: 'user', content: prompt };
|
|
51
|
-
const response = await this.makeRequest('/v1/chat/completions', {
|
|
52
|
-
model: this.model,
|
|
53
|
-
messages: [
|
|
54
|
-
{ role: 'system', content: AI_SYSTEM_PROMPT },
|
|
55
|
-
userMessage
|
|
56
|
-
],
|
|
57
|
-
temperature: 0.3,
|
|
58
|
-
max_tokens: 100,
|
|
59
|
-
stream: false
|
|
60
|
-
});
|
|
61
|
-
if (response.choices?.[0]?.message?.content) {
|
|
62
|
-
return {
|
|
63
|
-
name: this.sanitizeFilename(response.choices[0].message.content),
|
|
64
|
-
inputTokens: undefined,
|
|
65
|
-
outputTokens: undefined
|
|
66
|
-
};
|
|
7
|
+
async requestCompletion(prompt, imageData) {
|
|
8
|
+
const userMessage = imageData
|
|
9
|
+
? {
|
|
10
|
+
role: 'user',
|
|
11
|
+
content: [
|
|
12
|
+
{ type: 'text', text: prompt },
|
|
13
|
+
{ type: 'image_url', image_url: { url: imageData } }
|
|
14
|
+
]
|
|
67
15
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
.trim()
|
|
79
|
-
.replace(/^["']|["']$/g, '') // Remove quotes
|
|
80
|
-
.replace(/\.(txt|pdf|docx?|xlsx?|md|rtf)$/i, '') // Remove extensions
|
|
81
|
-
.replace(/[<>:"/\\|?*]/g, '-') // Replace invalid characters
|
|
82
|
-
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
83
|
-
.toLowerCase();
|
|
84
|
-
}
|
|
85
|
-
async makeRequest(endpoint, payload) {
|
|
86
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
87
|
-
const response = await fetch(url, {
|
|
88
|
-
method: 'POST',
|
|
89
|
-
headers: {
|
|
90
|
-
'Content-Type': 'application/json',
|
|
91
|
-
},
|
|
92
|
-
body: JSON.stringify(payload),
|
|
16
|
+
: { role: 'user', content: prompt };
|
|
17
|
+
const response = await this.makeRequest('/v1/chat/completions', {
|
|
18
|
+
model: this.model,
|
|
19
|
+
messages: [
|
|
20
|
+
{ role: 'system', content: AI_SYSTEM_PROMPT },
|
|
21
|
+
userMessage
|
|
22
|
+
],
|
|
23
|
+
temperature: 0.3,
|
|
24
|
+
max_tokens: 100,
|
|
25
|
+
stream: false
|
|
93
26
|
});
|
|
94
|
-
|
|
95
|
-
const errorText = await response.text();
|
|
96
|
-
if (response.status === 401 || response.status === 403) {
|
|
97
|
-
throw new AuthError(`LMStudio authentication failed`, { details: { status: response.status, body: errorText } });
|
|
98
|
-
}
|
|
99
|
-
if (response.status === 429) {
|
|
100
|
-
throw new RateLimitError(`LMStudio rate limit exceeded`, { details: { status: response.status } });
|
|
101
|
-
}
|
|
102
|
-
throw new NetworkError(`LMStudio API request failed: ${response.status} ${response.statusText}`, { details: { status: response.status, body: errorText } });
|
|
103
|
-
}
|
|
104
|
-
const data = await response.json();
|
|
105
|
-
return data;
|
|
27
|
+
return response.choices?.[0]?.message?.content;
|
|
106
28
|
}
|
|
107
29
|
// Method to check if LMStudio service is available
|
|
108
30
|
async isAvailable() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lmstudio-service.js","sourceRoot":"","sources":["../../src/services/lmstudio-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"lmstudio-service.js","sourceRoot":"","sources":["../../src/services/lmstudio-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAqC3D,MAAM,OAAO,eAAgB,SAAQ,gBAA+D;IAClG,YACE,OAAO,GAAG,uBAAuB,EACjC,KAAK,GAAG,aAAa;QAErB,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IAES,KAAK,CAAC,iBAAiB,CAAC,MAAc,EAAE,SAAkB;QAClE,MAAM,WAAW,GAAkB,SAAS;YAC1C,CAAC,CAAC;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;oBAC9B,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;iBACrD;aACF;YACH,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAEtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,sBAAsB,EAAE;YAC9D,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE;gBAC7C,WAAW;aACZ;YACD,WAAW,EAAE,GAAG;YAChB,UAAU,EAAE,GAAG;YACf,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC;IACjD,CAAC;IAED,mDAAmD;IACnD,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,YAAY,CAAC,CAAC;YAC1D,OAAO,QAAQ,CAAC,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,kCAAkC;IAClC,KAAK,CAAC,UAAU;QACd,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,YAAY,CAAC,CAAC;YAC1D,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,OAAO,EAAE,CAAC;YAE5B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,KAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,14 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { BaseLocalService } from './base-local-service.js';
|
|
2
|
+
interface OllamaResponse {
|
|
3
|
+
model: string;
|
|
4
|
+
response?: string;
|
|
5
|
+
message?: {
|
|
6
|
+
content: string;
|
|
7
|
+
role: string;
|
|
8
|
+
};
|
|
9
|
+
done: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface OllamaChatMessage {
|
|
12
|
+
role: 'system' | 'user' | 'assistant';
|
|
13
|
+
content: string;
|
|
14
|
+
images?: string[];
|
|
15
|
+
}
|
|
16
|
+
interface OllamaChatRequest {
|
|
17
|
+
model: string;
|
|
18
|
+
messages: OllamaChatMessage[];
|
|
19
|
+
stream: boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare class OllamaService extends BaseLocalService<OllamaChatRequest, OllamaResponse> {
|
|
6
22
|
constructor(baseUrl?: string, model?: string);
|
|
7
|
-
|
|
8
|
-
generateFileName(content: string, originalName: string, namingConvention?: string, category?: string, fileInfo?: FileInfo, language?: string, context?: string, imageData?: string): Promise<AINameResult>;
|
|
9
|
-
private sanitizeFilename;
|
|
10
|
-
private makeRequest;
|
|
23
|
+
protected requestCompletion(prompt: string, imageData?: string): Promise<string | undefined>;
|
|
11
24
|
isAvailable(): Promise<boolean>;
|
|
12
25
|
listModels(): Promise<string[]>;
|
|
13
26
|
}
|
|
27
|
+
export {};
|
|
14
28
|
//# sourceMappingURL=ollama-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ollama-service.d.ts","sourceRoot":"","sources":["../../src/services/ollama-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ollama-service.d.ts","sourceRoot":"","sources":["../../src/services/ollama-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,IAAI,EAAE,OAAO,CAAC;CACf;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,UAAU,iBAAiB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;CACjB;AAMD,qBAAa,aAAc,SAAQ,gBAAgB,CAAC,iBAAiB,EAAE,cAAc,CAAC;gBACxE,OAAO,SAA2B,EAAE,KAAK,SAAa;cAIlD,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAiB5F,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAS/B,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAUtC"}
|
|
@@ -1,93 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export class OllamaService {
|
|
4
|
-
name = 'Ollama';
|
|
5
|
-
baseUrl;
|
|
6
|
-
model;
|
|
1
|
+
import { AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
|
|
2
|
+
import { BaseLocalService } from './base-local-service.js';
|
|
3
|
+
export class OllamaService extends BaseLocalService {
|
|
7
4
|
constructor(baseUrl = 'http://localhost:11434', model = 'llama3.1') {
|
|
8
|
-
|
|
9
|
-
this.model = model;
|
|
5
|
+
super('Ollama', baseUrl, model);
|
|
10
6
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
throw new ConfigError(`Ollama base URL must point to localhost (got: ${hostname})`);
|
|
23
|
-
}
|
|
24
|
-
return url;
|
|
25
|
-
}
|
|
26
|
-
async generateFileName(content, originalName, namingConvention = 'kebab-case', category = 'general', fileInfo, language, context, imageData) {
|
|
27
|
-
try {
|
|
28
|
-
if (imageData && (!imageData.startsWith('data:image/') || !imageData.includes(','))) {
|
|
29
|
-
throw new NetworkError('Invalid image data format');
|
|
30
|
-
}
|
|
31
|
-
const prompt = buildFileNamePrompt({
|
|
32
|
-
content: imageData
|
|
33
|
-
? 'Analyze this image and generate an appropriate filename based on what you see.'
|
|
34
|
-
: content,
|
|
35
|
-
originalName,
|
|
36
|
-
namingConvention: namingConvention,
|
|
37
|
-
category: category,
|
|
38
|
-
fileInfo,
|
|
39
|
-
language,
|
|
40
|
-
context
|
|
41
|
-
});
|
|
42
|
-
const userMessage = imageData
|
|
43
|
-
? { role: 'user', content: prompt, images: [imageData.split(',')[1]] }
|
|
44
|
-
: { role: 'user', content: prompt };
|
|
45
|
-
const response = await this.makeRequest('/api/chat', {
|
|
46
|
-
model: this.model,
|
|
47
|
-
messages: [
|
|
48
|
-
{ role: 'system', content: AI_SYSTEM_PROMPT },
|
|
49
|
-
userMessage
|
|
50
|
-
],
|
|
51
|
-
stream: false
|
|
52
|
-
});
|
|
53
|
-
if (response.message?.content) {
|
|
54
|
-
return { name: this.sanitizeFilename(response.message.content), inputTokens: undefined, outputTokens: undefined };
|
|
55
|
-
}
|
|
56
|
-
throw new NetworkError('No response content from Ollama');
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
if (error instanceof AuthError || error instanceof NetworkError || error instanceof RateLimitError || error instanceof ConfigError)
|
|
60
|
-
throw error;
|
|
61
|
-
throw new NetworkError(`Ollama service failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error });
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
sanitizeFilename(filename) {
|
|
65
|
-
return filename
|
|
66
|
-
.trim()
|
|
67
|
-
.replace(/^["']|["']$/g, '')
|
|
68
|
-
.replace(/\.(txt|pdf|docx?|xlsx?|md|rtf)$/i, '')
|
|
69
|
-
.replace(/[<>:"/\\|?*]/g, '-')
|
|
70
|
-
.replace(/\s+/g, '-')
|
|
71
|
-
.toLowerCase();
|
|
72
|
-
}
|
|
73
|
-
async makeRequest(endpoint, payload) {
|
|
74
|
-
const url = `${this.baseUrl}${endpoint}`;
|
|
75
|
-
const response = await fetch(url, {
|
|
76
|
-
method: 'POST',
|
|
77
|
-
headers: { 'Content-Type': 'application/json' },
|
|
78
|
-
body: JSON.stringify(payload)
|
|
7
|
+
async requestCompletion(prompt, imageData) {
|
|
8
|
+
const userMessage = imageData
|
|
9
|
+
? { role: 'user', content: prompt, images: [imageData.split(',')[1]] }
|
|
10
|
+
: { role: 'user', content: prompt };
|
|
11
|
+
const response = await this.makeRequest('/api/chat', {
|
|
12
|
+
model: this.model,
|
|
13
|
+
messages: [
|
|
14
|
+
{ role: 'system', content: AI_SYSTEM_PROMPT },
|
|
15
|
+
userMessage
|
|
16
|
+
],
|
|
17
|
+
stream: false
|
|
79
18
|
});
|
|
80
|
-
|
|
81
|
-
const errorText = await response.text();
|
|
82
|
-
if (response.status === 401 || response.status === 403) {
|
|
83
|
-
throw new AuthError(`Ollama authentication failed`, { details: { status: response.status, body: errorText } });
|
|
84
|
-
}
|
|
85
|
-
if (response.status === 429) {
|
|
86
|
-
throw new RateLimitError(`Ollama rate limit exceeded`, { details: { status: response.status } });
|
|
87
|
-
}
|
|
88
|
-
throw new NetworkError(`Ollama API request failed: ${response.status} ${response.statusText}`, { details: { status: response.status, body: errorText } });
|
|
89
|
-
}
|
|
90
|
-
return response.json();
|
|
19
|
+
return response.message?.content;
|
|
91
20
|
}
|
|
92
21
|
async isAvailable() {
|
|
93
22
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ollama-service.js","sourceRoot":"","sources":["../../src/services/ollama-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ollama-service.js","sourceRoot":"","sources":["../../src/services/ollama-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAyB3D,MAAM,OAAO,aAAc,SAAQ,gBAAmD;IACpF,YAAY,OAAO,GAAG,wBAAwB,EAAE,KAAK,GAAG,UAAU;QAChE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAES,KAAK,CAAC,iBAAiB,CAAC,MAAc,EAAE,SAAkB;QAClE,MAAM,WAAW,GAAsB,SAAS;YAC9C,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;YACtE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAEtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE;YACnD,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE;gBAC7C,WAAW;aACZ;YACD,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;YACzD,OAAO,QAAQ,CAAC,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;YACzD,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import OpenAI from 'openai';
|
|
2
|
+
import { BaseCloudService, CloudApiError, TokenUsage } from './base-cloud-service.js';
|
|
3
|
+
export declare class OpenAIService extends BaseCloudService<OpenAI.Chat.Completions.ChatCompletion> {
|
|
4
4
|
private client;
|
|
5
|
-
private model;
|
|
6
5
|
constructor(apiKey: string, model?: string);
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
protected createTextCompletion(prompt: string): Promise<OpenAI.Chat.Completions.ChatCompletion>;
|
|
7
|
+
protected createImageCompletion(prompt: string, imageData: string): Promise<OpenAI.Chat.Completions.ChatCompletion>;
|
|
8
|
+
protected extractSuggestedName(response: OpenAI.Chat.Completions.ChatCompletion): string;
|
|
9
|
+
protected extractTokenUsage(response: OpenAI.Chat.Completions.ChatCompletion): TokenUsage;
|
|
10
|
+
protected asApiError(error: unknown): CloudApiError | undefined;
|
|
9
11
|
}
|
|
10
12
|
//# sourceMappingURL=openai-service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-service.d.ts","sourceRoot":"","sources":["../../src/services/openai-service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"openai-service.d.ts","sourceRoot":"","sources":["../../src/services/openai-service.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEtF,qBAAa,aAAc,SAAQ,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC;IACzF,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAK1C,SAAS,CAAC,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC;IAS/F,SAAS,CAAC,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC;IAiBnH,SAAS,CAAC,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,GAAG,MAAM;IAIxF,SAAS,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,GAAG,UAAU;IAOzF,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS;CAGhE"}
|
|
@@ -1,104 +1,46 @@
|
|
|
1
1
|
import OpenAI from 'openai';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { buildFileNamePrompt } from '../utils/ai-prompts.js';
|
|
5
|
-
export class OpenAIService {
|
|
6
|
-
name = 'OpenAI';
|
|
2
|
+
import { BaseCloudService } from './base-cloud-service.js';
|
|
3
|
+
export class OpenAIService extends BaseCloudService {
|
|
7
4
|
client;
|
|
8
|
-
model;
|
|
9
5
|
constructor(apiKey, model) {
|
|
6
|
+
super('OpenAI', model ?? 'gpt-5.5');
|
|
10
7
|
this.client = new OpenAI({ apiKey });
|
|
11
|
-
this.model = model ?? 'gpt-4o';
|
|
12
8
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
response = await this.client.chat.completions.create({
|
|
32
|
-
model: this.model,
|
|
33
|
-
messages: [
|
|
34
|
-
{
|
|
35
|
-
role: 'user',
|
|
36
|
-
content: [
|
|
37
|
-
{ type: 'text', text: prompt },
|
|
38
|
-
{ type: 'image_url', image_url: { url: imageData } }
|
|
39
|
-
]
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
|
-
max_tokens: 100,
|
|
43
|
-
temperature: 0.3
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
const prompt = buildFileNamePrompt({
|
|
48
|
-
content,
|
|
49
|
-
originalName,
|
|
50
|
-
namingConvention: convention,
|
|
51
|
-
category: fileCategory,
|
|
52
|
-
fileInfo,
|
|
53
|
-
language,
|
|
54
|
-
context
|
|
55
|
-
});
|
|
56
|
-
response = await this.client.chat.completions.create({
|
|
57
|
-
model: this.model,
|
|
58
|
-
messages: [{ role: 'user', content: prompt }],
|
|
59
|
-
max_tokens: 100,
|
|
60
|
-
temperature: 0.3
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
const suggestedName = response.choices[0]?.message?.content?.trim() || 'untitled-document';
|
|
64
|
-
return {
|
|
65
|
-
name: this.sanitizeFileName(suggestedName, convention),
|
|
66
|
-
inputTokens: response.usage?.prompt_tokens,
|
|
67
|
-
outputTokens: response.usage?.completion_tokens
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
catch (error) {
|
|
71
|
-
if (error instanceof AuthError || error instanceof NetworkError || error instanceof RateLimitError)
|
|
72
|
-
throw error;
|
|
73
|
-
if (error instanceof OpenAI.APIError) {
|
|
74
|
-
if (error.status === 401 || error.status === 403) {
|
|
75
|
-
throw new AuthError(`OpenAI authentication failed: ${error.message}`, { details: { status: error.status } });
|
|
76
|
-
}
|
|
77
|
-
if (error.status === 429) {
|
|
78
|
-
throw new RateLimitError(`OpenAI rate limit exceeded: ${error.message}`, { details: { status: error.status } });
|
|
9
|
+
createTextCompletion(prompt) {
|
|
10
|
+
return this.client.chat.completions.create({
|
|
11
|
+
model: this.model,
|
|
12
|
+
messages: [{ role: 'user', content: prompt }],
|
|
13
|
+
max_tokens: 100,
|
|
14
|
+
temperature: 0.3
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
createImageCompletion(prompt, imageData) {
|
|
18
|
+
return this.client.chat.completions.create({
|
|
19
|
+
model: this.model,
|
|
20
|
+
messages: [
|
|
21
|
+
{
|
|
22
|
+
role: 'user',
|
|
23
|
+
content: [
|
|
24
|
+
{ type: 'text', text: prompt },
|
|
25
|
+
{ type: 'image_url', image_url: { url: imageData } }
|
|
26
|
+
]
|
|
79
27
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
28
|
+
],
|
|
29
|
+
max_tokens: 100,
|
|
30
|
+
temperature: 0.3
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
extractSuggestedName(response) {
|
|
34
|
+
return response.choices[0]?.message?.content?.trim() || 'untitled-document';
|
|
35
|
+
}
|
|
36
|
+
extractTokenUsage(response) {
|
|
37
|
+
return {
|
|
38
|
+
inputTokens: response.usage?.prompt_tokens,
|
|
39
|
+
outputTokens: response.usage?.completion_tokens
|
|
40
|
+
};
|
|
84
41
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const safeForWindows = stripWindowsIllegalChars(nameWithoutExt);
|
|
88
|
-
let cleaned = applyNamingConvention(safeForWindows, convention);
|
|
89
|
-
if (!cleaned) {
|
|
90
|
-
cleaned = applyNamingConvention('untitled document', convention);
|
|
91
|
-
}
|
|
92
|
-
else if (cleaned.length > 100) {
|
|
93
|
-
cleaned = cleaned.substring(0, 100);
|
|
94
|
-
if (convention === 'kebab-case') {
|
|
95
|
-
cleaned = cleaned.replace(/-[^-]*$/, '');
|
|
96
|
-
}
|
|
97
|
-
else if (convention === 'snake_case') {
|
|
98
|
-
cleaned = cleaned.replace(/_[^_]*$/, '');
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return cleaned;
|
|
42
|
+
asApiError(error) {
|
|
43
|
+
return error instanceof OpenAI.APIError ? error : undefined;
|
|
102
44
|
}
|
|
103
45
|
}
|
|
104
46
|
//# sourceMappingURL=openai-service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-service.js","sourceRoot":"","sources":["../../src/services/openai-service.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"openai-service.js","sourceRoot":"","sources":["../../src/services/openai-service.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAA6B,MAAM,yBAAyB,CAAC;AAEtF,MAAM,OAAO,aAAc,SAAQ,gBAAwD;IACjF,MAAM,CAAS;IAEvB,YAAY,MAAc,EAAE,KAAc;QACxC,KAAK,CAAC,QAAQ,EAAE,KAAK,IAAI,SAAS,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;IAES,oBAAoB,CAAC,MAAc;QAC3C,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YACzC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAC7C,UAAU,EAAE,GAAG;YACf,WAAW,EAAE,GAAG;SACjB,CAAC,CAAC;IACL,CAAC;IAES,qBAAqB,CAAC,MAAc,EAAE,SAAiB;QAC/D,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YACzC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;wBAC9B,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;qBACrD;iBACF;aACF;YACD,UAAU,EAAE,GAAG;YACf,WAAW,EAAE,GAAG;SACjB,CAAC,CAAC;IACL,CAAC;IAES,oBAAoB,CAAC,QAAgD;QAC7E,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,mBAAmB,CAAC;IAC9E,CAAC;IAES,iBAAiB,CAAC,QAAgD;QAC1E,OAAO;YACL,WAAW,EAAE,QAAQ,CAAC,KAAK,EAAE,aAAa;YAC1C,YAAY,EAAE,QAAQ,CAAC,KAAK,EAAE,iBAAiB;SAChD,CAAC;IACJ,CAAC;IAES,UAAU,CAAC,KAAc;QACjC,OAAO,KAAK,YAAY,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,CAAC;CACF"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { NamingConvention } from './naming-conventions.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sanitizes AI-suggested filenames from cloud providers (Claude, OpenAI):
|
|
4
|
+
* strips the extension and Windows-illegal characters, applies the naming
|
|
5
|
+
* convention, and truncates overly long names at a word boundary.
|
|
6
|
+
*/
|
|
7
|
+
export declare function sanitizeCloudFileName(name: string, convention: NamingConvention): string;
|
|
8
|
+
/**
|
|
9
|
+
* Sanitizes AI-suggested filenames from local providers (Ollama, LMStudio):
|
|
10
|
+
* strips surrounding quotes and known extensions, replaces invalid characters
|
|
11
|
+
* and whitespace with hyphens, and lowercases the result.
|
|
12
|
+
*/
|
|
13
|
+
export declare function sanitizeLocalFileName(filename: string): string;
|
|
14
|
+
//# sourceMappingURL=ai-name-sanitizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-name-sanitizer.d.ts","sourceRoot":"","sources":["../../src/utils/ai-name-sanitizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAmB5G;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAkBxF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ9D"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { applyNamingConvention, stripWindowsIllegalChars } from './naming-conventions.js';
|
|
2
|
+
import { ParseError } from '../errors.js';
|
|
3
|
+
const MAX_NAME_WORDS = 12;
|
|
4
|
+
const PROSE_PREFIX = /^(based on|i can|i cannot|i am|i'm|this is|this appears|the document|the image|unable to|sorry|it appears|here is|here's)\b/i;
|
|
5
|
+
/**
|
|
6
|
+
* Reject AI output that is an explanation rather than a filename ("Based on
|
|
7
|
+
* the document content, I can only identify…"). Truncating prose to 100 chars
|
|
8
|
+
* produces garbage names — failing the file is the honest outcome.
|
|
9
|
+
*/
|
|
10
|
+
function assertLooksLikeFileName(raw) {
|
|
11
|
+
const asWords = raw.replace(/[-_]+/g, ' ').trim();
|
|
12
|
+
const wordCount = asWords.split(/\s+/).filter(Boolean).length;
|
|
13
|
+
if (wordCount > MAX_NAME_WORDS || PROSE_PREFIX.test(asWords)) {
|
|
14
|
+
throw new ParseError(`AI returned an explanation instead of a filename: "${raw.slice(0, 80)}…"`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Sanitizes AI-suggested filenames from cloud providers (Claude, OpenAI):
|
|
19
|
+
* strips the extension and Windows-illegal characters, applies the naming
|
|
20
|
+
* convention, and truncates overly long names at a word boundary.
|
|
21
|
+
*/
|
|
22
|
+
export function sanitizeCloudFileName(name, convention) {
|
|
23
|
+
const nameWithoutExt = name.replace(/\.[^/.]+$/, '');
|
|
24
|
+
assertLooksLikeFileName(nameWithoutExt);
|
|
25
|
+
const safeForWindows = stripWindowsIllegalChars(nameWithoutExt);
|
|
26
|
+
let cleaned = applyNamingConvention(safeForWindows, convention);
|
|
27
|
+
if (!cleaned) {
|
|
28
|
+
cleaned = applyNamingConvention('untitled document', convention);
|
|
29
|
+
}
|
|
30
|
+
else if (cleaned.length > 100) {
|
|
31
|
+
cleaned = cleaned.substring(0, 100);
|
|
32
|
+
if (convention === 'kebab-case') {
|
|
33
|
+
cleaned = cleaned.replace(/-[^-]*$/, '');
|
|
34
|
+
}
|
|
35
|
+
else if (convention === 'snake_case') {
|
|
36
|
+
cleaned = cleaned.replace(/_[^_]*$/, '');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return cleaned;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Sanitizes AI-suggested filenames from local providers (Ollama, LMStudio):
|
|
43
|
+
* strips surrounding quotes and known extensions, replaces invalid characters
|
|
44
|
+
* and whitespace with hyphens, and lowercases the result.
|
|
45
|
+
*/
|
|
46
|
+
export function sanitizeLocalFileName(filename) {
|
|
47
|
+
const unquoted = filename.trim().replace(/^["']|["']$/g, '');
|
|
48
|
+
assertLooksLikeFileName(unquoted);
|
|
49
|
+
return unquoted
|
|
50
|
+
.replace(/\.(txt|pdf|docx?|xlsx?|md|rtf)$/i, '') // Remove extensions
|
|
51
|
+
.replace(/[<>:"/\\|?*]/g, '-') // Replace invalid characters
|
|
52
|
+
.replace(/\s+/g, '-') // Replace spaces with hyphens
|
|
53
|
+
.toLowerCase();
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=ai-name-sanitizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-name-sanitizer.js","sourceRoot":"","sources":["../../src/utils/ai-name-sanitizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAoB,MAAM,yBAAyB,CAAC;AAC5G,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,YAAY,GAAG,8HAA8H,CAAC;AAEpJ;;;;GAIG;AACH,SAAS,uBAAuB,CAAC,GAAW;IAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAC9D,IAAI,SAAS,GAAG,cAAc,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,UAAU,CAAC,sDAAsD,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IACnG,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,UAA4B;IAC9E,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACrD,uBAAuB,CAAC,cAAc,CAAC,CAAC;IACxC,MAAM,cAAc,GAAG,wBAAwB,CAAC,cAAc,CAAC,CAAC;IAChE,IAAI,OAAO,GAAG,qBAAqB,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAEhE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,qBAAqB,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC;IACnE,CAAC;SAAM,IAAI,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAChC,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACpC,IAAI,UAAU,KAAK,YAAY,EAAE,CAAC;YAChC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,UAAU,KAAK,YAAY,EAAE,CAAC;YACvC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAC7D,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAClC,OAAO,QAAQ;SACZ,OAAO,CAAC,kCAAkC,EAAE,EAAE,CAAC,CAAC,oBAAoB;SACpE,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,6BAA6B;SAC3D,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,8BAA8B;SACnD,WAAW,EAAE,CAAC;AACnB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-prompts.d.ts","sourceRoot":"","sources":["../../src/utils/ai-prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAyB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAA2B,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"ai-prompts.d.ts","sourceRoot":"","sources":["../../src/utils/ai-prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAyB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAA2B,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CA4DlE;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,0KAA0K,CAAC"}
|
package/dist/utils/ai-prompts.js
CHANGED
|
@@ -57,7 +57,8 @@ Document Properties:`;
|
|
|
57
57
|
- Ignore irrelevant folder names that don't describe the document content
|
|
58
58
|
- Only use letters, numbers, and appropriate separators for the naming convention
|
|
59
59
|
- Focus on the document's actual content and purpose, not just metadata
|
|
60
|
-
-
|
|
60
|
+
- Prefer distinctive identifying details from the content (issuer/company/organization, document type, reference or invoice number, billing period) over generic terms — avoid vague names like "scanned-document", "bill-invoice", or "document-scan" unless the content truly offers nothing specific
|
|
61
|
+
- Current filename stem (without extension): "${currentStem}" — if the name you would generate is very similar or equivalent to this (same words, minor formatting differences), return this exact stem unchanged. Never do this when the stem is a meaningless camera/scanner/export name (e.g. IMG_1234, scan-001, DSC_0042); in that case describe whatever is identifiable in the content instead
|
|
61
62
|
|
|
62
63
|
${metadataContext}
|
|
63
64
|
${contextSection}
|