@matterailab/orbcode 0.1.8 → 0.1.10
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/bin/orbcode.js +3 -0
- package/dist/core/agent.js +20 -0
- package/dist/index.js +4 -0
- package/dist/ui/App.js +6 -2
- package/dist/ui/components/StatusBar.js +10 -17
- package/package.json +1 -1
package/bin/orbcode.js
CHANGED
package/dist/core/agent.js
CHANGED
|
@@ -61,6 +61,12 @@ export class Agent {
|
|
|
61
61
|
sessionApproveCommands = false;
|
|
62
62
|
abortController;
|
|
63
63
|
totalCost = 0;
|
|
64
|
+
/**
|
|
65
|
+
* Latest context window usage (input + output tokens from the most recent
|
|
66
|
+
* `usage` chunk). Mirrors `totalCost` so the status bar can show it across
|
|
67
|
+
* /resume, /clear and process restarts.
|
|
68
|
+
*/
|
|
69
|
+
contextTokens = 0;
|
|
64
70
|
title = "";
|
|
65
71
|
createdAt = new Date().toISOString();
|
|
66
72
|
taskId;
|
|
@@ -71,6 +77,7 @@ export class Agent {
|
|
|
71
77
|
this.messages = options.resume.messages;
|
|
72
78
|
this.todos = options.resume.todos;
|
|
73
79
|
this.totalCost = options.resume.totalCost;
|
|
80
|
+
this.contextTokens = options.resume.contextTokens;
|
|
74
81
|
this.title = options.resume.title;
|
|
75
82
|
this.createdAt = options.resume.createdAt;
|
|
76
83
|
this.firstMessageSent = this.messages.length > 0;
|
|
@@ -100,6 +107,15 @@ export class Agent {
|
|
|
100
107
|
get modelId() {
|
|
101
108
|
return this.options.modelId;
|
|
102
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Latest context window usage in tokens. Falls back to 0 for fresh
|
|
112
|
+
* sessions; equals the persisted value after a resume so the TUI can
|
|
113
|
+
* repopulate its `contextTokens` state without waiting for the next
|
|
114
|
+
* streaming chunk.
|
|
115
|
+
*/
|
|
116
|
+
get lastContextTokens() {
|
|
117
|
+
return this.contextTokens;
|
|
118
|
+
}
|
|
103
119
|
/** Replace the prompt-derived title with the backend-generated one. */
|
|
104
120
|
setTitle(title) {
|
|
105
121
|
this.title = title;
|
|
@@ -118,6 +134,7 @@ export class Agent {
|
|
|
118
134
|
this.todos = "";
|
|
119
135
|
this.firstMessageSent = false;
|
|
120
136
|
this.totalCost = 0;
|
|
137
|
+
this.contextTokens = 0;
|
|
121
138
|
this.title = "";
|
|
122
139
|
}
|
|
123
140
|
/** Write the current conversation to the sessions directory. */
|
|
@@ -133,6 +150,7 @@ export class Agent {
|
|
|
133
150
|
createdAt: this.createdAt,
|
|
134
151
|
updatedAt: new Date().toISOString(),
|
|
135
152
|
totalCost: this.totalCost,
|
|
153
|
+
contextTokens: this.contextTokens,
|
|
136
154
|
todos: this.todos,
|
|
137
155
|
messages: this.messages,
|
|
138
156
|
});
|
|
@@ -251,6 +269,7 @@ User time zone: ${timeZone}, UTC${timeZoneOffsetStr}`;
|
|
|
251
269
|
}
|
|
252
270
|
else if (chunk.type === "usage") {
|
|
253
271
|
this.totalCost += chunk.totalCost ?? 0;
|
|
272
|
+
this.contextTokens = (chunk.inputTokens ?? 0) + (chunk.outputTokens ?? 0);
|
|
254
273
|
onEvent({
|
|
255
274
|
type: "usage",
|
|
256
275
|
inputTokens: chunk.inputTokens,
|
|
@@ -330,6 +349,7 @@ User time zone: ${timeZone}, UTC${timeZoneOffsetStr}`;
|
|
|
330
349
|
break;
|
|
331
350
|
case "usage":
|
|
332
351
|
this.totalCost += chunk.totalCost ?? 0;
|
|
352
|
+
this.contextTokens = (chunk.inputTokens ?? 0) + (chunk.outputTokens ?? 0);
|
|
333
353
|
onEvent({
|
|
334
354
|
type: "usage",
|
|
335
355
|
inputTokens: chunk.inputTokens,
|
package/dist/index.js
CHANGED
|
@@ -68,6 +68,10 @@ function takeFlagValue(args, name) {
|
|
|
68
68
|
return value;
|
|
69
69
|
}
|
|
70
70
|
async function main() {
|
|
71
|
+
// Override Node's default process title so terminals (iTerm2 "current job
|
|
72
|
+
// name", VSCode terminal status, etc.) don't append " (node)" next to our
|
|
73
|
+
// own title. The bundled bin/orbcode.js also does this for the npm case.
|
|
74
|
+
process.title = "orbcode";
|
|
71
75
|
const args = process.argv.slice(2);
|
|
72
76
|
if (args.includes("--version") || args.includes("-v")) {
|
|
73
77
|
console.log(VERSION);
|
package/dist/ui/App.js
CHANGED
|
@@ -220,7 +220,7 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
220
220
|
void fetchTaskTitle(agent.taskId, token).then((title) => {
|
|
221
221
|
if (title && agentRef.current?.taskId === agent.taskId) {
|
|
222
222
|
setSessionTitle(title);
|
|
223
|
-
setTerminalTitle(
|
|
223
|
+
setTerminalTitle(title);
|
|
224
224
|
agent.setTitle(title);
|
|
225
225
|
}
|
|
226
226
|
});
|
|
@@ -363,12 +363,16 @@ export function App({ initialView, initialPrompt, initialSession, updateCheck, }
|
|
|
363
363
|
resetTranscript();
|
|
364
364
|
setTasks(session.todos ?? "");
|
|
365
365
|
setTotalCost(session.totalCost ?? 0);
|
|
366
|
+
// Repopulate the status bar immediately. Without this the context
|
|
367
|
+
// number only appears once the next streaming `usage` chunk arrives,
|
|
368
|
+
// which can be after several seconds of "ctx 0".
|
|
369
|
+
setContextTokens(session.contextTokens ?? 0);
|
|
366
370
|
if (session.title) {
|
|
367
371
|
// The stored title is already the backend one (or the prompt
|
|
368
372
|
// fallback); don't re-fetch for this task.
|
|
369
373
|
titleTaskRef.current = session.id;
|
|
370
374
|
setSessionTitle(session.title);
|
|
371
|
-
setTerminalTitle(
|
|
375
|
+
setTerminalTitle(session.title);
|
|
372
376
|
}
|
|
373
377
|
// Replay the conversation into the transcript.
|
|
374
378
|
for (const message of session.messages) {
|
|
@@ -33,25 +33,18 @@ function truncate(text, max) {
|
|
|
33
33
|
return text.length > max ? text.slice(0, max - 1) + "…" : text;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
36
|
+
* Formats the 5hr window as "5hr limit XX% · resets <relative>".
|
|
37
|
+
* Returns null when tiered usage is unavailable so the line can be hidden.
|
|
38
38
|
*/
|
|
39
|
-
function
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
];
|
|
45
|
-
const [label, pct, resetsAt] = windows.reduce((best, cur) => cur[1] >= best[1] ? cur : best);
|
|
46
|
-
return `${label} ${Math.round(pct)}% · resets ${formatRelativeTime(resetsAt)}`;
|
|
39
|
+
function fiveHourSummary(tu) {
|
|
40
|
+
if (!tu?.fiveHour)
|
|
41
|
+
return null;
|
|
42
|
+
const { percentage, resetsAt } = tu.fiveHour;
|
|
43
|
+
return `5hr limit ${Math.round(percentage)}% · resets ${formatRelativeTime(resetsAt)}`;
|
|
47
44
|
}
|
|
48
|
-
export function StatusBar({ modelId, contextTokens, totalCost, state, approvalMode, busy, title, plan, usagePercentage, tieredUsage, }) {
|
|
45
|
+
export function StatusBar({ modelId, contextTokens, totalCost: _totalCost, state, approvalMode, busy, title, plan: _plan, usagePercentage: _usagePercentage, tieredUsage, }) {
|
|
49
46
|
const model = getModel(modelId);
|
|
50
47
|
const contextPct = Math.min(100, Math.round((contextTokens / model.contextWindow) * 100));
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
: typeof usagePercentage === "number"
|
|
54
|
-
? `${Math.round(usagePercentage)}% used`
|
|
55
|
-
: null;
|
|
56
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { dimColor: true, children: [_jsx(Text, { color: approvalMode === "ask" ? undefined : COLORS.warning, children: MODE_LABELS[approvalMode] }), " (shift+tab to cycle)", busy && " · esc to interrupt", state ? _jsxs(Text, { color: COLORS.thinking, children: [" \u00B7 ", state] }) : null] }), _jsxs(Text, { dimColor: true, children: [title ? `${truncate(title, 32)} · ` : "", model.name, " \u00B7 ctx ", contextTokens.toLocaleString(), " (", contextPct, "%)", model.free ? " · free" : ` · $${totalCost.toFixed(4)}`] })] }), (plan || constrainedLine) && (_jsx(Box, { justifyContent: "flex-end", children: _jsxs(Text, { dimColor: true, children: [plan && constrainedLine ? " · " : "", constrainedLine] }) }))] }));
|
|
48
|
+
const fiveHourLine = fiveHourSummary(tieredUsage);
|
|
49
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { justifyContent: "space-between", children: [_jsxs(Text, { dimColor: true, children: [_jsx(Text, { color: approvalMode === "ask" ? undefined : COLORS.warning, children: MODE_LABELS[approvalMode] }), " (shift+tab to cycle)", busy && " · esc to interrupt", state ? _jsxs(Text, { color: COLORS.thinking, children: [" \u00B7 ", state] }) : null] }), _jsxs(Text, { dimColor: true, children: [title ? `${truncate(title, 32)} · ` : "", model.name, " \u00B7 ctx ", contextTokens.toLocaleString(), " (", contextPct, "%)"] })] }), fiveHourLine && (_jsx(Box, { justifyContent: "flex-end", children: _jsx(Text, { dimColor: true, children: fiveHourLine }) }))] }));
|
|
57
50
|
}
|