@botconnector/bccli 0.4.8 → 0.4.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.
Files changed (48) hide show
  1. package/dist/{acp-VEXFXT5P.js → acp-L4WGVJTY.js} +2 -2
  2. package/dist/{args-6XMUZDAK.js → args-EW7XJVQN.js} +4 -4
  3. package/dist/args.d.ts +6 -0
  4. package/dist/{chunk-OETLWNGS.js → chunk-24KDXQZT.js} +1 -1
  5. package/dist/{chunk-OUJRD7GG.js → chunk-3K66L37W.js} +8 -3
  6. package/dist/{chunk-CSIQDFEF.js → chunk-3PUWH62K.js} +1 -1
  7. package/dist/{chunk-WSSJH765.js → chunk-4WI5ON64.js} +1 -1
  8. package/dist/{chunk-KREGVQ63.js → chunk-5QCCV2JM.js} +11 -11
  9. package/dist/{chunk-RRECJTOG.js → chunk-67REFMSY.js} +1 -1
  10. package/dist/{chunk-TLCFBQ6E.js → chunk-6N2BBLIT.js} +2 -2
  11. package/dist/{chunk-EAR2C6TS.js → chunk-6RVAPAE5.js} +1 -1
  12. package/dist/{chunk-2YG6L3YO.js → chunk-7HDBPDAD.js} +2 -2
  13. package/dist/{chunk-O6NTUSSE.js → chunk-CBD52BYV.js} +1 -1
  14. package/dist/{chunk-Z2ELYJ5O.js → chunk-CIEPLTGS.js} +10 -3
  15. package/dist/{chunk-XCVYR5BK.js → chunk-ED5TTYQU.js} +12 -1
  16. package/dist/{chunk-62UTSCWF.js → chunk-MPYSYTSO.js} +1 -1
  17. package/dist/{chunk-AWBEZY4D.js → chunk-PGDYPOQW.js} +7 -5
  18. package/dist/{chunk-HPYI7L4W.js → chunk-SXRQ2Q7A.js} +4 -4
  19. package/dist/{chunk-DRJSANKH.js → chunk-USFXL5BS.js} +4 -2
  20. package/dist/{chunk-EOJNFGOP.js → chunk-VXRW5SYP.js} +74 -15
  21. package/dist/{chunk-6H6MPG7L.js → chunk-ZWXKBFKW.js} +7 -7
  22. package/dist/cli.js +39 -39
  23. package/dist/{completion-FETH4OGC.js → completion-GEFRIQWV.js} +1 -1
  24. package/dist/{config-RVMC23W3.js → config-JKMYKNAT.js} +2 -2
  25. package/dist/{config-KR5JI5RA.js → config-JLOH5M6B.js} +3 -3
  26. package/dist/{ghBotCli-Z6GWXCJ6.js → ghBotCli-WEVS6S6O.js} +14 -14
  27. package/dist/{integrationCli-GPRR6J6W.js → integrationCli-PKMICTHC.js} +2 -2
  28. package/dist/{login-DFSN77L3.js → login-2TZRODUL.js} +4 -4
  29. package/dist/{mcpCli-64P3SVUE.js → mcpCli-TAPKYGHO.js} +22 -7
  30. package/dist/{modelsCli-PFQY3G2Q.js → modelsCli-6PK7V2QZ.js} +6 -6
  31. package/dist/{oauthLogin-NR22NWEV.js → oauthLogin-UI34LQZU.js} +1 -1
  32. package/dist/{pluginsCli-UKRJ3AHN.js → pluginsCli-RCF33U5J.js} +6 -2
  33. package/dist/{prCli-6FCI36AJ.js → prCli-24PMW7NS.js} +1 -1
  34. package/dist/print.d.ts +1 -0
  35. package/dist/providerCli-XA4LTWRD.js +15 -0
  36. package/dist/sdk.js +93 -15
  37. package/dist/{serve-DTTFDRUY.js → serve-7BFCIEPJ.js} +27 -16
  38. package/dist/{serve-PKUTKQQT.js → serve-XNJI67DU.js} +3 -3
  39. package/dist/session-VAPIHHUG.js +12 -0
  40. package/dist/setup-GPIMZOWE.js +24 -0
  41. package/dist/{statsCli-FR7GOKWO.js → statsCli-QDHE4TKB.js} +1 -1
  42. package/dist/{transferCli-GIE4MEJ4.js → transferCli-7OKKYGG2.js} +4 -4
  43. package/dist/{ui-NZVOCQSU.js → ui-SZAIIJC5.js} +12 -12
  44. package/dist/{update-W2D2XJVP.js → update-JSNVVXPS.js} +2 -2
  45. package/package.json +1 -1
  46. package/dist/providerCli-PBSZECBM.js +0 -15
  47. package/dist/session-XABMBIIF.js +0 -12
  48. package/dist/setup-CLPQLTM7.js +0 -24
