@botconnector/bccli 0.4.1 → 0.4.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.
Files changed (69) hide show
  1. package/dist/{acp-3PHBAI64.js → acp-UYC6APR5.js} +30 -6
  2. package/dist/acp.d.ts +1 -0
  3. package/dist/agent.d.ts +5 -1
  4. package/dist/args-NRTZWWEV.js +13 -0
  5. package/dist/args.d.ts +2 -0
  6. package/dist/checkpoints.d.ts +1 -0
  7. package/dist/{chunk-BKGFA2PT.js → chunk-266SY3MC.js} +1 -1
  8. package/dist/{chunk-MFHCQQEN.js → chunk-3I5BW3ZT.js} +3 -2
  9. package/dist/{chunk-UZWJQF67.js → chunk-3WHI4LN3.js} +2 -1
  10. package/dist/{chunk-WNACAUDK.js → chunk-5YJ3ZQVR.js} +66 -9
  11. package/dist/{chunk-24JN75FZ.js → chunk-6M55UPES.js} +2 -2
  12. package/dist/{chunk-XV7ZVL4J.js → chunk-6UO5GX55.js} +26 -12
  13. package/dist/{chunk-R5RE46CW.js → chunk-C64KM37J.js} +36 -6
  14. package/dist/{chunk-GN4W3T6E.js → chunk-H7XJDKJY.js} +4 -4
  15. package/dist/{chunk-JHPGOAXU.js → chunk-HQW4BROS.js} +4 -5
  16. package/dist/{chunk-75BECX4F.js → chunk-IYR5PYA3.js} +4 -4
  17. package/dist/{chunk-45SDLMTV.js → chunk-JXM6REER.js} +1 -1
  18. package/dist/{chunk-BLOYFIAD.js → chunk-KPTML72Q.js} +1 -1
  19. package/dist/{chunk-WI22M2ID.js → chunk-NQOGVP3I.js} +2 -2
  20. package/dist/{chunk-QZKLZELZ.js → chunk-OK2USAXX.js} +28 -4
  21. package/dist/{chunk-PZRKU4UM.js → chunk-R72OV7RF.js} +421 -108
  22. package/dist/{chunk-FLQPE4S5.js → chunk-RX4UQX43.js} +4 -1
  23. package/dist/{chunk-BC7UXFE4.js → chunk-SVD4LHJC.js} +64 -3
  24. package/dist/{chunk-BXQQCUAC.js → chunk-TIMSYZIR.js} +7 -7
  25. package/dist/{chunk-MHIYLXJC.js → chunk-WFZKJY3R.js} +8 -4
  26. package/dist/{chunk-BGXNR74O.js → chunk-ZEPTUKNW.js} +119 -31
  27. package/dist/cli.js +60 -43
  28. package/dist/{completion-HPPFDAPB.js → completion-D2BSBTQO.js} +1 -1
  29. package/dist/{config-DIDLDONU.js → config-ENDRMOTP.js} +3 -3
  30. package/dist/{config-IU76CPK2.js → config-LAINORU5.js} +2 -2
  31. package/dist/config.d.ts +2 -0
  32. package/dist/{ghBotCli-E3VPHNZY.js → ghBotCli-TSLRORJJ.js} +21 -20
  33. package/dist/{integrationCli-Q2YPDTPG.js → integrationCli-HXCSEWH2.js} +2 -2
  34. package/dist/{login-FP3BB44A.js → login-RYESFZG6.js} +4 -4
  35. package/dist/lsp.d.ts +3 -0
  36. package/dist/mcp/manager.d.ts +18 -0
  37. package/dist/{mcpCli-SNBDUWJN.js → mcpCli-SETTCL3K.js} +6 -6
  38. package/dist/{modelsCli-CZ2HNGKR.js → modelsCli-OP7CDZC7.js} +6 -6
  39. package/dist/{oauthLogin-6FWQABJ7.js → oauthLogin-XWY7OHCG.js} +1 -1
  40. package/dist/{pluginsCli-LPIUUDWB.js → pluginsCli-ZQNKWZHJ.js} +2 -2
  41. package/dist/{prCli-5IMV3VNQ.js → prCli-6PUWTMTP.js} +20 -4
  42. package/dist/prCli.d.ts +13 -0
  43. package/dist/print.d.ts +2 -0
  44. package/dist/provider.d.ts +5 -0
  45. package/dist/providerCli-5OZI2GQQ.js +15 -0
  46. package/dist/sdk.d.ts +5 -1
  47. package/dist/sdk.js +733 -176
  48. package/dist/{serve-X2ZV4S5U.js → serve-4J7HA4JO.js} +17 -17
  49. package/dist/{serve-KWOECK5W.js → serve-K5AH4MGO.js} +3 -3
  50. package/dist/session-YQXCCKXI.js +12 -0
  51. package/dist/session.d.ts +3 -0
  52. package/dist/setup-S4AJT6XE.js +24 -0
  53. package/dist/slash/info.d.ts +1 -0
  54. package/dist/{statsCli-CF3R7ATL.js → statsCli-XR6BEZQQ.js} +2 -2
  55. package/dist/tokenBudget.d.ts +1 -1
  56. package/dist/tools/edit.d.ts +7 -2
  57. package/dist/tools/grep.d.ts +14 -2
  58. package/dist/tools/ignore.d.ts +2 -2
  59. package/dist/tools/paths.d.ts +2 -0
  60. package/dist/{transferCli-YUFPI6AH.js → transferCli-R2KEIWQR.js} +8 -6
  61. package/dist/ui/complete.d.ts +5 -0
  62. package/dist/ui/vim.d.ts +1 -1
  63. package/dist/{ui-IVIFNUCC.js → ui-36ZEK65V.js} +177 -54
  64. package/dist/{update-TZ2G6KQ3.js → update-FSS7X5WV.js} +2 -2
  65. package/package.json +1 -1
  66. package/dist/args-W6BNKY7K.js +0 -13
  67. package/dist/providerCli-NMUFXAAS.js +0 -15
  68. package/dist/session-GKEXDJKZ.js +0 -12
  69. package/dist/setup-OV3NWQN5.js +0 -24
