@huseynsnmz/pi-suite 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/LICENSE +21 -0
- package/README.md +88 -2
- package/SECURITY.md +5 -0
- package/package.json +112 -4
- package/packages/board/CHANGELOG.md +340 -0
- package/packages/board/CONTRIBUTING.md +7 -0
- package/packages/board/LICENSE +21 -0
- package/packages/board/README.md +88 -0
- package/packages/board/SECURITY.md +19 -0
- package/packages/board/docs/how-it-works.md +31 -0
- package/packages/board/docs/reference.md +278 -0
- package/packages/board/package.json +81 -0
- package/packages/board/runner/job-runner-legacy.mjs +68 -0
- package/packages/board/runner/job-runner.mjs +834 -0
- package/packages/board/runner/pty-runner-legacy.mjs +50 -0
- package/packages/board/runner/pty-runner.mjs +2175 -0
- package/packages/board/runner/state-coordinator.mjs +673 -0
- package/packages/board/runner/state-runner.mjs +203 -0
- package/packages/board/runner/title-runner.mjs +80 -0
- package/packages/board/src/commands/agent-board.ts +394 -0
- package/packages/board/src/commands/attach-decision.mjs +66 -0
- package/packages/board/src/commands/attach-flow.ts +251 -0
- package/packages/board/src/commands/bg.ts +103 -0
- package/packages/board/src/commands/board-model.ts +21 -0
- package/packages/board/src/core/atomic.mjs +184 -0
- package/packages/board/src/core/attach-delete.mjs +24 -0
- package/packages/board/src/core/attach-frame-proof.mjs +870 -0
- package/packages/board/src/core/attach-frame-reporter.mjs +648 -0
- package/packages/board/src/core/attach-frame-stream.mjs +266 -0
- package/packages/board/src/core/attach-view-proof.mjs +709 -0
- package/packages/board/src/core/auto-state.mjs +377 -0
- package/packages/board/src/core/background-runs.mjs +273 -0
- package/packages/board/src/core/code-refs-store.mjs +315 -0
- package/packages/board/src/core/code-refs.mjs +913 -0
- package/packages/board/src/core/config.mjs +77 -0
- package/packages/board/src/core/control-protocol.mjs +482 -0
- package/packages/board/src/core/coordinator-client.mjs +633 -0
- package/packages/board/src/core/coordinator-journal.mjs +282 -0
- package/packages/board/src/core/coordinator-protocol.mjs +14 -0
- package/packages/board/src/core/cwd-stats.mjs +114 -0
- package/packages/board/src/core/dashboard-render.mjs +10 -0
- package/packages/board/src/core/derive.mjs +117 -0
- package/packages/board/src/core/diagnostics.mjs +133 -0
- package/packages/board/src/core/double-press.mjs +26 -0
- package/packages/board/src/core/editor-state-reporter.mjs +112 -0
- package/packages/board/src/core/events.mjs +286 -0
- package/packages/board/src/core/evidence.mjs +245 -0
- package/packages/board/src/core/follow-up-queue.mjs +211 -0
- package/packages/board/src/core/foreground-preview-cache.mjs +117 -0
- package/packages/board/src/core/foreground-projection-cache.mjs +99 -0
- package/packages/board/src/core/frame-witness-update.mjs +35 -0
- package/packages/board/src/core/heuristics.mjs +313 -0
- package/packages/board/src/core/hold-input.mjs +27 -0
- package/packages/board/src/core/host-coordination.mjs +300 -0
- package/packages/board/src/core/host-crash.mjs +79 -0
- package/packages/board/src/core/host-probe.mjs +207 -0
- package/packages/board/src/core/host-protocol.mjs +73 -0
- package/packages/board/src/core/ids.mjs +35 -0
- package/packages/board/src/core/ime-cursor-coalesce.mjs +193 -0
- package/packages/board/src/core/invocation.mjs +43 -0
- package/packages/board/src/core/launch-options.mjs +418 -0
- package/packages/board/src/core/launch.mjs +157 -0
- package/packages/board/src/core/load-config.mjs +4 -0
- package/packages/board/src/core/locks.mjs +530 -0
- package/packages/board/src/core/paths.mjs +183 -0
- package/packages/board/src/core/pid.mjs +75 -0
- package/packages/board/src/core/prewarm-schedule.mjs +41 -0
- package/packages/board/src/core/process-identity-probe.mjs +228 -0
- package/packages/board/src/core/prompt-transport.mjs +13 -0
- package/packages/board/src/core/pty-attach-jiggle-controller.mjs +356 -0
- package/packages/board/src/core/pty-attach-jiggle-retry.mjs +109 -0
- package/packages/board/src/core/pty-attach-reconnect.mjs +61 -0
- package/packages/board/src/core/pty-attach-render.mjs +229 -0
- package/packages/board/src/core/pty-links.mjs +81 -0
- package/packages/board/src/core/pty-scroll.mjs +522 -0
- package/packages/board/src/core/pty-support.mjs +327 -0
- package/packages/board/src/core/repo.mjs +103 -0
- package/packages/board/src/core/rows.mjs +414 -0
- package/packages/board/src/core/screen-log-gc.mjs +198 -0
- package/packages/board/src/core/screen-log.mjs +160 -0
- package/packages/board/src/core/session-view.mjs +174 -0
- package/packages/board/src/core/slash-commands.mjs +52 -0
- package/packages/board/src/core/state-commands.mjs +757 -0
- package/packages/board/src/core/status-consistency.mjs +98 -0
- package/packages/board/src/core/steering-prompts.mjs +34 -0
- package/packages/board/src/core/steering.mjs +133 -0
- package/packages/board/src/core/stop-latch.mjs +41 -0
- package/packages/board/src/core/store.mjs +523 -0
- package/packages/board/src/core/suspend.mjs +36 -0
- package/packages/board/src/core/terminal-attach-client.mjs +981 -0
- package/packages/board/src/core/terminal-attach-protocol.mjs +460 -0
- package/packages/board/src/core/terminal-model.mjs +222 -0
- package/packages/board/src/core/terminal-query-sequences.mjs +239 -0
- package/packages/board/src/core/terminal-reply-guard.mjs +222 -0
- package/packages/board/src/core/terminal-snapshot.mjs +440 -0
- package/packages/board/src/core/test-knobs.mjs +28 -0
- package/packages/board/src/core/title.mjs +43 -0
- package/packages/board/src/core/types.mjs +414 -0
- package/packages/board/src/core/warm-host-sweeper.mjs +168 -0
- package/packages/board/src/core/worktree.mjs +65 -0
- package/packages/board/src/index.ts +303 -0
- package/packages/board/src/runtime/service.mjs +3222 -0
- package/packages/board/src/ui/chrome.ts +20 -0
- package/packages/board/src/ui/dashboard-decisions.mjs +55 -0
- package/packages/board/src/ui/dashboard-evidence.mjs +85 -0
- package/packages/board/src/ui/dashboard.ts +2237 -0
- package/packages/board/src/ui/double-left.ts +30 -0
- package/packages/board/src/ui/external-editor.ts +25 -0
- package/packages/board/src/ui/focus.ts +32 -0
- package/packages/board/src/ui/pty-attach.ts +3184 -0
- package/packages/board/src/ui/waiting.ts +30 -0
- package/packages/commands/LICENSE +21 -0
- package/packages/commands/README.md +19 -0
- package/packages/commands/package.json +22 -0
- package/packages/commands/src/index.ts +43 -0
- package/packages/commands/src/security-review.ts +109 -0
- package/packages/frame/CHANGELOG.md +113 -0
- package/packages/frame/CONTRIBUTING.md +7 -0
- package/packages/frame/LICENSE +22 -0
- package/packages/frame/README.md +105 -0
- package/packages/frame/SECURITY.md +16 -0
- package/packages/frame/docs/how-it-works.md +53 -0
- package/packages/frame/docs/reference.md +78 -0
- package/packages/frame/package.json +72 -0
- package/packages/frame/src/data/git.ts +90 -0
- package/packages/frame/src/data/github.ts +140 -0
- package/packages/frame/src/data/gitlab.ts +63 -0
- package/packages/frame/src/data/mode.ts +49 -0
- package/packages/frame/src/data/past-prompts.ts +57 -0
- package/packages/frame/src/data/runner.ts +32 -0
- package/packages/frame/src/data/snapshot.ts +223 -0
- package/packages/frame/src/data/start-notes.ts +104 -0
- package/packages/frame/src/data/telemetry.ts +337 -0
- package/packages/frame/src/data/todos.ts +162 -0
- package/packages/frame/src/data/totals.ts +94 -0
- package/packages/frame/src/data/visits.ts +45 -0
- package/packages/frame/src/index.ts +696 -0
- package/packages/frame/src/kit/LICENSE +21 -0
- package/packages/frame/src/kit/app/attribution.ts +30 -0
- package/packages/frame/src/kit/app/builder.ts +186 -0
- package/packages/frame/src/kit/app/claim.ts +27 -0
- package/packages/frame/src/kit/app/feature.ts +91 -0
- package/packages/frame/src/kit/app/host.ts +214 -0
- package/packages/frame/src/kit/app/renderers.ts +62 -0
- package/packages/frame/src/kit/app/scope.ts +125 -0
- package/packages/frame/src/kit/board/model.ts +337 -0
- package/packages/frame/src/kit/contract.ts +79 -0
- package/packages/frame/src/kit/contracts/accounts.ts +33 -0
- package/packages/frame/src/kit/contracts/ask.ts +120 -0
- package/packages/frame/src/kit/contracts/board.ts +6 -0
- package/packages/frame/src/kit/contracts/by.ts +22 -0
- package/packages/frame/src/kit/contracts/calls.ts +46 -0
- package/packages/frame/src/kit/contracts/events.ts +89 -0
- package/packages/frame/src/kit/contracts/mode.ts +26 -0
- package/packages/frame/src/kit/contracts/names.ts +12 -0
- package/packages/frame/src/kit/contracts/peers.ts +77 -0
- package/packages/frame/src/kit/contracts/prompts.ts +8 -0
- package/packages/frame/src/kit/contracts/screen.ts +75 -0
- package/packages/frame/src/kit/contracts/skills.ts +81 -0
- package/packages/frame/src/kit/contracts/sources.ts +81 -0
- package/packages/frame/src/kit/contracts/views.ts +42 -0
- package/packages/frame/src/kit/control.ts +59 -0
- package/packages/frame/src/kit/dark-theme.ts +41 -0
- package/packages/frame/src/kit/decode.ts +210 -0
- package/packages/frame/src/kit/events/log.ts +313 -0
- package/packages/frame/src/kit/format.ts +25 -0
- package/packages/frame/src/kit/host.ts +4 -0
- package/packages/frame/src/kit/index.ts +25 -0
- package/packages/frame/src/kit/legacy-tasks.ts +39 -0
- package/packages/frame/src/kit/overlay-images.ts +36 -0
- package/packages/frame/src/kit/paths.ts +16 -0
- package/packages/frame/src/kit/screen/client.ts +74 -0
- package/packages/frame/src/kit/screen/feature.ts +64 -0
- package/packages/frame/src/kit/screen/model.ts +182 -0
- package/packages/frame/src/kit/screen/view.ts +838 -0
- package/packages/frame/src/kit/settings/files.ts +90 -0
- package/packages/frame/src/kit/settings/setting.ts +57 -0
- package/packages/frame/src/kit/settings/store.ts +192 -0
- package/packages/frame/src/kit/sources/file.ts +129 -0
- package/packages/frame/src/kit/sources/follow.ts +155 -0
- package/packages/frame/src/kit/testing.ts +260 -0
- package/packages/frame/src/kit/totals.ts +72 -0
- package/packages/frame/src/kit/ui/entry.ts +75 -0
- package/packages/frame/src/kit/ui/frame.ts +212 -0
- package/packages/frame/src/kit/ui/glyphs.ts +132 -0
- package/packages/frame/src/kit/ui/scroll.ts +14 -0
- package/packages/frame/src/render/diff.ts +157 -0
- package/packages/frame/src/render/fit.ts +86 -0
- package/packages/frame/src/render/format.ts +108 -0
- package/packages/frame/src/render/frame.ts +106 -0
- package/packages/frame/src/render/glyphs.ts +189 -0
- package/packages/frame/src/render/keys.ts +43 -0
- package/packages/frame/src/render/lead.ts +37 -0
- package/packages/frame/src/render/paint.ts +135 -0
- package/packages/frame/src/render/safe.ts +19 -0
- package/packages/frame/src/render/segments.ts +735 -0
- package/packages/frame/src/render/streaming.ts +223 -0
- package/packages/frame/src/render/timing.ts +106 -0
- package/packages/frame/src/render/tools.ts +167 -0
- package/packages/frame/src/settings.ts +105 -0
- package/packages/frame/src/ui/editor.ts +272 -0
- package/packages/frame/src/ui/effort.ts +36 -0
- package/packages/frame/src/ui/footer.ts +165 -0
- package/packages/frame/src/ui/header.ts +362 -0
- package/packages/frame/src/ui/history-search.ts +23 -0
- package/packages/frame/src/ui/interrupt.ts +21 -0
- package/packages/frame/src/ui/outputs.ts +170 -0
- package/packages/frame/src/ui/pi-entries.ts +298 -0
- package/packages/frame/src/ui/previews.ts +72 -0
- package/packages/frame/src/ui/queue.ts +75 -0
- package/packages/frame/src/ui/screen.ts +59 -0
- package/packages/frame/src/ui/timings.ts +48 -0
- package/packages/frame/src/ui/todos.ts +205 -0
- package/packages/frame/src/ui/tool-frame.ts +595 -0
- package/packages/frame/src/ui/working.ts +25 -0
- package/packages/frame/src/view/state.ts +97 -0
- package/packages/frame/src/view/transcript.ts +175 -0
- package/packages/frame/src/view/view.ts +162 -0
- package/packages/memory/CONTRIBUTING.md +7 -0
- package/packages/memory/LICENSE +21 -0
- package/packages/memory/README.md +44 -0
- package/packages/memory/SECURITY.md +20 -0
- package/packages/memory/package.json +55 -0
- package/packages/memory/skills/review-memory/SKILL.md +48 -0
- package/packages/memory/src/index.ts +163 -0
- package/packages/memory/src/prompt.ts +25 -0
- package/packages/memory/src/review.ts +150 -0
- package/packages/memory/src/secrets.ts +22 -0
- package/packages/memory/src/store.ts +173 -0
- package/packages/modes/CHANGELOG.md +104 -0
- package/packages/modes/CONTRIBUTING.md +7 -0
- package/packages/modes/LICENSE +21 -0
- package/packages/modes/README.md +174 -0
- package/packages/modes/SECURITY.md +17 -0
- package/packages/modes/package.json +55 -0
- package/packages/modes/skills/review-permissions/SKILL.md +54 -0
- package/packages/modes/src/bash.ts +218 -0
- package/packages/modes/src/deny-off.ts +62 -0
- package/packages/modes/src/git-safety.ts +72 -0
- package/packages/modes/src/index.ts +338 -0
- package/packages/modes/src/interview.ts +206 -0
- package/packages/modes/src/judge-gate.ts +113 -0
- package/packages/modes/src/modes.ts +121 -0
- package/packages/modes/src/review/apply.mjs +156 -0
- package/packages/modes/src/review/collect.mjs +208 -0
- package/packages/modes/src/review/tools.ts +71 -0
- package/packages/modes/src/sandbox.ts +37 -0
- package/packages/router/CHANGELOG.md +8 -0
- package/packages/router/LICENSE +21 -0
- package/packages/router/README.md +90 -0
- package/packages/router/package.json +51 -0
- package/packages/router/src/config.ts +65 -0
- package/packages/router/src/index.ts +172 -0
- package/packages/router/src/judge.ts +51 -0
- package/packages/router/src/route.ts +67 -0
- package/packages/web/CHANGELOG.md +36 -0
- package/packages/web/LICENSE +21 -0
- package/packages/web/README.md +87 -0
- package/packages/web/SECURITY.md +13 -0
- package/packages/web/package.json +59 -0
- package/packages/web/src/config.ts +109 -0
- package/packages/web/src/dnsfilter.ts +97 -0
- package/packages/web/src/extract.ts +65 -0
- package/packages/web/src/index.ts +217 -0
- package/packages/web/src/markdown.ts +122 -0
- package/packages/web/src/net.ts +109 -0
- package/packages/web/src/reader.ts +73 -0
- package/packages/web/src/sanitize.ts +50 -0
- package/packages/web/src/search.ts +76 -0
- package/packages/web/src/secrets.ts +22 -0
- package/packages/web/src/validate.ts +186 -0
- package/src/index.ts +156 -0
- package/src/settings.ts +79 -0
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AccountWindow,
|
|
3
|
+
type BoardCard,
|
|
4
|
+
formatClock,
|
|
5
|
+
type PeerMark,
|
|
6
|
+
type RoomMark,
|
|
7
|
+
} from "../kit/index.ts";
|
|
8
|
+
import { visibleWidth } from "@earendil-works/pi-tui";
|
|
9
|
+
|
|
10
|
+
import { SANDBOX_STATUS, type Snapshot } from "../data/snapshot.ts";
|
|
11
|
+
import { promptOf, tokensOf, type Totals } from "../data/totals.ts";
|
|
12
|
+
import type { Piece } from "./fit.ts";
|
|
13
|
+
import {
|
|
14
|
+
basename,
|
|
15
|
+
CELL,
|
|
16
|
+
cell,
|
|
17
|
+
count,
|
|
18
|
+
fitWidth,
|
|
19
|
+
gauge,
|
|
20
|
+
latency,
|
|
21
|
+
money,
|
|
22
|
+
percent,
|
|
23
|
+
rate,
|
|
24
|
+
shortenPath,
|
|
25
|
+
stopwatch,
|
|
26
|
+
UNKNOWN,
|
|
27
|
+
} from "./format.ts";
|
|
28
|
+
import type { Glyphs } from "./glyphs.ts";
|
|
29
|
+
import type { Paint, Role } from "./paint.ts";
|
|
30
|
+
import { styledText } from "./safe.ts";
|
|
31
|
+
|
|
32
|
+
export const SEGMENT_IDS = [
|
|
33
|
+
"path",
|
|
34
|
+
"branch",
|
|
35
|
+
"host",
|
|
36
|
+
"me",
|
|
37
|
+
"session",
|
|
38
|
+
"board",
|
|
39
|
+
"peers",
|
|
40
|
+
"version",
|
|
41
|
+
"clock",
|
|
42
|
+
"provider",
|
|
43
|
+
"model",
|
|
44
|
+
"effort",
|
|
45
|
+
"context",
|
|
46
|
+
"speed",
|
|
47
|
+
"wait",
|
|
48
|
+
"server",
|
|
49
|
+
"elapsed",
|
|
50
|
+
"last",
|
|
51
|
+
"request",
|
|
52
|
+
"costRate",
|
|
53
|
+
"cost",
|
|
54
|
+
"quota",
|
|
55
|
+
"tokens",
|
|
56
|
+
"cache",
|
|
57
|
+
"average",
|
|
58
|
+
"statuses",
|
|
59
|
+
] as const;
|
|
60
|
+
|
|
61
|
+
export type BuiltinSegment = (typeof SEGMENT_IDS)[number];
|
|
62
|
+
type StatusSegment = `status:${string}`;
|
|
63
|
+
export type SegmentId = BuiltinSegment | StatusSegment;
|
|
64
|
+
|
|
65
|
+
const STATUS_PREFIX = "status:";
|
|
66
|
+
/** pi-modes reports the mode here, and it leads the statuses. */
|
|
67
|
+
const MODE_STATUS = "pi-modes";
|
|
68
|
+
const STATUS_PRIORITY = 95;
|
|
69
|
+
|
|
70
|
+
export function isSegmentId(value: string): value is SegmentId {
|
|
71
|
+
return (
|
|
72
|
+
(SEGMENT_IDS as readonly string[]).includes(value) ||
|
|
73
|
+
(value.startsWith(STATUS_PREFIX) && value.length > STATUS_PREFIX.length)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** The statuses segment skips them. */
|
|
78
|
+
const CLAIMED_BY_SEGMENT: Partial<Record<BuiltinSegment, string>> = {
|
|
79
|
+
model: "pi-providers:account",
|
|
80
|
+
peers: "pi-peers",
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export function claimedStatuses(slots: readonly (readonly SegmentId[])[]): Set<string> {
|
|
84
|
+
const claimed = new Set<string>();
|
|
85
|
+
for (const ids of slots) {
|
|
86
|
+
for (const id of ids) {
|
|
87
|
+
if (id.startsWith(STATUS_PREFIX)) claimed.add(id.slice(STATUS_PREFIX.length));
|
|
88
|
+
const bySegment = CLAIMED_BY_SEGMENT[id as BuiltinSegment];
|
|
89
|
+
if (bySegment !== undefined) claimed.add(bySegment);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return claimed;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface SegmentOptions {
|
|
96
|
+
pathLength: number;
|
|
97
|
+
gaugeCells: number;
|
|
98
|
+
claimed: ReadonlySet<string>;
|
|
99
|
+
labels: boolean;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface SegmentInput {
|
|
103
|
+
snapshot: Snapshot;
|
|
104
|
+
glyphs: Glyphs;
|
|
105
|
+
paint: Paint;
|
|
106
|
+
options: SegmentOptions;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
type Rendered = Omit<Piece, "priority"> | undefined;
|
|
110
|
+
|
|
111
|
+
interface Segment {
|
|
112
|
+
priority: number;
|
|
113
|
+
describe: string;
|
|
114
|
+
render(input: SegmentInput): Rendered;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const SEGMENTS: Record<BuiltinSegment, Segment> = {
|
|
118
|
+
path: {
|
|
119
|
+
priority: 70,
|
|
120
|
+
describe: "the working folder, with home as ~",
|
|
121
|
+
render: ({ snapshot, glyphs, paint, options }) => {
|
|
122
|
+
if (snapshot.cwd === "") return undefined;
|
|
123
|
+
const full = shortenPath(snapshot.cwd, snapshot.home, options.pathLength);
|
|
124
|
+
const icon = mark(glyphs.folder, paint, "path");
|
|
125
|
+
return {
|
|
126
|
+
text: `${icon}${paint.role("path", full)}`,
|
|
127
|
+
compact: `${icon}${paint.role("path", basename(snapshot.cwd))}`,
|
|
128
|
+
};
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
branch: {
|
|
132
|
+
priority: 82,
|
|
133
|
+
describe:
|
|
134
|
+
"the git branch: ↑ commits ahead, ↓ behind, + staged, ~ modified, ? untracked, ! conflicts, then stashes",
|
|
135
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
136
|
+
if (snapshot.branch === null) return undefined;
|
|
137
|
+
const icon = mark(glyphs.branch, paint, "branch");
|
|
138
|
+
const detached = snapshot.branch === "detached";
|
|
139
|
+
const name = detached
|
|
140
|
+
? paint.role("behind", "detached")
|
|
141
|
+
: paint.role("branch", fitWidth(snapshot.branch, 32, glyphs.ellipsis));
|
|
142
|
+
const git = snapshot.git;
|
|
143
|
+
if (git === undefined) return { text: `${icon}${name}` };
|
|
144
|
+
|
|
145
|
+
const marks = [
|
|
146
|
+
counted(git.ahead, glyphs.ahead, "ahead", paint),
|
|
147
|
+
counted(git.behind, glyphs.behind, "behind", paint),
|
|
148
|
+
counted(git.staged, glyphs.staged, "staged", paint),
|
|
149
|
+
counted(git.modified, glyphs.modified, "modified", paint),
|
|
150
|
+
counted(git.untracked, glyphs.untracked, "untracked", paint),
|
|
151
|
+
counted(git.conflicted, glyphs.conflicted, "conflicted", paint),
|
|
152
|
+
counted(git.stashed, glyphs.stashed, "untracked", paint),
|
|
153
|
+
].filter((part) => part !== "");
|
|
154
|
+
const dirty = git.staged + git.modified + git.untracked + git.conflicted > 0;
|
|
155
|
+
return {
|
|
156
|
+
text: marks.length === 0 ? `${icon}${name}` : `${icon}${name} ${marks.join(" ")}`,
|
|
157
|
+
compact: dirty ? `${icon}${name}${paint.role("modified", "*")}` : `${icon}${name}`,
|
|
158
|
+
};
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
host: {
|
|
162
|
+
priority: 20,
|
|
163
|
+
describe: "the machine, so a pasted line says where it came from",
|
|
164
|
+
render: ({ snapshot, glyphs, paint }) =>
|
|
165
|
+
snapshot.host === undefined || snapshot.host === ""
|
|
166
|
+
? undefined
|
|
167
|
+
: { text: `${mark(glyphs.host, paint, "host")}${paint.role("host", snapshot.host)}` },
|
|
168
|
+
},
|
|
169
|
+
peers: {
|
|
170
|
+
priority: 25,
|
|
171
|
+
describe:
|
|
172
|
+
"each room this session is in, with ● taking part, ◐ listening or ○ stepped out, then its agents by name and colour, each ◆ working, ⚑ waiting on you or ◇ idle, rooms apart by a rule, then @ how many messages wait for an answer",
|
|
173
|
+
// The whole folder, then one name, then the bare mark, as room runs out.
|
|
174
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
175
|
+
const peers = snapshot.peers;
|
|
176
|
+
// A draft is a line here, not a frame over the editor.
|
|
177
|
+
const drafts = (snapshot.peersWriting ?? []).map(
|
|
178
|
+
(mark) => `${paint.raw(mark.color, mark.handle)}${paint.dim(` \u2192 ${mark.to}`)}`,
|
|
179
|
+
);
|
|
180
|
+
const writing = drafts.length === 0 ? "" : paint.dim(drafts.join(", "));
|
|
181
|
+
const unread =
|
|
182
|
+
snapshot.peersUnread > 0 ? paint.role("waiting", `@${snapshot.peersUnread}`) : "";
|
|
183
|
+
const mentions = [writing, unread].filter((part) => part !== "").join(" ");
|
|
184
|
+
const rooms = snapshot.peersRooms.filter(
|
|
185
|
+
(room) => room.peers.length > 0 || room.mode !== "in",
|
|
186
|
+
);
|
|
187
|
+
if (rooms.length > 0) return roomsPiece(rooms, mentions, glyphs, paint);
|
|
188
|
+
const first = peers[0];
|
|
189
|
+
if (first === undefined) return mentions === "" ? undefined : { text: mentions };
|
|
190
|
+
const named = (peer: PeerMark) => paint.raw(peer.color, `${glyphs.peers} ${peer.handle}`);
|
|
191
|
+
const rest = peers.length > 1 ? paint.dim(` +${peers.length - 1}`) : "";
|
|
192
|
+
const groups = new Map<string, string[]>();
|
|
193
|
+
for (const peer of peers)
|
|
194
|
+
groups.set(peer.room ?? "", [...(groups.get(peer.room ?? "") ?? []), named(peer)]);
|
|
195
|
+
const apart = ` ${paint.dim(glyphs.apart)} `;
|
|
196
|
+
const full = joined(
|
|
197
|
+
[...groups.values()].map((group) => group.join(" ")).join(apart),
|
|
198
|
+
mentions,
|
|
199
|
+
);
|
|
200
|
+
const short = joined(named(first) + rest, mentions);
|
|
201
|
+
const least = joined(paint.raw(first.color, glyphs.peers), mentions);
|
|
202
|
+
return {
|
|
203
|
+
text: full,
|
|
204
|
+
shrink: (width) =>
|
|
205
|
+
visibleWidth(full) <= width ? full : visibleWidth(short) <= width ? short : least,
|
|
206
|
+
};
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
board: {
|
|
210
|
+
priority: 30,
|
|
211
|
+
describe:
|
|
212
|
+
"the card being done, then ⚑ how many open cards are blocked, ⧗ how many are to do and free to start, and ✓ how many are done, from the session's board",
|
|
213
|
+
// A short line cuts the words, not the counts.
|
|
214
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
215
|
+
const cards = snapshot.board?.cards ?? [];
|
|
216
|
+
const doing = cards.filter((card) => card.column === "doing");
|
|
217
|
+
const blocked = cards.filter(blocks).length;
|
|
218
|
+
const todo = cards.filter((card) => card.column === "todo" && !blocks(card)).length;
|
|
219
|
+
const done = cards.filter((card) => card.column === "done").length;
|
|
220
|
+
const counts = [
|
|
221
|
+
tally(blocked, glyphs.waiting, "waiting", paint),
|
|
222
|
+
tally(todo, glyphs.queued, "queued", paint),
|
|
223
|
+
tally(done, glyphs.done, "done", paint),
|
|
224
|
+
].filter((part) => part !== "");
|
|
225
|
+
const step = doing[0]?.title;
|
|
226
|
+
if (step === undefined && counts.length === 0) return undefined;
|
|
227
|
+
const more = doing.length > 1 ? paint.dim(` +${doing.length - 1}`) : "";
|
|
228
|
+
// Between cards the line stays, so what was done and what waits do not vanish.
|
|
229
|
+
const icon = mark(glyphs.board, paint, "board");
|
|
230
|
+
const words = (room: number) =>
|
|
231
|
+
step === undefined ? paint.dim("idle") : paint.text(fitWidth(step, room, glyphs.ellipsis));
|
|
232
|
+
const tail = `${more}${counts.length === 0 ? "" : ` ${counts.join(" ")}`}`;
|
|
233
|
+
const fixed = visibleWidth(icon) + visibleWidth(tail);
|
|
234
|
+
return {
|
|
235
|
+
text: `${icon}${words(Number.MAX_SAFE_INTEGER)}${tail}`,
|
|
236
|
+
shrink: (width) => `${icon}${words(Math.max(1, width - fixed))}${tail}`,
|
|
237
|
+
};
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
me: {
|
|
241
|
+
priority: 85,
|
|
242
|
+
describe: "the name the rooms call this session by, in its color",
|
|
243
|
+
render: ({ snapshot: { me }, paint }) => {
|
|
244
|
+
if (me === undefined) return undefined;
|
|
245
|
+
const name =
|
|
246
|
+
me.color === undefined ? paint.role("brand", me.handle) : paint.raw(me.color, me.handle);
|
|
247
|
+
return { text: paint.bold(name) };
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
session: {
|
|
251
|
+
priority: 45,
|
|
252
|
+
describe: "the session name, once it has one",
|
|
253
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
254
|
+
const name = snapshot.sessionName;
|
|
255
|
+
if (name === undefined || name === "") return undefined;
|
|
256
|
+
const icon = mark(glyphs.session, paint, "session");
|
|
257
|
+
return {
|
|
258
|
+
text: `${icon}${paint.role("session", fitWidth(name, 32, glyphs.ellipsis))}`,
|
|
259
|
+
compact: `${icon}${paint.role("session", fitWidth(name, 14, glyphs.ellipsis))}`,
|
|
260
|
+
};
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
version: {
|
|
264
|
+
priority: 10,
|
|
265
|
+
describe: "the pi version, so a report says which one it was",
|
|
266
|
+
render: ({ snapshot, glyphs, paint }) => ({
|
|
267
|
+
text: `${mark(glyphs.version, paint, "brand")}${paint.role("version", snapshot.version)}`,
|
|
268
|
+
}),
|
|
269
|
+
},
|
|
270
|
+
clock: {
|
|
271
|
+
priority: 15,
|
|
272
|
+
describe: "the time of day",
|
|
273
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
274
|
+
const time = formatClock(snapshot.now);
|
|
275
|
+
return { text: `${mark(glyphs.clock, paint, "clock")}${paint.role("clock", time)}` };
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
provider: {
|
|
279
|
+
priority: 40,
|
|
280
|
+
describe: "who serves the model, on its own",
|
|
281
|
+
render: ({ snapshot, paint }) =>
|
|
282
|
+
snapshot.model === undefined
|
|
283
|
+
? undefined
|
|
284
|
+
: { text: paint.role("provider", snapshot.model.provider) },
|
|
285
|
+
},
|
|
286
|
+
model: {
|
|
287
|
+
priority: 100,
|
|
288
|
+
describe: "the model doing the work, after its provider and the account it runs on",
|
|
289
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
290
|
+
const icon = mark(glyphs.model, paint, "provider");
|
|
291
|
+
if (snapshot.model === undefined) return { text: `${icon}${paint.dim("no model")}` };
|
|
292
|
+
const name = paint.bold(paint.role("model", snapshot.model.name));
|
|
293
|
+
const account =
|
|
294
|
+
snapshot.account === undefined
|
|
295
|
+
? ""
|
|
296
|
+
: `${paint.dim("(")}${paint.role("provider", snapshot.account.label)}${paint.dim(")")}`;
|
|
297
|
+
const provider = `${paint.role("provider", snapshot.model.provider)}${account}${paint.dim("/")}`;
|
|
298
|
+
return {
|
|
299
|
+
text: `${icon}${provider}${name}`,
|
|
300
|
+
compact: account === "" ? `${icon}${name}` : `${icon}${name} ${account}`,
|
|
301
|
+
};
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
quota: {
|
|
305
|
+
priority: 58,
|
|
306
|
+
describe: "what each window of the plan spent, and when it resets",
|
|
307
|
+
render: ({ snapshot, paint }) => {
|
|
308
|
+
const account = snapshot.account;
|
|
309
|
+
if (account === undefined) return undefined;
|
|
310
|
+
if (account.needsLogin === true) return { text: paint.stress(100, "sign in again") };
|
|
311
|
+
if (account.windows.length === 0) return undefined;
|
|
312
|
+
const text = account.windows.map((window) => quotaWindow(paint, window)).join(" ");
|
|
313
|
+
return { text };
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
effort: {
|
|
317
|
+
priority: 80,
|
|
318
|
+
describe: "how hard the model thinks, after a divider, in a color that warms with the level",
|
|
319
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
320
|
+
if (snapshot.model?.reasoning !== true) return undefined;
|
|
321
|
+
const level = snapshot.thinking ?? "off";
|
|
322
|
+
const label = level === "off" ? paint.dim(level) : paint.effort(level, level);
|
|
323
|
+
return { text: `${paint.dim(glyphs.apart)} ${label}` };
|
|
324
|
+
},
|
|
325
|
+
},
|
|
326
|
+
context: {
|
|
327
|
+
priority: 88,
|
|
328
|
+
describe: "how full the context window is, with the tokens in it over the window",
|
|
329
|
+
render: ({ snapshot, glyphs, paint, options }) => {
|
|
330
|
+
const context = snapshot.context;
|
|
331
|
+
if (context === undefined) return undefined;
|
|
332
|
+
const glyph =
|
|
333
|
+
glyphs.context === "" ? "" : `${paint.stress(context.percent, glyphs.context)} `;
|
|
334
|
+
const icon = `${glyph}${word(options, glyphs.context === "ctx" ? "" : "ctx", paint)}`;
|
|
335
|
+
const value = paint.stress(context.percent, percent(context.percent));
|
|
336
|
+
const cells = gauge(context.percent, options.gaugeCells);
|
|
337
|
+
const bar =
|
|
338
|
+
options.gaugeCells <= 0
|
|
339
|
+
? ""
|
|
340
|
+
: ` ${paint.stress(context.percent, glyphs.fill.repeat(cells.filled))}${paint.dim(glyphs.empty.repeat(cells.empty))}`;
|
|
341
|
+
const size = paint.dim(` ${count(context.tokens)}/${count(context.window)}`);
|
|
342
|
+
return { text: `${icon}${value}${bar}${size}`, compact: `${icon}${value}` };
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
speed: {
|
|
346
|
+
priority: 60,
|
|
347
|
+
describe:
|
|
348
|
+
"how fast tokens flow in this answer: out ↓ is the model writing, in ↑ is it reading the prompt",
|
|
349
|
+
render: ({ snapshot, glyphs, paint, options }) => {
|
|
350
|
+
const request = snapshot.request;
|
|
351
|
+
if (request === undefined) return undefined;
|
|
352
|
+
const out = speedCell(request.decode);
|
|
353
|
+
const value = request.estimated ? paint.muted(out) : paint.bold(paint.role("speed", out));
|
|
354
|
+
const down = `${flow("received", glyphs, paint, options)}${value}`;
|
|
355
|
+
const up = `${flow("sent", glyphs, paint, options)}${paint.role("sent", speedCell(request.prefill))}`;
|
|
356
|
+
const icon = mark(glyphs.speed, paint, "speed");
|
|
357
|
+
const unit = paint.dim(" tok/s");
|
|
358
|
+
return { text: `${icon}${down} ${up}${unit}`, compact: `${icon}${down}${unit}` };
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
wait: {
|
|
362
|
+
priority: 55,
|
|
363
|
+
describe:
|
|
364
|
+
"time to first token (ttft): from the request leaving to the model writing its first token",
|
|
365
|
+
render: ({ snapshot, glyphs, paint, options }) => {
|
|
366
|
+
const request = snapshot.request;
|
|
367
|
+
if (request === undefined) return undefined;
|
|
368
|
+
const ms = request.waiting ? request.waitingMs : request.waitMs;
|
|
369
|
+
const icon = glyphs.wait === "" ? "" : `${paint.role("wait", glyphs.wait)} `;
|
|
370
|
+
const value = cell(ms === undefined ? UNKNOWN : latency(ms), CELL.latency);
|
|
371
|
+
return {
|
|
372
|
+
text: `${icon}${word(options, "ttft", paint, glyphs.wait === "ttft")}${paint.role("wait", value)}`,
|
|
373
|
+
};
|
|
374
|
+
},
|
|
375
|
+
},
|
|
376
|
+
elapsed: {
|
|
377
|
+
priority: 65,
|
|
378
|
+
describe:
|
|
379
|
+
"the run: while the agent works, how long and how many calls to the model; once it answered, the same and what it cost",
|
|
380
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
381
|
+
const run = snapshot.run;
|
|
382
|
+
if (run === undefined) return undefined;
|
|
383
|
+
const role: Role = run.running ? "running" : "done";
|
|
384
|
+
const icon = mark(run.running ? glyphs.running : glyphs.done, paint, role);
|
|
385
|
+
const time = paint.role(role, stopwatch(run.elapsedMs));
|
|
386
|
+
const lead = run.running ? time : `${paint.dim("answered in ")}${time}`;
|
|
387
|
+
const calls = run.requests <= 1 ? "" : paint.dim(` ${run.requests} calls`);
|
|
388
|
+
const cost =
|
|
389
|
+
run.running || run.cost === 0 ? "" : ` ${paint.money(run.cost, money(run.cost))}`;
|
|
390
|
+
return { text: `${icon}${lead}${calls}${cost}`, compact: `${icon}${time}` };
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
last: {
|
|
394
|
+
priority: 55,
|
|
395
|
+
describe:
|
|
396
|
+
"the last call that finished: how long the model took to start, how long it thought, and how fast it wrote",
|
|
397
|
+
render: ({ snapshot, glyphs, paint, options }) => {
|
|
398
|
+
const last = snapshot.last;
|
|
399
|
+
if (last === undefined) return undefined;
|
|
400
|
+
const parts: string[] = [];
|
|
401
|
+
if (last.waitMs !== undefined) {
|
|
402
|
+
const wait = paint.role("wait", latency(last.waitMs));
|
|
403
|
+
parts.push(
|
|
404
|
+
options.labels
|
|
405
|
+
? `${wait}${paint.dim(" wait")}`
|
|
406
|
+
: `${mark(glyphs.wait, paint, "wait")}${wait}`,
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
if (last.thoughtMs !== undefined) {
|
|
410
|
+
const thought = paint.role("wait", latency(last.thoughtMs));
|
|
411
|
+
parts.push(
|
|
412
|
+
options.labels ? `${thought}${paint.dim(" thought")}` : `${thought}${paint.dim(" th")}`,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
if (last.decode !== undefined) {
|
|
416
|
+
const speed = `${paint.role("received", glyphs.received)}${paint.role("speed", rate(last.decode))}`;
|
|
417
|
+
parts.push(`${speed}${paint.dim(" tok/s")}`);
|
|
418
|
+
}
|
|
419
|
+
if (parts.length === 0) return undefined;
|
|
420
|
+
const label = options.labels ? paint.dim("last call ") : "";
|
|
421
|
+
return { text: `${label}${parts.join(" ")}`, compact: parts[0] };
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
server: {
|
|
425
|
+
priority: 53,
|
|
426
|
+
describe:
|
|
427
|
+
"how long the provider took to answer the request, before the model wrote anything: send to headers",
|
|
428
|
+
render: ({ snapshot, paint }) => {
|
|
429
|
+
const ms = snapshot.last?.serverMs;
|
|
430
|
+
if (ms === undefined) return undefined;
|
|
431
|
+
return { text: `${paint.role("wait", latency(ms))}${paint.dim(" server")}` };
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
request: {
|
|
435
|
+
priority: 30,
|
|
436
|
+
describe: "tokens of this answer: in ↑ is the prompt sent, out ↓ is what the model wrote",
|
|
437
|
+
render: ({ snapshot, glyphs, paint, options }) => {
|
|
438
|
+
const usage = snapshot.request?.usage;
|
|
439
|
+
if (usage === undefined) return undefined;
|
|
440
|
+
const sent = usage.input + usage.cacheRead + usage.cacheWrite;
|
|
441
|
+
const up = `${flow("sent", glyphs, paint, options)}${tokenCell(sent)}`;
|
|
442
|
+
const down = `${flow("received", glyphs, paint, options)}${tokenCell(usage.output)}`;
|
|
443
|
+
const cache = cacheParts(usage, glyphs, paint, options, true);
|
|
444
|
+
return {
|
|
445
|
+
text: `${up} ${paint.dim("(")}${cache}${paint.dim(")")} ${down}`,
|
|
446
|
+
compact: `${up} ${down}`,
|
|
447
|
+
};
|
|
448
|
+
},
|
|
449
|
+
},
|
|
450
|
+
costRate: {
|
|
451
|
+
priority: 26,
|
|
452
|
+
describe:
|
|
453
|
+
"what a million tokens cost in this answer, the number that says whether a model is cheap",
|
|
454
|
+
render: ({ snapshot, paint }) => {
|
|
455
|
+
const usage = snapshot.request?.usage;
|
|
456
|
+
if (usage === undefined) return undefined;
|
|
457
|
+
const tokens = tokensOf(usage);
|
|
458
|
+
if (usage.cost === 0 || tokens === 0) return undefined;
|
|
459
|
+
const perMillion = (usage.cost / tokens) * 1_000_000;
|
|
460
|
+
if (perMillion < 0.005) return undefined;
|
|
461
|
+
return {
|
|
462
|
+
text: `${paint.role("cost", `$${perMillion.toFixed(2)}`)}${paint.dim(" per Mtok")}`,
|
|
463
|
+
};
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
cost: {
|
|
467
|
+
priority: 75,
|
|
468
|
+
describe:
|
|
469
|
+
"what the session cost; `sub` means a subscription, so it is what the tokens would have cost",
|
|
470
|
+
render: ({ snapshot, glyphs, paint }) => {
|
|
471
|
+
const amount = snapshot.totals.cost;
|
|
472
|
+
if (amount === 0) return undefined;
|
|
473
|
+
const value = paint.money(amount, money(amount));
|
|
474
|
+
// On a plan the amount is what the tokens would cost at API prices.
|
|
475
|
+
const what = paint.dim(snapshot.subscription ? " at API prices" : " this session");
|
|
476
|
+
const icon = glyphs.cost === "$" ? "" : mark(glyphs.cost, paint, "cost");
|
|
477
|
+
return { text: `${icon}${value}${what}`, compact: `${icon}${value}` };
|
|
478
|
+
},
|
|
479
|
+
},
|
|
480
|
+
tokens: {
|
|
481
|
+
priority: 35,
|
|
482
|
+
describe:
|
|
483
|
+
"tokens of the whole session: ↑ sent to the model, cache included, and ↓ what it wrote back",
|
|
484
|
+
render: ({ snapshot, glyphs, paint, options }) => {
|
|
485
|
+
const totals = snapshot.totals;
|
|
486
|
+
if (tokensOf(totals) === 0) return undefined;
|
|
487
|
+
// Everything sent, not only the uncached part.
|
|
488
|
+
const sent = paint.role("sent", count(promptOf(totals)));
|
|
489
|
+
const got = paint.role("received", count(totals.output));
|
|
490
|
+
const arrows = `${paint.role("sent", glyphs.sent)}${sent} ${paint.role("received", glyphs.received)}${got}`;
|
|
491
|
+
if (!options.labels) return { text: arrows };
|
|
492
|
+
return {
|
|
493
|
+
text: `${paint.dim("in ")}${sent} ${paint.dim("out ")}${got}`,
|
|
494
|
+
compact: `${paint.dim("in ")}${sent} ${paint.dim("out ")}${got}`,
|
|
495
|
+
};
|
|
496
|
+
},
|
|
497
|
+
},
|
|
498
|
+
cache: {
|
|
499
|
+
priority: 30,
|
|
500
|
+
describe:
|
|
501
|
+
"how much of the last prompt was read from the cache; `new` when it was only just written",
|
|
502
|
+
render: ({ snapshot, glyphs, paint, options }) => {
|
|
503
|
+
const hit = snapshot.cacheHit;
|
|
504
|
+
if (hit === undefined) return undefined;
|
|
505
|
+
const icon = mark(glyphs.cacheRead, paint, "cache");
|
|
506
|
+
// Written to the cache and not read from it is the cache warming, not a miss.
|
|
507
|
+
if (snapshot.cacheWarming)
|
|
508
|
+
return { text: `${icon}${paint.muted(options.labels ? "cache new" : "new")}` };
|
|
509
|
+
const value =
|
|
510
|
+
hit >= 90 ? paint.role("done", percent(hit)) : paint.stress(100 - hit, percent(hit));
|
|
511
|
+
return {
|
|
512
|
+
// The numbers around it are the session's; this one is the last prompt's.
|
|
513
|
+
text: options.labels
|
|
514
|
+
? `${paint.dim("last prompt ")}${value}${paint.dim(" cached")}`
|
|
515
|
+
: `${icon}${value}`,
|
|
516
|
+
compact: options.labels ? `${paint.dim("cache ")}${value}` : `${icon}${value}`,
|
|
517
|
+
};
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
average: {
|
|
521
|
+
priority: 25,
|
|
522
|
+
describe:
|
|
523
|
+
"the session's average speeds: ↓ the model writing, ↑ it reading the prompt, cache included",
|
|
524
|
+
render: ({ snapshot, paint }) => {
|
|
525
|
+
const { decode, prefill } = snapshot.averages;
|
|
526
|
+
if (decode === undefined && prefill === undefined) return undefined;
|
|
527
|
+
const down =
|
|
528
|
+
decode === undefined ? "" : `${paint.dim("out ")}${paint.role("speed", rate(decode))}`;
|
|
529
|
+
const up =
|
|
530
|
+
prefill === undefined ? "" : `${paint.dim("in ")}${paint.role("sent", rate(prefill))}`;
|
|
531
|
+
const label = paint.dim("avg ");
|
|
532
|
+
const unit = paint.dim(" tok/s");
|
|
533
|
+
return {
|
|
534
|
+
text: `${label}${[down, up].filter((part) => part !== "").join(" ")}${unit}`,
|
|
535
|
+
compact: down === "" ? undefined : `${label}${down}${unit}`,
|
|
536
|
+
};
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
statuses: {
|
|
540
|
+
priority: 50,
|
|
541
|
+
describe: "what other packages report through setStatus, minus the ones placed on their own",
|
|
542
|
+
render: ({ snapshot, glyphs, paint, options }) => {
|
|
543
|
+
const shown = [...snapshot.statuses].filter(([key]) => !options.claimed.has(key));
|
|
544
|
+
const mode = shown.find(([key]) => key === MODE_STATUS)?.[1];
|
|
545
|
+
const texts = shown
|
|
546
|
+
.filter(([key]) => key !== MODE_STATUS && !(key === SANDBOX_STATUS && snapshot.sandbox !== undefined))
|
|
547
|
+
.map(([, text]) => quietStatus(text, paint));
|
|
548
|
+
const divider = ` ${paint.dim(glyphs.apart)} `;
|
|
549
|
+
const leads = [
|
|
550
|
+
...(mode === undefined ? [] : [quietStatus(mode, paint)]),
|
|
551
|
+
...(snapshot.sandbox === undefined
|
|
552
|
+
? []
|
|
553
|
+
: [
|
|
554
|
+
snapshot.sandbox === "on"
|
|
555
|
+
? paint.dim("sandbox: on")
|
|
556
|
+
: paint.raw("warning", "sandbox: off"),
|
|
557
|
+
]),
|
|
558
|
+
];
|
|
559
|
+
if (leads.length > 0) {
|
|
560
|
+
const lead = leads.join(divider);
|
|
561
|
+
if (texts.length === 0) return { text: lead };
|
|
562
|
+
return { text: `${lead}${divider}${texts.join(" ")}`, compact: lead };
|
|
563
|
+
}
|
|
564
|
+
if (texts.length === 0) return undefined;
|
|
565
|
+
const icon = glyphs.plug === "" ? "" : `${paint.dim(glyphs.plug)} `;
|
|
566
|
+
return {
|
|
567
|
+
text: `${icon}${texts.join(" ")}`,
|
|
568
|
+
compact: `${icon}${texts[0] ?? ""}`,
|
|
569
|
+
};
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Each room once, with how this session stands in it, then its agents with the marks the rooms panel
|
|
576
|
+
* and the board use: working, waiting on you, or idle.
|
|
577
|
+
*/
|
|
578
|
+
function roomsPiece(
|
|
579
|
+
rooms: readonly RoomMark[],
|
|
580
|
+
mentions: string,
|
|
581
|
+
glyphs: Glyphs,
|
|
582
|
+
paint: Paint,
|
|
583
|
+
): Rendered {
|
|
584
|
+
const roomMark = (room: RoomMark): string => {
|
|
585
|
+
if (room.mode === "listening") return paint.raw("muted", glyphs.roomListening);
|
|
586
|
+
if (room.mode === "out") return paint.dim(glyphs.roomOut);
|
|
587
|
+
return paint.raw("accent", glyphs.roomIn);
|
|
588
|
+
};
|
|
589
|
+
const agent = (peer: PeerMark): string => {
|
|
590
|
+
const name = paint.raw(peer.color, peer.handle);
|
|
591
|
+
if (peer.state === "waiting") return `${paint.role("waiting", glyphs.waiting || "!")} ${name}`;
|
|
592
|
+
if (peer.state === "running") return `${paint.raw(peer.color, glyphs.agentWorking)} ${name}`;
|
|
593
|
+
return `${paint.dim(glyphs.agentIdle)} ${name}`;
|
|
594
|
+
};
|
|
595
|
+
const label = (room: RoomMark): string => `${roomMark(room)} ${paint.dim(room.name)}`;
|
|
596
|
+
const apart = ` ${paint.dim(glyphs.apart)} `;
|
|
597
|
+
const full = joined(
|
|
598
|
+
rooms.map((room) => [label(room), ...room.peers.map(agent)].join(" ")).join(apart),
|
|
599
|
+
mentions,
|
|
600
|
+
);
|
|
601
|
+
const [head] = rooms;
|
|
602
|
+
if (head === undefined) return undefined;
|
|
603
|
+
const total = rooms.reduce((sum, room) => sum + room.peers.length, 0);
|
|
604
|
+
const first = rooms.find((room) => room.peers.length > 0)?.peers[0];
|
|
605
|
+
const short = joined(
|
|
606
|
+
[label(head), ...(first === undefined ? [] : [agent(first)])].join(" ") +
|
|
607
|
+
(total > 1 ? paint.dim(` +${total - 1}`) : ""),
|
|
608
|
+
mentions,
|
|
609
|
+
);
|
|
610
|
+
const least = joined(roomMark(head), mentions);
|
|
611
|
+
return {
|
|
612
|
+
text: full,
|
|
613
|
+
shrink: (width) => {
|
|
614
|
+
if (visibleWidth(full) <= width) return full;
|
|
615
|
+
return visibleWidth(short) <= width ? short : least;
|
|
616
|
+
},
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export function renderSegments(ids: readonly SegmentId[], input: SegmentInput): Piece[] {
|
|
621
|
+
return ids.flatMap((id): Piece[] => {
|
|
622
|
+
if (id.startsWith(STATUS_PREFIX)) {
|
|
623
|
+
const text = input.snapshot.statuses.get(id.slice(STATUS_PREFIX.length));
|
|
624
|
+
return text === undefined
|
|
625
|
+
? []
|
|
626
|
+
: [safePiece({ text: quietStatus(text, input.paint), priority: STATUS_PRIORITY })];
|
|
627
|
+
}
|
|
628
|
+
const segment = SEGMENTS[id as BuiltinSegment];
|
|
629
|
+
const rendered = segment.render(input);
|
|
630
|
+
return rendered === undefined ? [] : [safePiece({ ...rendered, priority: segment.priority })];
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/** A status that picked no color would stand out from the footer, so it takes the footer's. */
|
|
635
|
+
function quietStatus(text: string, paint: Paint): string {
|
|
636
|
+
return text.includes("\u001b[") ? text : paint.dim(text);
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/** A branch, a folder, a session's name or another package's status can hold anything. */
|
|
640
|
+
function safePiece(piece: Piece): Piece {
|
|
641
|
+
const { compact, shrink } = piece;
|
|
642
|
+
return {
|
|
643
|
+
...piece,
|
|
644
|
+
text: styledText(piece.text),
|
|
645
|
+
...(compact === undefined ? {} : { compact: styledText(compact) }),
|
|
646
|
+
...(shrink === undefined ? {} : { shrink: (width: number) => styledText(shrink(width)) }),
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/** `5h 62% resets in 2h`; the percent carries the color. */
|
|
651
|
+
function quotaWindow(paint: Paint, window: AccountWindow): string {
|
|
652
|
+
const used = paint.stress(window.used, `${window.used}%`);
|
|
653
|
+
const reset =
|
|
654
|
+
window.resetsIn === undefined ? "" : ` ${paint.muted(`resets in ${window.resetsIn}`)}`;
|
|
655
|
+
return `${paint.dim(window.name)} ${used}${reset}`;
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
function joined(...parts: string[]): string {
|
|
659
|
+
return parts.filter((part) => part !== "").join(" ");
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
function mark(glyph: string, paint: Paint, role: Role): string {
|
|
663
|
+
return glyph === "" ? "" : `${paint.role(role, glyph)} `;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
/** Counted as blocked, not as to do; a closed card no longer waits. */
|
|
667
|
+
function blocks(card: BoardCard): boolean {
|
|
668
|
+
return card.blocked !== undefined && card.column !== "done" && card.column !== "dropped";
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/** ASCII has no icon, so the word goes after the number. */
|
|
672
|
+
function tally(
|
|
673
|
+
size: number,
|
|
674
|
+
glyph: string,
|
|
675
|
+
role: "done" | "queued" | "waiting",
|
|
676
|
+
paint: Paint,
|
|
677
|
+
): string {
|
|
678
|
+
if (size === 0) return "";
|
|
679
|
+
return paint.role(role, glyph === "" ? `${size} ${role}` : `${glyph} ${size}`);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
function counted(value: number, glyph: string, role: Role, paint: Paint): string {
|
|
683
|
+
return value > 0 ? paint.role(role, `${glyph}${value}`) : "";
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function cacheParts(
|
|
687
|
+
usage: Totals,
|
|
688
|
+
glyphs: Glyphs,
|
|
689
|
+
paint: Paint,
|
|
690
|
+
options: SegmentOptions,
|
|
691
|
+
fixed = false,
|
|
692
|
+
): string {
|
|
693
|
+
const value = (tokens: number): string => {
|
|
694
|
+
if (fixed) return tokenCell(tokens);
|
|
695
|
+
return tokens > 0 ? count(tokens) : "";
|
|
696
|
+
};
|
|
697
|
+
const read = value(usage.cacheRead);
|
|
698
|
+
const write = value(usage.cacheWrite);
|
|
699
|
+
if (options.labels) {
|
|
700
|
+
const parts = [
|
|
701
|
+
read === "" ? "" : `${paint.dim("read ")}${paint.role("cache", read)}`,
|
|
702
|
+
write === "" ? "" : `${paint.dim("wrote ")}${paint.role("cache", write)}`,
|
|
703
|
+
].filter((part) => part !== "");
|
|
704
|
+
return parts.length === 0 ? "" : `${paint.dim("cache ")}${parts.join(" ")}`;
|
|
705
|
+
}
|
|
706
|
+
return [
|
|
707
|
+
read === "" ? "" : `${paint.role("cache", glyphs.cacheRead)}${read}`,
|
|
708
|
+
write === "" ? "" : `${paint.role("cache", glyphs.cacheWrite)}${write}`,
|
|
709
|
+
]
|
|
710
|
+
.filter((part) => part !== "")
|
|
711
|
+
.join(" ");
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function flow(
|
|
715
|
+
role: "sent" | "received",
|
|
716
|
+
glyphs: Glyphs,
|
|
717
|
+
paint: Paint,
|
|
718
|
+
options: SegmentOptions,
|
|
719
|
+
): string {
|
|
720
|
+
const arrow = role === "sent" ? glyphs.sent : glyphs.received;
|
|
721
|
+
const name = role === "sent" ? "in" : "out";
|
|
722
|
+
return paint.role(role, options.labels ? `${arrow}${name} ` : arrow);
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function word(options: SegmentOptions, text: string, paint: Paint, skip = false): string {
|
|
726
|
+
return options.labels && !skip && text !== "" ? paint.dim(`${text} `) : "";
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function speedCell(value: number | undefined): string {
|
|
730
|
+
return cell(value === undefined ? UNKNOWN : rate(value), CELL.rate);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function tokenCell(value: number): string {
|
|
734
|
+
return cell(value === 0 ? UNKNOWN : count(value), CELL.count);
|
|
735
|
+
}
|