@botconnector/bccli 0.4.2 → 0.4.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.
@@ -181,7 +181,7 @@ function runtimeAgent(cwd, allowAll = false) {
181
181
  async run(text, onChunk, signal, io) {
182
182
  if (!rt) {
183
183
  const { parseCliArgs } = await import("./args-NRTZWWEV.js");
184
- const { createRuntime } = await import("./setup-S4AJT6XE.js");
184
+ const { createRuntime } = await import("./setup-HVVHOIL4.js");
185
185
  rt = createRuntime({ cwd, args: { ...parseCliArgs([]), allowAll } });
186
186
  }
187
187
  if (io && !allowAll) rt.agent.askPermission = (req) => io.askPermission(req);
@@ -14,6 +14,11 @@ import { join } from "path";
14
14
  import { z } from "zod";
15
15
 
16
16
  // src/tools/sandbox.ts
17
+ function winPathToWsl(winPath) {
18
+ const normalized = winPath.replace(/\\/g, "/");
19
+ const m = /^([A-Za-z]):\/(.*)$/.exec(normalized);
20
+ return m ? `/mnt/${m[1].toLowerCase()}/${m[2]}` : normalized;
21
+ }
17
22
  function darwinProfile(cwd, network) {
18
23
  const writable = [cwd, "/tmp", "/private/tmp", "/dev", "/var/folders"].map((p) => `(subpath "${p}")`).join(" ");
19
24
  return [
@@ -57,6 +62,33 @@ function sandboxArgv(command, opts) {
57
62
  command
58
63
  ];
59
64
  }
65
+ if (opts.platform === "win32" && (opts.wslBwrap || process.env.BCCLI_WIN32_WSL_SANDBOX === "1")) {
66
+ const wslCwd = winPathToWsl(opts.cwd);
67
+ return [
68
+ "wsl.exe",
69
+ "--",
70
+ "bwrap",
71
+ "--ro-bind",
72
+ "/",
73
+ "/",
74
+ "--dev-bind",
75
+ "/dev",
76
+ "/dev",
77
+ "--proc",
78
+ "/proc",
79
+ "--bind",
80
+ wslCwd,
81
+ wslCwd,
82
+ "--tmpfs",
83
+ "/tmp",
84
+ "--die-with-parent",
85
+ ...opts.network ? [] : ["--unshare-net"],
86
+ "--",
87
+ "/bin/bash",
88
+ "-c",
89
+ command
90
+ ];
91
+ }
60
92
  return void 0;
61
93
  }
62
94
 
@@ -797,6 +829,7 @@ function normalizeGlobPath(path) {
797
829
  }
798
830
 
799
831
  export {
832
+ resolveShell,
800
833
  runHooks,
801
834
  renderBackgroundTasks,
802
835
  runCommand,
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  contentText
4
- } from "./chunk-5YJ3ZQVR.js";
4
+ } from "./chunk-XTOATEI6.js";
5
5
 
6
6
  // src/session.ts
7
7
  import { randomBytes } from "crypto";
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-HQW4BROS.js";
6
6
  import {
7
7
  createProvider
8
- } from "./chunk-5YJ3ZQVR.js";
8
+ } from "./chunk-XTOATEI6.js";
9
9
  import {
10
10
  PRESETS,
11
11
  readCredentials
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/version.ts
4
- var VERSION = true ? "0.4.2" : "0.0.0-dev";
4
+ var VERSION = true ? "0.4.3" : "0.0.0-dev";
5
5
 
6
6
  export {
7
7
  VERSION
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  listAllModels
4
- } from "./chunk-IYR5PYA3.js";
4
+ } from "./chunk-7I2ZGPUS.js";
5
5
  import {
6
6
  hasKey,
7
7
  providerName,
@@ -24,7 +24,7 @@ import {
24
24
  resolvePath,
25
25
  runHooks,
26
26
  validateGlobPattern
27
- } from "./chunk-ZEPTUKNW.js";
27
+ } from "./chunk-4W5SKQDC.js";
28
28
  import {
29
29
  createWebSearchTool,
30
30
  fetchTool
@@ -37,10 +37,10 @@ import {
37
37
  } from "./chunk-WFZKJY3R.js";
38
38
  import {
39
39
  addProviderKey
40
- } from "./chunk-H7XJDKJY.js";
40
+ } from "./chunk-GWVLPN4B.js";
41
41
  import {
42
42
  listAllModels
43
- } from "./chunk-IYR5PYA3.js";
43
+ } from "./chunk-7I2ZGPUS.js";
44
44
  import {
45
45
  providerName
46
46
  } from "./chunk-HQW4BROS.js";