@@ -36,6 +36,9 @@ function runAcp(input, output, opts) {
36
36
  const replyError = (id, code, message) => write({ jsonrpc: "2.0", id, error: { code, message } });
37
37
  const update = (sessionId, body) => write({ jsonrpc: "2.0", method: "session/update", params: { sessionId, update: body } });
38
38
  const ioFor = (sid, session) => ({
39
+ onThinking(delta) {
40
+ update(sid, { sessionUpdate: "agent_thought_chunk", content: { type: "text", text: delta } });
41
+ },
39
42
  toolStart(id, tool, target) {
40
43
  session.open.push({ id, tool, target });
41
44
  update(sid, { sessionUpdate: "tool_call", toolCallId: id, title: titleOf(tool, target), kind: kindOf(tool), status: "in_progress", rawInput: { target } });
@@ -89,7 +92,7 @@ function runAcp(input, output, opts) {
89
92
  protocolVersion: ACP_PROTOCOL_VERSION,
90
93
  agentCapabilities: {
91
94
  loadSession: false,
92
- promptCapabilities: { image: false, audio: false, embeddedContext: false }
95
+ promptCapabilities: { image: false, audio: false, embeddedContext: true }
93
96
  }
94
97
  });
95
98
  }
@@ -108,7 +111,17 @@ function runAcp(input, output, opts) {
108
111
  const sid = params?.sessionId ?? "";
109
112
  const session = sessions.get(sid);
110
113
  if (!session) return replyError(id, -32602, "Unknown session");
111
- const text = (params?.prompt ?? []).filter((p) => p?.type === "text").map((p) => String(p.text ?? "")).join("\n");
114
+ if (session.controller) return replyError(id, -32e3, "Session is busy");
115
+ const text = (params?.prompt ?? []).map((p) => {
116
+ if (p?.type === "text") return String(p.text ?? "");
117
+ if ((p?.type === "resource" || p?.type === "embeddedContext") && p.resource?.text) {
118
+ const label = p.resource.uri ? `
119
+ [${p.resource.uri}]
120
+ ` : "\n";
121
+ return `${label}${p.resource.text}`;
122
+ }
123
+ return "";
124
+ }).filter(Boolean).join("\n");
112
125
  const controller = new AbortController();
113
126
  session.controller = controller;
114
127
  let cancelled = false;
@@ -122,8 +135,10 @@ function runAcp(input, output, opts) {
122
135
  if (cancelled) return reply(id, { stopReason: "cancelled" });
123
136
  return replyError(id, -32603, e.message);
124
137
  } finally {
125
- session.controller = null;
126
- session.open = [];
138
+ if (session.controller === controller) {
139
+ session.controller = null;
140
+ session.open = [];
141
+ }
127
142
  }
128
143
  return reply(id, { stopReason: cancelled ? "cancelled" : "end_turn" });
129
144
  }
@@ -150,6 +165,10 @@ function runAcp(input, output, opts) {
150
165
  };
151
166
  const onEnd = () => {
152
167
  input.removeListener("data", onData);
168
+ for (const session of sessions.values()) {
169
+ session.controller?.abort();
170
+ for (const settle of [...session.pending]) settle("no");
171
+ }
153
172
  resolve();
154
173
  };
155
174
  input.on("data", onData);
@@ -161,17 +180,22 @@ function runtimeAgent(cwd, allowAll = false) {
161
180
  return {
162
181
  async run(text, onChunk, signal, io) {
163
182
  if (!rt) {
164
- const { parseCliArgs } = await import("./args-W6BNKY7K.js");
165
- const { createRuntime } = await import("./setup-OV3NWQN5.js");
183
+ const { parseCliArgs } = await import("./args-NRTZWWEV.js");
184
+ const { createRuntime } = await import("./setup-S4AJT6XE.js");
166
185
  rt = createRuntime({ cwd, args: { ...parseCliArgs([]), allowAll } });
167
186
  }
168
187
  if (io && !allowAll) rt.agent.askPermission = (req) => io.askPermission(req);
188
+ let turnError;
169
189
  rt.agent.onEvent = (event) => {
170
190
  if (event.type === "text") onChunk(event.delta);
191
+ else if (event.type === "thinking") io?.onThinking?.(event.delta);
171
192
  else if (event.type === "toolStart") io?.toolStart(event.id, event.tool, event.target);
172
193
  else if (event.type === "toolEnd") io?.toolEnd(event.id, event.output, event.isError);
194
+ else if (event.type === "error") turnError = event.message;
195
+ else if (event.type === "budgetExceeded") turnError = `Budget exceeded (${event.kind})`;
173
196
  };
174
197
  await rt.agent.run(text, signal);
198
+ if (turnError) throw new Error(turnError);
175
199
  }
176
200
  };
177
201
  }
package/dist/acp.d.ts CHANGED
@@ -5,6 +5,7 @@ export interface AcpIo {
5
5
  toolStart(id: string, tool: string, target: string): void;
6
6
  toolEnd(id: string, output: string, isError: boolean): void;
7
7
  askPermission(req: PermissionAsk): Promise<PermissionAnswer>;
8
+ onThinking?(delta: string): void;
8
9
  }
9
10
  /** The minimum an ACP client needs: run one prompt, stream text chunks, allow cancellation. */
10
11
  export interface AcpAgent {
package/dist/agent.d.ts CHANGED
@@ -109,14 +109,18 @@ export declare class Agent {
109
109
  private definitions;
110
110
  private turnTools;
111
111
  private turnDefinitions;
112
- private readonly readFiles;
112
+ readonly readFiles: Set<string>;
113
113
  private readonly webBudget;
114
114
  private readonly maxSteps?;
115
115
  private contextWindow;
116
+ private budgetModel;
116
117
  private systemPrompt;
117
118
  constructor(opts: AgentOptions);
118
119
  setSystemPrompt(text: string): void;
119
120
  setContextWindow(window: number): void;
121
+ getContextWindow(): number;
122
+ setBudgetModel(model: string): void;
123
+ getBudgetModel(): string;
120
124
  setTools(tools: Tool[]): void;
121
125
  private isParallelSafe;
122
126
  private push;
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ helpText,
4
+ parseCliArgs
5
+ } from "./chunk-6UO5GX55.js";
6
+ import "./chunk-WFZKJY3R.js";
7
+ import "./chunk-KPTML72Q.js";
8
+ import "./chunk-RX4UQX43.js";
9
+ import "./chunk-3WHI4LN3.js";
10
+ export {
11
+ helpText,
12
+ parseCliArgs
13
+ };
package/dist/args.d.ts CHANGED
@@ -45,6 +45,8 @@ export interface CliArgs {
45
45
  project?: string;
46
46
  /** `bccli pr` — explicit owner/name repo when cwd is not a git checkout of the PR. */
47
47
  repo?: string;
48
+ /** Automatic fallback model ref when the primary model fails with 429 or 5xx. */
49
+ fallbackModel?: string;
48
50
  }
49
51
  /** Help text in the current language (call it after the language is set). */
50
52
  export declare const helpText: () => string;
@@ -8,6 +8,7 @@ export declare class CheckpointStore {
8
8
  private readonly history;
9
9
  private current;
10
10
  private pendingRedo;
11
+ private offset;
11
12
  beginTurn(): void;
12
13
  /** Call before writing a file; only the first snapshot per file per turn is kept. */
13
14
  snapshot(path: string): Promise<void>;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/version.ts
4
- var VERSION = true ? "0.4.1" : "0.0.0-dev";
4
+ var VERSION = true ? "0.4.2" : "0.0.0-dev";
5
5
 
6
6
  export {
7
7
  VERSION
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  quarantineCorruptFile,
4
4
  writeFileAtomic
5
- } from "./chunk-FLQPE4S5.js";
5
+ } from "./chunk-RX4UQX43.js";
6
6
 
7
7
  // src/mcp/oauth.ts
8
8
  import { spawn } from "child_process";
@@ -156,8 +156,9 @@ async function authorizeMcpServer(spec, deps) {
156
156
  res.end("<html><body><p>BCCLI is connected. You can close this tab.</p></body></html>");
157
157
  settle.resolve(code);
158
158
  } catch (error) {
159
+ const escaped = error.message.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
159
160
  res.writeHead(400, { "content-type": "text/html; charset=utf-8" });
160
- res.end(`<html><body><p>${error.message}</p></body></html>`);
161
+ res.end(`<html><body><p>${escaped}</p></body></html>`);
161
162
  settle.reject(error);
162
163
  }
163
164
  });
@@ -458,7 +458,8 @@ Opsi:
458
458
  "gh is not available ({error}); install GitHub CLI first": "gh tidak tersedia ({error}); pasang GitHub CLI dulu",
459
459
  "gh pr checkout {n} failed (exit {code})": "gh pr checkout {n} gagal (exit {code})",
460
460
  "checked out PR #{n}": "checkout PR #{n} selesai",
461
- "--allow-all: the PR task runs every tool without asking": "--allow-all: tugas PR menjalankan semua alat tanpa bertanya"
461
+ "--allow-all: the PR task runs every tool without asking": "--allow-all: tugas PR menjalankan semua alat tanpa bertanya",
462
+ "view or change configuration settings": "lihat atau ubah pengaturan konfigurasi"
462
463
  };
