@botconnector/bccli 0.5.2 → 0.5.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.
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ 'use strict'
3
+
4
+ // This small launcher runs before importing the coding engine. npm's Windows
5
+ // shims use the system Node; on affected releases we may select a known-safe
6
+ // owner-installed Node without changing the system-wide PATH.
7
+ const { spawn, spawnSync } = require('node:child_process')
8
+ const { existsSync } = require('node:fs')
9
+ const { join, resolve } = require('node:path')
10
+ const { pathToFileURL } = require('node:url')
11
+
12
+ function unsafeWindowsNode(version, platform = process.platform) {
13
+ const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(String(version))
14
+ if (!m) return true
15
+ const [major, minor, patch] = m.slice(1).map(Number)
16
+ return platform === 'win32' && major === 24 && (minor < 22 || (minor === 22 && patch < 1))
17
+ }
18
+ function safeRuntime(version) {
19
+ const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(String(version))
20
+ if (!m) return false
21
+ const [major, minor, patch] = m.slice(1).map(Number)
22
+ return (major === 22 && (minor > 23 || (minor === 23 && patch >= 4))) ||
23
+ (major === 24 && (minor > 22 || (minor === 22 && patch >= 1)))
24
+ }
25
+ function selectRuntime({ platform = process.platform, nodeVersion = process.version,
26
+ execPath = process.execPath, env = process.env,
27
+ probe = (path) => {
28
+ const result = spawnSync(path, ['--version'], { encoding: 'utf8', timeout: 5000, windowsHide: true })
29
+ return result.status === 0 ? result.stdout.trim() : ''
30
+ },
31
+ exists = existsSync } = {}) {
32
+ if (!unsafeWindowsNode(nodeVersion, platform)) return execPath
33
+ const candidates = [
34
+ env.BCCLI_NODE_EXECUTABLE,
35
+ env.LOCALAPPDATA && join(env.LOCALAPPDATA, 'BotConnector', 'BCCLI-Preview', 'runtime', 'node.exe'),
36
+ ].filter(Boolean)
37
+ for (const candidate of candidates) {
38
+ const path = resolve(candidate)
39
+ if (path === resolve(execPath) || !exists(path)) continue
40
+ if (safeRuntime(probe(path))) return path
41
+ }
42
+ throw Error('BCCLI requires Node.js 24.22.1+ or 22.23.4+ on Windows. Set BCCLI_NODE_EXECUTABLE to an installed compatible node.exe, or update Node.js. No coding request was sent.')
43
+ }
44
+ async function main() {
45
+ const exe = selectRuntime()
46
+ const entry = join(__dirname, 'cli.mjs')
47
+ if (exe === process.execPath) {
48
+ await import(pathToFileURL(entry).href)
49
+ return
50
+ }
51
+ const child = spawn(exe, [entry, ...process.argv.slice(2)], {
52
+ stdio: 'inherit', cwd: process.cwd(), env: process.env, windowsHide: false, shell: false,
53
+ })
54
+ child.once('error', (error) => { console.error('BCCLI runtime launch failed: ' + error.message); process.exitCode = 1 })
55
+ child.once('exit', (code, signal) => { process.exitCode = code ?? (signal ? 130 : 1) })
56
+ }
57
+ if (require.main === module) main().catch((error) => {
58
+ console.error(error.message)
59
+ process.exitCode = 1
60
+ })
61
+ module.exports = { unsafeWindowsNode, safeRuntime, selectRuntime }
package/branding.mjs CHANGED
@@ -33,7 +33,7 @@ const SOURCE_HASHES = Object.freeze({
33
33
  'chunks/chunk-B3ZFUGTC.js': 'f3f57aafbaeab56ca58f5d3173751d30e5fef815aeaf8dc9b46e6ea9e85eb528',
34
34
  'chunks/chunk-GGVWG4SI.js': '945c7f4e9b3697003c165b7bc5d007c9b87140d1fad7be084d7b253d8d2b476c',
35
35
  })
36
- const BRAND_REVISION = 'bccli-new-20261031-safe-core-tools-v14-atomic-patch'
36
+ const BRAND_REVISION = 'bccli-20261031-win-bootstrap-session-v15'
37
37
  // Deliberately not Qwen's blue→purple→rose gradient. BotConnector-facing
38
38
  // coding CLI uses a distinct teal→green→amber palette in dark/light themes.
39
39
  export const BCCLI_COLORS = Object.freeze(['#00AFA0', '#22C55E', '#EAB308'])
@@ -249,6 +249,12 @@ export function patchUiChunk(file, text) {
249
249
  'customBannerTitle?customBannerTitle:">_ BCCLI | BotConnector"',
250
250
  'Ink banner title',
251
251
  )
