@xingular/harness 1.1.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +28 -25
- package/dist/cli.js +6675 -2501
- package/dist/sdk/accounts/accounts.d.ts +27 -3
- package/dist/sdk/accounts/accounts.js +78 -24
- package/dist/sdk/accounts/secrets.d.ts +12 -0
- package/dist/sdk/accounts/secrets.js +114 -7
- package/dist/sdk/accounts/store.d.ts +26 -1
- package/dist/sdk/accounts/store.js +1 -1
- package/dist/sdk/agents/builtin.js +15 -1
- package/dist/sdk/agents/planResearch.d.ts +5 -0
- package/dist/sdk/agents/planResearch.js +19 -0
- package/dist/sdk/agents/profiles.d.ts +2 -1
- package/dist/sdk/agents/profiles.js +9 -4
- package/dist/sdk/annotations/pageScript.js +349 -48
- package/dist/sdk/annotations/part.js +34 -23
- package/dist/sdk/annotations/store.d.ts +7 -0
- package/dist/sdk/annotations/store.js +13 -5
- package/dist/sdk/browser/backend.d.ts +16 -0
- package/dist/sdk/browser/backend.js +17 -0
- package/dist/sdk/browser/contracts.d.ts +19 -0
- package/dist/sdk/browser/contracts.js +116 -0
- package/dist/sdk/browser/tools.d.ts +1 -4
- package/dist/sdk/browser/tools.js +14 -91
- package/dist/sdk/cli.js +5 -4
- package/dist/sdk/commands/list.d.ts +3 -0
- package/dist/sdk/commands/list.js +75 -0
- package/dist/sdk/commands/registry.d.ts +250 -0
- package/dist/sdk/commands/registry.js +47 -0
- package/dist/sdk/commands/routes.d.ts +16 -0
- package/dist/sdk/commands/routes.js +33 -0
- package/dist/sdk/core/commandBudget.d.ts +23 -0
- package/dist/sdk/core/commandBudget.js +57 -0
- package/dist/sdk/core/index.js +17 -1
- package/dist/sdk/core/interactiveProfile.d.ts +46 -0
- package/dist/sdk/core/interactiveProfile.js +143 -0
- package/dist/sdk/core/workspace.d.ts +13 -0
- package/dist/sdk/core/workspace.js +88 -0
- package/dist/sdk/core/workspaceBashTool.d.ts +4 -0
- package/dist/sdk/core/workspaceBashTool.js +36 -0
- package/dist/sdk/core/workspaceSandbox.d.ts +31 -0
- package/dist/sdk/core/workspaceSandbox.js +320 -0
- package/dist/sdk/core/workspaceWriteOperations.d.ts +22 -0
- package/dist/sdk/core/workspaceWriteOperations.js +206 -0
- package/dist/sdk/doctor/types.d.ts +2 -2
- package/dist/sdk/harness.d.ts +7 -0
- package/dist/sdk/harness.js +42 -2
- package/dist/sdk/index.d.ts +6 -1
- package/dist/sdk/index.js +3 -0
- package/dist/sdk/pi/runtime.d.ts +2 -1
- package/dist/sdk/pi/runtime.js +2 -2
- package/dist/sdk/plugins/builtin.js +29 -12
- package/dist/sdk/plugins/builtinRegistry.d.ts +10 -3
- package/dist/sdk/plugins/builtinRegistry.js +30 -8
- package/dist/sdk/plugins/catalogue.js +2 -1
- package/dist/sdk/plugins/commandPrefix.d.ts +8 -0
- package/dist/sdk/plugins/commandPrefix.js +21 -0
- package/dist/sdk/plugins/firstParty.d.ts +1 -1
- package/dist/sdk/plugins/firstParty.js +8 -2
- package/dist/sdk/plugins/inventory.js +4 -3
- package/dist/sdk/plugins/manifest.d.ts +2 -0
- package/dist/sdk/plugins/manifest.js +2 -1
- package/dist/sdk/plugins/surfaces.d.ts +5 -0
- package/dist/sdk/plugins/surfaces.js +11 -0
- package/dist/sdk/plugins/types.d.ts +15 -0
- package/dist/sdk/prompt/compile.d.ts +14 -0
- package/dist/sdk/prompt/compile.js +65 -29
- package/dist/sdk/prompt/standard.d.ts +17 -0
- package/dist/sdk/prompt/standard.js +21 -0
- package/dist/sdk/server/methods.js +18 -0
- package/dist/sdk/server/remote.js +36 -11
- package/dist/sdk/sessions/compactionCheckpoint.d.ts +11 -0
- package/dist/sdk/sessions/compactionCheckpoint.js +79 -0
- package/dist/sdk/sessions/continuation.d.ts +3 -0
- package/dist/sdk/sessions/continuation.js +5 -1
- package/dist/sdk/sessions/contracts.d.ts +168 -0
- package/dist/sdk/sessions/contracts.js +59 -1
- package/dist/sdk/sessions/control.d.ts +72 -0
- package/dist/sdk/sessions/control.js +158 -0
- package/dist/sdk/sessions/initialBash.d.ts +7 -0
- package/dist/sdk/sessions/initialBash.js +7 -0
- package/dist/sdk/sessions/planMode.d.ts +14 -3
- package/dist/sdk/sessions/planMode.js +73 -16
- package/dist/sdk/sessions/registry.d.ts +1 -1
- package/dist/sdk/sessions/registry.js +2 -2
- package/dist/sdk/sessions/runStore.d.ts +14 -0
- package/dist/sdk/sessions/runStore.js +35 -0
- package/dist/sdk/sessions/runner.d.ts +40 -2
- package/dist/sdk/sessions/runner.js +161 -13
- package/dist/sdk/sessions/sessions.d.ts +23 -3
- package/dist/sdk/sessions/sessions.js +202 -17
- package/dist/sdk/sessions/subagentService.d.ts +9 -0
- package/dist/sdk/sessions/subagentService.js +63 -4
- package/dist/sdk/sessions/workSlice.d.ts +103 -0
- package/dist/sdk/sessions/workSlice.js +297 -0
- package/dist/sdk/sessions/workspaceBoundary.d.ts +15 -0
- package/dist/sdk/sessions/workspaceBoundary.js +46 -0
- package/dist/sdk/terminal/browserControl.js +2 -1
- package/dist/sdk/terminal/controller.d.ts +11 -3
- package/dist/sdk/terminal/controller.js +40 -13
- package/dist/sdk/terminal/opentui/theme.d.ts +43 -0
- package/dist/sdk/terminal/opentui/theme.js +87 -0
- package/dist/sdk/terminal/piExtension.d.ts +6 -1
- package/dist/sdk/terminal/piExtension.js +154 -54
- package/dist/sdk/terminal/standalone.d.ts +7 -0
- package/dist/sdk/terminal/standalone.js +72 -13
- package/dist/sdk/terminal/subagentMonitor.d.ts +11 -2
- package/dist/sdk/terminal/subagentMonitor.js +32 -1
- package/dist/sdk/terminal/terminals.d.ts +19 -2
- package/dist/sdk/terminal/terminals.js +245 -35
- package/dist/sdk/terminal/tui/annotateCommand.d.ts +1 -1
- package/dist/sdk/terminal/tui/annotateCommand.js +3 -3
- package/dist/sdk/terminal/tui/commandPages.d.ts +3 -1
- package/dist/sdk/terminal/tui/commandPages.js +3 -0
- package/dist/sdk/terminal/tui/commands.d.ts +2 -0
- package/dist/sdk/terminal/tui/commands.js +205 -124
- package/dist/sdk/terminal/tui/list.d.ts +14 -1
- package/dist/sdk/terminal/tui/list.js +96 -14
- package/dist/sdk/terminal/tui/pageOverlay.js +13 -4
- package/dist/sdk/terminal/tui/pages/annotatePage.d.ts +1 -1
- package/dist/sdk/terminal/tui/pages/notesPage.d.ts +5 -5
- package/dist/sdk/terminal/tui/pages/notesPage.js +18 -18
- package/dist/sdk/terminal/tui/pages/pluginsPage.d.ts +0 -5
- package/dist/sdk/terminal/tui/pages/pluginsPage.js +9 -3
- package/dist/sdk/terminal/tui/pages/subagentsPage.d.ts +1 -0
- package/dist/sdk/terminal/tui/pages/subagentsPage.js +98 -16
- package/dist/sdk/terminal/tui/pages/usagePage.d.ts +43 -0
- package/dist/sdk/terminal/tui/pages/usagePage.js +370 -0
- package/dist/sdk/terminal/tui/piAdapter.d.ts +1 -2
- package/dist/sdk/terminal/tui/piAdapter.js +3 -5
- package/dist/sdk/terminal/tui/pluginCommands.d.ts +2 -0
- package/dist/sdk/terminal/tui/pluginCommands.js +13 -9
- package/dist/sdk/terminal/tui/runtime.js +23 -3
- package/dist/sdk/terminal/tui/sessionPicker.d.ts +40 -0
- package/dist/sdk/terminal/tui/sessionPicker.js +141 -0
- package/dist/sdk/tools/checklist.js +1 -0
- package/dist/sdk/tools/checkpoint.d.ts +27 -0
- package/dist/sdk/tools/checkpoint.js +154 -0
- package/dist/sdk/tools/getCapabilities.d.ts +31 -0
- package/dist/sdk/tools/getCapabilities.js +34 -0
- package/dist/sdk/tools/spawnSubagent.js +4 -3
- package/dist/sdk/usage/analytics.d.ts +20 -0
- package/dist/sdk/usage/analytics.js +151 -0
- package/dist/sdk/usage/ledger.d.ts +38 -0
- package/dist/sdk/usage/ledger.js +142 -0
- package/dist/sdk/usage/limits/adapters.d.ts +11 -0
- package/dist/sdk/usage/limits/adapters.js +349 -0
- package/dist/sdk/usage/limits/capture.d.ts +8 -0
- package/dist/sdk/usage/limits/capture.js +47 -0
- package/dist/sdk/usage/limits/http.d.ts +12 -0
- package/dist/sdk/usage/limits/http.js +51 -0
- package/dist/sdk/usage/limits/normalize.d.ts +15 -0
- package/dist/sdk/usage/limits/normalize.js +211 -0
- package/dist/sdk/usage/limits/service.d.ts +30 -0
- package/dist/sdk/usage/limits/service.js +176 -0
- package/dist/sdk/usage/limits/types.d.ts +65 -0
- package/dist/sdk/usage/limits/types.js +1 -0
- package/dist/sdk/usage/serverMethods.d.ts +3 -0
- package/dist/sdk/usage/serverMethods.js +22 -0
- package/dist/sdk/usage/service.d.ts +32 -0
- package/dist/sdk/usage/service.js +66 -0
- package/dist/sdk/usage/settings.d.ts +7 -0
- package/dist/sdk/usage/settings.js +28 -0
- package/dist/sdk/usage/types.d.ts +60 -0
- package/dist/sdk/usage/types.js +4 -0
- package/dist/terminal-opentui.js +5207 -1883
- package/dist/terminal-pi.js +1658 -271
- package/docs/architecture/02-sessions-and-runs.md +128 -26
- package/docs/architecture/04-prompt.md +50 -12
- package/docs/architecture/06-plugins-and-capabilities.md +24 -7
- package/docs/architecture/07-tools-and-gate.md +104 -24
- package/docs/architecture/08-memory-and-history.md +4 -2
- package/docs/architecture/09-accounts-and-connectors.md +31 -1
- package/docs/architecture/10-server-protocol.md +58 -3
- package/docs/architecture/11-terminal.md +105 -42
- package/docs/architecture/12-storage.md +30 -2
- package/docs/architecture/13-world-tools.md +22 -1
- package/docs/architecture/14-surfaces-and-parity.md +54 -7
- package/docs/architecture/README.md +3 -1
- package/docs/architecture/proposals/PROP-0017-page-annotations.md +6 -2
- package/docs/architecture/proposals/PROP-0018-annotation-queue-and-app-targets.md +1 -1
- package/docs/architecture/proposals/PROP-0031-plugin-command-owner-namespaces.md +54 -0
- package/docs/architecture/proposals/PROP-0032-planning-quality.md +196 -0
- package/docs/architecture/proposals/PROP-0033-compaction-plan-checkpoints.md +75 -0
- package/docs/architecture/proposals/PROP-0034-conversation-fast-mode.md +120 -0
- package/docs/architecture/proposals/PROP-0035-retain-subagent-status.md +47 -0
- package/docs/architecture/proposals/PROP-0036-first-class-harness-commands.md +59 -0
- package/docs/architecture/proposals/PROP-0037-workspace-write-boundary.md +63 -0
- package/docs/architecture/proposals/PROP-0038-annotate-open-shadow-roots.md +73 -0
- package/docs/architecture/proposals/PROP-0039-workspace-boundary-hardening.md +139 -0
- package/docs/architecture/proposals/PROP-0040-one-launch-path.md +87 -0
- package/docs/architecture/proposals/PROP-0041-bounded-work-slices.md +247 -0
- package/docs/architecture/proposals/PROP-0042-usage-and-provider-limits.md +127 -0
- package/docs/architecture/proposals/PROP-0043-read-only-plan-bash.md +74 -0
- package/docs/architecture/proposals/README.md +14 -0
- package/docs/decisions/ADR-0001-owned-terminal.md +1 -1
- package/docs/decisions/ADR-0021-required-command-namespaces.md +1 -1
- package/docs/decisions/ADR-0031-plugin-command-owner-namespaces.md +37 -0
- package/docs/decisions/ADR-0032-compaction-run-checkpoints.md +33 -0
- package/docs/decisions/ADR-0033-plan-mode-research-delegation.md +47 -0
- package/docs/decisions/ADR-0034-first-class-harness-terminal-commands.md +25 -0
- package/docs/decisions/ADR-0035-one-launch-path.md +40 -0
- package/docs/decisions/ADR-0036-workspace-write-operation-boundary.md +54 -0
- package/docs/decisions/ADR-0037-usage-ledger-and-provider-limits.md +44 -0
- package/docs/decisions/ADR-0038-plan-bash-os-read-only.md +29 -0
- package/docs/experiments/EXP-0003-performance-audit/data/session-runtime-replication.json +420 -0
- package/docs/experiments/EXP-0003-performance-audit/data/session-runtime.json +420 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-compact.json +376 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-history.json +422 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-question.json +95 -0
- package/docs/experiments/EXP-0003-performance-audit/data/terminal-wide.json +1036 -0
- package/docs/experiments/EXP-0003-performance-audit/plan.md +109 -0
- package/docs/experiments/EXP-0003-performance-audit/results.md +151 -0
- package/docs/handoff-2026-10-25-app-parity-and-solo-tabs.md +202 -0
- package/docs/parity.md +80 -24
- package/docs/releases/1.1.1.md +28 -0
- package/docs/releases/1.2.0.md +49 -0
- package/docs/releases/4.0.0.md +10 -9
- package/docs/releases/README.md +35 -30
- package/docs/study/00-index.md +3 -0
- package/docs/study/STUDY-0003-long-horizon-runs.md +210 -0
- package/docs/terminal-tui.md +100 -59
- package/package.json +4 -3
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
import { matchesKey, stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
|
|
2
|
+
const PERIODS = [
|
|
3
|
+
{ key: '1', period: 'today', label: 'today' },
|
|
4
|
+
{ key: '2', period: '7d', label: '7d' },
|
|
5
|
+
{ key: '3', period: '30d', label: '30d' },
|
|
6
|
+
{ key: '0', period: 'all', label: 'all time' }
|
|
7
|
+
];
|
|
8
|
+
const SCOPES = [
|
|
9
|
+
{ key: 'c', scope: 'account', label: 'account' },
|
|
10
|
+
{ key: 's', scope: 'session', label: 'session' },
|
|
11
|
+
{ key: 'p', scope: 'project', label: 'project' },
|
|
12
|
+
{ key: 'a', scope: 'all', label: 'all accounts' }
|
|
13
|
+
];
|
|
14
|
+
const BARS = '▁▂▃▄▅▆▇█';
|
|
15
|
+
function clean(value) {
|
|
16
|
+
return stripTerminalSequences(value).replace(/[\x00-\x08\x0b-\x1f\x7f-\x9f]/g, ' ').replace(/\t/g, ' ');
|
|
17
|
+
}
|
|
18
|
+
function oneLine(value) {
|
|
19
|
+
return clean(value).replace(/\s+/g, ' ').trim();
|
|
20
|
+
}
|
|
21
|
+
function count(value) {
|
|
22
|
+
if (!Number.isFinite(value))
|
|
23
|
+
return '0';
|
|
24
|
+
if (value >= 1_000_000_000)
|
|
25
|
+
return `${(value / 1_000_000_000).toFixed(1)}B`;
|
|
26
|
+
if (value >= 1_000_000)
|
|
27
|
+
return `${(value / 1_000_000).toFixed(1)}M`;
|
|
28
|
+
if (value >= 1_000)
|
|
29
|
+
return `${(value / 1_000).toFixed(1)}K`;
|
|
30
|
+
return String(Math.round(value));
|
|
31
|
+
}
|
|
32
|
+
function money(value) {
|
|
33
|
+
if (value === null)
|
|
34
|
+
return '—';
|
|
35
|
+
return value >= 1 ? `$${value.toFixed(2)}` : `$${value.toFixed(3)}`;
|
|
36
|
+
}
|
|
37
|
+
function duration(milliseconds) {
|
|
38
|
+
if (milliseconds === null)
|
|
39
|
+
return '—';
|
|
40
|
+
if (milliseconds < 1_000)
|
|
41
|
+
return `${Math.round(milliseconds)}ms`;
|
|
42
|
+
return `${(milliseconds / 1_000).toFixed(1)}s`;
|
|
43
|
+
}
|
|
44
|
+
function until(iso, now = Date.now()) {
|
|
45
|
+
if (!iso)
|
|
46
|
+
return '';
|
|
47
|
+
const at = Date.parse(iso);
|
|
48
|
+
if (Number.isNaN(at))
|
|
49
|
+
return '';
|
|
50
|
+
const delta = at - now;
|
|
51
|
+
if (delta <= 0)
|
|
52
|
+
return 'reset due';
|
|
53
|
+
const minutes = Math.round(delta / 60_000);
|
|
54
|
+
if (minutes < 60)
|
|
55
|
+
return `resets in ${minutes}m`;
|
|
56
|
+
const hours = Math.floor(minutes / 60);
|
|
57
|
+
if (hours < 48)
|
|
58
|
+
return `resets in ${hours}h ${minutes % 60}m`;
|
|
59
|
+
return `resets in ${Math.floor(hours / 24)}d`;
|
|
60
|
+
}
|
|
61
|
+
function ago(iso) {
|
|
62
|
+
const at = Date.parse(iso);
|
|
63
|
+
if (Number.isNaN(at))
|
|
64
|
+
return 'unknown time';
|
|
65
|
+
const minutes = Math.max(0, Math.round((Date.now() - at) / 60_000));
|
|
66
|
+
if (minutes < 1)
|
|
67
|
+
return 'just now';
|
|
68
|
+
if (minutes < 60)
|
|
69
|
+
return `${minutes}m ago`;
|
|
70
|
+
const hours = Math.round(minutes / 60);
|
|
71
|
+
return hours < 48 ? `${hours}h ago` : `${Math.round(hours / 24)}d ago`;
|
|
72
|
+
}
|
|
73
|
+
function usageLine(totals) {
|
|
74
|
+
const parts = [
|
|
75
|
+
`${count(totals.inputTokens)} in`,
|
|
76
|
+
`${count(totals.cacheReadTokens)} cached`,
|
|
77
|
+
`${count(totals.outputTokens)} out`,
|
|
78
|
+
`${count(totals.requests)} requests`,
|
|
79
|
+
money(totals.costUsd)
|
|
80
|
+
];
|
|
81
|
+
if (totals.unpricedRequests)
|
|
82
|
+
parts.push(`${totals.unpricedRequests} unpriced`);
|
|
83
|
+
return parts.join(' · ');
|
|
84
|
+
}
|
|
85
|
+
function share(value, total) {
|
|
86
|
+
if (!total)
|
|
87
|
+
return '0%';
|
|
88
|
+
return `${Math.round((value / total) * 100)}%`;
|
|
89
|
+
}
|
|
90
|
+
function tokensOf(row) {
|
|
91
|
+
return row.inputTokens + row.cacheReadTokens + row.cacheWriteTokens + row.outputTokens;
|
|
92
|
+
}
|
|
93
|
+
/** A quota-left bar: green with room, amber when it narrows, red when it runs out. */
|
|
94
|
+
function barTone(percent, rejected = false) {
|
|
95
|
+
if (rejected)
|
|
96
|
+
return 'error';
|
|
97
|
+
if (percent === null || percent === undefined)
|
|
98
|
+
return 'dim';
|
|
99
|
+
if (percent >= 50)
|
|
100
|
+
return 'success';
|
|
101
|
+
if (percent >= 20)
|
|
102
|
+
return 'warning';
|
|
103
|
+
return 'error';
|
|
104
|
+
}
|
|
105
|
+
function bar(percent, width, theme, rejected = false) {
|
|
106
|
+
const track = '░'.repeat(width);
|
|
107
|
+
if (percent === null || percent === undefined)
|
|
108
|
+
return `[${theme.fg('dim', track)}]`;
|
|
109
|
+
const filled = Math.max(0, Math.min(width, Math.round((Math.max(0, Math.min(100, percent)) / 100) * width)));
|
|
110
|
+
return `[${theme.fg(barTone(percent, rejected), '█'.repeat(filled))}${theme.fg('dim', '░'.repeat(Math.max(0, width - filled)))}]`;
|
|
111
|
+
}
|
|
112
|
+
/** A label on the left and a quiet value on the right of the same row. */
|
|
113
|
+
function spread(left, right, width) {
|
|
114
|
+
if (!right)
|
|
115
|
+
return left;
|
|
116
|
+
const rightText = truncateToWidth(right, Math.max(0, width - visibleWidth(left) - 2), '');
|
|
117
|
+
return left + ' '.repeat(Math.max(1, width - visibleWidth(left) - visibleWidth(rightText))) + rightText;
|
|
118
|
+
}
|
|
119
|
+
export class UsagePage {
|
|
120
|
+
options;
|
|
121
|
+
presentation = 'centered';
|
|
122
|
+
focused = false;
|
|
123
|
+
tab;
|
|
124
|
+
period = '7d';
|
|
125
|
+
scope = 'account';
|
|
126
|
+
summary;
|
|
127
|
+
limitsSummary;
|
|
128
|
+
limits;
|
|
129
|
+
busy = false;
|
|
130
|
+
error;
|
|
131
|
+
refreshLimits;
|
|
132
|
+
offset = 0;
|
|
133
|
+
scrollRows = 1;
|
|
134
|
+
maxOffset = 0;
|
|
135
|
+
disposed = false;
|
|
136
|
+
constructor(options) {
|
|
137
|
+
this.options = options;
|
|
138
|
+
this.tab = options.initialTab ?? 'analysis';
|
|
139
|
+
this.period = options.initialPeriod ?? '7d';
|
|
140
|
+
this.refreshLimits = this.tab === 'limits';
|
|
141
|
+
}
|
|
142
|
+
tabs() {
|
|
143
|
+
return [{ id: 'analysis', title: 'Analysis' }, { id: 'limits', title: 'Limits' }];
|
|
144
|
+
}
|
|
145
|
+
activeTab() {
|
|
146
|
+
return this.tab;
|
|
147
|
+
}
|
|
148
|
+
selectTab(id) {
|
|
149
|
+
if ((id === 'analysis' || id === 'limits') && id !== this.tab) {
|
|
150
|
+
this.tab = id;
|
|
151
|
+
this.offset = 0;
|
|
152
|
+
this.options.changed();
|
|
153
|
+
if (id === 'limits' && !this.limits) {
|
|
154
|
+
this.refreshLimits = true;
|
|
155
|
+
void this.load();
|
|
156
|
+
}
|
|
157
|
+
else if (id === 'analysis' && !this.summary)
|
|
158
|
+
void this.load();
|
|
159
|
+
else if (id === 'limits' && this.refreshLimits)
|
|
160
|
+
void this.load();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
hint() {
|
|
164
|
+
return this.tab === 'analysis'
|
|
165
|
+
? '↑/↓ scroll · PgUp/PgDn page · 1 today 2 7d 3 30d 0 all · c account s session p project a all accounts · r refresh · Tab limits · Esc close'
|
|
166
|
+
: '↑/↓ scroll · PgUp/PgDn page · r read provider limits · Tab analysis · Esc close';
|
|
167
|
+
}
|
|
168
|
+
activate() {
|
|
169
|
+
if (!this.summary && !this.limits)
|
|
170
|
+
void this.load();
|
|
171
|
+
}
|
|
172
|
+
dispose() {
|
|
173
|
+
this.disposed = true;
|
|
174
|
+
}
|
|
175
|
+
async load() {
|
|
176
|
+
if (this.busy || this.disposed)
|
|
177
|
+
return;
|
|
178
|
+
this.busy = true;
|
|
179
|
+
this.error = undefined;
|
|
180
|
+
this.options.changed();
|
|
181
|
+
try {
|
|
182
|
+
if (this.tab === 'analysis') {
|
|
183
|
+
this.summary = await this.options.call('terminal.usage.summary', {
|
|
184
|
+
scope: this.scope,
|
|
185
|
+
period: this.period,
|
|
186
|
+
timezoneOffsetMinutes: -new Date().getTimezoneOffset()
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
// The provider's windows and this app's own ledger answer different questions; show both.
|
|
191
|
+
const [limits, summary] = await Promise.all([
|
|
192
|
+
this.options.call('terminal.usage.limits', { refresh: this.refreshLimits }),
|
|
193
|
+
this.options.call('terminal.usage.summary', {
|
|
194
|
+
scope: 'account',
|
|
195
|
+
period: '7d',
|
|
196
|
+
timezoneOffsetMinutes: -new Date().getTimezoneOffset()
|
|
197
|
+
})
|
|
198
|
+
]);
|
|
199
|
+
this.limits = limits;
|
|
200
|
+
this.limitsSummary = summary;
|
|
201
|
+
this.refreshLimits = false;
|
|
202
|
+
}
|
|
203
|
+
this.offset = 0;
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
this.error = error instanceof Error ? error.message : 'Usage could not be read.';
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
this.busy = false;
|
|
210
|
+
if (!this.disposed)
|
|
211
|
+
this.options.changed();
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
handleInput(data) {
|
|
215
|
+
const page = Math.max(1, this.scrollRows - 1);
|
|
216
|
+
if (matchesKey(data, 'up'))
|
|
217
|
+
this.offset = Math.max(0, this.offset - 1);
|
|
218
|
+
else if (matchesKey(data, 'down'))
|
|
219
|
+
this.offset = Math.min(this.maxOffset, this.offset + 1);
|
|
220
|
+
else if (matchesKey(data, 'pageUp'))
|
|
221
|
+
this.offset = Math.max(0, this.offset - page);
|
|
222
|
+
else if (matchesKey(data, 'pageDown'))
|
|
223
|
+
this.offset = Math.min(this.maxOffset, this.offset + page);
|
|
224
|
+
else if (matchesKey(data, 'home'))
|
|
225
|
+
this.offset = 0;
|
|
226
|
+
else if (matchesKey(data, 'end'))
|
|
227
|
+
this.offset = this.maxOffset;
|
|
228
|
+
else if (data === 'r') {
|
|
229
|
+
if (this.tab === 'limits')
|
|
230
|
+
this.refreshLimits = true;
|
|
231
|
+
void this.load();
|
|
232
|
+
}
|
|
233
|
+
else if (this.tab === 'analysis' && PERIODS.some((entry) => entry.key === data)) {
|
|
234
|
+
this.period = PERIODS.find((entry) => entry.key === data).period;
|
|
235
|
+
void this.load();
|
|
236
|
+
}
|
|
237
|
+
else if (this.tab === 'analysis' && SCOPES.some((entry) => entry.key === data)) {
|
|
238
|
+
this.scope = SCOPES.find((entry) => entry.key === data).scope;
|
|
239
|
+
void this.load();
|
|
240
|
+
}
|
|
241
|
+
else
|
|
242
|
+
return false;
|
|
243
|
+
this.options.changed();
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
analysisContent(width, theme) {
|
|
247
|
+
const summary = this.summary;
|
|
248
|
+
const header = [];
|
|
249
|
+
if (!summary) {
|
|
250
|
+
header.push(theme.fg(this.error ? 'error' : 'dim', this.error ?? (this.busy ? 'Reading usage…' : 'No usage to show.')));
|
|
251
|
+
return { header, body: [] };
|
|
252
|
+
}
|
|
253
|
+
const scope = SCOPES.find((entry) => entry.scope === summary.scope)?.label ?? summary.scope;
|
|
254
|
+
const period = PERIODS.find((entry) => entry.period === summary.period)?.label ?? summary.period;
|
|
255
|
+
header.push(theme.bold(theme.fg('accent', `${scope} · ${period}`)));
|
|
256
|
+
header.push(...wrapTextWithAnsi(theme.fg('muted', usageLine(summary.totals)), width));
|
|
257
|
+
const details = [
|
|
258
|
+
summary.totals.cacheHitPercent === null ? null : `${summary.totals.cacheHitPercent}% cache hits`,
|
|
259
|
+
`TTFT ${duration(summary.totals.avgTtftMs)}`,
|
|
260
|
+
`request ${duration(summary.totals.avgDurationMs)}`,
|
|
261
|
+
summary.totals.estimatedCostUsd ? `${money(summary.totals.estimatedCostUsd)} estimated` : null
|
|
262
|
+
].filter((entry) => Boolean(entry));
|
|
263
|
+
if (details.length)
|
|
264
|
+
header.push(...wrapTextWithAnsi(theme.fg('dim', details.join(' · ')), width));
|
|
265
|
+
if (this.error)
|
|
266
|
+
header.push(...wrapTextWithAnsi(theme.fg('error', this.error), width));
|
|
267
|
+
const body = [''];
|
|
268
|
+
const total = tokensOf(summary.totals);
|
|
269
|
+
const gap = () => {
|
|
270
|
+
if (body.length && body[body.length - 1] !== '')
|
|
271
|
+
body.push('');
|
|
272
|
+
};
|
|
273
|
+
if (summary.byDay.length) {
|
|
274
|
+
const max = Math.max(...summary.byDay.map((day) => day.inputTokens + day.cacheReadTokens + day.outputTokens), 1);
|
|
275
|
+
const spark = summary.byDay.slice(-14).map((day) => BARS[Math.min(BARS.length - 1, Math.round(((day.inputTokens + day.cacheReadTokens + day.outputTokens) / max) * (BARS.length - 1)))]).join('');
|
|
276
|
+
body.push(theme.fg('accent', spark));
|
|
277
|
+
for (const day of summary.byDay.slice(-3).reverse()) {
|
|
278
|
+
body.push(...wrapTextWithAnsi(theme.fg('muted', ` ${day.day} ${count(day.inputTokens + day.cacheReadTokens + day.outputTokens)} tokens · ${count(day.requests)} requests · ${money(day.costUsd)}`), width));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const section = (rows) => {
|
|
282
|
+
if (!rows.length)
|
|
283
|
+
return;
|
|
284
|
+
gap();
|
|
285
|
+
for (const row of rows) {
|
|
286
|
+
const line = ` ${oneLine(row.label)} ${share(tokensOf(row), total)} ${count(tokensOf(row))} tokens · ${count(row.requests)} requests · ${money(row.costUsd)}`;
|
|
287
|
+
body.push(...wrapTextWithAnsi(theme.fg('muted', line), width));
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
section(summary.byModel);
|
|
291
|
+
section(summary.byProvider);
|
|
292
|
+
section(summary.byAccount);
|
|
293
|
+
section(summary.bySession);
|
|
294
|
+
if (body.length === 1)
|
|
295
|
+
body.push(theme.fg('dim', 'No requests were recorded in this period.'));
|
|
296
|
+
while (body.length > 1 && body[body.length - 1] === '')
|
|
297
|
+
body.pop();
|
|
298
|
+
return { header, body };
|
|
299
|
+
}
|
|
300
|
+
limitsContent(width, theme) {
|
|
301
|
+
const report = this.limits;
|
|
302
|
+
const header = [];
|
|
303
|
+
if (!report) {
|
|
304
|
+
header.push(theme.fg(this.error ? 'error' : 'dim', this.error ?? (this.busy ? 'Reading provider limits…' : 'No limit readings yet.')));
|
|
305
|
+
return { header, body: [] };
|
|
306
|
+
}
|
|
307
|
+
if (this.error)
|
|
308
|
+
header.push(...wrapTextWithAnsi(theme.fg('error', this.error), width));
|
|
309
|
+
const body = [];
|
|
310
|
+
const barWidth = Math.max(8, Math.min(16, Math.floor(width / 6)));
|
|
311
|
+
for (const account of report.accounts) {
|
|
312
|
+
if (body.length)
|
|
313
|
+
body.push('');
|
|
314
|
+
body.push(theme.bold(oneLine(account.label)) + theme.fg('dim', `${account.plan ? ` · ${oneLine(account.plan)}` : ''} · ${account.health}`));
|
|
315
|
+
if (!account.providers.length) {
|
|
316
|
+
body.push(theme.fg('dim', ' No limit readings.'));
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
for (const provider of account.providers) {
|
|
320
|
+
body.push('');
|
|
321
|
+
const source = provider.source === 'provider' ? '' : provider.source;
|
|
322
|
+
const meta = [source, ago(provider.observedAt)].filter(Boolean).join(' · ');
|
|
323
|
+
const tone = provider.source === 'unavailable' ? 'warning' : 'muted';
|
|
324
|
+
body.push(...wrapTextWithAnsi(theme.fg(tone, ` ${provider.provider}${meta ? ` · ${meta}` : ''}${provider.reason ? ` · ${oneLine(provider.reason)}` : ''}`), width));
|
|
325
|
+
for (const limit of provider.limits) {
|
|
326
|
+
body.push('');
|
|
327
|
+
const reset = until(limit.resetsAt);
|
|
328
|
+
body.push(...wrapTextWithAnsi(spread(theme.bold(` ${oneLine(limit.label)}`), theme.fg('dim', reset), width), width));
|
|
329
|
+
const used = limit.usedPercent ?? null;
|
|
330
|
+
const remainingPercent = limit.remainingPercent
|
|
331
|
+
?? (used === null
|
|
332
|
+
? limit.remaining !== null && limit.remaining !== undefined && limit.limit !== null && limit.limit !== undefined && limit.limit > 0
|
|
333
|
+
? Math.max(0, Math.min(100, (limit.remaining / limit.limit) * 100))
|
|
334
|
+
: null
|
|
335
|
+
: Math.max(0, Math.min(100, 100 - used)));
|
|
336
|
+
const rejected = limit.status === 'rejected';
|
|
337
|
+
const tone = barTone(remainingPercent, rejected);
|
|
338
|
+
const absolute = limit.remaining !== null && limit.remaining !== undefined
|
|
339
|
+
? `${limit.unit === 'usd' ? money(limit.remaining) : limit.unit === 'currency' ? `${limit.remaining.toFixed(2)} ${limit.detail ?? ''}`.trim() : count(limit.remaining)} left`
|
|
340
|
+
: limit.limit !== null && limit.limit !== undefined
|
|
341
|
+
? `limit ${count(limit.limit)}${limit.unit && limit.unit !== 'percent' ? ` ${limit.unit}` : ''}`
|
|
342
|
+
: '';
|
|
343
|
+
const value = remainingPercent === null ? absolute || '—' : `${Math.round(remainingPercent)}% left`;
|
|
344
|
+
const suffix = remainingPercent !== null && absolute ? ` ${theme.fg('dim', absolute)}` : '';
|
|
345
|
+
body.push(...wrapTextWithAnsi(` ${bar(remainingPercent, barWidth, theme, rejected)} ${theme.fg(tone, value)}${suffix}`, width));
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (!report.accounts.length)
|
|
350
|
+
body.push(theme.fg('dim', 'No accounts.'));
|
|
351
|
+
const local = this.limitsSummary?.totals;
|
|
352
|
+
if (local) {
|
|
353
|
+
if (body.length)
|
|
354
|
+
body.push('');
|
|
355
|
+
body.push(...wrapTextWithAnsi(theme.fg('dim', `7d · ${count(local.inputTokens + local.cacheReadTokens + local.outputTokens)} tokens · ${count(local.requests)} requests · ${money(local.costUsd)}`), width));
|
|
356
|
+
}
|
|
357
|
+
return { header, body };
|
|
358
|
+
}
|
|
359
|
+
render(width, rows) {
|
|
360
|
+
if (width < 1 || rows < 1)
|
|
361
|
+
return [];
|
|
362
|
+
const theme = this.options.theme();
|
|
363
|
+
const { header, body } = this.tab === 'analysis' ? this.analysisContent(width, theme) : this.limitsContent(width, theme);
|
|
364
|
+
const bodyRows = Math.max(0, rows - header.length);
|
|
365
|
+
this.scrollRows = Math.max(1, bodyRows);
|
|
366
|
+
this.maxOffset = Math.max(0, body.length - bodyRows);
|
|
367
|
+
this.offset = Math.max(0, Math.min(this.offset, this.maxOffset));
|
|
368
|
+
return [...header, ...body.slice(this.offset, this.offset + bodyRows)].slice(0, rows).map((line) => truncateToWidth(line, width, ''));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Theme, ToolRenderers } from '@earendil-works/pi-coding-agent';
|
|
2
2
|
export interface PiAdapter {
|
|
3
|
-
builtins: InlineExtension[];
|
|
4
3
|
setTheme(theme: Theme): void;
|
|
5
4
|
withBuiltInRenderers(toolName: string, definition: ToolRenderers | undefined): ToolRenderers | undefined;
|
|
6
5
|
readClipboardText(): Promise<string | null>;
|
|
@@ -12,18 +12,16 @@ export function loadPiAdapter() {
|
|
|
12
12
|
const pi = await loadPi();
|
|
13
13
|
assertPiAdapterVersion(pi.VERSION);
|
|
14
14
|
const root = pi.getPackageDir();
|
|
15
|
-
const [
|
|
16
|
-
import(pathToFileURL(join(root, 'dist/extensions/index.js')).href),
|
|
15
|
+
const [theme, renderers, clipboard, images] = await Promise.all([
|
|
17
16
|
import(pathToFileURL(join(root, 'dist/modes/interactive/theme/theme.js')).href),
|
|
18
17
|
import(pathToFileURL(join(root, 'dist/core/tools/renderers/index.js')).href),
|
|
19
18
|
import(pathToFileURL(join(root, 'dist/utils/clipboard.js')).href),
|
|
20
19
|
import(pathToFileURL(join(root, 'dist/utils/clipboard-image.js')).href)
|
|
21
20
|
]);
|
|
22
|
-
if (
|
|
23
|
-
throw new Error('The pinned Pi terminal adapter no longer exposes its required
|
|
21
|
+
if (typeof theme.setThemeInstance !== 'function' || typeof renderers.withBuiltInRenderers !== 'function' || typeof clipboard.readClipboardText !== 'function' || typeof clipboard.readClipboardFilePaths !== 'function' || typeof images.readClipboardImage !== 'function' || typeof images.extensionForImageMimeType !== 'function') {
|
|
22
|
+
throw new Error('The pinned Pi terminal adapter no longer exposes its required presentation hooks.');
|
|
24
23
|
}
|
|
25
24
|
adapter = {
|
|
26
|
-
builtins: extensions.builtInExtensions,
|
|
27
25
|
setTheme: theme.setThemeInstance,
|
|
28
26
|
withBuiltInRenderers: renderers.withBuiltInRenderers,
|
|
29
27
|
readClipboardText: clipboard.readClipboardText,
|
|
@@ -6,6 +6,8 @@ type PageOptions = {
|
|
|
6
6
|
runId?: string;
|
|
7
7
|
url?: string;
|
|
8
8
|
annotate?: AnnotateStart;
|
|
9
|
+
usageTab?: 'analysis' | 'limit';
|
|
10
|
+
usagePeriod?: 'today' | '7d' | '30d' | 'all';
|
|
9
11
|
};
|
|
10
12
|
type OpenPage = (id: CommandPageId, options?: PageOptions) => Promise<void>;
|
|
11
13
|
type Handler = (argument: string) => Promise<void>;
|
|
@@ -3,23 +3,27 @@ import { parseAnnotate } from "./annotateCommand.js";
|
|
|
3
3
|
/** Terminal adapters for commands declared by Harness-owned feature plugins. */
|
|
4
4
|
export function createBuiltinPluginCommandHandlers(host, openPage) {
|
|
5
5
|
const handlers = new Map([
|
|
6
|
-
['
|
|
7
|
-
['
|
|
8
|
-
['trajectory', async (argument) => openPage('trajectory', { runId: argument })],
|
|
9
|
-
['subagents', async () => openPage('subagents')],
|
|
10
|
-
['agents', async () => openPage('agents')],
|
|
11
|
-
['notes', async () => openPage('notes')],
|
|
12
|
-
['annotate', async (argument) => {
|
|
6
|
+
['annotations:notes', async () => openPage('notes')],
|
|
7
|
+
['annotations:annotate', async (argument) => {
|
|
13
8
|
const start = parseAnnotate(argument);
|
|
14
9
|
if (typeof start === 'string')
|
|
15
10
|
host.ui.notify(start, 'error');
|
|
16
11
|
else
|
|
17
12
|
await openPage('annotate', { annotate: start });
|
|
18
|
-
}]
|
|
13
|
+
}],
|
|
14
|
+
['usage:analysis', async (argument) => {
|
|
15
|
+
const period = argument.trim().toLowerCase();
|
|
16
|
+
if (period && !['today', '7d', '30d', 'all'].includes(period)) {
|
|
17
|
+
host.ui.notify('Use /usage:analysis today, 7d, 30d or all.', 'error');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
await openPage('usage', { usageTab: 'analysis', ...(period ? { usagePeriod: period } : {}) });
|
|
21
|
+
}],
|
|
22
|
+
['usage:limit', async () => openPage('usage', { usageTab: 'limit' })]
|
|
19
23
|
]);
|
|
20
24
|
const commands = BUILTIN_PLUGIN_REGISTRY.commands();
|
|
21
25
|
for (const command of commands) {
|
|
22
|
-
if (!handlers.has(command.name))
|
|
26
|
+
if (!handlers.has(`${command.pluginId}:${command.name}`))
|
|
23
27
|
throw new Error(`No terminal handler for built-in plugin command ${command.pluginId}:${command.name}.`);
|
|
24
28
|
}
|
|
25
29
|
return handlers;
|
|
@@ -4,6 +4,8 @@ import { isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
|
4
4
|
import terminalExtension from "../piExtension.js";
|
|
5
5
|
import { normalModeTools, planModeTools, QUESTION_TOOL_NAMES } from "./planMode.js";
|
|
6
6
|
import { claimTerminalControl, controlCall } from "../control.js";
|
|
7
|
+
import { observeProviderResponses } from "../../usage/limits/capture.js";
|
|
8
|
+
import { limitHeaders } from "../../usage/limits/normalize.js";
|
|
7
9
|
import { loadPiAdapter } from "./piAdapter.js";
|
|
8
10
|
import { loadPi } from "../../pi/runtime.js";
|
|
9
11
|
import { sessionUiContext } from "./sessionUi.js";
|
|
@@ -101,8 +103,16 @@ export async function createTerminalRuntime(args, host) {
|
|
|
101
103
|
const identity = await controlCall('accounts.session').catch(() => null);
|
|
102
104
|
const account = identity && typeof identity.accountId === 'string' && typeof identity.label === 'string'
|
|
103
105
|
? Object.freeze({ accountId: identity.accountId, label: identity.label }) : null;
|
|
104
|
-
|
|
106
|
+
await loadPiAdapter();
|
|
105
107
|
const modelRuntime = await ModelRuntime.create({ authPath: join(agentDir, 'auth.json'), modelsPath: join(agentDir, 'models.json') });
|
|
108
|
+
// This process makes the model requests, so it is where response headers are seen. Only the
|
|
109
|
+
// limit-bearing headers cross the socket, and the parent throttles what it stores.
|
|
110
|
+
observeProviderResponses(modelRuntime, (provider, response) => {
|
|
111
|
+
const headers = limitHeaders(response.headers);
|
|
112
|
+
if (!Object.keys(headers).length)
|
|
113
|
+
return;
|
|
114
|
+
void controlCall('terminal.usage.headers', { provider, status: response.status, headers }).catch(() => undefined);
|
|
115
|
+
});
|
|
106
116
|
const listeners = new Set();
|
|
107
117
|
const changes = new Set();
|
|
108
118
|
const beforeChanges = new Set();
|
|
@@ -157,11 +167,21 @@ export async function createTerminalRuntime(args, host) {
|
|
|
157
167
|
additionalSkillPaths: prepared.skills, additionalPromptTemplatePaths: prepared.prompts,
|
|
158
168
|
additionalThemePaths: prepared.themes,
|
|
159
169
|
extensionFactories: [
|
|
160
|
-
...adapter.builtins,
|
|
161
170
|
{ name: 'harness-terminal-bridge', factory: pi => terminalExtension(pi, planRun, () => native.session), hidden: true },
|
|
162
171
|
// A model picked in the terminal reaches Pi as this event. A restore is not a pick.
|
|
163
172
|
{ name: 'harness-mode-choices', factory: pi => { pi.on('model_select', event => { if (event.source !== 'restore')
|
|
164
|
-
void modeChoices.record(recordingMode(), { model: `${event.model.provider}/${event.model.id}` }); }); }, hidden: true }
|
|
173
|
+
void modeChoices.record(recordingMode(), { model: `${event.model.provider}/${event.model.id}` }); }); }, hidden: true },
|
|
174
|
+
// The bridge registers parent capabilities when delegation is available. Runs without
|
|
175
|
+
// delegation still need the same frozen snapshot, after the bridge has begun the run.
|
|
176
|
+
{ name: 'harness-run-capabilities', factory: pi => {
|
|
177
|
+
pi.on('before_agent_start', async () => {
|
|
178
|
+
if (!pi.getAllTools().some(tool => tool.name === 'spawn_subagent')) {
|
|
179
|
+
const snapshot = await controlCall('terminal.subagents.register', { tools: pi.getActiveTools(), plan: planRun.active });
|
|
180
|
+
if (Array.isArray(snapshot.connectors))
|
|
181
|
+
planRun.connectors = snapshot.connectors;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
}, hidden: true }
|
|
165
185
|
],
|
|
166
186
|
appendSystemPromptOverride: (base) => [...base, ...prepared.appendSystemPrompt]
|
|
167
187
|
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { SessionInfo, Theme } from '@earendil-works/pi-coding-agent';
|
|
2
|
+
import { type TuiMouseEventResult } from '@earendil-works/pi-tui';
|
|
3
|
+
import type { TerminalPage } from "./page.js";
|
|
4
|
+
import type { TerminalMouseInput } from "./mouse.js";
|
|
5
|
+
export declare function formatSessionRelativeTime(value: Date, now?: number): string;
|
|
6
|
+
export declare function formatSessionSize(bytes: number | undefined): string;
|
|
7
|
+
export interface ResumeSessionsPageOptions {
|
|
8
|
+
theme: () => Theme;
|
|
9
|
+
cwd(): string;
|
|
10
|
+
branch(): string | null;
|
|
11
|
+
listSessions(signal: AbortSignal): Promise<SessionInfo[]>;
|
|
12
|
+
select(path: string): void;
|
|
13
|
+
cancel(): void;
|
|
14
|
+
changed(): void;
|
|
15
|
+
}
|
|
16
|
+
export declare class ResumeSessionsPage implements TerminalPage {
|
|
17
|
+
private readonly options;
|
|
18
|
+
readonly presentation: "fullscreen";
|
|
19
|
+
private readonly list;
|
|
20
|
+
private loading;
|
|
21
|
+
private loaded;
|
|
22
|
+
private disposed;
|
|
23
|
+
private error;
|
|
24
|
+
private abort?;
|
|
25
|
+
constructor(options: ResumeSessionsPageOptions);
|
|
26
|
+
get focused(): boolean;
|
|
27
|
+
set focused(value: boolean);
|
|
28
|
+
tabs(): {
|
|
29
|
+
id: string;
|
|
30
|
+
title: string;
|
|
31
|
+
}[];
|
|
32
|
+
activeTab(): string;
|
|
33
|
+
hint(): string;
|
|
34
|
+
activate(): void;
|
|
35
|
+
dispose(): void;
|
|
36
|
+
private load;
|
|
37
|
+
handleInput(data: string): boolean;
|
|
38
|
+
handleMouse(event: TerminalMouseInput): TuiMouseEventResult | undefined;
|
|
39
|
+
render(width: number, rows: number): string[];
|
|
40
|
+
}
|