@@ -180,8 +180,8 @@ function runtimeAgent(cwd, allowAll = false) {
180
180
  return {
181
181
  async run(text, onChunk, signal, io) {
182
182
  if (!rt) {
183
- const { parseCliArgs } = await import("./args-6XMUZDAK.js");
184
- const { createRuntime } = await import("./setup-CLPQLTM7.js");
183
+ const { parseCliArgs } = await import("./args-EW7XJVQN.js");
184
+ const { createRuntime } = await import("./setup-GPIMZOWE.js");
185
185
  rt = createRuntime({ cwd, args: { ...parseCliArgs([]), allowAll } });
186
186
  }
187
187
  if (io && !allowAll) rt.agent.askPermission = (req) => io.askPermission(req);
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  helpText,
4
4
  parseCliArgs
5
- } from "./chunk-Z2ELYJ5O.js";
5
+ } from "./chunk-CIEPLTGS.js";
6
6
  import "./chunk-WFZKJY3R.js";
7
- import "./chunk-EAR2C6TS.js";
8
- import "./chunk-DRJSANKH.js";
9
- import "./chunk-XCVYR5BK.js";
7
+ import "./chunk-6RVAPAE5.js";
8
+ import "./chunk-USFXL5BS.js";
9
+ import "./chunk-ED5TTYQU.js";
10
10
  export {
11
11
  helpText,
12
12
  parseCliArgs
package/dist/args.d.ts CHANGED
@@ -5,6 +5,10 @@ export interface CliArgs {
5
5
  command: 'run' | 'login' | 'models' | 'update' | 'acp' | 'serve' | 'plugins' | 'gh-bot' | 'provider' | 'mcp' | 'connect' | 'disconnect' | 'integrations' | 'completion' | 'stats' | 'export' | 'import' | 'pr';
6
6
  subArgs: string[];
7
7
  url?: string;
8
+ /** `bccli mcp add <name> --command <cmd>`: server stdio, bukan HTTP. */
9
+ stdioCommand?: string;
10
+ /** Argumen untuk server stdio (dipisah spasi). */
11
+ stdioArgs?: string;
8
12
  name?: string;
9
13
  keyEnv?: string;
10
14
  values: string[];
@@ -16,6 +20,8 @@ export interface CliArgs {
16
20
  continue: boolean;
17
21
  resume: boolean;
18
22
  allowAll: boolean;
23
+ /** `-p` untuk CI: kegagalan tool apa pun membuat exit code 1. */
24
+ failOnToolError: boolean;
19
25
  allowedTools: string[];
20
26
  worktree?: string;
21
27
  outputFormat?: 'text' | 'json' | 'stream-json';
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  contentText
4
- } from "./chunk-EOJNFGOP.js";
4
+ } from "./chunk-VXRW5SYP.js";
5
5
 
6
6
  // src/session.ts
7
7
  import { randomBytes } from "crypto";
@@ -2,13 +2,13 @@
2
2
  import {
3
3
  runCommand,
4
4
  runHooks
5
- } from "./chunk-CSIQDFEF.js";
5
+ } from "./chunk-3PUWH62K.js";
6
6
  import {
7
7
  ConfigError
8
- } from "./chunk-DRJSANKH.js";
8
+ } from "./chunk-USFXL5BS.js";
9
9
  import {
10
10
  t
11
- } from "./chunk-XCVYR5BK.js";
11
+ } from "./chunk-ED5TTYQU.js";
12
12
 
13
13
  // src/verify.ts
14
14
  var MAX_VERIFY_ROUNDS = 2;
@@ -90,6 +90,7 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
90
90
  case "toolEnd":
91
91
  if (event.isError) io.err.write(` \u2717 ${event.output.split("\n")[0]}
92
92
  `);
93
+ if (event.isError && opts.failOnToolError) failed = true;
93
94
  if ((event.tool === "edit" || event.tool === "write") && !event.isError) edited = true;
94
95
  if (format === "stream-json") emit({ type: "tool_result", tool: toolNames.get(event.id), isError: !!event.isError, output: event.output });
95
96
  break;
@@ -125,6 +126,9 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
125
126
  const controller = new AbortController();
126
127
  const onSigint = () => controller.abort();
127
128
  process.once("SIGINT", onSigint);
129
+ const onSigbreak = () => controller.abort();
130
+ const useSigbreak = process.platform === "win32";
131
+ if (useSigbreak) process.once("SIGBREAK", onSigbreak);
128
132
  const start = await runHooks(rt.config.hooks, "SessionStart", {}, { env: rt.env, cwd: rt.cwd });
129
133
  for (const warning of start.warnings) io.err.write(`${warning}
130
134
  `);
@@ -153,6 +157,7 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
153
157
  }
154
158
  } finally {
155
159
  process.off("SIGINT", onSigint);
160
+ if (useSigbreak) process.off("SIGBREAK", onSigbreak);
156
161
  }
