omnius 1.0.21 → 1.0.22
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/index.js +683 -76
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1474,7 +1474,7 @@ var init_security_classifier = __esm({
|
|
|
1474
1474
|
// ── Network reads (safe)
|
|
1475
1475
|
{ match: /^(web_search|web_fetch)$/, info: NETWORK_READ },
|
|
1476
1476
|
// ── Network outbound (mutating or remote inference)
|
|
1477
|
-
{ match: /^(image_generate|generate_image|vision|video_understand)$/, info: NETWORK_OUTBOUND },
|
|
1477
|
+
{ match: /^(image_generate|generate_image|generate_audio|generate_tts|create_audio_file|vision|video_understand|telegram_send_file)$/, info: NETWORK_OUTBOUND },
|
|
1478
1478
|
{ match: /^(transcribe_file|transcribe_url|youtube_download)$/, info: NETWORK_OUTBOUND },
|
|
1479
1479
|
{ match: /^(fortemi_bridge)$/, info: NETWORK_OUTBOUND },
|
|
1480
1480
|
// ── Memory tools
|
|
@@ -1491,7 +1491,7 @@ var init_security_classifier = __esm({
|
|
|
1491
1491
|
{ match: /^(file_read|file_explore|list_directory|grep_search|glob_find|find_files)$/, info: LOCAL_READ },
|
|
1492
1492
|
{ match: /^(image_read|ocr|ocr_pdf|ocr_image_advanced|pdf_to_text|structured_read|read_structured_file)$/, info: LOCAL_READ },
|
|
1493
1493
|
{ match: /^(symbol_search|impact_analysis|code_neighbors|repo_map|codebase_map|semantic_map|import_graph)$/, info: LOCAL_READ },
|
|
1494
|
-
{ match: /^(diagnostic|git_info|environment_snapshot|process_health|todo_read|explore_tools)$/, info: LOCAL_READ },
|
|
1494
|
+
{ match: /^(diagnostic|git_info|environment_snapshot|process_health|todo_read|explore_tools|telegram_media_recent)$/, info: LOCAL_READ },
|
|
1495
1495
|
{ match: /^(log_explore|log_packet|change_log|phase_recall|code_graph)$/, info: LOCAL_READ },
|
|
1496
1496
|
{ match: /^skill_(list|execute|read)$/, info: LOCAL_READ },
|
|
1497
1497
|
// ── Task completion (neutral signal)
|
|
@@ -5733,13 +5733,20 @@ var init_explore_tools = __esm({
|
|
|
5733
5733
|
diagnostic: "Run project diagnostics (build, test, lint)",
|
|
5734
5734
|
image_read: "Read and describe image contents",
|
|
5735
5735
|
screenshot: "Capture a screenshot of the desktop",
|
|
5736
|
+
ocr: "Extract text from images via OCR",
|
|
5736
5737
|
ocr_image: "Extract text from images via OCR",
|
|
5738
|
+
ocr_image_advanced: "Advanced OCR for images with layout-aware extraction",
|
|
5737
5739
|
ocr_pdf: "Extract text from PDF pages via OCR",
|
|
5738
5740
|
pdf_to_text: "Convert PDF to plain text",
|
|
5739
5741
|
vision: "Describe what's on screen using Moondream",
|
|
5742
|
+
video_understand: "Analyze a video file with transcription and keyframe understanding",
|
|
5743
|
+
audio_analyze: "Classify sounds, detect speech, inspect spectrum, or analyze audio files",
|
|
5740
5744
|
desktop_click: "Click at coordinates on the desktop",
|
|
5741
5745
|
desktop_describe: "Describe a region of the desktop",
|
|
5742
5746
|
transcribe_file: "Transcribe audio/video files to text",
|
|
5747
|
+
telegram_media_recent: "List recent Telegram media available in the current chat scope",
|
|
5748
|
+
generate_audio: "Generate sound effects or music with local model backends",
|
|
5749
|
+
generate_tts: "Generate speech from text with configured voice/TTS backends",
|
|
5743
5750
|
create_tool: "Create a new custom tool from a workflow",
|
|
5744
5751
|
manage_tools: "List, inspect, or remove custom tools",
|
|
5745
5752
|
skill_list: "List available AIWG skills",
|
|
@@ -84452,7 +84459,7 @@ var require_mime_types = __commonJS({
|
|
|
84452
84459
|
"../node_modules/mime-types/index.js"(exports) {
|
|
84453
84460
|
"use strict";
|
|
84454
84461
|
var db = require_mime_db();
|
|
84455
|
-
var
|
|
84462
|
+
var extname17 = __require("path").extname;
|
|
84456
84463
|
var EXTRACT_TYPE_REGEXP = /^\s*([^;\s]*)(?:;|\s|$)/;
|
|
84457
84464
|
var TEXT_TYPE_REGEXP = /^text\//i;
|
|
84458
84465
|
exports.charset = charset;
|
|
@@ -84506,7 +84513,7 @@ var require_mime_types = __commonJS({
|
|
|
84506
84513
|
if (!path11 || typeof path11 !== "string") {
|
|
84507
84514
|
return false;
|
|
84508
84515
|
}
|
|
84509
|
-
var extension4 =
|
|
84516
|
+
var extension4 = extname17("x." + path11).toLowerCase().substr(1);
|
|
84510
84517
|
if (!extension4) {
|
|
84511
84518
|
return false;
|
|
84512
84519
|
}
|
|
@@ -250402,6 +250409,11 @@ function getImageGenerationPreset(model) {
|
|
|
250402
250409
|
function imageGenerationQualityLadder() {
|
|
250403
250410
|
return IMAGE_GENERATION_QUALITY_LADDER.map((id) => getImageGenerationPreset(id)).filter((preset) => Boolean(preset));
|
|
250404
250411
|
}
|
|
250412
|
+
function imageGenerationFallbackAlternates(model) {
|
|
250413
|
+
if (!model)
|
|
250414
|
+
return [];
|
|
250415
|
+
return IMAGE_GENERATION_MODEL_PRESETS.filter((preset) => preset.fallbackFor?.includes(model));
|
|
250416
|
+
}
|
|
250405
250417
|
function inferImageGenerationBackend(model, requested) {
|
|
250406
250418
|
if (requested && isBackend(requested))
|
|
250407
250419
|
return requested;
|
|
@@ -250438,6 +250450,8 @@ function imageGenerationFallbackCandidates(requestedModel, requestedBackend, all
|
|
|
250438
250450
|
};
|
|
250439
250451
|
if (requestedModel) {
|
|
250440
250452
|
add2(imageCandidateFor(requestedModel, requestedBackend));
|
|
250453
|
+
for (const alternate of imageGenerationFallbackAlternates(requestedModel))
|
|
250454
|
+
add2(imageCandidateFor(alternate.id));
|
|
250441
250455
|
} else if (requestedBackend && requestedBackend !== "auto") {
|
|
250442
250456
|
const firstForBackend = ladder.find((preset) => preset.backend === requestedBackend);
|
|
250443
250457
|
add2(imageCandidateFor(firstForBackend?.id ?? (requestedBackend === "ollama" ? DEFAULT_OLLAMA_IMAGE_MODEL : DEFAULT_DIFFUSERS_IMAGE_MODEL), requestedBackend));
|
|
@@ -250448,8 +250462,11 @@ function imageGenerationFallbackCandidates(requestedModel, requestedBackend, all
|
|
|
250448
250462
|
return candidates.length ? candidates : [imageCandidateFor(DEFAULT_DIFFUSERS_IMAGE_MODEL, requestedBackend)];
|
|
250449
250463
|
const primaryIndex = requestedModel ? ladder.findIndex((preset) => preset.id === requestedModel) : requestedBackend && requestedBackend !== "auto" ? ladder.findIndex((preset) => preset.backend === requestedBackend) : 0;
|
|
250450
250464
|
const fallbackTail = primaryIndex >= 0 ? ladder.slice(primaryIndex) : ladder;
|
|
250451
|
-
for (const preset of fallbackTail)
|
|
250465
|
+
for (const preset of fallbackTail) {
|
|
250452
250466
|
add2(imageCandidateFor(preset.id));
|
|
250467
|
+
for (const alternate of imageGenerationFallbackAlternates(preset.id))
|
|
250468
|
+
add2(imageCandidateFor(alternate.id));
|
|
250469
|
+
}
|
|
250453
250470
|
return candidates;
|
|
250454
250471
|
}
|
|
250455
250472
|
function imageGenerationDir(repoRoot = ".") {
|
|
@@ -250817,6 +250834,78 @@ var init_image_generate = __esm({
|
|
|
250817
250834
|
height: 1024,
|
|
250818
250835
|
note: "Primary serious-generation baseline for maximum photorealism."
|
|
250819
250836
|
},
|
|
250837
|
+
{
|
|
250838
|
+
id: "black-forest-labs/FLUX.1-dev-FP8",
|
|
250839
|
+
label: "FLUX.1 dev FP8",
|
|
250840
|
+
backend: "diffusers",
|
|
250841
|
+
install: 'python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.1-dev-FP8 --steps 28 --guidance 3.5 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png',
|
|
250842
|
+
category: "Official FLUX fallback",
|
|
250843
|
+
sizeClass: "12B FLUX.1 dev FP8",
|
|
250844
|
+
quality: "Official lower-precision FLUX.1 dev route; best first fallback when full FLUX.1 dev is unavailable or too heavy.",
|
|
250845
|
+
minVramGB: 16,
|
|
250846
|
+
recommendedVramGB: 24,
|
|
250847
|
+
deployment: "Prefer this before third-party mirrors when loader support is available.",
|
|
250848
|
+
steps: 28,
|
|
250849
|
+
guidance: 3.5,
|
|
250850
|
+
width: 1024,
|
|
250851
|
+
height: 1024,
|
|
250852
|
+
fallbackFor: ["black-forest-labs/FLUX.1-dev"],
|
|
250853
|
+
note: "Official BFL FP8 fallback for FLUX.1 dev."
|
|
250854
|
+
},
|
|
250855
|
+
{
|
|
250856
|
+
id: "black-forest-labs/FLUX.1-Krea-dev",
|
|
250857
|
+
label: "FLUX.1 Krea dev",
|
|
250858
|
+
backend: "diffusers",
|
|
250859
|
+
install: 'python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.1-Krea-dev --steps 28 --guidance 3.5 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png',
|
|
250860
|
+
category: "Official FLUX fallback",
|
|
250861
|
+
sizeClass: "12B FLUX.1 dev-family",
|
|
250862
|
+
quality: "Official FLUX.1 dev-family aesthetic variant; useful when the base dev repo is unavailable and the requested task tolerates an opinionated realism bias.",
|
|
250863
|
+
minVramGB: 24,
|
|
250864
|
+
recommendedVramGB: 48,
|
|
250865
|
+
deployment: "Heavy Diffusers/ComfyUI route with FLUX.1 dev-family license considerations.",
|
|
250866
|
+
steps: 28,
|
|
250867
|
+
guidance: 3.5,
|
|
250868
|
+
width: 1024,
|
|
250869
|
+
height: 1024,
|
|
250870
|
+
fallbackFor: ["black-forest-labs/FLUX.1-dev"],
|
|
250871
|
+
note: "Official aesthetic FLUX.1 fallback."
|
|
250872
|
+
},
|
|
250873
|
+
{
|
|
250874
|
+
id: "lllyasviel/flux1-dev-bnb-nf4",
|
|
250875
|
+
label: "FLUX.1 dev BNB NF4",
|
|
250876
|
+
backend: "diffusers",
|
|
250877
|
+
install: 'python3 .omnius/image-gen/diffusers_text2image.py --model lllyasviel/flux1-dev-bnb-nf4 --steps 28 --guidance 3.5 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png',
|
|
250878
|
+
category: "Traceable FLUX fallback",
|
|
250879
|
+
sizeClass: "12B FLUX.1 dev NF4",
|
|
250880
|
+
quality: "Lower-memory community quantization; useful after official BFL sources, with some possible quality loss and loader brittleness.",
|
|
250881
|
+
minVramGB: 12,
|
|
250882
|
+
recommendedVramGB: 16,
|
|
250883
|
+
deployment: "Best with BNB-aware Diffusers/Forge-style runtimes. Falls through cleanly if the current runner cannot load it.",
|
|
250884
|
+
steps: 28,
|
|
250885
|
+
guidance: 3.5,
|
|
250886
|
+
width: 1024,
|
|
250887
|
+
height: 1024,
|
|
250888
|
+
fallbackFor: ["black-forest-labs/FLUX.1-dev", "black-forest-labs/FLUX.1-dev-FP8"],
|
|
250889
|
+
note: "Traceable low-VRAM NF4 fallback for FLUX.1 dev."
|
|
250890
|
+
},
|
|
250891
|
+
{
|
|
250892
|
+
id: "ChuckMcSneed/FLUX.1-dev",
|
|
250893
|
+
label: "FLUX.1 dev mirror",
|
|
250894
|
+
backend: "diffusers",
|
|
250895
|
+
install: 'python3 .omnius/image-gen/diffusers_text2image.py --model ChuckMcSneed/FLUX.1-dev --steps 28 --guidance 3.5 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png',
|
|
250896
|
+
category: "Traceable FLUX fallback",
|
|
250897
|
+
sizeClass: "12B FLUX.1 dev mirror",
|
|
250898
|
+
quality: "Lower-priority mirror fallback for FLUX.1 dev. Use only after official and reputable quantized options fail.",
|
|
250899
|
+
minVramGB: 24,
|
|
250900
|
+
recommendedVramGB: 48,
|
|
250901
|
+
deployment: "Treat as lower-trust than official BFL and well-known quantized conversions; verify provenance and license before relying on it.",
|
|
250902
|
+
steps: 28,
|
|
250903
|
+
guidance: 3.5,
|
|
250904
|
+
width: 1024,
|
|
250905
|
+
height: 1024,
|
|
250906
|
+
fallbackFor: ["black-forest-labs/FLUX.1-dev", "black-forest-labs/FLUX.1-dev-FP8"],
|
|
250907
|
+
note: "Traceable mirror fallback for FLUX.1 dev."
|
|
250908
|
+
},
|
|
250820
250909
|
{
|
|
250821
250910
|
id: "stabilityai/stable-diffusion-3.5-large",
|
|
250822
250911
|
label: "Stable Diffusion 3.5 Large",
|
|
@@ -250917,6 +251006,40 @@ var init_image_generate = __esm({
|
|
|
250917
251006
|
height: 1024,
|
|
250918
251007
|
note: "More deployable compact FLUX-family model."
|
|
250919
251008
|
},
|
|
251009
|
+
{
|
|
251010
|
+
id: "black-forest-labs/FLUX.2-klein-4b-fp8",
|
|
251011
|
+
label: "FLUX.2 Klein 4B FP8",
|
|
251012
|
+
backend: "diffusers",
|
|
251013
|
+
install: 'python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.2-klein-4b-fp8 --steps 8 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png',
|
|
251014
|
+
category: "Official FLUX fallback",
|
|
251015
|
+
sizeClass: "4B compact FLUX-family FP8",
|
|
251016
|
+
quality: "Official lower-precision FLUX.2 Klein route with better deployment fit than full-precision 4B.",
|
|
251017
|
+
minVramGB: 8,
|
|
251018
|
+
recommendedVramGB: 12,
|
|
251019
|
+
deployment: "Preferred lower-memory official FLUX.2 fallback when compatible with the current loader.",
|
|
251020
|
+
steps: 8,
|
|
251021
|
+
width: 1024,
|
|
251022
|
+
height: 1024,
|
|
251023
|
+
fallbackFor: ["black-forest-labs/FLUX.2-klein-4B", "x/flux2-klein"],
|
|
251024
|
+
note: "Official FP8 fallback for FLUX.2 Klein."
|
|
251025
|
+
},
|
|
251026
|
+
{
|
|
251027
|
+
id: "black-forest-labs/FLUX.2-klein-4b-nvfp4",
|
|
251028
|
+
label: "FLUX.2 Klein 4B NVFP4",
|
|
251029
|
+
backend: "diffusers",
|
|
251030
|
+
install: 'python3 .omnius/image-gen/diffusers_text2image.py --model black-forest-labs/FLUX.2-klein-4b-nvfp4 --steps 8 --width 1024 --height 1024 --prompt "..." --output .omnius/images/out.png',
|
|
251031
|
+
category: "Official FLUX fallback",
|
|
251032
|
+
sizeClass: "4B compact FLUX-family NVFP4",
|
|
251033
|
+
quality: "Official NVIDIA-oriented low-precision FLUX.2 Klein fallback.",
|
|
251034
|
+
minVramGB: 8,
|
|
251035
|
+
recommendedVramGB: 12,
|
|
251036
|
+
deployment: "Use when the runtime/GPU supports the NVFP4 path; otherwise the fallback ladder continues.",
|
|
251037
|
+
steps: 8,
|
|
251038
|
+
width: 1024,
|
|
251039
|
+
height: 1024,
|
|
251040
|
+
fallbackFor: ["black-forest-labs/FLUX.2-klein-4B", "x/flux2-klein", "black-forest-labs/FLUX.2-klein-4b-fp8"],
|
|
251041
|
+
note: "Official NVFP4 fallback for FLUX.2 Klein."
|
|
251042
|
+
},
|
|
250920
251043
|
{
|
|
250921
251044
|
id: "deepseek-ai/Janus-Pro-7B",
|
|
250922
251045
|
label: "Janus-Pro-7B",
|
|
@@ -251265,7 +251388,7 @@ if __name__ == "__main__":
|
|
|
251265
251388
|
`;
|
|
251266
251389
|
ImageGenerateTool = class {
|
|
251267
251390
|
name = "generate_image";
|
|
251268
|
-
description = "Generate an image from a text prompt using a local image-generation backend. Supports Ollama image models (x/z-image-turbo, x/flux2-klein), Python Diffusers models (SDXL Turbo default, FLUX.1 dev, SD3.5 Large, Tiny-SD, LCM, Sana Sprint), and stable-diffusion.cpp local checkpoints/GGUF. When fallback is enabled, auto generation tries ranked high-quality candidates first and falls back to smaller models if setup, download, or generation fails. Saves a PNG under .omnius/images and returns the file path.";
|
|
251391
|
+
description = "Generate an image from a text prompt using a local image-generation backend. Supports Ollama image models (x/z-image-turbo, x/flux2-klein), Python Diffusers models (SDXL Turbo default, FLUX.1 dev, SD3.5 Large, Tiny-SD, LCM, Sana Sprint), and stable-diffusion.cpp local checkpoints/GGUF. When fallback is enabled, auto generation tries ranked high-quality candidates first, including official/traceable FLUX fallbacks for Black Forest Labs models, and then falls back to smaller models if setup, download, or generation fails. Saves a PNG under .omnius/images and returns the file path.";
|
|
251269
251392
|
parameters = {
|
|
251270
251393
|
type: "object",
|
|
251271
251394
|
properties: {
|
|
@@ -251929,7 +252052,7 @@ ${errText.slice(0, 800)}`,
|
|
|
251929
252052
|
});
|
|
251930
252053
|
|
|
251931
252054
|
// packages/execution/dist/tools/audio-generate.js
|
|
251932
|
-
import { spawn as spawn10 } from "node:child_process";
|
|
252055
|
+
import { execFileSync as execFileSync2, spawn as spawn10 } from "node:child_process";
|
|
251933
252056
|
import { existsSync as existsSync24, readdirSync as readdirSync10, statSync as statSync9 } from "node:fs";
|
|
251934
252057
|
import { chmod as chmod4, mkdir as mkdir12, writeFile as writeFile17 } from "node:fs/promises";
|
|
251935
252058
|
import { join as join37 } from "node:path";
|
|
@@ -251953,6 +252076,56 @@ function backendPackages(backend) {
|
|
|
251953
252076
|
return TANGOFLUX_PACKAGES;
|
|
251954
252077
|
return DIFFUSERS_AUDIO_PACKAGES;
|
|
251955
252078
|
}
|
|
252079
|
+
function detectLegacyCudaComputeCapability() {
|
|
252080
|
+
try {
|
|
252081
|
+
const out = execFileSync2("nvidia-smi", ["--query-gpu=compute_cap,name", "--format=csv,noheader,nounits"], {
|
|
252082
|
+
encoding: "utf8",
|
|
252083
|
+
timeout: 5e3,
|
|
252084
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
252085
|
+
}).trim();
|
|
252086
|
+
const first2 = out.split(/\r?\n/).map((line) => line.trim()).find(Boolean);
|
|
252087
|
+
const match = first2?.match(/^(\d+)\.(\d+)\s*,?\s*(.*)$/);
|
|
252088
|
+
if (!match)
|
|
252089
|
+
return null;
|
|
252090
|
+
const major = Number(match[1]);
|
|
252091
|
+
const minor = Number(match[2]);
|
|
252092
|
+
if (!Number.isFinite(major) || !Number.isFinite(minor))
|
|
252093
|
+
return null;
|
|
252094
|
+
return { major, minor, name: match[3]?.trim() || void 0 };
|
|
252095
|
+
} catch {
|
|
252096
|
+
return null;
|
|
252097
|
+
}
|
|
252098
|
+
}
|
|
252099
|
+
function isLegacyCudaCapability(major, minor) {
|
|
252100
|
+
return major < 7 || major === 7 && minor < 5;
|
|
252101
|
+
}
|
|
252102
|
+
function torchInstallPlan(forceLegacyCuda = false) {
|
|
252103
|
+
if (process.env["OMNIUS_AUDIO_TORCH_INDEX_URL"]) {
|
|
252104
|
+
return {
|
|
252105
|
+
args: ["torch", "torchaudio", "--index-url", process.env["OMNIUS_AUDIO_TORCH_INDEX_URL"]],
|
|
252106
|
+
description: `env override ${process.env["OMNIUS_AUDIO_TORCH_INDEX_URL"]}`
|
|
252107
|
+
};
|
|
252108
|
+
}
|
|
252109
|
+
if (forceLegacyCuda) {
|
|
252110
|
+
return {
|
|
252111
|
+
args: ["torch==2.3.1", "torchaudio==2.3.1", "--index-url", "https://download.pytorch.org/whl/cu118"],
|
|
252112
|
+
description: "runtime-detected legacy CUDA GPU; using PyTorch 2.3.1 cu118 to avoid cuDNN 9 incompatibility"
|
|
252113
|
+
};
|
|
252114
|
+
}
|
|
252115
|
+
if (process.platform === "linux" && process.arch === "x64") {
|
|
252116
|
+
const gpu = detectLegacyCudaComputeCapability();
|
|
252117
|
+
if (gpu && isLegacyCudaCapability(gpu.major, gpu.minor)) {
|
|
252118
|
+
return {
|
|
252119
|
+
args: ["torch==2.3.1", "torchaudio==2.3.1", "--index-url", "https://download.pytorch.org/whl/cu118"],
|
|
252120
|
+
description: `CUDA legacy GPU SM ${gpu.major}.${gpu.minor}${gpu.name ? ` ${gpu.name}` : ""}; using PyTorch 2.3.1 cu118 to avoid cuDNN 9 incompatibility`
|
|
252121
|
+
};
|
|
252122
|
+
}
|
|
252123
|
+
}
|
|
252124
|
+
return { args: ["torch", "torchaudio"], description: "default PyTorch wheel selection" };
|
|
252125
|
+
}
|
|
252126
|
+
function withoutTorchPackages(packages) {
|
|
252127
|
+
return packages.filter((pkg) => pkg !== "torch" && pkg !== "torchaudio");
|
|
252128
|
+
}
|
|
251956
252129
|
function backendImportCheck(backend) {
|
|
251957
252130
|
if (backend === "transformers")
|
|
251958
252131
|
return "import torch, torchaudio, transformers, scipy\nfrom transformers import AutoProcessor, MusicgenForConditionalGeneration\n";
|
|
@@ -252151,6 +252324,69 @@ async function pythonCanImport2(command, code8, repoRoot, env2) {
|
|
|
252151
252324
|
async function pythonImportResult(command, code8, repoRoot, env2) {
|
|
252152
252325
|
return await runProcess3(command, ["-c", code8], { cwd: repoRoot, timeoutMs: 6e4, env: env2 });
|
|
252153
252326
|
}
|
|
252327
|
+
async function torchRuntimeCompatibilityResult(command, repoRoot, env2) {
|
|
252328
|
+
const code8 = [
|
|
252329
|
+
"import json, sys",
|
|
252330
|
+
"import torch",
|
|
252331
|
+
"payload={'torch': getattr(torch, '__version__', '?'), 'cuda_available': bool(torch.cuda.is_available())}",
|
|
252332
|
+
"if torch.cuda.is_available():",
|
|
252333
|
+
" cap=torch.cuda.get_device_capability(0)",
|
|
252334
|
+
" cudnn=torch.backends.cudnn.version() or 0",
|
|
252335
|
+
" payload.update({'capability': list(cap), 'cudnn': int(cudnn), 'device': torch.cuda.get_device_name(0)})",
|
|
252336
|
+
" if int(cudnn) >= 90000 and tuple(cap) < (7, 5):",
|
|
252337
|
+
" print(json.dumps(payload))",
|
|
252338
|
+
" raise SystemExit(42)",
|
|
252339
|
+
"print(json.dumps(payload))"
|
|
252340
|
+
].join("\n");
|
|
252341
|
+
return await runProcess3(command, ["-c", code8], { cwd: repoRoot, timeoutMs: 6e4, env: env2 });
|
|
252342
|
+
}
|
|
252343
|
+
async function repairTorchRuntime(command, repoRoot, env2, forceLegacyCuda = false, onProgress) {
|
|
252344
|
+
const plan = torchInstallPlan(forceLegacyCuda);
|
|
252345
|
+
onProgress?.({ stage: "setup", message: `Installing PyTorch runtime: ${plan.description}` });
|
|
252346
|
+
const result = await runProcess3(command, [
|
|
252347
|
+
"-m",
|
|
252348
|
+
"pip",
|
|
252349
|
+
"install",
|
|
252350
|
+
"--progress-bar",
|
|
252351
|
+
"on",
|
|
252352
|
+
"--prefer-binary",
|
|
252353
|
+
"--force-reinstall",
|
|
252354
|
+
...plan.args
|
|
252355
|
+
], {
|
|
252356
|
+
cwd: repoRoot,
|
|
252357
|
+
timeoutMs: 18e5,
|
|
252358
|
+
env: env2,
|
|
252359
|
+
progressLabel: `Installing PyTorch runtime (${plan.description})`,
|
|
252360
|
+
onProgress
|
|
252361
|
+
});
|
|
252362
|
+
if (result.code !== 0) {
|
|
252363
|
+
throw new Error(`Failed to install compatible PyTorch runtime (${plan.description}).
|
|
252364
|
+
${trimProcessText2(result.stderr || result.stdout)}`);
|
|
252365
|
+
}
|
|
252366
|
+
}
|
|
252367
|
+
async function ensureCompatibleTorchRuntime(command, repoRoot, env2, onProgress) {
|
|
252368
|
+
const existing = await torchRuntimeCompatibilityResult(command, repoRoot, env2);
|
|
252369
|
+
if (existing.code === 0)
|
|
252370
|
+
return;
|
|
252371
|
+
if (existing.code === 42) {
|
|
252372
|
+
await repairTorchRuntime(command, repoRoot, env2, true, onProgress);
|
|
252373
|
+
} else {
|
|
252374
|
+
await repairTorchRuntime(command, repoRoot, env2, false, onProgress);
|
|
252375
|
+
}
|
|
252376
|
+
const installed = await torchRuntimeCompatibilityResult(command, repoRoot, env2);
|
|
252377
|
+
if (installed.code === 0)
|
|
252378
|
+
return;
|
|
252379
|
+
if (installed.code === 42) {
|
|
252380
|
+
await repairTorchRuntime(command, repoRoot, env2, true, onProgress);
|
|
252381
|
+
const repaired = await torchRuntimeCompatibilityResult(command, repoRoot, env2);
|
|
252382
|
+
if (repaired.code === 0)
|
|
252383
|
+
return;
|
|
252384
|
+
throw new Error(`Audio-generation PyTorch runtime remains incompatible after cu118 repair.
|
|
252385
|
+
${trimProcessText2(repaired.stderr || repaired.stdout)}`);
|
|
252386
|
+
}
|
|
252387
|
+
throw new Error(`Audio-generation PyTorch runtime could not be prepared.
|
|
252388
|
+
${trimProcessText2(installed.stderr || installed.stdout)}`);
|
|
252389
|
+
}
|
|
252154
252390
|
function formatAudioSetupFailure(backend, text) {
|
|
252155
252391
|
const body = trimProcessText2(text);
|
|
252156
252392
|
const lowered = text.toLowerCase();
|
|
@@ -252161,6 +252397,9 @@ function formatAudioSetupFailure(backend, text) {
|
|
|
252161
252397
|
if (lowered.includes("cuda") && lowered.includes("not available")) {
|
|
252162
252398
|
notes2.push("CUDA was not available to the selected Python environment; install a Torch build matching this machine's CUDA runtime or use CPU-compatible settings.");
|
|
252163
252399
|
}
|
|
252400
|
+
if (lowered.includes("cudnn version") && lowered.includes("sm < 7.5")) {
|
|
252401
|
+
notes2.push("The installed PyTorch wheel uses cuDNN 9 on a legacy CUDA GPU. Omnius now repairs audio-generation venvs by reinstalling PyTorch 2.3.1 from the cu118 index for SM < 7.5 hardware.");
|
|
252402
|
+
}
|
|
252164
252403
|
return [body, ...notes2.map((note) => `
|
|
252165
252404
|
${note}`)].filter(Boolean).join("");
|
|
252166
252405
|
}
|
|
@@ -252189,9 +252428,13 @@ ${trimProcessText2(created.stderr || created.stdout)}`);
|
|
|
252189
252428
|
}
|
|
252190
252429
|
}
|
|
252191
252430
|
if (await pythonCanImport2(command, backendImportCheck(backend), repoRoot, pythonEnv)) {
|
|
252192
|
-
|
|
252431
|
+
await ensureCompatibleTorchRuntime(command, repoRoot, pythonEnv, onProgress);
|
|
252432
|
+
if (await pythonCanImport2(command, backendImportCheck(backend), repoRoot, pythonEnv)) {
|
|
252433
|
+
return { command, env: pythonEnv };
|
|
252434
|
+
}
|
|
252193
252435
|
}
|
|
252194
252436
|
const packages = backendPackages(backend);
|
|
252437
|
+
await ensureCompatibleTorchRuntime(command, repoRoot, pythonEnv, onProgress);
|
|
252195
252438
|
onProgress?.({ stage: "setup", message: `Installing ${backend} audio-generation Python packages` });
|
|
252196
252439
|
const pipArgs = [
|
|
252197
252440
|
"-m",
|
|
@@ -252203,7 +252446,7 @@ ${trimProcessText2(created.stderr || created.stdout)}`);
|
|
|
252203
252446
|
...backend === "audiocraft" ? ["--only-binary", "av"] : [],
|
|
252204
252447
|
"-U",
|
|
252205
252448
|
"pip",
|
|
252206
|
-
...packages
|
|
252449
|
+
...withoutTorchPackages(packages)
|
|
252207
252450
|
];
|
|
252208
252451
|
const pip = await runProcess3(command, pipArgs, {
|
|
252209
252452
|
cwd: repoRoot,
|
|
@@ -252220,6 +252463,12 @@ ${formatAudioSetupFailure(backend, pip.stderr || pip.stdout)}`);
|
|
|
252220
252463
|
if (importCheck.code !== 0) {
|
|
252221
252464
|
throw new Error(`Audio-generation Python environment at ${venvDir} was created, but required ${backend} imports still fail.
|
|
252222
252465
|
${formatAudioSetupFailure(backend, importCheck.stderr || importCheck.stdout)}`);
|
|
252466
|
+
}
|
|
252467
|
+
await ensureCompatibleTorchRuntime(command, repoRoot, pythonEnv, onProgress);
|
|
252468
|
+
if (!await pythonCanImport2(command, backendImportCheck(backend), repoRoot, pythonEnv)) {
|
|
252469
|
+
const retry = await pythonImportResult(command, backendImportCheck(backend), repoRoot, pythonEnv);
|
|
252470
|
+
throw new Error(`Audio-generation Python environment at ${venvDir} lost required ${backend} imports after PyTorch repair.
|
|
252471
|
+
${formatAudioSetupFailure(backend, retry.stderr || retry.stdout)}`);
|
|
252223
252472
|
}
|
|
252224
252473
|
return { command, env: pythonEnv };
|
|
252225
252474
|
}
|
|
@@ -252911,6 +253160,10 @@ def _snapshot_model(repo_id):
|
|
|
252911
253160
|
def _device():
|
|
252912
253161
|
import torch
|
|
252913
253162
|
if torch.cuda.is_available():
|
|
253163
|
+
cap = torch.cuda.get_device_capability(0)
|
|
253164
|
+
cudnn = torch.backends.cudnn.version() or 0
|
|
253165
|
+
if int(cudnn) >= 90000 and tuple(cap) < (7, 5):
|
|
253166
|
+
raise RuntimeError(f"PyTorch cuDNN {cudnn} is incompatible with CUDA device {torch.cuda.get_device_name(0)} SM {cap[0]}.{cap[1]}; recreate the audio venv or let Omnius repair it with a cu118-compatible Torch wheel")
|
|
252914
253167
|
return "cuda"
|
|
252915
253168
|
if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
|
|
252916
253169
|
return "mps"
|
|
@@ -253103,6 +253356,10 @@ def _snapshot_model(repo_id):
|
|
|
253103
253356
|
def _device():
|
|
253104
253357
|
import torch
|
|
253105
253358
|
if torch.cuda.is_available():
|
|
253359
|
+
cap = torch.cuda.get_device_capability(0)
|
|
253360
|
+
cudnn = torch.backends.cudnn.version() or 0
|
|
253361
|
+
if int(cudnn) >= 90000 and tuple(cap) < (7, 5):
|
|
253362
|
+
raise RuntimeError(f"PyTorch cuDNN {cudnn} is incompatible with CUDA device {torch.cuda.get_device_name(0)} SM {cap[0]}.{cap[1]}; recreate the audio venv or let Omnius repair it with a cu118-compatible Torch wheel")
|
|
253106
253363
|
return "cuda"
|
|
253107
253364
|
if hasattr(torch.backends, "mps") and torch.backends.mps.is_available():
|
|
253108
253365
|
return "mps"
|
|
@@ -477268,7 +477525,7 @@ var require_path_browserify = __commonJS({
|
|
|
477268
477525
|
return path11.slice(start2, end);
|
|
477269
477526
|
}
|
|
477270
477527
|
},
|
|
477271
|
-
extname: function
|
|
477528
|
+
extname: function extname17(path11) {
|
|
477272
477529
|
assertPath(path11);
|
|
477273
477530
|
var startDot = -1;
|
|
477274
477531
|
var startPart = 0;
|
|
@@ -507429,22 +507686,22 @@ Saved to: ${tempFile}`,
|
|
|
507429
507686
|
});
|
|
507430
507687
|
|
|
507431
507688
|
// packages/execution/dist/tools/audio-playback.js
|
|
507432
|
-
import { execFileSync as
|
|
507689
|
+
import { execFileSync as execFileSync3, execSync as execSync29, spawn as spawn16 } from "node:child_process";
|
|
507433
507690
|
import { copyFileSync as copyFileSync2, existsSync as existsSync40, statSync as statSync18, writeFileSync as writeFileSync16, mkdirSync as mkdirSync16, readdirSync as readdirSync14 } from "node:fs";
|
|
507434
507691
|
import { basename as basename12, extname as extname10, isAbsolute, join as join58 } from "node:path";
|
|
507435
507692
|
import { homedir as homedir14, tmpdir as tmpdir11 } from "node:os";
|
|
507436
507693
|
function hasCommand3(command) {
|
|
507437
507694
|
try {
|
|
507438
507695
|
if (process.platform === "win32") {
|
|
507439
|
-
|
|
507696
|
+
execFileSync3("where", [command], { stdio: "ignore", timeout: 2e3 });
|
|
507440
507697
|
} else {
|
|
507441
|
-
|
|
507698
|
+
execFileSync3("command", ["-v", command], { stdio: "ignore", timeout: 2e3 });
|
|
507442
507699
|
}
|
|
507443
507700
|
return true;
|
|
507444
507701
|
} catch {
|
|
507445
507702
|
if (process.platform !== "win32") {
|
|
507446
507703
|
try {
|
|
507447
|
-
|
|
507704
|
+
execFileSync3("which", [command], { stdio: "ignore", timeout: 2e3 });
|
|
507448
507705
|
return true;
|
|
507449
507706
|
} catch {
|
|
507450
507707
|
return false;
|
|
@@ -507499,7 +507756,7 @@ function playSoundFile(file, opts = {}) {
|
|
|
507499
507756
|
};
|
|
507500
507757
|
}
|
|
507501
507758
|
try {
|
|
507502
|
-
|
|
507759
|
+
execFileSync3(command.command, command.args, { timeout: opts.timeoutMs ?? 3e5, stdio: "pipe" });
|
|
507503
507760
|
return { ok: true, player: command.label };
|
|
507504
507761
|
} catch (err) {
|
|
507505
507762
|
return { ok: false, error: `Playback via ${command.label} failed: ${err instanceof Error ? err.message.slice(0, 300) : String(err).slice(0, 300)}` };
|
|
@@ -507646,13 +507903,13 @@ function ensureSupertonicInstalled() {
|
|
|
507646
507903
|
const py = findPython32();
|
|
507647
507904
|
if (!py)
|
|
507648
507905
|
throw new Error("python3 is required to set up Supertonic TTS.");
|
|
507649
|
-
|
|
507906
|
+
execFileSync3(py, ["-m", "venv", join58(voiceDir(), "supertonic3-venv")], { stdio: "pipe", timeout: 18e4 });
|
|
507650
507907
|
}
|
|
507651
507908
|
try {
|
|
507652
|
-
|
|
507909
|
+
execFileSync3(venvPy, ["-c", "import supertonic"], { stdio: "pipe", timeout: 1e4 });
|
|
507653
507910
|
} catch {
|
|
507654
|
-
|
|
507655
|
-
|
|
507911
|
+
execFileSync3(venvPy, ["-m", "pip", "install", "--quiet", "--upgrade", "pip"], { stdio: "pipe", timeout: 12e4 });
|
|
507912
|
+
execFileSync3(venvPy, ["-m", "pip", "install", "--quiet", "supertonic"], { stdio: "pipe", timeout: 6e5 });
|
|
507656
507913
|
}
|
|
507657
507914
|
mkdirSync16(voiceDir(), { recursive: true });
|
|
507658
507915
|
writeFileSync16(supertonicInferScript(), SUPERTONIC_INFER_PY, "utf-8");
|
|
@@ -507667,19 +507924,19 @@ function ensureMlxInstalled() {
|
|
|
507667
507924
|
const py = findPython32();
|
|
507668
507925
|
if (!py)
|
|
507669
507926
|
throw new Error("python3 is required to set up MLX Audio.");
|
|
507670
|
-
|
|
507927
|
+
execFileSync3(py, ["-m", "venv", join58(voiceDir(), "mlx-venv")], { stdio: "pipe", timeout: 18e4 });
|
|
507671
507928
|
}
|
|
507672
507929
|
try {
|
|
507673
|
-
|
|
507930
|
+
execFileSync3(venvPy, ["-c", "import mlx_audio"], { stdio: "pipe", timeout: 1e4 });
|
|
507674
507931
|
} catch {
|
|
507675
|
-
|
|
507676
|
-
|
|
507932
|
+
execFileSync3(venvPy, ["-m", "pip", "install", "--quiet", "--upgrade", "pip"], { stdio: "pipe", timeout: 12e4 });
|
|
507933
|
+
execFileSync3(venvPy, ["-m", "pip", "install", "--quiet", "mlx-audio"], { stdio: "pipe", timeout: 6e5 });
|
|
507677
507934
|
}
|
|
507678
507935
|
return venvPy;
|
|
507679
507936
|
}
|
|
507680
507937
|
function pythonCanImportLuxTts(venvPy) {
|
|
507681
507938
|
try {
|
|
507682
|
-
|
|
507939
|
+
execFileSync3(venvPy, [
|
|
507683
507940
|
"-c",
|
|
507684
507941
|
"import sys, os; sys.path.insert(0, os.environ['LUXTTS_REPO_PATH']); from zipvoice.luxvoice import LuxTTS; print('ok')"
|
|
507685
507942
|
], {
|
|
@@ -507693,7 +507950,7 @@ function pythonCanImportLuxTts(venvPy) {
|
|
|
507693
507950
|
}
|
|
507694
507951
|
}
|
|
507695
507952
|
function pipInstall(venvPy, packages, timeout2 = 9e5) {
|
|
507696
|
-
|
|
507953
|
+
execFileSync3(venvPy, ["-m", "pip", "install", "--prefer-binary", ...packages], {
|
|
507697
507954
|
stdio: "pipe",
|
|
507698
507955
|
timeout: timeout2,
|
|
507699
507956
|
env: process.env
|
|
@@ -507711,9 +507968,9 @@ function ensureLuxttsInstalled() {
|
|
|
507711
507968
|
if (!py)
|
|
507712
507969
|
throw new Error("python3 is required to set up LuxTTS voice cloning.");
|
|
507713
507970
|
if (!existsSync40(venvPy)) {
|
|
507714
|
-
|
|
507971
|
+
execFileSync3(py, ["-m", "venv", luxttsVenvDir()], { stdio: "pipe", timeout: 18e4 });
|
|
507715
507972
|
}
|
|
507716
|
-
|
|
507973
|
+
execFileSync3(venvPy, ["-m", "pip", "install", "--upgrade", "pip", "wheel", "setuptools<81"], {
|
|
507717
507974
|
stdio: "pipe",
|
|
507718
507975
|
timeout: 3e5
|
|
507719
507976
|
});
|
|
@@ -507721,7 +507978,7 @@ function ensureLuxttsInstalled() {
|
|
|
507721
507978
|
if (!existsSync40(join58(repoDir, "zipvoice", "luxvoice.py"))) {
|
|
507722
507979
|
if (!hasCommand3("git"))
|
|
507723
507980
|
throw new Error("git is required to set up LuxTTS voice cloning.");
|
|
507724
|
-
|
|
507981
|
+
execFileSync3("git", ["clone", "--depth", "1", "https://github.com/ysharma3501/LuxTTS.git", repoDir], {
|
|
507725
507982
|
stdio: "pipe",
|
|
507726
507983
|
timeout: 3e5
|
|
507727
507984
|
});
|
|
@@ -507761,10 +508018,10 @@ function ensurePiperInstalled() {
|
|
|
507761
508018
|
if (!py)
|
|
507762
508019
|
throw new Error("python3 is required to set up Piper TTS.");
|
|
507763
508020
|
mkdirSync16(voiceDir(), { recursive: true });
|
|
507764
|
-
|
|
508021
|
+
execFileSync3(py, ["-m", "venv", piperVenvDir()], { stdio: "pipe", timeout: 18e4 });
|
|
507765
508022
|
const venvPy = process.platform === "win32" ? join58(piperVenvDir(), "Scripts", "python.exe") : join58(piperVenvDir(), "bin", "python3");
|
|
507766
|
-
|
|
507767
|
-
|
|
508023
|
+
execFileSync3(venvPy, ["-m", "pip", "install", "--quiet", "--upgrade", "pip"], { stdio: "pipe", timeout: 12e4 });
|
|
508024
|
+
execFileSync3(venvPy, ["-m", "pip", "install", "--quiet", "piper-tts"], { stdio: "pipe", timeout: 6e5 });
|
|
507768
508025
|
}
|
|
507769
508026
|
if (!existsSync40(bin)) {
|
|
507770
508027
|
throw new Error("Piper TTS installed but the piper executable was not found in the managed venv.");
|
|
@@ -508294,7 +508551,7 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
|
|
|
508294
508551
|
"d=(np.clip(wav.cpu().numpy().squeeze(), -1, 1)*32767).astype(np.int16)",
|
|
508295
508552
|
"f=wave.open(args['output'], 'wb'); f.setnchannels(1); f.setsampwidth(2); f.setframerate(48000); f.writeframes(d.tobytes()); f.close()"
|
|
508296
508553
|
].join("; ");
|
|
508297
|
-
|
|
508554
|
+
execFileSync3(venvPy, ["-c", pyScript, JSON.stringify({ text, output: outputPath2, clone_ref: cloneRef, repo: repoDir, speed })], {
|
|
508298
508555
|
stdio: "pipe",
|
|
508299
508556
|
timeout: 12e4,
|
|
508300
508557
|
env: { ...process.env, LUXTTS_REPO_PATH: repoDir }
|
|
@@ -508307,7 +508564,7 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
|
|
|
508307
508564
|
const lang = typeof args["lang"] === "string" ? args["lang"] : "en";
|
|
508308
508565
|
const speed = numberArg3(args["speed"], 1.05);
|
|
508309
508566
|
const totalStep = Math.round(numberArg3(args["total_step"], 8));
|
|
508310
|
-
const stdout =
|
|
508567
|
+
const stdout = execFileSync3(venvPy, [supertonicInferScript()], {
|
|
508311
508568
|
input: JSON.stringify({ text, output_path: outputPath2, voice_name: voice, lang, speed, total_step: totalStep }),
|
|
508312
508569
|
encoding: "utf8",
|
|
508313
508570
|
stdio: ["pipe", "pipe", "pipe"],
|
|
@@ -508330,7 +508587,7 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
|
|
|
508330
508587
|
"args=json.loads(sys.argv[1])",
|
|
508331
508588
|
"tts_gen.main(['--model', args['model'], '--text', args['text'], '--voice', args['voice'], '--lang_code', args['lang'], '--audio_path', args['output']])"
|
|
508332
508589
|
].join("; ");
|
|
508333
|
-
|
|
508590
|
+
execFileSync3(py, ["-c", pyScript, JSON.stringify({ text, model, voice, lang, output: outputPath2 })], {
|
|
508334
508591
|
stdio: "pipe",
|
|
508335
508592
|
timeout: 18e4,
|
|
508336
508593
|
cwd: tmpdir11()
|
|
@@ -508351,7 +508608,7 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
|
|
|
508351
508608
|
} else {
|
|
508352
508609
|
throw new Error(`${requireModel ? "Raw ONNX" : "Piper"} TTS requires model=<path.onnx> or voice=<path.onnx>.`);
|
|
508353
508610
|
}
|
|
508354
|
-
|
|
508611
|
+
execFileSync3(piper, argv, { input: text, stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
|
|
508355
508612
|
return summary;
|
|
508356
508613
|
}
|
|
508357
508614
|
synthesizeEspeak(text, outputPath2, args) {
|
|
@@ -508359,7 +508616,7 @@ ${tried.map((line) => `- ${line}`).join("\n")}`,
|
|
|
508359
508616
|
throw new Error("Local fallback TTS command not found.");
|
|
508360
508617
|
const voice = typeof args["voice"] === "string" ? args["voice"] : "en";
|
|
508361
508618
|
const speed = Math.round(numberArg3(args["speed"], 160));
|
|
508362
|
-
|
|
508619
|
+
execFileSync3("espeak-ng", ["-v", voice, "-s", String(speed), "-w", outputPath2, text], {
|
|
508363
508620
|
stdio: "pipe",
|
|
508364
508621
|
timeout: 6e4
|
|
508365
508622
|
});
|
|
@@ -575505,7 +575762,7 @@ __export(image_ascii_preview_exports, {
|
|
|
575505
575762
|
extractSavedImagePath: () => extractSavedImagePath,
|
|
575506
575763
|
formatImageAsciiContext: () => formatImageAsciiContext
|
|
575507
575764
|
});
|
|
575508
|
-
import { execFileSync as
|
|
575765
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
575509
575766
|
import { createRequire as createRequire5 } from "node:module";
|
|
575510
575767
|
import { existsSync as existsSync94, readFileSync as readFileSync75, statSync as statSync32 } from "node:fs";
|
|
575511
575768
|
import { resolve as resolve37 } from "node:path";
|
|
@@ -575642,7 +575899,7 @@ function convertWithFfmpeg(imagePath, width, height, timeoutMs) {
|
|
|
575642
575899
|
`scale=${width}:${height}`,
|
|
575643
575900
|
"format=gray"
|
|
575644
575901
|
].join(",");
|
|
575645
|
-
const raw =
|
|
575902
|
+
const raw = execFileSync4(
|
|
575646
575903
|
"ffmpeg",
|
|
575647
575904
|
[
|
|
575648
575905
|
"-hide_banner",
|
|
@@ -596827,6 +597084,17 @@ var init_tool_policy = __esm({
|
|
|
596827
597084
|
"todo_write",
|
|
596828
597085
|
"web_search",
|
|
596829
597086
|
"web_fetch",
|
|
597087
|
+
"image_read",
|
|
597088
|
+
"ocr",
|
|
597089
|
+
"ocr_image_advanced",
|
|
597090
|
+
"ocr_pdf",
|
|
597091
|
+
"pdf_to_text",
|
|
597092
|
+
"vision",
|
|
597093
|
+
"transcribe_file",
|
|
597094
|
+
"video_understand",
|
|
597095
|
+
"audio_analyze",
|
|
597096
|
+
"explore_tools",
|
|
597097
|
+
"telegram_media_recent",
|
|
596830
597098
|
"generate_image",
|
|
596831
597099
|
"generate_audio",
|
|
596832
597100
|
"generate_tts",
|
|
@@ -596843,6 +597111,17 @@ var init_tool_policy = __esm({
|
|
|
596843
597111
|
"web_search",
|
|
596844
597112
|
"web_fetch",
|
|
596845
597113
|
"web_crawl",
|
|
597114
|
+
"image_read",
|
|
597115
|
+
"ocr",
|
|
597116
|
+
"ocr_image_advanced",
|
|
597117
|
+
"ocr_pdf",
|
|
597118
|
+
"pdf_to_text",
|
|
597119
|
+
"vision",
|
|
597120
|
+
"transcribe_file",
|
|
597121
|
+
"video_understand",
|
|
597122
|
+
"audio_analyze",
|
|
597123
|
+
"explore_tools",
|
|
597124
|
+
"telegram_media_recent",
|
|
596846
597125
|
"generate_image",
|
|
596847
597126
|
"generate_audio",
|
|
596848
597127
|
"generate_tts",
|
|
@@ -597417,12 +597696,12 @@ __export(vision_ingress_exports, {
|
|
|
597417
597696
|
queryVisionModel: () => queryVisionModel,
|
|
597418
597697
|
runVisionIngress: () => runVisionIngress
|
|
597419
597698
|
});
|
|
597420
|
-
import { execFileSync as
|
|
597699
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
597421
597700
|
import { existsSync as existsSync105, readFileSync as readFileSync86, unlinkSync as unlinkSync20 } from "node:fs";
|
|
597422
597701
|
import { join as join120 } from "node:path";
|
|
597423
597702
|
function isTesseractAvailable() {
|
|
597424
597703
|
try {
|
|
597425
|
-
|
|
597704
|
+
execFileSync5("tesseract", ["--version"], { stdio: "ignore", timeout: 3e3 });
|
|
597426
597705
|
return true;
|
|
597427
597706
|
} catch {
|
|
597428
597707
|
return false;
|
|
@@ -597463,7 +597742,7 @@ function advancedOcr(imagePath) {
|
|
|
597463
597742
|
for (const psm of psmModes) {
|
|
597464
597743
|
const outFile = `${tmpBase}_psm${psm}`;
|
|
597465
597744
|
try {
|
|
597466
|
-
|
|
597745
|
+
execFileSync5("tesseract", [
|
|
597467
597746
|
imagePath,
|
|
597468
597747
|
outFile,
|
|
597469
597748
|
"--psm",
|
|
@@ -597562,7 +597841,7 @@ var init_vision_ingress = __esm({
|
|
|
597562
597841
|
|
|
597563
597842
|
// packages/cli/src/tui/telegram-bridge.ts
|
|
597564
597843
|
import { mkdirSync as mkdirSync60, existsSync as existsSync106, unlinkSync as unlinkSync21, readdirSync as readdirSync36, statSync as statSync36, readFileSync as readFileSync87, writeFileSync as writeFileSync57 } from "node:fs";
|
|
597565
|
-
import { join as join121, resolve as resolve39, basename as basename23, relative as relative13, isAbsolute as isAbsolute7 } from "node:path";
|
|
597844
|
+
import { join as join121, resolve as resolve39, basename as basename23, relative as relative13, isAbsolute as isAbsolute7, extname as extname15 } from "node:path";
|
|
597566
597845
|
import { writeFile as writeFileAsync } from "node:fs/promises";
|
|
597567
597846
|
import { createHash as createHash19, randomInt } from "node:crypto";
|
|
597568
597847
|
function parseTelegramInteractionDecision(text, forcedRoute, options2 = {}) {
|
|
@@ -597760,6 +598039,19 @@ function summarizeTelegramMessageAttachments(msg) {
|
|
|
597760
598039
|
parts.push(`caption: ${truncateTelegramContextLine(msg.media.caption, 180)}`);
|
|
597761
598040
|
}
|
|
597762
598041
|
}
|
|
598042
|
+
if (msg.replyToMedia) {
|
|
598043
|
+
const details = [
|
|
598044
|
+
msg.replyToMedia.type,
|
|
598045
|
+
msg.replyToMedia.mimeType,
|
|
598046
|
+
msg.replyToMedia.fileName,
|
|
598047
|
+
msg.replyToMedia.duration ? `${msg.replyToMedia.duration}s` : "",
|
|
598048
|
+
msg.replyToMedia.fileSize ? `${msg.replyToMedia.fileSize} bytes` : ""
|
|
598049
|
+
].filter(Boolean).join(", ");
|
|
598050
|
+
parts.push(`replied-to media: ${details}`);
|
|
598051
|
+
if (msg.replyToMedia.caption) {
|
|
598052
|
+
parts.push(`replied-to caption: ${truncateTelegramContextLine(msg.replyToMedia.caption, 180)}`);
|
|
598053
|
+
}
|
|
598054
|
+
}
|
|
597763
598055
|
if (msg.poll) {
|
|
597764
598056
|
parts.push(`poll: ${truncateTelegramContextLine(msg.poll.question, 180)}`);
|
|
597765
598057
|
}
|
|
@@ -598133,6 +598425,25 @@ function telegramImageMime(media) {
|
|
|
598133
598425
|
if (ext === ".tif" || ext === ".tiff") return "image/tiff";
|
|
598134
598426
|
return "image/jpeg";
|
|
598135
598427
|
}
|
|
598428
|
+
function telegramCachedMediaIsImage(entry) {
|
|
598429
|
+
if (entry.mediaType === "photo") return true;
|
|
598430
|
+
if (entry.mimeType?.toLowerCase().startsWith("image/")) return true;
|
|
598431
|
+
return TELEGRAM_IMAGE_EXTENSIONS.has(extname15(entry.localPath).toLowerCase());
|
|
598432
|
+
}
|
|
598433
|
+
function telegramCachedMediaIsPdf(entry) {
|
|
598434
|
+
if (entry.mimeType?.toLowerCase() === "application/pdf") return true;
|
|
598435
|
+
return extname15(entry.localPath).toLowerCase() === ".pdf";
|
|
598436
|
+
}
|
|
598437
|
+
function telegramCachedMediaIsAudio(entry) {
|
|
598438
|
+
if (entry.mediaType === "audio" || entry.mediaType === "voice") return true;
|
|
598439
|
+
if (entry.mimeType?.toLowerCase().startsWith("audio/")) return true;
|
|
598440
|
+
return [".wav", ".mp3", ".flac", ".aac", ".m4a", ".ogg", ".opus"].includes(extname15(entry.localPath).toLowerCase());
|
|
598441
|
+
}
|
|
598442
|
+
function telegramCachedMediaIsVideo(entry) {
|
|
598443
|
+
if (entry.mediaType === "video" || entry.mediaType === "video_note" || entry.mediaType === "live_photo") return true;
|
|
598444
|
+
if (entry.mimeType?.toLowerCase().startsWith("video/")) return true;
|
|
598445
|
+
return [".mp4", ".mkv", ".avi", ".mov", ".webm"].includes(extname15(entry.localPath).toLowerCase());
|
|
598446
|
+
}
|
|
598136
598447
|
function isPathInside(root, path11) {
|
|
598137
598448
|
const rel = relative13(resolve39(root), resolve39(path11));
|
|
598138
598449
|
return rel === "" || Boolean(rel) && !rel.startsWith("..") && !isAbsolute7(rel);
|
|
@@ -598166,6 +598477,10 @@ function normalizeTelegramUpdate(update2) {
|
|
|
598166
598477
|
const username = message2.from?.username ?? message2.sender_chat?.username ?? "";
|
|
598167
598478
|
const chatType = message2.chat?.type ?? "private";
|
|
598168
598479
|
const media = normalizeTelegramMedia(message2);
|
|
598480
|
+
const replyTo = message2.reply_to_message && typeof message2.reply_to_message === "object" ? message2.reply_to_message : void 0;
|
|
598481
|
+
const replyToMedia = replyTo ? normalizeTelegramMedia(replyTo) : void 0;
|
|
598482
|
+
const replyToPoll = replyTo ? normalizeTelegramPoll(replyTo.poll) : void 0;
|
|
598483
|
+
const replyToText = replyTo ? replyTo.text || replyTo.caption || (replyToPoll ? formatTelegramPollSummary(replyToPoll) : "") : "";
|
|
598169
598484
|
const poll = normalizeTelegramPoll(message2.poll);
|
|
598170
598485
|
const livePhoto = normalizeTelegramLivePhoto(message2.live_photo);
|
|
598171
598486
|
const text = message2.text || message2.caption || (poll ? formatTelegramPollSummary(poll) : "");
|
|
@@ -598180,6 +598495,8 @@ function normalizeTelegramUpdate(update2) {
|
|
|
598180
598495
|
chatType,
|
|
598181
598496
|
chatTitle: message2.chat?.title,
|
|
598182
598497
|
media,
|
|
598498
|
+
replyToMedia,
|
|
598499
|
+
replyToText: replyToText || void 0,
|
|
598183
598500
|
poll,
|
|
598184
598501
|
livePhoto,
|
|
598185
598502
|
guestQueryId: typeof message2.guest_query_id === "string" ? message2.guest_query_id : void 0,
|
|
@@ -598188,9 +598505,9 @@ function normalizeTelegramUpdate(update2) {
|
|
|
598188
598505
|
isGuestMessage: sourceUpdateType === "guest_message",
|
|
598189
598506
|
isDirectMessages: Boolean(message2.chat?.is_direct_messages),
|
|
598190
598507
|
parentChatId: message2.chat?.parent_chat?.id ?? message2.direct_messages_topic?.parent_topic?.id,
|
|
598191
|
-
replyToMessageId:
|
|
598192
|
-
replyToUsername:
|
|
598193
|
-
replyToBot: Boolean(
|
|
598508
|
+
replyToMessageId: replyTo?.message_id,
|
|
598509
|
+
replyToUsername: replyTo?.from?.username ?? replyTo?.sender_chat?.username,
|
|
598510
|
+
replyToBot: Boolean(replyTo?.from?.is_bot),
|
|
598194
598511
|
mentionedUsernames: extractTelegramMentionedUsernames(message2, text),
|
|
598195
598512
|
sourceUpdateType
|
|
598196
598513
|
};
|
|
@@ -598337,7 +598654,7 @@ function renderTelegramSubAgentError(username, error) {
|
|
|
598337
598654
|
process.stdout.write(` ${c3.dim("⎿")} ${c3.red("✘")} @${username}: ${c3.dim(preview)}
|
|
598338
598655
|
`);
|
|
598339
598656
|
}
|
|
598340
|
-
var TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_PUBLIC_HELP_COMMANDS, MEDIA_CACHE_TTL_MS, TelegramBridge;
|
|
598657
|
+
var TELEGRAM_SAFETY_PROMPT, ADMIN_DM_PROMPT, ADMIN_GROUP_PROMPT, TELEGRAM_PUBLIC_SOUL_PROFILE, TELEGRAM_PUBLIC_ORCHESTRATOR_CONTRACT, TELEGRAM_PUBLIC_MEMORY_SCOPE_CONTRACT, GROUP_REPLY_DISCRETION_PROMPT, TELEGRAM_CHAT_MODE_PROMPT, ADMIN_CHAT_PROFILE_PROMPT, TELEGRAM_ACTION_RESPONSE_CONTRACT, TELEGRAM_CHAT_HISTORY_LIMIT, TELEGRAM_CONTEXT_RECENT_DEFAULT, TELEGRAM_CONTEXT_LINE_LIMIT, TELEGRAM_CONTEXT_SAMPLE_LIMIT, TELEGRAM_MEMORY_CARD_LIMIT, TELEGRAM_MEMORY_NOTE_LIMIT, TELEGRAM_MEMORY_STOPWORDS, TELEGRAM_PUBLIC_HELP_COMMANDS, TELEGRAM_IMAGE_EXTENSIONS, MEDIA_CACHE_TTL_MS, TelegramBridge;
|
|
598341
598658
|
var init_telegram_bridge = __esm({
|
|
598342
598659
|
"packages/cli/src/tui/telegram-bridge.ts"() {
|
|
598343
598660
|
"use strict";
|
|
@@ -598533,6 +598850,7 @@ Telegram response contract:
|
|
|
598533
598850
|
"your"
|
|
598534
598851
|
]);
|
|
598535
598852
|
TELEGRAM_PUBLIC_HELP_COMMANDS = /* @__PURE__ */ new Set(["help", "start", "auth", "call"]);
|
|
598853
|
+
TELEGRAM_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp", ".tiff", ".tif", ".svg"]);
|
|
598536
598854
|
MEDIA_CACHE_TTL_MS = 30 * 60 * 1e3;
|
|
598537
598855
|
TelegramBridge = class {
|
|
598538
598856
|
constructor(botToken, onMessage, agentConfig, repoRoot, toolPolicyConfig) {
|
|
@@ -598944,6 +599262,80 @@ Telegram response contract:
|
|
|
598944
599262
|
}
|
|
598945
599263
|
}
|
|
598946
599264
|
}
|
|
599265
|
+
updateLastTelegramUserMessageText(msg, text) {
|
|
599266
|
+
const sessionKey = this.sessionKeyForMessage(msg);
|
|
599267
|
+
const history = this.chatHistory.get(sessionKey);
|
|
599268
|
+
if (!history || !text.trim()) return;
|
|
599269
|
+
for (let i2 = history.length - 1; i2 >= 0; i2--) {
|
|
599270
|
+
const entry = history[i2];
|
|
599271
|
+
if (entry.role !== "user") continue;
|
|
599272
|
+
if (entry.messageId === msg.messageId || !entry.messageId && entry.text === msg.text) {
|
|
599273
|
+
entry.text = text.trim();
|
|
599274
|
+
entry.mediaSummary = summarizeTelegramMessageAttachments(msg) || entry.mediaSummary;
|
|
599275
|
+
this.updateTelegramMemoryCards(sessionKey, entry);
|
|
599276
|
+
this.saveTelegramConversationState(sessionKey);
|
|
599277
|
+
return;
|
|
599278
|
+
}
|
|
599279
|
+
}
|
|
599280
|
+
}
|
|
599281
|
+
recentTelegramMediaEntries(chatId, limit = 12) {
|
|
599282
|
+
const now = Date.now();
|
|
599283
|
+
return [...this.mediaCache.values()].filter((entry) => {
|
|
599284
|
+
if (chatId !== void 0 && String(entry.chatId) !== String(chatId)) return false;
|
|
599285
|
+
return now - entry.cachedAt <= MEDIA_CACHE_TTL_MS;
|
|
599286
|
+
}).sort((a2, b) => b.cachedAt - a2.cachedAt).slice(0, limit);
|
|
599287
|
+
}
|
|
599288
|
+
telegramMediaEntryMatchesKind(entry, kind) {
|
|
599289
|
+
if (kind === "image") return telegramCachedMediaIsImage(entry);
|
|
599290
|
+
if (kind === "pdf") return telegramCachedMediaIsPdf(entry);
|
|
599291
|
+
if (kind === "audio") return telegramCachedMediaIsAudio(entry);
|
|
599292
|
+
if (kind === "video") return telegramCachedMediaIsVideo(entry);
|
|
599293
|
+
if (kind === "transcribable") {
|
|
599294
|
+
return telegramCachedMediaIsAudio(entry) || telegramCachedMediaIsVideo(entry);
|
|
599295
|
+
}
|
|
599296
|
+
return true;
|
|
599297
|
+
}
|
|
599298
|
+
resolveTelegramScopedMediaPath(rawValue, chatId, currentMsg, kind) {
|
|
599299
|
+
const raw = String(rawValue ?? "").trim();
|
|
599300
|
+
const repoRoot = this.repoRoot || ".";
|
|
599301
|
+
const creativeRoot = telegramCreativeWorkspaceRoot(repoRoot, chatId);
|
|
599302
|
+
const mediaEntries = this.recentTelegramMediaEntries(chatId, 60).filter((entry) => this.telegramMediaEntryMatchesKind(entry, kind));
|
|
599303
|
+
const aliases = /* @__PURE__ */ new Set(["", "latest", "last", "current", "this", "that", "it", "reply", "replied", "replied-to", "replied_to"]);
|
|
599304
|
+
if (aliases.has(raw.toLowerCase())) {
|
|
599305
|
+
const replied = currentMsg?.replyToMessageId ? mediaEntries.find((entry2) => entry2.messageId === currentMsg.replyToMessageId) : void 0;
|
|
599306
|
+
const entry = replied ?? mediaEntries[0];
|
|
599307
|
+
if (!entry) {
|
|
599308
|
+
return { ok: false, error: `No recent ${kind} media is available in this Telegram chat scope.` };
|
|
599309
|
+
}
|
|
599310
|
+
return { ok: true, path: entry.localPath };
|
|
599311
|
+
}
|
|
599312
|
+
const matchingEntry = mediaEntries.find((entry) => {
|
|
599313
|
+
if (resolve39(entry.localPath) === resolve39(raw)) return true;
|
|
599314
|
+
if (basename23(entry.localPath) === raw) return true;
|
|
599315
|
+
if (entry.fileUniqueId === raw || entry.fileId === raw) return true;
|
|
599316
|
+
if (entry.messageId && String(entry.messageId) === raw) return true;
|
|
599317
|
+
return false;
|
|
599318
|
+
});
|
|
599319
|
+
if (matchingEntry) return { ok: true, path: matchingEntry.localPath };
|
|
599320
|
+
const creativeCandidate = isAbsolute7(raw) ? resolve39(raw) : resolve39(creativeRoot, raw);
|
|
599321
|
+
if (isPathInside(creativeRoot, creativeCandidate) && existsSync106(creativeCandidate)) {
|
|
599322
|
+
return { ok: true, path: creativeCandidate };
|
|
599323
|
+
}
|
|
599324
|
+
return {
|
|
599325
|
+
ok: false,
|
|
599326
|
+
error: `Path is outside this Telegram chat's media/workspace scope or does not exist: ${raw || "(empty)"}`
|
|
599327
|
+
};
|
|
599328
|
+
}
|
|
599329
|
+
resolveTelegramScopedOutputPath(rawValue, chatId, fallbackName) {
|
|
599330
|
+
const repoRoot = this.repoRoot || ".";
|
|
599331
|
+
const creativeRoot = telegramCreativeWorkspaceRoot(repoRoot, chatId);
|
|
599332
|
+
const raw = String(rawValue || fallbackName).trim() || fallbackName;
|
|
599333
|
+
const outputPath2 = isAbsolute7(raw) ? resolve39(raw) : resolve39(creativeRoot, raw);
|
|
599334
|
+
if (!isPathInside(creativeRoot, outputPath2)) {
|
|
599335
|
+
return { ok: false, error: `Output path must stay inside this Telegram chat's creative workspace: ${raw}` };
|
|
599336
|
+
}
|
|
599337
|
+
return { ok: true, path: outputPath2 };
|
|
599338
|
+
}
|
|
598947
599339
|
updateTelegramParticipantProfile(sessionKey, msg, text) {
|
|
598948
599340
|
const participantKey = String(msg.fromUserId || msg.username || msg.firstName || "unknown");
|
|
598949
599341
|
const participants = this.chatParticipants.get(sessionKey) ?? /* @__PURE__ */ new Map();
|
|
@@ -599118,6 +599510,22 @@ ${notes2}`;
|
|
|
599118
599510
|
sections.push(`### Zettelkasten Memory Recall
|
|
599119
599511
|
${cardLines.join("\n")}`);
|
|
599120
599512
|
}
|
|
599513
|
+
const recentMedia = this.recentTelegramMediaEntries(msg.chatId, 10);
|
|
599514
|
+
if (recentMedia.length > 0) {
|
|
599515
|
+
const mediaLines = recentMedia.map((entry) => {
|
|
599516
|
+
const kind = telegramCachedMediaIsImage(entry) ? "image" : entry.mediaType;
|
|
599517
|
+
const replyMark = msg.replyToMessageId && entry.messageId === msg.replyToMessageId ? " replied-to" : "";
|
|
599518
|
+
const caption = entry.caption ? ` caption:${truncateTelegramContextLine(entry.caption, 120)}` : "";
|
|
599519
|
+
const extracted = entry.extractedContent ? `
|
|
599520
|
+
${truncateTelegramContextLine(entry.extractedContent.replace(/\s+/g, " "), 220)}` : "";
|
|
599521
|
+
return `- message_id ${entry.messageId}${replyMark}: ${kind}; path ${entry.localPath}; file ${basename23(entry.localPath)}${caption}${extracted}`;
|
|
599522
|
+
});
|
|
599523
|
+
sections.push([
|
|
599524
|
+
"### Recent Chat Media",
|
|
599525
|
+
"Use these paths only as tool inputs when the user asks about media in this chat. Do not quote local paths in the visible Telegram reply.",
|
|
599526
|
+
mediaLines.join("\n")
|
|
599527
|
+
].join("\n"));
|
|
599528
|
+
}
|
|
599121
599529
|
if (olderCount > 0) {
|
|
599122
599530
|
const older = history.slice(0, olderCount);
|
|
599123
599531
|
const bySpeaker = /* @__PURE__ */ new Map();
|
|
@@ -599814,8 +600222,8 @@ Join: ${newUrl}`);
|
|
|
599814
600222
|
}
|
|
599815
600223
|
}
|
|
599816
600224
|
let steeringText = msg.text;
|
|
599817
|
-
if (msg.media) {
|
|
599818
|
-
const mediaContext = await this.
|
|
600225
|
+
if (msg.media || msg.replyToMedia) {
|
|
600226
|
+
const mediaContext = await this.processMediaContextForMessage(msg);
|
|
599819
600227
|
if (mediaContext) {
|
|
599820
600228
|
steeringText += `
|
|
599821
600229
|
|
|
@@ -599889,8 +600297,8 @@ ${mediaContext}`;
|
|
|
599889
600297
|
this.tuiWrite(() => renderTelegramSubAgentStart(msg.username, msg.text, isAdminDM));
|
|
599890
600298
|
try {
|
|
599891
600299
|
let mediaContext = "";
|
|
599892
|
-
if (msg.media) {
|
|
599893
|
-
mediaContext = await this.
|
|
600300
|
+
if (msg.media || msg.replyToMedia) {
|
|
600301
|
+
mediaContext = await this.processMediaContextForMessage(msg);
|
|
599894
600302
|
}
|
|
599895
600303
|
const result = await this.runSubAgent(msg, subAgent, mediaContext);
|
|
599896
600304
|
if (subAgent.typingInterval) {
|
|
@@ -599992,8 +600400,8 @@ ${mediaContext}`;
|
|
|
599992
600400
|
this.tuiWrite(() => renderTelegramSubAgentEvent(msg.username, `admin chat with full context/tools (${this.interactionMode})`));
|
|
599993
600401
|
try {
|
|
599994
600402
|
let mediaContext = "";
|
|
599995
|
-
if (msg.media) {
|
|
599996
|
-
mediaContext = await this.
|
|
600403
|
+
if (msg.media || msg.replyToMedia) {
|
|
600404
|
+
mediaContext = await this.processMediaContextForMessage(msg);
|
|
599997
600405
|
}
|
|
599998
600406
|
const result = await this.runSubAgent(msg, subAgent, mediaContext, "chat");
|
|
599999
600407
|
if (subAgent.typingInterval) {
|
|
@@ -600076,7 +600484,7 @@ ${mediaContext}`;
|
|
|
600076
600484
|
}
|
|
600077
600485
|
this.tuiWrite(() => renderTelegramSubAgentEvent(msg.username, `live inference: chat reply (${this.interactionMode})`));
|
|
600078
600486
|
try {
|
|
600079
|
-
const mediaContext = msg.media || msg.
|
|
600487
|
+
const mediaContext = msg.media || msg.replyToMedia || msg.livePhoto ? await this.processMediaContextForMessage(msg) : "";
|
|
600080
600488
|
const finalText = await this.runTelegramChatCompletion(
|
|
600081
600489
|
msg,
|
|
600082
600490
|
toolContext,
|
|
@@ -600569,6 +600977,128 @@ ${lines.join("\n\n")}` };
|
|
|
600569
600977
|
}
|
|
600570
600978
|
};
|
|
600571
600979
|
}
|
|
600980
|
+
if (tool.name === "image_read") {
|
|
600981
|
+
return {
|
|
600982
|
+
...tool,
|
|
600983
|
+
description: "Read only images from this Telegram chat's media cache or creative workspace. Use path='reply' for the replied-to image or path='latest' for the most recent chat image.",
|
|
600984
|
+
execute: async (args) => {
|
|
600985
|
+
const resolved = this.resolveTelegramScopedMediaPath(args["path"], chatId, currentMsg, "image");
|
|
600986
|
+
if (!resolved.ok) return { success: false, output: "", error: resolved.error };
|
|
600987
|
+
return tool.execute({ ...args, path: resolved.path });
|
|
600988
|
+
}
|
|
600989
|
+
};
|
|
600990
|
+
}
|
|
600991
|
+
if (tool.name === "ocr") {
|
|
600992
|
+
return {
|
|
600993
|
+
...tool,
|
|
600994
|
+
description: "Extract text only from images in this Telegram chat's media cache or creative workspace. Use path='reply' or path='latest' for chat media references.",
|
|
600995
|
+
execute: async (args) => {
|
|
600996
|
+
const resolved = this.resolveTelegramScopedMediaPath(args["path"], chatId, currentMsg, "image");
|
|
600997
|
+
if (!resolved.ok) return { success: false, output: "", error: resolved.error };
|
|
600998
|
+
return tool.execute({ ...args, path: resolved.path });
|
|
600999
|
+
}
|
|
601000
|
+
};
|
|
601001
|
+
}
|
|
601002
|
+
if (tool.name === "vision") {
|
|
601003
|
+
return {
|
|
601004
|
+
...tool,
|
|
601005
|
+
description: "Analyze only images from this Telegram chat's media cache or creative workspace. Use image='reply' for the replied-to image or image='latest' for the most recent chat image.",
|
|
601006
|
+
execute: async (args) => {
|
|
601007
|
+
const resolved = this.resolveTelegramScopedMediaPath(args["image"], chatId, currentMsg, "image");
|
|
601008
|
+
if (!resolved.ok) return { success: false, output: "", error: resolved.error };
|
|
601009
|
+
return tool.execute({ ...args, image: resolved.path });
|
|
601010
|
+
}
|
|
601011
|
+
};
|
|
601012
|
+
}
|
|
601013
|
+
if (tool.name === "ocr_image_advanced") {
|
|
601014
|
+
return {
|
|
601015
|
+
...tool,
|
|
601016
|
+
description: "Advanced OCR only for images in this Telegram chat's media cache or creative workspace. Batch directory mode is disabled in public Telegram scope.",
|
|
601017
|
+
execute: async (args) => {
|
|
601018
|
+
if (args["batch"] === true) return { success: false, output: "", error: "Batch directory OCR is not available in public Telegram scope." };
|
|
601019
|
+
const resolved = this.resolveTelegramScopedMediaPath(args["image"], chatId, currentMsg, "image");
|
|
601020
|
+
if (!resolved.ok) return { success: false, output: "", error: resolved.error };
|
|
601021
|
+
const next = { ...args, image: resolved.path };
|
|
601022
|
+
if (typeof next["output_dir"] === "string" && next["output_dir"].trim()) {
|
|
601023
|
+
const output = this.resolveTelegramScopedOutputPath(next["output_dir"], chatId, "ocr-output");
|
|
601024
|
+
if (!output.ok) return { success: false, output: "", error: output.error };
|
|
601025
|
+
next["output_dir"] = output.path;
|
|
601026
|
+
}
|
|
601027
|
+
return tool.execute(next);
|
|
601028
|
+
}
|
|
601029
|
+
};
|
|
601030
|
+
}
|
|
601031
|
+
if (tool.name === "transcribe_file") {
|
|
601032
|
+
return {
|
|
601033
|
+
...tool,
|
|
601034
|
+
description: "Transcribe only audio/video files from this Telegram chat's media cache or creative workspace. Use path='reply' or path='latest' for chat media references.",
|
|
601035
|
+
execute: async (args) => {
|
|
601036
|
+
const resolved = this.resolveTelegramScopedMediaPath(args["path"], chatId, currentMsg, "transcribable");
|
|
601037
|
+
if (!resolved.ok) return { success: false, output: "", error: resolved.error };
|
|
601038
|
+
return tool.execute({ ...args, path: resolved.path });
|
|
601039
|
+
}
|
|
601040
|
+
};
|
|
601041
|
+
}
|
|
601042
|
+
if (tool.name === "pdf_to_text") {
|
|
601043
|
+
return {
|
|
601044
|
+
...tool,
|
|
601045
|
+
description: "Extract text only from PDFs in this Telegram chat's media cache or creative workspace. Use path='reply' or path='latest' for chat document references.",
|
|
601046
|
+
execute: async (args) => {
|
|
601047
|
+
const resolved = this.resolveTelegramScopedMediaPath(args["path"], chatId, currentMsg, "pdf");
|
|
601048
|
+
if (!resolved.ok) return { success: false, output: "", error: resolved.error };
|
|
601049
|
+
return tool.execute({ ...args, path: resolved.path });
|
|
601050
|
+
}
|
|
601051
|
+
};
|
|
601052
|
+
}
|
|
601053
|
+
if (tool.name === "ocr_pdf") {
|
|
601054
|
+
return {
|
|
601055
|
+
...tool,
|
|
601056
|
+
description: "OCR only PDFs from this Telegram chat's media cache or creative workspace. Output, when requested, is forced into this chat's creative workspace.",
|
|
601057
|
+
execute: async (args) => {
|
|
601058
|
+
const input = this.resolveTelegramScopedMediaPath(args["input"], chatId, currentMsg, "pdf");
|
|
601059
|
+
if (!input.ok) return { success: false, output: "", error: input.error };
|
|
601060
|
+
const next = { ...args, input: input.path };
|
|
601061
|
+
if (typeof next["output"] === "string" && next["output"].trim()) {
|
|
601062
|
+
const output = this.resolveTelegramScopedOutputPath(next["output"], chatId, `ocr-${Date.now()}.pdf`);
|
|
601063
|
+
if (!output.ok) return { success: false, output: "", error: output.error };
|
|
601064
|
+
next["output"] = output.path;
|
|
601065
|
+
}
|
|
601066
|
+
return tool.execute(next);
|
|
601067
|
+
}
|
|
601068
|
+
};
|
|
601069
|
+
}
|
|
601070
|
+
if (tool.name === "video_understand") {
|
|
601071
|
+
return {
|
|
601072
|
+
...tool,
|
|
601073
|
+
description: "Analyze only video files from this Telegram chat's media cache or creative workspace. URL download is disabled in public Telegram scope; use path='reply' or path='latest'.",
|
|
601074
|
+
execute: async (args) => {
|
|
601075
|
+
if (args["url"]) return { success: false, output: "", error: "URL video analysis is not available in public Telegram scope. Use a video posted in this chat." };
|
|
601076
|
+
const resolved = this.resolveTelegramScopedMediaPath(args["path"], chatId, currentMsg, "video");
|
|
601077
|
+
if (!resolved.ok) return { success: false, output: "", error: resolved.error };
|
|
601078
|
+
return tool.execute({ ...args, path: resolved.path });
|
|
601079
|
+
}
|
|
601080
|
+
};
|
|
601081
|
+
}
|
|
601082
|
+
if (tool.name === "audio_analyze") {
|
|
601083
|
+
return {
|
|
601084
|
+
...tool,
|
|
601085
|
+
description: "Analyze only audio files from this Telegram chat's media cache or creative workspace. Microphone/listen mode is disabled in public Telegram scope.",
|
|
601086
|
+
execute: async (args) => {
|
|
601087
|
+
if (String(args["action"] || "").toLowerCase() === "listen") {
|
|
601088
|
+
return { success: false, output: "", error: "Continuous microphone listening is not available in Telegram public scope." };
|
|
601089
|
+
}
|
|
601090
|
+
const resolved = this.resolveTelegramScopedMediaPath(args["file"] ?? args["path"], chatId, currentMsg, "audio");
|
|
601091
|
+
if (!resolved.ok) return { success: false, output: "", error: resolved.error };
|
|
601092
|
+
return tool.execute({ ...args, file: resolved.path, path: resolved.path });
|
|
601093
|
+
}
|
|
601094
|
+
};
|
|
601095
|
+
}
|
|
601096
|
+
if (tool.name === "explore_tools") {
|
|
601097
|
+
return {
|
|
601098
|
+
...tool,
|
|
601099
|
+
description: "List and explain the tools available in this Telegram public/group scope. Do not invent unavailable tool names."
|
|
601100
|
+
};
|
|
601101
|
+
}
|
|
600572
601102
|
return tool;
|
|
600573
601103
|
});
|
|
600574
601104
|
}
|
|
@@ -600732,11 +601262,16 @@ Scoped workspace: ${scopedRoot}`,
|
|
|
600732
601262
|
new ImageReadTool(repoRoot),
|
|
600733
601263
|
new OCRTool(repoRoot),
|
|
600734
601264
|
new VisionTool(repoRoot),
|
|
601265
|
+
new OcrImageAdvancedTool(repoRoot),
|
|
600735
601266
|
new OcrPdfTool(repoRoot),
|
|
600736
601267
|
new PdfToTextTool(repoRoot),
|
|
600737
601268
|
// Transcription tools
|
|
600738
601269
|
new TranscribeFileTool(repoRoot),
|
|
600739
|
-
new TranscribeUrlTool(repoRoot)
|
|
601270
|
+
new TranscribeUrlTool(repoRoot),
|
|
601271
|
+
new VideoUnderstandTool(repoRoot),
|
|
601272
|
+
new AudioAnalyzeTool(),
|
|
601273
|
+
new ExploreToolsTool(),
|
|
601274
|
+
this.buildTelegramMediaRecentTool(chatId, msg)
|
|
600740
601275
|
];
|
|
600741
601276
|
const adminTools = [
|
|
600742
601277
|
new ShellTool(repoRoot),
|
|
@@ -600839,6 +601374,55 @@ Scoped workspace: ${scopedRoot}`,
|
|
|
600839
601374
|
]);
|
|
600840
601375
|
return tools.filter((tool) => !blocked.has(tool.name));
|
|
600841
601376
|
}
|
|
601377
|
+
buildTelegramMediaRecentTool(chatId, currentMsg) {
|
|
601378
|
+
const bridge = this;
|
|
601379
|
+
return {
|
|
601380
|
+
name: "telegram_media_recent",
|
|
601381
|
+
description: "List recent media files available in this Telegram chat scope, including safe aliases for image_read, ocr, vision, transcribe_file, pdf_to_text, video_understand, and audio_analyze.",
|
|
601382
|
+
parameters: {
|
|
601383
|
+
type: "object",
|
|
601384
|
+
properties: {
|
|
601385
|
+
kind: {
|
|
601386
|
+
type: "string",
|
|
601387
|
+
enum: ["media", "image", "audio", "video", "pdf", "transcribable"],
|
|
601388
|
+
description: "Filter by media kind. Defaults to all recent chat media."
|
|
601389
|
+
},
|
|
601390
|
+
limit: { type: "number", description: "Maximum entries to return, 1-20. Default: 10." }
|
|
601391
|
+
}
|
|
601392
|
+
},
|
|
601393
|
+
async execute(args) {
|
|
601394
|
+
const start2 = performance.now();
|
|
601395
|
+
const kind = String(args["kind"] || "media").toLowerCase();
|
|
601396
|
+
const limit = typeof args["limit"] === "number" && Number.isFinite(args["limit"]) ? Math.max(1, Math.min(20, Math.floor(args["limit"]))) : 10;
|
|
601397
|
+
const entries = bridge.recentTelegramMediaEntries(chatId, 60).filter((entry) => bridge.telegramMediaEntryMatchesKind(entry, kind)).slice(0, limit);
|
|
601398
|
+
if (entries.length === 0) {
|
|
601399
|
+
return { success: true, output: `No recent ${kind} media is available in this Telegram chat scope.`, durationMs: performance.now() - start2 };
|
|
601400
|
+
}
|
|
601401
|
+
const lines = entries.map((entry, index) => {
|
|
601402
|
+
const parts = [
|
|
601403
|
+
`${index + 1}. message_id ${entry.messageId || "unknown"}`,
|
|
601404
|
+
currentMsg?.replyToMessageId === entry.messageId ? "replied-to" : "",
|
|
601405
|
+
telegramCachedMediaIsImage(entry) ? "image" : telegramCachedMediaIsPdf(entry) ? "pdf" : telegramCachedMediaIsAudio(entry) ? "audio" : telegramCachedMediaIsVideo(entry) ? "video" : entry.mediaType,
|
|
601406
|
+
`file=${basename23(entry.localPath)}`,
|
|
601407
|
+
`path=${entry.localPath}`,
|
|
601408
|
+
entry.caption ? `caption=${truncateTelegramContextLine(entry.caption, 140)}` : ""
|
|
601409
|
+
].filter(Boolean);
|
|
601410
|
+
const extracted = entry.extractedContent ? `
|
|
601411
|
+
context: ${truncateTelegramContextLine(entry.extractedContent.replace(/\s+/g, " "), 240)}` : "";
|
|
601412
|
+
return `${parts.join("; ")}${extracted}`;
|
|
601413
|
+
});
|
|
601414
|
+
return {
|
|
601415
|
+
success: true,
|
|
601416
|
+
output: [
|
|
601417
|
+
"Recent scoped Telegram media:",
|
|
601418
|
+
"Use path='reply' for replied-to media, path='latest' for the most recent matching item, or one of the listed paths.",
|
|
601419
|
+
lines.join("\n")
|
|
601420
|
+
].join("\n"),
|
|
601421
|
+
durationMs: performance.now() - start2
|
|
601422
|
+
};
|
|
601423
|
+
}
|
|
601424
|
+
};
|
|
601425
|
+
}
|
|
600842
601426
|
imageGenerationDefaultsForRepo(repoRoot) {
|
|
600843
601427
|
const settings = resolveSettings(repoRoot);
|
|
600844
601428
|
return {
|
|
@@ -601056,30 +601640,36 @@ ${knownList}` : "Private-user telegram_send_file target must be this DM or a kno
|
|
|
601056
601640
|
* Downloads the file, runs it through the appropriate pipeline,
|
|
601057
601641
|
* caches it, and returns a text description for the agent.
|
|
601058
601642
|
*/
|
|
601059
|
-
async processMedia(msg) {
|
|
601060
|
-
|
|
601061
|
-
|
|
601062
|
-
const
|
|
601643
|
+
async processMedia(msg, source = "message") {
|
|
601644
|
+
const media = source === "reply" ? msg.replyToMedia : msg.media;
|
|
601645
|
+
if (!media) return "";
|
|
601646
|
+
const { type, fileId, fileUniqueId, mimeType, caption } = media;
|
|
601647
|
+
const isImageMedia = telegramMediaIsImage(media);
|
|
601648
|
+
const sourceMessageId = source === "reply" ? msg.replyToMessageId : msg.messageId;
|
|
601649
|
+
const sourceLabel = source === "reply" ? "replied-to " : "";
|
|
601063
601650
|
let ext = ".bin";
|
|
601064
|
-
if (isImageMedia) ext = telegramImageExtension(
|
|
601651
|
+
if (isImageMedia) ext = telegramImageExtension(media);
|
|
601065
601652
|
else if (type === "audio" || type === "voice") ext = ".ogg";
|
|
601066
601653
|
else if (type === "video" || type === "video_note" || type === "live_photo") ext = ".mp4";
|
|
601067
|
-
else if (
|
|
601068
|
-
const dotIdx =
|
|
601069
|
-
if (dotIdx >= 0) ext =
|
|
601654
|
+
else if (media.fileName) {
|
|
601655
|
+
const dotIdx = media.fileName.lastIndexOf(".");
|
|
601656
|
+
if (dotIdx >= 0) ext = media.fileName.slice(dotIdx);
|
|
601070
601657
|
}
|
|
601071
601658
|
const localPath = await this.downloadTelegramFile(fileId, ext);
|
|
601072
601659
|
if (!localPath) return `[Media: ${type} — failed to download]`;
|
|
601073
601660
|
const cacheEntry = {
|
|
601074
601661
|
localPath,
|
|
601075
601662
|
fileId,
|
|
601663
|
+
fileUniqueId,
|
|
601076
601664
|
chatId: msg.chatId,
|
|
601665
|
+
messageId: sourceMessageId ?? 0,
|
|
601077
601666
|
username: msg.username,
|
|
601078
601667
|
mediaType: type,
|
|
601079
601668
|
mimeType,
|
|
601669
|
+
caption,
|
|
601080
601670
|
cachedAt: Date.now()
|
|
601081
601671
|
};
|
|
601082
|
-
this.mediaCache.set(fileUniqueId
|
|
601672
|
+
this.mediaCache.set(`${String(msg.chatId)}:${String(sourceMessageId ?? 0)}:${fileUniqueId}`, cacheEntry);
|
|
601083
601673
|
const metadataKey = String(msg.chatId);
|
|
601084
601674
|
if (!this.mediaMetadata.has(metadataKey)) {
|
|
601085
601675
|
this.mediaMetadata.set(metadataKey, []);
|
|
@@ -601100,7 +601690,7 @@ ${knownList}` : "Private-user telegram_send_file target must be this DM or a kno
|
|
|
601100
601690
|
{
|
|
601101
601691
|
path: localPath,
|
|
601102
601692
|
buffer: readFileSync87(localPath),
|
|
601103
|
-
mime: telegramImageMime(
|
|
601693
|
+
mime: telegramImageMime(media)
|
|
601104
601694
|
},
|
|
601105
601695
|
this.agentConfig?.model ?? ""
|
|
601106
601696
|
);
|
|
@@ -601109,10 +601699,10 @@ ${knownList}` : "Private-user telegram_send_file target must be this DM or a kno
|
|
|
601109
601699
|
} catch {
|
|
601110
601700
|
}
|
|
601111
601701
|
if (visionContext) {
|
|
601112
|
-
description = `[
|
|
601702
|
+
description = `[${sourceLabel}image received: ${localPath}${caption ? ` — caption: "${caption}"` : ""}
|
|
601113
601703
|
${visionContext}]`;
|
|
601114
601704
|
} else {
|
|
601115
|
-
description = `[
|
|
601705
|
+
description = `[${sourceLabel}image received and saved to ${localPath}${caption ? ` — caption: "${caption}"` : ""}. You can use image_read, ocr, or vision tools to analyze it.]`;
|
|
601116
601706
|
}
|
|
601117
601707
|
try {
|
|
601118
601708
|
await fetch("http://127.0.0.1:11435/v1/memory/ingest", {
|
|
@@ -601136,9 +601726,9 @@ ${visionContext}]`;
|
|
|
601136
601726
|
} catch {
|
|
601137
601727
|
}
|
|
601138
601728
|
if (transcription) {
|
|
601139
|
-
description = `[
|
|
601729
|
+
description = `[${sourceLabel}voice message transcribed: "${transcription}"${caption ? ` — caption: "${caption}"` : ""}]`;
|
|
601140
601730
|
} else {
|
|
601141
|
-
description = `[
|
|
601731
|
+
description = `[${sourceLabel}audio/voice message received and saved to ${localPath}${caption ? ` — caption: "${caption}"` : ""}. You can use transcribe_file to transcribe it.]`;
|
|
601142
601732
|
}
|
|
601143
601733
|
try {
|
|
601144
601734
|
await fetch("http://127.0.0.1:11435/v1/memory/ingest", {
|
|
@@ -601151,13 +601741,30 @@ ${visionContext}]`;
|
|
|
601151
601741
|
}
|
|
601152
601742
|
} else if (type === "video" || type === "video_note" || type === "live_photo") {
|
|
601153
601743
|
const label = type === "live_photo" ? "Live photo" : "Video";
|
|
601154
|
-
description = `[${label} received and saved to ${localPath}${caption ? ` — caption: "${caption}"` : ""}.]`;
|
|
601744
|
+
description = `[${sourceLabel}${label.toLowerCase()} received and saved to ${localPath}${caption ? ` — caption: "${caption}"` : ""}. You can use video_understand or transcribe_file to analyze it.]`;
|
|
601155
601745
|
} else if (type === "document") {
|
|
601156
|
-
description = `[
|
|
601746
|
+
description = `[${sourceLabel}document received: ${media.fileName || "unnamed"}${mimeType ? ` (${mimeType})` : ""}, saved to ${localPath}${caption ? ` — caption: "${caption}"` : ""}.]`;
|
|
601157
601747
|
}
|
|
601158
601748
|
cacheEntry.extractedContent = description;
|
|
601159
601749
|
return description;
|
|
601160
601750
|
}
|
|
601751
|
+
async processMediaContextForMessage(msg) {
|
|
601752
|
+
const parts = [];
|
|
601753
|
+
if (msg.media) {
|
|
601754
|
+
const current = await this.processMedia(msg, "message");
|
|
601755
|
+
if (current) parts.push(current);
|
|
601756
|
+
}
|
|
601757
|
+
if (msg.replyToMedia) {
|
|
601758
|
+
const replied = await this.processMedia(msg, "reply");
|
|
601759
|
+
if (replied) parts.push(replied);
|
|
601760
|
+
}
|
|
601761
|
+
const text = parts.join("\n\n");
|
|
601762
|
+
if (text) this.updateLastTelegramUserMessageText(msg, `${msg.text}
|
|
601763
|
+
|
|
601764
|
+
[Media context]
|
|
601765
|
+
${text}`.trim());
|
|
601766
|
+
return text;
|
|
601767
|
+
}
|
|
601161
601768
|
/** Clean up expired media cache entries (older than 30 minutes) */
|
|
601162
601769
|
cleanupMediaCache() {
|
|
601163
601770
|
const now = Date.now();
|
|
@@ -625743,7 +626350,7 @@ var clipboard_media_exports = {};
|
|
|
625743
626350
|
__export(clipboard_media_exports, {
|
|
625744
626351
|
pasteClipboardImageToFile: () => pasteClipboardImageToFile
|
|
625745
626352
|
});
|
|
625746
|
-
import { execFileSync as
|
|
626353
|
+
import { execFileSync as execFileSync6, execSync as execSync58 } from "node:child_process";
|
|
625747
626354
|
import { mkdirSync as mkdirSync72, readFileSync as readFileSync99, rmSync as rmSync5, writeFileSync as writeFileSync67 } from "node:fs";
|
|
625748
626355
|
import { join as join136 } from "node:path";
|
|
625749
626356
|
function pasteClipboardImageToFile(repoRoot) {
|
|
@@ -625760,7 +626367,7 @@ function readClipboardImage() {
|
|
|
625760
626367
|
try {
|
|
625761
626368
|
execSync58("command -v pngpaste", { stdio: "ignore", timeout: 1e3 });
|
|
625762
626369
|
const tmp = `/tmp/omnius-clipboard-${Date.now()}.png`;
|
|
625763
|
-
|
|
626370
|
+
execFileSync6("pngpaste", [tmp], { timeout: 3e3 });
|
|
625764
626371
|
const buffer2 = readFileSync99(tmp);
|
|
625765
626372
|
try {
|
|
625766
626373
|
rmSync5(tmp);
|
|
@@ -625780,7 +626387,7 @@ function readClipboardImage() {
|
|
|
625780
626387
|
];
|
|
625781
626388
|
for (const attempt of attempts) {
|
|
625782
626389
|
try {
|
|
625783
|
-
const buffer2 =
|
|
626390
|
+
const buffer2 = execFileSync6(attempt.cmd, attempt.args, { timeout: 3e3, maxBuffer: 25 * 1024 * 1024 });
|
|
625784
626391
|
if (buffer2.length > 0) return { buffer: buffer2, mime: attempt.mime, ext: attempt.ext };
|
|
625785
626392
|
} catch {
|
|
625786
626393
|
continue;
|
|
@@ -625797,7 +626404,7 @@ function readClipboardImage() {
|
|
|
625797
626404
|
"$img.Save($ms,[Drawing.Imaging.ImageFormat]::Png);",
|
|
625798
626405
|
"[Console]::OpenStandardOutput().Write($ms.ToArray(),0,$ms.Length)"
|
|
625799
626406
|
].join("");
|
|
625800
|
-
const buffer2 =
|
|
626407
|
+
const buffer2 = execFileSync6("powershell.exe", ["-NoProfile", "-Command", ps], {
|
|
625801
626408
|
timeout: 5e3,
|
|
625802
626409
|
maxBuffer: 25 * 1024 * 1024
|
|
625803
626410
|
});
|
|
@@ -625816,7 +626423,7 @@ var init_clipboard_media = __esm({
|
|
|
625816
626423
|
|
|
625817
626424
|
// packages/cli/src/tui/interactive.ts
|
|
625818
626425
|
import { cwd } from "node:process";
|
|
625819
|
-
import { resolve as resolve44, join as join137, dirname as dirname38, extname as
|
|
626426
|
+
import { resolve as resolve44, join as join137, dirname as dirname38, extname as extname16, relative as relative14 } from "node:path";
|
|
625820
626427
|
import { createRequire as createRequire8 } from "node:module";
|
|
625821
626428
|
import { fileURLToPath as fileURLToPath18 } from "node:url";
|
|
625822
626429
|
import {
|
|
@@ -633118,7 +633725,7 @@ Execute this skill now. Follow the behavioral guidance above.`;
|
|
|
633118
633725
|
const imgPath = resolve44(repoRoot, cleanPath);
|
|
633119
633726
|
const imgBuffer = readFileSync100(imgPath);
|
|
633120
633727
|
const base642 = imgBuffer.toString("base64");
|
|
633121
|
-
const ext =
|
|
633728
|
+
const ext = extname16(cleanPath).toLowerCase();
|
|
633122
633729
|
const mime = ext === ".png" ? "image/png" : ext === ".gif" ? "image/gif" : ext === ".webp" ? "image/webp" : "image/jpeg";
|
|
633123
633730
|
const asciiContext = await renderAsciiPreviewForImage(
|
|
633124
633731
|
imgPath,
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "omnius",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.22",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED