@pentoshi/clai 3.9.7 → 3.11.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/LICENSE +21 -0
- package/README.md +81 -9
- package/dist/agent/context-manager.d.ts +2 -1
- package/dist/agent/context-manager.js +16 -2
- package/dist/agent/context-manager.js.map +1 -1
- package/dist/agent/loop-guard.d.ts +18 -0
- package/dist/agent/loop-guard.js +50 -2
- package/dist/agent/loop-guard.js.map +1 -1
- package/dist/agent/must-continue.js +4 -8
- package/dist/agent/must-continue.js.map +1 -1
- package/dist/agent/prompt-composer.js +8 -8
- package/dist/agent/prompt-composer.js.map +1 -1
- package/dist/agent/runner.d.ts +1 -0
- package/dist/agent/runner.js +238 -74
- package/dist/agent/runner.js.map +1 -1
- package/dist/agent/session-policy.d.ts +1 -2
- package/dist/agent/session-policy.js +3 -2
- package/dist/agent/session-policy.js.map +1 -1
- package/dist/agent/tool-call-parser.js +6 -6
- package/dist/agent/tool-call-parser.js.map +1 -1
- package/dist/app/adapters/current-agent-adapter.js +1 -0
- package/dist/app/adapters/current-agent-adapter.js.map +1 -1
- package/dist/app/controllers/session-controller.js +2 -0
- package/dist/app/controllers/session-controller.js.map +1 -1
- package/dist/app/controllers/session-turn-request.d.ts +1 -0
- package/dist/app/controllers/session-turn-request.js +4 -1
- package/dist/app/controllers/session-turn-request.js.map +1 -1
- package/dist/app/ports/agent-port.d.ts +1 -0
- package/dist/attachments/clipboard-image.js +5 -4
- package/dist/attachments/clipboard-image.js.map +1 -1
- package/dist/attachments/image-content.d.ts +20 -2
- package/dist/attachments/image-content.js +201 -1
- package/dist/attachments/image-content.js.map +1 -1
- package/dist/attachments/image-prepare.d.ts +25 -0
- package/dist/attachments/image-prepare.js +390 -0
- package/dist/attachments/image-prepare.js.map +1 -0
- package/dist/attachments/service.d.ts +4 -2
- package/dist/attachments/service.js +51 -21
- package/dist/attachments/service.js.map +1 -1
- package/dist/commands/providers.d.ts +5 -2
- package/dist/commands/providers.js +121 -7
- package/dist/commands/providers.js.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/llm/agentrouter.js +2 -2
- package/dist/llm/agentrouter.js.map +1 -1
- package/dist/llm/anthropic.d.ts +5 -4
- package/dist/llm/anthropic.js +28 -25
- package/dist/llm/anthropic.js.map +1 -1
- package/dist/llm/aws-mantle.js +19 -20
- package/dist/llm/aws-mantle.js.map +1 -1
- package/dist/llm/bynara.js +2 -2
- package/dist/llm/bynara.js.map +1 -1
- package/dist/llm/capabilities.d.ts +19 -0
- package/dist/llm/capabilities.js +273 -16
- package/dist/llm/capabilities.js.map +1 -1
- package/dist/llm/gemini.js +16 -8
- package/dist/llm/gemini.js.map +1 -1
- package/dist/llm/groq.js +2 -2
- package/dist/llm/groq.js.map +1 -1
- package/dist/llm/http.d.ts +7 -1
- package/dist/llm/http.js +210 -8
- package/dist/llm/http.js.map +1 -1
- package/dist/llm/kimchi.js +2 -2
- package/dist/llm/kimchi.js.map +1 -1
- package/dist/llm/lightning.d.ts +11 -0
- package/dist/llm/lightning.js +108 -0
- package/dist/llm/lightning.js.map +1 -0
- package/dist/llm/modal.d.ts +16 -0
- package/dist/llm/modal.js +182 -0
- package/dist/llm/modal.js.map +1 -0
- package/dist/llm/nvidia.js +2 -2
- package/dist/llm/nvidia.js.map +1 -1
- package/dist/llm/ollama.js +3 -3
- package/dist/llm/ollama.js.map +1 -1
- package/dist/llm/openai.js +2 -2
- package/dist/llm/openai.js.map +1 -1
- package/dist/llm/openrouter.js +2 -2
- package/dist/llm/openrouter.js.map +1 -1
- package/dist/llm/provider.d.ts +11 -0
- package/dist/llm/provider.js +316 -1
- package/dist/llm/provider.js.map +1 -1
- package/dist/llm/qwen-cloud.js +2 -5
- package/dist/llm/qwen-cloud.js.map +1 -1
- package/dist/llm/router.js +127 -7
- package/dist/llm/router.js.map +1 -1
- package/dist/llm/system-messages.d.ts +5 -0
- package/dist/llm/system-messages.js +14 -0
- package/dist/llm/system-messages.js.map +1 -1
- package/dist/llm/token-usage.d.ts +13 -0
- package/dist/llm/token-usage.js +31 -0
- package/dist/llm/token-usage.js.map +1 -1
- package/dist/llm/tokenrouter.d.ts +15 -0
- package/dist/llm/tokenrouter.js +106 -0
- package/dist/llm/tokenrouter.js.map +1 -0
- package/dist/modes/agent.d.ts +1 -0
- package/dist/modes/agent.js.map +1 -1
- package/dist/modes/ask.js +15 -4
- package/dist/modes/ask.js.map +1 -1
- package/dist/prompts/embedded.js +2 -2
- package/dist/prompts/embedded.js.map +1 -1
- package/dist/prompts/index.d.ts +6 -0
- package/dist/prompts/index.js +39 -21
- package/dist/prompts/index.js.map +1 -1
- package/dist/prompts/system.agent.md +15 -27
- package/dist/prompts/system.ask.md +1 -1
- package/dist/repl/slash-commands.js +53 -0
- package/dist/repl/slash-commands.js.map +1 -1
- package/dist/repl.js +16 -9
- package/dist/repl.js.map +1 -1
- package/dist/store/config.d.ts +55 -0
- package/dist/store/config.js +127 -0
- package/dist/store/config.js.map +1 -1
- package/dist/store/history.d.ts +2 -1
- package/dist/store/history.js +15 -16
- package/dist/store/history.js.map +1 -1
- package/dist/store/keys.js +41 -2
- package/dist/store/keys.js.map +1 -1
- package/dist/tools/definitions.js +31 -6
- package/dist/tools/definitions.js.map +1 -1
- package/dist/tools/image.d.ts +0 -7
- package/dist/tools/image.js +76 -22
- package/dist/tools/image.js.map +1 -1
- package/dist/tools/jobs.d.ts +11 -1
- package/dist/tools/jobs.js +67 -0
- package/dist/tools/jobs.js.map +1 -1
- package/dist/tools/ocr.d.ts +28 -0
- package/dist/tools/ocr.js +316 -0
- package/dist/tools/ocr.js.map +1 -0
- package/dist/tools/pdf.d.ts +0 -12
- package/dist/tools/pdf.js +436 -150
- package/dist/tools/pdf.js.map +1 -1
- package/dist/tools/registry.js +5 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/tui/format-keys.js +10 -0
- package/dist/tui/format-keys.js.map +1 -1
- package/dist/tui-v2/actions/action-id.d.ts +1 -1
- package/dist/tui-v2/actions/action-id.js +3 -0
- package/dist/tui-v2/actions/action-id.js.map +1 -1
- package/dist/tui-v2/actions/format-shortcuts.js +4 -1
- package/dist/tui-v2/actions/format-shortcuts.js.map +1 -1
- package/dist/tui-v2/actions/keymap.js +6 -1
- package/dist/tui-v2/actions/keymap.js.map +1 -1
- package/dist/tui-v2/app/App.js +3 -1
- package/dist/tui-v2/app/App.js.map +1 -1
- package/dist/tui-v2/app/commands/key-commands.js +117 -15
- package/dist/tui-v2/app/commands/key-commands.js.map +1 -1
- package/dist/tui-v2/app/commands/picker-commands.js +80 -11
- package/dist/tui-v2/app/commands/picker-commands.js.map +1 -1
- package/dist/tui-v2/bootstrap/console-guard.d.ts +20 -0
- package/dist/tui-v2/bootstrap/console-guard.js +73 -0
- package/dist/tui-v2/bootstrap/console-guard.js.map +1 -0
- package/dist/tui-v2/bootstrap/start-tui-v2.js +14 -0
- package/dist/tui-v2/bootstrap/start-tui-v2.js.map +1 -1
- package/dist/tui-v2/components/composer/composer-input-box.js +5 -1
- package/dist/tui-v2/components/composer/composer-input-box.js.map +1 -1
- package/dist/tui-v2/components/jobs/jobs-panel.js +25 -1
- package/dist/tui-v2/components/jobs/jobs-panel.js.map +1 -1
- package/dist/tui-v2/components/modal/keys-modal.js +15 -8
- package/dist/tui-v2/components/modal/keys-modal.js.map +1 -1
- package/dist/tui-v2/components/modal/secret-modal.js +13 -4
- package/dist/tui-v2/components/modal/secret-modal.js.map +1 -1
- package/dist/tui-v2/components/pager/pager.js +103 -12
- package/dist/tui-v2/components/pager/pager.js.map +1 -1
- package/dist/tui-v2/components/status/status-line.d.ts +8 -2
- package/dist/tui-v2/components/status/status-line.js +12 -8
- package/dist/tui-v2/components/status/status-line.js.map +1 -1
- package/dist/tui-v2/components/transcript/thinking-block.d.ts +14 -4
- package/dist/tui-v2/components/transcript/thinking-block.js +53 -8
- package/dist/tui-v2/components/transcript/thinking-block.js.map +1 -1
- package/dist/tui-v2/components/transcript/transcript-row.d.ts +2 -0
- package/dist/tui-v2/components/transcript/transcript-row.js +2 -2
- package/dist/tui-v2/components/transcript/transcript-row.js.map +1 -1
- package/dist/tui-v2/components/transcript/transcript-view.js +8 -1
- package/dist/tui-v2/components/transcript/transcript-view.js.map +1 -1
- package/dist/tui-v2/composer/composer-action-port.d.ts +15 -3
- package/dist/tui-v2/composer/composer-action-port.js +49 -6
- package/dist/tui-v2/composer/composer-action-port.js.map +1 -1
- package/dist/tui-v2/composer/composer-editor.js +37 -44
- package/dist/tui-v2/composer/composer-editor.js.map +1 -1
- package/dist/tui-v2/composer/draft-actions.d.ts +31 -0
- package/dist/tui-v2/composer/draft-actions.js +45 -0
- package/dist/tui-v2/composer/draft-actions.js.map +1 -0
- package/dist/tui-v2/composer/textarea-keybindings.d.ts +1 -1
- package/dist/tui-v2/composer/textarea-keybindings.js +33 -0
- package/dist/tui-v2/composer/textarea-keybindings.js.map +1 -1
- package/dist/tui-v2/composer/use-draft-actions.d.ts +32 -0
- package/dist/tui-v2/composer/use-draft-actions.js +59 -0
- package/dist/tui-v2/composer/use-draft-actions.js.map +1 -0
- package/dist/tui-v2/controllers/overlay-controller.d.ts +13 -0
- package/dist/tui-v2/controllers/overlay-controller.js +5 -2
- package/dist/tui-v2/controllers/overlay-controller.js.map +1 -1
- package/dist/tui-v2/rendering/artifact-pager-source.d.ts +10 -0
- package/dist/tui-v2/rendering/artifact-pager-source.js +6 -0
- package/dist/tui-v2/rendering/artifact-pager-source.js.map +1 -1
- package/dist/tui-v2/rendering/job-tail-source.d.ts +21 -0
- package/dist/tui-v2/rendering/job-tail-source.js +87 -0
- package/dist/tui-v2/rendering/job-tail-source.js.map +1 -0
- package/dist/tui-v2/state/use-has-draft.d.ts +6 -0
- package/dist/tui-v2/state/use-has-draft.js +13 -0
- package/dist/tui-v2/state/use-has-draft.js.map +1 -0
- package/dist/types.d.ts +7 -1
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -1
- package/dist/ui/mentions.d.ts +7 -2
- package/dist/ui/mentions.js +52 -117
- package/dist/ui/mentions.js.map +1 -1
- package/dist/version.generated.d.ts +2 -2
- package/dist/version.generated.js +2 -2
- package/dist/version.generated.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export const MAX_IMAGE_BYTES = 3_750_000;
|
|
2
1
|
const MODEL_IMAGE_MEDIA_TYPES = new Set([
|
|
3
2
|
"image/png",
|
|
4
3
|
"image/jpeg",
|
|
@@ -48,4 +47,205 @@ export function detectModelImageMediaType(bytes) {
|
|
|
48
47
|
}
|
|
49
48
|
return undefined;
|
|
50
49
|
}
|
|
50
|
+
export function detectConvertibleImageFormat(bytes) {
|
|
51
|
+
const native = detectModelImageMediaType(bytes);
|
|
52
|
+
if (native)
|
|
53
|
+
return native;
|
|
54
|
+
if (bytes.length >= 2 && bytes[0] === 0x42 && bytes[1] === 0x4d) {
|
|
55
|
+
return "image/bmp";
|
|
56
|
+
}
|
|
57
|
+
if (bytes.length >= 4) {
|
|
58
|
+
const isLittleEndianTiff = bytes[0] === 0x49 && bytes[1] === 0x49 && bytes[2] === 0x2a && bytes[3] === 0x00;
|
|
59
|
+
const isBigEndianTiff = bytes[0] === 0x4d && bytes[1] === 0x4d && bytes[2] === 0x00 && bytes[3] === 0x2a;
|
|
60
|
+
if (isLittleEndianTiff || isBigEndianTiff)
|
|
61
|
+
return "image/tiff";
|
|
62
|
+
if (bytes[0] === 0x00 &&
|
|
63
|
+
bytes[1] === 0x00 &&
|
|
64
|
+
bytes[2] === 0x01 &&
|
|
65
|
+
bytes[3] === 0x00) {
|
|
66
|
+
return "image/x-icon";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (bytes.length >= 12) {
|
|
70
|
+
const boxType = String.fromCharCode(bytes[4], bytes[5], bytes[6], bytes[7]);
|
|
71
|
+
if (boxType === "ftyp") {
|
|
72
|
+
const brand = String.fromCharCode(bytes[8], bytes[9], bytes[10], bytes[11]).toLowerCase();
|
|
73
|
+
if (brand.startsWith("av"))
|
|
74
|
+
return "image/avif";
|
|
75
|
+
return "image/heic";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return undefined;
|
|
79
|
+
}
|
|
80
|
+
function pngDimensions(bytes) {
|
|
81
|
+
if (bytes.length < 24)
|
|
82
|
+
return undefined;
|
|
83
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
84
|
+
const width = view.getUint32(16, false);
|
|
85
|
+
const height = view.getUint32(20, false);
|
|
86
|
+
return width > 0 && height > 0 ? { width, height } : undefined;
|
|
87
|
+
}
|
|
88
|
+
function gifDimensions(bytes) {
|
|
89
|
+
if (bytes.length < 10)
|
|
90
|
+
return undefined;
|
|
91
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
92
|
+
const width = view.getUint16(6, true);
|
|
93
|
+
const height = view.getUint16(8, true);
|
|
94
|
+
return width > 0 && height > 0 ? { width, height } : undefined;
|
|
95
|
+
}
|
|
96
|
+
function jpegDimensions(bytes) {
|
|
97
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
98
|
+
let offset = 2;
|
|
99
|
+
while (offset + 9 < bytes.length) {
|
|
100
|
+
if (bytes[offset] !== 0xff) {
|
|
101
|
+
offset += 1;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
let marker = bytes[offset + 1];
|
|
105
|
+
while (marker === 0xff && offset + 2 < bytes.length) {
|
|
106
|
+
offset += 1;
|
|
107
|
+
marker = bytes[offset + 1];
|
|
108
|
+
}
|
|
109
|
+
if (marker === 0xd8 || marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {
|
|
110
|
+
offset += 2;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (marker === 0xd9 || marker === 0xda)
|
|
114
|
+
return undefined;
|
|
115
|
+
const length = view.getUint16(offset + 2, false);
|
|
116
|
+
if (length < 2)
|
|
117
|
+
return undefined;
|
|
118
|
+
const isStartOfFrame = marker >= 0xc0 &&
|
|
119
|
+
marker <= 0xcf &&
|
|
120
|
+
marker !== 0xc4 &&
|
|
121
|
+
marker !== 0xc8 &&
|
|
122
|
+
marker !== 0xcc;
|
|
123
|
+
if (isStartOfFrame) {
|
|
124
|
+
if (offset + 9 >= bytes.length)
|
|
125
|
+
return undefined;
|
|
126
|
+
const height = view.getUint16(offset + 5, false);
|
|
127
|
+
const width = view.getUint16(offset + 7, false);
|
|
128
|
+
return width > 0 && height > 0 ? { width, height } : undefined;
|
|
129
|
+
}
|
|
130
|
+
offset += 2 + length;
|
|
131
|
+
}
|
|
132
|
+
return undefined;
|
|
133
|
+
}
|
|
134
|
+
function webpDimensions(bytes) {
|
|
135
|
+
if (bytes.length < 30)
|
|
136
|
+
return undefined;
|
|
137
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
138
|
+
const chunk = String.fromCharCode(bytes[12], bytes[13], bytes[14], bytes[15]);
|
|
139
|
+
if (chunk === "VP8X") {
|
|
140
|
+
const width = 1 + (bytes[24] | (bytes[25] << 8) | (bytes[26] << 16));
|
|
141
|
+
const height = 1 + (bytes[27] | (bytes[28] << 8) | (bytes[29] << 16));
|
|
142
|
+
return width > 1 && height > 1 ? { width, height } : undefined;
|
|
143
|
+
}
|
|
144
|
+
if (chunk === "VP8 ") {
|
|
145
|
+
if (bytes.length < 30)
|
|
146
|
+
return undefined;
|
|
147
|
+
if (bytes[23] !== 0x9d || bytes[24] !== 0x01 || bytes[25] !== 0x2a) {
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
const width = view.getUint16(26, true) & 0x3fff;
|
|
151
|
+
const height = view.getUint16(28, true) & 0x3fff;
|
|
152
|
+
return width > 0 && height > 0 ? { width, height } : undefined;
|
|
153
|
+
}
|
|
154
|
+
if (chunk === "VP8L") {
|
|
155
|
+
if (bytes.length < 25 || bytes[20] !== 0x2f)
|
|
156
|
+
return undefined;
|
|
157
|
+
const packed = bytes[21] |
|
|
158
|
+
(bytes[22] << 8) |
|
|
159
|
+
(bytes[23] << 16) |
|
|
160
|
+
(bytes[24] << 24);
|
|
161
|
+
const width = (packed & 0x3fff) + 1;
|
|
162
|
+
const height = ((packed >>> 14) & 0x3fff) + 1;
|
|
163
|
+
return width > 0 && height > 0 ? { width, height } : undefined;
|
|
164
|
+
}
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
export function readImageDimensions(bytes) {
|
|
168
|
+
switch (detectModelImageMediaType(bytes)) {
|
|
169
|
+
case "image/png":
|
|
170
|
+
return pngDimensions(bytes);
|
|
171
|
+
case "image/gif":
|
|
172
|
+
return gifDimensions(bytes);
|
|
173
|
+
case "image/jpeg":
|
|
174
|
+
return jpegDimensions(bytes);
|
|
175
|
+
case "image/webp":
|
|
176
|
+
return webpDimensions(bytes);
|
|
177
|
+
default:
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const ANTHROPIC_BUDGET = {
|
|
182
|
+
maxBytes: 3_600_000,
|
|
183
|
+
hardMaxBytes: 3_900_000,
|
|
184
|
+
maxDimension: 1568,
|
|
185
|
+
maxCount: 20,
|
|
186
|
+
maxTotalBytes: 12_000_000,
|
|
187
|
+
label: "Anthropic",
|
|
188
|
+
};
|
|
189
|
+
const OPENAI_BUDGET = {
|
|
190
|
+
maxBytes: 4_500_000,
|
|
191
|
+
hardMaxBytes: 18_000_000,
|
|
192
|
+
maxDimension: 2048,
|
|
193
|
+
maxCount: 10,
|
|
194
|
+
maxTotalBytes: 15_000_000,
|
|
195
|
+
label: "OpenAI",
|
|
196
|
+
};
|
|
197
|
+
const GEMINI_BUDGET = {
|
|
198
|
+
maxBytes: 4_500_000,
|
|
199
|
+
hardMaxBytes: 14_000_000,
|
|
200
|
+
maxDimension: 3072,
|
|
201
|
+
maxCount: 12,
|
|
202
|
+
maxTotalBytes: 15_000_000,
|
|
203
|
+
label: "Gemini",
|
|
204
|
+
};
|
|
205
|
+
const LOCAL_BUDGET = {
|
|
206
|
+
maxBytes: 6_000_000,
|
|
207
|
+
hardMaxBytes: 40_000_000,
|
|
208
|
+
maxDimension: 2048,
|
|
209
|
+
maxCount: 8,
|
|
210
|
+
maxTotalBytes: 24_000_000,
|
|
211
|
+
label: "Ollama",
|
|
212
|
+
};
|
|
213
|
+
const GATEWAY_BUDGET = {
|
|
214
|
+
maxBytes: 3_600_000,
|
|
215
|
+
hardMaxBytes: 5_000_000,
|
|
216
|
+
maxDimension: 2048,
|
|
217
|
+
maxCount: 8,
|
|
218
|
+
maxTotalBytes: 10_000_000,
|
|
219
|
+
label: "gateway",
|
|
220
|
+
};
|
|
221
|
+
function budgetForModelFamily(model) {
|
|
222
|
+
if (/claude|anthropic/i.test(model))
|
|
223
|
+
return ANTHROPIC_BUDGET;
|
|
224
|
+
if (/gemini|gemma/i.test(model))
|
|
225
|
+
return GEMINI_BUDGET;
|
|
226
|
+
if (/gpt-|^o[134]|openai/i.test(model))
|
|
227
|
+
return OPENAI_BUDGET;
|
|
228
|
+
return undefined;
|
|
229
|
+
}
|
|
230
|
+
export function imageBudgetFor(provider, model = "") {
|
|
231
|
+
switch (provider) {
|
|
232
|
+
case "anthropic":
|
|
233
|
+
return ANTHROPIC_BUDGET;
|
|
234
|
+
case "openai":
|
|
235
|
+
return OPENAI_BUDGET;
|
|
236
|
+
case "gemini":
|
|
237
|
+
return GEMINI_BUDGET;
|
|
238
|
+
case "ollama":
|
|
239
|
+
return LOCAL_BUDGET;
|
|
240
|
+
default:
|
|
241
|
+
return budgetForModelFamily(model) ?? GATEWAY_BUDGET;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
export function formatByteSize(bytes) {
|
|
245
|
+
if (bytes < 1024)
|
|
246
|
+
return `${bytes} B`;
|
|
247
|
+
if (bytes < 1024 * 1024)
|
|
248
|
+
return `${(bytes / 1024).toFixed(0)} KB`;
|
|
249
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
250
|
+
}
|
|
51
251
|
//# sourceMappingURL=image-content.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-content.js","sourceRoot":"","sources":["../../src/attachments/image-content.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"image-content.js","sourceRoot":"","sources":["../../src/attachments/image-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { imageBudgetFor, type ImageBudget, type ImageDimensions, type ModelImageMediaType } from "./image-content.js";
|
|
2
|
+
export { imageBudgetFor };
|
|
3
|
+
export type { ImageBudget };
|
|
4
|
+
export interface PreparedImage {
|
|
5
|
+
readonly ok: true;
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly sourcePath: string;
|
|
8
|
+
readonly mediaType: ModelImageMediaType;
|
|
9
|
+
readonly byteLength: number;
|
|
10
|
+
readonly dimensions?: ImageDimensions | undefined;
|
|
11
|
+
readonly transcoded: boolean;
|
|
12
|
+
readonly resized?: boolean | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface RejectedImage {
|
|
15
|
+
readonly ok: false;
|
|
16
|
+
readonly sourcePath: string;
|
|
17
|
+
readonly reason: string;
|
|
18
|
+
readonly recoverable: boolean;
|
|
19
|
+
}
|
|
20
|
+
export type ImagePreparation = PreparedImage | RejectedImage;
|
|
21
|
+
export declare function hasImageTranscoder(): boolean;
|
|
22
|
+
export declare function imageTranscoderHint(): string;
|
|
23
|
+
export declare function clearImagePreparationCache(): void;
|
|
24
|
+
export declare function prepareImageForModel(sourcePath: string, budget: ImageBudget, baseDir?: string): ImagePreparation;
|
|
25
|
+
export declare function describePreparedImage(prepared: PreparedImage): string;
|
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { closeSync, existsSync, mkdirSync, openSync, readSync, rmSync, statSync, } from "node:fs";
|
|
3
|
+
import { platform } from "node:os";
|
|
4
|
+
import { basename, extname, join } from "node:path";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { findExecutableSync } from "../os/command.js";
|
|
7
|
+
import { safeCwd } from "../os/cwd.js";
|
|
8
|
+
import { scratchDirFor } from "../prompts/index.js";
|
|
9
|
+
import { detectConvertibleImageFormat, detectModelImageMediaType, formatByteSize, imageBudgetFor, readImageDimensions, } from "./image-content.js";
|
|
10
|
+
export { imageBudgetFor };
|
|
11
|
+
const HEADER_PROBE_BYTES = 262_144;
|
|
12
|
+
const ABSOLUTE_MAX_SOURCE_BYTES = 268_435_456;
|
|
13
|
+
const MIN_TARGET_DIMENSION = 320;
|
|
14
|
+
const TRANSCODE_TIMEOUT_MS = 30_000;
|
|
15
|
+
function readHead(path, length) {
|
|
16
|
+
const handle = openSync(path, "r");
|
|
17
|
+
try {
|
|
18
|
+
const buffer = Buffer.alloc(length);
|
|
19
|
+
const read = readSync(handle, buffer, 0, length, 0);
|
|
20
|
+
return buffer.subarray(0, read);
|
|
21
|
+
}
|
|
22
|
+
finally {
|
|
23
|
+
closeSync(handle);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function probeImage(path, byteLength) {
|
|
27
|
+
const head = readHead(path, Math.min(byteLength, HEADER_PROBE_BYTES));
|
|
28
|
+
return {
|
|
29
|
+
byteLength,
|
|
30
|
+
mediaType: detectModelImageMediaType(head),
|
|
31
|
+
format: detectConvertibleImageFormat(head),
|
|
32
|
+
dimensions: readImageDimensions(head),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function resolveBinary(command) {
|
|
36
|
+
return findExecutableSync(command);
|
|
37
|
+
}
|
|
38
|
+
function hasBinary(command) {
|
|
39
|
+
return resolveBinary(command) !== undefined;
|
|
40
|
+
}
|
|
41
|
+
function runQuiet(command, argv) {
|
|
42
|
+
const executable = resolveBinary(command);
|
|
43
|
+
if (!executable)
|
|
44
|
+
return false;
|
|
45
|
+
const result = spawnSync(executable, argv, {
|
|
46
|
+
stdio: "ignore",
|
|
47
|
+
timeout: TRANSCODE_TIMEOUT_MS,
|
|
48
|
+
});
|
|
49
|
+
return result.status === 0 && !result.error;
|
|
50
|
+
}
|
|
51
|
+
const transcoders = [
|
|
52
|
+
{
|
|
53
|
+
id: "magick",
|
|
54
|
+
available: () => hasBinary("magick"),
|
|
55
|
+
run: (source, destination, target) => runQuiet("magick", magickArgv(source, destination, target)),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
id: "convert",
|
|
59
|
+
available: () => platform() !== "win32" && hasBinary("convert"),
|
|
60
|
+
run: (source, destination, target) => runQuiet("convert", magickArgv(source, destination, target)),
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: "sips",
|
|
64
|
+
available: () => platform() === "darwin" && hasBinary("sips"),
|
|
65
|
+
run: (source, destination, target) => {
|
|
66
|
+
const argv = ["-s", "format", target.format === "png" ? "png" : "jpeg"];
|
|
67
|
+
if (target.format === "jpeg") {
|
|
68
|
+
argv.push("-s", "formatOptions", String(target.quality));
|
|
69
|
+
}
|
|
70
|
+
if (target.shrinks)
|
|
71
|
+
argv.push("-Z", String(target.maxDimension));
|
|
72
|
+
argv.push(source, "--out", destination);
|
|
73
|
+
return runQuiet("sips", argv);
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: "ffmpeg",
|
|
78
|
+
available: () => hasBinary("ffmpeg"),
|
|
79
|
+
run: (source, destination, target) => {
|
|
80
|
+
if (target.format !== "png")
|
|
81
|
+
return false;
|
|
82
|
+
return runQuiet("ffmpeg", [
|
|
83
|
+
"-y",
|
|
84
|
+
"-loglevel",
|
|
85
|
+
"error",
|
|
86
|
+
"-i",
|
|
87
|
+
source,
|
|
88
|
+
"-vf",
|
|
89
|
+
`scale=w=min(iw\\,${target.maxDimension}):h=min(ih\\,${target.maxDimension}):force_original_aspect_ratio=decrease`,
|
|
90
|
+
"-frames:v",
|
|
91
|
+
"1",
|
|
92
|
+
destination,
|
|
93
|
+
]);
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
];
|
|
97
|
+
function magickArgv(source, destination, target) {
|
|
98
|
+
const argv = [`${source}[0]`, "-auto-orient", "-strip"];
|
|
99
|
+
if (target.shrinks) {
|
|
100
|
+
argv.push("-resize", `${target.maxDimension}x${target.maxDimension}>`);
|
|
101
|
+
}
|
|
102
|
+
if (target.format === "jpeg") {
|
|
103
|
+
argv.push("-background", "white", "-alpha", "remove", "-alpha", "off");
|
|
104
|
+
argv.push("-quality", String(target.quality));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
argv.push("-define", "png:compression-level=9");
|
|
108
|
+
}
|
|
109
|
+
argv.push(destination);
|
|
110
|
+
return argv;
|
|
111
|
+
}
|
|
112
|
+
function externalDimensions(path) {
|
|
113
|
+
const parse = (stdout) => {
|
|
114
|
+
const width = /pixelWidth:\s*(\d+)/.exec(stdout)?.[1];
|
|
115
|
+
const height = /pixelHeight:\s*(\d+)/.exec(stdout)?.[1];
|
|
116
|
+
if (width && height) {
|
|
117
|
+
return { width: Number(width), height: Number(height) };
|
|
118
|
+
}
|
|
119
|
+
const pair = /^(\d+)\s+(\d+)/.exec(stdout.trim());
|
|
120
|
+
return pair ? { width: Number(pair[1]), height: Number(pair[2]) } : undefined;
|
|
121
|
+
};
|
|
122
|
+
const attempts = [];
|
|
123
|
+
if (hasBinary("magick")) {
|
|
124
|
+
attempts.push(["magick", ["identify", "-format", "%w %h", `${path}[0]`]]);
|
|
125
|
+
}
|
|
126
|
+
if (platform() !== "win32" && hasBinary("identify")) {
|
|
127
|
+
attempts.push(["identify", ["-format", "%w %h", `${path}[0]`]]);
|
|
128
|
+
}
|
|
129
|
+
if (platform() === "darwin" && hasBinary("sips")) {
|
|
130
|
+
attempts.push(["sips", ["-g", "pixelWidth", "-g", "pixelHeight", path]]);
|
|
131
|
+
}
|
|
132
|
+
for (const [command, argv] of attempts) {
|
|
133
|
+
const executable = resolveBinary(command);
|
|
134
|
+
if (!executable)
|
|
135
|
+
continue;
|
|
136
|
+
const result = spawnSync(executable, argv, {
|
|
137
|
+
encoding: "utf8",
|
|
138
|
+
timeout: 10_000,
|
|
139
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
140
|
+
});
|
|
141
|
+
if (result.status !== 0 || typeof result.stdout !== "string")
|
|
142
|
+
continue;
|
|
143
|
+
const dimensions = parse(result.stdout);
|
|
144
|
+
if (dimensions && dimensions.width > 0 && dimensions.height > 0) {
|
|
145
|
+
return dimensions;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
function availableTranscoders() {
|
|
151
|
+
return transcoders.filter((transcoder) => transcoder.available());
|
|
152
|
+
}
|
|
153
|
+
export function hasImageTranscoder() {
|
|
154
|
+
return availableTranscoders().length > 0;
|
|
155
|
+
}
|
|
156
|
+
export function imageTranscoderHint() {
|
|
157
|
+
return platform() === "darwin"
|
|
158
|
+
? "install ImageMagick (brew install imagemagick) or resize it with sips"
|
|
159
|
+
: platform() === "win32"
|
|
160
|
+
? "install ImageMagick (scoop install imagemagick) and retry"
|
|
161
|
+
: "install ImageMagick (apt install imagemagick / dnf install ImageMagick) and retry";
|
|
162
|
+
}
|
|
163
|
+
function scale(dimension, factor) {
|
|
164
|
+
return Math.max(MIN_TARGET_DIMENSION, Math.round(dimension * factor));
|
|
165
|
+
}
|
|
166
|
+
function ladderFor(budget, probe) {
|
|
167
|
+
const longestEdge = probe.dimensions
|
|
168
|
+
? Math.max(probe.dimensions.width, probe.dimensions.height)
|
|
169
|
+
: undefined;
|
|
170
|
+
const base = longestEdge === undefined
|
|
171
|
+
? budget.maxDimension
|
|
172
|
+
: Math.max(MIN_TARGET_DIMENSION, Math.min(budget.maxDimension, longestEdge));
|
|
173
|
+
const photographic = probe.format === "image/jpeg" ||
|
|
174
|
+
probe.format === "image/heic" ||
|
|
175
|
+
probe.format === "image/avif";
|
|
176
|
+
const dimensions = photographic
|
|
177
|
+
? [base, scale(base, 0.75), scale(base, 0.5), 1280, 1024, 800]
|
|
178
|
+
: [base, base, base, scale(base, 0.75), scale(base, 0.75), 1280, 1024, 800];
|
|
179
|
+
const plan = photographic
|
|
180
|
+
? [
|
|
181
|
+
["jpeg", dimensions[0], 88],
|
|
182
|
+
["jpeg", dimensions[1], 84],
|
|
183
|
+
["jpeg", dimensions[2], 78],
|
|
184
|
+
]
|
|
185
|
+
: [
|
|
186
|
+
["png", dimensions[0], 100],
|
|
187
|
+
["jpeg", dimensions[1], 92],
|
|
188
|
+
["jpeg", dimensions[2], 80],
|
|
189
|
+
["png", dimensions[3], 100],
|
|
190
|
+
["jpeg", dimensions[4], 85],
|
|
191
|
+
];
|
|
192
|
+
plan.push(["jpeg", Math.min(base, 1280), 78], ["jpeg", Math.min(base, 1024), 70], ["jpeg", Math.min(base, 800), 58]);
|
|
193
|
+
const seen = new Set();
|
|
194
|
+
const targets = [];
|
|
195
|
+
for (const [format, maxDimension, quality] of plan) {
|
|
196
|
+
const key = `${format}:${maxDimension}:${quality}`;
|
|
197
|
+
if (seen.has(key))
|
|
198
|
+
continue;
|
|
199
|
+
seen.add(key);
|
|
200
|
+
targets.push({
|
|
201
|
+
format,
|
|
202
|
+
maxDimension,
|
|
203
|
+
quality,
|
|
204
|
+
shrinks: longestEdge === undefined || longestEdge > maxDimension,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return targets;
|
|
208
|
+
}
|
|
209
|
+
function fittedDirectory(baseDir) {
|
|
210
|
+
const directory = join(scratchDirFor(baseDir), "attachments", "fitted");
|
|
211
|
+
mkdirSync(directory, { recursive: true });
|
|
212
|
+
return directory;
|
|
213
|
+
}
|
|
214
|
+
function stemFor(path) {
|
|
215
|
+
const extension = extname(path);
|
|
216
|
+
const stem = basename(path, extension)
|
|
217
|
+
.replace(/[^\w.-]+/g, "_")
|
|
218
|
+
.replace(/_+/g, "_")
|
|
219
|
+
.slice(0, 60);
|
|
220
|
+
return stem || "image";
|
|
221
|
+
}
|
|
222
|
+
function transcodeToBudget(source, probe, budget, baseDir) {
|
|
223
|
+
const tools = availableTranscoders();
|
|
224
|
+
if (tools.length === 0)
|
|
225
|
+
return undefined;
|
|
226
|
+
const directory = fittedDirectory(baseDir);
|
|
227
|
+
const fingerprint = createHash("sha1")
|
|
228
|
+
.update(`${source}|${probe.byteLength}|${budget.maxBytes}|${budget.maxDimension}`)
|
|
229
|
+
.digest("hex")
|
|
230
|
+
.slice(0, 12);
|
|
231
|
+
const stem = stemFor(source);
|
|
232
|
+
let best;
|
|
233
|
+
for (const target of ladderFor(budget, probe)) {
|
|
234
|
+
const extension = target.format === "png" ? "png" : "jpg";
|
|
235
|
+
const destination = join(directory, `${fingerprint}-${target.maxDimension}q${target.quality}-${stem}.${extension}`);
|
|
236
|
+
for (const tool of tools) {
|
|
237
|
+
rmSync(destination, { force: true });
|
|
238
|
+
if (!tool.run(source, destination, target))
|
|
239
|
+
continue;
|
|
240
|
+
let byteLength = 0;
|
|
241
|
+
try {
|
|
242
|
+
byteLength = statSync(destination).size;
|
|
243
|
+
}
|
|
244
|
+
catch {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (byteLength === 0) {
|
|
248
|
+
rmSync(destination, { force: true });
|
|
249
|
+
continue;
|
|
250
|
+
}
|
|
251
|
+
const head = readHead(destination, Math.min(byteLength, HEADER_PROBE_BYTES));
|
|
252
|
+
const mediaType = detectModelImageMediaType(head);
|
|
253
|
+
if (!mediaType) {
|
|
254
|
+
rmSync(destination, { force: true });
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
const dimensions = readImageDimensions(head);
|
|
258
|
+
const sourceEdge = probe.dimensions
|
|
259
|
+
? Math.max(probe.dimensions.width, probe.dimensions.height)
|
|
260
|
+
: undefined;
|
|
261
|
+
const outputEdge = dimensions
|
|
262
|
+
? Math.max(dimensions.width, dimensions.height)
|
|
263
|
+
: undefined;
|
|
264
|
+
const candidate = {
|
|
265
|
+
ok: true,
|
|
266
|
+
path: destination,
|
|
267
|
+
sourcePath: source,
|
|
268
|
+
mediaType,
|
|
269
|
+
byteLength,
|
|
270
|
+
dimensions,
|
|
271
|
+
transcoded: true,
|
|
272
|
+
resized: sourceEdge === undefined ||
|
|
273
|
+
outputEdge === undefined ||
|
|
274
|
+
outputEdge < sourceEdge,
|
|
275
|
+
};
|
|
276
|
+
if (byteLength <= budget.maxBytes)
|
|
277
|
+
return candidate;
|
|
278
|
+
if (!best || byteLength < best.byteLength)
|
|
279
|
+
best = candidate;
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (best && best.byteLength <= budget.hardMaxBytes)
|
|
284
|
+
return best;
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
const preparationCache = new Map();
|
|
288
|
+
function cacheKey(path, mtimeMs, size, budget) {
|
|
289
|
+
return `${path}|${Math.round(mtimeMs)}|${size}|${budget.maxBytes}|${budget.hardMaxBytes}|${budget.maxDimension}`;
|
|
290
|
+
}
|
|
291
|
+
export function clearImagePreparationCache() {
|
|
292
|
+
preparationCache.clear();
|
|
293
|
+
}
|
|
294
|
+
function reject(sourcePath, reason, recoverable = false) {
|
|
295
|
+
return { ok: false, sourcePath, reason, recoverable };
|
|
296
|
+
}
|
|
297
|
+
export function prepareImageForModel(sourcePath, budget, baseDir = safeCwd()) {
|
|
298
|
+
let info;
|
|
299
|
+
try {
|
|
300
|
+
info = statSync(sourcePath);
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
return reject(sourcePath, `could not be read (${error instanceof Error ? error.message : String(error)})`);
|
|
304
|
+
}
|
|
305
|
+
if (!info.isFile())
|
|
306
|
+
return reject(sourcePath, "is not a regular file");
|
|
307
|
+
if (info.size === 0)
|
|
308
|
+
return reject(sourcePath, "is empty (0 bytes)");
|
|
309
|
+
if (info.size > ABSOLUTE_MAX_SOURCE_BYTES) {
|
|
310
|
+
return reject(sourcePath, `is ${formatByteSize(info.size)}, far beyond any model image limit`);
|
|
311
|
+
}
|
|
312
|
+
const key = cacheKey(sourcePath, info.mtimeMs, info.size, budget);
|
|
313
|
+
const cached = preparationCache.get(key);
|
|
314
|
+
if (cached && (!cached.ok || existsSync(cached.path)))
|
|
315
|
+
return cached;
|
|
316
|
+
const outcome = prepare(sourcePath, info.size, budget, baseDir);
|
|
317
|
+
preparationCache.set(key, outcome);
|
|
318
|
+
return outcome;
|
|
319
|
+
}
|
|
320
|
+
function prepare(sourcePath, size, budget, baseDir) {
|
|
321
|
+
let probe;
|
|
322
|
+
try {
|
|
323
|
+
probe = probeImage(sourcePath, size);
|
|
324
|
+
}
|
|
325
|
+
catch (error) {
|
|
326
|
+
return reject(sourcePath, `could not be read (${error instanceof Error ? error.message : String(error)})`);
|
|
327
|
+
}
|
|
328
|
+
const format = probe.format;
|
|
329
|
+
if (!format) {
|
|
330
|
+
return reject(sourcePath, "its bytes are not a supported image (expected PNG, JPEG, GIF, WebP, BMP, TIFF, HEIC or AVIF)");
|
|
331
|
+
}
|
|
332
|
+
if (!probe.dimensions &&
|
|
333
|
+
(probe.mediaType === undefined || size > budget.maxBytes)) {
|
|
334
|
+
const external = externalDimensions(sourcePath);
|
|
335
|
+
if (external)
|
|
336
|
+
probe = { ...probe, dimensions: external };
|
|
337
|
+
}
|
|
338
|
+
const oversizedDimensions = probe.dimensions
|
|
339
|
+
? Math.max(probe.dimensions.width, probe.dimensions.height) >
|
|
340
|
+
budget.maxDimension
|
|
341
|
+
: false;
|
|
342
|
+
const nativeAndFitting = probe.mediaType !== undefined &&
|
|
343
|
+
size <= budget.maxBytes &&
|
|
344
|
+
!oversizedDimensions;
|
|
345
|
+
if (nativeAndFitting) {
|
|
346
|
+
return {
|
|
347
|
+
ok: true,
|
|
348
|
+
path: sourcePath,
|
|
349
|
+
sourcePath,
|
|
350
|
+
mediaType: probe.mediaType,
|
|
351
|
+
byteLength: size,
|
|
352
|
+
dimensions: probe.dimensions,
|
|
353
|
+
transcoded: false,
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const fitted = transcodeToBudget(sourcePath, probe, budget, baseDir);
|
|
357
|
+
if (fitted)
|
|
358
|
+
return fitted;
|
|
359
|
+
if (probe.mediaType && size <= budget.hardMaxBytes) {
|
|
360
|
+
return {
|
|
361
|
+
ok: true,
|
|
362
|
+
path: sourcePath,
|
|
363
|
+
sourcePath,
|
|
364
|
+
mediaType: probe.mediaType,
|
|
365
|
+
byteLength: size,
|
|
366
|
+
dimensions: probe.dimensions,
|
|
367
|
+
transcoded: false,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
if (!probe.mediaType) {
|
|
371
|
+
return reject(sourcePath, `is ${format.replace("image/", "").toUpperCase()}, which models cannot read, and no converter is installed — ${imageTranscoderHint()}`, true);
|
|
372
|
+
}
|
|
373
|
+
return reject(sourcePath, `is ${formatByteSize(size)}, above the ${formatByteSize(budget.hardMaxBytes)} per-image limit for ${budget.label} models` +
|
|
374
|
+
(hasImageTranscoder()
|
|
375
|
+
? " and could not be downscaled far enough"
|
|
376
|
+
: ` and no downscaler is installed — ${imageTranscoderHint()}`), true);
|
|
377
|
+
}
|
|
378
|
+
export function describePreparedImage(prepared) {
|
|
379
|
+
const size = formatByteSize(prepared.byteLength);
|
|
380
|
+
const dimensions = prepared.dimensions
|
|
381
|
+
? `${prepared.dimensions.width}x${prepared.dimensions.height}`
|
|
382
|
+
: "unknown size";
|
|
383
|
+
const format = prepared.mediaType.replace("image/", "").toUpperCase();
|
|
384
|
+
if (!prepared.transcoded)
|
|
385
|
+
return `${dimensions} ${format} (${size})`;
|
|
386
|
+
return prepared.resized
|
|
387
|
+
? `resized to ${dimensions} ${format} (${size}) to fit the model input limit`
|
|
388
|
+
: `converted to ${dimensions} ${format} (${size}) so the model can read it`;
|
|
389
|
+
}
|
|
390
|
+
//# sourceMappingURL=image-prepare.js.map
|