157
162
  const stop = await runHooks(rt.config.hooks, "Stop", {}, { env: rt.env, cwd: rt.cwd });
158
163
  for (const warning of stop.warnings) io.err.write(`${warning}
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-PSO7R5AD.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-XCVYR5BK.js";
7
+ } from "./chunk-ED5TTYQU.js";
8
8
 
9
9
  // src/tools/bash.ts
10
10
  import { spawn } from "child_process";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-PSO7R5AD.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-XCVYR5BK.js";
7
+ } from "./chunk-ED5TTYQU.js";
8
8
 
9
9
  // src/tools/websearch.ts
10
10
  import { z } from "zod";
@@ -24,11 +24,11 @@ import {
24
24
  resolvePath,
25
25
  runHooks,
26
26
  validateGlobPattern
27
- } from "./chunk-CSIQDFEF.js";
27
+ } from "./chunk-3PUWH62K.js";
28
28
  import {
29
29
  createWebSearchTool,
30
30
  fetchTool
31
- } from "./chunk-WSSJH765.js";
31
+ } from "./chunk-4WI5ON64.js";
32
32
  import {
33
33
  defineTool
34
34
  } from "./chunk-PSO7R5AD.js";
@@ -37,39 +37,39 @@ import {
37
37
  } from "./chunk-WFZKJY3R.js";
38
38
  import {
39
39
  addProviderKey
40
- } from "./chunk-AWBEZY4D.js";
40
+ } from "./chunk-PGDYPOQW.js";
41
41
  import {
42
42
  listAllModels
43
- } from "./chunk-HPYI7L4W.js";
43
+ } from "./chunk-SXRQ2Q7A.js";
44
44
  import {
45
45
  providerName
46
- } from "./chunk-62UTSCWF.js";
46
+ } from "./chunk-MPYSYTSO.js";
47
47
  import {
48
48
  FileOAuthProvider,
49
49
  hasStoredTokens
50
- } from "./chunk-RRECJTOG.js";
50
+ } from "./chunk-67REFMSY.js";
51
51
  import {
52
52
  Session
53
- } from "./chunk-OETLWNGS.js";
53
+ } from "./chunk-24KDXQZT.js";
54
54
  import {
55
55
  completionAssistantMessage,
56
56
  createProvider,
57
57
  hasPendingProgrammaticReplay,
58
58
  imageUserMessage,
59
59
  withFallbackProvider
60
- } from "./chunk-EOJNFGOP.js";
60
+ } from "./chunk-VXRW5SYP.js";
61
61
  import {
62
62
  expandEnvVars
63
- } from "./chunk-2YG6L3YO.js";
63
+ } from "./chunk-7HDBPDAD.js";
64
64
  import {
65
65
  ConfigError,
66
66
  bccliHome,
67
67
  loadConfig,
68
68
  resolveModel
69
- } from "./chunk-DRJSANKH.js";
69
+ } from "./chunk-USFXL5BS.js";
70
70
  import {
71
71
  t
72
- } from "./chunk-XCVYR5BK.js";
72
+ } from "./chunk-ED5TTYQU.js";
73
73
 
74
74
  // src/textToolCalls.ts
75
75
  var PAIR = /(?:<arg_key>\s*|`)?([A-Za-z_]\w*)(?:\s*<\/arg_key>)?`?\s*(?:=\s*`|<arg_value>)([\s\S]*?)<\/arg_value>/g;
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  quarantineCorruptFile,
4
4
  writeFileAtomic
5
- } from "./chunk-DRJSANKH.js";
5
+ } from "./chunk-USFXL5BS.js";
6
6
 
7
7
  // src/mcp/oauth.ts
8
8
  import { spawn } from "child_process";
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  ConfigError
4
- } from "./chunk-DRJSANKH.js";
4
+ } from "./chunk-USFXL5BS.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-XCVYR5BK.js";
7
+ } from "./chunk-ED5TTYQU.js";
8
8
 
9
9
  // src/mcp/catalog.ts
10
10
  var CATALOG = [
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  t
4
- } from "./chunk-XCVYR5BK.js";
4
+ } from "./chunk-ED5TTYQU.js";
5
5
 
6
6
  // src/reasoning.ts
7
7
  var REASONING_LEVELS = ["auto", "off", "low", "medium", "high", "max"];
@@ -2,10 +2,10 @@
2
2
  import {
3
3
  ConfigError,
4
4
  writeFileAtomic
5
- } from "./chunk-DRJSANKH.js";
5
+ } from "./chunk-USFXL5BS.js";
6
6
  import {
7
7
  t
8
- } from "./chunk-XCVYR5BK.js";
8
+ } from "./chunk-ED5TTYQU.js";
9
9
 
10
10
  // src/mcp/config.ts
11
11
  import { createHash } from "crypto";
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/version.ts
4
- var VERSION = true ? "0.4.8" : "0.0.0-dev";
4
+ var VERSION = true ? "0.4.10" : "0.0.0-dev";
5
5
 