@@ -50,14 +50,14 @@ import {
50
50
  } from "./chunk-3I5BW3ZT.js";
51
51
  import {
52
52
  Session
53
- } from "./chunk-SVD4LHJC.js";
53
+ } from "./chunk-6AUROP2E.js";
54
54
  import {
55
55
  completionAssistantMessage,
56
56
  createProvider,
57
57
  hasPendingProgrammaticReplay,
58
58
  imageUserMessage,
59
59
  withFallbackProvider
60
- } from "./chunk-5YJ3ZQVR.js";
60
+ } from "./chunk-XTOATEI6.js";
61
61
  import {
62
62
  expandEnvVars
63
63
  } from "./chunk-6M55UPES.js";
@@ -1914,14 +1914,14 @@ function createRepoMapTool() {
1914
1914
  }
1915
1915
 
1916
1916
  // src/tools/diagnostics.ts
1917
- import { extname as extname3 } from "path";
1917
+ import { extname as extname3, relative as relative2 } from "path";
1918
1918
  import { z as z14 } from "zod";
1919
1919
 
1920
1920
  // src/lsp.ts
1921
1921
  import { spawn } from "child_process";
1922
1922
  import { readFileSync as readFileSync4 } from "fs";
1923
1923
  import { extname as extname2 } from "path";
1924
- import { pathToFileURL } from "url";
1924
+ import { fileURLToPath, pathToFileURL } from "url";
1925
1925
  var LANGUAGE_IDS = {
1926
1926
  ".ts": "typescript",
1927
1927
  ".tsx": "typescriptreact",
@@ -1989,86 +1989,253 @@ function createLspParser(onMessage) {
1989
1989
  }
1990
1990
  };
1991
1991
  }
