open-multi-agent-kit 0.94.0 → 0.95.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 +38 -0
- package/README.md +100 -646
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +11 -11
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts +27 -2
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +239 -56
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/context-budget-v2-cache-invalidation.d.ts.map +1 -1
- package/dist/core/context-budget-v2-cache-invalidation.js +1 -1
- package/dist/core/context-budget-v2-cache-invalidation.js.map +1 -1
- package/dist/core/context-budget-v2-cache-provider.d.ts +5 -1
- package/dist/core/context-budget-v2-cache-provider.d.ts.map +1 -1
- package/dist/core/context-budget-v2-cache-provider.js +37 -7
- package/dist/core/context-budget-v2-cache-provider.js.map +1 -1
- package/dist/core/context-budget-v2-cache-telemetry.d.ts +1 -1
- package/dist/core/context-budget-v2-cache-telemetry.d.ts.map +1 -1
- package/dist/core/context-budget-v2-cache-telemetry.js +2 -1
- package/dist/core/context-budget-v2-cache-telemetry.js.map +1 -1
- package/dist/core/context-budget-v2-cache.d.ts.map +1 -1
- package/dist/core/context-budget-v2-cache.js +3 -0
- package/dist/core/context-budget-v2-cache.js.map +1 -1
- package/dist/core/context-budget-v2-representation-cache.d.ts.map +1 -1
- package/dist/core/context-budget-v2-representation-cache.js +4 -3
- package/dist/core/context-budget-v2-representation-cache.js.map +1 -1
- package/dist/core/context-budget-v2-selection.d.ts.map +1 -1
- package/dist/core/context-budget-v2-selection.js +4 -0
- package/dist/core/context-budget-v2-selection.js.map +1 -1
- package/dist/core/context-budget-v2-types.d.ts +3 -0
- package/dist/core/context-budget-v2-types.d.ts.map +1 -1
- package/dist/core/context-budget-v2-types.js.map +1 -1
- package/dist/core/extensions/types.d.ts +2 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/github-repository.d.ts +3 -0
- package/dist/core/github-repository.d.ts.map +1 -0
- package/dist/core/github-repository.js +3 -0
- package/dist/core/github-repository.js.map +1 -0
- package/dist/core/model-registry.d.ts +3 -3
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +9 -8
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/provider-usage.d.ts +48 -0
- package/dist/core/provider-usage.d.ts.map +1 -0
- package/dist/core/provider-usage.js +780 -0
- package/dist/core/provider-usage.js.map +1 -0
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -0
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-termination.d.ts.map +1 -1
- package/dist/core/session-termination.js +8 -1
- package/dist/core/session-termination.js.map +1 -1
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +15 -8
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/skills-catalog-cache.d.ts +1 -0
- package/dist/core/skills-catalog-cache.d.ts.map +1 -1
- package/dist/core/skills-catalog-cache.js +42 -11
- package/dist/core/skills-catalog-cache.js.map +1 -1
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +1 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/read.d.ts +1 -1
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/components/index.d.ts +1 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -1
- package/dist/modes/interactive/components/index.js +1 -0
- package/dist/modes/interactive/components/index.js.map +1 -1
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/model-selector.js +5 -3
- package/dist/modes/interactive/components/model-selector.js.map +1 -1
- package/dist/modes/interactive/components/oauth-account-selector.d.ts +16 -0
- package/dist/modes/interactive/components/oauth-account-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/oauth-account-selector.js +61 -0
- package/dist/modes/interactive/components/oauth-account-selector.js.map +1 -0
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +25 -4
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/components/status-sidebar.d.ts +21 -1
- package/dist/modes/interactive/components/status-sidebar.d.ts.map +1 -1
- package/dist/modes/interactive/components/status-sidebar.js +165 -6
- package/dist/modes/interactive/components/status-sidebar.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +99 -15
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/image-resize-core.d.ts.map +1 -1
- package/dist/utils/image-resize-core.js +2 -2
- package/dist/utils/image-resize-core.js.map +1 -1
- package/docs/development.md +3 -2
- package/docs/providers.md +19 -4
- package/docs/settings.md +9 -9
- package/docs/usage.md +10 -9
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +24 -24
- package/package.json +6 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status-sidebar.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/status-sidebar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAQxF,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD;AAaD;;;;;;;GAOG;AACH,qBAAa,sBAAuB,YAAW,SAAS;IACvD,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,qBAAqB,CAAgB;IAC7C,OAAO,CAAC,eAAe,CAAe;IAGtC,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,QAAQ,CAAqE;IAErF,YACC,UAAU,EAAE,MAAM,YAAY,EAC9B,UAAU,EAAE,0BAA0B,EACtC,qBAAqB,EAAE,MAAM,OAAO,EACpC,eAAe,GAAE,MAAM,MAAiB,EAMxC;IAED,UAAU,IAAI,IAAI,CAGjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAkH9B;IAED;;;OAGG;IACH,OAAO,CAAC,UAAU;IAuBlB,OAAO,CAAC,cAAc;IAetB,0EAAwE;IACxE,OAAO,CAAC,SAAS;CAwBjB","sourcesContent":["import type { Component } from \"omk-tui\";\nimport { truncateToWidth, visibleWidth } from \"omk-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { loadMcpInventory, type McpServerEntry } from \"../../../core/mcp-inventory.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { boxBottom, boxTextLine, boxTop, sidebarRule } from \"./control-panel-box.ts\";\nimport { classifyMcpStability } from \"./control-panel-runtime-status.ts\";\nimport { formatBytes, formatCwdForFooter, formatPackageIntake, formatTokens } from \"./footer.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport const STATUS_SIDEBAR_WIDTH = 34;\nexport const STATUS_SIDEBAR_MAX_WIDTH = 48;\nexport const STATUS_SIDEBAR_MIN_WIDTH = 96;\n/** Blank columns between the main content and the pinned rail (part of the reserved gutter). */\nexport const STATUS_SIDEBAR_GUTTER_GAP = 1;\n\n/**\n * Responsive rail width: ~26% of the terminal, clamped to [34, 48] so the rail\n * grows on fullscreen terminals while the content column stays dominant.\n * The pinned overlay and the reserved gutter must both use this function.\n */\nexport function statusSidebarWidth(termWidth: number): number {\n\treturn Math.max(STATUS_SIDEBAR_WIDTH, Math.min(STATUS_SIDEBAR_MAX_WIDTH, Math.floor(termWidth * 0.26)));\n}\n\n/**\n * Responsive MCP roster rows: taller terminals list more servers before the\n * \"+N more\" collapse. ~24 rows of the rail are fixed chrome (header, model,\n * context, tokens, system), the rest is available to the roster.\n */\nexport function mcpMaxRows(termRows: number): number {\n\treturn Math.max(4, Math.min(18, termRows - 24));\n}\n\nconst METER_CELLS = 12;\nconst METER_MAX_CELLS = 24;\n/** Rolling window of CPU samples feeding the activity sparkline (wide rails show more history). */\nconst SPARK_WINDOW = 44;\n/** Minimum interval between sparkline samples so the rail animates, not jitters. */\nconst SPARK_SAMPLE_MS = 1000;\n/** MCP inventory is read from disk; cache it so per-frame renders stay cheap. */\nconst MCP_CACHE_TTL_MS = 5000;\n\nconst SPARK_CHARS = [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"] as const;\n\n/**\n * Fixed right-rail sidebar that shows the bottom-bar (footer) status vertically,\n * styled after opencode's pinned side rails plus a live MCP server roster.\n *\n * Pinned via the `pinStatusSidebar` setting (or OMK_PIN_STATUS_SIDEBAR=1) and\n * toggled with app.sidebar.toggle (Ctrl+Q). While pinned, the bottom footer is\n * removed so the same data is not rendered twice.\n */\nexport class StatusSidebarComponent implements Component {\n\tprivate getSession: () => AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\tprivate getAutoCompactEnabled: () => boolean;\n\tprivate getTerminalRows: () => number;\n\n\t// --- living elements: rolling CPU sparkline + cached MCP roster ---\n\tprivate cpuHistory: number[] = [];\n\tprivate lastSparkSampleAt = 0;\n\tprivate mcpCache: { at: number; cwd: string; entries: McpServerEntry[] } | undefined;\n\n\tconstructor(\n\t\tgetSession: () => AgentSession,\n\t\tfooterData: ReadonlyFooterDataProvider,\n\t\tgetAutoCompactEnabled: () => boolean,\n\t\tgetTerminalRows: () => number = () => 32,\n\t) {\n\t\tthis.getSession = getSession;\n\t\tthis.footerData = footerData;\n\t\tthis.getAutoCompactEnabled = getAutoCompactEnabled;\n\t\tthis.getTerminalRows = getTerminalRows;\n\t}\n\n\tinvalidate(): void {\n\t\t// Mostly stateless: status recomputes from the live session each render.\n\t\t// Only the sparkline history and MCP cache persist across frames.\n\t}\n\n\trender(width: number): string[] {\n\t\tconst session = this.getSession();\n\t\tconst state = session.state;\n\t\tconst lines: string[] = [boxTop(width, \"STATUS RAIL\")];\n\n\t\t// --- Live header: session uptime + CPU activity sparkline ---\n\t\tlines.push(\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"up \")}${theme.fg(\"accent\", formatUptime(process.uptime()))}`),\n\t\t);\n\t\tlines.push(boxTextLine(width, this.sparkline(width)));\n\n\t\t// --- Location ---\n\t\tconst cwd = formatCwdForFooter(session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"cwd \")}${theme.fg(\"text\", cwd)}`));\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"git \")}${theme.fg(\"accent\", branch)}`));\n\t\t}\n\t\tconst sessionName = session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"sess \")}${theme.fg(\"text\", sessionName)}`));\n\t\t}\n\n\t\t// --- Model ---\n\t\tlines.push(sidebarRule(width, \"MODEL\"));\n\t\tlines.push(\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"id \")}${theme.fg(\"accent\", state.model?.id ?? \"no-model\")}`),\n\t\t);\n\t\tif (state.model?.reasoning) {\n\t\t\tlines.push(\n\t\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"think \")}${theme.fg(\"mdCode\", state.thinkingLevel || \"off\")}`),\n\t\t\t);\n\t\t}\n\n\t\t// --- Context ---\n\t\tlines.push(sidebarRule(width, \"CONTEXT\"));\n\t\tconst contextUsage = session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst percent = contextUsage?.percent ?? null;\n\t\tconst auto = this.getAutoCompactEnabled() ? \" (auto)\" : \"\";\n\t\tconst percentLabel =\n\t\t\tpercent === null\n\t\t\t\t? `?/${formatTokens(contextWindow)}${auto}`\n\t\t\t\t: `${percent.toFixed(1)}%/${formatTokens(contextWindow)}${auto}`;\n\t\tconst percentColor: ThemeColor =\n\t\t\tpercent !== null && percent > 90 ? \"error\" : percent !== null && percent > 70 ? \"warning\" : \"success\";\n\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"ctx \")}${theme.fg(percentColor, percentLabel)}`));\n\t\tlines.push(boxTextLine(width, meter(percent, width)));\n\n\t\t// --- Tokens (cumulative across all session entries, mirrors the footer) ---\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\t\tfor (const entry of session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\t\tlines.push(sidebarRule(width, \"TOKENS\"));\n\t\tlines.push(\n\t\t\tboxTextLine(\n\t\t\t\twidth,\n\t\t\t\t`${theme.fg(\"success\", `↑${formatTokens(totalInput)}`)} ${theme.fg(\"accent\", `↓${formatTokens(totalOutput)}`)}`,\n\t\t\t),\n\t\t);\n\t\tlines.push(\n\t\t\tboxTextLine(\n\t\t\t\twidth,\n\t\t\t\t`${theme.fg(\"muted\", `R${formatTokens(totalCacheRead)}`)} ${theme.fg(\"muted\", `W${formatTokens(totalCacheWrite)}`)}`,\n\t\t\t),\n\t\t);\n\t\tconst usingSubscription = state.model ? session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tlines.push(\n\t\t\t\tboxTextLine(\n\t\t\t\t\twidth,\n\t\t\t\t\t`${theme.fg(\"muted\", \"cost \")}${theme.fg(\"warning\", `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// --- MCP roster (opencode-style server list with stability dots) ---\n\t\tlines.push(...this.mcpSection(width, session.sessionManager.getCwd()));\n\n\t\t// --- System ---\n\t\tlines.push(sidebarRule(width, \"SYSTEM\"));\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tconst mem = this.footerData.getMemoryRssBytes();\n\t\tconst sysParts: string[] = [];\n\t\tif (cpu !== null) sysParts.push(`cpu ${cpu.toFixed(0)}%`);\n\t\tif (mem !== null) sysParts.push(`mem ${formatBytes(mem)}`);\n\t\tif (sysParts.length > 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"muted\", sysParts.join(\" \"))));\n\t\t}\n\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", formatPackageIntake(this.footerData.getPackageIntakeSummary()))));\n\n\t\t// --- Extension statuses ---\n\t\tconst statuses = this.footerData.getExtensionStatuses();\n\t\tif (statuses.size > 0) {\n\t\t\tlines.push(sidebarRule(width, \"EXT\"));\n\t\t\tfor (const [key, value] of statuses) {\n\t\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"mdCode\", key)} ${theme.fg(\"muted\", value)}`));\n\t\t\t}\n\t\t}\n\n\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", `${keyText(\"app.sidebar.toggle\")} unpin`)));\n\t\tlines.push(boxBottom(width));\n\t\treturn lines;\n\t}\n\n\t/**\n\t * MCP server roster with a stable/total counter in the section rule and a\n\t * stability dot per server. Reads are cached so the rail stays cheap to render.\n\t */\n\tprivate mcpSection(width: number, cwd: string): string[] {\n\t\tconst entries = this.loadMcpEntries(cwd);\n\t\tconst stable = entries.filter((entry) => classifyMcpStability(entry) === \"stable\").length;\n\t\tconst countColor: ThemeColor = entries.length === 0 ? \"dim\" : stable === entries.length ? \"success\" : \"warning\";\n\t\tconst count = entries.length === 0 ? \"0\" : `${stable}/${entries.length}`;\n\n\t\tconst lines: string[] = [railRule(width, \"MCP\", theme.fg(countColor, count))];\n\t\tif (entries.length === 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", \"none configured\")));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst shown = entries.slice(0, mcpMaxRows(this.getTerminalRows()));\n\t\tfor (const entry of shown) {\n\t\t\tlines.push(boxTextLine(width, mcpRow(entry, width)));\n\t\t}\n\t\tconst hidden = entries.length - shown.length;\n\t\tif (hidden > 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", `+${hidden} more…`)));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate loadMcpEntries(cwd: string): McpServerEntry[] {\n\t\tconst now = Date.now();\n\t\tif (this.mcpCache && this.mcpCache.cwd === cwd && now - this.mcpCache.at < MCP_CACHE_TTL_MS) {\n\t\t\treturn this.mcpCache.entries;\n\t\t}\n\t\tlet entries: McpServerEntry[] = [];\n\t\ttry {\n\t\t\tentries = loadMcpInventory(cwd).entries;\n\t\t} catch {\n\t\t\tentries = [];\n\t\t}\n\t\tthis.mcpCache = { at: now, cwd, entries };\n\t\treturn entries;\n\t}\n\n\t/** Rolling CPU sparkline — a living pulse along the top of the rail. */\n\tprivate sparkline(width: number): string {\n\t\tconst now = Date.now();\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tif (cpu !== null && now - this.lastSparkSampleAt >= SPARK_SAMPLE_MS) {\n\t\t\tthis.lastSparkSampleAt = now;\n\t\t\tthis.cpuHistory.push(Math.max(0, Math.min(100, cpu)));\n\t\t\tif (this.cpuHistory.length > SPARK_WINDOW) {\n\t\t\t\tthis.cpuHistory.shift();\n\t\t\t}\n\t\t}\n\n\t\tconst inner = Math.max(0, width - 4);\n\t\tconst label = theme.fg(\"muted\", \"act \");\n\t\tconst cells = Math.max(0, inner - visibleWidth(label));\n\t\tif (cells === 0 || this.cpuHistory.length === 0) {\n\t\t\treturn `${label}${theme.fg(\"borderMuted\", \"▁\".repeat(Math.max(0, cells)))}`;\n\t\t}\n\n\t\t// Right-align the window so the sparkline grows in from the left edge.\n\t\tconst window = this.cpuHistory.slice(-cells);\n\t\tconst bars = window.map((sample) => SPARK_CHARS[Math.min(7, Math.floor(sample / 12.6))]).join(\"\");\n\t\tconst pad = \"▁\".repeat(Math.max(0, cells - window.length));\n\t\treturn `${label}${theme.fg(\"borderMuted\", pad)}${theme.fg(\"accent\", bars)}`;\n\t}\n}\n\n/** Section rule with a right-aligned counter, e.g. `│─ MCP ─────── 22/24 │`. */\nfunction railRule(width: number, label: string, right: string): string {\n\tconst left = `${theme.fg(\"borderMuted\", \"─\")}${theme.fg(\"accent\", ` ${label} `)}`;\n\tconst rightText = ` ${right} `;\n\tconst fill = Math.max(0, width - 2 - visibleWidth(left) - visibleWidth(rightText));\n\tconst line = `${theme.fg(\"borderMuted\", \"│\")}${left}${theme.fg(\"borderMuted\", \"─\".repeat(fill))}${rightText}${theme.fg(\"borderMuted\", \"│\")}`;\n\treturn visibleWidth(line) <= width ? line : truncateToWidth(line, width, \"\");\n}\n\n/** One MCP server row: stability dot + name, truncated to the rail width. */\nfunction mcpRow(entry: McpServerEntry, width: number): string {\n\tconst stability = classifyMcpStability(entry);\n\tconst dot =\n\t\tstability === \"stable\"\n\t\t\t? theme.fg(\"success\", \"●\")\n\t\t\t: stability === \"overridden\"\n\t\t\t\t? theme.fg(\"dim\", \"◐\")\n\t\t\t\t: theme.fg(\"warning\", \"○\");\n\tconst nameColor: ThemeColor = stability === \"stable\" ? \"text\" : stability === \"overridden\" ? \"dim\" : \"warning\";\n\tconst nameWidth = Math.max(1, width - 4 - 2); // frame (4) + dot + space (2)\n\tconst name = truncateToWidth(entry.name, nameWidth, \"…\");\n\treturn `${dot} ${theme.fg(nameColor, name)}`;\n}\n\nfunction formatUptime(seconds: number): string {\n\tconst total = Math.max(0, Math.floor(seconds));\n\tconst h = Math.floor(total / 3600);\n\tconst m = Math.floor((total % 3600) / 60);\n\tconst s = total % 60;\n\tconst pad = (n: number) => n.toString().padStart(2, \"0\");\n\treturn h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;\n}\n\nfunction meter(percent: number | null, width: number): string {\n\t// Fill the rail: frame (4) + space + up to \"100%\" (4) stay reserved.\n\tconst cells = Math.max(METER_CELLS, Math.min(METER_MAX_CELLS, width - 9));\n\tif (percent === null) {\n\t\treturn `${theme.fg(\"borderMuted\", \"░\".repeat(cells))} ${theme.fg(\"muted\", \"??%\")}`;\n\t}\n\tconst clamped = Math.max(0, Math.min(100, percent));\n\tconst filled = Math.round((clamped / 100) * cells);\n\tconst color: ThemeColor = clamped >= 85 ? \"warning\" : clamped >= 65 ? \"mdCode\" : \"success\";\n\treturn `${theme.fg(color, \"█\".repeat(filled))}${theme.fg(\"borderMuted\", \"░\".repeat(cells - filled))} ${theme.fg(color, `${Math.round(clamped)}%`)}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"status-sidebar.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/status-sidebar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAC;AAExF,OAAO,EACN,KAAK,kBAAkB,EAKvB,uBAAuB,EACvB,KAAK,yBAAyB,EAE9B,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EAAE,uBAAuB,EAAE,CAAC;AASnC,eAAO,MAAM,oBAAoB,KAAK,CAAC;AACvC,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,eAAO,MAAM,wBAAwB,KAAK,CAAC;AAC3C,gGAAgG;AAChG,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAE3C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnD;AAYD,KAAK,wBAAwB,GAAG,CAC/B,OAAO,EAAE,YAAY,EACrB,QAAQ,CAAC,EAAE,MAAM,KACb,OAAO,CAAC,yBAAyB,GAAG,SAAS,CAAC,CAAC;AACpD,KAAK,iBAAiB,GAAG,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAAC;AAC5F,KAAK,oBAAoB,GAAG,QAAQ,CAAC;IACpC,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,sBAAsB,CAAC,EAAE,wBAAwB,CAAC;IAClD,+EAA+E;IAC/E,eAAe,CAAC,EAAE,iBAAiB,CAAC;CACpC,CAAC,CAAC;AAEH;;;;;;;GAOG;AACH,qBAAa,sBAAuB,YAAW,SAAS;IACvD,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,UAAU,CAA6B;IAC/C,OAAO,CAAC,qBAAqB,CAAgB;IAC7C,OAAO,CAAC,eAAe,CAAe;IACtC,OAAO,CAAC,aAAa,CAAa;IAClC,OAAO,CAAC,sBAAsB,CAA2B;IAGzD,OAAO,CAAC,UAAU,CAAgB;IAClC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,QAAQ,CAAqE;IACrF,OAAO,CAAC,iBAAiB,CAAgD;IACzE,OAAO,CAAC,wBAAwB,CAA2B;IAC3D,OAAO,CAAC,0BAA0B,CAAyB;IAC3D,OAAO,CAAC,0BAA0B,CAA6B;IAC/D,OAAO,CAAC,yBAAyB,CAAmC;IACpE,OAAO,CAAC,yBAAyB,CAA6B;IAE9D,YACC,UAAU,EAAE,MAAM,YAAY,EAC9B,UAAU,EAAE,0BAA0B,EACtC,qBAAqB,EAAE,MAAM,OAAO,EACpC,eAAe,GAAE,MAAM,MAAiB,EACxC,OAAO,GAAE,oBAAyB,EAgBlC;IAED,UAAU,IAAI,IAAI,CAGjB;IAED,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAuH9B;IAED,OAAO,CAAC,wBAAwB;IA2DhC,OAAO,CAAC,wBAAwB;IA+BhC;;;OAGG;IACH,OAAO,CAAC,UAAU;IAuBlB,OAAO,CAAC,cAAc;IAetB,0EAAwE;IACxE,OAAO,CAAC,SAAS;CAwBjB","sourcesContent":["import type { Component } from \"omk-tui\";\nimport { truncateToWidth, visibleWidth } from \"omk-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { loadMcpInventory, type McpServerEntry } from \"../../../core/mcp-inventory.ts\";\nimport {\n\ttype CodexUsageSnapshot,\n\tgetConfiguredSubscriptionUsageProviders,\n\tgetSubscriptionUsageRevision,\n\tgetSubscriptionUsageSource,\n\tloadSubscriptionUsage,\n\tparseCodexUsageSnapshot,\n\ttype SubscriptionUsageSnapshot,\n\ttype SubscriptionUsageWindow,\n} from \"../../../core/provider-usage.ts\";\n\nexport { parseCodexUsageSnapshot };\n\nimport { stripAnsi } from \"../../../utils/ansi.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { boxBottom, boxTextLine, boxTop, sidebarRule } from \"./control-panel-box.ts\";\nimport { classifyMcpStability } from \"./control-panel-runtime-status.ts\";\nimport { formatBytes, formatCwdForFooter, formatPackageIntake, formatTokens } from \"./footer.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport const STATUS_SIDEBAR_WIDTH = 34;\nexport const STATUS_SIDEBAR_MAX_WIDTH = 48;\nexport const STATUS_SIDEBAR_MIN_WIDTH = 96;\n/** Blank columns between the main content and the pinned rail (part of the reserved gutter). */\nexport const STATUS_SIDEBAR_GUTTER_GAP = 1;\n\n/**\n * Responsive rail width: ~26% of the terminal, clamped to [34, 48] so the rail\n * grows on fullscreen terminals while the content column stays dominant.\n * The pinned overlay and the reserved gutter must both use this function.\n */\nexport function statusSidebarWidth(termWidth: number): number {\n\treturn Math.max(STATUS_SIDEBAR_WIDTH, Math.min(STATUS_SIDEBAR_MAX_WIDTH, Math.floor(termWidth * 0.26)));\n}\n\n/**\n * Responsive MCP roster rows: taller terminals list more servers before the\n * \"+N more\" collapse. ~24 rows of the rail are fixed chrome (header, model,\n * context, tokens, system), the rest is available to the roster.\n */\nexport function mcpMaxRows(termRows: number): number {\n\treturn Math.max(4, Math.min(18, termRows - 24));\n}\n\nconst METER_CELLS = 12;\nconst METER_MAX_CELLS = 24;\n/** Rolling window of CPU samples feeding the activity sparkline (wide rails show more history). */\nconst SPARK_WINDOW = 44;\n/** Minimum interval between sparkline samples so the rail animates, not jitters. */\nconst SPARK_SAMPLE_MS = 1000;\n/** MCP inventory is read from disk; cache it so per-frame renders stay cheap. */\nconst MCP_CACHE_TTL_MS = 5000;\nconst SPARK_CHARS = [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"] as const;\n\ntype SubscriptionUsageFetcher = (\n\tsession: AgentSession,\n\tprovider?: string,\n) => Promise<SubscriptionUsageSnapshot | undefined>;\ntype CodexUsageFetcher = (session: AgentSession) => Promise<CodexUsageSnapshot | undefined>;\ntype StatusSidebarOptions = Readonly<{\n\trequestRender?: () => void;\n\tfetchSubscriptionUsage?: SubscriptionUsageFetcher;\n\t/** Compatibility for callers that injected the original Codex-only fetcher. */\n\tfetchCodexUsage?: CodexUsageFetcher;\n}>;\n\n/**\n * Fixed right-rail sidebar that shows the bottom-bar (footer) status vertically,\n * styled after opencode's pinned side rails plus a live MCP server roster.\n *\n * Pinned via the `pinStatusSidebar` setting (or OMK_PIN_STATUS_SIDEBAR=1) and\n * toggled with app.sidebar.toggle (Ctrl+Q). While pinned, the bottom footer is\n * removed so the same data is not rendered twice.\n */\nexport class StatusSidebarComponent implements Component {\n\tprivate getSession: () => AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\tprivate getAutoCompactEnabled: () => boolean;\n\tprivate getTerminalRows: () => number;\n\tprivate requestRender: () => void;\n\tprivate fetchSubscriptionUsage: SubscriptionUsageFetcher;\n\n\t// --- living elements: rolling CPU sparkline + cached MCP/usage data ---\n\tprivate cpuHistory: number[] = [];\n\tprivate lastSparkSampleAt = 0;\n\tprivate mcpCache: { at: number; cwd: string; entries: McpServerEntry[] } | undefined;\n\tprivate subscriptionUsage = new Map<string, SubscriptionUsageSnapshot>();\n\tprivate subscriptionUsageSession: AgentSession | undefined;\n\tprivate subscriptionUsageProviders: readonly string[] = [];\n\tprivate subscriptionUsageFetchedAt = new Map<string, number>();\n\tprivate subscriptionUsageInFlight = new Map<string, AgentSession>();\n\tprivate subscriptionUsageRevision = new Map<string, number>();\n\n\tconstructor(\n\t\tgetSession: () => AgentSession,\n\t\tfooterData: ReadonlyFooterDataProvider,\n\t\tgetAutoCompactEnabled: () => boolean,\n\t\tgetTerminalRows: () => number = () => 32,\n\t\toptions: StatusSidebarOptions = {},\n\t) {\n\t\tthis.getSession = getSession;\n\t\tthis.footerData = footerData;\n\t\tthis.getAutoCompactEnabled = getAutoCompactEnabled;\n\t\tthis.getTerminalRows = getTerminalRows;\n\t\tthis.requestRender = options.requestRender ?? (() => {});\n\t\tconst legacyFetcher = options.fetchCodexUsage;\n\t\tthis.fetchSubscriptionUsage =\n\t\t\toptions.fetchSubscriptionUsage ??\n\t\t\t(legacyFetcher\n\t\t\t\t? async (session, provider) =>\n\t\t\t\t\t\tgetSubscriptionUsageSource(provider)?.kind === \"codex\"\n\t\t\t\t\t\t\t? codexSubscriptionSnapshot(await legacyFetcher(session))\n\t\t\t\t\t\t\t: loadSubscriptionUsage(session, undefined, provider)\n\t\t\t\t: (session, provider) => loadSubscriptionUsage(session, undefined, provider));\n\t}\n\n\tinvalidate(): void {\n\t\t// Mostly stateless: status recomputes from the live session each render.\n\t\t// Only the sparkline history and MCP cache persist across frames.\n\t}\n\n\trender(width: number): string[] {\n\t\tconst session = this.getSession();\n\t\tconst state = session.state;\n\t\tthis.refreshSubscriptionUsage(session);\n\t\tconst lines: string[] = [boxTop(width, \"STATUS RAIL\")];\n\n\t\t// --- Live header: session uptime + CPU activity sparkline ---\n\t\tlines.push(\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"up \")}${theme.fg(\"accent\", formatUptime(process.uptime()))}`),\n\t\t);\n\t\tlines.push(boxTextLine(width, this.sparkline(width)));\n\n\t\t// --- Location ---\n\t\tconst cwd = formatCwdForFooter(session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"cwd \")}${theme.fg(\"text\", cwd)}`));\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"git \")}${theme.fg(\"accent\", branch)}`));\n\t\t}\n\t\tconst sessionName = session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"sess \")}${theme.fg(\"text\", sessionName)}`));\n\t\t}\n\n\t\t// --- Model ---\n\t\tlines.push(sidebarRule(width, \"MODEL\"));\n\t\tlines.push(\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"id \")}${theme.fg(\"accent\", state.model?.id ?? \"no-model\")}`),\n\t\t);\n\t\tif (state.model?.reasoning) {\n\t\t\tlines.push(\n\t\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"think \")}${theme.fg(\"mdCode\", state.thinkingLevel || \"off\")}`),\n\t\t\t);\n\t\t}\n\n\t\t// --- Active provider subscription quota ---\n\t\tconst usageLines = this.subscriptionUsageSection(width);\n\t\tlines.push(...usageLines);\n\n\t\t// --- Context ---\n\t\tlines.push(sidebarRule(width, \"CONTEXT\"));\n\t\tconst contextUsage = session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst percent = contextUsage?.percent ?? null;\n\t\tconst auto = this.getAutoCompactEnabled() ? \" (auto)\" : \"\";\n\t\tconst percentLabel =\n\t\t\tpercent === null\n\t\t\t\t? `?/${formatTokens(contextWindow)}${auto}`\n\t\t\t\t: `${percent.toFixed(1)}%/${formatTokens(contextWindow)}${auto}`;\n\t\tconst percentColor: ThemeColor =\n\t\t\tpercent !== null && percent > 90 ? \"error\" : percent !== null && percent > 70 ? \"warning\" : \"success\";\n\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"ctx \")}${theme.fg(percentColor, percentLabel)}`));\n\t\tlines.push(boxTextLine(width, meter(percent, width)));\n\n\t\t// --- Tokens (cumulative across all session entries, mirrors the footer) ---\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\t\tfor (const entry of session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\t\tlines.push(sidebarRule(width, \"TOKENS\"));\n\t\tlines.push(\n\t\t\tboxTextLine(\n\t\t\t\twidth,\n\t\t\t\t`${theme.fg(\"success\", `↑${formatTokens(totalInput)}`)} ${theme.fg(\"accent\", `↓${formatTokens(totalOutput)}`)}`,\n\t\t\t),\n\t\t);\n\t\tlines.push(\n\t\t\tboxTextLine(\n\t\t\t\twidth,\n\t\t\t\t`${theme.fg(\"muted\", `R${formatTokens(totalCacheRead)}`)} ${theme.fg(\"muted\", `W${formatTokens(totalCacheWrite)}`)}`,\n\t\t\t),\n\t\t);\n\t\tconst usingSubscription = state.model ? session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tlines.push(\n\t\t\t\tboxTextLine(\n\t\t\t\t\twidth,\n\t\t\t\t\t`${theme.fg(\"muted\", \"cost \")}${theme.fg(\"warning\", `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// --- MCP roster (opencode-style server list with stability dots) ---\n\t\tlines.push(...this.mcpSection(width, session.sessionManager.getCwd(), usageLines.length));\n\n\t\t// --- System ---\n\t\tlines.push(sidebarRule(width, \"SYSTEM\"));\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tconst mem = this.footerData.getMemoryRssBytes();\n\t\tconst sysParts: string[] = [];\n\t\tif (cpu !== null) sysParts.push(`cpu ${cpu.toFixed(0)}%`);\n\t\tif (mem !== null) sysParts.push(`mem ${formatBytes(mem)}`);\n\t\tif (sysParts.length > 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"muted\", sysParts.join(\" \"))));\n\t\t}\n\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", formatPackageIntake(this.footerData.getPackageIntakeSummary()))));\n\n\t\t// --- Extension statuses ---\n\t\tconst statuses = this.footerData.getExtensionStatuses();\n\t\tif (statuses.size > 0) {\n\t\t\tlines.push(sidebarRule(width, \"EXT\"));\n\t\t\tfor (const [key, value] of statuses) {\n\t\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"mdCode\", key)} ${theme.fg(\"muted\", value)}`));\n\t\t\t}\n\t\t}\n\n\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", `${keyText(\"app.sidebar.toggle\")} unpin`)));\n\t\tlines.push(boxBottom(width));\n\t\treturn lines;\n\t}\n\n\tprivate refreshSubscriptionUsage(session: AgentSession): void {\n\t\tif (this.subscriptionUsageSession !== session) {\n\t\t\tthis.subscriptionUsage.clear();\n\t\t\tthis.subscriptionUsageFetchedAt.clear();\n\t\t\tthis.subscriptionUsageInFlight.clear();\n\t\t\tthis.subscriptionUsageRevision.clear();\n\t\t\tthis.subscriptionUsageSession = session;\n\t\t}\n\n\t\tconst providers = getConfiguredSubscriptionUsageProviders(session);\n\t\tthis.subscriptionUsageProviders = providers;\n\t\tconst configured = new Set(providers);\n\t\tfor (const provider of this.subscriptionUsage.keys()) {\n\t\t\tif (!configured.has(provider)) {\n\t\t\t\tthis.subscriptionUsage.delete(provider);\n\t\t\t\tthis.subscriptionUsageFetchedAt.delete(provider);\n\t\t\t\tthis.subscriptionUsageRevision.delete(provider);\n\t\t\t}\n\t\t}\n\n\t\tconst now = Date.now();\n\t\tfor (const provider of providers) {\n\t\t\tconst source = getSubscriptionUsageSource(provider);\n\t\t\tconst revision = getSubscriptionUsageRevision(provider);\n\t\t\tconst revisionChanged = revision !== (this.subscriptionUsageRevision.get(provider) ?? 0);\n\t\t\tif (\n\t\t\t\t!source ||\n\t\t\t\tthis.subscriptionUsageInFlight.has(provider) ||\n\t\t\t\t(!revisionChanged && now - (this.subscriptionUsageFetchedAt.get(provider) ?? 0) < source.ttlMs)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.subscriptionUsageFetchedAt.set(provider, now);\n\t\t\tthis.subscriptionUsageRevision.set(provider, revision);\n\t\t\tthis.subscriptionUsageInFlight.set(provider, session);\n\t\t\tvoid this.fetchSubscriptionUsage(session, provider)\n\t\t\t\t.then((snapshot) => {\n\t\t\t\t\tif (snapshot && this.getSession() === session && this.subscriptionUsageProviders.includes(provider)) {\n\t\t\t\t\t\tthis.subscriptionUsage.set(provider, snapshot);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {\n\t\t\t\t\tif (this.getSession() === session && this.subscriptionUsageProviders.includes(provider)) {\n\t\t\t\t\t\tthis.subscriptionUsage.set(provider, {\n\t\t\t\t\t\t\tlabel: source.label,\n\t\t\t\t\t\t\twindows: [],\n\t\t\t\t\t\t\tmessage: \"usage unavailable\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tif (this.subscriptionUsageInFlight.get(provider) === session) {\n\t\t\t\t\t\tthis.subscriptionUsageInFlight.delete(provider);\n\t\t\t\t\t}\n\t\t\t\t\tthis.requestRender();\n\t\t\t\t});\n\t\t}\n\t}\n\n\tprivate subscriptionUsageSection(width: number): string[] {\n\t\tif (this.subscriptionUsageProviders.length === 0) return [];\n\t\tconst lines = [railRule(width, \"USAGE\", theme.fg(\"accent\", String(this.subscriptionUsageProviders.length)))];\n\t\tfor (const provider of this.subscriptionUsageProviders) {\n\t\t\tconst source = getSubscriptionUsageSource(provider);\n\t\t\tconst snapshot = this.subscriptionUsage.get(provider);\n\t\t\tconst label = snapshot?.label ?? source?.label;\n\t\t\tif (!label) continue;\n\t\t\tif (!snapshot) {\n\t\t\t\tif (this.subscriptionUsageInFlight.has(provider)) {\n\t\t\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"accent\", label)} ${theme.fg(\"dim\", \"loading…\")}`));\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (snapshot.windows.length === 0) {\n\t\t\t\tlines.push(boxTextLine(width, theme.fg(\"accent\", label)));\n\t\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", snapshot.message ?? \"usage unavailable\")));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"accent\", label)));\n\t\t\tfor (const window of snapshot.windows) {\n\t\t\t\tlines.push(boxTextLine(width, usageMeter(window.label, window, width)));\n\t\t\t}\n\t\t\tconst resets = snapshot.windows.flatMap((window) =>\n\t\t\t\twindow.resetsAt === undefined ? [] : [`${window.label} ${formatReset(window.resetsAt)}`],\n\t\t\t);\n\t\t\tif (resets.length > 0) lines.push(boxTextLine(width, theme.fg(\"dim\", `reset ${resets.join(\" · \")}`)));\n\t\t}\n\t\treturn lines.length === 1 ? [] : lines;\n\t}\n\n\t/**\n\t * MCP server roster with a stable/total counter in the section rule and a\n\t * stability dot per server. Reads are cached so the rail stays cheap to render.\n\t */\n\tprivate mcpSection(width: number, cwd: string, reservedRows = 0): string[] {\n\t\tconst entries = this.loadMcpEntries(cwd);\n\t\tconst stable = entries.filter((entry) => classifyMcpStability(entry) === \"stable\").length;\n\t\tconst countColor: ThemeColor = entries.length === 0 ? \"dim\" : stable === entries.length ? \"success\" : \"warning\";\n\t\tconst count = entries.length === 0 ? \"0\" : `${stable}/${entries.length}`;\n\n\t\tconst lines: string[] = [railRule(width, \"MCP\", theme.fg(countColor, count))];\n\t\tif (entries.length === 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", \"none configured\")));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst shown = entries.slice(0, mcpMaxRows(this.getTerminalRows() - reservedRows));\n\t\tfor (const entry of shown) {\n\t\t\tlines.push(boxTextLine(width, mcpRow(entry, width)));\n\t\t}\n\t\tconst hidden = entries.length - shown.length;\n\t\tif (hidden > 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", `+${hidden} more…`)));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate loadMcpEntries(cwd: string): McpServerEntry[] {\n\t\tconst now = Date.now();\n\t\tif (this.mcpCache && this.mcpCache.cwd === cwd && now - this.mcpCache.at < MCP_CACHE_TTL_MS) {\n\t\t\treturn this.mcpCache.entries;\n\t\t}\n\t\tlet entries: McpServerEntry[] = [];\n\t\ttry {\n\t\t\tentries = loadMcpInventory(cwd).entries;\n\t\t} catch {\n\t\t\tentries = [];\n\t\t}\n\t\tthis.mcpCache = { at: now, cwd, entries };\n\t\treturn entries;\n\t}\n\n\t/** Rolling CPU sparkline — a living pulse along the top of the rail. */\n\tprivate sparkline(width: number): string {\n\t\tconst now = Date.now();\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tif (cpu !== null && now - this.lastSparkSampleAt >= SPARK_SAMPLE_MS) {\n\t\t\tthis.lastSparkSampleAt = now;\n\t\t\tthis.cpuHistory.push(Math.max(0, Math.min(100, cpu)));\n\t\t\tif (this.cpuHistory.length > SPARK_WINDOW) {\n\t\t\t\tthis.cpuHistory.shift();\n\t\t\t}\n\t\t}\n\n\t\tconst inner = Math.max(0, width - 4);\n\t\tconst label = theme.fg(\"muted\", \"act \");\n\t\tconst cells = Math.max(0, inner - visibleWidth(label));\n\t\tif (cells === 0 || this.cpuHistory.length === 0) {\n\t\t\treturn `${label}${theme.fg(\"borderMuted\", \"▁\".repeat(Math.max(0, cells)))}`;\n\t\t}\n\n\t\t// Right-align the window so the sparkline grows in from the left edge.\n\t\tconst window = this.cpuHistory.slice(-cells);\n\t\tconst bars = window.map((sample) => SPARK_CHARS[Math.min(7, Math.floor(sample / 12.6))]).join(\"\");\n\t\tconst pad = \"▁\".repeat(Math.max(0, cells - window.length));\n\t\treturn `${label}${theme.fg(\"borderMuted\", pad)}${theme.fg(\"accent\", bars)}`;\n\t}\n}\n\n/** Section rule with a right-aligned counter, e.g. `│─ MCP ─────── 22/24 │`. */\nfunction railRule(width: number, label: string, right: string): string {\n\tconst left = `${theme.fg(\"borderMuted\", \"─\")}${theme.fg(\"accent\", ` ${label} `)}`;\n\tconst rightText = ` ${right} `;\n\tconst fill = Math.max(0, width - 2 - visibleWidth(left) - visibleWidth(rightText));\n\tconst line = `${theme.fg(\"borderMuted\", \"│\")}${left}${theme.fg(\"borderMuted\", \"─\".repeat(fill))}${rightText}${theme.fg(\"borderMuted\", \"│\")}`;\n\treturn visibleWidth(line) <= width ? line : truncateToWidth(line, width, \"\");\n}\n\n/** One MCP server row: stability dot + name, truncated to the rail width. */\nfunction mcpRow(entry: McpServerEntry, width: number): string {\n\tconst stability = classifyMcpStability(entry);\n\tconst dot =\n\t\tstability === \"stable\"\n\t\t\t? theme.fg(\"success\", \"●\")\n\t\t\t: stability === \"overridden\"\n\t\t\t\t? theme.fg(\"dim\", \"◐\")\n\t\t\t\t: theme.fg(\"warning\", \"○\");\n\tconst nameColor: ThemeColor = stability === \"stable\" ? \"text\" : stability === \"overridden\" ? \"dim\" : \"warning\";\n\tconst nameWidth = Math.max(1, width - 4 - 2); // frame (4) + dot + space (2)\n\tconst safeName = stripAnsi(entry.name)\n\t\t.replace(/[\\t\\r\\n]+/g, \" \")\n\t\t.replace(/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f\\u200b-\\u200f\\u202a-\\u202e\\u2060-\\u206f]/g, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\tconst name = truncateToWidth(safeName || \"<unnamed>\", nameWidth, \"…\");\n\treturn `${dot} ${theme.fg(nameColor, name)}`;\n}\n\nfunction formatUptime(seconds: number): string {\n\tconst total = Math.max(0, Math.floor(seconds));\n\tconst h = Math.floor(total / 3600);\n\tconst m = Math.floor((total % 3600) / 60);\n\tconst s = total % 60;\n\tconst pad = (n: number) => n.toString().padStart(2, \"0\");\n\treturn h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;\n}\n\nfunction usageMeter(label: string, window: SubscriptionUsageWindow, width: number): string {\n\tconst percent = Math.max(0, Math.min(100, window.usedPercent));\n\tconst percentText = `${Math.round(percent)}%`;\n\tconst labelText = `${label} `;\n\tconst innerWidth = Math.max(0, width - 4);\n\tconst cells = Math.max(4, Math.min(METER_MAX_CELLS, innerWidth - labelText.length - percentText.length - 1));\n\tconst filled = Math.round((percent / 100) * cells);\n\tconst color = usageColor(percent);\n\treturn `${theme.fg(\"muted\", labelText)}${theme.fg(color, \"█\".repeat(filled))}${theme.fg(\"borderMuted\", \"░\".repeat(cells - filled))} ${theme.fg(color, percentText)}`;\n}\n\nfunction usageColor(percent: number): ThemeColor {\n\tif (percent >= 90) return \"error\";\n\tif (percent >= 75) return \"warning\";\n\treturn \"success\";\n}\n\nfunction codexSubscriptionSnapshot(snapshot: CodexUsageSnapshot | undefined): SubscriptionUsageSnapshot | undefined {\n\tif (!snapshot) return undefined;\n\treturn {\n\t\tlabel: \"CODEX\",\n\t\twindows: [\n\t\t\tsnapshot.fiveHour ? { label: \"5H\", ...snapshot.fiveHour } : undefined,\n\t\t\tsnapshot.sevenDay ? { label: \"7D\", ...snapshot.sevenDay } : undefined,\n\t\t].filter((window): window is SubscriptionUsageWindow => window !== undefined),\n\t};\n}\n\nfunction formatReset(resetsAt: number): string {\n\tconst seconds = Math.max(0, Math.ceil(resetsAt - Date.now() / 1000));\n\tif (seconds < 60) return \"now\";\n\tconst totalMinutes = Math.ceil(seconds / 60);\n\tif (totalMinutes < 60) return `${totalMinutes}m`;\n\tif (totalMinutes < 24 * 60) {\n\t\tconst hours = Math.floor(totalMinutes / 60);\n\t\tconst minutes = totalMinutes % 60;\n\t\treturn `${hours}h${minutes > 0 ? `${minutes}m` : \"\"}`;\n\t}\n\tconst totalHours = Math.ceil(totalMinutes / 60);\n\tconst days = Math.floor(totalHours / 24);\n\tconst hours = totalHours % 24;\n\treturn `${days}d${hours > 0 ? `${hours}h` : \"\"}`;\n}\n\nfunction meter(percent: number | null, width: number): string {\n\t// Fill the rail: frame (4) + space + up to \"100%\" (4) stay reserved.\n\tconst cells = Math.max(METER_CELLS, Math.min(METER_MAX_CELLS, width - 9));\n\tif (percent === null) {\n\t\treturn `${theme.fg(\"borderMuted\", \"░\".repeat(cells))} ${theme.fg(\"muted\", \"??%\")}`;\n\t}\n\tconst clamped = Math.max(0, Math.min(100, percent));\n\tconst filled = Math.round((clamped / 100) * cells);\n\tconst color: ThemeColor = clamped >= 85 ? \"warning\" : clamped >= 65 ? \"mdCode\" : \"success\";\n\treturn `${theme.fg(color, \"█\".repeat(filled))}${theme.fg(\"borderMuted\", \"░\".repeat(cells - filled))} ${theme.fg(color, `${Math.round(clamped)}%`)}`;\n}\n"]}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { truncateToWidth, visibleWidth } from "omk-tui";
|
|
2
2
|
import { loadMcpInventory } from "../../../core/mcp-inventory.js";
|
|
3
|
+
import { getConfiguredSubscriptionUsageProviders, getSubscriptionUsageRevision, getSubscriptionUsageSource, loadSubscriptionUsage, parseCodexUsageSnapshot, } from "../../../core/provider-usage.js";
|
|
4
|
+
export { parseCodexUsageSnapshot };
|
|
5
|
+
import { stripAnsi } from "../../../utils/ansi.js";
|
|
3
6
|
import { theme } from "../theme/theme.js";
|
|
4
7
|
import { boxBottom, boxTextLine, boxTop, sidebarRule } from "./control-panel-box.js";
|
|
5
8
|
import { classifyMcpStability } from "./control-panel-runtime-status.js";
|
|
@@ -48,15 +51,32 @@ export class StatusSidebarComponent {
|
|
|
48
51
|
footerData;
|
|
49
52
|
getAutoCompactEnabled;
|
|
50
53
|
getTerminalRows;
|
|
51
|
-
|
|
54
|
+
requestRender;
|
|
55
|
+
fetchSubscriptionUsage;
|
|
56
|
+
// --- living elements: rolling CPU sparkline + cached MCP/usage data ---
|
|
52
57
|
cpuHistory = [];
|
|
53
58
|
lastSparkSampleAt = 0;
|
|
54
59
|
mcpCache;
|
|
55
|
-
|
|
60
|
+
subscriptionUsage = new Map();
|
|
61
|
+
subscriptionUsageSession;
|
|
62
|
+
subscriptionUsageProviders = [];
|
|
63
|
+
subscriptionUsageFetchedAt = new Map();
|
|
64
|
+
subscriptionUsageInFlight = new Map();
|
|
65
|
+
subscriptionUsageRevision = new Map();
|
|
66
|
+
constructor(getSession, footerData, getAutoCompactEnabled, getTerminalRows = () => 32, options = {}) {
|
|
56
67
|
this.getSession = getSession;
|
|
57
68
|
this.footerData = footerData;
|
|
58
69
|
this.getAutoCompactEnabled = getAutoCompactEnabled;
|
|
59
70
|
this.getTerminalRows = getTerminalRows;
|
|
71
|
+
this.requestRender = options.requestRender ?? (() => { });
|
|
72
|
+
const legacyFetcher = options.fetchCodexUsage;
|
|
73
|
+
this.fetchSubscriptionUsage =
|
|
74
|
+
options.fetchSubscriptionUsage ??
|
|
75
|
+
(legacyFetcher
|
|
76
|
+
? async (session, provider) => getSubscriptionUsageSource(provider)?.kind === "codex"
|
|
77
|
+
? codexSubscriptionSnapshot(await legacyFetcher(session))
|
|
78
|
+
: loadSubscriptionUsage(session, undefined, provider)
|
|
79
|
+
: (session, provider) => loadSubscriptionUsage(session, undefined, provider));
|
|
60
80
|
}
|
|
61
81
|
invalidate() {
|
|
62
82
|
// Mostly stateless: status recomputes from the live session each render.
|
|
@@ -65,6 +85,7 @@ export class StatusSidebarComponent {
|
|
|
65
85
|
render(width) {
|
|
66
86
|
const session = this.getSession();
|
|
67
87
|
const state = session.state;
|
|
88
|
+
this.refreshSubscriptionUsage(session);
|
|
68
89
|
const lines = [boxTop(width, "STATUS RAIL")];
|
|
69
90
|
// --- Live header: session uptime + CPU activity sparkline ---
|
|
70
91
|
lines.push(boxTextLine(width, `${theme.fg("muted", "up ")}${theme.fg("accent", formatUptime(process.uptime()))}`));
|
|
@@ -86,6 +107,9 @@ export class StatusSidebarComponent {
|
|
|
86
107
|
if (state.model?.reasoning) {
|
|
87
108
|
lines.push(boxTextLine(width, `${theme.fg("muted", "think ")}${theme.fg("mdCode", state.thinkingLevel || "off")}`));
|
|
88
109
|
}
|
|
110
|
+
// --- Active provider subscription quota ---
|
|
111
|
+
const usageLines = this.subscriptionUsageSection(width);
|
|
112
|
+
lines.push(...usageLines);
|
|
89
113
|
// --- Context ---
|
|
90
114
|
lines.push(sidebarRule(width, "CONTEXT"));
|
|
91
115
|
const contextUsage = session.getContextUsage();
|
|
@@ -121,7 +145,7 @@ export class StatusSidebarComponent {
|
|
|
121
145
|
lines.push(boxTextLine(width, `${theme.fg("muted", "cost ")}${theme.fg("warning", `$${totalCost.toFixed(3)}${usingSubscription ? " (sub)" : ""}`)}`));
|
|
122
146
|
}
|
|
123
147
|
// --- MCP roster (opencode-style server list with stability dots) ---
|
|
124
|
-
lines.push(...this.mcpSection(width, session.sessionManager.getCwd()));
|
|
148
|
+
lines.push(...this.mcpSection(width, session.sessionManager.getCwd(), usageLines.length));
|
|
125
149
|
// --- System ---
|
|
126
150
|
lines.push(sidebarRule(width, "SYSTEM"));
|
|
127
151
|
const cpu = this.footerData.getCpuPercent();
|
|
@@ -147,11 +171,96 @@ export class StatusSidebarComponent {
|
|
|
147
171
|
lines.push(boxBottom(width));
|
|
148
172
|
return lines;
|
|
149
173
|
}
|
|
174
|
+
refreshSubscriptionUsage(session) {
|
|
175
|
+
if (this.subscriptionUsageSession !== session) {
|
|
176
|
+
this.subscriptionUsage.clear();
|
|
177
|
+
this.subscriptionUsageFetchedAt.clear();
|
|
178
|
+
this.subscriptionUsageInFlight.clear();
|
|
179
|
+
this.subscriptionUsageRevision.clear();
|
|
180
|
+
this.subscriptionUsageSession = session;
|
|
181
|
+
}
|
|
182
|
+
const providers = getConfiguredSubscriptionUsageProviders(session);
|
|
183
|
+
this.subscriptionUsageProviders = providers;
|
|
184
|
+
const configured = new Set(providers);
|
|
185
|
+
for (const provider of this.subscriptionUsage.keys()) {
|
|
186
|
+
if (!configured.has(provider)) {
|
|
187
|
+
this.subscriptionUsage.delete(provider);
|
|
188
|
+
this.subscriptionUsageFetchedAt.delete(provider);
|
|
189
|
+
this.subscriptionUsageRevision.delete(provider);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
const now = Date.now();
|
|
193
|
+
for (const provider of providers) {
|
|
194
|
+
const source = getSubscriptionUsageSource(provider);
|
|
195
|
+
const revision = getSubscriptionUsageRevision(provider);
|
|
196
|
+
const revisionChanged = revision !== (this.subscriptionUsageRevision.get(provider) ?? 0);
|
|
197
|
+
if (!source ||
|
|
198
|
+
this.subscriptionUsageInFlight.has(provider) ||
|
|
199
|
+
(!revisionChanged && now - (this.subscriptionUsageFetchedAt.get(provider) ?? 0) < source.ttlMs)) {
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
this.subscriptionUsageFetchedAt.set(provider, now);
|
|
203
|
+
this.subscriptionUsageRevision.set(provider, revision);
|
|
204
|
+
this.subscriptionUsageInFlight.set(provider, session);
|
|
205
|
+
void this.fetchSubscriptionUsage(session, provider)
|
|
206
|
+
.then((snapshot) => {
|
|
207
|
+
if (snapshot && this.getSession() === session && this.subscriptionUsageProviders.includes(provider)) {
|
|
208
|
+
this.subscriptionUsage.set(provider, snapshot);
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
.catch(() => {
|
|
212
|
+
if (this.getSession() === session && this.subscriptionUsageProviders.includes(provider)) {
|
|
213
|
+
this.subscriptionUsage.set(provider, {
|
|
214
|
+
label: source.label,
|
|
215
|
+
windows: [],
|
|
216
|
+
message: "usage unavailable",
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
.finally(() => {
|
|
221
|
+
if (this.subscriptionUsageInFlight.get(provider) === session) {
|
|
222
|
+
this.subscriptionUsageInFlight.delete(provider);
|
|
223
|
+
}
|
|
224
|
+
this.requestRender();
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
subscriptionUsageSection(width) {
|
|
229
|
+
if (this.subscriptionUsageProviders.length === 0)
|
|
230
|
+
return [];
|
|
231
|
+
const lines = [railRule(width, "USAGE", theme.fg("accent", String(this.subscriptionUsageProviders.length)))];
|
|
232
|
+
for (const provider of this.subscriptionUsageProviders) {
|
|
233
|
+
const source = getSubscriptionUsageSource(provider);
|
|
234
|
+
const snapshot = this.subscriptionUsage.get(provider);
|
|
235
|
+
const label = snapshot?.label ?? source?.label;
|
|
236
|
+
if (!label)
|
|
237
|
+
continue;
|
|
238
|
+
if (!snapshot) {
|
|
239
|
+
if (this.subscriptionUsageInFlight.has(provider)) {
|
|
240
|
+
lines.push(boxTextLine(width, `${theme.fg("accent", label)} ${theme.fg("dim", "loading…")}`));
|
|
241
|
+
}
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (snapshot.windows.length === 0) {
|
|
245
|
+
lines.push(boxTextLine(width, theme.fg("accent", label)));
|
|
246
|
+
lines.push(boxTextLine(width, theme.fg("dim", snapshot.message ?? "usage unavailable")));
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
lines.push(boxTextLine(width, theme.fg("accent", label)));
|
|
250
|
+
for (const window of snapshot.windows) {
|
|
251
|
+
lines.push(boxTextLine(width, usageMeter(window.label, window, width)));
|
|
252
|
+
}
|
|
253
|
+
const resets = snapshot.windows.flatMap((window) => window.resetsAt === undefined ? [] : [`${window.label} ${formatReset(window.resetsAt)}`]);
|
|
254
|
+
if (resets.length > 0)
|
|
255
|
+
lines.push(boxTextLine(width, theme.fg("dim", `reset ${resets.join(" · ")}`)));
|
|
256
|
+
}
|
|
257
|
+
return lines.length === 1 ? [] : lines;
|
|
258
|
+
}
|
|
150
259
|
/**
|
|
151
260
|
* MCP server roster with a stable/total counter in the section rule and a
|
|
152
261
|
* stability dot per server. Reads are cached so the rail stays cheap to render.
|
|
153
262
|
*/
|
|
154
|
-
mcpSection(width, cwd) {
|
|
263
|
+
mcpSection(width, cwd, reservedRows = 0) {
|
|
155
264
|
const entries = this.loadMcpEntries(cwd);
|
|
156
265
|
const stable = entries.filter((entry) => classifyMcpStability(entry) === "stable").length;
|
|
157
266
|
const countColor = entries.length === 0 ? "dim" : stable === entries.length ? "success" : "warning";
|
|
@@ -161,7 +270,7 @@ export class StatusSidebarComponent {
|
|
|
161
270
|
lines.push(boxTextLine(width, theme.fg("dim", "none configured")));
|
|
162
271
|
return lines;
|
|
163
272
|
}
|
|
164
|
-
const shown = entries.slice(0, mcpMaxRows(this.getTerminalRows()));
|
|
273
|
+
const shown = entries.slice(0, mcpMaxRows(this.getTerminalRows() - reservedRows));
|
|
165
274
|
for (const entry of shown) {
|
|
166
275
|
lines.push(boxTextLine(width, mcpRow(entry, width)));
|
|
167
276
|
}
|
|
@@ -228,7 +337,12 @@ function mcpRow(entry, width) {
|
|
|
228
337
|
: theme.fg("warning", "○");
|
|
229
338
|
const nameColor = stability === "stable" ? "text" : stability === "overridden" ? "dim" : "warning";
|
|
230
339
|
const nameWidth = Math.max(1, width - 4 - 2); // frame (4) + dot + space (2)
|
|
231
|
-
const
|
|
340
|
+
const safeName = stripAnsi(entry.name)
|
|
341
|
+
.replace(/[\t\r\n]+/g, " ")
|
|
342
|
+
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2060-\u206f]/g, "")
|
|
343
|
+
.replace(/\s+/g, " ")
|
|
344
|
+
.trim();
|
|
345
|
+
const name = truncateToWidth(safeName || "<unnamed>", nameWidth, "…");
|
|
232
346
|
return `${dot} ${theme.fg(nameColor, name)}`;
|
|
233
347
|
}
|
|
234
348
|
function formatUptime(seconds) {
|
|
@@ -239,6 +353,51 @@ function formatUptime(seconds) {
|
|
|
239
353
|
const pad = (n) => n.toString().padStart(2, "0");
|
|
240
354
|
return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;
|
|
241
355
|
}
|
|
356
|
+
function usageMeter(label, window, width) {
|
|
357
|
+
const percent = Math.max(0, Math.min(100, window.usedPercent));
|
|
358
|
+
const percentText = `${Math.round(percent)}%`;
|
|
359
|
+
const labelText = `${label} `;
|
|
360
|
+
const innerWidth = Math.max(0, width - 4);
|
|
361
|
+
const cells = Math.max(4, Math.min(METER_MAX_CELLS, innerWidth - labelText.length - percentText.length - 1));
|
|
362
|
+
const filled = Math.round((percent / 100) * cells);
|
|
363
|
+
const color = usageColor(percent);
|
|
364
|
+
return `${theme.fg("muted", labelText)}${theme.fg(color, "█".repeat(filled))}${theme.fg("borderMuted", "░".repeat(cells - filled))} ${theme.fg(color, percentText)}`;
|
|
365
|
+
}
|
|
366
|
+
function usageColor(percent) {
|
|
367
|
+
if (percent >= 90)
|
|
368
|
+
return "error";
|
|
369
|
+
if (percent >= 75)
|
|
370
|
+
return "warning";
|
|
371
|
+
return "success";
|
|
372
|
+
}
|
|
373
|
+
function codexSubscriptionSnapshot(snapshot) {
|
|
374
|
+
if (!snapshot)
|
|
375
|
+
return undefined;
|
|
376
|
+
return {
|
|
377
|
+
label: "CODEX",
|
|
378
|
+
windows: [
|
|
379
|
+
snapshot.fiveHour ? { label: "5H", ...snapshot.fiveHour } : undefined,
|
|
380
|
+
snapshot.sevenDay ? { label: "7D", ...snapshot.sevenDay } : undefined,
|
|
381
|
+
].filter((window) => window !== undefined),
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
function formatReset(resetsAt) {
|
|
385
|
+
const seconds = Math.max(0, Math.ceil(resetsAt - Date.now() / 1000));
|
|
386
|
+
if (seconds < 60)
|
|
387
|
+
return "now";
|
|
388
|
+
const totalMinutes = Math.ceil(seconds / 60);
|
|
389
|
+
if (totalMinutes < 60)
|
|
390
|
+
return `${totalMinutes}m`;
|
|
391
|
+
if (totalMinutes < 24 * 60) {
|
|
392
|
+
const hours = Math.floor(totalMinutes / 60);
|
|
393
|
+
const minutes = totalMinutes % 60;
|
|
394
|
+
return `${hours}h${minutes > 0 ? `${minutes}m` : ""}`;
|
|
395
|
+
}
|
|
396
|
+
const totalHours = Math.ceil(totalMinutes / 60);
|
|
397
|
+
const days = Math.floor(totalHours / 24);
|
|
398
|
+
const hours = totalHours % 24;
|
|
399
|
+
return `${days}d${hours > 0 ? `${hours}h` : ""}`;
|
|
400
|
+
}
|
|
242
401
|
function meter(percent, width) {
|
|
243
402
|
// Fill the rail: frame (4) + space + up to "100%" (4) stay reserved.
|
|
244
403
|
const cells = Math.max(METER_CELLS, Math.min(METER_MAX_CELLS, width - 9));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status-sidebar.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/status-sidebar.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component } from \"omk-tui\";\nimport { truncateToWidth, visibleWidth } from \"omk-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { loadMcpInventory, type McpServerEntry } from \"../../../core/mcp-inventory.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { boxBottom, boxTextLine, boxTop, sidebarRule } from \"./control-panel-box.ts\";\nimport { classifyMcpStability } from \"./control-panel-runtime-status.ts\";\nimport { formatBytes, formatCwdForFooter, formatPackageIntake, formatTokens } from \"./footer.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport const STATUS_SIDEBAR_WIDTH = 34;\nexport const STATUS_SIDEBAR_MAX_WIDTH = 48;\nexport const STATUS_SIDEBAR_MIN_WIDTH = 96;\n/** Blank columns between the main content and the pinned rail (part of the reserved gutter). */\nexport const STATUS_SIDEBAR_GUTTER_GAP = 1;\n\n/**\n * Responsive rail width: ~26% of the terminal, clamped to [34, 48] so the rail\n * grows on fullscreen terminals while the content column stays dominant.\n * The pinned overlay and the reserved gutter must both use this function.\n */\nexport function statusSidebarWidth(termWidth: number): number {\n\treturn Math.max(STATUS_SIDEBAR_WIDTH, Math.min(STATUS_SIDEBAR_MAX_WIDTH, Math.floor(termWidth * 0.26)));\n}\n\n/**\n * Responsive MCP roster rows: taller terminals list more servers before the\n * \"+N more\" collapse. ~24 rows of the rail are fixed chrome (header, model,\n * context, tokens, system), the rest is available to the roster.\n */\nexport function mcpMaxRows(termRows: number): number {\n\treturn Math.max(4, Math.min(18, termRows - 24));\n}\n\nconst METER_CELLS = 12;\nconst METER_MAX_CELLS = 24;\n/** Rolling window of CPU samples feeding the activity sparkline (wide rails show more history). */\nconst SPARK_WINDOW = 44;\n/** Minimum interval between sparkline samples so the rail animates, not jitters. */\nconst SPARK_SAMPLE_MS = 1000;\n/** MCP inventory is read from disk; cache it so per-frame renders stay cheap. */\nconst MCP_CACHE_TTL_MS = 5000;\n\nconst SPARK_CHARS = [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"] as const;\n\n/**\n * Fixed right-rail sidebar that shows the bottom-bar (footer) status vertically,\n * styled after opencode's pinned side rails plus a live MCP server roster.\n *\n * Pinned via the `pinStatusSidebar` setting (or OMK_PIN_STATUS_SIDEBAR=1) and\n * toggled with app.sidebar.toggle (Ctrl+Q). While pinned, the bottom footer is\n * removed so the same data is not rendered twice.\n */\nexport class StatusSidebarComponent implements Component {\n\tprivate getSession: () => AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\tprivate getAutoCompactEnabled: () => boolean;\n\tprivate getTerminalRows: () => number;\n\n\t// --- living elements: rolling CPU sparkline + cached MCP roster ---\n\tprivate cpuHistory: number[] = [];\n\tprivate lastSparkSampleAt = 0;\n\tprivate mcpCache: { at: number; cwd: string; entries: McpServerEntry[] } | undefined;\n\n\tconstructor(\n\t\tgetSession: () => AgentSession,\n\t\tfooterData: ReadonlyFooterDataProvider,\n\t\tgetAutoCompactEnabled: () => boolean,\n\t\tgetTerminalRows: () => number = () => 32,\n\t) {\n\t\tthis.getSession = getSession;\n\t\tthis.footerData = footerData;\n\t\tthis.getAutoCompactEnabled = getAutoCompactEnabled;\n\t\tthis.getTerminalRows = getTerminalRows;\n\t}\n\n\tinvalidate(): void {\n\t\t// Mostly stateless: status recomputes from the live session each render.\n\t\t// Only the sparkline history and MCP cache persist across frames.\n\t}\n\n\trender(width: number): string[] {\n\t\tconst session = this.getSession();\n\t\tconst state = session.state;\n\t\tconst lines: string[] = [boxTop(width, \"STATUS RAIL\")];\n\n\t\t// --- Live header: session uptime + CPU activity sparkline ---\n\t\tlines.push(\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"up \")}${theme.fg(\"accent\", formatUptime(process.uptime()))}`),\n\t\t);\n\t\tlines.push(boxTextLine(width, this.sparkline(width)));\n\n\t\t// --- Location ---\n\t\tconst cwd = formatCwdForFooter(session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"cwd \")}${theme.fg(\"text\", cwd)}`));\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"git \")}${theme.fg(\"accent\", branch)}`));\n\t\t}\n\t\tconst sessionName = session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"sess \")}${theme.fg(\"text\", sessionName)}`));\n\t\t}\n\n\t\t// --- Model ---\n\t\tlines.push(sidebarRule(width, \"MODEL\"));\n\t\tlines.push(\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"id \")}${theme.fg(\"accent\", state.model?.id ?? \"no-model\")}`),\n\t\t);\n\t\tif (state.model?.reasoning) {\n\t\t\tlines.push(\n\t\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"think \")}${theme.fg(\"mdCode\", state.thinkingLevel || \"off\")}`),\n\t\t\t);\n\t\t}\n\n\t\t// --- Context ---\n\t\tlines.push(sidebarRule(width, \"CONTEXT\"));\n\t\tconst contextUsage = session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst percent = contextUsage?.percent ?? null;\n\t\tconst auto = this.getAutoCompactEnabled() ? \" (auto)\" : \"\";\n\t\tconst percentLabel =\n\t\t\tpercent === null\n\t\t\t\t? `?/${formatTokens(contextWindow)}${auto}`\n\t\t\t\t: `${percent.toFixed(1)}%/${formatTokens(contextWindow)}${auto}`;\n\t\tconst percentColor: ThemeColor =\n\t\t\tpercent !== null && percent > 90 ? \"error\" : percent !== null && percent > 70 ? \"warning\" : \"success\";\n\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"ctx \")}${theme.fg(percentColor, percentLabel)}`));\n\t\tlines.push(boxTextLine(width, meter(percent, width)));\n\n\t\t// --- Tokens (cumulative across all session entries, mirrors the footer) ---\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\t\tfor (const entry of session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\t\tlines.push(sidebarRule(width, \"TOKENS\"));\n\t\tlines.push(\n\t\t\tboxTextLine(\n\t\t\t\twidth,\n\t\t\t\t`${theme.fg(\"success\", `↑${formatTokens(totalInput)}`)} ${theme.fg(\"accent\", `↓${formatTokens(totalOutput)}`)}`,\n\t\t\t),\n\t\t);\n\t\tlines.push(\n\t\t\tboxTextLine(\n\t\t\t\twidth,\n\t\t\t\t`${theme.fg(\"muted\", `R${formatTokens(totalCacheRead)}`)} ${theme.fg(\"muted\", `W${formatTokens(totalCacheWrite)}`)}`,\n\t\t\t),\n\t\t);\n\t\tconst usingSubscription = state.model ? session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tlines.push(\n\t\t\t\tboxTextLine(\n\t\t\t\t\twidth,\n\t\t\t\t\t`${theme.fg(\"muted\", \"cost \")}${theme.fg(\"warning\", `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// --- MCP roster (opencode-style server list with stability dots) ---\n\t\tlines.push(...this.mcpSection(width, session.sessionManager.getCwd()));\n\n\t\t// --- System ---\n\t\tlines.push(sidebarRule(width, \"SYSTEM\"));\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tconst mem = this.footerData.getMemoryRssBytes();\n\t\tconst sysParts: string[] = [];\n\t\tif (cpu !== null) sysParts.push(`cpu ${cpu.toFixed(0)}%`);\n\t\tif (mem !== null) sysParts.push(`mem ${formatBytes(mem)}`);\n\t\tif (sysParts.length > 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"muted\", sysParts.join(\" \"))));\n\t\t}\n\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", formatPackageIntake(this.footerData.getPackageIntakeSummary()))));\n\n\t\t// --- Extension statuses ---\n\t\tconst statuses = this.footerData.getExtensionStatuses();\n\t\tif (statuses.size > 0) {\n\t\t\tlines.push(sidebarRule(width, \"EXT\"));\n\t\t\tfor (const [key, value] of statuses) {\n\t\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"mdCode\", key)} ${theme.fg(\"muted\", value)}`));\n\t\t\t}\n\t\t}\n\n\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", `${keyText(\"app.sidebar.toggle\")} unpin`)));\n\t\tlines.push(boxBottom(width));\n\t\treturn lines;\n\t}\n\n\t/**\n\t * MCP server roster with a stable/total counter in the section rule and a\n\t * stability dot per server. Reads are cached so the rail stays cheap to render.\n\t */\n\tprivate mcpSection(width: number, cwd: string): string[] {\n\t\tconst entries = this.loadMcpEntries(cwd);\n\t\tconst stable = entries.filter((entry) => classifyMcpStability(entry) === \"stable\").length;\n\t\tconst countColor: ThemeColor = entries.length === 0 ? \"dim\" : stable === entries.length ? \"success\" : \"warning\";\n\t\tconst count = entries.length === 0 ? \"0\" : `${stable}/${entries.length}`;\n\n\t\tconst lines: string[] = [railRule(width, \"MCP\", theme.fg(countColor, count))];\n\t\tif (entries.length === 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", \"none configured\")));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst shown = entries.slice(0, mcpMaxRows(this.getTerminalRows()));\n\t\tfor (const entry of shown) {\n\t\t\tlines.push(boxTextLine(width, mcpRow(entry, width)));\n\t\t}\n\t\tconst hidden = entries.length - shown.length;\n\t\tif (hidden > 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", `+${hidden} more…`)));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate loadMcpEntries(cwd: string): McpServerEntry[] {\n\t\tconst now = Date.now();\n\t\tif (this.mcpCache && this.mcpCache.cwd === cwd && now - this.mcpCache.at < MCP_CACHE_TTL_MS) {\n\t\t\treturn this.mcpCache.entries;\n\t\t}\n\t\tlet entries: McpServerEntry[] = [];\n\t\ttry {\n\t\t\tentries = loadMcpInventory(cwd).entries;\n\t\t} catch {\n\t\t\tentries = [];\n\t\t}\n\t\tthis.mcpCache = { at: now, cwd, entries };\n\t\treturn entries;\n\t}\n\n\t/** Rolling CPU sparkline — a living pulse along the top of the rail. */\n\tprivate sparkline(width: number): string {\n\t\tconst now = Date.now();\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tif (cpu !== null && now - this.lastSparkSampleAt >= SPARK_SAMPLE_MS) {\n\t\t\tthis.lastSparkSampleAt = now;\n\t\t\tthis.cpuHistory.push(Math.max(0, Math.min(100, cpu)));\n\t\t\tif (this.cpuHistory.length > SPARK_WINDOW) {\n\t\t\t\tthis.cpuHistory.shift();\n\t\t\t}\n\t\t}\n\n\t\tconst inner = Math.max(0, width - 4);\n\t\tconst label = theme.fg(\"muted\", \"act \");\n\t\tconst cells = Math.max(0, inner - visibleWidth(label));\n\t\tif (cells === 0 || this.cpuHistory.length === 0) {\n\t\t\treturn `${label}${theme.fg(\"borderMuted\", \"▁\".repeat(Math.max(0, cells)))}`;\n\t\t}\n\n\t\t// Right-align the window so the sparkline grows in from the left edge.\n\t\tconst window = this.cpuHistory.slice(-cells);\n\t\tconst bars = window.map((sample) => SPARK_CHARS[Math.min(7, Math.floor(sample / 12.6))]).join(\"\");\n\t\tconst pad = \"▁\".repeat(Math.max(0, cells - window.length));\n\t\treturn `${label}${theme.fg(\"borderMuted\", pad)}${theme.fg(\"accent\", bars)}`;\n\t}\n}\n\n/** Section rule with a right-aligned counter, e.g. `│─ MCP ─────── 22/24 │`. */\nfunction railRule(width: number, label: string, right: string): string {\n\tconst left = `${theme.fg(\"borderMuted\", \"─\")}${theme.fg(\"accent\", ` ${label} `)}`;\n\tconst rightText = ` ${right} `;\n\tconst fill = Math.max(0, width - 2 - visibleWidth(left) - visibleWidth(rightText));\n\tconst line = `${theme.fg(\"borderMuted\", \"│\")}${left}${theme.fg(\"borderMuted\", \"─\".repeat(fill))}${rightText}${theme.fg(\"borderMuted\", \"│\")}`;\n\treturn visibleWidth(line) <= width ? line : truncateToWidth(line, width, \"\");\n}\n\n/** One MCP server row: stability dot + name, truncated to the rail width. */\nfunction mcpRow(entry: McpServerEntry, width: number): string {\n\tconst stability = classifyMcpStability(entry);\n\tconst dot =\n\t\tstability === \"stable\"\n\t\t\t? theme.fg(\"success\", \"●\")\n\t\t\t: stability === \"overridden\"\n\t\t\t\t? theme.fg(\"dim\", \"◐\")\n\t\t\t\t: theme.fg(\"warning\", \"○\");\n\tconst nameColor: ThemeColor = stability === \"stable\" ? \"text\" : stability === \"overridden\" ? \"dim\" : \"warning\";\n\tconst nameWidth = Math.max(1, width - 4 - 2); // frame (4) + dot + space (2)\n\tconst name = truncateToWidth(entry.name, nameWidth, \"…\");\n\treturn `${dot} ${theme.fg(nameColor, name)}`;\n}\n\nfunction formatUptime(seconds: number): string {\n\tconst total = Math.max(0, Math.floor(seconds));\n\tconst h = Math.floor(total / 3600);\n\tconst m = Math.floor((total % 3600) / 60);\n\tconst s = total % 60;\n\tconst pad = (n: number) => n.toString().padStart(2, \"0\");\n\treturn h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;\n}\n\nfunction meter(percent: number | null, width: number): string {\n\t// Fill the rail: frame (4) + space + up to \"100%\" (4) stay reserved.\n\tconst cells = Math.max(METER_CELLS, Math.min(METER_MAX_CELLS, width - 9));\n\tif (percent === null) {\n\t\treturn `${theme.fg(\"borderMuted\", \"░\".repeat(cells))} ${theme.fg(\"muted\", \"??%\")}`;\n\t}\n\tconst clamped = Math.max(0, Math.min(100, percent));\n\tconst filled = Math.round((clamped / 100) * cells);\n\tconst color: ThemeColor = clamped >= 85 ? \"warning\" : clamped >= 65 ? \"mdCode\" : \"success\";\n\treturn `${theme.fg(color, \"█\".repeat(filled))}${theme.fg(\"borderMuted\", \"░\".repeat(cells - filled))} ${theme.fg(color, `${Math.round(clamped)}%`)}`;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"status-sidebar.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/status-sidebar.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Component } from \"omk-tui\";\nimport { truncateToWidth, visibleWidth } from \"omk-tui\";\nimport type { AgentSession } from \"../../../core/agent-session.ts\";\nimport type { ReadonlyFooterDataProvider } from \"../../../core/footer-data-provider.ts\";\nimport { loadMcpInventory, type McpServerEntry } from \"../../../core/mcp-inventory.ts\";\nimport {\n\ttype CodexUsageSnapshot,\n\tgetConfiguredSubscriptionUsageProviders,\n\tgetSubscriptionUsageRevision,\n\tgetSubscriptionUsageSource,\n\tloadSubscriptionUsage,\n\tparseCodexUsageSnapshot,\n\ttype SubscriptionUsageSnapshot,\n\ttype SubscriptionUsageWindow,\n} from \"../../../core/provider-usage.ts\";\n\nexport { parseCodexUsageSnapshot };\n\nimport { stripAnsi } from \"../../../utils/ansi.ts\";\nimport { type ThemeColor, theme } from \"../theme/theme.ts\";\nimport { boxBottom, boxTextLine, boxTop, sidebarRule } from \"./control-panel-box.ts\";\nimport { classifyMcpStability } from \"./control-panel-runtime-status.ts\";\nimport { formatBytes, formatCwdForFooter, formatPackageIntake, formatTokens } from \"./footer.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\n\nexport const STATUS_SIDEBAR_WIDTH = 34;\nexport const STATUS_SIDEBAR_MAX_WIDTH = 48;\nexport const STATUS_SIDEBAR_MIN_WIDTH = 96;\n/** Blank columns between the main content and the pinned rail (part of the reserved gutter). */\nexport const STATUS_SIDEBAR_GUTTER_GAP = 1;\n\n/**\n * Responsive rail width: ~26% of the terminal, clamped to [34, 48] so the rail\n * grows on fullscreen terminals while the content column stays dominant.\n * The pinned overlay and the reserved gutter must both use this function.\n */\nexport function statusSidebarWidth(termWidth: number): number {\n\treturn Math.max(STATUS_SIDEBAR_WIDTH, Math.min(STATUS_SIDEBAR_MAX_WIDTH, Math.floor(termWidth * 0.26)));\n}\n\n/**\n * Responsive MCP roster rows: taller terminals list more servers before the\n * \"+N more\" collapse. ~24 rows of the rail are fixed chrome (header, model,\n * context, tokens, system), the rest is available to the roster.\n */\nexport function mcpMaxRows(termRows: number): number {\n\treturn Math.max(4, Math.min(18, termRows - 24));\n}\n\nconst METER_CELLS = 12;\nconst METER_MAX_CELLS = 24;\n/** Rolling window of CPU samples feeding the activity sparkline (wide rails show more history). */\nconst SPARK_WINDOW = 44;\n/** Minimum interval between sparkline samples so the rail animates, not jitters. */\nconst SPARK_SAMPLE_MS = 1000;\n/** MCP inventory is read from disk; cache it so per-frame renders stay cheap. */\nconst MCP_CACHE_TTL_MS = 5000;\nconst SPARK_CHARS = [\"▁\", \"▂\", \"▃\", \"▄\", \"▅\", \"▆\", \"▇\", \"█\"] as const;\n\ntype SubscriptionUsageFetcher = (\n\tsession: AgentSession,\n\tprovider?: string,\n) => Promise<SubscriptionUsageSnapshot | undefined>;\ntype CodexUsageFetcher = (session: AgentSession) => Promise<CodexUsageSnapshot | undefined>;\ntype StatusSidebarOptions = Readonly<{\n\trequestRender?: () => void;\n\tfetchSubscriptionUsage?: SubscriptionUsageFetcher;\n\t/** Compatibility for callers that injected the original Codex-only fetcher. */\n\tfetchCodexUsage?: CodexUsageFetcher;\n}>;\n\n/**\n * Fixed right-rail sidebar that shows the bottom-bar (footer) status vertically,\n * styled after opencode's pinned side rails plus a live MCP server roster.\n *\n * Pinned via the `pinStatusSidebar` setting (or OMK_PIN_STATUS_SIDEBAR=1) and\n * toggled with app.sidebar.toggle (Ctrl+Q). While pinned, the bottom footer is\n * removed so the same data is not rendered twice.\n */\nexport class StatusSidebarComponent implements Component {\n\tprivate getSession: () => AgentSession;\n\tprivate footerData: ReadonlyFooterDataProvider;\n\tprivate getAutoCompactEnabled: () => boolean;\n\tprivate getTerminalRows: () => number;\n\tprivate requestRender: () => void;\n\tprivate fetchSubscriptionUsage: SubscriptionUsageFetcher;\n\n\t// --- living elements: rolling CPU sparkline + cached MCP/usage data ---\n\tprivate cpuHistory: number[] = [];\n\tprivate lastSparkSampleAt = 0;\n\tprivate mcpCache: { at: number; cwd: string; entries: McpServerEntry[] } | undefined;\n\tprivate subscriptionUsage = new Map<string, SubscriptionUsageSnapshot>();\n\tprivate subscriptionUsageSession: AgentSession | undefined;\n\tprivate subscriptionUsageProviders: readonly string[] = [];\n\tprivate subscriptionUsageFetchedAt = new Map<string, number>();\n\tprivate subscriptionUsageInFlight = new Map<string, AgentSession>();\n\tprivate subscriptionUsageRevision = new Map<string, number>();\n\n\tconstructor(\n\t\tgetSession: () => AgentSession,\n\t\tfooterData: ReadonlyFooterDataProvider,\n\t\tgetAutoCompactEnabled: () => boolean,\n\t\tgetTerminalRows: () => number = () => 32,\n\t\toptions: StatusSidebarOptions = {},\n\t) {\n\t\tthis.getSession = getSession;\n\t\tthis.footerData = footerData;\n\t\tthis.getAutoCompactEnabled = getAutoCompactEnabled;\n\t\tthis.getTerminalRows = getTerminalRows;\n\t\tthis.requestRender = options.requestRender ?? (() => {});\n\t\tconst legacyFetcher = options.fetchCodexUsage;\n\t\tthis.fetchSubscriptionUsage =\n\t\t\toptions.fetchSubscriptionUsage ??\n\t\t\t(legacyFetcher\n\t\t\t\t? async (session, provider) =>\n\t\t\t\t\t\tgetSubscriptionUsageSource(provider)?.kind === \"codex\"\n\t\t\t\t\t\t\t? codexSubscriptionSnapshot(await legacyFetcher(session))\n\t\t\t\t\t\t\t: loadSubscriptionUsage(session, undefined, provider)\n\t\t\t\t: (session, provider) => loadSubscriptionUsage(session, undefined, provider));\n\t}\n\n\tinvalidate(): void {\n\t\t// Mostly stateless: status recomputes from the live session each render.\n\t\t// Only the sparkline history and MCP cache persist across frames.\n\t}\n\n\trender(width: number): string[] {\n\t\tconst session = this.getSession();\n\t\tconst state = session.state;\n\t\tthis.refreshSubscriptionUsage(session);\n\t\tconst lines: string[] = [boxTop(width, \"STATUS RAIL\")];\n\n\t\t// --- Live header: session uptime + CPU activity sparkline ---\n\t\tlines.push(\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"up \")}${theme.fg(\"accent\", formatUptime(process.uptime()))}`),\n\t\t);\n\t\tlines.push(boxTextLine(width, this.sparkline(width)));\n\n\t\t// --- Location ---\n\t\tconst cwd = formatCwdForFooter(session.sessionManager.getCwd(), process.env.HOME || process.env.USERPROFILE);\n\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"cwd \")}${theme.fg(\"text\", cwd)}`));\n\t\tconst branch = this.footerData.getGitBranch();\n\t\tif (branch) {\n\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"git \")}${theme.fg(\"accent\", branch)}`));\n\t\t}\n\t\tconst sessionName = session.sessionManager.getSessionName();\n\t\tif (sessionName) {\n\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"sess \")}${theme.fg(\"text\", sessionName)}`));\n\t\t}\n\n\t\t// --- Model ---\n\t\tlines.push(sidebarRule(width, \"MODEL\"));\n\t\tlines.push(\n\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"id \")}${theme.fg(\"accent\", state.model?.id ?? \"no-model\")}`),\n\t\t);\n\t\tif (state.model?.reasoning) {\n\t\t\tlines.push(\n\t\t\t\tboxTextLine(width, `${theme.fg(\"muted\", \"think \")}${theme.fg(\"mdCode\", state.thinkingLevel || \"off\")}`),\n\t\t\t);\n\t\t}\n\n\t\t// --- Active provider subscription quota ---\n\t\tconst usageLines = this.subscriptionUsageSection(width);\n\t\tlines.push(...usageLines);\n\n\t\t// --- Context ---\n\t\tlines.push(sidebarRule(width, \"CONTEXT\"));\n\t\tconst contextUsage = session.getContextUsage();\n\t\tconst contextWindow = contextUsage?.contextWindow ?? state.model?.contextWindow ?? 0;\n\t\tconst percent = contextUsage?.percent ?? null;\n\t\tconst auto = this.getAutoCompactEnabled() ? \" (auto)\" : \"\";\n\t\tconst percentLabel =\n\t\t\tpercent === null\n\t\t\t\t? `?/${formatTokens(contextWindow)}${auto}`\n\t\t\t\t: `${percent.toFixed(1)}%/${formatTokens(contextWindow)}${auto}`;\n\t\tconst percentColor: ThemeColor =\n\t\t\tpercent !== null && percent > 90 ? \"error\" : percent !== null && percent > 70 ? \"warning\" : \"success\";\n\t\tlines.push(boxTextLine(width, `${theme.fg(\"muted\", \"ctx \")}${theme.fg(percentColor, percentLabel)}`));\n\t\tlines.push(boxTextLine(width, meter(percent, width)));\n\n\t\t// --- Tokens (cumulative across all session entries, mirrors the footer) ---\n\t\tlet totalInput = 0;\n\t\tlet totalOutput = 0;\n\t\tlet totalCacheRead = 0;\n\t\tlet totalCacheWrite = 0;\n\t\tlet totalCost = 0;\n\t\tfor (const entry of session.sessionManager.getEntries()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"assistant\") {\n\t\t\t\ttotalInput += entry.message.usage.input;\n\t\t\t\ttotalOutput += entry.message.usage.output;\n\t\t\t\ttotalCacheRead += entry.message.usage.cacheRead;\n\t\t\t\ttotalCacheWrite += entry.message.usage.cacheWrite;\n\t\t\t\ttotalCost += entry.message.usage.cost.total;\n\t\t\t}\n\t\t}\n\t\tlines.push(sidebarRule(width, \"TOKENS\"));\n\t\tlines.push(\n\t\t\tboxTextLine(\n\t\t\t\twidth,\n\t\t\t\t`${theme.fg(\"success\", `↑${formatTokens(totalInput)}`)} ${theme.fg(\"accent\", `↓${formatTokens(totalOutput)}`)}`,\n\t\t\t),\n\t\t);\n\t\tlines.push(\n\t\t\tboxTextLine(\n\t\t\t\twidth,\n\t\t\t\t`${theme.fg(\"muted\", `R${formatTokens(totalCacheRead)}`)} ${theme.fg(\"muted\", `W${formatTokens(totalCacheWrite)}`)}`,\n\t\t\t),\n\t\t);\n\t\tconst usingSubscription = state.model ? session.modelRegistry.isUsingOAuth(state.model) : false;\n\t\tif (totalCost || usingSubscription) {\n\t\t\tlines.push(\n\t\t\t\tboxTextLine(\n\t\t\t\t\twidth,\n\t\t\t\t\t`${theme.fg(\"muted\", \"cost \")}${theme.fg(\"warning\", `$${totalCost.toFixed(3)}${usingSubscription ? \" (sub)\" : \"\"}`)}`,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\n\t\t// --- MCP roster (opencode-style server list with stability dots) ---\n\t\tlines.push(...this.mcpSection(width, session.sessionManager.getCwd(), usageLines.length));\n\n\t\t// --- System ---\n\t\tlines.push(sidebarRule(width, \"SYSTEM\"));\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tconst mem = this.footerData.getMemoryRssBytes();\n\t\tconst sysParts: string[] = [];\n\t\tif (cpu !== null) sysParts.push(`cpu ${cpu.toFixed(0)}%`);\n\t\tif (mem !== null) sysParts.push(`mem ${formatBytes(mem)}`);\n\t\tif (sysParts.length > 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"muted\", sysParts.join(\" \"))));\n\t\t}\n\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", formatPackageIntake(this.footerData.getPackageIntakeSummary()))));\n\n\t\t// --- Extension statuses ---\n\t\tconst statuses = this.footerData.getExtensionStatuses();\n\t\tif (statuses.size > 0) {\n\t\t\tlines.push(sidebarRule(width, \"EXT\"));\n\t\t\tfor (const [key, value] of statuses) {\n\t\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"mdCode\", key)} ${theme.fg(\"muted\", value)}`));\n\t\t\t}\n\t\t}\n\n\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", `${keyText(\"app.sidebar.toggle\")} unpin`)));\n\t\tlines.push(boxBottom(width));\n\t\treturn lines;\n\t}\n\n\tprivate refreshSubscriptionUsage(session: AgentSession): void {\n\t\tif (this.subscriptionUsageSession !== session) {\n\t\t\tthis.subscriptionUsage.clear();\n\t\t\tthis.subscriptionUsageFetchedAt.clear();\n\t\t\tthis.subscriptionUsageInFlight.clear();\n\t\t\tthis.subscriptionUsageRevision.clear();\n\t\t\tthis.subscriptionUsageSession = session;\n\t\t}\n\n\t\tconst providers = getConfiguredSubscriptionUsageProviders(session);\n\t\tthis.subscriptionUsageProviders = providers;\n\t\tconst configured = new Set(providers);\n\t\tfor (const provider of this.subscriptionUsage.keys()) {\n\t\t\tif (!configured.has(provider)) {\n\t\t\t\tthis.subscriptionUsage.delete(provider);\n\t\t\t\tthis.subscriptionUsageFetchedAt.delete(provider);\n\t\t\t\tthis.subscriptionUsageRevision.delete(provider);\n\t\t\t}\n\t\t}\n\n\t\tconst now = Date.now();\n\t\tfor (const provider of providers) {\n\t\t\tconst source = getSubscriptionUsageSource(provider);\n\t\t\tconst revision = getSubscriptionUsageRevision(provider);\n\t\t\tconst revisionChanged = revision !== (this.subscriptionUsageRevision.get(provider) ?? 0);\n\t\t\tif (\n\t\t\t\t!source ||\n\t\t\t\tthis.subscriptionUsageInFlight.has(provider) ||\n\t\t\t\t(!revisionChanged && now - (this.subscriptionUsageFetchedAt.get(provider) ?? 0) < source.ttlMs)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.subscriptionUsageFetchedAt.set(provider, now);\n\t\t\tthis.subscriptionUsageRevision.set(provider, revision);\n\t\t\tthis.subscriptionUsageInFlight.set(provider, session);\n\t\t\tvoid this.fetchSubscriptionUsage(session, provider)\n\t\t\t\t.then((snapshot) => {\n\t\t\t\t\tif (snapshot && this.getSession() === session && this.subscriptionUsageProviders.includes(provider)) {\n\t\t\t\t\t\tthis.subscriptionUsage.set(provider, snapshot);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.catch(() => {\n\t\t\t\t\tif (this.getSession() === session && this.subscriptionUsageProviders.includes(provider)) {\n\t\t\t\t\t\tthis.subscriptionUsage.set(provider, {\n\t\t\t\t\t\t\tlabel: source.label,\n\t\t\t\t\t\t\twindows: [],\n\t\t\t\t\t\t\tmessage: \"usage unavailable\",\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.finally(() => {\n\t\t\t\t\tif (this.subscriptionUsageInFlight.get(provider) === session) {\n\t\t\t\t\t\tthis.subscriptionUsageInFlight.delete(provider);\n\t\t\t\t\t}\n\t\t\t\t\tthis.requestRender();\n\t\t\t\t});\n\t\t}\n\t}\n\n\tprivate subscriptionUsageSection(width: number): string[] {\n\t\tif (this.subscriptionUsageProviders.length === 0) return [];\n\t\tconst lines = [railRule(width, \"USAGE\", theme.fg(\"accent\", String(this.subscriptionUsageProviders.length)))];\n\t\tfor (const provider of this.subscriptionUsageProviders) {\n\t\t\tconst source = getSubscriptionUsageSource(provider);\n\t\t\tconst snapshot = this.subscriptionUsage.get(provider);\n\t\t\tconst label = snapshot?.label ?? source?.label;\n\t\t\tif (!label) continue;\n\t\t\tif (!snapshot) {\n\t\t\t\tif (this.subscriptionUsageInFlight.has(provider)) {\n\t\t\t\t\tlines.push(boxTextLine(width, `${theme.fg(\"accent\", label)} ${theme.fg(\"dim\", \"loading…\")}`));\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (snapshot.windows.length === 0) {\n\t\t\t\tlines.push(boxTextLine(width, theme.fg(\"accent\", label)));\n\t\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", snapshot.message ?? \"usage unavailable\")));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"accent\", label)));\n\t\t\tfor (const window of snapshot.windows) {\n\t\t\t\tlines.push(boxTextLine(width, usageMeter(window.label, window, width)));\n\t\t\t}\n\t\t\tconst resets = snapshot.windows.flatMap((window) =>\n\t\t\t\twindow.resetsAt === undefined ? [] : [`${window.label} ${formatReset(window.resetsAt)}`],\n\t\t\t);\n\t\t\tif (resets.length > 0) lines.push(boxTextLine(width, theme.fg(\"dim\", `reset ${resets.join(\" · \")}`)));\n\t\t}\n\t\treturn lines.length === 1 ? [] : lines;\n\t}\n\n\t/**\n\t * MCP server roster with a stable/total counter in the section rule and a\n\t * stability dot per server. Reads are cached so the rail stays cheap to render.\n\t */\n\tprivate mcpSection(width: number, cwd: string, reservedRows = 0): string[] {\n\t\tconst entries = this.loadMcpEntries(cwd);\n\t\tconst stable = entries.filter((entry) => classifyMcpStability(entry) === \"stable\").length;\n\t\tconst countColor: ThemeColor = entries.length === 0 ? \"dim\" : stable === entries.length ? \"success\" : \"warning\";\n\t\tconst count = entries.length === 0 ? \"0\" : `${stable}/${entries.length}`;\n\n\t\tconst lines: string[] = [railRule(width, \"MCP\", theme.fg(countColor, count))];\n\t\tif (entries.length === 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", \"none configured\")));\n\t\t\treturn lines;\n\t\t}\n\n\t\tconst shown = entries.slice(0, mcpMaxRows(this.getTerminalRows() - reservedRows));\n\t\tfor (const entry of shown) {\n\t\t\tlines.push(boxTextLine(width, mcpRow(entry, width)));\n\t\t}\n\t\tconst hidden = entries.length - shown.length;\n\t\tif (hidden > 0) {\n\t\t\tlines.push(boxTextLine(width, theme.fg(\"dim\", `+${hidden} more…`)));\n\t\t}\n\t\treturn lines;\n\t}\n\n\tprivate loadMcpEntries(cwd: string): McpServerEntry[] {\n\t\tconst now = Date.now();\n\t\tif (this.mcpCache && this.mcpCache.cwd === cwd && now - this.mcpCache.at < MCP_CACHE_TTL_MS) {\n\t\t\treturn this.mcpCache.entries;\n\t\t}\n\t\tlet entries: McpServerEntry[] = [];\n\t\ttry {\n\t\t\tentries = loadMcpInventory(cwd).entries;\n\t\t} catch {\n\t\t\tentries = [];\n\t\t}\n\t\tthis.mcpCache = { at: now, cwd, entries };\n\t\treturn entries;\n\t}\n\n\t/** Rolling CPU sparkline — a living pulse along the top of the rail. */\n\tprivate sparkline(width: number): string {\n\t\tconst now = Date.now();\n\t\tconst cpu = this.footerData.getCpuPercent();\n\t\tif (cpu !== null && now - this.lastSparkSampleAt >= SPARK_SAMPLE_MS) {\n\t\t\tthis.lastSparkSampleAt = now;\n\t\t\tthis.cpuHistory.push(Math.max(0, Math.min(100, cpu)));\n\t\t\tif (this.cpuHistory.length > SPARK_WINDOW) {\n\t\t\t\tthis.cpuHistory.shift();\n\t\t\t}\n\t\t}\n\n\t\tconst inner = Math.max(0, width - 4);\n\t\tconst label = theme.fg(\"muted\", \"act \");\n\t\tconst cells = Math.max(0, inner - visibleWidth(label));\n\t\tif (cells === 0 || this.cpuHistory.length === 0) {\n\t\t\treturn `${label}${theme.fg(\"borderMuted\", \"▁\".repeat(Math.max(0, cells)))}`;\n\t\t}\n\n\t\t// Right-align the window so the sparkline grows in from the left edge.\n\t\tconst window = this.cpuHistory.slice(-cells);\n\t\tconst bars = window.map((sample) => SPARK_CHARS[Math.min(7, Math.floor(sample / 12.6))]).join(\"\");\n\t\tconst pad = \"▁\".repeat(Math.max(0, cells - window.length));\n\t\treturn `${label}${theme.fg(\"borderMuted\", pad)}${theme.fg(\"accent\", bars)}`;\n\t}\n}\n\n/** Section rule with a right-aligned counter, e.g. `│─ MCP ─────── 22/24 │`. */\nfunction railRule(width: number, label: string, right: string): string {\n\tconst left = `${theme.fg(\"borderMuted\", \"─\")}${theme.fg(\"accent\", ` ${label} `)}`;\n\tconst rightText = ` ${right} `;\n\tconst fill = Math.max(0, width - 2 - visibleWidth(left) - visibleWidth(rightText));\n\tconst line = `${theme.fg(\"borderMuted\", \"│\")}${left}${theme.fg(\"borderMuted\", \"─\".repeat(fill))}${rightText}${theme.fg(\"borderMuted\", \"│\")}`;\n\treturn visibleWidth(line) <= width ? line : truncateToWidth(line, width, \"\");\n}\n\n/** One MCP server row: stability dot + name, truncated to the rail width. */\nfunction mcpRow(entry: McpServerEntry, width: number): string {\n\tconst stability = classifyMcpStability(entry);\n\tconst dot =\n\t\tstability === \"stable\"\n\t\t\t? theme.fg(\"success\", \"●\")\n\t\t\t: stability === \"overridden\"\n\t\t\t\t? theme.fg(\"dim\", \"◐\")\n\t\t\t\t: theme.fg(\"warning\", \"○\");\n\tconst nameColor: ThemeColor = stability === \"stable\" ? \"text\" : stability === \"overridden\" ? \"dim\" : \"warning\";\n\tconst nameWidth = Math.max(1, width - 4 - 2); // frame (4) + dot + space (2)\n\tconst safeName = stripAnsi(entry.name)\n\t\t.replace(/[\\t\\r\\n]+/g, \" \")\n\t\t.replace(/[\\u0000-\\u0008\\u000b\\u000c\\u000e-\\u001f\\u007f-\\u009f\\u200b-\\u200f\\u202a-\\u202e\\u2060-\\u206f]/g, \"\")\n\t\t.replace(/\\s+/g, \" \")\n\t\t.trim();\n\tconst name = truncateToWidth(safeName || \"<unnamed>\", nameWidth, \"…\");\n\treturn `${dot} ${theme.fg(nameColor, name)}`;\n}\n\nfunction formatUptime(seconds: number): string {\n\tconst total = Math.max(0, Math.floor(seconds));\n\tconst h = Math.floor(total / 3600);\n\tconst m = Math.floor((total % 3600) / 60);\n\tconst s = total % 60;\n\tconst pad = (n: number) => n.toString().padStart(2, \"0\");\n\treturn h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${pad(m)}:${pad(s)}`;\n}\n\nfunction usageMeter(label: string, window: SubscriptionUsageWindow, width: number): string {\n\tconst percent = Math.max(0, Math.min(100, window.usedPercent));\n\tconst percentText = `${Math.round(percent)}%`;\n\tconst labelText = `${label} `;\n\tconst innerWidth = Math.max(0, width - 4);\n\tconst cells = Math.max(4, Math.min(METER_MAX_CELLS, innerWidth - labelText.length - percentText.length - 1));\n\tconst filled = Math.round((percent / 100) * cells);\n\tconst color = usageColor(percent);\n\treturn `${theme.fg(\"muted\", labelText)}${theme.fg(color, \"█\".repeat(filled))}${theme.fg(\"borderMuted\", \"░\".repeat(cells - filled))} ${theme.fg(color, percentText)}`;\n}\n\nfunction usageColor(percent: number): ThemeColor {\n\tif (percent >= 90) return \"error\";\n\tif (percent >= 75) return \"warning\";\n\treturn \"success\";\n}\n\nfunction codexSubscriptionSnapshot(snapshot: CodexUsageSnapshot | undefined): SubscriptionUsageSnapshot | undefined {\n\tif (!snapshot) return undefined;\n\treturn {\n\t\tlabel: \"CODEX\",\n\t\twindows: [\n\t\t\tsnapshot.fiveHour ? { label: \"5H\", ...snapshot.fiveHour } : undefined,\n\t\t\tsnapshot.sevenDay ? { label: \"7D\", ...snapshot.sevenDay } : undefined,\n\t\t].filter((window): window is SubscriptionUsageWindow => window !== undefined),\n\t};\n}\n\nfunction formatReset(resetsAt: number): string {\n\tconst seconds = Math.max(0, Math.ceil(resetsAt - Date.now() / 1000));\n\tif (seconds < 60) return \"now\";\n\tconst totalMinutes = Math.ceil(seconds / 60);\n\tif (totalMinutes < 60) return `${totalMinutes}m`;\n\tif (totalMinutes < 24 * 60) {\n\t\tconst hours = Math.floor(totalMinutes / 60);\n\t\tconst minutes = totalMinutes % 60;\n\t\treturn `${hours}h${minutes > 0 ? `${minutes}m` : \"\"}`;\n\t}\n\tconst totalHours = Math.ceil(totalMinutes / 60);\n\tconst days = Math.floor(totalHours / 24);\n\tconst hours = totalHours % 24;\n\treturn `${days}d${hours > 0 ? `${hours}h` : \"\"}`;\n}\n\nfunction meter(percent: number | null, width: number): string {\n\t// Fill the rail: frame (4) + space + up to \"100%\" (4) stay reserved.\n\tconst cells = Math.max(METER_CELLS, Math.min(METER_MAX_CELLS, width - 9));\n\tif (percent === null) {\n\t\treturn `${theme.fg(\"borderMuted\", \"░\".repeat(cells))} ${theme.fg(\"muted\", \"??%\")}`;\n\t}\n\tconst clamped = Math.max(0, Math.min(100, percent));\n\tconst filled = Math.round((clamped / 100) * cells);\n\tconst color: ThemeColor = clamped >= 85 ? \"warning\" : clamped >= 65 ? \"mdCode\" : \"success\";\n\treturn `${theme.fg(color, \"█\".repeat(filled))}${theme.fg(\"borderMuted\", \"░\".repeat(cells - filled))} ${theme.fg(color, `${Math.round(clamped)}%`)}`;\n}\n"]}
|
|
@@ -362,6 +362,7 @@ export declare class InteractiveMode {
|
|
|
362
362
|
private getLogoutProviderOptions;
|
|
363
363
|
private showLoginAuthTypeSelector;
|
|
364
364
|
private showLoginProviderSelector;
|
|
365
|
+
private showOAuthAccountSelector;
|
|
365
366
|
private showOAuthSelector;
|
|
366
367
|
private completeProviderAuthentication;
|
|
367
368
|
private showBedrockSetupDialog;
|
|
@@ -376,6 +377,7 @@ export declare class InteractiveMode {
|
|
|
376
377
|
private handleCopyCommand;
|
|
377
378
|
private handleNameCommand;
|
|
378
379
|
private handleSessionCommand;
|
|
380
|
+
private handleStarCommand;
|
|
379
381
|
private handleChangelogCommand;
|
|
380
382
|
/**
|
|
381
383
|
* Get capitalized display string for an app keybinding action.
|