@vascend/talos 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +37 -12
- package/dist/core/agent.d.ts +16 -3
- package/dist/core/agent.js +170 -70
- package/dist/core/background.d.ts +10 -3
- package/dist/core/background.js +55 -9
- package/dist/core/bootstrap.js +1 -0
- package/dist/core/compaction.js +1 -1
- package/dist/core/config-storage.d.ts +5 -0
- package/dist/core/config-storage.js +83 -0
- package/dist/core/config.d.ts +11 -2
- package/dist/core/config.js +24 -8
- package/dist/core/context-ledger.js +2 -1
- package/dist/core/context-policy.d.ts +1 -1
- package/dist/core/context-policy.js +10 -7
- package/dist/core/credentials.js +29 -30
- package/dist/core/effort.d.ts +1 -1
- package/dist/core/effort.js +5 -1
- package/dist/core/execution-guidance.d.ts +1 -1
- package/dist/core/execution-guidance.js +2 -0
- package/dist/core/file-coordination.d.ts +3 -0
- package/dist/core/file-coordination.js +28 -0
- package/dist/core/file-lease-store.d.ts +3 -0
- package/dist/core/file-lease-store.js +28 -12
- package/dist/core/fusion.d.ts +24 -0
- package/dist/core/fusion.js +80 -0
- package/dist/core/model-limits.d.ts +1 -0
- package/dist/core/model-limits.js +5 -1
- package/dist/core/permission-queue.js +15 -15
- package/dist/core/request-plan.js +4 -3
- package/dist/core/session-naming.d.ts +3 -0
- package/dist/core/session-naming.js +47 -0
- package/dist/core/session-usage.js +4 -1
- package/dist/core/session.d.ts +5 -2
- package/dist/core/session.js +22 -7
- package/dist/core/settings.d.ts +2 -0
- package/dist/core/settings.js +5 -16
- package/dist/core/step-policy.d.ts +18 -0
- package/dist/core/step-policy.js +6 -0
- package/dist/core/storage-health.d.ts +24 -0
- package/dist/core/storage-health.js +72 -0
- package/dist/core/tool-artifacts.d.ts +2 -0
- package/dist/core/tool-artifacts.js +21 -3
- package/dist/core/tool-result-index.js +29 -33
- package/dist/core/tool-result-store.js +6 -5
- package/dist/core/types.d.ts +26 -2
- package/dist/core/user-inbox.d.ts +9 -0
- package/dist/core/user-inbox.js +22 -3
- package/dist/core/user-input.js +1 -1
- package/dist/fusion/autopilot-fast.d.ts +59 -0
- package/dist/fusion/autopilot-fast.js +213 -0
- package/dist/fusion/autopilot.d.ts +75 -0
- package/dist/fusion/autopilot.js +308 -0
- package/dist/fusion/focus.d.ts +30 -0
- package/dist/fusion/focus.js +122 -0
- package/dist/fusion/gate.d.ts +30 -0
- package/dist/fusion/gate.js +95 -0
- package/dist/fusion/jev.d.ts +71 -0
- package/dist/fusion/jev.js +164 -0
- package/dist/fusion/receipts.d.ts +25 -0
- package/dist/fusion/receipts.js +34 -0
- package/dist/fusion/route.d.ts +25 -0
- package/dist/fusion/route.js +84 -0
- package/dist/fusion/run.d.ts +4 -0
- package/dist/fusion/run.js +186 -0
- package/dist/horizon/engine.d.ts +20 -1
- package/dist/horizon/engine.js +57 -0
- package/dist/horizon/index.d.ts +2 -2
- package/dist/horizon/index.js +1 -1
- package/dist/horizon/types.d.ts +29 -0
- package/dist/index.js +12 -0
- package/dist/providers/anthropic-oauth.d.ts +8 -3
- package/dist/providers/anthropic-oauth.js +13 -10
- package/dist/providers/anthropic.d.ts +9 -2
- package/dist/providers/anthropic.js +64 -21
- package/dist/providers/cli-models.d.ts +30 -0
- package/dist/providers/cli-models.js +194 -0
- package/dist/providers/model-effort.js +1 -1
- package/dist/providers/openai-oauth.d.ts +8 -3
- package/dist/providers/openai-oauth.js +9 -3
- package/dist/providers/openrouter-manual-fallback.d.ts +3 -1
- package/dist/providers/openrouter-manual-fallback.js +20 -20
- package/dist/providers/registry.js +3 -2
- package/dist/providers/retry.d.ts +3 -0
- package/dist/providers/retry.js +44 -1
- package/dist/remote/cifratura.d.ts +12 -0
- package/dist/remote/cifratura.js +66 -0
- package/dist/remote/index.d.ts +15 -0
- package/dist/remote/index.js +65 -0
- package/dist/remote/relay-client.d.ts +58 -0
- package/dist/remote/relay-client.js +315 -0
- package/dist/remote/sessione-remota.d.ts +60 -0
- package/dist/remote/sessione-remota.js +266 -0
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +3 -1
- package/dist/tools/builtin.js +41 -20
- package/dist/tools/capabilities.d.ts +3 -1
- package/dist/tools/capabilities.js +5 -2
- package/dist/tools/computer-use.js +2 -1
- package/dist/tools/pagination.js +1 -1
- package/dist/tools/program.js +34 -21
- package/dist/tools/team-tools.js +11 -6
- package/dist/tui/account-entry.js +14 -9
- package/dist/tui/agent-label.d.ts +2 -0
- package/dist/tui/agent-label.js +5 -0
- package/dist/tui/agent-sessions-screen.d.ts +35 -0
- package/dist/tui/agent-sessions-screen.js +178 -0
- package/dist/tui/agent-sessions.d.ts +53 -0
- package/dist/tui/agent-sessions.js +236 -0
- package/dist/tui/bottom-input.d.ts +57 -2
- package/dist/tui/bottom-input.js +449 -54
- package/dist/tui/branding.js +8 -4
- package/dist/tui/capability-lists.d.ts +10 -0
- package/dist/tui/capability-lists.js +41 -0
- package/dist/tui/command-output-registry.js +4 -0
- package/dist/tui/command-transcript.d.ts +1 -1
- package/dist/tui/command-transcript.js +17 -21
- package/dist/tui/commands.js +6 -2
- package/dist/tui/compaction-status.d.ts +7 -0
- package/dist/tui/compaction-status.js +39 -0
- package/dist/tui/config-recovery.d.ts +12 -0
- package/dist/tui/config-recovery.js +54 -0
- package/dist/tui/context-meter.d.ts +3 -0
- package/dist/tui/context-meter.js +35 -8
- package/dist/tui/diff.d.ts +9 -0
- package/dist/tui/diff.js +43 -19
- package/dist/tui/fusion-menu.d.ts +5 -0
- package/dist/tui/fusion-menu.js +89 -0
- package/dist/tui/i18n-catalog.js +351 -2
- package/dist/tui/live-command.d.ts +1 -0
- package/dist/tui/live-command.js +9 -0
- package/dist/tui/live-output.d.ts +1 -0
- package/dist/tui/live-output.js +11 -0
- package/dist/tui/logo.js +3 -0
- package/dist/tui/model-details.js +13 -12
- package/dist/tui/model-picker.d.ts +2 -0
- package/dist/tui/model-picker.js +59 -0
- package/dist/tui/model-selection.js +19 -10
- package/dist/tui/native-scrollback.d.ts +2 -1
- package/dist/tui/native-scrollback.js +31 -6
- package/dist/tui/prompt-queue-store.d.ts +1 -0
- package/dist/tui/prompt-queue-store.js +4 -0
- package/dist/tui/prompt-queue.d.ts +3 -0
- package/dist/tui/prompt-queue.js +19 -1
- package/dist/tui/provider-label.d.ts +2 -0
- package/dist/tui/provider-label.js +9 -0
- package/dist/tui/provider-menu.js +25 -24
- package/dist/tui/queue-screen.js +2 -1
- package/dist/tui/queued-questions.js +2 -4
- package/dist/tui/quick-help.d.ts +1 -1
- package/dist/tui/quick-help.js +3 -0
- package/dist/tui/remote-command.d.ts +7 -0
- package/dist/tui/remote-command.js +79 -0
- package/dist/tui/repl.d.ts +11 -2
- package/dist/tui/repl.js +457 -327
- package/dist/tui/review-store.d.ts +8 -1
- package/dist/tui/review-store.js +64 -11
- package/dist/tui/review-team.js +4 -3
- package/dist/tui/runtime-error.d.ts +2 -0
- package/dist/tui/runtime-error.js +26 -0
- package/dist/tui/scrollback-archive.d.ts +16 -0
- package/dist/tui/scrollback-archive.js +90 -0
- package/dist/tui/selector-mouse.d.ts +1 -0
- package/dist/tui/selector-mouse.js +1 -1
- package/dist/tui/selector.d.ts +11 -0
- package/dist/tui/selector.js +28 -4
- package/dist/tui/session-controls.d.ts +26 -0
- package/dist/tui/session-controls.js +65 -0
- package/dist/tui/session-naming-notice.d.ts +14 -0
- package/dist/tui/session-naming-notice.js +59 -0
- package/dist/tui/session-picker.d.ts +2 -0
- package/dist/tui/session-picker.js +32 -0
- package/dist/tui/session-replay.d.ts +1 -0
- package/dist/tui/session-replay.js +101 -32
- package/dist/tui/session-transcript-layout.d.ts +9 -0
- package/dist/tui/session-transcript-layout.js +41 -0
- package/dist/tui/startup.js +63 -33
- package/dist/tui/status-line.d.ts +1 -1
- package/dist/tui/status-line.js +20 -12
- package/dist/tui/storage-warning.d.ts +2 -0
- package/dist/tui/storage-warning.js +35 -0
- package/dist/tui/talos-art-data.d.ts +1 -0
- package/dist/tui/talos-art-data.js +2 -0
- package/dist/tui/talos-art.d.ts +1 -0
- package/dist/tui/talos-art.js +9 -0
- package/dist/tui/task-labels.d.ts +1 -0
- package/dist/tui/task-labels.js +8 -0
- package/dist/tui/terminal-text.d.ts +2 -1
- package/dist/tui/terminal-text.js +28 -2
- package/dist/tui/theme.js +2 -2
- package/dist/tui/tool-presentation.d.ts +3 -0
- package/dist/tui/tool-presentation.js +98 -0
- package/dist/tui/tool-style.js +4 -0
- package/dist/tui/transcript-store.d.ts +25 -0
- package/dist/tui/transcript-store.js +151 -0
- package/dist/tui/usage-footer.d.ts +1 -1
- package/dist/tui/usage-footer.js +3 -1
- package/dist/tui/vascend-id.d.ts +49 -0
- package/dist/tui/vascend-id.js +321 -0
- package/dist/tui/web-login-screen.js +4 -3
- package/dist/tui/web-login.d.ts +7 -15
- package/dist/tui/web-login.js +38 -78
- package/dist/tui/worker-transcripts.d.ts +31 -0
- package/dist/tui/worker-transcripts.js +91 -0
- package/dist/vascend/candidate-collector.d.ts +6 -5
- package/dist/vascend/candidate-collector.js +44 -43
- package/dist/vascend/context-feedback-loop.js +2 -0
- package/dist/vascend/context-lint.js +32 -2
- package/dist/vascend/context-market-selection.js +12 -5
- package/dist/vascend/context-pack-render.js +10 -4
- package/dist/vascend/context-packet-mirrors.d.ts +8 -0
- package/dist/vascend/context-packet-mirrors.js +24 -0
- package/dist/vascend/context-plan-events.d.ts +4 -0
- package/dist/vascend/context-plan-events.js +44 -0
- package/dist/vascend/context-snapshot.d.ts +1 -0
- package/dist/vascend/context-snapshot.js +8 -5
- package/dist/vascend/context-store-reader.d.ts +20 -0
- package/dist/vascend/context-store-reader.js +46 -0
- package/dist/vascend/context-window-compiler.d.ts +1 -0
- package/dist/vascend/context-window-compiler.js +1 -0
- package/dist/vascend/file-lock.d.ts +1 -0
- package/dist/vascend/file-lock.js +26 -0
- package/dist/vascend/granular-kingdom.d.ts +1 -0
- package/dist/vascend/granular-kingdom.js +52 -17
- package/dist/vascend/kingdom-bus.d.ts +1 -0
- package/dist/vascend/kingdom-bus.js +3 -0
- package/dist/vascend/kingdom-channel.d.ts +2 -0
- package/dist/vascend/kingdom-channel.js +3 -0
- package/dist/vascend/kingdom-live.d.ts +32 -4
- package/dist/vascend/kingdom-live.js +149 -13
- package/dist/vascend/kingdom-run.d.ts +5 -1
- package/dist/vascend/kingdom-run.js +6 -2
- package/dist/vascend/kingdom.d.ts +3 -0
- package/dist/vascend/kingdom.js +22 -11
- package/dist/vascend/memory-packet.d.ts +2 -1
- package/dist/vascend/memory-packet.js +31 -44
- package/dist/vascend/memory-store.d.ts +1 -0
- package/dist/vascend/memory-store.js +12 -4
- package/dist/vascend/plan-store.d.ts +3 -1
- package/dist/vascend/plan-store.js +6 -4
- package/dist/vascend/room-memory.d.ts +13 -2
- package/dist/vascend/room-memory.js +48 -6
- package/dist/vascend/runtime.d.ts +3 -11
- package/dist/vascend/runtime.js +11 -123
- package/dist/vascend/session-search.js +2 -0
- package/dist/vascend/team-session.js +3 -2
- package/dist/vascend/vision.d.ts +3 -1
- package/dist/vascend/vision.js +6 -4
- package/dist/vascend/worker-session.d.ts +20 -0
- package/dist/vascend/worker-session.js +83 -0
- package/package.json +2 -2
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { shimmerText } from "./compaction-status.js";
|
|
2
|
+
// A short, non-scrolling transition from the first prompt to its saved title.
|
|
3
|
+
export class SessionNamingNotice {
|
|
4
|
+
refresh;
|
|
5
|
+
title = "";
|
|
6
|
+
startedAt = 0;
|
|
7
|
+
pending = false;
|
|
8
|
+
timer;
|
|
9
|
+
constructor(refresh) {
|
|
10
|
+
this.refresh = refresh;
|
|
11
|
+
}
|
|
12
|
+
show(title) {
|
|
13
|
+
this.clear();
|
|
14
|
+
this.title = title;
|
|
15
|
+
this.startedAt = Date.now();
|
|
16
|
+
let showingTitle = false;
|
|
17
|
+
this.timer = setInterval(() => {
|
|
18
|
+
if (!this.pending && Date.now() - this.startedAt >= 5900)
|
|
19
|
+
this.clear();
|
|
20
|
+
else if (this.pending || Date.now() - this.startedAt < 900 || !showingTitle) {
|
|
21
|
+
showingTitle = Date.now() - this.startedAt >= 900;
|
|
22
|
+
this.refresh();
|
|
23
|
+
}
|
|
24
|
+
}, 100);
|
|
25
|
+
this.timer.unref?.();
|
|
26
|
+
this.refresh();
|
|
27
|
+
}
|
|
28
|
+
start() {
|
|
29
|
+
this.show("Renaming");
|
|
30
|
+
this.pending = true;
|
|
31
|
+
}
|
|
32
|
+
finish(title) {
|
|
33
|
+
this.show(title);
|
|
34
|
+
this.startedAt = Date.now() - 900;
|
|
35
|
+
this.refresh();
|
|
36
|
+
}
|
|
37
|
+
lines(now = Date.now()) {
|
|
38
|
+
if (!this.title)
|
|
39
|
+
return [];
|
|
40
|
+
const elapsed = Math.max(0, now - this.startedAt);
|
|
41
|
+
if (!this.pending && elapsed >= 5900)
|
|
42
|
+
return [];
|
|
43
|
+
if (!this.pending && elapsed >= 900)
|
|
44
|
+
return [this.title];
|
|
45
|
+
// Una sola riga stabile: lo shimmer cambia soltanto luminosita', senza
|
|
46
|
+
// griglia 3x3, cubi o glyph che saltano tra un frame e l'altro.
|
|
47
|
+
const frame = Math.floor(elapsed / 100);
|
|
48
|
+
return [shimmerText("Renaming", frame)];
|
|
49
|
+
}
|
|
50
|
+
line(now = Date.now()) { return this.lines(now).join("\n"); }
|
|
51
|
+
clear() {
|
|
52
|
+
if (this.timer)
|
|
53
|
+
clearInterval(this.timer);
|
|
54
|
+
this.timer = undefined;
|
|
55
|
+
this.title = "";
|
|
56
|
+
this.pending = false;
|
|
57
|
+
this.refresh();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { fitLine, oneLine, padLine, terminalWidth } from "./terminal-text.js";
|
|
2
|
+
// A dense session list leaves the conversation title most of the screen width.
|
|
3
|
+
export function renderSessionPicker(opts) {
|
|
4
|
+
const width = Math.max(1, opts.columns - 2);
|
|
5
|
+
const t = opts.theme;
|
|
6
|
+
const row = (text) => " " + fitLine(text, width);
|
|
7
|
+
const page = Math.max(1, opts.pageSize);
|
|
8
|
+
const index = Math.max(0, Math.min(opts.index, opts.options.length - 1));
|
|
9
|
+
const start = Math.max(0, Math.min(index - Math.floor(page / 2), opts.options.length - page));
|
|
10
|
+
const visible = opts.options.slice(start, start + page);
|
|
11
|
+
const filter = width >= 40 ? "[Cwd] Sort:[Updated]" : "";
|
|
12
|
+
const searchWidth = Math.max(1, width - (filter ? filter.length + 2 : 0));
|
|
13
|
+
const search = padLine(oneLine(opts.query || "Type to search"), searchWidth);
|
|
14
|
+
const lines = [row(t.accent(opts.title)), "", row(t.muted(search) + " " + t.silver(filter)), ""];
|
|
15
|
+
const ageWidth = Math.min(10, Math.max(3, Math.floor(width / 5)));
|
|
16
|
+
for (const [offset, option] of visible.entries()) {
|
|
17
|
+
const active = start + offset === index;
|
|
18
|
+
const text = (active ? "› " : " ") + padLine(option.age || "", ageWidth)
|
|
19
|
+
+ " " + oneLine(option.label);
|
|
20
|
+
lines.push(row(active ? t.selected(padLine(text, width)) : t.silver(text)));
|
|
21
|
+
}
|
|
22
|
+
if (!visible.length)
|
|
23
|
+
lines.push(row(t.muted("No matching sessions")));
|
|
24
|
+
while (lines.length < page + 4)
|
|
25
|
+
lines.push("");
|
|
26
|
+
const count = `${opts.options.length ? index + 1 : 0} / ${opts.options.length}`
|
|
27
|
+
+ (opts.totalCount !== undefined && opts.totalCount !== opts.options.length ? ` (${opts.totalCount} total)` : "");
|
|
28
|
+
lines.push(row(t.muted("─".repeat(Math.max(0, width - terminalWidth(count) - 1)) + " " + count)));
|
|
29
|
+
lines.push(row(t.silver("enter resume esc back tab details")));
|
|
30
|
+
lines.push(row(t.muted("↑/↓ browse PgUp/PgDn page ctrl+u clear search")));
|
|
31
|
+
return lines;
|
|
32
|
+
}
|
|
@@ -7,4 +7,5 @@ export declare function replaySession(session: Session, control: {
|
|
|
7
7
|
writeConversation?(text: string): void;
|
|
8
8
|
replayConversation?(renderer: (columns: number) => string): void;
|
|
9
9
|
}, theme: TuiTheme, columns: number): void;
|
|
10
|
+
export declare function visibleUserText(text: string): string;
|
|
10
11
|
export declare function resumeReplay(session: Session, theme: TuiTheme, columns: number): string;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { renderAssistantSegment, renderUserMessage } from "./branding.js";
|
|
2
|
-
import {
|
|
3
|
-
import { plainText, wrapText } from "./terminal-text.js";
|
|
2
|
+
import { commandOutput, commandPreview, commandResultState, commandSource } from "./command-transcript.js";
|
|
4
3
|
import { tr } from "./i18n.js";
|
|
4
|
+
import { livePreview, liveResultSummary, backgroundResultSummary } from "./live-output.js";
|
|
5
|
+
import { mcpName, mcpOutput, mcpPreview } from "./mcp-transcript.js";
|
|
6
|
+
import { loadedSkill, skillPreview } from "./skill-transcript.js";
|
|
7
|
+
import { fitLine, terminalWidth } from "./terminal-text.js";
|
|
8
|
+
import { toolArg, toolLabel } from "./tool-presentation.js";
|
|
5
9
|
// The model's history() is compacted; the visible conversation uses the
|
|
6
10
|
// original nodes on the selected branch, including archived messages.
|
|
7
11
|
export function sessionConversation(session) {
|
|
@@ -13,25 +17,51 @@ export function replaySession(session, control, theme, columns) {
|
|
|
13
17
|
else
|
|
14
18
|
(control.writeConversation ?? control.write).call(control, resumeReplay(session, theme, columns));
|
|
15
19
|
}
|
|
20
|
+
function attachmentLabel(part) {
|
|
21
|
+
const source = part.source && !part.source.startsWith("data:") ? ` · ${part.source}` : "";
|
|
22
|
+
return `[${tr("immagine", "image")} · ${part.mime}${source}]`;
|
|
23
|
+
}
|
|
24
|
+
function genericToolHeader(call, theme, columns) {
|
|
25
|
+
const label = toolLabel(call.name);
|
|
26
|
+
const arg = livePreview(toolArg(call), columns - terminalWidth(label) - 9);
|
|
27
|
+
return fitLine(" " + theme.muted("◇ " + label) + " " + theme.dim(arg), Math.max(1, columns - 1));
|
|
28
|
+
}
|
|
29
|
+
export function visibleUserText(text) {
|
|
30
|
+
// Talos persists the exact request sent to the model. With the Vascend
|
|
31
|
+
// workflow that request contains an internal GOAL/MODE envelope, while the
|
|
32
|
+
// live chat shows only what the user typed. Resume must preserve that same
|
|
33
|
+
// presentation without changing the stored model history.
|
|
34
|
+
if (!text.includes("[VASCEND_GOAL]"))
|
|
35
|
+
return text;
|
|
36
|
+
const markers = [...text.matchAll(/(?:^|\r?\n)INPUT_UTENTE\r?\n/g)];
|
|
37
|
+
const marker = markers.at(-1);
|
|
38
|
+
if (marker?.index === undefined)
|
|
39
|
+
return text;
|
|
40
|
+
return text.slice(marker.index + marker[0].length).trim();
|
|
41
|
+
}
|
|
42
|
+
// A resumed session uses the same compact blocks as a live turn. No resume
|
|
43
|
+
// banner or restored-context footer is inserted into the conversation.
|
|
16
44
|
export function resumeReplay(session, theme, columns) {
|
|
17
45
|
const messages = sessionConversation(session);
|
|
18
|
-
const width = Math.max(2, columns - 5);
|
|
19
46
|
const out = [];
|
|
20
47
|
const calls = new Map();
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
48
|
+
const backgroundPolls = new Map();
|
|
49
|
+
const results = new Map();
|
|
50
|
+
for (const message of messages) {
|
|
51
|
+
for (const part of message.content)
|
|
52
|
+
if (part.type === "tool_result" && !results.has(part.callId))
|
|
53
|
+
results.set(part.callId, part);
|
|
54
|
+
}
|
|
55
|
+
const attachment = (part, role) => {
|
|
56
|
+
const caption = attachmentLabel(part);
|
|
57
|
+
if (role === "user")
|
|
58
|
+
out.push(renderUserMessage(caption, theme, columns));
|
|
59
|
+
else
|
|
60
|
+
out.push(fitLine(theme.dark(" │ ") + theme.dark("⎿ ") + theme.dim(caption), Math.max(1, columns - 1)) + "\n");
|
|
30
61
|
};
|
|
31
|
-
heading(tr("ripresa sessione {name}({id}) · {count} messaggi", "Resumed session {name}({id}) · {count} messages", {
|
|
32
|
-
name: session.name ? `"${session.name}" ` : "", id: session.id, count: messages.length,
|
|
33
|
-
}), theme.bold);
|
|
34
62
|
for (const message of messages) {
|
|
63
|
+
if (message.role === "system")
|
|
64
|
+
continue;
|
|
35
65
|
let text = "";
|
|
36
66
|
const flushText = () => {
|
|
37
67
|
if (!text.trim()) {
|
|
@@ -39,13 +69,9 @@ export function resumeReplay(session, theme, columns) {
|
|
|
39
69
|
return;
|
|
40
70
|
}
|
|
41
71
|
if (message.role === "user")
|
|
42
|
-
out.push(renderUserMessage(text, theme, columns));
|
|
72
|
+
out.push(renderUserMessage(visibleUserText(text), theme, columns));
|
|
43
73
|
else if (message.role === "assistant")
|
|
44
74
|
out.push(renderAssistantSegment(text, theme, columns));
|
|
45
|
-
else {
|
|
46
|
-
heading(message.role === "system" ? tr("Sistema", "System") : tr("Strumento", "Tool"));
|
|
47
|
-
literal(text);
|
|
48
|
-
}
|
|
49
75
|
text = "";
|
|
50
76
|
};
|
|
51
77
|
for (const part of message.content) {
|
|
@@ -54,25 +80,68 @@ export function resumeReplay(session, theme, columns) {
|
|
|
54
80
|
continue;
|
|
55
81
|
}
|
|
56
82
|
flushText();
|
|
83
|
+
if (part.type === "image") {
|
|
84
|
+
attachment(part, message.role);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
57
87
|
if (part.type === "tool_call") {
|
|
58
88
|
calls.set(part.id, part);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
89
|
+
if (part.name === "bash_output")
|
|
90
|
+
continue;
|
|
91
|
+
if (part.name === "vascend_mark")
|
|
92
|
+
continue;
|
|
93
|
+
const result = results.get(part.id);
|
|
94
|
+
const skill = loadedSkill(part);
|
|
95
|
+
if (skill) {
|
|
96
|
+
out.push("\n" + skillPreview(skill, theme, columns, result?.isError ? "error" : result ? "ok" : "running").join("\n") + "\n");
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (mcpName(part.name)) {
|
|
100
|
+
out.push("\n" + mcpPreview(part, theme, columns, result?.isError ? "error" : result ? "ok" : "stopped").join("\n") + "\n");
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const source = part.name === "bash" ? commandSource(part) : undefined;
|
|
104
|
+
if (source !== undefined) {
|
|
105
|
+
out.push("\n" + commandPreview(source, theme, columns, result ? commandResultState(result.output, result.isError) : "stopped").lines.join("\n") + "\n");
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
out.push("\n" + genericToolHeader(part, theme, columns) + "\n");
|
|
109
|
+
continue;
|
|
62
110
|
}
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
111
|
+
if (part.type === "tool_result") {
|
|
112
|
+
const call = calls.get(part.callId);
|
|
113
|
+
if (call?.name === "bash_output") {
|
|
114
|
+
const id = String(call.input["id"] ?? call.id);
|
|
115
|
+
const snapshot = JSON.stringify([part.isError ?? false, part.output, part.media]);
|
|
116
|
+
if (backgroundPolls.get(id) === snapshot)
|
|
117
|
+
continue;
|
|
118
|
+
backgroundPolls.set(id, snapshot);
|
|
119
|
+
out.push("\n" + fitLine(" " + (part.isError ? theme.red("× ") : theme.muted("◇ "))
|
|
120
|
+
+ theme.muted(toolLabel("bash_output")), Math.max(1, columns - 1)) + "\n");
|
|
121
|
+
}
|
|
122
|
+
if (call?.name === "vascend_mark")
|
|
123
|
+
continue;
|
|
124
|
+
const skill = call && loadedSkill(call);
|
|
125
|
+
if (skill) {
|
|
126
|
+
if (part.isError)
|
|
127
|
+
out.push(commandOutput(part.output, theme, columns, true).join("\n") + "\n");
|
|
128
|
+
}
|
|
129
|
+
else if (call && mcpName(call.name)) {
|
|
130
|
+
out.push(mcpOutput(part.output, part.isError, theme, columns).join("\n") + "\n");
|
|
131
|
+
}
|
|
132
|
+
else if (call?.name === "bash" && commandSource(call) !== undefined) {
|
|
133
|
+
out.push(commandOutput(part.output, theme, columns, Boolean(part.isError)).join("\n") + "\n");
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const summary = call?.name === "bash_output" ? backgroundResultSummary(part.output, columns) : liveResultSummary(part.output, null, columns);
|
|
137
|
+
out.push(fitLine(theme.dark(" └ ") + (part.isError ? theme.red("× ") : "")
|
|
138
|
+
+ (part.isError ? theme.red(summary) : theme.dim(summary)), Math.max(1, columns - 1)) + "\n");
|
|
139
|
+
}
|
|
67
140
|
for (const media of part.media ?? [])
|
|
68
|
-
attachment(media);
|
|
69
|
-
}
|
|
70
|
-
else if (part.type === "image") {
|
|
71
|
-
attachment(part);
|
|
141
|
+
attachment(media, "tool");
|
|
72
142
|
}
|
|
73
143
|
}
|
|
74
144
|
flushText();
|
|
75
145
|
}
|
|
76
|
-
heading(tr("contesto ripreso: continua pure da qui", "Context restored: continue from here"));
|
|
77
146
|
return out.join("");
|
|
78
147
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Wrap = (text: string, width: number, wordWrap: boolean) => readonly string[];
|
|
2
|
+
export declare class SessionTranscriptLayout {
|
|
3
|
+
private readonly wrap;
|
|
4
|
+
private readonly sessions;
|
|
5
|
+
constructor(wrap?: Wrap);
|
|
6
|
+
retain(ids: ReadonlySet<string>): void;
|
|
7
|
+
lines(id: string, values: readonly string[], width: number): readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { wrapText } from "./terminal-text.js";
|
|
2
|
+
// Reflow only changed blocks (usually the streaming tail), not the complete
|
|
3
|
+
// worker transcript on every token, timer tick or selection change. This is a
|
|
4
|
+
// bounded rendering cache; the session store remains the source of truth.
|
|
5
|
+
export class SessionTranscriptLayout {
|
|
6
|
+
wrap;
|
|
7
|
+
sessions = new Map();
|
|
8
|
+
constructor(wrap = wrapText) {
|
|
9
|
+
this.wrap = wrap;
|
|
10
|
+
}
|
|
11
|
+
retain(ids) {
|
|
12
|
+
for (const id of this.sessions.keys())
|
|
13
|
+
if (!ids.has(id))
|
|
14
|
+
this.sessions.delete(id);
|
|
15
|
+
}
|
|
16
|
+
lines(id, values, width) {
|
|
17
|
+
const blocks = values.filter(Boolean);
|
|
18
|
+
const cached = this.sessions.get(id);
|
|
19
|
+
const previous = cached?.width === width ? cached : undefined;
|
|
20
|
+
if (previous && blocks.length === previous.blocks.length && blocks.every((text, i) => text === previous.blocks[i])) {
|
|
21
|
+
this.sessions.delete(id);
|
|
22
|
+
this.sessions.set(id, previous);
|
|
23
|
+
return previous.lines;
|
|
24
|
+
}
|
|
25
|
+
const wrapped = new Map();
|
|
26
|
+
const lines = [];
|
|
27
|
+
for (const text of blocks) {
|
|
28
|
+
const rows = wrapped.get(text) ?? previous?.wrapped.get(text) ?? this.wrap(text, width, true);
|
|
29
|
+
wrapped.set(text, rows);
|
|
30
|
+
if (lines.length)
|
|
31
|
+
lines.push("");
|
|
32
|
+
for (const row of rows)
|
|
33
|
+
lines.push(row);
|
|
34
|
+
}
|
|
35
|
+
this.sessions.delete(id);
|
|
36
|
+
this.sessions.set(id, { width, blocks, wrapped, lines });
|
|
37
|
+
if (this.sessions.size > 8)
|
|
38
|
+
this.sessions.delete(this.sessions.keys().next().value);
|
|
39
|
+
return lines;
|
|
40
|
+
}
|
|
41
|
+
}
|
package/dist/tui/startup.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { TALOS_VERSION } from "../core/version.js";
|
|
2
|
+
import { talosArtwork } from "./talos-art.js";
|
|
2
3
|
import { tr } from "./i18n.js";
|
|
3
4
|
import { fitLine, oneLine, padLine, terminalWidth, wrapText } from "./terminal-text.js";
|
|
4
5
|
function modelLabel(d, t, width) {
|
|
@@ -11,51 +12,80 @@ export function permissionLabel(mode = "normal") {
|
|
|
11
12
|
const labels = { normal: tr("Conferma azioni", "Ask before actions"), acceptEdits: tr("Modifiche automatiche", "Automatic edits"), auto: tr("Senza conferme", "No confirmations") };
|
|
12
13
|
return labels[mode] ?? labels["normal"];
|
|
13
14
|
}
|
|
14
|
-
//
|
|
15
|
+
// Keep the project name visible instead of wrapping a Windows path mid-word.
|
|
16
|
+
// Both path separators are supported, independently of the host platform.
|
|
17
|
+
function workspaceLabel(value, width) {
|
|
18
|
+
const path = oneLine(value);
|
|
19
|
+
if (terminalWidth(path) <= width)
|
|
20
|
+
return path;
|
|
21
|
+
const end = path.replace(/[\\/]+$/, "");
|
|
22
|
+
const split = Math.max(end.lastIndexOf("/"), end.lastIndexOf("\\"));
|
|
23
|
+
if (split < 0)
|
|
24
|
+
return fitLine(path, width);
|
|
25
|
+
const suffix = "…" + end.slice(split);
|
|
26
|
+
if (terminalWidth(suffix) >= width)
|
|
27
|
+
return fitLine(suffix, width);
|
|
28
|
+
const budget = width - terminalWidth(suffix);
|
|
29
|
+
const prefix = oneLine(fitLine(end.slice(0, split), budget, false));
|
|
30
|
+
const boundary = Math.max(prefix.lastIndexOf("/"), prefix.lastIndexOf("\\"));
|
|
31
|
+
return (boundary >= 0 ? prefix.slice(0, boundary + 1) : prefix) + suffix;
|
|
32
|
+
}
|
|
33
|
+
// The native renderer refreshes this header when the settings or viewport change.
|
|
15
34
|
export function buildTerminalStartup(d, t) {
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
35
|
+
const available = Math.max(0, Math.floor(d.width) - 1);
|
|
36
|
+
const framed = available >= 24;
|
|
37
|
+
const inset = framed ? " " : "";
|
|
38
|
+
const inner = framed ? Math.min(88, available - 7) : available;
|
|
39
|
+
const fullContent = Math.max(1, inner - terminalWidth(inset) * 2);
|
|
40
|
+
const art = talosArtwork(Math.min(30, fullContent));
|
|
41
|
+
const artWidth = Math.max(0, ...art.map(terminalWidth));
|
|
42
|
+
const artBeside = art.length > 0 && fullContent >= artWidth + 3 + 35;
|
|
43
|
+
const content = artBeside ? fullContent - artWidth - 3 : fullContent;
|
|
44
|
+
const brand = t.accent(">_ ") + t.bold(t.silver("Vascend Talos"));
|
|
45
|
+
const version = t.muted(`v${TALOS_VERSION}`);
|
|
46
|
+
const gap = content - terminalWidth(brand) - terminalWidth(version);
|
|
47
|
+
const body = gap >= 2
|
|
48
|
+
? [brand + " ".repeat(gap) + version]
|
|
49
|
+
: [fitLine(brand, content), fitLine(version, content)];
|
|
50
|
+
if (content >= 30)
|
|
51
|
+
body.push(t.muted(fitLine(tr("Sviluppato da Vascend Technologies", "Developed by Vascend Technologies"), content)));
|
|
52
|
+
body.push("");
|
|
53
|
+
const labels = {
|
|
54
|
+
model: tr("Modello", "Model"), workspace: "Workspace",
|
|
55
|
+
permissions: tr("Permessi", "Permissions"), chat: "Chat",
|
|
56
|
+
};
|
|
57
|
+
const labelWidth = Math.max(...Object.values(labels).map(terminalWidth)) + 2;
|
|
58
|
+
const stacked = content < labelWidth + 22;
|
|
59
|
+
const valueWidth = Math.max(1, content - (stacked ? 2 : labelWidth));
|
|
60
|
+
const field = (label, rows) => {
|
|
26
61
|
if (stacked) {
|
|
27
62
|
body.push(t.muted(label));
|
|
28
|
-
body.push(...
|
|
63
|
+
body.push(...rows.map(line => " " + line));
|
|
29
64
|
}
|
|
30
65
|
else {
|
|
31
|
-
|
|
32
|
-
body.push(...rows.map((line, i) => (i === 0 ? t.muted(label.padEnd(labelWidth)) : " ".repeat(labelWidth)) + paint(line)));
|
|
66
|
+
body.push(...rows.map((line, index) => (index === 0 ? t.muted(padLine(label, labelWidth)) : " ".repeat(labelWidth)) + line));
|
|
33
67
|
}
|
|
34
68
|
};
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const shortcut = tr(" /model cambia", " /model change");
|
|
41
|
-
if (terminalWidth(model) + shortcut.length <= inner - labelWidth)
|
|
42
|
-
model += t.accent(shortcut);
|
|
43
|
-
body.push(t.muted(tr("modello:", "model:").padEnd(labelWidth)) + model);
|
|
44
|
-
}
|
|
45
|
-
field(tr("cartella:", "directory:"), d.cwd, t.silver);
|
|
46
|
-
field(tr("permessi:", "permissions:"), permission, permissionPaint);
|
|
69
|
+
field(labels.model, [modelLabel(d, t, valueWidth)]);
|
|
70
|
+
field(labels.workspace, [t.silver(workspaceLabel(d.cwd, valueWidth))]);
|
|
71
|
+
const permission = (d.permissions === "auto" ? "! " : "") + permissionLabel(d.permissions);
|
|
72
|
+
const permissionPaint = d.permissions === "auto" ? t.yellow : t.silver;
|
|
73
|
+
field(labels.permissions, wrapText(permission, valueWidth, true).map(line => permissionPaint(line)));
|
|
47
74
|
if (d.sessionLabel)
|
|
48
|
-
field(
|
|
49
|
-
if (
|
|
50
|
-
return body.map(line => fitLine(line,
|
|
51
|
-
const
|
|
52
|
-
|
|
75
|
+
field(labels.chat, [t.silver(fitLine(oneLine(d.sessionLabel), valueWidth))]);
|
|
76
|
+
if (!framed)
|
|
77
|
+
return body.map(line => fitLine(line, available));
|
|
78
|
+
const decorated = artBeside
|
|
79
|
+
? Array.from({ length: Math.max(body.length, art.length) }, (_, i) => padLine(body[i] ?? "", content) + " " + (art[i] ?? ""))
|
|
80
|
+
: art.length ? [...body, "", ...art.map(line => " ".repeat(Math.floor((fullContent - artWidth) / 2)) + line)] : body;
|
|
81
|
+
const edge = (left, right) => " " + t.chatBorder(left + "─".repeat(inner + 2) + right);
|
|
82
|
+
return [edge("╭", "╮"), ...decorated.map(line => " " + t.chatBorder("│ ") + padLine(inset + fitLine(line, fullContent), inner) + t.chatBorder(" │")), edge("╰", "╯")];
|
|
53
83
|
}
|
|
54
84
|
export function buildStartupRuntime(d, t) {
|
|
55
85
|
const width = Math.max(1, d.width - 3);
|
|
56
86
|
let line = modelLabel(d, t, width);
|
|
57
87
|
const remaining = width - terminalWidth(line) - 3;
|
|
58
88
|
if (remaining >= 8)
|
|
59
|
-
line += t.dim(" · ") + t.muted(
|
|
60
|
-
return fitLine(" " + line, Math.max(
|
|
89
|
+
line += t.dim(" · ") + t.muted(workspaceLabel(d.cwd, remaining));
|
|
90
|
+
return fitLine(" " + line, Math.max(0, d.width - 1));
|
|
61
91
|
}
|
|
@@ -35,4 +35,4 @@ export declare const DEFAULT_ORDER: readonly string[];
|
|
|
35
35
|
export declare const BUILTIN_SEGMENTS: Readonly<Record<string, SegmentRender>>;
|
|
36
36
|
export declare function buildStatusLine(d: StatusData, theme: TuiTheme, opts?: StatusOptions): string;
|
|
37
37
|
export declare function buildTerminalHeader(d: StatusData, theme: TuiTheme): readonly string[];
|
|
38
|
-
export declare function buildTerminalWelcome(width: number, theme: TuiTheme): string;
|
|
38
|
+
export declare function buildTerminalWelcome(width: number, theme: TuiTheme, accountNote?: string): string;
|
package/dist/tui/status-line.js
CHANGED
|
@@ -104,17 +104,13 @@ export const BUILTIN_SEGMENTS = {
|
|
|
104
104
|
const head = r.castles > 1 ? `◆ ${r.castles} gruppi · ${r.done}/${r.total}` : `◆ ${r.done}/${r.total}`;
|
|
105
105
|
return t.silver(head) + t.dark(" Ctrl+G");
|
|
106
106
|
},
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
// perche' StatusData.bg viene ricalcolato dal backgroundManager.
|
|
107
|
+
// Count only live commands. Completed output remains available in /bg,
|
|
108
|
+
// but its history must not look like terminals left open.
|
|
110
109
|
bg: (d, t) => {
|
|
111
110
|
const b = d.bg;
|
|
112
|
-
if (!b ||
|
|
111
|
+
if (!b || b.running <= 0)
|
|
113
112
|
return null;
|
|
114
|
-
|
|
115
|
-
? t.accent("⚙ ") + t.silver(`${b.running}/${b.total} bg`)
|
|
116
|
-
: t.dark("⚙ ") + t.muted(`${b.total} bg`);
|
|
117
|
-
return head + t.dark(" /bg");
|
|
113
|
+
return t.silver(tr("{count} bg attivi", "{count} bg running", { count: b.running })) + t.dark(" /bg");
|
|
118
114
|
},
|
|
119
115
|
time: (_d, t) => {
|
|
120
116
|
const dt = new Date();
|
|
@@ -205,9 +201,21 @@ export function buildTerminalHeader(d, theme) {
|
|
|
205
201
|
const context = theme.muted(tr("Progetto ", "Project ")) + theme.silver(project) + theme.muted(chat);
|
|
206
202
|
return [...identity, context].map((line) => " " + fitLine(line, width));
|
|
207
203
|
}
|
|
208
|
-
export function buildTerminalWelcome(width, theme) {
|
|
209
|
-
const
|
|
210
|
-
|
|
204
|
+
export function buildTerminalWelcome(width, theme, accountNote = "") {
|
|
205
|
+
const available = Math.max(0, width - 3);
|
|
206
|
+
const shortcuts = [
|
|
207
|
+
{ command: "/model", label: tr("Modello", "Model") },
|
|
208
|
+
{ command: "/chats", label: tr("Chat recenti", "Recent chats") },
|
|
209
|
+
{ command: "/help", label: tr("Guida", "Help") },
|
|
211
210
|
];
|
|
212
|
-
|
|
211
|
+
const items = shortcuts.map(({ command, label }) => theme.accent(command) + " " + theme.muted(label));
|
|
212
|
+
const inline = items.join(theme.dim(" · "));
|
|
213
|
+
const lines = terminalWidth(inline) <= available ? [inline] : shortcuts.flatMap(({ command, label }) => {
|
|
214
|
+
if (terminalWidth(command + " " + label) <= available)
|
|
215
|
+
return [theme.accent(command) + " " + theme.muted(label)];
|
|
216
|
+
return [theme.accent(fitLine(command, available)), ...wrapText(label, Math.max(1, available), true).map(line => theme.muted(line))];
|
|
217
|
+
});
|
|
218
|
+
if (accountNote)
|
|
219
|
+
lines.push(...wrapText(oneLine(accountNote), Math.max(1, available), true).map(line => theme.muted(line)));
|
|
220
|
+
return "\n" + lines.map(line => fitLine(" " + line, Math.max(0, width - 1))).join("\n") + "\n\n";
|
|
213
221
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { tr } from "./i18n.js";
|
|
2
|
+
import { wrapText } from "./terminal-text.js";
|
|
3
|
+
function reason(code) {
|
|
4
|
+
switch (code) {
|
|
5
|
+
case "ENOSPC": return tr("disco pieno", "disk full");
|
|
6
|
+
case "EDQUOT": return tr("quota disco esaurita", "disk quota exceeded");
|
|
7
|
+
case "EACCES":
|
|
8
|
+
case "EPERM": return tr("permesso negato", "permission denied");
|
|
9
|
+
case "EROFS": return tr("disco in sola lettura", "read-only filesystem");
|
|
10
|
+
case "EIO": return tr("errore di lettura/scrittura", "I/O error");
|
|
11
|
+
case "ENOENT":
|
|
12
|
+
case "ENOTDIR":
|
|
13
|
+
case "EISDIR": return tr("percorso non disponibile", "path unavailable");
|
|
14
|
+
case "EMFILE":
|
|
15
|
+
case "ENFILE": return tr("troppi file aperti", "too many open files");
|
|
16
|
+
case "EBUSY": return tr("file occupato", "file busy");
|
|
17
|
+
default: return tr("errore di salvataggio", "storage error");
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function storageWarning(issues, columns, paint = (text) => text) {
|
|
21
|
+
// A still-failing store takes precedence over the short recovery notice.
|
|
22
|
+
const issue = issues.find(item => !item.recovered && item.kind === "transcript")
|
|
23
|
+
?? issues.find(item => !item.recovered) ?? issues[0];
|
|
24
|
+
if (!issue)
|
|
25
|
+
return [];
|
|
26
|
+
const target = issue.kind === "transcript" ? tr("conversazione", "conversation")
|
|
27
|
+
: issue.kind === "queue" ? tr("coda", "queue") : tr("consumi token", "token usage");
|
|
28
|
+
const detail = reason(issue.code) + (issue.code === "UNKNOWN" ? "" : ` - ${issue.code}`);
|
|
29
|
+
const text = issue.recovered
|
|
30
|
+
? tr("Salvataggio {target} ripreso · alcuni dati precedenti potrebbero mancare", "Saving {target} resumed · some earlier data may be missing", { target })
|
|
31
|
+
: columns < 65
|
|
32
|
+
? tr("{target}: dati non salvati ({code})", "{target}: data not saved ({code})", { target, code: issue.code === "UNKNOWN" ? detail : issue.code })
|
|
33
|
+
: tr("Salvataggio {target} fallito ({reason}) · i dati recenti potrebbero non essere salvati", "Saving {target} failed ({reason}) · recent data may not be saved", { target, reason: detail });
|
|
34
|
+
return wrapText("! " + text, Math.max(1, columns - 3), true).map(row => paint(" " + row));
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const TALOS_LOGO_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1536 1536\" width=\"1536\" height=\"1536\">\n<g transform=\"translate(40, 283) scale(0.9479)\">\n<g transform=\"translate(0.000000,1024.000000) scale(0.100000,-0.100000)\"\nfill=\"#ffffff\" stroke=\"none\">\n<path d=\"M4090 8420 l0 -1529 143 -3 142 -3 0 -507 c0 -278 -4 -510 -8 -515\n-5 -4 -121 -8 -258 -7 l-249 1 0 -294 0 -293 -57 -63 c-31 -34 -169 -180 -306\n-325 -138 -144 -256 -271 -264 -282 -14 -20 -12 -20 619 -20 l633 0 -135 -133\nc-74 -73 -242 -237 -372 -364 l-238 -231 0 -331 0 -331 315 0 315 0 0 154 0\n154 78 4 c42 2 193 2 335 0 l257 -3 0 -289 0 -288 340 -228 340 -228 0 -258 0\n-258 190 0 190 0 0 -370 0 -370 170 0 169 0 3 -128 3 -127 213 -3 212 -2 0\n-165 0 -165 79 0 c43 0 105 -3 139 -6 l60 -7 -1 -141 -2 -141 535 0 535 0 3\n140 c2 96 6 142 15 147 7 4 67 8 135 8 l122 0 0 165 0 165 209 0 209 0 7 130\n8 130 168 0 169 0 0 370 0 370 190 0 190 0 0 243 c0 173 4 249 13 265 6 12\n158 121 337 241 l325 218 3 292 2 291 335 0 335 0 0 -155 0 -155 320 0 320 0\n0 325 0 325 -262 258 c-145 141 -311 304 -370 361 -60 58 -108 108 -108 113 0\n4 282 9 627 10 l627 3 -125 132 c-68 72 -211 224 -316 337 l-193 205 0 295 0\n296 -222 0 c-123 0 -240 3 -260 6 l-38 7 0 513 0 514 140 0 140 0 0 1530 0\n1530 -360 0 -360 0 0 -175 0 -175 -170 0 -170 0 0 -180 0 -180 -185 0 -185 0\n0 -180 0 -180 -187 -2 -188 -3 -3 -192 -2 -193 -190 0 -190 0 0 -295 0 -295\n-117 0 c-65 -1 -246 -2 -403 -3 l-285 -2 -3 -397 -2 -398 -595 0 -595 0 0 394\n0 394 -27 6 c-16 4 -143 6 -283 5 -140 -1 -308 -1 -372 -1 l-118 2 0 295 0\n295 -190 0 -190 0 -1 183 c-1 100 -1 185 0 190 0 4 -41 8 -91 8 -51 1 -111 2\n-133 3 -22 1 -66 3 -97 4 l-58 2 0 180 0 180 -185 0 -185 0 0 180 0 180 -170\n0 -170 0 0 175 0 175 -360 0 -360 0 0 -1530z m688 273 l2 -183 159 0 158 0 5\n-117 c3 -65 5 -197 4 -293 l-1 -175 155 -5 155 -5 3 -237 2 -237 148 -3 147\n-3 0 -103 0 -103 -415 -403 -415 -404 -3 364 -2 364 -160 0 -160 0 0 865 0\n866 108 -3 107 -3 3 -182z m6022 -678 l0 -865 -160 0 -160 0 0 -361 c0 -198\n-4 -359 -8 -357 -5 2 -194 183 -421 403 l-412 400 3 100 3 100 147 3 148 3 2\n237 3 237 158 3 157 3 0 294 0 295 165 0 165 0 0 185 0 185 105 0 105 0 0\n-865z m-850 -2857 l0 -178 -104 0 -104 0 -217 -225 -217 -225 -434 0 -434 0 0\n249 0 249 31 6 c17 3 156 6 308 6 l276 1 150 144 150 144 85 3 c47 2 181 3\n297 3 l213 0 0 -177z m-3790 27 l145 -145 307 0 308 0 0 -255 0 -255 -433 0\n-433 0 -218 225 -218 225 -104 0 -104 0 0 175 0 175 303 0 302 0 145 -145z\nm2260 -1305 l0 -320 215 0 215 0 0 -1045 0 -1045 -150 0 -150 0 0 -135 0 -135\n-870 0 -870 0 0 135 0 135 -150 0 -150 0 2 1042 3 1043 213 3 212 2 0 320 0\n320 740 0 740 0 0 -320z\"/>\n<path d=\"M7030 2095 l0 -315 135 0 135 0 0 -135 0 -135 380 0 380 0 0 135 0\n135 134 0 134 0 7 123 c3 67 5 208 3 312 l-3 190 -652 3 -653 2 0 -315z\"/>\n</g>\n</g>\n</svg>\n";
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// Bundled from vascend-horizon/assets/logo.svg, used by scripts/dev.js.
|
|
2
|
+
export const TALOS_LOGO_SVG = "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 1536 1536\" width=\"1536\" height=\"1536\">\n<g transform=\"translate(40, 283) scale(0.9479)\">\n<g transform=\"translate(0.000000,1024.000000) scale(0.100000,-0.100000)\"\nfill=\"#ffffff\" stroke=\"none\">\n<path d=\"M4090 8420 l0 -1529 143 -3 142 -3 0 -507 c0 -278 -4 -510 -8 -515\n-5 -4 -121 -8 -258 -7 l-249 1 0 -294 0 -293 -57 -63 c-31 -34 -169 -180 -306\n-325 -138 -144 -256 -271 -264 -282 -14 -20 -12 -20 619 -20 l633 0 -135 -133\nc-74 -73 -242 -237 -372 -364 l-238 -231 0 -331 0 -331 315 0 315 0 0 154 0\n154 78 4 c42 2 193 2 335 0 l257 -3 0 -289 0 -288 340 -228 340 -228 0 -258 0\n-258 190 0 190 0 0 -370 0 -370 170 0 169 0 3 -128 3 -127 213 -3 212 -2 0\n-165 0 -165 79 0 c43 0 105 -3 139 -6 l60 -7 -1 -141 -2 -141 535 0 535 0 3\n140 c2 96 6 142 15 147 7 4 67 8 135 8 l122 0 0 165 0 165 209 0 209 0 7 130\n8 130 168 0 169 0 0 370 0 370 190 0 190 0 0 243 c0 173 4 249 13 265 6 12\n158 121 337 241 l325 218 3 292 2 291 335 0 335 0 0 -155 0 -155 320 0 320 0\n0 325 0 325 -262 258 c-145 141 -311 304 -370 361 -60 58 -108 108 -108 113 0\n4 282 9 627 10 l627 3 -125 132 c-68 72 -211 224 -316 337 l-193 205 0 295 0\n296 -222 0 c-123 0 -240 3 -260 6 l-38 7 0 513 0 514 140 0 140 0 0 1530 0\n1530 -360 0 -360 0 0 -175 0 -175 -170 0 -170 0 0 -180 0 -180 -185 0 -185 0\n0 -180 0 -180 -187 -2 -188 -3 -3 -192 -2 -193 -190 0 -190 0 0 -295 0 -295\n-117 0 c-65 -1 -246 -2 -403 -3 l-285 -2 -3 -397 -2 -398 -595 0 -595 0 0 394\n0 394 -27 6 c-16 4 -143 6 -283 5 -140 -1 -308 -1 -372 -1 l-118 2 0 295 0\n295 -190 0 -190 0 -1 183 c-1 100 -1 185 0 190 0 4 -41 8 -91 8 -51 1 -111 2\n-133 3 -22 1 -66 3 -97 4 l-58 2 0 180 0 180 -185 0 -185 0 0 180 0 180 -170\n0 -170 0 0 175 0 175 -360 0 -360 0 0 -1530z m688 273 l2 -183 159 0 158 0 5\n-117 c3 -65 5 -197 4 -293 l-1 -175 155 -5 155 -5 3 -237 2 -237 148 -3 147\n-3 0 -103 0 -103 -415 -403 -415 -404 -3 364 -2 364 -160 0 -160 0 0 865 0\n866 108 -3 107 -3 3 -182z m6022 -678 l0 -865 -160 0 -160 0 0 -361 c0 -198\n-4 -359 -8 -357 -5 2 -194 183 -421 403 l-412 400 3 100 3 100 147 3 148 3 2\n237 3 237 158 3 157 3 0 294 0 295 165 0 165 0 0 185 0 185 105 0 105 0 0\n-865z m-850 -2857 l0 -178 -104 0 -104 0 -217 -225 -217 -225 -434 0 -434 0 0\n249 0 249 31 6 c17 3 156 6 308 6 l276 1 150 144 150 144 85 3 c47 2 181 3\n297 3 l213 0 0 -177z m-3790 27 l145 -145 307 0 308 0 0 -255 0 -255 -433 0\n-433 0 -218 225 -218 225 -104 0 -104 0 0 175 0 175 303 0 302 0 145 -145z\nm2260 -1305 l0 -320 215 0 215 0 0 -1045 0 -1045 -150 0 -150 0 0 -135 0 -135\n-870 0 -870 0 0 135 0 135 -150 0 -150 0 2 1042 3 1043 213 3 212 2 0 320 0\n320 740 0 740 0 0 -320z\"/>\n<path d=\"M7030 2095 l0 -315 135 0 135 0 0 -135 0 -135 380 0 380 0 0 135 0\n135 134 0 134 0 7 123 c3 67 5 208 3 312 l-3 190 -652 3 -653 2 0 -315z\"/>\n</g>\n</g>\n</svg>\n";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function talosArtwork(columns: number): readonly string[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { renderBlockLogo } from "./logo.js";
|
|
2
|
+
import { TALOS_LOGO_SVG } from "./talos-art-data.js";
|
|
3
|
+
// Match Horizon scripts/dev.js: same SVG, renderer and 30 x 14 dimensions.
|
|
4
|
+
// Bundle the SVG so installed Talos does not depend on the Horizon checkout.
|
|
5
|
+
const logo = Object.freeze(renderBlockLogo(TALOS_LOGO_SVG, { columns: 30, rows: 14, samples: 4, ansi: true }));
|
|
6
|
+
const empty = Object.freeze([]);
|
|
7
|
+
export function talosArtwork(columns) {
|
|
8
|
+
return columns >= 30 ? logo : empty;
|
|
9
|
+
}
|
package/dist/tui/task-labels.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { tr } from "./i18n.js";
|
|
2
|
+
// Translate the status at render time, leaving task/group identifiers intact.
|
|
3
|
+
export function taskActivityLabel(task, status) {
|
|
4
|
+
if (status === "running")
|
|
5
|
+
return tr("attività {task} avviata", "task {task} started", { task });
|
|
6
|
+
if (status === "failed")
|
|
7
|
+
return tr("attività {task} non riuscita", "task {task} failed", { task });
|
|
8
|
+
return tr("attività {task} completata", "task {task} completed", { task });
|
|
9
|
+
}
|
|
2
10
|
export const TASK_STATUS_LABELS = {
|
|
3
11
|
get pending() { return tr("da fare", "pending"); },
|
|
4
12
|
get running() { return tr("in corso", "running"); },
|
|
@@ -3,5 +3,6 @@ export declare function terminalWidth(value: string): number;
|
|
|
3
3
|
export declare function exceedsTerminalWidth(value: string, width: number): boolean;
|
|
4
4
|
export declare function fitLine(value: string, width: number, ellipsis?: boolean): string;
|
|
5
5
|
export declare function padLine(value: string, width: number): string;
|
|
6
|
-
export declare function
|
|
6
|
+
export declare function wrappedLineCount(value: string, width: number): number;
|
|
7
|
+
export declare function wrapText(value: string, width: number, wordWrap?: boolean, maxLines?: number): string[];
|
|
7
8
|
export declare function oneLine(value: string): string;
|