463
464
 
464
465
  // src/i18n.ts
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  reasoningPayload
4
- } from "./chunk-BLOYFIAD.js";
4
+ } from "./chunk-KPTML72Q.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-UZWJQF67.js";
7
+ } from "./chunk-3WHI4LN3.js";
8
8
 
9
9
  // src/provider.ts
10
10
  import OpenAI from "openai";
@@ -171,7 +171,7 @@ async function readStream(res, onText, onThinking) {
171
171
  }
172
172
  current = target;
173
173
  if (tc.id) target.id = tc.id;
174
- if (tc.function?.name && !target.name) target.name = tc.function.name;
174
+ if (tc.function?.name && target.name !== tc.function.name) target.name += tc.function.name;
175
175
  if (tc.function?.arguments) target.arguments += tc.function.arguments;
176
176
  }
177
177
  };
@@ -378,7 +378,16 @@ function responsesFinishReason(items, toolCallCount, incompleteReason) {
378
378
  function hasPendingProgrammaticReplay(messages) {
379
379
  let pending = false;
380
380
  for (const message of messages) {
381
+ if (message.role === "user" && typeof message.content === "string") {
382
+ pending = false;
383
+ continue;
384
+ }
385
+ if (message.role === "tool" && message.content === "Cancelled by the user.") {
386
+ pending = false;
387
+ continue;
388
+ }
381
389
  if (message.role !== "assistant") continue;
390
+ if (message.content) pending = false;
382
391
  for (const item of message.responses_output_items ?? []) {
383
392
  if (item.type === "program") pending = true;
384
393
  else if (item.type === "message" && pending) pending = false;
@@ -452,7 +461,7 @@ async function readResponsesStream(res, onText, onThinking) {
452
461
  calls.set(index, {
453
462
  id: String(event.item.call_id || current?.id || event.item.id || `call_${index}`),
454
463
  name: String(event.item.name || current?.name || ""),
455
- arguments: String(event.item.arguments ?? current?.arguments ?? ""),
464
+ arguments: String(event.item.arguments || current?.arguments || ""),
456
465
  ...programCaller(event.item) ?? current?.caller ? { caller: programCaller(event.item) ?? current?.caller } : {}
457
466
  });
458
467
  }
@@ -688,7 +697,7 @@ async function readResponsesWebSocketTurn(events, ws, onText, onThinking, signal
688
697
  calls.set(index, {
689
698
  id: String(event.item.call_id || current?.id || event.item.id || `call_${index}`),
690
699
  name: String(event.item.name || current?.name || ""),
691
- arguments: String(event.item.arguments ?? current?.arguments ?? ""),
700
+ arguments: String(event.item.arguments || current?.arguments || ""),
692
701
  ...programCaller(event.item) ?? current?.caller ? { caller: programCaller(event.item) ?? current?.caller } : {}
693
702
  });
694
703
  }
@@ -749,7 +758,7 @@ function createProvider(options) {
749
758
  const isBotConnectorCloud = /^https:\/\/api\.botconnector\.id\/v1\/?$/.test(baseURL) && apiKey?.startsWith("bc_live_");
750
759
  if (isBotConnectorCloud) {
751
760
  headers["x-botconnector-client"] = "bccli";
752
- headers["x-botconnector-client-version"] = "0.4.1";
761
+ headers["x-botconnector-client-version"] = "0.4.2";
753
762
  }
754
763
  const useNativeResponses = isBotConnectorCloud && providerId === "bc-cloud" && model === "gpt-6-luna";
755
764
  const useResponsesWebSocket = useNativeResponses && Boolean(apiKey) && (fetchOverride === void 0 || responsesWebSocketFactory !== void 0);
@@ -780,7 +789,7 @@ function createProvider(options) {
780
789
  baseURL: baseURL.replace(/\/+$/, ""),
781
790
  defaultHeaders: {
782
791
  "x-botconnector-client": "bccli",
783
- "x-botconnector-client-version": "0.4.1"
792
+ "x-botconnector-client-version": "0.4.2"
784
793
  }
785
794
  });
786
795
  lunaWs = responsesWebSocketFactory ? responsesWebSocketFactory(client) : new ResponsesWS(client, { reconnect: null });
@@ -863,8 +872,13 @@ function createProvider(options) {
863
872
  }
864
873
  async function post(path, body, signal) {
865
874
  let lastError;
875
+ let retryAfterWaitMs;
866
876
  for (let attempt = 0; attempt < 4; attempt++) {
867
- if (attempt > 0) await sleep(retryDelayMs * 2 ** (attempt - 1), signal);
877
+ if (attempt > 0) {
878
+ const delay = retryAfterWaitMs ?? retryDelayMs * 2 ** (attempt - 1);
879
+ retryAfterWaitMs = void 0;
880
+ await sleep(delay, signal);
881
+ }
868
882
  let res;
869
883
  try {
870
884
  res = await doFetch(`${baseURL}${path}`, { method: "POST", headers, body: JSON.stringify(body), signal });
@@ -874,6 +888,11 @@ function createProvider(options) {
874
888
  continue;
875
889
  }
876
890
  if (res.ok) return res;
891
+ const retryHeader = res.headers.get("retry-after");
892
+ if (retryHeader) {
893
+ const secs = Number(retryHeader);
894
+ if (Number.isFinite(secs) && secs > 0 && secs <= 30) retryAfterWaitMs = Math.ceil(secs * 1e3);
895
+ }
877
896
  const text = await res.text().catch(() => "");
878
897
  lastError = new ProviderError(t("{status} from {url}: {message}", { status: res.status, url: baseURL, message: errorMessage(text) }), res.status);
879
898
  const deterministicQuota429 = res.status === 429 && /"code"\s*:\s*"(?:luna_monthly_quota_exhausted|plan_allowance_exhausted)"/i.test(text);
@@ -936,11 +955,49 @@ function createProvider(options) {
936
955
  }
937
956
  };
938
957
  }
958
+ function withFallbackProvider(primary, fallbacks) {
959
+ return {
960
+ async chat(req) {
961
+ let emitted = false;
962
+ const wrappedReq = {
963
+ ...req,
964
+ onText: req.onText ? (delta) => {
965
+ emitted = true;
966
+ req.onText?.(delta);
967
+ } : () => {
968
+ emitted = true;
969
+ }
970
+ };
971
+ try {
972
+ return await primary.chat(wrappedReq);
973
+ } catch (primaryError) {
974
+ if (emitted || req.signal?.aborted) throw primaryError;
975
+ const status = primaryError instanceof ProviderError ? primaryError.status : void 0;
976
+ const canFallback = status === void 0 || status === 429 || status === 408 || status >= 500;
977
+ if (!canFallback) throw primaryError;
978
+ const chain = fallbacks();
979
+ if (!chain.length) throw primaryError;
980
+ let lastError = primaryError;
981
+ for (const fb of chain) {
982
+ if (req.signal?.aborted) throw lastError;
983
+ try {
984
+ return await fb.chat(req);
985
+ } catch (fbError) {
986
+ lastError = fbError;
987
+ }
988
+ }
989
+ throw lastError;
990
+ }
991
+ },
992
+ listModels: () => primary.listModels()
993
+ };
994
+ }
939
995
 
940
996
  export {
941
997
  contentText,
942
998
  imageUserMessage,
943
999
  hasPendingProgrammaticReplay,
944
1000
  completionAssistantMessage,
945
- createProvider
1001
+ createProvider,
1002
+ withFallbackProvider
946
1003
  };
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  ConfigError,
4
4
  writeFileAtomic
5
- } from "./chunk-FLQPE4S5.js";
5
+ } from "./chunk-RX4UQX43.js";
6
6
  import {
7
7
  t
8
- } from "./chunk-UZWJQF67.js";
8
+ } from "./chunk-3WHI4LN3.js";
9
9
 
10
10
  // src/mcp/config.ts
11
11
  import { createHash } from "crypto";
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  MODE_ORDER
4
- } from "./chunk-MHIYLXJC.js";
4
+ } from "./chunk-WFZKJY3R.js";
5
5
  import {
6
6
  REASONING_LEVELS,
7
7
  isReasoningLevel
8
- } from "./chunk-BLOYFIAD.js";
8
+ } from "./chunk-KPTML72Q.js";
9
9
  import {
10
10
  ConfigError
11
- } from "./chunk-FLQPE4S5.js";
11
+ } from "./chunk-RX4UQX43.js";
12
12
  import {
13
13
  parseLang,
14
14
  t
15
- } from "./chunk-UZWJQF67.js";
15
+ } from "./chunk-3WHI4LN3.js";
16
16
 