6
6
  export {
7
7
  VERSION
@@ -5,14 +5,14 @@ import {
5
5
  import {
6
6
  REASONING_LEVELS,
7
7
  isReasoningLevel
8
- } from "./chunk-EAR2C6TS.js";
8
+ } from "./chunk-6RVAPAE5.js";
9
9
  import {
10
10
  ConfigError
11
- } from "./chunk-DRJSANKH.js";
11
+ } from "./chunk-USFXL5BS.js";
12
12
  import {
13
13
  parseLang,
14
14
  t
15
- } from "./chunk-XCVYR5BK.js";
15
+ } from "./chunk-ED5TTYQU.js";
16
16
 
17
17
  // src/args.ts
18
18
  import { parseArgs } from "util";
@@ -112,6 +112,7 @@ Options:
112
112
  -r, --resume pick a session to continue
113
113
  --allow-all run all tools without asking for permission
114
114
  --allowed-tools <a,b> tools allowed without asking: bash, edit, fetch
115
+ --fail-on-tool-error with -p: exit 1 when any tool call fails (for CI)
115
116
  -w, --worktree <name> run in git worktree ../<repo>.worktrees/<name> (created, or reused if it exists)
116
117
  --output-format <fmt> text | json | stream-json for -p (machine-readable output)
117
118
  --permission-mode <mode> default | acceptEdits | plan | allowAll
