@boundless.network/setup 0.1.1 → 0.1.2

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/dist/app.js CHANGED
@@ -19,8 +19,7 @@ import { Frame, Muted, Para, Title } from "./ui/frame.js";
19
19
  import { MultiSelect, Select } from "./ui/menu.js";
20
20
  import { Spinner } from "./ui/spinner.js";
21
21
  import { LOGO, LOGO_COLUMNS, LOGO_ROWS } from "./ui/logo.js";
22
- import { ModelTable } from "./ui/table.js";
23
- import { TextInput } from "./ui/text-input.js";
22
+ import { ModelSelect } from "./ui/table.js";
24
23
  import { BAD, BRAND, MUTED, OK, WARN } from "./ui/theme.js";
25
24
  const NAV = "↑↓ navigate enter select";
26
25
  function message(error) {
@@ -173,9 +172,7 @@ export function App({ onFinish }) {
173
172
  setScreen({ kind: "model", role: "primary" });
174
173
  }, onError: (error) => fail(error, { kind: "catalog" }), wizard: wizard }));
175
174
  case "model":
176
- return (_jsx(ModelPicker, { catalog: wizard.catalog, harnesses: chosen(), onOther: () => setScreen({ kind: "type-model", role: screen.role }), onSelect: (id) => chooseModel(screen.role, id), role: screen.role }));
177
- case "type-model":
178
- return (_jsx(TypeModel, { catalog: wizard.catalog, onCancel: () => setScreen({ kind: "model", role: screen.role }), onSubmit: (id) => chooseModel(screen.role, id) }));
175
+ return (_jsx(ModelPicker, { catalog: wizard.catalog, harnesses: chosen(), onSelect: (id) => chooseModel(screen.role, id), role: screen.role }));
179
176
  case "review":