252
+ if (result.includes('const versionLabel=formatVersionLabel(version);')) {
253
+ result = replaceOnce(result,
254
+ 'const versionLabel=formatVersionLabel(version);',
255
+ 'const versionLabel="engine "+formatVersionLabel(version);',
256
+ 'Clarify upstream engine version in BCCLI banner')
257
+ }
252
258
  result = replaceOnce(result,
253
259
  'Add a QWEN.md file to give Qwen Code persistent project context.',
254
260
  'Use /help to explore BCCLI tools and commands.',
package/cli.mjs CHANGED
File without changes
package/core.mjs CHANGED
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url'
8
8
  import { startGatewayProxy } from './proxy.mjs'
9
9
  import { GATEWAY } from './gateway.mjs'
10
10
  import { listProviderDefinitions } from './providers.mjs'
11
+ import { ensureIsolatedSessionSettings } from './session-settings.mjs'
11
12
 
12
13
  export { GATEWAY } from './gateway.mjs'
13
14
  export const DEFAULT_MODEL = 'ling-3.0-flash'
@@ -21,7 +22,7 @@ export function assertCandidateRuntimeSupported({ platform = process.platform, n
21
22
  if (!parts) throw Error('Cannot verify the Node.js version for BCCLI candidate.')
22
23
  const major = Number(parts[1]), minor = Number(parts[2]), patch = Number(parts[3])
23
24
  if (platform === 'win32' && major === 24 && (minor < 22 || (minor === 22 && patch < 1))) {
24
- throw Error('Windows Node.js ' + nodeVersion + ' may crash after coding tools. Use Node.js 24.22.1 or newer, or Node.js 22.23.4. Existing public BCCLI is unaffected.')
25
+ throw Error('Windows Node.js ' + nodeVersion + ' may crash after coding tools. Use Node.js 24.22.1 or newer, or Node.js 22.23.4. No coding request was sent.')
25
26
  }
26
27
  }
27
28
  const MODEL_ID = /^[a-zA-Z0-9][a-zA-Z0-9._:/+-]{0,255}$/
@@ -266,8 +267,10 @@ export async function launchQwen({ key, model, prompt, mcpConfig, continueSessio
266
267
  const proxy = await proxyFactory({ key, endpoint: upstream, liveProvider: true, initialProvider: providerId, searchApiKey })
267
268
  try {
268
269
  const args = [entrypoint, ...qwenArguments(model, prompt, proxy.baseUrl, { mcpConfig, continueSession, resumeSession, projectDir: process.cwd() })]
270
+ const childEnv = childEnvironment(env, proxy.token, model, { endpoint: proxy.baseUrl, providerId, searchAvailable: Boolean(searchApiKey?.trim()) })
271
+ ensureIsolatedSessionSettings(childEnv.QWEN_HOME)
269
272
  const child = spawnImpl(process.execPath, args, {
270
- cwd: process.cwd(), env: childEnvironment(env, proxy.token, model, { endpoint: proxy.baseUrl, providerId, searchAvailable: Boolean(searchApiKey?.trim()) }),
273
+ cwd: process.cwd(), env: childEnv,
271
274
  stdio: 'inherit', windowsHide: false, shell: false,
272
275
  })
273
276
  return await new Promise((resolve, reject) => {
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@botconnector/bccli",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "BCCLI | Full coding engine and legacy SDK with verified Apache-2.0 bundled runtime",
5
5
  "type": "module",
6
6
  "bin": {
7
- "bccli": "./cli.mjs"
7
+ "bccli": "./bccli-bootstrap.cjs"
8
8
  },
9
9
  "engines": {
10
10
  "node": ">=22"
@@ -36,6 +36,8 @@
36
36
  "LICENSE",
37
37
  "THIRD_PARTY_NOTICES.md",
38
38
  "cli.mjs",
39
+ "bccli-bootstrap.cjs",
40
+ "session-settings.mjs",
39
41
  "core.mjs",
40
42
  "proxy.mjs",
41
43
  "gateway.mjs",
@@ -79,8 +81,8 @@
79
81
  },
80
82
  "bccliRuntimeSource": {
81
83
  "repository": "https://github.com/farijarifriyanto-debug/bccli-runtime",
82
- "commit": "bc4924b0613a759bbd46da414b506b68a2f444ff",
83
- "release": "bccli-v0.5.2",
84
- "originalSha256": "a801ea65e710dd5825d2867c0df9ed6e172417214bb64d77a9960e7dae1a3c5e"
84
+ "commit": "eb5e057b3f568e9def717cdc3d8a3b11ce82544f",
85
+ "release": "bccli-v0.5.3",
86
+ "originalSha256": "3a185b33af625ef9df3552e40a9390d4d271b0b977971433357b2f592b5553bc"
85
87
  }
86
88
  }
@@ -0,0 +1,26 @@
1
+ // A provider-scoped QWEN_HOME is intentionally separate from ~/.qwen.
2
+ // The upstream engine warns when the isolated directory has no settings.json;
3
+ // create only the minimal blank configuration, never migrate tokens or plugins.
4
+ import { existsSync, lstatSync, mkdirSync, writeFileSync } from 'node:fs'
5
+ import { dirname, join } from 'node:path'
6
+
7
+ export function ensureIsolatedSessionSettings(home) {
8
+ if (typeof home !== 'string' || !home.trim()) throw Error('BCCLI session home missing.')
9
+ mkdirSync(home, { recursive: true, mode: 0o700 })
10
+ for (const dir of [dirname(home), home]) {
11
+ const info = lstatSync(dir)
12
+ if (!info.isDirectory() || info.isSymbolicLink()) throw Error('BCCLI session directory must not be a symlink.')
13
+ }
14
+ const file = join(home, 'settings.json')
15
+ if (existsSync(file)) {
16
+ if (!lstatSync(file).isFile()) throw Error('BCCLI session settings must be a regular file.')
17
+ return false
18
+ }
19
+ try {
20
+ writeFileSync(file, '{}\n', { flag: 'wx', mode: 0o600 })
21
+ } catch (error) {
22
+ if (error.code !== 'EEXIST' || !lstatSync(file).isFile()) throw error
23
+ return false
24
+ }
25
+ return true
26
+ }