17
17
  // src/args.ts
18
18
  import { parseArgs } from "util";
@@ -41,9 +41,18 @@ var LOOPBACK_BIND = /* @__PURE__ */ new Set(["127.0.0.1", "localhost", "::1"]);
41
41
  function trustFence(headers, port) {
42
42
  const host = headers.host;
43
43
  if (!host) return "missing Host header";
44
- const sep = host.lastIndexOf(":");
45
- const hostname = sep > -1 ? host.slice(0, sep) : host;
46
- const hostPort = sep > -1 ? Number(host.slice(sep + 1)) : 80;
44
+ let hostname;
45
+ let hostPort;
46
+ if (host.startsWith("[")) {
47
+ const close = host.indexOf("]");
48
+ if (close === -1) return `non-loopback Host: ${host}`;
49
+ hostname = host.slice(0, close + 1);
50
+ hostPort = host[close + 1] === ":" ? Number(host.slice(close + 2)) : 80;
51
+ } else {
52
+ const sep = host.lastIndexOf(":");
53
+ hostname = sep > -1 ? host.slice(0, sep) : host;
54
+ hostPort = sep > -1 ? Number(host.slice(sep + 1)) : 80;
55
+ }
47
56
  if (!LOOPBACK_HOSTS.has(hostname)) return `non-loopback Host: ${hostname}`;
48
57
  if (hostPort !== port) return `Host port ${hostPort} does not match listener ${port}`;
49
58
  if (headers.origin) {
@@ -111,9 +120,12 @@ Options:
111
120
  -h, --help this help`;
112
121
  var helpText = () => t(HELP);
113
122
  function parseCliArgs(argv) {
114
- const expanded = argv.map(
115
- (a, i) => a === "--models" && (i + 1 >= argv.length || argv[i + 1].startsWith("-")) ? "--models=all" : a
116
- );
123
+ const expanded = argv.map((a, i) => {
124
+ const nextIsFlagOrEnd = i + 1 >= argv.length || argv[i + 1].startsWith("-");
125
+ if (a === "--models" && nextIsFlagOrEnd) return "--models=all";
126
+ if (a === "--project" && nextIsFlagOrEnd) return "--project=";
127
+ return a;
128
+ });
117
129
  const { values, positionals } = parseArgs({
118
130
  args: expanded,
119
131
  allowPositionals: true,
@@ -145,7 +157,8 @@ function parseCliArgs(argv) {
145
157
  tools: { type: "string" },
146
158
  models: { type: "string" },
147
159
  project: { type: "string" },
148
- repo: { type: "string" }
160
+ repo: { type: "string" },
161
+ "fallback-model": { type: "string" }
149
162
  }
150
163
  });
151
164
  const reasoning = values.reasoning;
@@ -219,7 +232,8 @@ function parseCliArgs(argv) {
219
232
  tools,
220
233
  models,
221
234
  project: values.project,
222
- repo: values.repo
235
+ repo: values.repo,
236
+ fallbackModel: values["fallback-model"]
223
237
  };
224
238
  }
225
239
 
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  runCommand,
4
4
  runHooks
5
- } from "./chunk-BGXNR74O.js";
5
+ } from "./chunk-ZEPTUKNW.js";
6
6
  import {
7
7
  ConfigError
8
- } from "./chunk-FLQPE4S5.js";
8
+ } from "./chunk-RX4UQX43.js";
9
9
  import {
10
10
  t
11
- } from "./chunk-UZWJQF67.js";
11
+ } from "./chunk-3WHI4LN3.js";
12
12
 
13
13
  // src/verify.ts
14
14
  var MAX_VERIFY_ROUNDS = 2;
@@ -40,10 +40,19 @@ function toolLabel(tool) {
40
40
  if (tool === "web_search") return "Search";
41
41
  return tool.charAt(0).toUpperCase() + tool.slice(1);
42
42
  }
43
+ function resolvePrintPrompt(prompt, stdinText) {
44
+ const cleanStdin = stdinText?.trim();
45
+ const cleanPrompt = prompt?.trim();
46
+ if (cleanStdin && cleanPrompt) return `${cleanStdin}
47
+
48
+ ${cleanPrompt}`;
49
+ return cleanPrompt || cleanStdin || void 0;
50
+ }
43
51
  async function runPrint(rt, prompt, io = { out: process.stdout, err: process.stderr }, opts = {}) {
44
52
  const format = opts.outputFormat ?? "text";
45
53
  const emit = (line) => io.out.write(`${JSON.stringify(line)}
46
54
  `);
55
+ const startedMs = Date.now();
47
56
  let result = "";
48
57
  const toolNames = /* @__PURE__ */ new Map();
49
58
  rt.interaction.approvePlan = async () => {
@@ -65,6 +74,9 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
65
74
  result += event.delta;
66
75
  }
67
76
  break;
77
+ case "textReplace":
78
+ result = event.text;
79
+ break;
68
80
  case "toolStart":
69
81
  toolNames.set(event.id, event.tool);
70
82
  io.err.write(`\u23BF ${toolLabel(event.tool)} ${event.target}
@@ -142,12 +154,29 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
142
154
  for (const warning of stop.warnings) io.err.write(`${warning}
143
155
  `);
144
156
  await rt.emitPlugins("Stop");
157
+ const durationMs = Math.max(0, Date.now() - startedMs);
145
158
  if (format === "json") {
146
- io.out.write(`${JSON.stringify({ status: failed ? "failed" : "ok", model: rt.modelRef, result })}
147
- `);
159
+ io.out.write(
160
+ `${JSON.stringify({
161
+ status: failed ? "failed" : "ok",
162
+ model: rt.modelRef,
163
+ session_id: rt.session.id,
164
+ usage: rt.agent.totalUsage,
165
+ duration_ms: durationMs,
166
+ result
167
+ })}
168
+ `
169
+ );
148
170
  } else if (format === "stream-json") {
149
171
  if (result) emit({ type: "message", text: result });
150
- emit({ type: "result", status: failed ? "failed" : "ok", model: rt.modelRef });
172
+ emit({
173
+ type: "result",
174
+ status: failed ? "failed" : "ok",
175
+ model: rt.modelRef,
176
+ session_id: rt.session.id,
177
+ usage: rt.agent.totalUsage,
178
+ duration_ms: durationMs
179
+ });
151
180
  } else if (wroteText) {
152
181
  io.out.write("\n");
153
182
  }
@@ -225,6 +254,7 @@ export {
225
254
  runVerify,
226
255
  verifyFollowup,
227
256
  toolLabel,
257
+ resolvePrintPrompt,
228
258
  runPrint,
229
259
  resolveWorktree,
230
260
  worktreeListText
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  listAllModels
4
- } from "./chunk-75BECX4F.js";
4
+ } from "./chunk-IYR5PYA3.js";
5
5
  import {
6
6
  hasKey,
7
7
  providerName,
8
8
  removeGlobalProvider,
9
9
  writeGlobalConfig
10
- } from "./chunk-JHPGOAXU.js";
10
+ } from "./chunk-HQW4BROS.js";
11
11
  import {
12
12
  PRESETS,
13
13
  loadConfig,
14
14
  saveCredential
15
- } from "./chunk-FLQPE4S5.js";
15
+ } from "./chunk-RX4UQX43.js";
16
16
  import {
17
17
  t
18
- } from "./chunk-UZWJQF67.js";
18
+ } from "./chunk-3WHI4LN3.js";
19
19
 
