laohuang 0.8.2 → 0.8.3

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 CHANGED
@@ -29,8 +29,15 @@ Google Vertex, OAuth-only providers, and OpenAI Codex. Use `/providers` to see
29
29
  available, configured, and verified status independently.
30
30
 
31
31
  `/login <provider>` stores API-key credentials, `/logout <provider>` removes
32
- them, `/model <provider> <model>` switches the session, and `/apikey` remains a
33
- compatibility alias. `verified` means an explicitly authorized live native
32
+ them, and `/model` opens a searchable list of all available models from
33
+ configured providers, including credentials supplied through environment variables.
34
+ Search by provider ID, model ID, or model name; scroll to browse the full list.
35
+ If no models are available, the CLI points to `/login`.
36
+ `/model <provider>` narrows the list, `/model <provider> <model>` switches
37
+ directly, and `/apikey` remains a compatibility alias. Switching models affects
38
+ only the current session, not the default profile.
39
+
40
+ `verified` means an explicitly authorized live native
34
41
  tool-call/tool-result E2E was recorded; no providers are checked in as verified
35
42
  by default.
36
43
 
package/dist/bin.js CHANGED
@@ -10022,7 +10022,7 @@ function renderMarkdownStyledLines(text, width) {
10022
10022
  const layoutWidth = Math.max(12, width);
10023
10023
  const lines2 = [];
10024
10024
  let previous = null;
10025
- for (const block of renderBlocks(clean, layoutWidth)) {
10025
+ for (const block of renderBlocks(clean, layoutWidth, Math.max(1, width))) {
10026
10026
  const leadingBlank = previous === null ? block.kind === "list" || block.kind === "table" || block.kind === "quote" : previous !== "hr";
10027
10027
  if (leadingBlank) {
10028
10028
  lines2.push(line());
@@ -10046,7 +10046,7 @@ function styleKey(style) {
10046
10046
  style.background ?? ""
10047
10047
  ].join("|");
10048
10048
  }
10049
- function renderBlocks(text, layoutWidth) {
10049
+ function renderBlocks(text, layoutWidth, tableWidth) {
10050
10050
  const lines2 = text.replace(/\r\n/g, "\n").split("\n");
10051
10051
  const blocks = [];
10052
10052
  let paragraph = [];
@@ -10094,6 +10094,7 @@ function renderBlocks(text, layoutWidth) {
10094
10094
  const delimiter = splitTableRow(lines2[index + 1].trim());
10095
10095
  if (header !== null && delimiter !== null && delimiter.length === header.length && delimiter.every((cell) => /^:?-+:?$/.test(cell))) {
10096
10096
  flushParagraph();
10097
+ const tableStart = index;
10097
10098
  index += 2;
10098
10099
  const rows = [];
10099
10100
  while (index < lines2.length) {
@@ -10110,7 +10111,7 @@ function renderBlocks(text, layoutWidth) {
10110
10111
  }
10111
10112
  blocks.push({
10112
10113
  kind: "table",
10113
- lines: renderTable(header, rows, layoutWidth)
10114
+ lines: renderTable(header, rows, tableWidth, lines2.slice(tableStart, index))
10114
10115
  });
10115
10116
  continue;
10116
10117
  }
@@ -10249,7 +10250,7 @@ function segmentsWidth(segments) {
10249
10250
  }
10250
10251
  return width;
10251
10252
  }
10252
- function renderTable(header, rows, layoutWidth) {
10253
+ function renderTable(header, rows, layoutWidth, rawLines) {
10253
10254
  const columnCount = header.length;
10254
10255
  const borderStyle = {
10255
10256
  foreground: "border_muted"
@@ -10261,12 +10262,26 @@ function renderTable(header, rows, layoutWidth) {
10261
10262
  const normalize = (cells) => Array.from({ length: columnCount }, (_, i) => cells[i] ?? "");
10262
10263
  const headerCells = normalize(header).map((cell) => parseInline(cell, headerStyle));
10263
10264
  const bodyCells = rows.map((row) => normalize(row).map((cell) => parseInline(cell, {})));
10265
+ const minWidths = Array(columnCount).fill(1);
10266
+ for (const row of [headerCells, ...bodyCells]) {
10267
+ for (let i = 0; i < columnCount; i += 1) {
10268
+ for (const segment of row[i]) {
10269
+ for (const cluster of graphemeClusters(segment.text)) {
10270
+ minWidths[i] = Math.max(minWidths[i], clusterWidth(cluster));
10271
+ }
10272
+ }
10273
+ }
10274
+ }
10275
+ const spacingWidth = 2 * columnCount;
10276
+ if (minWidths.reduce((total, width) => total + width, spacingWidth) > layoutWidth) {
10277
+ return rawLines.flatMap((text) => wrapSegments([{ text, style: {} }], layoutWidth));
10278
+ }
10264
10279
  const widths = Array.from({ length: columnCount }, (_, i) => Math.max(1, segmentsWidth(headerCells[i]), ...bodyCells.map((row) => segmentsWidth(row[i]))));
10265
10280
  const totalWidth = () => widths.reduce((total, w) => total + w, 0) + 2 * (columnCount - 1) + 2;
10266
- while (totalWidth() > layoutWidth && Math.max(...widths) > 1) {
10267
- let widest = 0;
10268
- for (let i = 1; i < widths.length; i += 1) {
10269
- if (widths[i] > widths[widest]) {
10281
+ while (totalWidth() > layoutWidth) {
10282
+ let widest = -1;
10283
+ for (let i = 0; i < widths.length; i += 1) {
10284
+ if (widths[i] > minWidths[i] && (widest === -1 || widths[i] > widths[widest])) {
10270
10285
  widest = i;
10271
10286
  }
10272
10287
  }
@@ -10296,7 +10311,7 @@ function renderTableRows(cells, widths, borderStyle, padStyle) {
10296
10311
  if (i > 0) {
10297
10312
  line2.push({ style: borderStyle, text: " " });
10298
10313
  }
10299
- const cellLine = wrappedCells[i][row];
10314
+ const cellLine = wrappedCells[i][row] ?? [];
10300
10315
  line2.push(...cellLine);
10301
10316
  const pad = widths[i] - segmentsWidth(cellLine);
10302
10317
  if (pad > 0) {
@@ -14737,7 +14752,7 @@ var SessionCommands = class {
14737
14752
  this.registry = new CommandRegistry([
14738
14753
  {
14739
14754
  name: "/model",
14740
- description: "\u9009\u62E9\u5F53\u524D\u4F9B\u5E94\u5546\u7684\u6A21\u578B\u6216\u5207\u6362\u4F9B\u5E94\u5546",
14755
+ description: "\u641C\u7D22\u5E76\u9009\u62E9\u5DF2\u914D\u7F6E\u4F9B\u5E94\u5546\u7684\u6A21\u578B",
14741
14756
  usage: "/model [provider|model] [model]",
14742
14757
  handler: (args) => this.handleModel(args),
14743
14758
  allowedStates: IDLE_ONLY,
@@ -15124,16 +15139,10 @@ ${sessionDisplayDescription(session, {
15124
15139
  }
15125
15140
  let provider;
15126
15141
  let modelName;
15127
- if (args.length === 0) {
15128
- const selectedProvider = await this.selectModelProvider();
15129
- if (selectedProvider === null) {
15130
- return true;
15131
- }
15132
- provider = selectedProvider;
15133
- } else if (args.length === 2) {
15142
+ if (args.length === 2) {
15134
15143
  provider = args[0];
15135
15144
  modelName = args[1];
15136
- } else {
15145
+ } else if (args.length === 1) {
15137
15146
  const argument = args[0];
15138
15147
  if (this.#catalog.getProvider(argument) !== void 0) {
15139
15148
  provider = argument;
@@ -15144,9 +15153,24 @@ ${sessionDisplayDescription(session, {
15144
15153
  }
15145
15154
  if (modelName === void 0) {
15146
15155
  try {
15147
- const models = await this.#selector.listModels(provider, "");
15156
+ let models;
15157
+ if (provider === void 0) {
15158
+ const available = await this.#selector.listConfiguredModels();
15159
+ models = available.models;
15160
+ for (const failure of available.errors) {
15161
+ this.notice(
15162
+ `Could not list models for ${failure.provider}: ${errorMessage10(failure.error)}`,
15163
+ "warning"
15164
+ );
15165
+ }
15166
+ } else {
15167
+ models = await this.#selector.listModels(provider, "");
15168
+ }
15148
15169
  if (models.length === 0) {
15149
- this.notice(`No models available for provider: ${provider}`, "error");
15170
+ this.notice(
15171
+ provider === void 0 ? "No models available from configured providers. Use /login to configure a provider." : `No models available for provider: ${provider}. Use /login ${provider} to configure credentials.`,
15172
+ "warning"
15173
+ );
15150
15174
  return true;
15151
15175
  }
15152
15176
  if (this.#presenter === null) {
@@ -15155,26 +15179,34 @@ ${sessionDisplayDescription(session, {
15155
15179
  }
15156
15180
  const selected = await this.#presenter.select({
15157
15181
  id: "model-name",
15158
- title: `Select model for ${provider}`,
15159
- items: models.map((model) => ({
15160
- value: `${provider}/${model.id}`,
15161
- label: model.name,
15162
- description: provider
15182
+ title: provider === void 0 ? "Select model from configured providers" : `Select model for ${provider}`,
15183
+ items: models.map((model2) => ({
15184
+ value: `${model2.provider}/${model2.id}`,
15185
+ label: model2.name,
15186
+ description: model2.provider
15163
15187
  })),
15164
- currentValue: this.#currentConfig.provider === provider ? `${provider}/${this.#currentConfig.model}` : void 0,
15188
+ currentValue: `${this.#currentConfig.provider}/${this.#currentConfig.model}`,
15165
15189
  searchable: true,
15166
- maxVisible: 20
15190
+ maxVisible: 10
15167
15191
  });
15168
15192
  if (selected === null) {
15169
15193
  return true;
15170
15194
  }
15171
- const prefix = `${provider}/`;
15172
- modelName = selected.startsWith(prefix) ? selected.slice(prefix.length) : selected;
15195
+ const model = models.find((candidate) => `${candidate.provider}/${candidate.id}` === selected);
15196
+ if (model === void 0) {
15197
+ this.notice("Selected model is no longer available. Run /model to refresh the list.", "error");
15198
+ return true;
15199
+ }
15200
+ provider = model.provider;
15201
+ modelName = model.id;
15173
15202
  } catch (error) {
15174
15203
  this.notice(`Could not list models: ${errorMessage10(error)}`, "error");
15175
15204
  return true;
15176
15205
  }
15177
15206
  }
15207
+ if (provider === void 0) {
15208
+ return true;
15209
+ }
15178
15210
  let selection;
15179
15211
  try {
15180
15212
  selection = await this.#selector.selectExact({
@@ -15187,6 +15219,7 @@ ${sessionDisplayDescription(session, {
15187
15219
  return true;
15188
15220
  }
15189
15221
  if (selection === null) {
15222
+ this.notice(`Use /login ${provider} to configure credentials before switching models.`, "warning");
15190
15223
  return true;
15191
15224
  }
15192
15225
  const previousProvider = this.#currentConfig.provider;
@@ -15445,23 +15478,6 @@ ${sessionDisplayDescription(session, {
15445
15478
  )
15446
15479
  };
15447
15480
  }
15448
- async selectModelProvider() {
15449
- if (this.#presenter === null) {
15450
- this.notice("Model provider selection is unavailable.", "error");
15451
- return null;
15452
- }
15453
- const selected = await this.#presenter.select({
15454
- id: "model-provider",
15455
- title: "Select model provider",
15456
- items: this.#selector.listProviders().map((provider) => ({
15457
- value: provider.id,
15458
- label: provider.name,
15459
- description: provider.id
15460
- })),
15461
- currentValue: this.#currentConfig.provider
15462
- });
15463
- return selected;
15464
- }
15465
15481
  *modelCompletions(args) {
15466
15482
  if (args.length === 0) {
15467
15483
  yield ["current", "\u663E\u793A\u5F53\u524D\u6A21\u578B"];
@@ -15578,12 +15594,30 @@ var ModelSelector = class {
15578
15594
  return this.#catalog.listProviders();
15579
15595
  }
15580
15596
  async listModels(provider, query) {
15597
+ if (!await this.#providerAuth.ensureConfigured(provider, { promptIfMissing: false })) {
15598
+ return [];
15599
+ }
15581
15600
  await this.#catalog.refresh(provider);
15582
15601
  return filterModels(
15583
15602
  await this.#catalog.listAvailableModels(provider),
15584
- query,
15585
- 20
15603
+ query
15604
+ );
15605
+ }
15606
+ async listConfiguredModels() {
15607
+ const providers = this.#catalog.listProviders();
15608
+ const results = await Promise.allSettled(
15609
+ providers.map((provider) => this.listModels(provider.id, ""))
15586
15610
  );
15611
+ const models = [];
15612
+ const errors = [];
15613
+ for (const [index, result] of results.entries()) {
15614
+ if (result.status === "fulfilled") {
15615
+ models.push(...result.value);
15616
+ } else {
15617
+ errors.push({ provider: providers[index].id, error: result.reason });
15618
+ }
15619
+ }
15620
+ return { models: filterModels(models, ""), errors };
15587
15621
  }
15588
15622
  async selectExact(options) {
15589
15623
  const provider = this.#catalog.getProvider(options.providerName);
@@ -15615,7 +15649,7 @@ var ModelSelector = class {
15615
15649
  };
15616
15650
  }
15617
15651
  };
15618
- function filterModels(models, query, limit = 20) {
15652
+ function filterModels(models, query) {
15619
15653
  const normalized = query.toLowerCase().trim();
15620
15654
  const terms = normalized.split(/\s+/).filter((term) => term.length > 0);
15621
15655
  const matched = models.filter((model) => {
@@ -15639,9 +15673,9 @@ function filterModels(models, query, limit = 20) {
15639
15673
  if (leftPrefix !== rightPrefix) {
15640
15674
  return leftPrefix ? -1 : 1;
15641
15675
  }
15642
- return left.id.localeCompare(right.id);
15676
+ return left.provider.localeCompare(right.provider) || left.id.localeCompare(right.id);
15643
15677
  });
15644
- return matched.slice(0, limit);
15678
+ return matched;
15645
15679
  }
15646
15680
 
15647
15681
  // src/plain-command-presenter.ts
@@ -17323,6 +17357,10 @@ async function main(argv, options = {}) {
17323
17357
  await sessionRecorder.close();
17324
17358
  await sessionController.close();
17325
17359
  }
17360
+ const renderError = terminalUi?.renderError ?? null;
17361
+ if (renderError !== null) {
17362
+ throw new Error(`Terminal rendering failed: ${errorMessage11(renderError)}`, { cause: renderError });
17363
+ }
17326
17364
  return cleanShutdown ? 0 : 1;
17327
17365
  }
17328
17366
  function defaultSessionsRoot(environ) {