180
177
  return (_jsx(Review, { harnesses: chosen(), input: planInput(), onCancel: () => exit(), onPlans: (plans) => {
181
178
  wizard.plans = plans;
@@ -312,22 +309,10 @@ function Catalog({ onDone, onError, wizard }) {
312
309
  }, []);
313
310
  return (_jsx(Frame, { hints: "", children: _jsx(Spinner, { label: "Reading the model catalog" }) }));
314
311
  }
315
- function ModelPicker({ catalog, harnesses, onOther, onSelect, role, }) {
312
+ function ModelPicker({ catalog, harnesses, onSelect, role, }) {
316
313
  const lead = harnesses.find((harness) => role === "primary" || harness.backgroundDefault !== null) ?? harnesses[0];
317
314
  const recommended = role === "primary" ? lead?.defaultModel : lead?.backgroundDefault;
318
- const ids = new Set(catalog.map((model) => model.id));
319
- const cheapest = [...catalog].sort((a, b) => a.outputPerMillionTokens - b.outputPerMillionTokens).map((model) => model.id);
320
- const picks = [recommended, ...cheapest].filter((id) => typeof id === "string" && ids.has(id));
321
- const options = [...new Set(picks)].slice(0, 4).map((id, index) => ({
322
- hint: index === 0 ? `recommended for ${lead?.name ?? "you"}` : undefined,
323
- label: id,
324
- value: id,
325
- }));
326
- return (_jsxs(Frame, { hints: NAV, children: [_jsx(Title, { children: role === "primary" ? "Which model should be the default?" : "Which model for background work?" }), _jsxs(Muted, { center: true, children: [role === "primary" ? "Every configured harness gets this model." : "Cheap, fast tasks (Claude Code haiku slot, OpenCode small_model, omp tiny).", " Prices are USD per 1M tokens from the live catalog."] }), _jsx(ModelTable, { models: catalog }), _jsx(Box, { justifyContent: "center", children: _jsx(Select, { onSelect: (id) => (id === "__other" ? onOther() : onSelect(id)), options: [...options, { label: "Another model from the table", value: "__other" }] }) })] }));
327
- }
328
- function TypeModel({ catalog, onCancel, onSubmit }) {
329
- const [rejected, setRejected] = useState(null);
330
- return (_jsxs(Frame, { hints: "enter confirm esc back", children: [_jsx(Title, { children: "Type a model ID" }), _jsx(ModelTable, { models: catalog }), _jsx(Box, { justifyContent: "center", children: _jsx(TextInput, { onCancel: onCancel, onSubmit: (id) => (catalog.some((model) => model.id === id) ? onSubmit(id) : setRejected(id)), placeholder: "model id" }) }), rejected ? (_jsx(Box, { justifyContent: "center", marginTop: 1, children: _jsxs(Text, { color: WARN, children: [rejected, " is not served right now."] }) })) : null] }));
315
+ return (_jsxs(Frame, { hints: NAV, children: [_jsx(Title, { children: role === "primary" ? "Which model should be the default?" : "Which model for background work?" }), _jsxs(Muted, { center: true, children: [role === "primary" ? "Every configured harness gets this model." : "Cheap, fast tasks (Claude Code haiku slot, OpenCode small_model, omp tiny).", " Prices are USD per 1M tokens from the live catalog", lead ? `; the recommendation is ${lead.name}'s documented default.` : "."] }), _jsx(ModelSelect, { models: catalog, onSelect: onSelect, recommended: recommended ?? undefined }, role)] }));
331
316
  }
332
317
  function Review({ harnesses, input, onCancel, onPlans, onProceed, wizard, }) {
333
318
  const [plans, setPlans] = useState(null);
package/dist/ui/table.js CHANGED
@@ -1,17 +1,40 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from "ink";
2
+ import { Box, Text, useInput } from "ink";
3
+ import { useState } from "react";
3
4
  import { usd } from "../lib/catalog.js";
4
- import { MUTED } from "./theme.js";
5
- const columns = (model) => [
5
+ import { BRAND, MUTED } from "./theme.js";
6
+ const HEADER = ["ID", "Input /1M", "Output /1M", "Context", ""];
7
+ const cells = (model, recommended) => [
6
8
  model.id,
7
9
  usd(model.inputPerMillionTokens),
8
10
  usd(model.outputPerMillionTokens),
9
11
  model.contextTokens.toLocaleString("en-US"),
12
+ model.id === recommended ? "recommended" : "",
10
13
  ];
11
- export function ModelTable({ models }) {
12
- const header = ["ID", "Input /1M", "Output /1M", "Context"];
13
- const rows = models.map(columns);
14
- const widths = header.map((cell, column) => Math.max(cell.length, ...rows.map((row) => row[column]?.length ?? 0)));
15
- const line = (cells) => cells.map((cell, column) => (column === 0 ? cell.padEnd(widths[column] ?? 0) : cell.padStart(widths[column] ?? 0))).join(" ");
16
- return (_jsxs(Box, { alignItems: "center", flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { color: MUTED, children: line(header) }), rows.map((row) => (_jsx(Text, { children: line(row) }, row[0])))] }));
14
+ export function ModelSelect({ models, onSelect, recommended, }) {
15
+ const start = Math.max(models.findIndex((model) => model.id === recommended), 0);
16
+ const [index, setIndex] = useState(start);
17
+ useInput((input, key) => {
18
+ if (key.upArrow || input === "k") {
19
+ setIndex((index + models.length - 1) % models.length);
20
+ }
21
+ else if (key.downArrow || input === "j") {
22
+ setIndex((index + 1) % models.length);
23
+ }
24
+ else if (key.return) {
25
+ const chosen = models[index];
26
+ if (chosen) {
27
+ onSelect(chosen.id);
28
+ }
29
+ }
30
+ });
31
+ const rows = models.map((model) => cells(model, recommended));
32
+ const widths = HEADER.map((cell, column) => Math.max(cell.length, ...rows.map((row) => row[column]?.length ?? 0)));
33
+ const line = (row) => row
34
+ .map((cell, column) => (column === 0 || column === 4 ? cell.padEnd(widths[column] ?? 0) : cell.padStart(widths[column] ?? 0)))
35
+ .join(" ");
36
+ return (_jsx(Box, { justifyContent: "center", marginBottom: 1, children: _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: MUTED, children: ` ${line(HEADER)}` }), rows.map((row, position) => {
37
+ const active = position === index;
38
+ return (_jsxs(Text, { bold: active, color: active ? BRAND : undefined, children: [active ? "▸ " : " ", line(row)] }, row[0]));
39
+ })] }) }));
17
40
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boundless.network/setup",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Set up Boundless as the inference provider for your coding harness. Run: npx -y @boundless.network/setup@latest",
5
5
  "license": "MIT",
6
6
  "type": "module",