20
20
  // src/providerCli.ts
21
21
  async function addProviderKey(providerId, key, deps) {
@@ -4,11 +4,11 @@ import {
4
4
  bccliHome,
5
5
  readCredentials,
6
6
  readJsonConfig,
7
- removeCredential
8
- } from "./chunk-FLQPE4S5.js";
7
+ removeCredential,
8
+ writeFileAtomic
9
+ } from "./chunk-RX4UQX43.js";
9
10
 
10
11
  // src/providers.ts
11
- import { mkdirSync, writeFileSync } from "fs";
12
12
  import { join } from "path";
13
13
  function providerName(config, id) {
14
14
  return PRESETS.find((p) => p.id === id)?.name ?? config.providers[id]?.name ?? id;
@@ -24,8 +24,7 @@ function readGlobalConfigFile(env = process.env) {
24
24
  return readJsonConfig(globalPath(env));
25
25
  }
26
26
  function save(data, env) {
27
- mkdirSync(bccliHome(env), { recursive: true });
28
- writeFileSync(globalPath(env), `${JSON.stringify(data, null, 2)}
27
+ writeFileAtomic(globalPath(env), `${JSON.stringify(data, null, 2)}
29
28
  `);
30
29
  }
31
30
  function writeGlobalConfig(patch, env = process.env) {
@@ -2,17 +2,17 @@
2
2
  import {
3
3
  hasKey,
4
4
  providerName
5
- } from "./chunk-JHPGOAXU.js";
5
+ } from "./chunk-HQW4BROS.js";
6
6
  import {
7
7
  createProvider
8
- } from "./chunk-WNACAUDK.js";
8
+ } from "./chunk-5YJ3ZQVR.js";
9
9
  import {
10
10
  PRESETS,
11
11
  readCredentials
12
- } from "./chunk-FLQPE4S5.js";
12
+ } from "./chunk-RX4UQX43.js";
13
13
  import {
14
14
  t
15
- } from "./chunk-UZWJQF67.js";
15
+ } from "./chunk-3WHI4LN3.js";
16
16
 
17
17
  // src/models.ts
18
18
  function order(ids) {
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-PSO7R5AD.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-UZWJQF67.js";
7
+ } from "./chunk-3WHI4LN3.js";
8
8
 
9
9
  // src/tools/websearch.ts
10
10
  import { z } from "zod";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  t
4
- } from "./chunk-UZWJQF67.js";
4
+ } from "./chunk-3WHI4LN3.js";
5
5
 
6
6
  // src/reasoning.ts
7
7
  var REASONING_LEVELS = ["auto", "off", "low", "medium", "high", "max"];
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigError
4
- } from "./chunk-FLQPE4S5.js";
4
+ } from "./chunk-RX4UQX43.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-UZWJQF67.js";
7
+ } from "./chunk-3WHI4LN3.js";
8
8
 
9
9
  // src/mcp/catalog.ts
10
10
  var CATALOG = [