@selesai/code 0.11.0 → 0.13.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 +26 -0
- package/dist/cli/startup-ui.js +2 -1
- package/dist/core/agent-session.d.ts +12 -1
- package/dist/core/agent-session.js +77 -9
- package/dist/core/compaction/branch-summarization.js +4 -3
- package/dist/core/compaction/compaction.d.ts +5 -0
- package/dist/core/compaction/compaction.js +19 -4
- package/dist/core/extensions/index.d.ts +1 -1
- package/dist/core/extensions/runner.d.ts +5 -0
- package/dist/core/extensions/runner.js +48 -1
- package/dist/core/extensions/types.d.ts +18 -1
- package/dist/core/llama/client.d.ts +6 -0
- package/dist/core/llama/client.js +7 -0
- package/dist/core/llama/provider.d.ts +3 -1
- package/dist/core/llama/provider.js +28 -6
- package/dist/core/session-manager.js +4 -2
- package/dist/core/settings-manager.d.ts +8 -1
- package/dist/core/settings-manager.js +17 -0
- package/dist/core/usage-totals.d.ts +2 -0
- package/dist/core/usage-totals.js +10 -0
- package/dist/defaults/models.json +32 -9
- package/dist/extensions/pi-zentui/extensions/zentui/format.ts +2 -1
- package/dist/extensions/pi-zentui/test/format.test.ts +15 -1
- package/dist/extensions/tokenin-onboarding.ts +112 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/main.js +2 -0
- package/dist/modes/interactive/components/footer.js +2 -2
- package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/dist/modes/interactive/components/settings-selector.js +10 -0
- package/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/dist/modes/interactive/interactive-mode.js +59 -43
- package/dist/modes/rpc/rpc-client.d.ts +7 -0
- package/dist/modes/rpc/rpc-client.js +7 -0
- package/dist/modes/rpc/rpc-mode.js +3 -0
- package/dist/modes/rpc/rpc-types.d.ts +12 -0
- package/dist/utils/shell.js +6 -4
- package/docs/settings.md +4 -0
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import type { Transport } from "@earendil-works/pi-ai";
|
|
3
|
-
import type { ScrollViewScrollbar, TuiMode as RendererTuiMode } from "@earendil-works/pi-tui";
|
|
3
|
+
import type { ScrollViewScrollbar, TerminalCapabilities, TuiMode as RendererTuiMode } from "@earendil-works/pi-tui";
|
|
4
4
|
export interface CompactionSettings {
|
|
5
5
|
enabled?: boolean;
|
|
6
6
|
reserveTokens?: number;
|
|
@@ -28,6 +28,9 @@ export interface TerminalSettings {
|
|
|
28
28
|
imageWidthCells?: number;
|
|
29
29
|
clearOnShrink?: boolean;
|
|
30
30
|
showTerminalProgress?: boolean;
|
|
31
|
+
hyperlinks?: boolean | "auto";
|
|
32
|
+
images?: "kitty" | "iterm2" | "auto" | false;
|
|
33
|
+
trueColor?: boolean | "auto";
|
|
31
34
|
}
|
|
32
35
|
export interface ImageSettings {
|
|
33
36
|
autoResize?: boolean;
|
|
@@ -104,6 +107,7 @@ export interface Settings {
|
|
|
104
107
|
defaultTools?: string[];
|
|
105
108
|
fullscreenExitOutput?: FullscreenExitOutput;
|
|
106
109
|
fullscreenScrollbar?: ScrollViewScrollbar;
|
|
110
|
+
fullscreenCopyOnSelect?: boolean;
|
|
107
111
|
images?: ImageSettings;
|
|
108
112
|
enabledModels?: string[];
|
|
109
113
|
doubleEscapeAction?: "fork" | "tree" | "none";
|
|
@@ -285,6 +289,7 @@ export declare class SettingsManager {
|
|
|
285
289
|
setThemePaths(paths: string[]): void;
|
|
286
290
|
setProjectThemePaths(paths: string[]): void;
|
|
287
291
|
getThinkingBudgets(): ThinkingBudgetsSettings | undefined;
|
|
292
|
+
getTerminalCapabilityOverrides(): Partial<TerminalCapabilities>;
|
|
288
293
|
getShowImages(): boolean;
|
|
289
294
|
setShowImages(show: boolean): void;
|
|
290
295
|
getImageWidthCells(): number;
|
|
@@ -299,6 +304,8 @@ export declare class SettingsManager {
|
|
|
299
304
|
setFullscreenScrollbar(mode: ScrollViewScrollbar): void;
|
|
300
305
|
getFullscreenExitOutput(): FullscreenExitOutput;
|
|
301
306
|
setFullscreenExitOutput(output: FullscreenExitOutput): void;
|
|
307
|
+
getFullscreenCopyOnSelect(): boolean;
|
|
308
|
+
setFullscreenCopyOnSelect(enabled: boolean): void;
|
|
302
309
|
getImageAutoResize(): boolean;
|
|
303
310
|
setImageAutoResize(enabled: boolean): void;
|
|
304
311
|
getBlockImages(): boolean;
|
|
@@ -786,6 +786,15 @@ export class SettingsManager {
|
|
|
786
786
|
getThinkingBudgets() {
|
|
787
787
|
return this.settings.thinkingBudgets;
|
|
788
788
|
}
|
|
789
|
+
getTerminalCapabilityOverrides() {
|
|
790
|
+
const terminal = this.settings.terminal;
|
|
791
|
+
const images = terminal?.images;
|
|
792
|
+
return {
|
|
793
|
+
...(images === "kitty" || images === "iterm2" ? { images } : images === false ? { images: null } : {}),
|
|
794
|
+
...(typeof terminal?.trueColor === "boolean" ? { trueColor: terminal.trueColor } : {}),
|
|
795
|
+
...(typeof terminal?.hyperlinks === "boolean" ? { hyperlinks: terminal.hyperlinks } : {}),
|
|
796
|
+
};
|
|
797
|
+
}
|
|
789
798
|
getShowImages() {
|
|
790
799
|
return this.settings.terminal?.showImages ?? true;
|
|
791
800
|
}
|
|
@@ -864,6 +873,14 @@ export class SettingsManager {
|
|
|
864
873
|
this.markModified("fullscreenExitOutput");
|
|
865
874
|
this.save();
|
|
866
875
|
}
|
|
876
|
+
getFullscreenCopyOnSelect() {
|
|
877
|
+
return this.settings.fullscreenCopyOnSelect ?? true;
|
|
878
|
+
}
|
|
879
|
+
setFullscreenCopyOnSelect(enabled) {
|
|
880
|
+
this.globalSettings.fullscreenCopyOnSelect = enabled;
|
|
881
|
+
this.markModified("fullscreenCopyOnSelect");
|
|
882
|
+
this.save();
|
|
883
|
+
}
|
|
867
884
|
getImageAutoResize() {
|
|
868
885
|
return this.settings.images?.autoResize ?? true;
|
|
869
886
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Usage } from "@earendil-works/pi-ai/compat";
|
|
2
2
|
import type { SessionEntry } from "./session-manager.ts";
|
|
3
|
+
/** Format a USD cost with enough precision that sub-cent per-call costs don't round to $0.000. */
|
|
4
|
+
export declare function formatCost(cost: number): string;
|
|
3
5
|
export interface UsageTotals {
|
|
4
6
|
input: number;
|
|
5
7
|
output: number;
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/** Format a USD cost with enough precision that sub-cent per-call costs don't round to $0.000. */
|
|
2
|
+
export function formatCost(cost) {
|
|
3
|
+
if (cost === 0)
|
|
4
|
+
return "$0.000";
|
|
5
|
+
if (cost >= 1)
|
|
6
|
+
return `$${cost.toFixed(3)}`;
|
|
7
|
+
if (cost >= 0.01)
|
|
8
|
+
return `$${cost.toFixed(4)}`;
|
|
9
|
+
return `$${cost.toFixed(6)}`;
|
|
10
|
+
}
|
|
1
11
|
export function createUsageTotals() {
|
|
2
12
|
return {
|
|
3
13
|
input: 0,
|
|
@@ -24,7 +24,10 @@
|
|
|
24
24
|
"high": "high",
|
|
25
25
|
"xhigh": null,
|
|
26
26
|
"max": "max"
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
|
+
"compat": {
|
|
29
|
+
"requiresReasoningContentOnAssistantMessages": true
|
|
30
|
+
}
|
|
28
31
|
},
|
|
29
32
|
{
|
|
30
33
|
"id": "celestial-max",
|
|
@@ -40,7 +43,10 @@
|
|
|
40
43
|
"high": "high",
|
|
41
44
|
"xhigh": null,
|
|
42
45
|
"max": "max"
|
|
43
|
-
}
|
|
46
|
+
},
|
|
47
|
+
"compat": {
|
|
48
|
+
"requiresReasoningContentOnAssistantMessages": true
|
|
49
|
+
}
|
|
44
50
|
},
|
|
45
51
|
{
|
|
46
52
|
"id": "celestial-ultra",
|
|
@@ -56,7 +62,10 @@
|
|
|
56
62
|
"high": "high",
|
|
57
63
|
"xhigh": null,
|
|
58
64
|
"max": "max"
|
|
59
|
-
}
|
|
65
|
+
},
|
|
66
|
+
"compat": {
|
|
67
|
+
"requiresReasoningContentOnAssistantMessages": true
|
|
68
|
+
}
|
|
60
69
|
},
|
|
61
70
|
{
|
|
62
71
|
"id": "deepseek-v4-flash",
|
|
@@ -72,7 +81,10 @@
|
|
|
72
81
|
"high": "high",
|
|
73
82
|
"xhigh": null,
|
|
74
83
|
"max": "max"
|
|
75
|
-
}
|
|
84
|
+
},
|
|
85
|
+
"compat": {
|
|
86
|
+
"requiresReasoningContentOnAssistantMessages": true
|
|
87
|
+
}
|
|
76
88
|
},
|
|
77
89
|
{
|
|
78
90
|
"id": "deepseek-v4-flash-vision-exp",
|
|
@@ -88,7 +100,10 @@
|
|
|
88
100
|
"high": "high",
|
|
89
101
|
"xhigh": null,
|
|
90
102
|
"max": "max"
|
|
91
|
-
}
|
|
103
|
+
},
|
|
104
|
+
"compat": {
|
|
105
|
+
"requiresReasoningContentOnAssistantMessages": true
|
|
106
|
+
}
|
|
92
107
|
},
|
|
93
108
|
{
|
|
94
109
|
"id": "deepseek-v4-pro",
|
|
@@ -104,7 +119,10 @@
|
|
|
104
119
|
"high": "high",
|
|
105
120
|
"xhigh": null,
|
|
106
121
|
"max": "max"
|
|
107
|
-
}
|
|
122
|
+
},
|
|
123
|
+
"compat": {
|
|
124
|
+
"requiresReasoningContentOnAssistantMessages": true
|
|
125
|
+
}
|
|
108
126
|
},
|
|
109
127
|
{
|
|
110
128
|
"id": "gpt-5.6-luna",
|
|
@@ -200,7 +218,10 @@
|
|
|
200
218
|
"reasoning": false,
|
|
201
219
|
"input": ["text", "image"],
|
|
202
220
|
"contextWindow": 256000,
|
|
203
|
-
"maxTokens": 64000
|
|
221
|
+
"maxTokens": 64000,
|
|
222
|
+
"compat": {
|
|
223
|
+
"thinkingFormat": "qwen"
|
|
224
|
+
}
|
|
204
225
|
},
|
|
205
226
|
{
|
|
206
227
|
"id": "gemini-3.7-flash",
|
|
@@ -219,7 +240,8 @@
|
|
|
219
240
|
"maxTokens": 8192,
|
|
220
241
|
"compat": {
|
|
221
242
|
"supportsDeveloperRole": false,
|
|
222
|
-
"supportsReasoningEffort": false
|
|
243
|
+
"supportsReasoningEffort": false,
|
|
244
|
+
"thinkingFormat": "qwen"
|
|
223
245
|
}
|
|
224
246
|
},
|
|
225
247
|
{
|
|
@@ -231,7 +253,8 @@
|
|
|
231
253
|
"maxTokens": 8192,
|
|
232
254
|
"compat": {
|
|
233
255
|
"supportsDeveloperRole": false,
|
|
234
|
-
"supportsReasoningEffort": false
|
|
256
|
+
"supportsReasoningEffort": false,
|
|
257
|
+
"thinkingFormat": "qwen"
|
|
235
258
|
}
|
|
236
259
|
}
|
|
237
260
|
]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { homedir, hostname, userInfo } from "node:os";
|
|
2
2
|
import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
|
|
3
|
+
import { formatCost } from "../../../../core/usage-totals.ts";
|
|
3
4
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
4
5
|
import type {
|
|
5
6
|
ColorSource,
|
|
@@ -360,7 +361,7 @@ export function buildTokenLabel(totals: UsageTotals, cacheHitIcon = ""): str
|
|
|
360
361
|
}
|
|
361
362
|
|
|
362
363
|
export function buildCostLabel(totals: UsageTotals): string {
|
|
363
|
-
return
|
|
364
|
+
return formatCost(totals.cost);
|
|
364
365
|
}
|
|
365
366
|
|
|
366
367
|
export function buildSessionDurationLabel(startEpoch: number): string {
|
|
@@ -134,7 +134,7 @@ describe("usage formatting", () => {
|
|
|
134
134
|
const totals = getUsageTotals(ctx as never);
|
|
135
135
|
|
|
136
136
|
expect(totals.cost).toBe(0.0123);
|
|
137
|
-
expect(buildCostLabel(totals)).toBe("$0.
|
|
137
|
+
expect(buildCostLabel(totals)).toBe("$0.0123");
|
|
138
138
|
});
|
|
139
139
|
|
|
140
140
|
it("keeps the rate-card estimate when no reconcile entry exists", () => {
|
|
@@ -162,6 +162,20 @@ describe("usage formatting", () => {
|
|
|
162
162
|
expect(totals.cost).toBe(0.0123);
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
+
it("shows sub-cent reconciled costs without rounding to zero", () => {
|
|
166
|
+
const assistant = makeMessageUsageEntry("assistant", makeUsage(1_000, 500, 0), {
|
|
167
|
+
provider: "tokenin",
|
|
168
|
+
responseId: "chatcmpl-873",
|
|
169
|
+
});
|
|
170
|
+
const reconcile = makeReconcileEntry("tokenin", "chatcmpl-873", 0.00033601652);
|
|
171
|
+
const ctx = makeSessionContext([assistant, reconcile]);
|
|
172
|
+
|
|
173
|
+
const totals = getUsageTotals(ctx as never);
|
|
174
|
+
|
|
175
|
+
expect(totals.cost).toBe(0.00033601652);
|
|
176
|
+
expect(buildCostLabel(totals)).toBe("$0.000336");
|
|
177
|
+
});
|
|
178
|
+
|
|
165
179
|
it("formats cache atoms independently and omits zero totals", () => {
|
|
166
180
|
expect(buildCacheReadLabel(1_200)).toBe("R1.2k");
|
|
167
181
|
expect(buildCacheWriteLabel(300)).toBe("W300");
|
|
@@ -16,6 +16,7 @@ import type { AuthStorage, ExtensionAPI, ExtensionCommandContext, ExtensionConte
|
|
|
16
16
|
|
|
17
17
|
export const TOKEN_IN_PROVIDER = "tokenin";
|
|
18
18
|
export const TOKEN_IN_DASHBOARD_URL = "https://token.selesai.in/dashboard/tokens";
|
|
19
|
+
export const TOKEN_IN_DEFAULT_BASE_URL = "https://lite.andlet.me/v1";
|
|
19
20
|
export const ONBOARDING_MARKER_NAME = ".tokenInOnboardingComplete";
|
|
20
21
|
export const PLACEHOLDER_API_KEY = "sk-xxx";
|
|
21
22
|
|
|
@@ -239,6 +240,93 @@ async function promptForToken(ctx: ExtensionContext): Promise<string | undefined
|
|
|
239
240
|
return ctx.ui.input("Paste your Token-In token", "sk-...");
|
|
240
241
|
}
|
|
241
242
|
|
|
243
|
+
// ---------------------------------------------------------------------------
|
|
244
|
+
// /tokenin usage — LiteLLM /key/info quota lookup
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
|
|
247
|
+
export interface TokenInUsage {
|
|
248
|
+
spend: number;
|
|
249
|
+
maxBudget?: number;
|
|
250
|
+
budgetResetAt?: string;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Parse a LiteLLM /key/info payload into spend/budget/reset. Exported for tests. */
|
|
254
|
+
export function parseTokenInUsage(payload: unknown): TokenInUsage | undefined {
|
|
255
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return undefined;
|
|
256
|
+
const obj = payload as Record<string, unknown>;
|
|
257
|
+
const info =
|
|
258
|
+
obj.info && typeof obj.info === "object" && !Array.isArray(obj.info)
|
|
259
|
+
? (obj.info as Record<string, unknown>)
|
|
260
|
+
: obj;
|
|
261
|
+
const spend = Number(info.spend);
|
|
262
|
+
if (!Number.isFinite(spend) || spend < 0) return undefined;
|
|
263
|
+
const usage: TokenInUsage = { spend };
|
|
264
|
+
const maxBudget = Number(info.max_budget);
|
|
265
|
+
if (Number.isFinite(maxBudget) && maxBudget > 0) usage.maxBudget = maxBudget;
|
|
266
|
+
if (typeof info.budget_reset_at === "string") usage.budgetResetAt = info.budget_reset_at;
|
|
267
|
+
return usage;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Format usage as a boxed multi-line display with a progress bar. Exported for tests. */
|
|
271
|
+
export function formatTokenInUsage(usage: TokenInUsage): string {
|
|
272
|
+
const money = (v: number) => `$${v.toFixed(2)}`;
|
|
273
|
+
const pct = usage.maxBudget !== undefined ? (usage.spend / usage.maxBudget) * 100 : 0;
|
|
274
|
+
const barWidth = 40;
|
|
275
|
+
const filled = usage.maxBudget !== undefined ? Math.min(barWidth, Math.round((pct / 100) * barWidth)) : 0;
|
|
276
|
+
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
277
|
+
const pctText = usage.maxBudget !== undefined ? `${pct.toFixed(1)}%` : "—";
|
|
278
|
+
|
|
279
|
+
let summary = `Spent ${money(usage.spend)}`;
|
|
280
|
+
if (usage.maxBudget !== undefined) {
|
|
281
|
+
summary += ` of ${money(usage.maxBudget)} budget (${pct.toFixed(1)}%)`;
|
|
282
|
+
} else {
|
|
283
|
+
summary += " (no budget limit set)";
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
let footer = usage.maxBudget !== undefined ? `${money(Math.max(0, usage.maxBudget - usage.spend))} remaining` : "";
|
|
287
|
+
if (usage.budgetResetAt) {
|
|
288
|
+
const d = new Date(usage.budgetResetAt);
|
|
289
|
+
if (!Number.isNaN(d.getTime())) {
|
|
290
|
+
const ms = d.getTime() - Date.now();
|
|
291
|
+
if (ms > 0) {
|
|
292
|
+
const days = Math.floor(ms / 86_400_000);
|
|
293
|
+
const hours = Math.floor((ms % 86_400_000) / 3_600_000);
|
|
294
|
+
const reset = days > 0 ? `${days}d ${hours}h` : `${Math.max(1, Math.ceil(ms / 3_600_000))}h`;
|
|
295
|
+
footer += footer ? ` · resets in ${reset}` : `resets in ${reset}`;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const W = 50; // total box width
|
|
301
|
+
const inner = W - 4; // content width between │ padding
|
|
302
|
+
const pad = (s: string) => s + " ".repeat(Math.max(0, inner - s.length));
|
|
303
|
+
const top = `╭─ Token-In usage ${"─".repeat(W - 19)}╮`;
|
|
304
|
+
const bottom = `╰${"─".repeat(W - 2)}╯`;
|
|
305
|
+
return [
|
|
306
|
+
top,
|
|
307
|
+
`│ ${pad(summary)} │`,
|
|
308
|
+
`│ ${bar} ${pctText.padStart(5)} │`,
|
|
309
|
+
`│ ${pad(footer)} │`,
|
|
310
|
+
bottom,
|
|
311
|
+
].join("\n");
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/** Query LiteLLM /key/info at the proxy root with the account key. Exported for tests. */
|
|
315
|
+
export async function fetchTokenInUsage(apiKey: string, baseUrl: string): Promise<TokenInUsage> {
|
|
316
|
+
const root = baseUrl.replace(/\/v1\/?$/, "");
|
|
317
|
+
const response = await fetch(`${root}/key/info`, {
|
|
318
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "application/json" },
|
|
319
|
+
signal: AbortSignal.timeout(5_000),
|
|
320
|
+
});
|
|
321
|
+
if (!response.ok) {
|
|
322
|
+
throw new Error(`Token-In usage endpoint returned HTTP ${response.status}`);
|
|
323
|
+
}
|
|
324
|
+
const payload: unknown = await response.json();
|
|
325
|
+
const usage = parseTokenInUsage(payload);
|
|
326
|
+
if (!usage) throw new Error("Token-In usage endpoint returned an unexpected payload");
|
|
327
|
+
return usage;
|
|
328
|
+
}
|
|
329
|
+
|
|
242
330
|
// ---------------------------------------------------------------------------
|
|
243
331
|
// /tokenin subcommand handlers
|
|
244
332
|
// ---------------------------------------------------------------------------
|
|
@@ -327,6 +415,25 @@ async function handleTokenInSwitch(ctx: ExtensionCommandContext): Promise<void>
|
|
|
327
415
|
ctx.ui.notify(`Switched to account ${account.label}.`, "info");
|
|
328
416
|
}
|
|
329
417
|
|
|
418
|
+
async function handleTokenInUsage(ctx: ExtensionCommandContext): Promise<void> {
|
|
419
|
+
const apiKey = getStoredTokenInApiKey(ctx.modelRegistry.authStorage);
|
|
420
|
+
if (!apiKey) {
|
|
421
|
+
ctx.ui.notify("No active Token-In token. Use /tokenin add first.", "warning");
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
const auth = readTokenInAuth();
|
|
425
|
+
const activeId = getActiveTokenInAccountId(ctx.modelRegistry.authStorage);
|
|
426
|
+
const account = auth.accounts.find((a) => a.id === activeId);
|
|
427
|
+
const baseUrl = account?.baseUrl ?? getTokenInBaseUrl(readModelsJson()) ?? TOKEN_IN_DEFAULT_BASE_URL;
|
|
428
|
+
try {
|
|
429
|
+
const usage = await fetchTokenInUsage(apiKey, baseUrl);
|
|
430
|
+
ctx.ui.notify(formatTokenInUsage(usage), "info");
|
|
431
|
+
} catch (err) {
|
|
432
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
433
|
+
ctx.ui.notify(`Could not fetch Token-In usage: ${message}`, "warning");
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
330
437
|
async function handleTokenInRemove(ctx: ExtensionCommandContext): Promise<void> {
|
|
331
438
|
if (!ctx.hasUI) {
|
|
332
439
|
ctx.ui.notify("This command requires interactive mode.", "warning");
|
|
@@ -441,12 +548,13 @@ export default function tokenInOnboardingExtension(pi: ExtensionAPI): void {
|
|
|
441
548
|
});
|
|
442
549
|
|
|
443
550
|
pi.registerCommand("tokenin", {
|
|
444
|
-
description: "Manage TokenIN accounts: /tokenin add|switch|remove",
|
|
551
|
+
description: "Manage TokenIN accounts: /tokenin add|switch|remove|usage",
|
|
445
552
|
getArgumentCompletions: (prefix: string) => {
|
|
446
553
|
const subcommands = [
|
|
447
554
|
{ name: "add", description: "Add a TokenIN account" },
|
|
448
555
|
{ name: "switch", description: "Switch active account" },
|
|
449
556
|
{ name: "remove", description: "Remove a saved account" },
|
|
557
|
+
{ name: "usage", description: "Show Token-IN quota usage" },
|
|
450
558
|
];
|
|
451
559
|
const lowerPrefix = prefix.toLowerCase();
|
|
452
560
|
const items = subcommands.filter((s) => s.name.toLowerCase().startsWith(lowerPrefix));
|
|
@@ -463,8 +571,10 @@ export default function tokenInOnboardingExtension(pi: ExtensionAPI): void {
|
|
|
463
571
|
await handleTokenInSwitch(ctx);
|
|
464
572
|
} else if (sub === "remove") {
|
|
465
573
|
await handleTokenInRemove(ctx);
|
|
574
|
+
} else if (sub === "usage") {
|
|
575
|
+
await handleTokenInUsage(ctx);
|
|
466
576
|
} else {
|
|
467
|
-
ctx.ui.notify("Usage: /tokenin add|switch|remove", "info");
|
|
577
|
+
ctx.ui.notify("Usage: /tokenin add|switch|remove|usage", "info");
|
|
468
578
|
}
|
|
469
579
|
},
|
|
470
580
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { readStoredCredential } from "./core/auth-storage.ts";
|
|
|
5
5
|
export { ensureTool, getToolPath, type ManagedTool } from "./utils/tools-manager.ts";
|
|
6
6
|
export { type BranchPreparation, type BranchSummaryResult, type CollectEntriesResult, type CompactionResult, type CutPointResult, calculateContextTokens, collectEntriesForBranchSummary, compact, DEFAULT_COMPACTION_SETTINGS, estimateTokens, type FileOperations, findCutPoint, findTurnStartIndex, type GenerateBranchSummaryOptions, generateBranchSummary, generateSummary, generateSummaryWithUsage, getLastAssistantUsage, prepareBranchEntries, serializeConversation, shouldCompact, } from "./core/compaction/index.ts";
|
|
7
7
|
export { createEventBus, type EventBus, type EventBusController } from "./core/event-bus.ts";
|
|
8
|
-
export type { AgentEndEvent, AgentSettledEvent, AgentStartEvent, AgentToolResult, AgentToolUpdateCallback, AppKeybinding, AutocompleteProviderFactory, BashToolCallEvent, BeforeAgentStartEvent, BeforeAgentStartEventResult, BeforeProviderHeadersEvent, BeforeProviderRequestEvent, BeforeProviderRequestEventResult, BuildSystemPromptOptions, CompactOptions, ContextEvent, ContextUsage, CustomToolCallEvent, EditToolCallEvent, EntryRenderer, EntryRenderOptions, ExecOptions, ExecResult, Extension, ExtensionActions, ExtensionAPI, ExtensionCommandContext, ExtensionCommandContextActions, ExtensionContext, ExtensionContextActions, ExtensionError, ExtensionEvent, ExtensionFactory, ExtensionFlag, ExtensionHandler, ExtensionRuntime, ExtensionShortcut, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetOptions, FindToolCallEvent, GrepToolCallEvent, InlineExtension, InputEvent, InputEventResult, InputSource, KeybindingsManager, LoadExtensionsResult, LsToolCallEvent, MessageEndEvent, MessageRenderer, MessageRenderOptions, MessageStartEvent, MessageUpdateEvent, ProjectTrustContext, ProjectTrustEvent, ProjectTrustEventDecision, ProjectTrustEventResult, ProjectTrustHandler, ProviderConfig, ProviderModelConfig, ReadToolCallEvent, RegisteredCommand, RegisteredTool, ResolvedCommand, SessionBeforeCompactEvent, SessionBeforeForkEvent, SessionBeforeSwitchEvent, SessionBeforeTreeEvent, SessionCompactEvent, SessionInfoChangedEvent, SessionShutdownEvent, SessionStartEvent, SessionTreeEvent, SlashCommandInfo, SlashCommandSource, SourceInfo, TerminalInputHandler, ToolCallEvent, ToolCallEventResult, ToolDefinition, ToolExecutionEndEvent, ToolExecutionMode, ToolExecutionStartEvent, ToolExecutionUpdateEvent, ToolInfo, ToolRenderResultOptions, ToolResultEvent, TurnEndEvent, TurnStartEvent, UserBashEvent, UserBashEventResult, WidgetPlacement, WorkingIndicatorOptions, WriteToolCallEvent, } from "./core/extensions/index.ts";
|
|
8
|
+
export type { AgentEndEvent, AgentSettledEvent, AgentStartEvent, AgentToolResult, AgentToolUpdateCallback, AppKeybinding, AutocompleteProviderFactory, BashToolCallEvent, BeforeAgentStartEvent, BeforeAgentStartEventResult, BeforeProviderHeadersEvent, BeforeProviderRequestEvent, BeforeProviderRequestEventResult, BuildSystemPromptOptions, CompactOptions, ContextEvent, ContextUsage, CustomToolCallEvent, EditToolCallEvent, EntryRenderer, EntryRenderOptions, ExecOptions, ExecResult, Extension, ExtensionActions, ExtensionAPI, ExtensionCommandContext, ExtensionCommandContextActions, ExtensionContext, ExtensionContextActions, ExtensionError, ExtensionEvent, ExtensionFactory, ExtensionFlag, ExtensionHandler, ExtensionRuntime, ExtensionShortcut, ExtensionUIContext, ExtensionUIDialogOptions, ExtensionWidgetOptions, FindToolCallEvent, GrepToolCallEvent, InlineExtension, InputEvent, InputEventResult, InputSource, KeybindingsManager, LoadExtensionsResult, LsToolCallEvent, MessageEndEvent, MessageRenderer, MessageRenderOptions, MessageStartEvent, MessageUpdateEvent, ProjectTrustContext, ProjectTrustEvent, ProjectTrustEventDecision, ProjectTrustEventResult, ProjectTrustHandler, ProviderConfig, ProviderModelConfig, ReadToolCallEvent, RegisteredCommand, RegisteredTool, ResolvedCommand, SessionBeforeCompactEvent, SessionBeforeForkEvent, SessionBeforeSwitchEvent, SessionBeforeTreeEvent, SessionCompactEvent, SessionInfoChangedEvent, SessionShutdownEvent, SessionStartEvent, SessionTreeEvent, SlashCommandInfo, SlashCommandSource, SourceInfo, TerminalInputHandler, ToolCallEvent, ToolCallEventResult, ToolDefinition, ToolExecutionEndEvent, ToolExecutionMode, ToolExecutionStartEvent, ToolExecutionUpdateEvent, ToolInfo, ToolRenderResultOptions, ToolResultEvent, TurnEndEvent, TurnStartEvent, UIPromptEndEvent, UIPromptKind, UIPromptStartEvent, UserBashEvent, UserBashEventResult, WidgetPlacement, WorkingIndicatorOptions, WriteToolCallEvent, } from "./core/extensions/index.ts";
|
|
9
9
|
export { createExtensionRuntime, defineTool, discoverAndLoadExtensions, ExtensionRunner, isBashToolResult, isEditToolResult, isFindToolResult, isGrepToolResult, isLsToolResult, isReadToolResult, isToolCallEventType, isWriteToolResult, wrapRegisteredTool, wrapRegisteredTools, } from "./core/extensions/index.ts";
|
|
10
10
|
export type { ReadonlyFooterDataProvider } from "./core/footer-data-provider.ts";
|
|
11
11
|
export { convertToLlm } from "./core/messages.ts";
|
|
@@ -32,4 +32,5 @@ export { copyToClipboard } from "./utils/clipboard.ts";
|
|
|
32
32
|
export { parseFrontmatter, stripFrontmatter } from "./utils/frontmatter.ts";
|
|
33
33
|
export { convertToPng } from "./utils/image-convert.ts";
|
|
34
34
|
export { formatDimensionNote, type ResizedImage, resizeImage } from "./utils/image-resize.ts";
|
|
35
|
+
export { detectSupportedImageMimeTypeFromFile } from "./utils/mime.ts";
|
|
35
36
|
export { getShellConfig } from "./utils/shell.ts";
|
package/dist/index.js
CHANGED
|
@@ -43,5 +43,6 @@ export { copyToClipboard } from "./utils/clipboard.js";
|
|
|
43
43
|
export { parseFrontmatter, stripFrontmatter } from "./utils/frontmatter.js";
|
|
44
44
|
export { convertToPng } from "./utils/image-convert.js";
|
|
45
45
|
export { formatDimensionNote, resizeImage } from "./utils/image-resize.js";
|
|
46
|
+
export { detectSupportedImageMimeTypeFromFile } from "./utils/mime.js";
|
|
46
47
|
// Shell utilities
|
|
47
48
|
export { getShellConfig } from "./utils/shell.js";
|
package/dist/main.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { createInterface } from "node:readline";
|
|
8
8
|
import { modelsAreEqual } from "@earendil-works/pi-ai";
|
|
9
|
+
import { setCapabilityOverrides } from "@earendil-works/pi-tui";
|
|
9
10
|
import chalk from "chalk";
|
|
10
11
|
import { parseArgs, printHelp } from "./cli/args.js";
|
|
11
12
|
import { CredentialPrintError, isCredentialPrintHelp, parseCredentialPrintCommand, printCredentialPrintHelp, resolveCredentialForPrint, validateCredentialPrintArgs, } from "./cli/credential-print.js";
|
|
@@ -659,6 +660,7 @@ export async function main(args, options) {
|
|
|
659
660
|
time("createAgentSessionRuntime");
|
|
660
661
|
const { services, session, modelFallbackMessage } = runtime;
|
|
661
662
|
const { settingsManager, modelRuntime, resourceLoader } = services;
|
|
663
|
+
setCapabilityOverrides(settingsManager.getTerminalCapabilityOverrides());
|
|
662
664
|
applyHttpProxySettings(settingsManager.getGlobalSettings().httpProxy);
|
|
663
665
|
configureHttpDispatcher(settingsManager.getHttpIdleTimeoutMs());
|
|
664
666
|
if (parsed.help) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
2
2
|
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
3
3
|
import { areExperimentalFeaturesEnabled } from "../../../core/experimental.js";
|
|
4
|
-
import { addUsageToTotals, createUsageTotals } from "../../../core/usage-totals.js";
|
|
4
|
+
import { addUsageToTotals, createUsageTotals, formatCost } from "../../../core/usage-totals.js";
|
|
5
5
|
import { theme } from "../theme/theme.js";
|
|
6
6
|
/**
|
|
7
7
|
* Sanitize text for display in a single-line status.
|
|
@@ -127,7 +127,7 @@ export class FooterComponent {
|
|
|
127
127
|
? state.model.provider === "kimi-coding" || this.session.modelRuntime.isUsingOAuth(state.model.provider)
|
|
128
128
|
: false;
|
|
129
129
|
if (usageTotals.cost || usingSubscription) {
|
|
130
|
-
const costStr =
|
|
130
|
+
const costStr = `${formatCost(usageTotals.cost)}${usingSubscription ? " (sub)" : ""}`;
|
|
131
131
|
statsParts.push(costStr);
|
|
132
132
|
}
|
|
133
133
|
// Colorize context percentage based on usage
|
|
@@ -49,6 +49,7 @@ export interface SettingsConfig {
|
|
|
49
49
|
tuiMode: TuiMode;
|
|
50
50
|
fullscreenExitOutput: FullscreenExitOutput;
|
|
51
51
|
fullscreenScrollbar: ScrollViewScrollbar;
|
|
52
|
+
fullscreenCopyOnSelect: boolean;
|
|
52
53
|
warnings: WarningSettings;
|
|
53
54
|
skills: SkillToggleItem[];
|
|
54
55
|
}
|
|
@@ -86,6 +87,7 @@ export interface SettingsCallbacks {
|
|
|
86
87
|
onTuiModeChange: (mode: TuiMode) => void;
|
|
87
88
|
onFullscreenExitOutputChange: (output: FullscreenExitOutput) => void;
|
|
88
89
|
onFullscreenScrollbarChange: (mode: ScrollViewScrollbar) => void;
|
|
90
|
+
onFullscreenCopyOnSelectChange: (enabled: boolean) => void;
|
|
89
91
|
onWarningsChange: (warnings: WarningSettings) => void;
|
|
90
92
|
onSkillsToggle: (item: SkillToggleItem, enabled: boolean) => void;
|
|
91
93
|
onSkillsReload: () => void;
|
|
@@ -474,6 +474,13 @@ export class SettingsSelectorComponent extends Container {
|
|
|
474
474
|
currentValue: config.fullscreenScrollbar,
|
|
475
475
|
values: ["auto", "always", "hidden"],
|
|
476
476
|
},
|
|
477
|
+
{
|
|
478
|
+
id: "fullscreen-copy-on-select",
|
|
479
|
+
label: "Fullscreen copy on select",
|
|
480
|
+
description: "Automatically copy selected text in fullscreen mode; disable to copy selections with Ctrl+X",
|
|
481
|
+
currentValue: config.fullscreenCopyOnSelect ? "true" : "false",
|
|
482
|
+
values: ["true", "false"],
|
|
483
|
+
},
|
|
477
484
|
{
|
|
478
485
|
id: "theme",
|
|
479
486
|
label: "Theme",
|
|
@@ -697,6 +704,9 @@ export class SettingsSelectorComponent extends Container {
|
|
|
697
704
|
case "fullscreen-scrollbar":
|
|
698
705
|
callbacks.onFullscreenScrollbarChange(newValue);
|
|
699
706
|
break;
|
|
707
|
+
case "fullscreen-copy-on-select":
|
|
708
|
+
callbacks.onFullscreenCopyOnSelectChange(newValue === "true");
|
|
709
|
+
break;
|
|
700
710
|
case "theme":
|
|
701
711
|
callbacks.onThemeChange(newValue);
|
|
702
712
|
break;
|
|
@@ -43,6 +43,7 @@ interface InteractiveTuiOptions {
|
|
|
43
43
|
logDirectory: string;
|
|
44
44
|
terminal?: Terminal;
|
|
45
45
|
onRightClickPaste?: () => void;
|
|
46
|
+
fullscreenCopyOnSelect?: boolean;
|
|
46
47
|
}
|
|
47
48
|
/** Composition root for selecting the interactive terminal renderer. */
|
|
48
49
|
export declare function createInteractiveTui(options: InteractiveTuiOptions): TuiMainScreen | TuiAltScreen;
|
|
@@ -201,6 +202,7 @@ export declare class InteractiveMode {
|
|
|
201
202
|
private setExtensionStatus;
|
|
202
203
|
private showStatusIndicator;
|
|
203
204
|
private clearStatusIndicator;
|
|
205
|
+
private showWorkingStatusIndicator;
|
|
204
206
|
private setWorkingVisible;
|
|
205
207
|
private setWorkingIndicator;
|
|
206
208
|
private setHiddenThinkingLabel;
|
|
@@ -346,6 +348,8 @@ export declare class InteractiveMode {
|
|
|
346
348
|
private cycleModel;
|
|
347
349
|
private toggleToolOutputExpansion;
|
|
348
350
|
private setToolsExpanded;
|
|
351
|
+
/** Update rendered assistant messages without rebuilding live tool components. */
|
|
352
|
+
private updateThinkingBlockVisibility;
|
|
349
353
|
private toggleThinkingBlockVisibility;
|
|
350
354
|
private handleOpenExternalEditor;
|
|
351
355
|
clearEditor(): void;
|