1992
- function getDiagnostics(opts) {
1993
- return new Promise((resolve) => {
1994
- const child = spawn(opts.server.command, opts.server.args ?? [], {
1995
- cwd: opts.cwd,
1996
- stdio: ["pipe", "pipe", "ignore"],
1997
- shell: useShellForLsp(opts.server.command)
1992
+ function extractHoverText(contents) {
1993
+ if (!contents) return "";
1994
+ if (typeof contents === "string") return contents.trim();
1995
+ if (Array.isArray(contents)) return contents.map(extractHoverText).filter(Boolean).join("\n\n");
1996
+ if (typeof contents === "object" && "value" in contents && typeof contents.value === "string") {
1997
+ return contents.value.trim();
1998
+ }
1999
+ return "";
2000
+ }
2001
+ function normalizeLocations(raw) {
2002
+ const items = Array.isArray(raw) ? raw : raw ? [raw] : [];
2003
+ const out = [];
2004
+ for (const item of items) {
2005
+ if (!item || typeof item !== "object") continue;
2006
+ const rec = item;
2007
+ const uri = rec.uri ?? rec.targetUri;
2008
+ const start = rec.range?.start ?? rec.targetSelectionRange?.start ?? rec.targetRange?.start;
2009
+ if (!uri) continue;
2010
+ let path = uri;
2011
+ try {
2012
+ if (uri.startsWith("file:")) path = fileURLToPath(uri);
2013
+ } catch {
2014
+ }
2015
+ out.push({
2016
+ uri,
2017
+ path,
2018
+ line: start?.line ?? 0,
2019
+ character: start?.character ?? 0
1998
2020
  });
1999
- const uri = pathToFileURL(opts.file).href;
2000
- let settled = false;
2001
- let nextId = 2;
2002
- const send = (msg) => {
2003
- try {
2004
- child.stdin.write(encodeLsp(msg));
2005
- } catch {
2006
- }
2007
- };
2008
- const finish = (diags) => {
2009
- if (settled) return;
2010
- settled = true;
2011
- clearTimeout(timer);
2012
- opts.signal?.removeEventListener("abort", onAbort);
2013
- send({ jsonrpc: "2.0", id: nextId++, method: "shutdown" });
2014
- send({ jsonrpc: "2.0", method: "exit" });
2015
- const killer = setTimeout(() => {
2016
- try {
2017
- child.kill();
2018
- } catch {
2021
+ }
2022
+ return out;
2023
+ }
2024
+ function createWarmLspClient(opts) {
2025
+ let child;
2026
+ let initPromise;
2027
+ let nextId = 2;
2028
+ const opened = /* @__PURE__ */ new Map();
2029
+ const pendingRpc = /* @__PURE__ */ new Map();
2030
+ const diagWaiters = /* @__PURE__ */ new Map();
2031
+ const send = (msg) => {
2032
+ try {
2033
+ child?.stdin?.write(encodeLsp(msg));
2034
+ } catch {
2035
+ }
2036
+ };
2037
+ const resetState = () => {
2038
+ child = void 0;
2039
+ initPromise = void 0;
2040
+ opened.clear();
2041
+ for (const [, resolve] of pendingRpc) resolve(void 0);
2042
+ pendingRpc.clear();
2043
+ for (const [, waiters] of diagWaiters) {
2044
+ for (const w of waiters) w([]);
2045
+ }
2046
+ diagWaiters.clear();
2047
+ };
2048
+ const ensureStarted = () => {
2049
+ if (initPromise && child && !child.killed) return initPromise;
2050
+ opened.clear();
2051
+ initPromise = new Promise((resolveInit) => {
2052
+ const proc = spawn(opts.server.command, opts.server.args ?? [], {
2053
+ cwd: opts.cwd,
2054
+ stdio: ["pipe", "pipe", "ignore"],
2055
+ shell: useShellForLsp(opts.server.command)
2056
+ });
2057
+ child = proc;
2058
+ let initSettled = false;
2059
+ const settleInit = () => {
2060
+ if (!initSettled) {
2061
+ initSettled = true;
2062
+ resolveInit();
2019
2063
  }
2020
- }, 500);
2021
- killer.unref?.();
2022
- resolve(diags);
2023
- };
2024
- const onAbort = () => finish([]);
2025
- if (opts.signal?.aborted) return finish([]);
2026
- opts.signal?.addEventListener("abort", onAbort, { once: true });
2027
- const timer = setTimeout(() => finish([]), opts.timeoutMs ?? 8e3);
2028
- const parser = createLspParser((msg) => {
2029
- if (msg.id === 1 && msg.result) {
2030
- send({ jsonrpc: "2.0", method: "initialized", params: {} });
2031
- let text = "";
2032
- try {
2033
- text = readFileSync4(opts.file, "utf8");
2034
- } catch {
2064
+ };
2065
+ const parser = createLspParser((msg) => {
2066
+ if (msg.id === 1 && msg.result !== void 0) {
2067
+ send({ jsonrpc: "2.0", method: "initialized", params: {} });
2068
+ settleInit();
2069
+ return;
2035
2070
  }
2036
- send({
2037
- jsonrpc: "2.0",
2038
- method: "textDocument/didOpen",
2039
- params: { textDocument: { uri, languageId: languageIdFor(opts.file), version: 1, text } }
2040
- });
2041
- }
2042
- if (msg.method === "textDocument/publishDiagnostics" && msg.params?.uri === uri) {
2043
- finish(
2044
- (msg.params.diagnostics ?? []).map((d) => ({
2071
+ if (msg.id !== void 0 && pendingRpc.has(msg.id)) {
2072
+ const fn = pendingRpc.get(msg.id);
2073
+ pendingRpc.delete(msg.id);
2074
+ fn?.(msg.result);
2075
+ return;
2076
+ }
2077
+ if (msg.method === "textDocument/publishDiagnostics" && msg.params?.uri) {
2078
+ const uri = msg.params.uri;
2079
+ const diags = (msg.params.diagnostics ?? []).map((d) => ({
2045
2080
  line: d.range?.start?.line ?? 0,
2046
2081
  character: d.range?.start?.character ?? 0,
2047
2082
  severity: d.severity ?? 1,
2048
2083
  message: String(d.message ?? "")
2049
- }))
2050
- );
2051
- }
2084
+ }));
2085
+ const list = diagWaiters.get(uri);
2086
+ if (list?.length) {
2087
+ diagWaiters.delete(uri);
2088
+ for (const w of list) w(diags);
2089
+ }
2090
+ }
2091
+ });
2092
+ proc.stdout?.on("data", (c) => parser.push(c));
2093
+ proc.on("error", () => {
2094
+ resetState();
2095
+ settleInit();
2096
+ });
2097
+ proc.on("close", () => {
2098
+ resetState();
2099
+ settleInit();
2100
+ });
2101
+ send({
2102
+ jsonrpc: "2.0",
2103
+ id: 1,
2104
+ method: "initialize",
2105
+ params: { processId: process.pid, capabilities: {}, rootUri: pathToFileURL(opts.cwd).href }
2106
+ });
2052
2107
  });
2053
- child.stdout?.on("data", (c) => parser.push(c));
2054
- child.on("error", () => finish([]));
2055
- child.on("close", () => finish([]));
2056
- send({
2057
- jsonrpc: "2.0",
2058
- id: 1,
2059
- method: "initialize",
2060
- params: { processId: process.pid, capabilities: {}, rootUri: pathToFileURL(opts.cwd).href }
2108
+ return initPromise;
2109
+ };
2110
+ const syncDocument = (file) => {
2111
+ const uri = pathToFileURL(file).href;
2112
+ let text = "";
2113
+ try {
2114
+ text = readFileSync4(file, "utf8");
2115
+ } catch {
2116
+ }
2117
+ const prevVersion = opened.get(uri);
2118
+ if (prevVersion === void 0) {
2119
+ opened.set(uri, 1);
2120
+ send({
2121
+ jsonrpc: "2.0",
2122
+ method: "textDocument/didOpen",
2123
+ params: { textDocument: { uri, languageId: languageIdFor(file), version: 1, text } }
2124
+ });
2125
+ } else {
2126
+ const version = prevVersion + 1;
2127
+ opened.set(uri, version);
2128
+ send({
2129
+ jsonrpc: "2.0",
2130
+ method: "textDocument/didChange",
2131
+ params: {
2132
+ textDocument: { uri, version },
2133
+ contentChanges: [{ text }]
2134
+ }
2135
+ });
2136
+ }
2137
+ return uri;
2138
+ };
2139
+ const request = (method, params, timeoutMs = opts.timeoutMs ?? 8e3) => new Promise((resolve) => {
2140
+ const id = nextId++;
2141
+ const timer = setTimeout(() => {
2142
+ pendingRpc.delete(id);
2143
+ resolve(void 0);
2144
+ }, timeoutMs);
2145
+ pendingRpc.set(id, (res) => {
2146
+ clearTimeout(timer);
2147
+ resolve(res);
2061
2148
  });
2149
+ send({ jsonrpc: "2.0", id, method, params });
2062
2150
  });
2151
+ return {
2152
+ async diagnostics(file, timeoutMs = opts.timeoutMs ?? 8e3) {
2153
+ await ensureStarted();
2154
+ if (!child) return [];
2155
+ const uri = pathToFileURL(file).href;
2156
+ return new Promise((resolve) => {
2157
+ const timer = setTimeout(() => {
2158
+ const list2 = diagWaiters.get(uri) ?? [];
2159
+ diagWaiters.set(
2160
+ uri,
2161
+ list2.filter((w) => w !== onDiag)
2162
+ );
2163
+ resolve([]);
2164
+ }, timeoutMs);
2165
+ const onDiag = (diags) => {
2166
+ clearTimeout(timer);
2167
+ resolve(diags);
2168
+ };
2169
+ const list = diagWaiters.get(uri) ?? [];
2170
+ list.push(onDiag);
2171
+ diagWaiters.set(uri, list);
2172
+ syncDocument(file);
2173
+ });
2174
+ },
2175
+ async hover(file, line, character, timeoutMs) {
2176
+ await ensureStarted();
2177
+ if (!child) return "";
2178
+ const uri = syncDocument(file);
2179
+ const res = await request("textDocument/hover", { textDocument: { uri }, position: { line, character } }, timeoutMs);
2180
+ return extractHoverText(res?.contents);
2181
+ },
2182
+ async definition(file, line, character, timeoutMs) {
2183
+ await ensureStarted();
2184
+ if (!child) return [];
2185
+ const uri = syncDocument(file);
2186
+ const res = await request("textDocument/definition", { textDocument: { uri }, position: { line, character } }, timeoutMs);
2187
+ return normalizeLocations(res);
2188
+ },
2189
+ async references(file, line, character, timeoutMs) {
2190
+ await ensureStarted();
2191
+ if (!child) return [];
2192
+ const uri = syncDocument(file);
2193
+ const res = await request(
2194
+ "textDocument/references",
2195
+ { textDocument: { uri }, position: { line, character }, context: { includeDeclaration: true } },
2196
+ timeoutMs
2197
+ );
2198
+ return normalizeLocations(res);
2199
+ },
2200
+ async close() {
2201
+ if (!child) return;
2202
+ const proc = child;
2203
+ send({ jsonrpc: "2.0", id: nextId++, method: "shutdown" });
2204
+ send({ jsonrpc: "2.0", method: "exit" });
2205
+ resetState();
2206
+ const killer = setTimeout(() => {
2207
+ try {
2208
+ proc.kill();
2209
+ } catch {
2210
+ }
2211
+ }, 200);
2212
+ killer.unref?.();
2213
+ }
2214
+ };
2063
2215
  }
2064
2216
 
2065
2217
  // src/tools/diagnostics.ts
2066
2218
  var SEVERITY = { 1: "error", 2: "warning", 3: "info", 4: "hint" };
2067
- var SCHEMA = z14.object({ path: z14.string().describe("File to check (absolute or relative to the working directory)") });
2219
+ var SCHEMA = z14.object({
2220
+ path: z14.string().describe("File to check (absolute or relative to the working directory)"),
2221
+ action: z14.enum(["diagnostics", "hover", "definition", "references"]).optional().describe("LSP query action, default diagnostics"),
2222
+ line: z14.number().int().min(1).optional().describe("1-based line number for hover, definition, or references"),
2223
+ character: z14.number().int().min(1).optional().describe("1-based column number for hover, definition, or references")
2224
+ });
2068
2225
  function createDiagnosticsTool(servers) {
2226
+ const warmPool = /* @__PURE__ */ new Map();
2227
+ const clientFor = (server, cwd) => {
2228
+ const key = `${cwd}::${server.command}::${(server.args ?? []).join("\0")}`;
2229
+ let client = warmPool.get(key);
2230
+ if (!client) {
2231
+ client = createWarmLspClient({ server, cwd, timeoutMs: 15e3 });
2232
+ warmPool.set(key, client);
2233
+ }
2234
+ return client;
2235
+ };
2069
2236
  return defineTool({
2070
2237
  name: "diagnostics",
2071
- description: 'Run the configured LSP server(s) on one file and return its diagnostics (errors/warnings). Configure servers under "lsp.servers" in ~/.bccli/config.json, e.g. { "extensions": [".ts"], "command": "typescript-language-server", "args": ["--stdio"] }.',
2238
+ description: 'Query the configured LSP server(s) on a file for diagnostics (errors/warnings), hover docs, definition, or references. Configure servers under "lsp.servers" in ~/.bccli/config.json, e.g. { "extensions": [".ts"], "command": "typescript-language-server", "args": ["--stdio"] }.',
2072
2239
  schema: SCHEMA,
2073
2240
  kind: "read",
2074
2241
  target: (input) => input.path,
@@ -2079,7 +2246,24 @@ function createDiagnosticsTool(servers) {
2079
2246
  if (!server) {
2080
2247
  return { output: t("No LSP server is configured for {ext} files.", { ext: ext || "(none)" }), isError: true };
2081
2248
  }
2082
- const diags = await getDiagnostics({ server, cwd: ctx.cwd, file, timeoutMs: 15e3 });
2249
+ const client = clientFor(server, ctx.cwd);
2250
+ const action = input.action ?? "diagnostics";
2251
+ const line0 = Math.max(0, (input.line ?? 1) - 1);
2252
+ const char0 = Math.max(0, (input.character ?? 1) - 1);
2253
+ if (action === "hover") {
2254
+ const text = await client.hover(file, line0, char0, 15e3);
2255
+ return { output: text || "No hover information." };
2256
+ }
2257
+ if (action === "definition" || action === "references") {
2258
+ const locs = action === "definition" ? await client.definition(file, line0, char0, 15e3) : await client.references(file, line0, char0, 15e3);
2259
+ if (!locs.length) return { output: `No ${action} found.` };
2260
+ const formatted = locs.map((l) => {
2261
+ const rel = relative2(ctx.cwd, l.path).replace(/\\/g, "/") || input.path;
2262
+ return `${rel}:${l.line + 1}:${l.character + 1}`;
2263
+ });
2264
+ return { output: formatted.join("\n") };
2265
+ }
2266
+ const diags = await client.diagnostics(file, 15e3);
2083
2267
  if (!diags.length) return { output: "No diagnostics." };
2084
2268
  const lines = diags.map((d) => `${input.path}:${d.line + 1}:${d.character + 1} ${SEVERITY[d.severity] ?? "error"} ${d.message}`);
2085
2269
  return { output: lines.join("\n"), isError: diags.some((d) => d.severity === 1) };
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  runCommand,
4
4
  runHooks
5
- } from "./chunk-ZEPTUKNW.js";
5
+ } from "./chunk-4W5SKQDC.js";
6
6
  import {
7
7
  ConfigError
8
8
  } from "./chunk-RX4UQX43.js";
@@ -72,8 +72,12 @@ async function runPrint(rt, prompt, io = { out: process.stdout, err: process.std
72
72
  wroteText = true;
73
73
  } else {
74
74
  result += event.delta;
75
+ if (format === "stream-json") emit({ type: "text_delta", delta: event.delta });
75
76
  }
76
77
  break;
78
+ case "thinking":
79
+ if (format === "stream-json") emit({ type: "thinking_delta", delta: event.delta });
80
+ break;
77
81
  case "textReplace":
78
82
  result = event.text;
79
83
  break;
@@ -758,7 +758,7 @@ function createProvider(options) {
758
758
  const isBotConnectorCloud = /^https:\/\/api\.botconnector\.id\/v1\/?$/.test(baseURL) && apiKey?.startsWith("bc_live_");
759
759
  if (isBotConnectorCloud) {
760
760
  headers["x-botconnector-client"] = "bccli";
761
- headers["x-botconnector-client-version"] = "0.4.2";
761
+ headers["x-botconnector-client-version"] = "0.4.3";
762
762
  }
763
763
  const useNativeResponses = isBotConnectorCloud && providerId === "bc-cloud" && model === "gpt-6-luna";
764
764
  const useResponsesWebSocket = useNativeResponses && Boolean(apiKey) && (fetchOverride === void 0 || responsesWebSocketFactory !== void 0);
@@ -789,7 +789,7 @@ function createProvider(options) {
789
789
  baseURL: baseURL.replace(/\/+$/, ""),
790
790
  defaultHeaders: {
791
791
  "x-botconnector-client": "bccli",
792
- "x-botconnector-client-version": "0.4.2"
792
+ "x-botconnector-client-version": "0.4.3"
793
793
  }
794
794
  });
795
795
  lunaWs = responsesWebSocketFactory ? responsesWebSocketFactory(client) : new ResponsesWS(client, { reconnect: null });
@@ -91,7 +91,7 @@ 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-5OZI2GQQ.js");
94
+ const { runProviderCommand } = await import("./providerCli-V2576JXE.js");
95
95
  const { parseCliArgs: parse } = await import("./args-NRTZWWEV.js");
96
96
  return runProviderCommand(parse(["provider", "add", providerId]), {
97
97
  env: process.env,