@matterailab/orbcode 0.3.4 → 0.4.1
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/README.md +3 -3
- package/dist/api/models.js +7 -18
- package/dist/ui/App.js +166 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -185,10 +185,9 @@ sessions.
|
|
|
185
185
|
| ------------------------- | ------- | ---------- | ---------------------- |
|
|
186
186
|
| `axon-eido-3-code-pro` | 400k | 64k | $3/M in · $9/M out |
|
|
187
187
|
| `axon-eido-3-code-mini` | 400k | 64k | $1.5/M in · $4.5/M out |
|
|
188
|
-
| `axon-
|
|
189
|
-
| `axon-code-2-5-mini` | 400k | 64k | free |
|
|
188
|
+
| `axon-eido-3-flash` | 200k | 64k | free |
|
|
190
189
|
|
|
191
|
-
`axon-eido-3-code-mini` is the default. All
|
|
190
|
+
`axon-eido-3-code-mini` is the default. All three support native JSON tool calls
|
|
192
191
|
and image input. Cost comes from the API's usage chunks (`is_byok`-aware) and is
|
|
193
192
|
shown in the status bar.
|
|
194
193
|
|
|
@@ -298,6 +297,7 @@ MatterAI gateway untouched.
|
|
|
298
297
|
| `/status` | version, model, account, gateway, context usage, cost, approval modes |
|
|
299
298
|
| `/usage` | fetch plan usage |
|
|
300
299
|
| `/init` | analyze the codebase and create/improve `AGENTS.md` in the repo's `.orb/` directory |
|
|
300
|
+
| `/task` | reference a previous session as context for the current task — opens a picker if no ID is given |
|
|
301
301
|
| `/link` | link other repos on your machine so changes here are checked against them (enter a folder path) |
|
|
302
302
|
| `/mcp` | manage MCP servers — enable, disable, reconnect, view status & tool counts |
|
|
303
303
|
| `/login` | start the browser sign-in flow |
|
package/dist/api/models.js
CHANGED
|
@@ -95,28 +95,17 @@ export const ANTHROPIC_MODELS = {
|
|
|
95
95
|
* interactive picker for now.
|
|
96
96
|
*/
|
|
97
97
|
export const BUILTIN_AXON_MODELS = {
|
|
98
|
-
"axon-
|
|
99
|
-
id: "axon-
|
|
100
|
-
name: "Axon
|
|
101
|
-
description: "Axon
|
|
102
|
-
contextWindow:
|
|
98
|
+
"axon-eido-3-flash": {
|
|
99
|
+
id: "axon-eido-3-flash",
|
|
100
|
+
name: "Axon Eido 3 Flash (free)",
|
|
101
|
+
description: "Axon Eido is a fast and low cost general purpose model for low-effort day-to-day tasks",
|
|
102
|
+
contextWindow: 200000,
|
|
103
103
|
maxOutputTokens: 64000,
|
|
104
104
|
supportsImages: true,
|
|
105
|
-
inputPrice: 0.
|
|
106
|
-
outputPrice: 0.
|
|
105
|
+
inputPrice: 0.0,
|
|
106
|
+
outputPrice: 0.0,
|
|
107
107
|
free: true,
|
|
108
108
|
},
|
|
109
|
-
"axon-code-2-5-pro": {
|
|
110
|
-
id: "axon-code-2-5-pro",
|
|
111
|
-
name: "Axon Code 2.5 Pro",
|
|
112
|
-
description: "Axon Code 2.5 Pro is the next-generation of Axon Code for coding tasks, currently in experimental stage.",
|
|
113
|
-
contextWindow: 400000,
|
|
114
|
-
maxOutputTokens: 64000,
|
|
115
|
-
supportsImages: true,
|
|
116
|
-
inputPrice: 0.000002,
|
|
117
|
-
outputPrice: 0.000006,
|
|
118
|
-
free: false,
|
|
119
|
-
},
|
|
120
109
|
"axon-eido-3-code-pro": {
|
|
121
110
|
id: "axon-eido-3-code-pro",
|
|
122
111
|
name: "Axon Eido 3 Pro",
|
package/dist/ui/App.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
3
|
-
import { Box,
|
|
3
|
+
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
4
4
|
import open from "open";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import { COLORS, VERSION } from "../branding.js";
|
|
@@ -226,6 +226,9 @@ function rowId() {
|
|
|
226
226
|
}
|
|
227
227
|
export function App({ initialView, initialPrompt, initialSession, systemPromptOverride, updateCheck, }) {
|
|
228
228
|
const { exit } = useApp();
|
|
229
|
+
const { stdout } = useStdout();
|
|
230
|
+
const termRows = stdout?.rows ?? 24;
|
|
231
|
+
const termCols = stdout?.columns ?? 80;
|
|
229
232
|
const [settings, setSettings] = useState(() => loadSettings());
|
|
230
233
|
const [view, setView] = useState(initialView ?? (getAuthToken(settings) ? "chat" : "login"));
|
|
231
234
|
const [updateInfo, setUpdateInfo] = useState(null);
|
|
@@ -266,7 +269,6 @@ export function App({ initialView, initialPrompt, initialSession, systemPromptOv
|
|
|
266
269
|
const [mcpMigrationEntries, setMcpMigrationEntries] = useState(null);
|
|
267
270
|
// Project-scope MCP servers awaiting the user's approval at startup.
|
|
268
271
|
const [pendingMcpApproval, setPendingMcpApproval] = useState(null);
|
|
269
|
-
const [staticKey, setStaticKey] = useState(0);
|
|
270
272
|
const [tasks, setTasks] = useState("");
|
|
271
273
|
const [contextTokens, setContextTokens] = useState(0);
|
|
272
274
|
const [totalCost, setTotalCost] = useState(0);
|
|
@@ -356,7 +358,6 @@ export function App({ initialView, initialPrompt, initialSession, systemPromptOv
|
|
|
356
358
|
modelName: getModel(loadSettings().model).name,
|
|
357
359
|
},
|
|
358
360
|
]);
|
|
359
|
-
setStaticKey((k) => k + 1);
|
|
360
361
|
}, []);
|
|
361
362
|
const handleEvent = useCallback((event) => {
|
|
362
363
|
switch (event.type) {
|
|
@@ -1068,7 +1069,6 @@ export function App({ initialView, initialPrompt, initialSession, systemPromptOv
|
|
|
1068
1069
|
if (process.stdout.isTTY) {
|
|
1069
1070
|
process.stdout.write("\x1b[2J\x1b[H");
|
|
1070
1071
|
}
|
|
1071
|
-
setStaticKey((k) => k + 1);
|
|
1072
1072
|
}
|
|
1073
1073
|
});
|
|
1074
1074
|
const handleLogin = useCallback((token, profile) => {
|
|
@@ -1103,48 +1103,170 @@ export function App({ initialView, initialPrompt, initialSession, systemPromptOv
|
|
|
1103
1103
|
!resumableSessions &&
|
|
1104
1104
|
!taskPickerSessions &&
|
|
1105
1105
|
!linkManagerOpen;
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1106
|
+
// ── Viewport management ─────────────────────────────────────────────
|
|
1107
|
+
// Instead of <Static> (which permanently writes to stdout and causes
|
|
1108
|
+
// uncontrolled scrolling), we render everything in the dynamic region and
|
|
1109
|
+
// only show the rows that fit above the pinned input box + status bar.
|
|
1110
|
+
// This keeps the input box always at the bottom of the terminal.
|
|
1111
|
+
// Height of the fixed bottom chrome (input box border + text + status bar +
|
|
1112
|
+
// margins). Intentionally slightly overestimated so we never push the
|
|
1113
|
+
// input box off-screen.
|
|
1114
|
+
let bottomHeight = 8;
|
|
1115
|
+
if (queuedMessages.length > 0)
|
|
1116
|
+
bottomHeight += 2 + Math.min(5, queuedMessages.length);
|
|
1117
|
+
if (pendingApproval ||
|
|
1118
|
+
pendingFollowup ||
|
|
1119
|
+
pendingHookTrust ||
|
|
1120
|
+
pendingMcpApproval ||
|
|
1121
|
+
modelPickerOpen ||
|
|
1122
|
+
mcpPickerOpen ||
|
|
1123
|
+
mcpMigrationEntries ||
|
|
1124
|
+
resumableSessions ||
|
|
1125
|
+
taskPickerSessions ||
|
|
1126
|
+
linkManagerOpen)
|
|
1127
|
+
bottomHeight += 10;
|
|
1128
|
+
// Height of the middle dynamic elements (between rows and input box).
|
|
1129
|
+
let middleHeight = 0;
|
|
1130
|
+
if (streamingReasoning)
|
|
1131
|
+
middleHeight += 4;
|
|
1132
|
+
if (taskLines.length > 0)
|
|
1133
|
+
middleHeight += 1 + Math.min(10, taskLines.length);
|
|
1134
|
+
if (busy && !streamingText && !streamingReasoning)
|
|
1135
|
+
middleHeight += 1;
|
|
1136
|
+
if (updateInfo?.updateAvailable && updateInfo.latest)
|
|
1137
|
+
middleHeight += 4;
|
|
1138
|
+
// Available height for committed rows + streaming text.
|
|
1139
|
+
const availableHeight = Math.max(3, termRows - bottomHeight - middleHeight);
|
|
1140
|
+
// Estimate how many terminal lines a row will occupy.
|
|
1141
|
+
const wrapWidth = Math.max(20, termCols - 4);
|
|
1142
|
+
const visibleRows = useMemo(() => {
|
|
1143
|
+
let used = 0;
|
|
1144
|
+
let start = rows.length;
|
|
1145
|
+
for (let i = rows.length - 1; i >= 0; i--) {
|
|
1146
|
+
const h = estimateRowLines(rows[i], wrapWidth);
|
|
1147
|
+
if (used + h > availableHeight)
|
|
1148
|
+
break;
|
|
1149
|
+
used += h;
|
|
1150
|
+
start = i;
|
|
1151
|
+
}
|
|
1152
|
+
return rows.slice(start);
|
|
1153
|
+
}, [rows, availableHeight, wrapWidth]);
|
|
1154
|
+
// Cap streaming text to the space left after committed rows.
|
|
1155
|
+
const rowsHeight = useMemo(() => visibleRows.reduce((s, r) => s + estimateRowLines(r, wrapWidth), 0), [visibleRows, wrapWidth]);
|
|
1156
|
+
const streamingMaxLines = Math.max(3, availableHeight - rowsHeight);
|
|
1157
|
+
const streamingDisplay = streamingText
|
|
1158
|
+
? tailForHeight(streamingText, streamingMaxLines, wrapWidth)
|
|
1159
|
+
: "";
|
|
1160
|
+
const streamingReasoningDisplay = streamingReasoning
|
|
1161
|
+
? tailForHeight(streamingReasoning, 3, wrapWidth)
|
|
1162
|
+
: "";
|
|
1163
|
+
// Spacer to push the input box to the bottom when content is short.
|
|
1164
|
+
const streamingHeight = streamingDisplay
|
|
1165
|
+
? Math.min(streamingMaxLines, wrapHeight(streamingDisplay, wrapWidth))
|
|
1166
|
+
: 0;
|
|
1167
|
+
const spacerHeight = Math.max(0, availableHeight - rowsHeight - streamingHeight);
|
|
1168
|
+
return (_jsx(Box, { flexDirection: "column", children: view === "login" ? (_jsx(LoginSection, { onLogin: handleLogin })) : (_jsxs(Box, { flexDirection: "column", children: [visibleRows.map((row) => (_jsx(RowView, { row: row }, row.id))), updateInfo?.updateAvailable && updateInfo.latest && (_jsxs(Box, { marginTop: 1, flexDirection: "column", borderStyle: "round", borderColor: COLORS.warning, paddingX: 2, alignSelf: "flex-start", children: [_jsxs(Text, { color: COLORS.warning, bold: true, children: ["\u2191 Update available: v", updateInfo.current, " \u2192 v", updateInfo.latest] }), _jsxs(Text, { children: ["Run ", _jsx(Text, { color: COLORS.accent, children: "orbcode update" }), " to install the latest version, then relaunch."] })] })), streamingReasoning && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: COLORS.thinking, italic: true, children: "\u2726 Thinking\u2026" }), _jsx(Box, { paddingLeft: 2, children: _jsx(Text, { dimColor: true, italic: true, children: streamingReasoningDisplay }) })] })), streamingDisplay && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: [_jsx(Text, { color: COLORS.primary, children: "\u25CF " }), streamingDisplay] }) })), spacerHeight > 0 && _jsx(Box, { height: spacerHeight }), taskLines.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, paddingLeft: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: "Tasks" }), taskLines.slice(0, 10).map((line, i) => (_jsx(Text, { dimColor: /^[-*]\s*\[x\]/i.test(line), children: line
|
|
1169
|
+
.replace(/^[-*]\s*\[x\]/i, " ■")
|
|
1170
|
+
.replace(/^[-*]\s*\[-\]/, " ◧")
|
|
1171
|
+
.replace(/^[-*]\s*\[ \]/, " □") }, i))), taskLines.length > 10 && (_jsxs(Text, { dimColor: true, children: [" \u2026 ", taskLines.length - 10, " more"] }))] })), modelPickerOpen && (_jsx(Box, { marginTop: 1, children: _jsx(ModelPicker, { currentId: settings.model, onSelect: (modelId) => {
|
|
1172
|
+
setModelPickerOpen(false);
|
|
1173
|
+
switchModel(modelId);
|
|
1174
|
+
}, onCancel: () => setModelPickerOpen(false) }) })), resumableSessions && (_jsx(Box, { marginTop: 1, children: _jsx(SessionPicker, { sessions: resumableSessions, onSelect: handleResume, onCancel: () => setResumableSessions(null) }) })), taskPickerSessions && (_jsx(Box, { marginTop: 1, children: _jsx(SessionPicker, { sessions: taskPickerSessions, title: "Reference a previous task", onSelect: handleTaskSelect, onCancel: () => setTaskPickerSessions(null) }) })), linkManagerOpen && (_jsx(Box, { marginTop: 1, children: _jsx(LinkManager, { links: links, status: linkStatus, onAdd: (input) => {
|
|
1175
|
+
const result = addLink(process.cwd(), input);
|
|
1176
|
+
setLinkStatus(result.message);
|
|
1177
|
+
if (result.ok)
|
|
1119
1178
|
setLinks(loadLinks(process.cwd()));
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1179
|
+
}, onRemove: (link) => {
|
|
1180
|
+
removeLink(process.cwd(), link.path);
|
|
1181
|
+
setLinks(loadLinks(process.cwd()));
|
|
1182
|
+
setLinkStatus(`Unlinked ${path.basename(link.path)}`);
|
|
1183
|
+
}, onClose: () => setLinkManagerOpen(false) }) })), pendingHookTrust && (_jsx(Box, { marginTop: 1, children: _jsx(HookTrustPrompt, { cwd: process.cwd(), commands: pendingHookTrust.commands, onDecision: resolveHookTrust }) })), pendingMcpApproval && (_jsx(Box, { marginTop: 1, children: _jsx(McpApprovalPrompt, { serverNames: pendingMcpApproval, onApprove: resolveMcpApproval }) })), mcpPickerOpen && mcpManagerRef.current && (_jsx(Box, { marginTop: 1, children: _jsx(McpPicker, { manager: mcpManagerRef.current, onChanged: () => {
|
|
1184
|
+
const m = mcpManagerRef.current;
|
|
1185
|
+
if (m)
|
|
1186
|
+
saveMcpApproval(process.cwd(), m.getEnabled(), m.getDisabled());
|
|
1187
|
+
}, onCancel: () => setMcpPickerOpen(false), onDeleted: handleMcpServerDeleted }) })), mcpMigrationEntries && (_jsx(Box, { marginTop: 1, children: _jsx(McpMigrationPicker, { entries: mcpMigrationEntries, onCancel: () => setMcpMigrationEntries(null), onConfirm: (selected) => {
|
|
1188
|
+
setMcpMigrationEntries(null);
|
|
1189
|
+
resolveMcpMigration(selected);
|
|
1190
|
+
} }) })), pendingApproval && (_jsx(Box, { marginTop: 1, children: _jsx(ApprovalPrompt, { request: pendingApproval.request, onDecision: (decision) => {
|
|
1191
|
+
pendingApproval.resolve(decision);
|
|
1192
|
+
setPendingApproval(null);
|
|
1193
|
+
} }) })), pendingFollowup && (_jsx(Box, { marginTop: 1, children: _jsx(FollowupPrompt, { question: pendingFollowup.question, suggestions: pendingFollowup.suggestions, onAnswer: (answer) => {
|
|
1194
|
+
pushRow({ kind: "user", text: answer });
|
|
1195
|
+
pendingFollowup.resolve(answer);
|
|
1196
|
+
setPendingFollowup(null);
|
|
1197
|
+
} }) })), busy &&
|
|
1198
|
+
!pendingApproval &&
|
|
1199
|
+
!pendingFollowup &&
|
|
1200
|
+
!pendingHookTrust &&
|
|
1201
|
+
!pendingMcpApproval &&
|
|
1202
|
+
!mcpPickerOpen &&
|
|
1203
|
+
!mcpMigrationEntries &&
|
|
1204
|
+
!streamingText &&
|
|
1205
|
+
!streamingReasoning && (_jsx(Box, { marginTop: 1, children: _jsx(Spinner, { label: busyLabel }) })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [queuedMessages.length > 0 && (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginBottom: 1, children: [_jsxs(Text, { dimColor: true, bold: true, children: ["Queue (", queuedMessages.length, ")"] }), queuedMessages.slice(0, 5).map((msg, i) => (_jsxs(Text, { dimColor: true, children: [i + 1, ". ", truncateForQueue(msg).replace(/\n/g, "↵")] }, i))), queuedMessages.length > 5 && (_jsxs(Text, { dimColor: true, children: [" \u2026 ", queuedMessages.length - 5, " more"] }))] })), _jsx(InputBox, { active: inputActive, slashCommands: SLASH_COMMANDS, onSubmit: handleSubmit }), _jsx(StatusBar, { modelId: settings.model, contextTokens: contextTokens, totalCost: totalCost, state: busy ? busyLabel : "", approvalMode: approvalMode, busy: busy, title: sessionTitle, plan: usage?.plan, usagePercentage: usage?.usagePercentage, tieredUsage: usage?.tieredUsage })] })] })) }));
|
|
1206
|
+
}
|
|
1207
|
+
/** Count the number of terminal rows a block of text will occupy (accounting
|
|
1208
|
+
* for wrapping at `width` columns). */
|
|
1209
|
+
function wrapHeight(text, width) {
|
|
1210
|
+
return text.split("\n").reduce((sum, line) => sum + Math.max(1, Math.ceil(Math.max(1, line.length) / width)), 0);
|
|
1211
|
+
}
|
|
1212
|
+
/** Return the tail of `text` that fits within `maxLines` terminal rows,
|
|
1213
|
+
* accounting for line wrapping at `width` columns. */
|
|
1214
|
+
function tailForHeight(text, maxLines, width) {
|
|
1215
|
+
const lines = text.split("\n");
|
|
1216
|
+
const result = [];
|
|
1217
|
+
let height = 0;
|
|
1218
|
+
for (let i = lines.length - 1; i >= 0 && height < maxLines; i--) {
|
|
1219
|
+
const line = lines[i];
|
|
1220
|
+
const wrapped = Math.max(1, Math.ceil(Math.max(1, line.length) / width));
|
|
1221
|
+
if (height + wrapped > maxLines) {
|
|
1222
|
+
const remaining = maxLines - height;
|
|
1223
|
+
if (remaining > 0) {
|
|
1224
|
+
const chars = remaining * width;
|
|
1225
|
+
result.unshift(line.slice(-chars));
|
|
1226
|
+
}
|
|
1227
|
+
break;
|
|
1228
|
+
}
|
|
1229
|
+
result.unshift(line);
|
|
1230
|
+
height += wrapped;
|
|
1231
|
+
}
|
|
1232
|
+
return result.join("\n");
|
|
1144
1233
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1234
|
+
/** Estimate how many terminal lines a committed row will occupy. Intentionally
|
|
1235
|
+
* slightly conservative (overestimates) so we never show too many rows and
|
|
1236
|
+
* push the input box off-screen. */
|
|
1237
|
+
function estimateRowLines(row, width) {
|
|
1238
|
+
const w = Math.max(20, width);
|
|
1239
|
+
function wrapped(text) {
|
|
1240
|
+
return text.split("\n").reduce((sum, line) => sum + Math.max(1, Math.ceil(Math.max(1, line.length) / w)), 0);
|
|
1241
|
+
}
|
|
1242
|
+
switch (row.kind) {
|
|
1243
|
+
case "header":
|
|
1244
|
+
return 9;
|
|
1245
|
+
case "user":
|
|
1246
|
+
return 1 + wrapped(row.text);
|
|
1247
|
+
case "assistant":
|
|
1248
|
+
return 1 + wrapped(row.text);
|
|
1249
|
+
case "reasoning":
|
|
1250
|
+
return row.expanded ? 1 + wrapped(row.text) : 1;
|
|
1251
|
+
case "tool": {
|
|
1252
|
+
let h = 1;
|
|
1253
|
+
if (row.diff) {
|
|
1254
|
+
h += Math.min(62, row.diff.split("\n").length) + 1;
|
|
1255
|
+
}
|
|
1256
|
+
else if (row.resultPreview) {
|
|
1257
|
+
h += wrapped(row.resultPreview);
|
|
1258
|
+
}
|
|
1259
|
+
return h;
|
|
1260
|
+
}
|
|
1261
|
+
case "info":
|
|
1262
|
+
return Math.max(1, wrapped(row.text));
|
|
1263
|
+
case "error":
|
|
1264
|
+
return Math.max(1, wrapped(row.text));
|
|
1265
|
+
case "completion":
|
|
1266
|
+
return 2 + wrapped(row.text);
|
|
1267
|
+
default:
|
|
1268
|
+
return 1;
|
|
1269
|
+
}
|
|
1148
1270
|
}
|
|
1149
1271
|
const QUEUE_PREVIEW_LIMIT = 80;
|
|
1150
1272
|
function truncateForQueue(text) {
|