omnius 1.0.11 → 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/dist/index.js +398 -67
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
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]);
|
|
@@ -572637,6 +572767,8 @@ ${preview.ascii}`;
|
|
|
572637
572767
|
function extractSavedImagePath(text, repoRoot) {
|
|
572638
572768
|
const patterns = [
|
|
572639
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,
|
|
572640
572772
|
/Screenshot saved:\s*([^\n\r]+)/i,
|
|
572641
572773
|
/Screenshot:\s*([^\n\r]+)/i,
|
|
572642
572774
|
/Saved to:\s*([^\n\r]+)/i,
|
|
@@ -583153,6 +583285,9 @@ async function handleImageCommand(ctx3, arg, hasLocal) {
|
|
|
583153
583285
|
const backend = String(parsed.flags["backend"] ?? settings.imageBackend ?? inferImageGenerationBackend(model, void 0));
|
|
583154
583286
|
const tool = new ImageGenerateTool(ctx3.repoRoot, ctx3.config.backendUrl);
|
|
583155
583287
|
const prompt = parsed.prompt;
|
|
583288
|
+
tool.setProgressCallback((event) => {
|
|
583289
|
+
renderInfo(formatImageGenerationProgress(event));
|
|
583290
|
+
});
|
|
583156
583291
|
renderInfo(`Generating image with ${model} (${backend})...`);
|
|
583157
583292
|
const result = await tool.execute({
|
|
583158
583293
|
prompt,
|
|
@@ -583175,13 +583310,31 @@ async function handleImageCommand(ctx3, arg, hasLocal) {
|
|
|
583175
583310
|
if (imagePath) {
|
|
583176
583311
|
const preview = await buildImageAsciiPreview2(imagePath);
|
|
583177
583312
|
const displayPath = relative11(ctx3.repoRoot, imagePath).startsWith("..") ? imagePath : relative11(ctx3.repoRoot, imagePath);
|
|
583178
|
-
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
|
+
}
|
|
583179
583318
|
renderInfo(`File: ${imagePath}`);
|
|
583319
|
+
} else {
|
|
583320
|
+
renderWarning("Generated image preview skipped: no saved image path was found in the tool output.");
|
|
583180
583321
|
}
|
|
583181
|
-
} catch {
|
|
583322
|
+
} catch (err) {
|
|
583323
|
+
renderWarning(`Generated image preview failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
583182
583324
|
}
|
|
583183
583325
|
return "handled";
|
|
583184
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
|
+
}
|
|
583185
583338
|
async function showHelpMenu(ctx3) {
|
|
583186
583339
|
const slashCommands = getSlashHelpEntries();
|
|
583187
583340
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -589051,13 +589204,13 @@ function fg2564(code8, text) {
|
|
|
589051
589204
|
return isTTY8 ? `\x1B[38;5;${code8}m${text}\x1B[0m` : text;
|
|
589052
589205
|
}
|
|
589053
589206
|
function dimText(text) {
|
|
589054
|
-
return isTTY8 ? `\x1B[
|
|
589207
|
+
return isTTY8 ? `\x1B[38;5;${tuiTextDim()}m${text}\x1B[0m` : text;
|
|
589055
589208
|
}
|
|
589056
589209
|
function italicText(text) {
|
|
589057
589210
|
return isTTY8 ? `\x1B[3m${text}\x1B[0m` : text;
|
|
589058
589211
|
}
|
|
589059
589212
|
function dimItalic(text) {
|
|
589060
|
-
return isTTY8 ? `\x1B[
|
|
589213
|
+
return isTTY8 ? `\x1B[3m\x1B[38;5;${tuiTextDim()}m${text}\x1B[0m` : text;
|
|
589061
589214
|
}
|
|
589062
589215
|
function boldText(text) {
|
|
589063
589216
|
return isTTY8 ? `\x1B[1m${text}\x1B[0m` : text;
|
|
@@ -589068,6 +589221,7 @@ var init_stream_renderer = __esm({
|
|
|
589068
589221
|
"use strict";
|
|
589069
589222
|
init_layout2();
|
|
589070
589223
|
init_text_selection();
|
|
589224
|
+
init_theme();
|
|
589071
589225
|
isTTY8 = process.stdout.isTTY ?? false;
|
|
589072
589226
|
PASTEL = {
|
|
589073
589227
|
key: 222,
|
|
@@ -589082,14 +589236,14 @@ var init_stream_renderer = __esm({
|
|
|
589082
589236
|
// grey-blue — null
|
|
589083
589237
|
bracket: 75,
|
|
589084
589238
|
// soft blue — { } [ ]
|
|
589085
|
-
colon:
|
|
589086
|
-
//
|
|
589239
|
+
colon: 250,
|
|
589240
|
+
// readable grey — : ,
|
|
589087
589241
|
keyword: 117,
|
|
589088
589242
|
// sky blue — function, return, if, else
|
|
589089
|
-
comment:
|
|
589090
|
-
//
|
|
589091
|
-
thinking:
|
|
589092
|
-
//
|
|
589243
|
+
comment: 250,
|
|
589244
|
+
// readable grey — // comments
|
|
589245
|
+
thinking: 250,
|
|
589246
|
+
// readable grey for thinking tokens
|
|
589093
589247
|
toolArg: 111,
|
|
589094
589248
|
// dim periwinkle for tool arg tokens
|
|
589095
589249
|
// Markdown
|
|
@@ -589103,10 +589257,10 @@ var init_stream_renderer = __esm({
|
|
|
589103
589257
|
// light peach — `code`
|
|
589104
589258
|
link: 111,
|
|
589105
589259
|
// periwinkle — [link](url)
|
|
589106
|
-
blockquote:
|
|
589107
|
-
// grey — > quote
|
|
589108
|
-
hr:
|
|
589109
|
-
//
|
|
589260
|
+
blockquote: 250,
|
|
589261
|
+
// readable grey — > quote
|
|
589262
|
+
hr: 250,
|
|
589263
|
+
// readable grey — ---
|
|
589110
589264
|
// Diff / patch
|
|
589111
589265
|
diffAdded: 114,
|
|
589112
589266
|
// mint green — + lines
|
|
@@ -589116,8 +589270,8 @@ var init_stream_renderer = __esm({
|
|
|
589116
589270
|
// blue — @@ headers
|
|
589117
589271
|
diffMeta: 222,
|
|
589118
589272
|
// gold — --- +++ file headers
|
|
589119
|
-
diffContext:
|
|
589120
|
-
// grey — context lines
|
|
589273
|
+
diffContext: 250,
|
|
589274
|
+
// readable grey — context lines
|
|
589121
589275
|
// Shell
|
|
589122
589276
|
shellVar: 222,
|
|
589123
589277
|
// gold — $VAR
|
|
@@ -593433,7 +593587,7 @@ function formatTelegramCreativeWorkspacePrompt(root) {
|
|
|
593433
593587
|
"Creative file tools are scoped to that folder only.",
|
|
593434
593588
|
"Allowed: create new files, read/list files in this workspace, and edit/patch files already created in this workspace.",
|
|
593435
593589
|
"Forbidden: delete files, access paths outside this workspace, mutate the project tree, run shell commands, or touch system state.",
|
|
593436
|
-
"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."
|
|
593437
593591
|
].join("\n");
|
|
593438
593592
|
}
|
|
593439
593593
|
function collectGeneratedArtifactPathsFromText(text, root) {
|
|
@@ -594530,6 +594684,14 @@ function mimeForPath(path11, fallbackKind) {
|
|
|
594530
594684
|
if (fallbackKind === "video") return "video/mp4";
|
|
594531
594685
|
return "application/octet-stream";
|
|
594532
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
|
+
}
|
|
594533
594695
|
function renderTelegramStart(botUsername, adminId, mode = "auto") {
|
|
594534
594696
|
process.stdout.write(`
|
|
594535
594697
|
${c3.cyan("✈")} ${c3.bold("Telegram Bridge")} connected as @${botUsername}
|
|
@@ -596443,7 +596605,7 @@ ${msg.text}`;
|
|
|
596443
596605
|
const toolHint = [
|
|
596444
596606
|
"You have access to isolated per-chat memory (memory_write, memory_read, memory_search) scoped to this conversation.",
|
|
596445
596607
|
"You can remember facts about users and retrieve them later. You also have web_search and web_fetch to look up information.",
|
|
596446
|
-
"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.",
|
|
596447
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.",
|
|
596448
596610
|
creativeWorkspace
|
|
596449
596611
|
].filter(Boolean).join("\n\n");
|
|
@@ -596589,7 +596751,8 @@ ${creativeWorkspace}` : ""}`;
|
|
|
596589
596751
|
new ImpactAnalysisTool(repoRoot),
|
|
596590
596752
|
new CodeNeighborsTool(repoRoot),
|
|
596591
596753
|
new ProcessHealthTool(),
|
|
596592
|
-
fullSubAgentTool
|
|
596754
|
+
fullSubAgentTool,
|
|
596755
|
+
this.buildTelegramSendFileTool(context2, repoRoot, chatId)
|
|
596593
596756
|
];
|
|
596594
596757
|
const allTools = context2 === "telegram-admin-dm" ? adminTools : sharedReadMemoryWebTools;
|
|
596595
596758
|
if (this.contextWindowSize > 0) {
|
|
@@ -596622,9 +596785,122 @@ ${creativeWorkspace}` : ""}`;
|
|
|
596622
596785
|
this.agentConfig?.backendUrl
|
|
596623
596786
|
).map((tool) => adaptTool5(tool, todoSessionId));
|
|
596624
596787
|
adaptedTools.push(...creativeTools);
|
|
596788
|
+
adaptedTools.push(adaptTool5(this.buildTelegramSendFileTool(context2, repoRoot, chatId), todoSessionId));
|
|
596625
596789
|
}
|
|
596626
596790
|
return [...adaptedTools, taskComplete];
|
|
596627
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
|
+
}
|
|
596628
596904
|
/** Check if a message is from the admin user (uses fromUserId, NOT chatId) */
|
|
596629
596905
|
isAdminUser(msg) {
|
|
596630
596906
|
if (!this.adminUserId) return false;
|
|
@@ -596852,17 +597128,41 @@ ${visionContext}]`;
|
|
|
596852
597128
|
}
|
|
596853
597129
|
return options2.html ? this.sendMessageHTML(msg.chatId, text, options2.replyToMessageId) : this.sendMessage(msg.chatId, text);
|
|
596854
597130
|
}
|
|
596855
|
-
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 = {}) {
|
|
596856
597152
|
const { method, field } = mediaTelegramMethod(media.kind);
|
|
597153
|
+
const caption = options2.caption?.trim();
|
|
596857
597154
|
if (media.source === "url") {
|
|
596858
597155
|
const result2 = await this.apiCall(method, {
|
|
596859
597156
|
chat_id: chatId,
|
|
596860
|
-
[field]: media.value
|
|
597157
|
+
[field]: media.value,
|
|
597158
|
+
...caption ? { caption } : {},
|
|
597159
|
+
...options2.replyToMessageId ? { reply_to_message_id: options2.replyToMessageId } : {}
|
|
596861
597160
|
});
|
|
597161
|
+
if (result2.ok === false) throw new Error(String(result2.description || `Telegram ${method} failed`));
|
|
596862
597162
|
this.state.messagesSent++;
|
|
596863
597163
|
return result2.result?.message_id ?? null;
|
|
596864
597164
|
}
|
|
596865
|
-
if (!existsSync105(media.value))
|
|
597165
|
+
if (!existsSync105(media.value)) throw new Error(`File does not exist: ${media.value}`);
|
|
596866
597166
|
const buffer2 = readFileSync86(media.value);
|
|
596867
597167
|
const boundary = `----omnius-media-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
596868
597168
|
const filename = basename22(media.value);
|
|
@@ -596878,6 +597178,8 @@ ${visionContext}]`;
|
|
|
596878
597178
|
`));
|
|
596879
597179
|
};
|
|
596880
597180
|
addField("chat_id", String(chatId));
|
|
597181
|
+
if (caption) addField("caption", caption);
|
|
597182
|
+
if (options2.replyToMessageId) addField("reply_to_message_id", String(options2.replyToMessageId));
|
|
596881
597183
|
parts.push(Buffer.from(`--${boundary}\r
|
|
596882
597184
|
`));
|
|
596883
597185
|
parts.push(Buffer.from(
|
|
@@ -596904,7 +597206,7 @@ Content-Type: ${contentType}\r
|
|
|
596904
597206
|
this.state.messagesSent++;
|
|
596905
597207
|
return result.result?.message_id ?? null;
|
|
596906
597208
|
}
|
|
596907
|
-
|
|
597209
|
+
throw new Error(String(result.description || `Telegram ${method} failed`));
|
|
596908
597210
|
}
|
|
596909
597211
|
async sendGeneratedArtifactsFromSubAgent(msg, subAgent, finalText, includeMentioned) {
|
|
596910
597212
|
const root = subAgent.creativeWorkspaceRoot;
|
|
@@ -622423,23 +622725,44 @@ async function renderAsciiPreviewForImage(imagePath, displayPath, title, writer)
|
|
|
622423
622725
|
formatImageAsciiContext: formatImageAsciiContext2
|
|
622424
622726
|
} = await Promise.resolve().then(() => (init_image_ascii_preview(), image_ascii_preview_exports));
|
|
622425
622727
|
const preview = await buildImageAsciiPreview2(imagePath);
|
|
622426
|
-
if (!preview)
|
|
622728
|
+
if (!preview) {
|
|
622729
|
+
writer(() => renderWarning(`Image ASCII preview unavailable for ${displayPath}.`));
|
|
622730
|
+
return "";
|
|
622731
|
+
}
|
|
622427
622732
|
writer(() => renderImageAsciiPreview(title, displayPath, preview.ascii, preview.renderer));
|
|
622428
622733
|
return formatImageAsciiContext2(preview, displayPath);
|
|
622429
622734
|
} catch {
|
|
622430
622735
|
return "";
|
|
622431
622736
|
}
|
|
622432
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
|
+
}
|
|
622433
622748
|
async function renderAsciiPreviewForToolResult(toolName, output, repoRoot, writer) {
|
|
622434
622749
|
if (!output) return;
|
|
622435
622750
|
try {
|
|
622436
622751
|
const { extractSavedImagePath: extractSavedImagePath2 } = await Promise.resolve().then(() => (init_image_ascii_preview(), image_ascii_preview_exports));
|
|
622437
622752
|
const imagePath = extractSavedImagePath2(output, repoRoot);
|
|
622438
|
-
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
|
+
}
|
|
622439
622759
|
const displayPath = relative14(repoRoot, imagePath).startsWith("..") ? imagePath : relative14(repoRoot, imagePath);
|
|
622440
622760
|
const title = toolName === "generate_image" ? "Generated image" : toolName === "screenshot" ? "Screenshot" : toolName === "camera_capture" ? "Camera frame" : "Image";
|
|
622441
622761
|
await renderAsciiPreviewForImage(imagePath, displayPath, title, writer);
|
|
622442
|
-
} catch {
|
|
622762
|
+
} catch (err) {
|
|
622763
|
+
if (toolName === "generate_image") {
|
|
622764
|
+
writer(() => renderWarning(`Generated image preview failed: ${err instanceof Error ? err.message : String(err)}`));
|
|
622765
|
+
}
|
|
622443
622766
|
}
|
|
622444
622767
|
}
|
|
622445
622768
|
async function runSelfImprovementCycle(repoRoot) {
|
|
@@ -623413,6 +623736,14 @@ ${entry.fullContent}`
|
|
|
623413
623736
|
fn();
|
|
623414
623737
|
}
|
|
623415
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
|
+
}
|
|
623416
623747
|
runner.onEvent((event) => {
|
|
623417
623748
|
emotionEngine?.appraise(event);
|
|
623418
623749
|
switch (event.type) {
|
|
@@ -623589,7 +623920,7 @@ ${entry.fullContent}`
|
|
|
623589
623920
|
}
|
|
623590
623921
|
});
|
|
623591
623922
|
}
|
|
623592
|
-
if (event.success) {
|
|
623923
|
+
if (event.success || event.toolName === "generate_image") {
|
|
623593
623924
|
void renderAsciiPreviewForToolResult(event.toolName, event.content ?? "", repoRoot, contentWrite);
|
|
623594
623925
|
}
|
|
623595
623926
|
if (voice?.enabled && voice.voiceMode === "voicechat" && _voiceChatSession2?.isActive && event.toolName === "task_complete") {
|
|
@@ -623718,7 +624049,7 @@ ${entry.fullContent}`
|
|
|
623718
624049
|
if (_apiCallbacks?.onStatus)
|
|
623719
624050
|
_apiCallbacks.onStatus(event.content ?? "");
|
|
623720
624051
|
if (isNeovimActive()) {
|
|
623721
|
-
writeToNeovimOutput(`\x1B[
|
|
624052
|
+
writeToNeovimOutput(`\x1B[38;5;250m${event.content ?? ""}\x1B[0m\r
|
|
623722
624053
|
`);
|
|
623723
624054
|
} else {
|
|
623724
624055
|
contentWrite(() => renderInfo(event.content ?? ""));
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omnius",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.12",
|
|
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.12",
|
|
10
10
|
"bundleDependencies": [
|
|
11
11
|
"image-to-ascii"
|
|
12
12
|
],
|
package/package.json
CHANGED