@@ -137,6 +138,9 @@ function parseCliArgs(argv) {
137
138
  continue: { type: "boolean", short: "c" },
138
139
  resume: { type: "boolean", short: "r" },
139
140
  "allow-all": { type: "boolean" },
141
+ command: { type: "string" },
142
+ args: { type: "string" },
143
+ "fail-on-tool-error": { type: "boolean" },
140
144
  "allowed-tools": { type: "string" },
141
145
  worktree: { type: "string", short: "w" },
142
146
  "output-format": { type: "string" },
@@ -210,6 +214,7 @@ function parseCliArgs(argv) {
210
214
  continue: !!values.continue,
211
215
  resume: !!values.resume,
212
216
  allowAll: !!values["allow-all"],
217
+ failOnToolError: !!values["fail-on-tool-error"],
213
218
  allowedTools: (values["allowed-tools"] ?? "").split(",").map((s) => s.trim()).filter(Boolean),
214
219
  worktree: values.worktree,
215
220
  outputFormat,
@@ -221,6 +226,8 @@ function parseCliArgs(argv) {
221
226
  logout: !!values.logout,
222
227
  subArgs: ["models", "plugins", "gh-bot", "provider", "mcp", "connect", "disconnect", "integrations", "completion", "stats", "export", "import", "pr"].includes(command) ? rest : [],
223
228
  url: values.url,
229
+ stdioCommand: values.command,
230
+ stdioArgs: values.args,
224
231
  name: values.name,
225
232
  keyEnv: values["key-env"],
226
233
  values: values.value ?? [],
@@ -136,6 +136,7 @@ Options:
136
136
  -r, --resume pick a session to continue
137
137
  --allow-all run all tools without asking for permission
138
138
  --allowed-tools <a,b> tools allowed without asking: bash, edit, fetch
139
+ --fail-on-tool-error with -p: exit 1 when any tool call fails (for CI)
139
140
  -w, --worktree <name> run in git worktree ../<repo>.worktrees/<name> (created, or reused if it exists)
140
141
  --output-format <fmt> text | json | stream-json for -p (machine-readable output)
141
142
  --permission-mode <mode> default | acceptEdits | plan | allowAll
@@ -166,6 +167,7 @@ Opsi:
166
167
  -r, --resume pilih sesi untuk dilanjutkan
167
168
  --allow-all jalankan semua alat tanpa minta izin
168
169
  --allowed-tools <a,b> alat yang boleh tanpa izin: bash, edit, fetch
170
+ --fail-on-tool-error dengan -p: keluar 1 bila ada tool gagal (untuk CI)
169
171
  -w, --worktree <name> jalankan di git worktree ../<repo>.worktrees/<name> (dibuat, atau dipakai ulang jika ada)
170
172
  --output-format <fmt> text | json | stream-json untuk -p (output mesin)
171
173
  --permission-mode <mode> default | acceptEdits | plan | allowAll
@@ -224,6 +226,7 @@ Opsi:
224
226
  'npm removed the files of {name} but the config entry {entry} remains: {reason}. bccli will refuse to start while that entry is listed \u2014 fix the config error at {path}, then run "bccli plugins remove {name}" again (safe to repeat), or delete the entry manually.': 'npm sudah menghapus file {name} tetapi entry config {entry} tertinggal: {reason}. bccli menolak start selama entry itu terdaftar \u2014 perbaiki error config di {path}, lalu jalankan lagi "bccli plugins remove {name}" (aman diulang), atau hapus entry secara manual.',
225
227
  "Skipping malformed entry: {entry} \u2014 fix it manually in {path}.": "Melewatkan entry rusak: {entry} \u2014 perbaiki manual di {path}.",
226
228
  "No managed plugins to update.": "Tidak ada plugin terelola untuk diperbarui.",
229
+ "No plugins are installed. Install one with: bccli plugins install <spec>": "Belum ada plugin terpasang. Pasang dengan: bccli plugins install <spec>",
227
230
  "update requires a plugin name or no argument: bccli plugins update [name]": "update butuh nama plugin atau tanpa argumen: bccli plugins update [nama]",
228
231
  "{name} updated to latest.": "{name} diperbarui ke versi terbaru.",
229
232
  "Use either --key or --logout, not both.": "Pakai --key atau --logout, jangan keduanya.",
@@ -281,6 +284,13 @@ Opsi:
281
284
  "No API key for {id} yet. Run `bccli login` or set the env var {env}.": "API key untuk {id} belum ada. Jalankan `bccli login` atau set env {env}.",
282
285
  "Error from the provider: {message}": "Error dari provider: {message}",
283
286
  "Cannot connect to {url}: {error}": "Tidak bisa terhubung ke {url}: {error}",
287
+ "The API key was rejected. Check the key for this provider (bccli login) or its API key environment variable.": "Kunci API ditolak. Periksa kunci provider ini (bccli login) atau env var kuncinya.",
288
+ "Model or endpoint not found. Check the model name and the provider base URL.": "Model atau endpoint tidak ditemukan. Periksa nama model dan base URL provider.",
289
+ "Rate limit reached. Wait a moment, or switch model with /model.": "Batas laju tercapai. Tunggu sebentar, atau ganti model dengan /model.",
290
+ "The provider is having trouble. Try again, or switch model with /model.": "Penyedia sedang bermasalah. Coba lagi, atau ganti model dengan /model.",
291
+ "The provider did not respond within {seconds}s. Check the connection, then try again.": "Penyedia tidak merespons dalam {seconds} detik. Periksa koneksi, lalu coba lagi.",
292
+ "The provider stopped sending data for {seconds}s. The answer above may be incomplete; try again.": "Penyedia berhenti mengirim data selama {seconds} detik. Jawaban di atas mungkin tidak lengkap; coba lagi.",
293
+ "The response ended before it finished (no finish_reason or [DONE]). The answer above is incomplete; try again.": "Respons berakhir sebelum selesai (tanpa finish_reason atau [DONE]). Jawaban di atas tidak lengkap; coba lagi.",
284
294
  "{status} from {url}: {message}": "{status} dari {url}: {message}",
285
295
  "Request failed": "Permintaan gagal",
286
296
  "Connection closed before the response finished.": "Koneksi ditutup sebelum respons selesai.",
@@ -305,7 +315,8 @@ Opsi:
305
315
  "Usage: bccli mcp {action} <name>": "Pemakaian: bccli mcp {action} <nama>",
306
316
  "{name} removed.": "{name} dihapus.",
307
317
  "{name} installed ({url}).": "{name} terpasang ({url}).",
308
- "{name} is not in the catalog. For another remote server use: bccli mcp add {name} --url <url>": "{name} tidak ada di katalog. Untuk server remote lain pakai: bccli mcp add {name} --url <url>",
318
+ "{name} installed (stdio: {command}).": "{name} terpasang (stdio: {command}).",
319
+ "{name} is not in the catalog. For another server use: bccli mcp add {name} --url <url> (HTTP) or bccli mcp add {name} --command <cmd> (stdio).": "{name} tidak ada di katalog. Untuk server lain pakai: bccli mcp add {name} --url <url> (HTTP) atau bccli mcp add {name} --command <cmd> (stdio).",
309
320
  "{name} installed. Active in the next bccli session.": "{name} terpasang. Aktif di sesi bccli berikutnya.",
310
321
  "control a browser (open pages, click, fill forms, screenshot)": "kontrol browser (buka halaman, klik, isi form, screenshot)",
311
322
  "up-to-date library documentation": "dokumentasi library terbaru",
@@ -6,7 +6,7 @@ import {
6
6
  readJsonConfig,
7
7
  removeCredential,
8
8
  writeFileAtomic
9
- } from "./chunk-DRJSANKH.js";
9
+ } from "./chunk-USFXL5BS.js";
10
10
 
11
11
  // src/providers.ts
12
12
  import { join } from "path";
@@ -1,21 +1,21 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  listAllModels
4
- } from "./chunk-HPYI7L4W.js";
4
+ } from "./chunk-SXRQ2Q7A.js";
5
5
  import {
6
6
  hasKey,
7
7
  providerName,
8
8
  removeGlobalProvider,
9
9
  writeGlobalConfig
10
- } from "./chunk-62UTSCWF.js";
10
+ } from "./chunk-MPYSYTSO.js";
11
11
  import {
12
12
  PRESETS,
13
13
  loadConfig,
14
14
  saveCredential
15
- } from "./chunk-DRJSANKH.js";
15
+ } from "./chunk-USFXL5BS.js";
16
16
  import {
17
17
  t
18
- } from "./chunk-XCVYR5BK.js";
18
+ } from "./chunk-ED5TTYQU.js";
19
19
 
