omnius 1.0.10 → 1.0.12
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/README.md +2 -2
- package/dist/index.js +424 -77
- package/dist/postinstall-daemon.cjs +15 -0
- package/dist/preinstall.cjs +25 -1
- package/node_modules/image-to-ascii/bin/image-to-ascii.cjs +18 -0
- package/node_modules/image-to-ascii/index.cjs +257 -0
- package/node_modules/image-to-ascii/index.d.ts +48 -0
- package/node_modules/image-to-ascii/package.json +22 -0
- package/npm-shrinkwrap.json +18 -557
- package/package.json +7 -3
- package/vendor/image-to-ascii/bin/image-to-ascii.cjs +18 -0
- package/vendor/image-to-ascii/index.cjs +257 -0
- package/vendor/image-to-ascii/index.d.ts +48 -0
- package/vendor/image-to-ascii/package.json +22 -0
package/dist/index.js
CHANGED
|
@@ -181,7 +181,7 @@ function printWarning(message2) {
|
|
|
181
181
|
`);
|
|
182
182
|
}
|
|
183
183
|
function printInfo(message2) {
|
|
184
|
-
process.stdout.write(`${c.cyan("
|
|
184
|
+
process.stdout.write(`${c.cyan("i")} ${message2}
|
|
185
185
|
`);
|
|
186
186
|
}
|
|
187
187
|
function printKeyValue(key, value2, indent = 0) {
|
|
@@ -229,7 +229,7 @@ var init_output = __esm({
|
|
|
229
229
|
isStderrTTY = process.stderr.isTTY;
|
|
230
230
|
c = {
|
|
231
231
|
bold: (t2) => ansi("1", t2, isTTY),
|
|
232
|
-
dim: (t2) => ansi("
|
|
232
|
+
dim: (t2) => ansi("38;5;250", t2, isTTY),
|
|
233
233
|
red: (t2) => ansi("31", t2, isTTY),
|
|
234
234
|
green: (t2) => ansi("32", t2, isTTY),
|
|
235
235
|
yellow: (t2) => ansi("33", t2, isTTY),
|
|
@@ -250335,6 +250335,36 @@ import { spawn as spawn9 } from "node:child_process";
|
|
|
250335
250335
|
import { existsSync as existsSync23, statSync as statSync8 } from "node:fs";
|
|
250336
250336
|
import { chmod as chmod3, mkdir as mkdir11, writeFile as writeFile16 } from "node:fs/promises";
|
|
250337
250337
|
import { join as join36, resolve as resolve18 } from "node:path";
|
|
250338
|
+
function parsePercent(text) {
|
|
250339
|
+
const match = text.match(/\b(\d{1,3})%\b/);
|
|
250340
|
+
if (!match)
|
|
250341
|
+
return void 0;
|
|
250342
|
+
const value2 = Number(match[1]);
|
|
250343
|
+
if (!Number.isFinite(value2))
|
|
250344
|
+
return void 0;
|
|
250345
|
+
return Math.max(0, Math.min(100, value2));
|
|
250346
|
+
}
|
|
250347
|
+
function cleanProgressText(text) {
|
|
250348
|
+
return text.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "").replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
250349
|
+
}
|
|
250350
|
+
function parseStructuredProgress(text) {
|
|
250351
|
+
const trimmed = text.trim();
|
|
250352
|
+
if (!trimmed.startsWith("{"))
|
|
250353
|
+
return null;
|
|
250354
|
+
try {
|
|
250355
|
+
const parsed = JSON.parse(trimmed);
|
|
250356
|
+
if (parsed["omnius_progress"] !== true)
|
|
250357
|
+
return null;
|
|
250358
|
+
const stage = String(parsed["stage"] ?? "process");
|
|
250359
|
+
const message2 = String(parsed["message"] ?? "").trim();
|
|
250360
|
+
const percent = typeof parsed["percent"] === "number" ? parsed["percent"] : void 0;
|
|
250361
|
+
if (!message2)
|
|
250362
|
+
return null;
|
|
250363
|
+
return { stage, message: message2, percent };
|
|
250364
|
+
} catch {
|
|
250365
|
+
return null;
|
|
250366
|
+
}
|
|
250367
|
+
}
|
|
250338
250368
|
function numberArg(value2, fallback) {
|
|
250339
250369
|
const n2 = Number(value2);
|
|
250340
250370
|
return Number.isFinite(n2) && n2 > 0 ? n2 : fallback;
|
|
@@ -250430,6 +250460,7 @@ function imageGenerationSetupPlan(backend, repoRoot = ".", model) {
|
|
|
250430
250460
|
}
|
|
250431
250461
|
async function runProcess2(command, args, options2) {
|
|
250432
250462
|
return new Promise((resolveProcess) => {
|
|
250463
|
+
const startedAt2 = Date.now();
|
|
250433
250464
|
const child = spawn9(command, args, {
|
|
250434
250465
|
cwd: options2.cwd,
|
|
250435
250466
|
env: { ...process.env, ...options2.env },
|
|
@@ -250437,22 +250468,66 @@ async function runProcess2(command, args, options2) {
|
|
|
250437
250468
|
});
|
|
250438
250469
|
let stdout = "";
|
|
250439
250470
|
let stderr = "";
|
|
250471
|
+
let lastProgress = "";
|
|
250472
|
+
let lastProgressAt = 0;
|
|
250473
|
+
const emitProgress = (stream, raw) => {
|
|
250474
|
+
if (!options2.onProgress)
|
|
250475
|
+
return;
|
|
250476
|
+
const parts = raw.split(/[\r\n]+/).filter((part) => part.trim());
|
|
250477
|
+
for (const part of parts.length > 0 ? parts : [raw]) {
|
|
250478
|
+
const clean3 = cleanProgressText(part);
|
|
250479
|
+
if (!clean3)
|
|
250480
|
+
continue;
|
|
250481
|
+
const now = Date.now();
|
|
250482
|
+
const structured = parseStructuredProgress(clean3);
|
|
250483
|
+
if (clean3 === lastProgress && now - lastProgressAt < 750)
|
|
250484
|
+
continue;
|
|
250485
|
+
lastProgress = clean3;
|
|
250486
|
+
lastProgressAt = now;
|
|
250487
|
+
if (structured) {
|
|
250488
|
+
options2.onProgress({ ...structured, elapsedMs: now - startedAt2 });
|
|
250489
|
+
} else {
|
|
250490
|
+
options2.onProgress({
|
|
250491
|
+
stage: stream === "stderr" ? "download" : "process",
|
|
250492
|
+
message: clean3.length > 220 ? clean3.slice(0, 217) + "..." : clean3,
|
|
250493
|
+
percent: parsePercent(clean3),
|
|
250494
|
+
elapsedMs: now - startedAt2
|
|
250495
|
+
});
|
|
250496
|
+
}
|
|
250497
|
+
}
|
|
250498
|
+
};
|
|
250440
250499
|
const timer = setTimeout(() => {
|
|
250441
250500
|
child.kill("SIGTERM");
|
|
250442
250501
|
}, options2.timeoutMs);
|
|
250443
250502
|
timer.unref();
|
|
250503
|
+
const heartbeat = setInterval(() => {
|
|
250504
|
+
if (!options2.onProgress || !options2.progressLabel)
|
|
250505
|
+
return;
|
|
250506
|
+
options2.onProgress({
|
|
250507
|
+
stage: "process",
|
|
250508
|
+
message: `${options2.progressLabel} still running`,
|
|
250509
|
+
elapsedMs: Date.now() - startedAt2
|
|
250510
|
+
});
|
|
250511
|
+
}, 5e3);
|
|
250512
|
+
heartbeat.unref();
|
|
250444
250513
|
child.stdout?.on("data", (chunk) => {
|
|
250445
|
-
|
|
250514
|
+
const text = chunk.toString();
|
|
250515
|
+
stdout += text;
|
|
250516
|
+
emitProgress("stdout", text);
|
|
250446
250517
|
});
|
|
250447
250518
|
child.stderr?.on("data", (chunk) => {
|
|
250448
|
-
|
|
250519
|
+
const text = chunk.toString();
|
|
250520
|
+
stderr += text;
|
|
250521
|
+
emitProgress("stderr", text);
|
|
250449
250522
|
});
|
|
250450
250523
|
child.on("error", (err) => {
|
|
250451
250524
|
clearTimeout(timer);
|
|
250525
|
+
clearInterval(heartbeat);
|
|
250452
250526
|
resolveProcess({ code: 127, stdout, stderr: stderr + String(err.message || err) });
|
|
250453
250527
|
});
|
|
250454
250528
|
child.on("close", (code8) => {
|
|
250455
250529
|
clearTimeout(timer);
|
|
250530
|
+
clearInterval(heartbeat);
|
|
250456
250531
|
resolveProcess({ code: code8, stdout, stderr });
|
|
250457
250532
|
});
|
|
250458
250533
|
});
|
|
@@ -250468,6 +250543,8 @@ function imageGenerationPythonEnv(repoRoot) {
|
|
|
250468
250543
|
const hf = join36(root, "huggingface");
|
|
250469
250544
|
return {
|
|
250470
250545
|
PYTHONUNBUFFERED: "1",
|
|
250546
|
+
HF_HUB_DISABLE_PROGRESS_BARS: "0",
|
|
250547
|
+
TQDM_DISABLE: "0",
|
|
250471
250548
|
HF_HOME: hf,
|
|
250472
250549
|
HUGGINGFACE_HUB_CACHE: join36(hf, "hub"),
|
|
250473
250550
|
TRANSFORMERS_CACHE: join36(hf, "transformers"),
|
|
@@ -250494,7 +250571,7 @@ async function pythonCanImport(command, code8, repoRoot, env2) {
|
|
|
250494
250571
|
const result = await runProcess2(command, ["-c", code8], { cwd: repoRoot, timeoutMs: 6e4, env: env2 });
|
|
250495
250572
|
return result.code === 0;
|
|
250496
250573
|
}
|
|
250497
|
-
async function ensurePythonFor(repoRoot, kind, explicit) {
|
|
250574
|
+
async function ensurePythonFor(repoRoot, kind, explicit, onProgress) {
|
|
250498
250575
|
const pythonEnv = imageGenerationPythonEnv(repoRoot);
|
|
250499
250576
|
await ensureImageGenerationCacheDirs(repoRoot);
|
|
250500
250577
|
if (explicit)
|
|
@@ -250505,7 +250582,14 @@ async function ensurePythonFor(repoRoot, kind, explicit) {
|
|
|
250505
250582
|
const venvDir = kind === "diffusers" ? diffusersVenvDir(repoRoot) : sdcppVenvDir(repoRoot);
|
|
250506
250583
|
const command = venvPython(venvDir);
|
|
250507
250584
|
if (!existsSync23(command)) {
|
|
250508
|
-
|
|
250585
|
+
onProgress?.({ stage: "setup", message: `Creating image-generation Python environment at ${venvDir}` });
|
|
250586
|
+
const created = await runProcess2("python3", ["-m", "venv", venvDir], {
|
|
250587
|
+
cwd: repoRoot,
|
|
250588
|
+
timeoutMs: 18e4,
|
|
250589
|
+
env: pythonEnv,
|
|
250590
|
+
progressLabel: "Creating image-generation Python environment",
|
|
250591
|
+
onProgress
|
|
250592
|
+
});
|
|
250509
250593
|
if (created.code !== 0) {
|
|
250510
250594
|
throw new Error(`Failed to create image-generation venv at ${venvDir}.
|
|
250511
250595
|
${trimProcessText(created.stderr || created.stdout)}`);
|
|
@@ -250516,10 +250600,13 @@ ${trimProcessText(created.stderr || created.stdout)}`);
|
|
|
250516
250600
|
return { command, env: pythonEnv };
|
|
250517
250601
|
}
|
|
250518
250602
|
const packages = kind === "diffusers" ? DIFFUSERS_PYTHON_PACKAGES : SDCPP_PYTHON_PACKAGES;
|
|
250519
|
-
|
|
250603
|
+
onProgress?.({ stage: "setup", message: `Installing ${kind} image-generation Python packages` });
|
|
250604
|
+
const pip = await runProcess2(command, ["-m", "pip", "install", "--progress-bar", "on", "-U", "pip", ...packages], {
|
|
250520
250605
|
cwd: repoRoot,
|
|
250521
250606
|
timeoutMs: 18e5,
|
|
250522
|
-
env: pythonEnv
|
|
250607
|
+
env: pythonEnv,
|
|
250608
|
+
progressLabel: `Installing ${kind} image-generation Python packages`,
|
|
250609
|
+
onProgress
|
|
250523
250610
|
});
|
|
250524
250611
|
if (pip.code !== 0) {
|
|
250525
250612
|
throw new Error(`Failed to install ${kind} image-generation packages into ${venvDir}.
|
|
@@ -250893,9 +250980,16 @@ var init_image_generate = __esm({
|
|
|
250893
250980
|
import argparse
|
|
250894
250981
|
import json
|
|
250895
250982
|
import os
|
|
250983
|
+
import sys
|
|
250896
250984
|
import time
|
|
250897
250985
|
from pathlib import Path
|
|
250898
250986
|
|
|
250987
|
+
def _progress(stage, message, percent=None):
|
|
250988
|
+
payload = {"omnius_progress": True, "stage": stage, "message": message}
|
|
250989
|
+
if percent is not None:
|
|
250990
|
+
payload["percent"] = percent
|
|
250991
|
+
print(json.dumps(payload), file=sys.stderr, flush=True)
|
|
250992
|
+
|
|
250899
250993
|
def _device():
|
|
250900
250994
|
import torch
|
|
250901
250995
|
if torch.cuda.is_available():
|
|
@@ -250943,12 +251037,15 @@ def main():
|
|
|
250943
251037
|
kwargs["variant"] = args.variant
|
|
250944
251038
|
|
|
250945
251039
|
try:
|
|
251040
|
+
_progress("load", f"loading model {args.model}")
|
|
250946
251041
|
pipeline_cls = _pipeline_class(args.model)
|
|
250947
251042
|
pipe = pipeline_cls.from_pretrained(args.model, **kwargs)
|
|
250948
251043
|
except Exception:
|
|
251044
|
+
_progress("load", f"retrying model load without variant for {args.model}")
|
|
250949
251045
|
kwargs.pop("variant", None)
|
|
250950
251046
|
pipeline_cls = _pipeline_class(args.model)
|
|
250951
251047
|
pipe = pipeline_cls.from_pretrained(args.model, **kwargs)
|
|
251048
|
+
_progress("load", f"model loaded on {device}")
|
|
250952
251049
|
|
|
250953
251050
|
if hasattr(pipe, "enable_attention_slicing"):
|
|
250954
251051
|
try:
|
|
@@ -250976,14 +251073,17 @@ def main():
|
|
|
250976
251073
|
"height": args.height,
|
|
250977
251074
|
}
|
|
250978
251075
|
try:
|
|
251076
|
+
_progress("generate", f"generating {args.width}x{args.height} image with {args.steps} steps")
|
|
250979
251077
|
image = pipe(**call_kwargs).images[0]
|
|
250980
251078
|
except TypeError:
|
|
250981
251079
|
call_kwargs.pop("width", None)
|
|
250982
251080
|
call_kwargs.pop("height", None)
|
|
251081
|
+
_progress("generate", f"generating image with model-native dimensions and {args.steps} steps")
|
|
250983
251082
|
image = pipe(**call_kwargs).images[0]
|
|
250984
251083
|
|
|
250985
251084
|
out = Path(args.output)
|
|
250986
251085
|
out.parent.mkdir(parents=True, exist_ok=True)
|
|
251086
|
+
_progress("save", f"saving image to {out}")
|
|
250987
251087
|
image.save(out)
|
|
250988
251088
|
print(json.dumps({
|
|
250989
251089
|
"ok": True,
|
|
@@ -251094,10 +251194,27 @@ if __name__ == "__main__":
|
|
|
251094
251194
|
cwd;
|
|
251095
251195
|
ollamaUrl;
|
|
251096
251196
|
cachedImageModel = null;
|
|
251197
|
+
progressHandler = null;
|
|
251198
|
+
lastProgressMessage = "";
|
|
251199
|
+
lastProgressAt = 0;
|
|
251097
251200
|
constructor(cwd4, ollamaUrl = "http://localhost:11434") {
|
|
251098
251201
|
this.cwd = cwd4;
|
|
251099
251202
|
this.ollamaUrl = ollamaUrl.replace(/\/v1\/?$/, "").replace(/\/$/, "");
|
|
251100
251203
|
}
|
|
251204
|
+
setProgressCallback(handler) {
|
|
251205
|
+
this.progressHandler = handler;
|
|
251206
|
+
}
|
|
251207
|
+
emitProgress(event) {
|
|
251208
|
+
if (!this.progressHandler)
|
|
251209
|
+
return;
|
|
251210
|
+
const now = Date.now();
|
|
251211
|
+
const key = `${event.stage}:${event.percent ?? ""}:${event.message}`;
|
|
251212
|
+
if (key === this.lastProgressMessage && now - this.lastProgressAt < 750)
|
|
251213
|
+
return;
|
|
251214
|
+
this.lastProgressMessage = key;
|
|
251215
|
+
this.lastProgressAt = now;
|
|
251216
|
+
this.progressHandler(event);
|
|
251217
|
+
}
|
|
251101
251218
|
async execute(args) {
|
|
251102
251219
|
const start2 = performance.now();
|
|
251103
251220
|
const action = String(args["action"] ?? "generate");
|
|
@@ -251239,7 +251356,7 @@ ${errText.slice(0, 800)}`,
|
|
|
251239
251356
|
const filepath = outputPath(this.cwd);
|
|
251240
251357
|
let python;
|
|
251241
251358
|
try {
|
|
251242
|
-
python = await ensurePythonFor(this.cwd, "diffusers", typeof args.python === "string" ? args.python : void 0);
|
|
251359
|
+
python = await ensurePythonFor(this.cwd, "diffusers", typeof args.python === "string" ? args.python : void 0, (event) => this.emitProgress(event));
|
|
251243
251360
|
} catch (err) {
|
|
251244
251361
|
const plan = imageGenerationSetupPlan("diffusers", this.cwd, args.model);
|
|
251245
251362
|
return {
|
|
@@ -251273,7 +251390,14 @@ ${errText.slice(0, 800)}`,
|
|
|
251273
251390
|
];
|
|
251274
251391
|
if (args.seed !== void 0)
|
|
251275
251392
|
argv.push("--seed", String(args.seed));
|
|
251276
|
-
|
|
251393
|
+
this.emitProgress({ stage: "load", message: `Starting image generation with ${args.model}` });
|
|
251394
|
+
const result = await runProcess2(python.command, argv, {
|
|
251395
|
+
cwd: this.cwd,
|
|
251396
|
+
timeoutMs: 9e5,
|
|
251397
|
+
env: python.env,
|
|
251398
|
+
progressLabel: `Downloading/loading ${args.model}`,
|
|
251399
|
+
onProgress: (event) => this.emitProgress(event)
|
|
251400
|
+
});
|
|
251277
251401
|
if (result.code !== 0 || !existsSync23(filepath)) {
|
|
251278
251402
|
const plan = imageGenerationSetupPlan("diffusers", this.cwd, args.model);
|
|
251279
251403
|
return {
|
|
@@ -535558,7 +535682,7 @@ Respond with EXACTLY this structure before your next tool call:
|
|
|
535558
535682
|
this.emit({
|
|
535559
535683
|
type: "tool_result",
|
|
535560
535684
|
toolName: tc.name,
|
|
535561
|
-
content:
|
|
535685
|
+
content: this.toolResultEventContent(tc.name, output),
|
|
535562
535686
|
success: result.success,
|
|
535563
535687
|
turn,
|
|
535564
535688
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -536667,7 +536791,7 @@ Full content available via: repl_exec(code="data = retrieve('${handleId}')") or
|
|
|
536667
536791
|
this.emit({
|
|
536668
536792
|
type: "tool_result",
|
|
536669
536793
|
toolName: tc.name,
|
|
536670
|
-
content:
|
|
536794
|
+
content: this.toolResultEventContent(tc.name, output),
|
|
536671
536795
|
success: result.success,
|
|
536672
536796
|
turn,
|
|
536673
536797
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -537511,6 +537635,12 @@ ${marker}` : marker);
|
|
|
537511
537635
|
*
|
|
537512
537636
|
* This replaces scattered post-hoc truncation with a single normalization point.
|
|
537513
537637
|
*/
|
|
537638
|
+
toolResultEventContent(toolName, output) {
|
|
537639
|
+
if (toolName === "generate_image" || toolName === "screenshot" || toolName === "camera_capture" || /(?:Image generated|Screenshot saved|Saved to|Output saved to):?\s+/i.test(output)) {
|
|
537640
|
+
return output.slice(0, 2e3);
|
|
537641
|
+
}
|
|
537642
|
+
return output.slice(0, 200);
|
|
537643
|
+
}
|
|
537514
537644
|
normalizeToolOutput(result, toolName, args, turn) {
|
|
537515
537645
|
const { toolOutputMaxChars: maxLen } = this.contextLimits();
|
|
537516
537646
|
const modelContent = result.llmContent ?? result.output;
|
|
@@ -545376,7 +545506,7 @@ var init_spinner = __esm({
|
|
|
545376
545506
|
info(message2) {
|
|
545377
545507
|
this.stop();
|
|
545378
545508
|
const msg = message2 ?? this._text;
|
|
545379
|
-
process.stdout.write(`\x1B[
|
|
545509
|
+
process.stdout.write(`\x1B[36mi\x1B[0m ${msg}
|
|
545380
545510
|
`);
|
|
545381
545511
|
return this;
|
|
545382
545512
|
}
|
|
@@ -550194,8 +550324,8 @@ var init_theme = __esm({
|
|
|
550194
550324
|
// terminal default foreground
|
|
550195
550325
|
textPrimary: -1,
|
|
550196
550326
|
// terminal default foreground
|
|
550197
|
-
textDim:
|
|
550198
|
-
//
|
|
550327
|
+
textDim: 250,
|
|
550328
|
+
// readable dim text without ANSI faint
|
|
550199
550329
|
boxColor: -1
|
|
550200
550330
|
// terminal default foreground
|
|
550201
550331
|
},
|
|
@@ -550207,8 +550337,8 @@ var init_theme = __esm({
|
|
|
550207
550337
|
// teal
|
|
550208
550338
|
textPrimary: 37,
|
|
550209
550339
|
// teal
|
|
550210
|
-
textDim:
|
|
550211
|
-
// grey
|
|
550340
|
+
textDim: 250,
|
|
550341
|
+
// readable grey
|
|
550212
550342
|
boxColor: 37
|
|
550213
550343
|
// teal
|
|
550214
550344
|
},
|
|
@@ -550217,7 +550347,7 @@ var init_theme = __esm({
|
|
|
550217
550347
|
bg: -1,
|
|
550218
550348
|
accent: 37,
|
|
550219
550349
|
textPrimary: 252,
|
|
550220
|
-
textDim:
|
|
550350
|
+
textDim: 250,
|
|
550221
550351
|
boxColor: 252
|
|
550222
550352
|
}
|
|
550223
550353
|
};
|
|
@@ -551457,7 +551587,7 @@ ${icon} \x1B[38;5;198m${message2}\x1B[0m
|
|
|
551457
551587
|
function renderInfo(message2) {
|
|
551458
551588
|
const redir = _contentWriteHook?.redirect?.();
|
|
551459
551589
|
const dim = dimFg();
|
|
551460
|
-
const icon = `${dim}
|
|
551590
|
+
const icon = `${dim}i\x1B[0m`;
|
|
551461
551591
|
const text = `${icon} ${dim}${message2}\x1B[0m
|
|
551462
551592
|
`;
|
|
551463
551593
|
if (redir) {
|
|
@@ -551746,7 +551876,7 @@ var init_render = __esm({
|
|
|
551746
551876
|
isTTY2 = process.stdout.isTTY ?? false;
|
|
551747
551877
|
c3 = {
|
|
551748
551878
|
bold: (t2) => ansi2("1", t2),
|
|
551749
|
-
dim: (t2) =>
|
|
551879
|
+
dim: (t2) => isTTY2 ? `${dimFg()}${t2}\x1B[0m` : t2,
|
|
551750
551880
|
italic: (t2) => ansi2("3", t2),
|
|
551751
551881
|
red: (t2) => ansi2("31", t2),
|
|
551752
551882
|
green: (t2) => ansi2("32", t2),
|
|
@@ -551761,10 +551891,10 @@ var init_render = __esm({
|
|
|
551761
551891
|
ui = {
|
|
551762
551892
|
/** Primary text — lighter grey (252) for main content */
|
|
551763
551893
|
primary: (t2) => fg256(252, t2),
|
|
551764
|
-
/** Sub-text —
|
|
551765
|
-
sub: (t2) => fg256(
|
|
551766
|
-
/** Dim text —
|
|
551767
|
-
hint: (t2) => fg256(
|
|
551894
|
+
/** Sub-text — readable grey for secondary info */
|
|
551895
|
+
sub: (t2) => fg256(tuiTextDim(), t2),
|
|
551896
|
+
/** Dim text — readable grey for hints/placeholders */
|
|
551897
|
+
hint: (t2) => fg256(tuiTextDim(), t2),
|
|
551768
551898
|
/** Error text — magenta (bright) for errors */
|
|
551769
551899
|
error: (t2) => fg256(198, t2),
|
|
551770
551900
|
/** Warning text — warm orange for warnings */
|
|
@@ -551801,14 +551931,14 @@ var init_render = __esm({
|
|
|
551801
551931
|
// light peach
|
|
551802
551932
|
link: 111,
|
|
551803
551933
|
// periwinkle
|
|
551804
|
-
blockquote:
|
|
551805
|
-
// grey
|
|
551806
|
-
hr:
|
|
551807
|
-
//
|
|
551808
|
-
listBullet:
|
|
551809
|
-
// grey
|
|
551810
|
-
tableBar:
|
|
551811
|
-
// grey
|
|
551934
|
+
blockquote: 250,
|
|
551935
|
+
// readable grey
|
|
551936
|
+
hr: 250,
|
|
551937
|
+
// readable grey
|
|
551938
|
+
listBullet: 250,
|
|
551939
|
+
// readable grey
|
|
551940
|
+
tableBar: 250
|
|
551941
|
+
// readable grey
|
|
551812
551942
|
};
|
|
551813
551943
|
TOOL_ICONS = {
|
|
551814
551944
|
file_read: "📄",
|
|
@@ -559067,7 +559197,7 @@ var init_daemon_registry = __esm({
|
|
|
559067
559197
|
bar += `${PANEL_BG} ${BTN_BG}\x1B[${fgColor}m ${label} \x1B[${dotColor}m●\x1B[${fgColor}m `;
|
|
559068
559198
|
}
|
|
559069
559199
|
if (!bar) {
|
|
559070
|
-
return `${PANEL_BG} \x1B[38;5;
|
|
559200
|
+
return `${PANEL_BG} \x1B[38;5;250m⠀ no active daemons`;
|
|
559071
559201
|
}
|
|
559072
559202
|
return bar + PANEL_BG;
|
|
559073
559203
|
}
|
|
@@ -562478,7 +562608,7 @@ ${CONTENT_BG_SEQ}`);
|
|
|
562478
562608
|
if (fullLine.length <= availWidth) {
|
|
562479
562609
|
let displayLine;
|
|
562480
562610
|
if (ghost) {
|
|
562481
|
-
displayLine = fullLine + `\x1B[7m\x1B[38;5;${TEXT_DIM}m${ghost[0]}\x1B[0m${PANEL_BG_SEQ}\x1B[
|
|
562611
|
+
displayLine = fullLine + `\x1B[7m\x1B[38;5;${TEXT_DIM}m${ghost[0]}\x1B[0m${PANEL_BG_SEQ}\x1B[38;5;${TEXT_DIM}m${ghost.slice(1)}\x1B[0m${PANEL_BG_SEQ}`;
|
|
562482
562612
|
} else {
|
|
562483
562613
|
displayLine = _StatusBar.insertVisualCursor(fullLine, cursorPos);
|
|
562484
562614
|
}
|
|
@@ -563677,13 +563807,13 @@ var init_tui_select = __esm({
|
|
|
563677
563807
|
selectColors = {
|
|
563678
563808
|
orange: (t2) => fg2563(208, t2),
|
|
563679
563809
|
green: (t2) => ansi3("32", t2),
|
|
563680
|
-
dim: (t2) =>
|
|
563810
|
+
dim: (t2) => fg2563(tuiTextDim(), t2),
|
|
563681
563811
|
bold: (t2) => ansi3("1", t2),
|
|
563682
563812
|
cyan: (t2) => ansi3("36", t2),
|
|
563683
563813
|
/** Lighter grey for filter matches (252 = near-white) */
|
|
563684
563814
|
matchLight: (t2) => fg2563(252, t2),
|
|
563685
|
-
/**
|
|
563686
|
-
matchDark: (t2) => fg2563(
|
|
563815
|
+
/** Readable grey for non-matching items */
|
|
563816
|
+
matchDark: (t2) => fg2563(tuiTextDim(), t2)
|
|
563687
563817
|
};
|
|
563688
563818
|
}
|
|
563689
563819
|
});
|
|
@@ -568228,7 +568358,7 @@ var init_drop_panel = __esm({
|
|
|
568228
568358
|
isTTY4 = process.stdout.isTTY ?? false;
|
|
568229
568359
|
dc = {
|
|
568230
568360
|
bold: (t2) => ansi4("1", t2),
|
|
568231
|
-
dim: (t2) => ansi4("
|
|
568361
|
+
dim: (t2) => ansi4("38;5;250", t2),
|
|
568232
568362
|
cyan: (t2) => ansi4("36", t2),
|
|
568233
568363
|
green: (t2) => ansi4("32", t2),
|
|
568234
568364
|
red: (t2) => ansi4("31", t2),
|
|
@@ -570727,7 +570857,7 @@ async function connectRPC(state, neovimPkg, cols) {
|
|
|
570727
570857
|
state.outputChanId = chanId;
|
|
570728
570858
|
await nvim.request("nvim_chan_send", [
|
|
570729
570859
|
chanId,
|
|
570730
|
-
"\x1B[36m── Agent Output ──\x1B[0m\r\n\r\n\x1B[
|
|
570860
|
+
"\x1B[36m── Agent Output ──\x1B[0m\r\n\r\n\x1B[38;5;250mAgent activity will appear here.\x1B[0m\r\n\x1B[38;5;250mCtrl+N toggles focus to input.\x1B[0m\r\n"
|
|
570731
570861
|
]);
|
|
570732
570862
|
await nvim.request("nvim_win_set_option", [win.id, "number", false]);
|
|
570733
570863
|
await nvim.request("nvim_win_set_option", [win.id, "relativenumber", false]);
|
|
@@ -572498,22 +572628,29 @@ function normalizeAscii(ascii2, width, height) {
|
|
|
572498
572628
|
const lines = ascii2.replace(ANSI_PATTERN, "").replace(/\r/g, "").split("\n").map((line) => line.replace(/\s+$/g, "")).filter((line, idx, all2) => line.length > 0 || idx > 0 && idx < all2.length - 1);
|
|
572499
572629
|
return lines.slice(0, height).map((line) => line.length > width ? line.slice(0, width) : line).join("\n").trimEnd();
|
|
572500
572630
|
}
|
|
572631
|
+
function previewFailure(message2, width) {
|
|
572632
|
+
const clean3 = message2.replace(/\s+/g, " ").trim();
|
|
572633
|
+
const text = `[image-to-ascii preview failed: ${clean3 || "unknown error"}]`;
|
|
572634
|
+
return text.length > width ? `${text.slice(0, Math.max(0, width - 1))}]` : text;
|
|
572635
|
+
}
|
|
572501
572636
|
async function convertWithImageToAscii(imagePath, width, height, timeoutMs) {
|
|
572502
572637
|
let imageToAscii;
|
|
572503
572638
|
try {
|
|
572504
572639
|
const require4 = createRequire4(import.meta.url);
|
|
572505
572640
|
const mod2 = require4("image-to-ascii");
|
|
572506
572641
|
imageToAscii = typeof mod2 === "function" ? mod2 : mod2.default;
|
|
572507
|
-
if (typeof imageToAscii !== "function")
|
|
572508
|
-
|
|
572509
|
-
|
|
572642
|
+
if (typeof imageToAscii !== "function") {
|
|
572643
|
+
return { ascii: null, error: "module did not export a function" };
|
|
572644
|
+
}
|
|
572645
|
+
} catch (err) {
|
|
572646
|
+
return { ascii: null, error: err instanceof Error ? err.message : String(err) };
|
|
572510
572647
|
}
|
|
572511
572648
|
return new Promise((resolvePreview) => {
|
|
572512
572649
|
let settled = false;
|
|
572513
572650
|
const timer = setTimeout(() => {
|
|
572514
572651
|
if (!settled) {
|
|
572515
572652
|
settled = true;
|
|
572516
|
-
resolvePreview(null);
|
|
572653
|
+
resolvePreview({ ascii: null, error: `timed out after ${timeoutMs}ms` });
|
|
572517
572654
|
}
|
|
572518
572655
|
}, timeoutMs).unref();
|
|
572519
572656
|
try {
|
|
@@ -572532,18 +572669,20 @@ async function convertWithImageToAscii(imagePath, width, height, timeoutMs) {
|
|
|
572532
572669
|
settled = true;
|
|
572533
572670
|
clearTimeout(timer);
|
|
572534
572671
|
if (err || !converted) {
|
|
572535
|
-
resolvePreview(null);
|
|
572672
|
+
resolvePreview({ ascii: null, error: err?.message || "empty renderer output" });
|
|
572536
572673
|
return;
|
|
572537
572674
|
}
|
|
572538
572675
|
const normalized = normalizeAscii(converted, width, height);
|
|
572539
|
-
resolvePreview(
|
|
572676
|
+
resolvePreview(
|
|
572677
|
+
normalized ? { ascii: normalized } : { ascii: null, error: "empty normalized renderer output" }
|
|
572678
|
+
);
|
|
572540
572679
|
}
|
|
572541
572680
|
);
|
|
572542
|
-
} catch {
|
|
572681
|
+
} catch (err) {
|
|
572543
572682
|
if (!settled) {
|
|
572544
572683
|
settled = true;
|
|
572545
572684
|
clearTimeout(timer);
|
|
572546
|
-
resolvePreview(null);
|
|
572685
|
+
resolvePreview({ ascii: null, error: err instanceof Error ? err.message : String(err) });
|
|
572547
572686
|
}
|
|
572548
572687
|
}
|
|
572549
572688
|
});
|
|
@@ -572607,8 +572746,15 @@ async function buildImageAsciiPreview(inputPath, options2 = {}) {
|
|
|
572607
572746
|
const height = clamp5(options2.height ?? defaults3.height, 8, 60);
|
|
572608
572747
|
const timeoutMs = clamp5(options2.timeoutMs ?? 5e3, 500, 3e4);
|
|
572609
572748
|
if (options2.preferPackage !== false) {
|
|
572610
|
-
const
|
|
572611
|
-
if (
|
|
572749
|
+
const result = await convertWithImageToAscii(imagePath, width, height, timeoutMs);
|
|
572750
|
+
if (result.ascii) return { path: imagePath, ascii: result.ascii, renderer: "image-to-ascii", width, height };
|
|
572751
|
+
return {
|
|
572752
|
+
path: imagePath,
|
|
572753
|
+
ascii: previewFailure(result.error || "renderer unavailable", width),
|
|
572754
|
+
renderer: "image-to-ascii",
|
|
572755
|
+
width,
|
|
572756
|
+
height
|
|
572757
|
+
};
|
|
572612
572758
|
}
|
|
572613
572759
|
const fallback = convertWithFfmpeg(imagePath, width, height, timeoutMs);
|
|
572614
572760
|
if (fallback) return { path: imagePath, ascii: fallback, renderer: "ffmpeg", width, height };
|
|
@@ -572621,6 +572767,8 @@ ${preview.ascii}`;
|
|
|
572621
572767
|
function extractSavedImagePath(text, repoRoot) {
|
|
572622
572768
|
const patterns = [
|
|
572623
572769
|
/Image generated:\s*([^\n\r]+)/i,
|
|
572770
|
+
/Image generated at\s+([^\s\n\r]+\.(?:png|jpg|jpeg|webp|gif))/i,
|
|
572771
|
+
/Output saved to\s+([^\s\n\r]+\.(?:png|jpg|jpeg|webp|gif))/i,
|
|
572624
572772
|
/Screenshot saved:\s*([^\n\r]+)/i,
|
|
572625
572773
|
/Screenshot:\s*([^\n\r]+)/i,
|
|
572626
572774
|
/Saved to:\s*([^\n\r]+)/i,
|
|
@@ -583137,6 +583285,9 @@ async function handleImageCommand(ctx3, arg, hasLocal) {
|
|
|
583137
583285
|
const backend = String(parsed.flags["backend"] ?? settings.imageBackend ?? inferImageGenerationBackend(model, void 0));
|
|
583138
583286
|
const tool = new ImageGenerateTool(ctx3.repoRoot, ctx3.config.backendUrl);
|
|
583139
583287
|
const prompt = parsed.prompt;
|
|
583288
|
+
tool.setProgressCallback((event) => {
|
|
583289
|
+
renderInfo(formatImageGenerationProgress(event));
|
|
583290
|
+
});
|
|
583140
583291
|
renderInfo(`Generating image with ${model} (${backend})...`);
|
|
583141
583292
|
const result = await tool.execute({
|
|
583142
583293
|
prompt,
|
|
@@ -583159,13 +583310,31 @@ async function handleImageCommand(ctx3, arg, hasLocal) {
|
|
|
583159
583310
|
if (imagePath) {
|
|
583160
583311
|
const preview = await buildImageAsciiPreview2(imagePath);
|
|
583161
583312
|
const displayPath = relative11(ctx3.repoRoot, imagePath).startsWith("..") ? imagePath : relative11(ctx3.repoRoot, imagePath);
|
|
583162
|
-
if (preview)
|
|
583313
|
+
if (preview) {
|
|
583314
|
+
renderImageAsciiPreview("Generated image", displayPath, preview.ascii, preview.renderer);
|
|
583315
|
+
} else {
|
|
583316
|
+
renderWarning(`Generated image preview unavailable for ${displayPath}.`);
|
|
583317
|
+
}
|
|
583163
583318
|
renderInfo(`File: ${imagePath}`);
|
|
583319
|
+
} else {
|
|
583320
|
+
renderWarning("Generated image preview skipped: no saved image path was found in the tool output.");
|
|
583164
583321
|
}
|
|
583165
|
-
} catch {
|
|
583322
|
+
} catch (err) {
|
|
583323
|
+
renderWarning(`Generated image preview failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
583166
583324
|
}
|
|
583167
583325
|
return "handled";
|
|
583168
583326
|
}
|
|
583327
|
+
function formatImageGenerationProgress(event) {
|
|
583328
|
+
const pct = event.percent;
|
|
583329
|
+
const elapsed = event.elapsedMs && event.elapsedMs > 1500 ? ` ${Math.round(event.elapsedMs / 1e3)}s` : "";
|
|
583330
|
+
if (typeof pct === "number") {
|
|
583331
|
+
const width = 20;
|
|
583332
|
+
const filled = Math.max(0, Math.min(width, Math.round(pct / 100 * width)));
|
|
583333
|
+
const bar = `${"#".repeat(filled)}${"-".repeat(width - filled)}`;
|
|
583334
|
+
return `Image ${event.stage}: [${bar}] ${pct}% ${event.message}${elapsed}`;
|
|
583335
|
+
}
|
|
583336
|
+
return `Image ${event.stage}: ${event.message}${elapsed}`;
|
|
583337
|
+
}
|
|
583169
583338
|
async function showHelpMenu(ctx3) {
|
|
583170
583339
|
const slashCommands = getSlashHelpEntries();
|
|
583171
583340
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -589035,13 +589204,13 @@ function fg2564(code8, text) {
|
|
|
589035
589204
|
return isTTY8 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
|
|
589036
589205
|
}
|
|
589037
589206
|
function dimText(text) {
|
|
589038
|
-
return isTTY8 ? `\x1B[
|
|
589207
|
+
return isTTY8 ? `\x1B[38;5;${tuiTextDim()}m${text}\x1B[0m` : text;
|
|
589039
589208
|
}
|
|
589040
589209
|
function italicText(text) {
|
|
589041
589210
|
return isTTY8 ? `\x1B[3m${text}\x1B[0m` : text;
|
|
589042
589211
|
}
|
|
589043
589212
|
function dimItalic(text) {
|
|
589044
|
-
return isTTY8 ? `\x1B[
|
|
589213
|
+
return isTTY8 ? `\x1B[3m\x1B[38;5;${tuiTextDim()}m${text}\x1B[0m` : text;
|
|
589045
589214
|
}
|
|
589046
589215
|
function boldText(text) {
|
|
589047
589216
|
return isTTY8 ? `\x1B[1m${text}\x1B[0m` : text;
|
|
@@ -589052,6 +589221,7 @@ var init_stream_renderer = __esm({
|
|
|
589052
589221
|
"use strict";
|
|
589053
589222
|
init_layout2();
|
|
589054
589223
|
init_text_selection();
|
|
589224
|
+
init_theme();
|
|
589055
589225
|
isTTY8 = process.stdout.isTTY ?? false;
|
|
589056
589226
|
PASTEL = {
|
|
589057
589227
|
key: 222,
|
|
@@ -589066,14 +589236,14 @@ var init_stream_renderer = __esm({
|
|
|
589066
589236
|
// grey-blue — null
|
|
589067
589237
|
bracket: 75,
|
|
589068
589238
|
// soft blue — { } [ ]
|
|
589069
|
-
colon:
|
|
589070
|
-
//
|
|
589239
|
+
colon: 250,
|
|
589240
|
+
// readable grey — : ,
|
|
589071
589241
|
keyword: 117,
|
|
589072
589242
|
// sky blue — function, return, if, else
|
|
589073
|
-
comment:
|
|
589074
|
-
//
|
|
589075
|
-
thinking:
|
|
589076
|
-
//
|
|
589243
|
+
comment: 250,
|
|
589244
|
+
// readable grey — // comments
|
|
589245
|
+
thinking: 250,
|
|
589246
|
+
// readable grey for thinking tokens
|
|
589077
589247
|
toolArg: 111,
|
|
589078
589248
|
// dim periwinkle for tool arg tokens
|
|
589079
589249
|
// Markdown
|
|
@@ -589087,10 +589257,10 @@ var init_stream_renderer = __esm({
|
|
|
589087
589257
|
// light peach — `code`
|
|
589088
589258
|
link: 111,
|
|
589089
589259
|
// periwinkle — [link](url)
|
|
589090
|
-
blockquote:
|
|
589091
|
-
// grey — > quote
|
|
589092
|
-
hr:
|
|
589093
|
-
//
|
|
589260
|
+
blockquote: 250,
|
|
589261
|
+
// readable grey — > quote
|
|
589262
|
+
hr: 250,
|
|
589263
|
+
// readable grey — ---
|
|
589094
589264
|
// Diff / patch
|
|
589095
589265
|
diffAdded: 114,
|
|
589096
589266
|
// mint green — + lines
|
|
@@ -589100,8 +589270,8 @@ var init_stream_renderer = __esm({
|
|
|
589100
589270
|
// blue — @@ headers
|
|
589101
589271
|
diffMeta: 222,
|
|
589102
589272
|
// gold — --- +++ file headers
|
|
589103
|
-
diffContext:
|
|
589104
|
-
// grey — context lines
|
|
589273
|
+
diffContext: 250,
|
|
589274
|
+
// readable grey — context lines
|
|
589105
589275
|
// Shell
|
|
589106
589276
|
shellVar: 222,
|
|
589107
589277
|
// gold — $VAR
|
|
@@ -593417,7 +593587,7 @@ function formatTelegramCreativeWorkspacePrompt(root) {
|
|
|
593417
593587
|
"Creative file tools are scoped to that folder only.",
|
|
593418
593588
|
"Allowed: create new files, read/list files in this workspace, and edit/patch files already created in this workspace.",
|
|
593419
593589
|
"Forbidden: delete files, access paths outside this workspace, mutate the project tree, run shell commands, or touch system state.",
|
|
593420
|
-
"When
|
|
593590
|
+
"When a user asks for an artifact to be sent, create it here and then call telegram_send_file. The bridge also auto-attaches recorded artifacts as a fallback. Refer to the attachment naturally; do not expose filesystem paths unless the admin explicitly asks."
|
|
593421
593591
|
].join("\n");
|
|
593422
593592
|
}
|
|
593423
593593
|
function collectGeneratedArtifactPathsFromText(text, root) {
|
|
@@ -594514,6 +594684,14 @@ function mimeForPath(path11, fallbackKind) {
|
|
|
594514
594684
|
if (fallbackKind === "video") return "video/mp4";
|
|
594515
594685
|
return "application/octet-stream";
|
|
594516
594686
|
}
|
|
594687
|
+
function normalizeTelegramSendKind(rawKind, path11) {
|
|
594688
|
+
const requested = typeof rawKind === "string" ? rawKind.trim().toLowerCase() : "auto";
|
|
594689
|
+
if (requested === "photo") return "image";
|
|
594690
|
+
if (requested === "image" || requested === "animation" || requested === "audio" || requested === "voice" || requested === "video" || requested === "document") {
|
|
594691
|
+
return requested;
|
|
594692
|
+
}
|
|
594693
|
+
return classifyMedia(path11) ?? "document";
|
|
594694
|
+
}
|
|
594517
594695
|
function renderTelegramStart(botUsername, adminId, mode = "auto") {
|
|
594518
594696
|
process.stdout.write(`
|
|
594519
594697
|
${c3.cyan("✈")} ${c3.bold("Telegram Bridge")} connected as @${botUsername}
|
|
@@ -596427,7 +596605,7 @@ ${msg.text}`;
|
|
|
596427
596605
|
const toolHint = [
|
|
596428
596606
|
"You have access to isolated per-chat memory (memory_write, memory_read, memory_search) scoped to this conversation.",
|
|
596429
596607
|
"You can remember facts about users and retrieve them later. You also have web_search and web_fetch to look up information.",
|
|
596430
|
-
"If the user asks you to create or send a file, image, or audio artifact,
|
|
596608
|
+
"If the user asks you to create or send a file, image, or audio artifact, create it with the scoped creative tools and call telegram_send_file to upload it. The bridge still auto-attaches generated files as a fallback when tool results record them.",
|
|
596431
596609
|
"For image generation requests, decide from the conversation whether generate_image is appropriate; do not ask the user to use a hardcoded shortcut when the request is clear.",
|
|
596432
596610
|
creativeWorkspace
|
|
596433
596611
|
].filter(Boolean).join("\n\n");
|
|
@@ -596573,7 +596751,8 @@ ${creativeWorkspace}` : ""}`;
|
|
|
596573
596751
|
new ImpactAnalysisTool(repoRoot),
|
|
596574
596752
|
new CodeNeighborsTool(repoRoot),
|
|
596575
596753
|
new ProcessHealthTool(),
|
|
596576
|
-
fullSubAgentTool
|
|
596754
|
+
fullSubAgentTool,
|
|
596755
|
+
this.buildTelegramSendFileTool(context2, repoRoot, chatId)
|
|
596577
596756
|
];
|
|
596578
596757
|
const allTools = context2 === "telegram-admin-dm" ? adminTools : sharedReadMemoryWebTools;
|
|
596579
596758
|
if (this.contextWindowSize > 0) {
|
|
@@ -596606,9 +596785,122 @@ ${creativeWorkspace}` : ""}`;
|
|
|
596606
596785
|
this.agentConfig?.backendUrl
|
|
596607
596786
|
).map((tool) => adaptTool5(tool, todoSessionId));
|
|
596608
596787
|
adaptedTools.push(...creativeTools);
|
|
596788
|
+
adaptedTools.push(adaptTool5(this.buildTelegramSendFileTool(context2, repoRoot, chatId), todoSessionId));
|
|
596609
596789
|
}
|
|
596610
596790
|
return [...adaptedTools, taskComplete];
|
|
596611
596791
|
}
|
|
596792
|
+
buildTelegramSendFileTool(context2, repoRoot, currentChatId) {
|
|
596793
|
+
const bridge = this;
|
|
596794
|
+
const adminDm = context2 === "telegram-admin-dm";
|
|
596795
|
+
const scopedRoot = adminDm ? void 0 : telegramCreativeWorkspaceRoot(repoRoot, currentChatId);
|
|
596796
|
+
return {
|
|
596797
|
+
name: "telegram_send_file",
|
|
596798
|
+
description: adminDm ? "Upload an existing local file to a Telegram chat. Admin DM scope may target the current chat, a numeric chat/user id, or a @username if the bot is allowed to message it. This only sends the file; it does not create, edit, or delete files." : `Upload an existing file from this chat's scoped creative workspace to the current Telegram chat. Public/group scope cannot target other chats and cannot send files outside ${scopedRoot}.`,
|
|
596799
|
+
parameters: {
|
|
596800
|
+
type: "object",
|
|
596801
|
+
properties: {
|
|
596802
|
+
path: {
|
|
596803
|
+
type: "string",
|
|
596804
|
+
description: adminDm ? "Local file path to send. Relative paths resolve from the repo root; absolute paths are allowed for admin DM." : "File path inside the scoped creative workspace."
|
|
596805
|
+
},
|
|
596806
|
+
chat_id: {
|
|
596807
|
+
type: "string",
|
|
596808
|
+
description: "Admin DM only. Optional target chat/user id or @username. Defaults to the current Telegram chat."
|
|
596809
|
+
},
|
|
596810
|
+
user_id: {
|
|
596811
|
+
type: "string",
|
|
596812
|
+
description: "Admin DM only. Optional numeric Telegram user id to send to, if the bot can message that user."
|
|
596813
|
+
},
|
|
596814
|
+
kind: {
|
|
596815
|
+
type: "string",
|
|
596816
|
+
enum: ["auto", "image", "photo", "document", "audio", "voice", "video", "animation"],
|
|
596817
|
+
description: "How Telegram should send the file. Defaults to auto based on extension."
|
|
596818
|
+
},
|
|
596819
|
+
caption: {
|
|
596820
|
+
type: "string",
|
|
596821
|
+
description: "Optional Telegram caption."
|
|
596822
|
+
},
|
|
596823
|
+
reply_to_message_id: {
|
|
596824
|
+
type: "number",
|
|
596825
|
+
description: "Optional Telegram message id to reply to."
|
|
596826
|
+
}
|
|
596827
|
+
},
|
|
596828
|
+
required: ["path"]
|
|
596829
|
+
},
|
|
596830
|
+
async execute(args) {
|
|
596831
|
+
const start2 = performance.now();
|
|
596832
|
+
const rawPath = typeof args["path"] === "string" ? args["path"].trim() : "";
|
|
596833
|
+
if (!rawPath) {
|
|
596834
|
+
return { success: false, output: "", error: "path is required", durationMs: performance.now() - start2 };
|
|
596835
|
+
}
|
|
596836
|
+
const target = bridge.resolveTelegramFileTarget(args, currentChatId, adminDm);
|
|
596837
|
+
if (!target.ok) {
|
|
596838
|
+
return { success: false, output: "", error: target.error, durationMs: performance.now() - start2 };
|
|
596839
|
+
}
|
|
596840
|
+
const file = bridge.resolveTelegramFilePath(rawPath, repoRoot, scopedRoot);
|
|
596841
|
+
if (!file.ok) {
|
|
596842
|
+
return { success: false, output: "", error: file.error, durationMs: performance.now() - start2 };
|
|
596843
|
+
}
|
|
596844
|
+
const kind = normalizeTelegramSendKind(args["kind"], file.path);
|
|
596845
|
+
const caption = typeof args["caption"] === "string" ? args["caption"].trim().slice(0, 1024) : void 0;
|
|
596846
|
+
const replyTo = Number(args["reply_to_message_id"]);
|
|
596847
|
+
try {
|
|
596848
|
+
const messageId = await bridge.sendTelegramFileToChat(target.chatId, file.path, {
|
|
596849
|
+
kind,
|
|
596850
|
+
caption: caption || void 0,
|
|
596851
|
+
replyToMessageId: Number.isFinite(replyTo) && replyTo > 0 ? Math.floor(replyTo) : void 0
|
|
596852
|
+
});
|
|
596853
|
+
return {
|
|
596854
|
+
success: true,
|
|
596855
|
+
output: `Sent Telegram file: ${basename22(file.path)} as ${kind} to ${String(target.chatId)}${messageId ? ` (message_id ${messageId})` : ""}`,
|
|
596856
|
+
llmContent: `Sent ${basename22(file.path)} to Telegram as ${kind}.`,
|
|
596857
|
+
durationMs: performance.now() - start2,
|
|
596858
|
+
mutated: false,
|
|
596859
|
+
mutatedFiles: []
|
|
596860
|
+
};
|
|
596861
|
+
} catch (err) {
|
|
596862
|
+
return {
|
|
596863
|
+
success: false,
|
|
596864
|
+
output: "",
|
|
596865
|
+
error: `Telegram file upload failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
596866
|
+
durationMs: performance.now() - start2
|
|
596867
|
+
};
|
|
596868
|
+
}
|
|
596869
|
+
}
|
|
596870
|
+
};
|
|
596871
|
+
}
|
|
596872
|
+
resolveTelegramFileTarget(args, currentChatId, adminDm) {
|
|
596873
|
+
const rawTarget = args["chat_id"] ?? args["target_chat_id"] ?? args["user_id"] ?? args["username"];
|
|
596874
|
+
if (!adminDm) {
|
|
596875
|
+
if (rawTarget !== void 0 && String(rawTarget).trim() && String(rawTarget).trim() !== String(currentChatId)) {
|
|
596876
|
+
return { ok: false, error: "Public/group telegram_send_file cannot target another chat. It may only send to the current chat." };
|
|
596877
|
+
}
|
|
596878
|
+
if (currentChatId === void 0) return { ok: false, error: "Current Telegram chat id is unavailable." };
|
|
596879
|
+
return { ok: true, chatId: currentChatId };
|
|
596880
|
+
}
|
|
596881
|
+
if (rawTarget === void 0 || !String(rawTarget).trim()) {
|
|
596882
|
+
if (currentChatId === void 0) return { ok: false, error: "No target chat_id/user_id provided and current chat id is unavailable." };
|
|
596883
|
+
return { ok: true, chatId: currentChatId };
|
|
596884
|
+
}
|
|
596885
|
+
const target = String(rawTarget).trim();
|
|
596886
|
+
if (/^-?\d+$/.test(target)) return { ok: true, chatId: target };
|
|
596887
|
+
const cleanUsername = target.replace(/^@/, "");
|
|
596888
|
+
if (/^[A-Za-z0-9_]{5,32}$/.test(cleanUsername)) {
|
|
596889
|
+
return { ok: true, chatId: `@${cleanUsername}` };
|
|
596890
|
+
}
|
|
596891
|
+
return { ok: false, error: "Expected chat_id/user_id as digits, or a valid @username." };
|
|
596892
|
+
}
|
|
596893
|
+
resolveTelegramFilePath(rawPath, repoRoot, scopedRoot) {
|
|
596894
|
+
const base3 = scopedRoot ? resolve39(scopedRoot) : resolve39(repoRoot);
|
|
596895
|
+
const trimmed = rawPath.trim().replace(/^["']|["']$/g, "");
|
|
596896
|
+
const abs = isAbsolute6(trimmed) ? resolve39(trimmed) : resolve39(base3, trimmed);
|
|
596897
|
+
if (scopedRoot && !isPathInside(base3, abs)) {
|
|
596898
|
+
return { ok: false, error: `Public/group telegram_send_file can only send files inside ${base3}.` };
|
|
596899
|
+
}
|
|
596900
|
+
if (!existsSync105(abs)) return { ok: false, error: `File does not exist: ${trimmed}` };
|
|
596901
|
+
if (!statSync35(abs).isFile()) return { ok: false, error: `Path is not a file: ${trimmed}` };
|
|
596902
|
+
return { ok: true, path: abs };
|
|
596903
|
+
}
|
|
596612
596904
|
/** Check if a message is from the admin user (uses fromUserId, NOT chatId) */
|
|
596613
596905
|
isAdminUser(msg) {
|
|
596614
596906
|
if (!this.adminUserId) return false;
|
|
@@ -596836,17 +597128,41 @@ ${visionContext}]`;
|
|
|
596836
597128
|
}
|
|
596837
597129
|
return options2.html ? this.sendMessageHTML(msg.chatId, text, options2.replyToMessageId) : this.sendMessage(msg.chatId, text);
|
|
596838
597130
|
}
|
|
596839
|
-
async
|
|
597131
|
+
async sendTelegramFileToChat(chatId, path11, options2) {
|
|
597132
|
+
const abs = resolve39(path11);
|
|
597133
|
+
if (!existsSync105(abs) || !statSync35(abs).isFile()) {
|
|
597134
|
+
throw new Error(`File does not exist or is not a regular file: ${path11}`);
|
|
597135
|
+
}
|
|
597136
|
+
return this.sendMediaReferenceStrict(chatId, {
|
|
597137
|
+
original: abs,
|
|
597138
|
+
value: abs,
|
|
597139
|
+
kind: options2.kind,
|
|
597140
|
+
source: "file",
|
|
597141
|
+
audioAsVoice: options2.kind === "voice"
|
|
597142
|
+
}, options2);
|
|
597143
|
+
}
|
|
597144
|
+
async sendMediaReference(chatId, media, options2 = {}) {
|
|
597145
|
+
try {
|
|
597146
|
+
return await this.sendMediaReferenceStrict(chatId, media, options2);
|
|
597147
|
+
} catch {
|
|
597148
|
+
return null;
|
|
597149
|
+
}
|
|
597150
|
+
}
|
|
597151
|
+
async sendMediaReferenceStrict(chatId, media, options2 = {}) {
|
|
596840
597152
|
const { method, field } = mediaTelegramMethod(media.kind);
|
|
597153
|
+
const caption = options2.caption?.trim();
|
|
596841
597154
|
if (media.source === "url") {
|
|
596842
597155
|
const result2 = await this.apiCall(method, {
|
|
596843
597156
|
chat_id: chatId,
|
|
596844
|
-
[field]: media.value
|
|
597157
|
+
[field]: media.value,
|
|
597158
|
+
...caption ? { caption } : {},
|
|
597159
|
+
...options2.replyToMessageId ? { reply_to_message_id: options2.replyToMessageId } : {}
|
|
596845
597160
|
});
|
|
597161
|
+
if (result2.ok === false) throw new Error(String(result2.description || `Telegram ${method} failed`));
|
|
596846
597162
|
this.state.messagesSent++;
|
|
596847
597163
|
return result2.result?.message_id ?? null;
|
|
596848
597164
|
}
|
|
596849
|
-
if (!existsSync105(media.value))
|
|
597165
|
+
if (!existsSync105(media.value)) throw new Error(`File does not exist: ${media.value}`);
|
|
596850
597166
|
const buffer2 = readFileSync86(media.value);
|
|
596851
597167
|
const boundary = `----omnius-media-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
596852
597168
|
const filename = basename22(media.value);
|
|
@@ -596862,6 +597178,8 @@ ${visionContext}]`;
|
|
|
596862
597178
|
`));
|
|
596863
597179
|
};
|
|
596864
597180
|
addField("chat_id", String(chatId));
|
|
597181
|
+
if (caption) addField("caption", caption);
|
|
597182
|
+
if (options2.replyToMessageId) addField("reply_to_message_id", String(options2.replyToMessageId));
|
|
596865
597183
|
parts.push(Buffer.from(`--${boundary}\r
|
|
596866
597184
|
`));
|
|
596867
597185
|
parts.push(Buffer.from(
|
|
@@ -596888,7 +597206,7 @@ Content-Type: ${contentType}\r
|
|
|
596888
597206
|
this.state.messagesSent++;
|
|
596889
597207
|
return result.result?.message_id ?? null;
|
|
596890
597208
|
}
|
|
596891
|
-
|
|
597209
|
+
throw new Error(String(result.description || `Telegram ${method} failed`));
|
|
596892
597210
|
}
|
|
596893
597211
|
async sendGeneratedArtifactsFromSubAgent(msg, subAgent, finalText, includeMentioned) {
|
|
596894
597212
|
const root = subAgent.creativeWorkspaceRoot;
|
|
@@ -622407,23 +622725,44 @@ async function renderAsciiPreviewForImage(imagePath, displayPath, title, writer)
|
|
|
622407
622725
|
formatImageAsciiContext: formatImageAsciiContext2
|
|
622408
622726
|
} = await Promise.resolve().then(() => (init_image_ascii_preview(), image_ascii_preview_exports));
|
|
622409
622727
|
const preview = await buildImageAsciiPreview2(imagePath);
|
|
622410
|
-
if (!preview)
|
|
622728
|
+
if (!preview) {
|
|
622729
|
+
writer(() => renderWarning(`Image ASCII preview unavailable for ${displayPath}.`));
|
|
622730
|
+
return "";
|
|
622731
|
+
}
|
|
622411
622732
|
writer(() => renderImageAsciiPreview(title, displayPath, preview.ascii, preview.renderer));
|
|
622412
622733
|
return formatImageAsciiContext2(preview, displayPath);
|
|
622413
622734
|
} catch {
|
|
622414
622735
|
return "";
|
|
622415
622736
|
}
|
|
622416
622737
|
}
|
|
622738
|
+
function formatImageGenerationProgress2(event) {
|
|
622739
|
+
const elapsed = event.elapsedMs && event.elapsedMs > 1500 ? ` ${Math.round(event.elapsedMs / 1e3)}s` : "";
|
|
622740
|
+
if (typeof event.percent === "number") {
|
|
622741
|
+
const width = 20;
|
|
622742
|
+
const filled = Math.max(0, Math.min(width, Math.round(event.percent / 100 * width)));
|
|
622743
|
+
const bar = `${"#".repeat(filled)}${"-".repeat(width - filled)}`;
|
|
622744
|
+
return `Image ${event.stage}: [${bar}] ${event.percent}% ${event.message}${elapsed}`;
|
|
622745
|
+
}
|
|
622746
|
+
return `Image ${event.stage}: ${event.message}${elapsed}`;
|
|
622747
|
+
}
|
|
622417
622748
|
async function renderAsciiPreviewForToolResult(toolName, output, repoRoot, writer) {
|
|
622418
622749
|
if (!output) return;
|
|
622419
622750
|
try {
|
|
622420
622751
|
const { extractSavedImagePath: extractSavedImagePath2 } = await Promise.resolve().then(() => (init_image_ascii_preview(), image_ascii_preview_exports));
|
|
622421
622752
|
const imagePath = extractSavedImagePath2(output, repoRoot);
|
|
622422
|
-
if (!imagePath)
|
|
622753
|
+
if (!imagePath) {
|
|
622754
|
+
if (toolName === "generate_image") {
|
|
622755
|
+
writer(() => renderWarning("Generated image preview skipped: no saved image path was found in the tool output."));
|
|
622756
|
+
}
|
|
622757
|
+
return;
|
|
622758
|
+
}
|
|
622423
622759
|
const displayPath = relative14(repoRoot, imagePath).startsWith("..") ? imagePath : relative14(repoRoot, imagePath);
|
|
622424
622760
|
const title = toolName === "generate_image" ? "Generated image" : toolName === "screenshot" ? "Screenshot" : toolName === "camera_capture" ? "Camera frame" : "Image";
|
|
622425
622761
|
await renderAsciiPreviewForImage(imagePath, displayPath, title, writer);
|
|
622426
|
-
} catch {
|
|
622762
|
+
} catch (err) {
|
|
622763
|
+
if (toolName === "generate_image") {
|
|
622764
|
+
writer(() => renderWarning(`Generated image preview failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
622765
|
+
}
|
|
622427
622766
|
}
|
|
622428
622767
|
}
|
|
622429
622768
|
async function runSelfImprovementCycle(repoRoot) {
|
|
@@ -623397,6 +623736,14 @@ ${entry.fullContent}`
|
|
|
623397
623736
|
fn();
|
|
623398
623737
|
}
|
|
623399
623738
|
};
|
|
623739
|
+
for (const tool of tools) {
|
|
623740
|
+
const maybeProgressTool = tool;
|
|
623741
|
+
if (typeof maybeProgressTool.setProgressCallback === "function") {
|
|
623742
|
+
maybeProgressTool.setProgressCallback((event) => {
|
|
623743
|
+
contentWrite(() => renderInfo(formatImageGenerationProgress2(event)));
|
|
623744
|
+
});
|
|
623745
|
+
}
|
|
623746
|
+
}
|
|
623400
623747
|
runner.onEvent((event) => {
|
|
623401
623748
|
emotionEngine?.appraise(event);
|
|
623402
623749
|
switch (event.type) {
|
|
@@ -623573,7 +623920,7 @@ ${entry.fullContent}`
|
|
|
623573
623920
|
}
|
|
623574
623921
|
});
|
|
623575
623922
|
}
|
|
623576
|
-
if (event.success) {
|
|
623923
|
+
if (event.success || event.toolName === "generate_image") {
|
|
623577
623924
|
void renderAsciiPreviewForToolResult(event.toolName, event.content ?? "", repoRoot, contentWrite);
|
|
623578
623925
|
}
|
|
623579
623926
|
if (voice?.enabled && voice.voiceMode === "voicechat" && _voiceChatSession2?.isActive && event.toolName === "task_complete") {
|
|
@@ -623702,7 +624049,7 @@ ${entry.fullContent}`
|
|
|
623702
624049
|
if (_apiCallbacks?.onStatus)
|
|
623703
624050
|
_apiCallbacks.onStatus(event.content ?? "");
|
|
623704
624051
|
if (isNeovimActive()) {
|
|
623705
|
-
writeToNeovimOutput(`\x1B[
|
|
624052
|
+
writeToNeovimOutput(`\x1B[38;5;250m${event.content ?? ""}\x1B[0m\r
|
|
623706
624053
|
`);
|
|
623707
624054
|
} else {
|
|
623708
624055
|
contentWrite(() => renderInfo(event.content ?? ""));
|