20
20
  // src/providerCli.ts
21
21
  async function addProviderKey(providerId, key, deps) {
@@ -49,7 +49,8 @@ async function runProviderCommand(args, deps) {
49
49
  return 1;
50
50
  }
51
51
  const preset = PRESETS.find((p) => p.id === id);
52
- if (!preset && !config.providers[id]) {
52
+ const createdEntry = !preset && !config.providers[id];
53
+ if (createdEntry) {
53
54
  if (!args.url) {
54
55
  deps.err(t("{id} is not a preset. For a custom provider add --url <OpenAI-compatible base URL>.", { id }));
55
56
  return 1;
@@ -63,6 +64,7 @@ async function runProviderCommand(args, deps) {
63
64
  const key = needsKey ? await deps.readSecret(t("API key for {id} (leave empty if not needed): ", { id })) : "";
64
65
  const result = await addProviderKey(id, key, deps);
65
66
  if (!result.ok) {
67
+ if (createdEntry) removeGlobalProvider(id, deps.env);
66
68
  deps.err(result.error);
67
69
  return 1;
68
70
  }
@@ -2,17 +2,17 @@
2
2
  import {
3
3
  hasKey,
4
4
  providerName
5
- } from "./chunk-62UTSCWF.js";
5
+ } from "./chunk-MPYSYTSO.js";
6
6
  import {
7
7
  createProvider
8
- } from "./chunk-EOJNFGOP.js";
8
+ } from "./chunk-VXRW5SYP.js";
9
9
  import {
10
10
  PRESETS,
11
11
  readCredentials
12
- } from "./chunk-DRJSANKH.js";
12
+ } from "./chunk-USFXL5BS.js";
13
13
  import {
14
14
  t
15
- } from "./chunk-XCVYR5BK.js";
15
+ } from "./chunk-ED5TTYQU.js";
16
16
 
17
17
  // src/models.ts
18
18
  function order(ids) {
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  t
4
- } from "./chunk-XCVYR5BK.js";
4
+ } from "./chunk-ED5TTYQU.js";
5
5
 
6
6
  // src/config.ts
7
7
  import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
@@ -339,7 +339,9 @@ function resolveModel(config, modelRef, env = process.env) {
339
339
  if (!provider) {
340
340
  throw new ConfigError(t('Provider "{id}" is not in the config. Available: {list}', { id: providerId, list: Object.keys(config.providers).join(", ") }));
341
341
  }
342
- const apiKey = readCredentials(env)[providerId] || (provider.apiKeyEnv ? env[provider.apiKeyEnv] : void 0) || void 0;
342
+ const untrusted = config.projectProviders?.includes(providerId) ?? false;
343
+ const stored = untrusted ? void 0 : readCredentials(env)[providerId];
344
+ const apiKey = stored || (provider.apiKeyEnv ? env[provider.apiKeyEnv] : void 0) || void 0;
343
345
  if (provider.apiKeyEnv && !apiKey) {
344
346
  throw new ConfigError(t("No API key for {id} yet. Run `bccli login` or set the env var {env}.", { id: providerId, env: provider.apiKeyEnv }));
345
347
  }
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  reasoningPayload
4
- } from "./chunk-EAR2C6TS.js";
4
+ } from "./chunk-6RVAPAE5.js";
5
5
  import {
6
6
  t
7
- } from "./chunk-XCVYR5BK.js";
7
+ } from "./chunk-ED5TTYQU.js";
8
8
 
9
9
  // src/provider.ts
10
10
  import OpenAI from "openai";
@@ -78,6 +78,17 @@ function outputReasoningSummary(items) {
78
78
  }).map((part) => part && typeof part === "object" && "text" in part && typeof part.text === "string" ? part.text : "").filter(Boolean).join("\n");
79
79
  }
80
80
  var retryable = (status) => status === 429 || status >= 500;
81
+ function httpIdleMs() {
82
+ const raw = Number(process.env.BCCLI_HTTP_IDLE_TIMEOUT_MS);
83
+ return Number.isFinite(raw) && raw > 0 ? raw : 6e4;
84
+ }
85
+ function statusHint(status) {
86
+ if (status === 401 || status === 403) return t("The API key was rejected. Check the key for this provider (bccli login) or its API key environment variable.");
87
+ if (status === 404) return t("Model or endpoint not found. Check the model name and the provider base URL.");
88
+ if (status === 429) return t("Rate limit reached. Wait a moment, or switch model with /model.");
89
+ if (status >= 500) return t("The provider is having trouble. Try again, or switch model with /model.");
90
+ return void 0;
91
+ }
81
92
  function detectRepetition(text) {
82
93
  const tail = text.slice(-600);
83
94
  for (let len = 1; len <= 12; len++) {
@@ -120,7 +131,7 @@ var toUsage = (u) => u ? {
120
131
  ...u.prompt_tokens_details?.cached_tokens != null ? { cachedInputTokens: u.prompt_tokens_details.cached_tokens } : {},
121
132
  ...u.prompt_tokens_details?.cache_write_tokens != null ? { cacheWriteTokens: u.prompt_tokens_details.cache_write_tokens } : {}
122
133
  } : void 0;
123
- async function readStream(res, onText, onThinking) {
134
+ async function readStream(res, onText, onThinking, signal) {
124
135
  let text = "";
125
136
  let thinking = "";
126
137
  const splitter = new ThinkSplitter();
@@ -137,6 +148,7 @@ async function readStream(res, onText, onThinking) {
137
148
  };
138
149
  let usage;
139
150
  let finishReason;
151
+ let sawDone = false;
140
152
  const calls = [];
141
153
  const byIndex = /* @__PURE__ */ new Map();
142
154
  let current;
@@ -146,7 +158,11 @@ async function readStream(res, onText, onThinking) {
146
158
  const handle = (line) => {
147
159
  if (!line.startsWith("data:")) return;
148
160
  const data = line.slice(5).trim();
149
- if (!data || data === "[DONE]") return;
161
+ if (!data) return;
162
+ if (data === "[DONE]") {
163
+ sawDone = true;
164
+ return;
165
+ }
150
166
  let event;
151
167
  try {
152
168
  event = JSON.parse(data);
@@ -175,18 +191,45 @@ async function readStream(res, onText, onThinking) {
175
191
  if (tc.function?.arguments) target.arguments += tc.function.arguments;
176
192
  }
177
193
  };
178
- for await (const chunk of res.body) {
179
- buffer += decoder.decode(chunk, { stream: true });
180
- let newline = buffer.indexOf("\n");
181
- while (newline >= 0 && loopAt < 0) {
182
- handle(buffer.slice(0, newline).trim());
183
- buffer = buffer.slice(newline + 1);
184
- newline = buffer.indexOf("\n");
194
+ const iterator = res.body[Symbol.asyncIterator]();
195
+ const idle = httpIdleMs();
196
+ const onAbort = () => void iterator.return?.();
197
+ if (signal?.aborted) onAbort();
198
+ else signal?.addEventListener("abort", onAbort);
199
+ try {
200
+ for (; ; ) {
201
+ let timer;
202
+ const stalled = new Promise((_, reject) => {
203
+ timer = setTimeout(
204
+ () => reject(new ProviderError(t("The provider stopped sending data for {seconds}s. The answer above may be incomplete; try again.", { seconds: String(Math.round(idle / 1e3)) }))),
205
+ idle
206
+ );
207
+ });
208
+ let step;
209
+ try {
210
+ step = await Promise.race([iterator.next(), stalled]);
211
+ } finally {
212
+ if (timer) clearTimeout(timer);
213
+ }
214
+ if (step.done) break;
215
+ buffer += decoder.decode(step.value, { stream: true });
216
+ let newline = buffer.indexOf("\n");
217
+ while (newline >= 0 && loopAt < 0) {
218
+ handle(buffer.slice(0, newline).trim());
219
+ buffer = buffer.slice(newline + 1);
220
+ newline = buffer.indexOf("\n");
221
+ }
222
+ if (loopAt >= 0) return { text: text.slice(0, loopAt), toolCalls: [], thinking: thinking || void 0, usage, finishReason: "repetition" };
185
223
  }
186
- if (loopAt >= 0) return { text: text.slice(0, loopAt), toolCalls: [], thinking: thinking || void 0, usage, finishReason: "repetition" };
224
+ } finally {
225
+ signal?.removeEventListener("abort", onAbort);
226
+ void iterator.return?.();
187
227
  }
188
228
  handle(buffer.trim());
189
229
  emit(splitter.flush());
230
+ if (!sawDone && !finishReason) {
231
+ throw new ProviderError(t("The response ended before it finished (no finish_reason or [DONE]). The answer above is incomplete; try again."));
232
+ }
190
233
  const toolCalls = calls.map((call, index) => ({ ...call, id: call.id || `call_${index}` }));
191
234
  return { text, toolCalls, thinking: thinking || void 0, usage, finishReason };
192
235
  }
@@ -882,12 +925,26 @@ function createProvider(options) {
882
925
  await sleep(delay, signal);
883
926
  }
884
927
  let res;
928
+ const idleAbort = new AbortController();
929
+ const forwardAbort = () => idleAbort.abort(signal?.reason);
930
+ if (signal?.aborted) forwardAbort();
931
+ else signal?.addEventListener("abort", forwardAbort);
932
+ const idle = httpIdleMs();
933
+ const headerTimer = setTimeout(() => idleAbort.abort(new Error("idle-timeout")), idle);
885
934
  try {
886
- res = await doFetch(`${baseURL}${path}`, { method: "POST", headers, body: JSON.stringify(body), signal });
935
+ res = await doFetch(`${baseURL}${path}`, { method: "POST", headers, body: JSON.stringify(body), signal: idleAbort.signal });
887
936
  } catch (error) {
888
937
  if (signal?.aborted) throw error;
938
+ if (idleAbort.signal.aborted) {
939
+ throw new ProviderError(
940
+ t("The provider did not respond within {seconds}s. Check the connection, then try again.", { seconds: String(Math.round(idle / 1e3)) })
941
+ );
942
+ }
889
943
  lastError = new ProviderError(t("Cannot connect to {url}: {error}", { url: baseURL, error: error.message }));
890
944
  continue;
945
+ } finally {
946
+ clearTimeout(headerTimer);
947
+ signal?.removeEventListener("abort", forwardAbort);
891
948
  }
892
949
  if (res.ok) return res;
893
950
  const retryHeader = res.headers.get("retry-after");
@@ -896,7 +953,9 @@ function createProvider(options) {
896
953
  if (Number.isFinite(secs) && secs > 0 && secs <= 30) retryAfterWaitMs = Math.ceil(secs * 1e3);
897
954
  }
898
955
  const text = await res.text().catch(() => "");
899
- lastError = new ProviderError(t("{status} from {url}: {message}", { status: res.status, url: baseURL, message: errorMessage(text) }), res.status);
956
+ const raw = t("{status} from {url}: {message}", { status: res.status, url: baseURL, message: errorMessage(text) });
957
+ const hint = statusHint(res.status);
958
+ lastError = new ProviderError(hint ? `${hint} (${raw})` : raw, res.status);
900
959
  const deterministicQuota429 = res.status === 429 && /"code"\s*:\s*"(?:luna_monthly_quota_exhausted|plan_allowance_exhausted)"/i.test(text);
901
960
  if (!retryable(res.status) || deterministicQuota429) throw lastError;
902
961
  }
@@ -946,7 +1005,7 @@ function createProvider(options) {
946
1005
  );
947
1006
  if ((res.headers.get("content-type") ?? "").includes("application/json")) return readJson(res, onText, onThinking);
948
1007
  if (!res.body) throw new ProviderError(t("Empty response from the provider"));
949
- return readStream(res, onText, onThinking);
1008
+ return readStream(res, onText, onThinking, signal);
950
1009
  },
951
1010
  async listModels() {
952
1011
  const res = await doFetch(`${baseURL}/models`, { headers });
@@ -1,14 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  defaultOpen
4
- } from "./chunk-RRECJTOG.js";
4
+ } from "./chunk-67REFMSY.js";
5
5
  import {
6
6
  removeCredential,
7
7
  saveCredential
8
- } from "./chunk-DRJSANKH.js";
8
+ } from "./chunk-USFXL5BS.js";
9
9
  import {
10
10
  t
11
- } from "./chunk-XCVYR5BK.js";
11
+ } from "./chunk-ED5TTYQU.js";
12
12
 
13
13
  // src/login.ts
14
14
  function readSecret(prompt) {
@@ -91,8 +91,8 @@ function defaultLoginDeps() {
91
91
  fetch: globalThis.fetch,
92
92
  // Wiring NYATA jalur lama: persis runLogin lama (provider add + prompt key).
93
93
  legacyAdd: async (providerId, targetCwd) => {
94
- const { runProviderCommand } = await import("./providerCli-PBSZECBM.js");
95
- const { parseCliArgs: parse } = await import("./args-6XMUZDAK.js");
94
+ const { runProviderCommand } = await import("./providerCli-XA4LTWRD.js");
95
+ const { parseCliArgs: parse } = await import("./args-EW7XJVQN.js");
96
96
  return runProviderCommand(parse(["provider", "add", providerId]), {
97
97
  env: process.env,
98
98
  cwd: targetCwd,
@@ -117,12 +117,12 @@ async function runLogin(providerId, cwd, args, deps) {
117
117
  );
118
118
  return 1;
119
119
  }
120
- const { runOauthLogout } = await import("./oauthLogin-NR22NWEV.js");
120
+ const { runOauthLogout } = await import("./oauthLogin-UI34LQZU.js");
121
121
  return runOauthLogout({ out: deps.out, err: deps.err, env: deps.env, removeCredential: deps.removeCredential });
122
122
  }
123
123
  if (args.key) return deps.legacyAdd(providerId, cwd);
124
124
  if (providerId === "openrouter") {
125
- const { runOauthLogin } = await import("./oauthLogin-NR22NWEV.js");
125
+ const { runOauthLogin } = await import("./oauthLogin-UI34LQZU.js");
126
126
  return runOauthLogin({
127
127
  out: deps.out,
128
128
  err: deps.err,