@botconnector/bccli 0.5.0-rc.4 → 0.5.0

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 (181) hide show
  1. package/cli.mjs +14 -6
  2. package/legacy/LICENSE +21 -0
  3. package/legacy/README.en.md +253 -0
  4. package/legacy/README.md +250 -0
  5. package/legacy/dist/abort.d.ts +5 -0
  6. package/legacy/dist/acp-HWHF25KU.js +221 -0
  7. package/legacy/dist/acp.d.ts +44 -0
  8. package/legacy/dist/agent.d.ts +158 -0
  9. package/legacy/dist/agents.d.ts +4 -0
  10. package/legacy/dist/args-XA77XUMD.js +13 -0
  11. package/legacy/dist/args.d.ts +59 -0
  12. package/legacy/dist/budget.d.ts +26 -0
  13. package/legacy/dist/checkpoints.d.ts +35 -0
  14. package/legacy/dist/chunk-2IP5F3EK.js +56 -0
  15. package/legacy/dist/chunk-5CVHTYBQ.js +270 -0
  16. package/legacy/dist/chunk-6LO6YRDY.js +216 -0
  17. package/legacy/dist/chunk-6PJRWKVI.js +50 -0
  18. package/legacy/dist/chunk-7OTFZK4B.js +159 -0
  19. package/legacy/dist/chunk-7U7CMKKI.js +861 -0
  20. package/legacy/dist/chunk-A3NBWIEQ.js +2706 -0
  21. package/legacy/dist/chunk-FY543EQW.js +50 -0
  22. package/legacy/dist/chunk-IDZLHEVL.js +524 -0
  23. package/legacy/dist/chunk-IF5RNXBI.js +341 -0
  24. package/legacy/dist/chunk-M5T4AM4I.js +256 -0
  25. package/legacy/dist/chunk-NGFWBVHD.js +71 -0
  26. package/legacy/dist/chunk-OCEQ2TRJ.js +1075 -0
  27. package/legacy/dist/chunk-OK2USAXX.js +88 -0
  28. package/legacy/dist/chunk-PSO7R5AD.js +10 -0
  29. package/legacy/dist/chunk-RLSQAZBT.js +78 -0
  30. package/legacy/dist/chunk-VPFCIUC6.js +91 -0
  31. package/legacy/dist/chunk-XP5MTJN2.js +8 -0
  32. package/legacy/dist/chunk-YGBTNDPK.js +202 -0
  33. package/legacy/dist/chunk-YYTFJUO2.js +148 -0
  34. package/legacy/dist/chunk-ZXWRLN7E.js +378 -0
  35. package/legacy/dist/cli.d.ts +1 -0
  36. package/legacy/dist/cli.js +233 -0
  37. package/legacy/dist/clipboardImage.d.ts +17 -0
  38. package/legacy/dist/commands.d.ts +16 -0
  39. package/legacy/dist/completion-IRVEVW6H.js +125 -0
  40. package/legacy/dist/completion.d.ts +14 -0
  41. package/legacy/dist/config-OVWCUVY7.js +25 -0
  42. package/legacy/dist/config-S3CZUIBB.js +34 -0
  43. package/legacy/dist/config.d.ts +99 -0
  44. package/legacy/dist/context.d.ts +13 -0
  45. package/legacy/dist/contextWindow.d.ts +3 -0
  46. package/legacy/dist/diff.d.ts +7 -0
  47. package/legacy/dist/exit.d.ts +9 -0
  48. package/legacy/dist/extensions.d.ts +34 -0
  49. package/legacy/dist/ghBotCli-FYKJDJOD.js +680 -0
  50. package/legacy/dist/ghBotCli.d.ts +9 -0
  51. package/legacy/dist/ghBotDispatch.d.ts +68 -0
  52. package/legacy/dist/ghBotRunner.d.ts +82 -0
  53. package/legacy/dist/ghBotServe.d.ts +21 -0
  54. package/legacy/dist/ghBotWebhook.d.ts +45 -0
  55. package/legacy/dist/hooks.d.ts +27 -0
  56. package/legacy/dist/i18n/id.d.ts +1 -0
  57. package/legacy/dist/i18n.d.ts +16 -0
  58. package/legacy/dist/integrationCli-GY73S2K7.js +876 -0
  59. package/legacy/dist/integrationCli.d.ts +9 -0
  60. package/legacy/dist/keybinds.d.ts +22 -0
  61. package/legacy/dist/login-KSN63EM4.js +18 -0
  62. package/legacy/dist/login.d.ts +21 -0
  63. package/legacy/dist/lsp.d.ts +73 -0
  64. package/legacy/dist/mcp/catalog.d.ts +14 -0
  65. package/legacy/dist/mcp/config.d.ts +36 -0
  66. package/legacy/dist/mcp/manager.d.ts +52 -0
  67. package/legacy/dist/mcp/oauth.d.ts +82 -0
  68. package/legacy/dist/mcpCli-VWSCCDWT.js +113 -0
  69. package/legacy/dist/mcpCli.d.ts +3 -0
  70. package/legacy/dist/mcpServer/serve.d.ts +8 -0
  71. package/legacy/dist/mcpServer/server.d.ts +19 -0
  72. package/legacy/dist/models.d.ts +13 -0
  73. package/legacy/dist/modelsCli-4Q6KFWV2.js +36 -0
  74. package/legacy/dist/modelsCli.d.ts +15 -0
  75. package/legacy/dist/npmRunner-LU43GM3W.js +46 -0
  76. package/legacy/dist/npmRunner.d.ts +21 -0
  77. package/legacy/dist/oauthLogin-JWXQZTMZ.js +210 -0
  78. package/legacy/dist/oauthLogin.d.ts +47 -0
  79. package/legacy/dist/permissions.d.ts +30 -0
  80. package/legacy/dist/pluginSpec.d.ts +25 -0
  81. package/legacy/dist/plugins.d.ts +45 -0
  82. package/legacy/dist/pluginsCli-7XWDLJEL.js +288 -0
  83. package/legacy/dist/pluginsCli.d.ts +16 -0
  84. package/legacy/dist/prCli-FH7N26D4.js +94 -0
  85. package/legacy/dist/prCli.d.ts +46 -0
  86. package/legacy/dist/presets.d.ts +8 -0
  87. package/legacy/dist/print.d.ts +17 -0
  88. package/legacy/dist/provider.d.ts +145 -0
  89. package/legacy/dist/providerCli-YMG4G56R.js +15 -0
  90. package/legacy/dist/providerCli.d.ts +17 -0
  91. package/legacy/dist/providers.d.ts +6 -0
  92. package/legacy/dist/reasoning.d.ts +12 -0
  93. package/legacy/dist/repomap.d.ts +10 -0
  94. package/legacy/dist/sdk.d.ts +61 -0
  95. package/legacy/dist/sdk.js +6532 -0
  96. package/legacy/dist/serve-HSFF6MGB.js +178 -0
  97. package/legacy/dist/serve-HUQNVOSP.js +532 -0
  98. package/legacy/dist/serve.d.ts +37 -0
  99. package/legacy/dist/serveHeartbeat.d.ts +14 -0
  100. package/legacy/dist/serveProtocol.d.ts +47 -0
  101. package/legacy/dist/session-YM36TGU7.js +12 -0
  102. package/legacy/dist/session.d.ts +33 -0
  103. package/legacy/dist/setup-3YB6EKYX.js +24 -0
  104. package/legacy/dist/setup.d.ts +79 -0
  105. package/legacy/dist/slash/copy.d.ts +1 -0
  106. package/legacy/dist/slash/diff.d.ts +1 -0
  107. package/legacy/dist/slash/doctor.d.ts +18 -0
  108. package/legacy/dist/slash/export.d.ts +2 -0
  109. package/legacy/dist/slash/info.d.ts +46 -0
  110. package/legacy/dist/slash/memory.d.ts +10 -0
  111. package/legacy/dist/statsCli-I4TSDU3X.js +353 -0
  112. package/legacy/dist/statsCli.d.ts +62 -0
  113. package/legacy/dist/textToolCalls.d.ts +6 -0
  114. package/legacy/dist/thinking.d.ts +17 -0
  115. package/legacy/dist/tokenBudget.d.ts +22 -0
  116. package/legacy/dist/tools/bash.d.ts +54 -0
  117. package/legacy/dist/tools/diagnostics.d.ts +13 -0
  118. package/legacy/dist/tools/edit.d.ts +12 -0
  119. package/legacy/dist/tools/fetch.d.ts +41 -0
  120. package/legacy/dist/tools/glob.d.ts +5 -0
  121. package/legacy/dist/tools/globSafety.d.ts +7 -0
  122. package/legacy/dist/tools/grep.d.ts +25 -0
  123. package/legacy/dist/tools/ignore.d.ts +2 -0
  124. package/legacy/dist/tools/index.d.ts +12 -0
  125. package/legacy/dist/tools/memory.d.ts +13 -0
  126. package/legacy/dist/tools/paths.d.ts +4 -0
  127. package/legacy/dist/tools/plan.d.ts +10 -0
  128. package/legacy/dist/tools/read.d.ts +16 -0
  129. package/legacy/dist/tools/repomap.d.ts +2 -0
  130. package/legacy/dist/tools/sandbox.d.ts +15 -0
  131. package/legacy/dist/tools/shell.d.ts +18 -0
  132. package/legacy/dist/tools/skill.d.ts +3 -0
  133. package/legacy/dist/tools/task.d.ts +28 -0
  134. package/legacy/dist/tools/todo.d.ts +13 -0
  135. package/legacy/dist/tools/types.d.ts +52 -0
  136. package/legacy/dist/tools/websearch.d.ts +9 -0
  137. package/legacy/dist/tools/write.d.ts +5 -0
  138. package/legacy/dist/transferCli-3B4LMOYI.js +73 -0
  139. package/legacy/dist/transferCli.d.ts +23 -0
  140. package/legacy/dist/ui/App.d.ts +7 -0
  141. package/legacy/dist/ui/DiffView.d.ts +3 -0
  142. package/legacy/dist/ui/EditableText.d.ts +7 -0
  143. package/legacy/dist/ui/LinePrompt.d.ts +6 -0
  144. package/legacy/dist/ui/ListPicker.d.ts +11 -0
  145. package/legacy/dist/ui/Markdown.d.ts +8 -0
  146. package/legacy/dist/ui/McpMenu.d.ts +11 -0
  147. package/legacy/dist/ui/ModelPicker.d.ts +6 -0
  148. package/legacy/dist/ui/PermissionPrompt.d.ts +5 -0
  149. package/legacy/dist/ui/PlanApproval.d.ts +5 -0
  150. package/legacy/dist/ui/PromptInput.d.ts +20 -0
  151. package/legacy/dist/ui/ProviderMenu.d.ts +10 -0
  152. package/legacy/dist/ui/Spinner.d.ts +4 -0
  153. package/legacy/dist/ui/StatusBar.d.ts +14 -0
  154. package/legacy/dist/ui/TodoList.d.ts +4 -0
  155. package/legacy/dist/ui/ToolBlock.d.ts +14 -0
  156. package/legacy/dist/ui/complete.d.ts +8 -0
  157. package/legacy/dist/ui/index.d.ts +7 -0
  158. package/legacy/dist/ui/lineEdit.d.ts +42 -0
  159. package/legacy/dist/ui/liveCap.d.ts +29 -0
  160. package/legacy/dist/ui/markdownLayout.d.ts +23 -0
  161. package/legacy/dist/ui/replay.d.ts +4 -0
  162. package/legacy/dist/ui/stdinFilter.d.ts +18 -0
  163. package/legacy/dist/ui/table.d.ts +20 -0
  164. package/legacy/dist/ui/theme.d.ts +5 -0
  165. package/legacy/dist/ui/thinkingMouse.d.ts +38 -0
  166. package/legacy/dist/ui/transcript.d.ts +55 -0
  167. package/legacy/dist/ui/tty.d.ts +11 -0
  168. package/legacy/dist/ui/vim.d.ts +26 -0
  169. package/legacy/dist/ui-TAZMRXRI.js +3044 -0
  170. package/legacy/dist/update-5V425FIK.js +145 -0
  171. package/legacy/dist/update.d.ts +30 -0
  172. package/legacy/dist/verify.d.ts +16 -0
  173. package/legacy/dist/version.d.ts +1 -0
  174. package/legacy/dist/webBudget.d.ts +25 -0
  175. package/legacy/dist/worktree.d.ts +12 -0
  176. package/legacy/package.json +77 -0
  177. package/legacy-compat.mjs +42 -0
  178. package/package.json +24 -4
  179. package/provider-cli.mjs +1 -2
  180. package/providers.mjs +19 -6
  181. package/startup-menu.mjs +14 -13
@@ -0,0 +1,145 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ VERSION
4
+ } from "./chunk-XP5MTJN2.js";
5
+ import {
6
+ t
7
+ } from "./chunk-IDZLHEVL.js";
8
+
9
+ // src/update.ts
10
+ import { spawnSync } from "child_process";
11
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
12
+ import { dirname, join } from "path";
13
+ var REGISTRY_URL = "https://registry.npmjs.org/-/package/@botconnector/bccli/dist-tags";
14
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
15
+ function splitVersion(v) {
16
+ const i = v.indexOf("-");
17
+ const coreStr = i < 0 ? v : v.slice(0, i);
18
+ const preStr = i < 0 ? "" : v.slice(i + 1);
19
+ return {
20
+ core: coreStr.split(".").map((p) => Number.parseInt(p, 10) || 0),
21
+ pre: preStr ? preStr.split(".") : []
22
+ };
23
+ }
24
+ function compareVersions(a, b) {
25
+ const A = splitVersion(a);
26
+ const B = splitVersion(b);
27
+ for (let i = 0; i < Math.max(A.core.length, B.core.length); i++) {
28
+ const x = A.core[i] ?? 0;
29
+ const y = B.core[i] ?? 0;
30
+ if (x !== y) return x < y ? -1 : 1;
31
+ }
32
+ if (!A.pre.length && !B.pre.length) return 0;
33
+ if (!A.pre.length) return 1;
34
+ if (!B.pre.length) return -1;
35
+ for (let i = 0; i < Math.max(A.pre.length, B.pre.length); i++) {
36
+ const x = A.pre[i];
37
+ const y = B.pre[i];
38
+ if (x === void 0) return -1;
39
+ if (y === void 0) return 1;
40
+ const nx = Number.parseInt(x, 10);
41
+ const ny = Number.parseInt(y, 10);
42
+ if (String(nx) === x && String(ny) === y) {
43
+ if (nx !== ny) return nx < ny ? -1 : 1;
44
+ } else if (x !== y) {
45
+ return x < y ? -1 : 1;
46
+ }
47
+ }
48
+ return 0;
49
+ }
50
+ function channelFor(version) {
51
+ return version.includes("-") ? "next" : "latest";
52
+ }
53
+ function cachePath(home) {
54
+ return join(home, "update-check.json");
55
+ }
56
+ function readCache(home) {
57
+ try {
58
+ const parsed = JSON.parse(readFileSync(cachePath(home), "utf8"));
59
+ return typeof parsed?.checkedAt === "string" ? parsed : void 0;
60
+ } catch {
61
+ return void 0;
62
+ }
63
+ }
64
+ function writeCache(home, cache) {
65
+ mkdirSync(home, { recursive: true });
66
+ const tmp = `${cachePath(home)}.tmp`;
67
+ writeFileSync(tmp, JSON.stringify(cache));
68
+ renameSync(tmp, cachePath(home));
69
+ }
70
+ function dueForCheck(cache, now, intervalMs = CHECK_INTERVAL_MS) {
71
+ if (!cache) return true;
72
+ const at = Date.parse(cache.checkedAt);
73
+ if (!Number.isFinite(at)) return true;
74
+ return now.getTime() - at >= intervalMs;
75
+ }
76
+ function updateNotice(current, latest) {
77
+ if (!latest || latest.length > 64 || !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(latest) || compareVersions(current, latest) >= 0) return void 0;
78
+ return t("A new bccli is available: {latest} (you have {current}). Run: bccli update", { latest, current });
79
+ }
80
+ function notifyOnce(home, cache) {
81
+ const message = updateNotice(VERSION, cache?.latest);
82
+ if (!message || !cache?.latest || cache.notified === cache.latest) return void 0;
83
+ writeCache(home, { ...cache, notified: cache.latest });
84
+ return message;
85
+ }
86
+ async function checkForUpdate(deps) {
87
+ try {
88
+ const cached = readCache(deps.home);
89
+ if (!dueForCheck(cached, deps.now)) return notifyOnce(deps.home, cached);
90
+ const res = await deps.fetch(REGISTRY_URL, { signal: AbortSignal.timeout(deps.timeoutMs ?? 3e3) });
91
+ if (!res.ok) return notifyOnce(deps.home, cached);
92
+ const tags = await res.json();
93
+ const latest = tags[channelFor(VERSION)] ?? tags.latest;
94
+ const updated = { checkedAt: deps.now.toISOString(), latest, notified: cached?.notified };
95
+ writeCache(deps.home, updated);
96
+ return notifyOnce(deps.home, updated);
97
+ } catch {
98
+ return void 0;
99
+ }
100
+ }
101
+ function updateSpawnCommand(version, platform = process.platform, nodeExecPath = process.execPath, targetVersion) {
102
+ const spec = `@botconnector/bccli@${targetVersion ?? channelFor(version)}`;
103
+ if (platform !== "win32") return { command: "npm", args: ["i", "-g", spec] };
104
+ const npmCli = join(dirname(nodeExecPath), "node_modules", "npm", "bin", "npm-cli.js");
105
+ return { command: nodeExecPath, args: [npmCli, "i", "-g", spec], npmCli };
106
+ }
107
+ async function runUpdateCommand(deps) {
108
+ const current = deps.env.BCCLI_VERSION_OVERRIDE ?? VERSION;
109
+ let target;
110
+ try {
111
+ const response = await (deps.fetch ?? fetch)(REGISTRY_URL, {
112
+ signal: AbortSignal.timeout(8e3),
113
+ cache: "no-store",
114
+ headers: { "cache-control": "no-cache" }
115
+ });
116
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
117
+ const tags = await response.json();
118
+ target = tags[channelFor(current)];
119
+ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(target ?? "")) throw new Error("invalid registry version");
120
+ } catch (error) {
121
+ console.error(`Could not verify BCCLI update version: ${String(error)}. No changes made.`);
122
+ return 1;
123
+ }
124
+ if (compareVersions(target, current) <= 0) {
125
+ console.log(`BCCLI ${current} is already up to date (registry: ${target}); no changes made.`);
126
+ return 0;
127
+ }
128
+ const spec = updateSpawnCommand(current, deps.platform, deps.nodeExecPath, target);
129
+ if (spec.npmCli && !existsSync(spec.npmCli)) {
130
+ console.error(`npm CLI was not found at ${spec.npmCli}. Update manually with: npm install -g @botconnector/bccli@${channelFor(deps.env.BCCLI_VERSION_OVERRIDE ?? VERSION)}`);
131
+ return 1;
132
+ }
133
+ const r = (deps.spawn ?? spawnSync)(spec.command, spec.args, { stdio: "inherit", env: deps.env, shell: false });
134
+ if (r.error) console.error(`BCCLI update failed: ${r.error.message}. Try: npm install -g @botconnector/bccli@${channelFor(deps.env.BCCLI_VERSION_OVERRIDE ?? VERSION)}`);
135
+ return typeof r.status === "number" ? r.status : 1;
136
+ }
137
+ export {
138
+ channelFor,
139
+ checkForUpdate,
140
+ compareVersions,
141
+ dueForCheck,
142
+ runUpdateCommand,
143
+ updateNotice,
144
+ updateSpawnCommand
145
+ };
@@ -0,0 +1,30 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ export interface UpdateCache {
3
+ checkedAt: string;
4
+ latest?: string;
5
+ notified?: string;
6
+ }
7
+ /** Simplified semver: numeric cores, prerelease sorts below release, numeric prerelease parts compare numerically. */
8
+ export declare function compareVersions(a: string, b: string): number;
9
+ export declare function channelFor(version: string): 'next' | 'latest';
10
+ export declare function dueForCheck(cache: UpdateCache | undefined, now: Date, intervalMs?: number): boolean;
11
+ export declare function updateNotice(current: string, latest: string | undefined): string | undefined;
12
+ /** Best-effort registry check with a 24h cache; never throws, never blocks startup for long. */
13
+ export declare function checkForUpdate(deps: {
14
+ home: string;
15
+ fetch: typeof fetch;
16
+ now: Date;
17
+ timeoutMs?: number;
18
+ }): Promise<string | undefined>;
19
+ export declare function updateSpawnCommand(version: string, platform?: NodeJS.Platform, nodeExecPath?: string, targetVersion?: string): {
20
+ command: string;
21
+ args: string[];
22
+ npmCli?: string;
23
+ };
24
+ export declare function runUpdateCommand(deps: {
25
+ env: NodeJS.ProcessEnv;
26
+ spawn?: typeof spawnSync;
27
+ platform?: NodeJS.Platform;
28
+ nodeExecPath?: string;
29
+ fetch?: typeof fetch;
30
+ }): Promise<number>;
@@ -0,0 +1,16 @@
1
+ import { runCommand } from './tools/bash';
2
+ export interface VerifyFailure {
3
+ cmd: string;
4
+ exitCode: number;
5
+ output: string;
6
+ }
7
+ /** Fix rounds per user turn; keeps a failing verify command from looping forever. */
8
+ export declare const MAX_VERIFY_ROUNDS = 2;
9
+ export declare function runVerify(commands: string[], opts: {
10
+ cwd: string;
11
+ env: NodeJS.ProcessEnv;
12
+ signal: AbortSignal;
13
+ timeoutMs?: number;
14
+ run?: typeof runCommand;
15
+ }): Promise<VerifyFailure[]>;
16
+ export declare function verifyFollowup(failures: VerifyFailure[], maxBytes?: number): string;
@@ -0,0 +1 @@
1
+ export declare const VERSION: string;
@@ -0,0 +1,25 @@
1
+ import type { ChatMessage } from './provider';
2
+ export declare const isStub: (content: string) => boolean;
3
+ /**
4
+ * Replaces old `fetch` results with a one-line stub, in one batch and only once they add up to more than
5
+ * PRUNE_MIN_TOKENS, so the prompt prefix (and its cache) changes rarely. The newest KEEP_TOKENS worth stay intact.
6
+ */
7
+ export declare function pruneOldFetches(messages: ChatMessage[]): boolean;
8
+ export interface WebBudgetLimits {
9
+ perTurn?: number;
10
+ sessionChars?: number;
11
+ }
12
+ export declare class WebBudget {
13
+ private fetches;
14
+ private chars;
15
+ private readonly perTurn;
16
+ private readonly sessionChars;
17
+ constructor(limits?: WebBudgetLimits);
18
+ startTurn(): void;
19
+ /** Gives back a reservation, e.g. when the fetch was served from cache and cost nothing. */
20
+ refund(): void;
21
+ reset(): void;
22
+ /** Reserves one fetch; returns the refusal to show the model when over budget. */
23
+ take(): string | undefined;
24
+ record(chars: number): void;
25
+ }
@@ -0,0 +1,12 @@
1
+ export interface WorktreePlan {
2
+ path: string;
3
+ created: boolean;
4
+ }
5
+ /**
6
+ * Resolve (creating when needed) the sibling worktree for `-w <name>`: always derived from the
7
+ * MAIN checkout, so running inside an existing worktree nests nothing. The returned path becomes
8
+ * the session cwd, so config, prompts, sessions, and permissions all follow it.
9
+ */
10
+ export declare function resolveWorktree(cwd: string, name: string, bin?: string): Promise<WorktreePlan>;
11
+ /** Read-only status for /worktree: what exists and how to remove it safely. */
12
+ export declare function worktreeListText(cwd: string, bin?: string): Promise<string>;
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@botconnector/bccli",
3
+ "version": "0.4.17",
4
+ "description": "BCCLI — BotConnector AI coding agent for the terminal",
5
+ "type": "module",
6
+ "bin": {
7
+ "bccli": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "engines": {
13
+ "node": ">=22"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/farijarifriyanto-debug/bccli.git"
18
+ },
19
+ "license": "MIT",
20
+ "scripts": {
21
+ "build": "tsup && tsc -p tsconfig.build.json",
22
+ "typecheck": "tsc --noEmit",
23
+ "lint": "biome lint --error-on-warnings src test",
24
+ "test": "vitest run",
25
+ "coverage": "vitest run --coverage"
26
+ },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.32.1",
29
+ "diff": "^9.0.0",
30
+ "ink": "^8.0.0",
31
+ "openai": "^7.27.0",
32
+ "react": "^19.3.0",
33
+ "undici": "^8.12.1",
34
+ "ws": "^8.22.0",
35
+ "yaml": "^2.9.1",
36
+ "zod": "^4.6.5"
37
+ },
38
+ "devDependencies": {
39
+ "@biomejs/biome": "^2.5.14",
40
+ "@types/node": "^26.6.3",
41
+ "@types/react": "^19.3.0",
42
+ "@types/ws": "^8.18.2",
43
+ "@vitest/coverage-v8": "^5.0.5",
44
+ "ink-testing-library": "^4.0.0",
45
+ "tsup": "^9.0.0",
46
+ "typescript": "^7.0.2",
47
+ "vitest": "^5.0.2"
48
+ },
49
+ "overrides": {
50
+ "esbuild": "0.28.2"
51
+ },
52
+ "main": "dist/sdk.js",
53
+ "types": "dist/sdk.d.ts",
54
+ "exports": {
55
+ ".": {
56
+ "types": "./dist/sdk.d.ts",
57
+ "default": "./dist/sdk.js"
58
+ }
59
+ },
60
+ "keywords": [
61
+ "bccli",
62
+ "ai",
63
+ "coding-agent",
64
+ "agent",
65
+ "cli",
66
+ "tui",
67
+ "terminal",
68
+ "mcp",
69
+ "openai",
70
+ "indonesia"
71
+ ],
72
+ "author": "BotConnector (https://botconnector.id)",
73
+ "bugs": {
74
+ "url": "https://github.com/farijarifriyanto-debug/bccli/issues"
75
+ },
76
+ "homepage": "https://github.com/farijarifriyanto-debug/bccli#readme"
77
+ }
@@ -0,0 +1,42 @@
1
+ // Stable CLI compatibility bridge. There is only one user-visible executable:
2
+ // bccli. Explicit legacy commands continue to use the original BCCLI 0.4.17
3
+ // implementation bundled within the same distribution. Never reinterpret a
4
+ // legacy subcommand as a chargeable AI prompt.
5
+ import { spawn } from 'node:child_process'
6
+ import { existsSync } from 'node:fs'
7
+ import { fileURLToPath } from 'node:url'
8
+
9
+ const LEGACY_COMMANDS = new Set([
10
+ 'login', 'logout', 'mcp', 'connect', 'disconnect', 'integrations',
11
+ 'update', 'serve', 'acp', 'plugins', 'gh-bot', 'completion',
12
+ 'stats', 'export', 'import', 'pr',
13
+ ])
14
+ const LEGACY_OPTIONS = new Set([
15
+ '--output-format', '--fail-on-tool-error', '--allow-all',
16
+ '--allowed-tools', '--reasoning', '--lang', '--permission-mode',
17
+ '--worktree', '-w', '--fallback-model', '--base-url',
18
+ ])
19
+
20
+ export function legacyCompatibilityMode(argv) {
21
+ if (!Array.isArray(argv) || argv.length === 0) return false
22
+ if (LEGACY_COMMANDS.has(argv[0])) return true
23
+ // Explicit flags from BCCLI 0.4.17 preserve that version's exact
24
+ // semantics, rather than silently mapping to the new engine.
25
+ if (argv.some(arg => LEGACY_OPTIONS.has(arg) || [...LEGACY_OPTIONS].some(flag => arg.startsWith(flag + '=')))) return true
26
+ // The old provider CLI permits custom providers, whereas the new CLI
27
+ // accepts only fixed HTTPS presets. Keep the legacy route explicit.
28
+ if (argv[0] === 'provider' && argv[1] === 'add' && argv.some(arg => ['--url','--name','--key-env'].includes(arg))) return true
29
+ return false
30
+ }
31
+
32
+ export async function runLegacyCli(argv, { spawnImpl = spawn, node = process.execPath, env = process.env, cwd = process.cwd() } = {}) {
33
+ const entrypoint = fileURLToPath(new URL('./legacy/dist/cli.js', import.meta.url))
34
+ if (spawnImpl === spawn && !existsSync(entrypoint)) throw Error('Bundled BCCLI compatibility files are missing; reinstall the release package. No AI request was sent.')
35
+ const child = spawnImpl(node, [entrypoint, ...argv], {
36
+ cwd, env, stdio: 'inherit', shell: false, windowsHide: false,
37
+ })
38
+ return new Promise((resolve, reject) => {
39
+ child.once('error', reject)
40
+ child.once('exit', (code, signal) => resolve(code ?? (signal ? 130 : 1)))
41
+ })
42
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@botconnector/bccli",
3
- "version": "0.5.0-rc.4",
4
- "description": "BCCLI | BotConnector coding agent powered by Qwen Code",
3
+ "version": "0.5.0",
4
+ "description": "BCCLI | BotConnector multi-provider coding agent with stable CLI/SDK compatibility",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "bccli": "./cli.mjs"
@@ -10,6 +10,15 @@
10
10
  "node": ">=22"
11
11
  },
12
12
  "dependencies": {
13
+ "@modelcontextprotocol/sdk": "^1.32.1",
14
+ "diff": "^9.0.0",
15
+ "ink": "^8.0.0",
16
+ "openai": "^7.27.0",
17
+ "react": "^19.3.0",
18
+ "undici": "^8.12.1",
19
+ "ws": "^8.22.0",
20
+ "yaml": "^2.9.1",
21
+ "zod": "^4.6.5",
13
22
  "@qwen-code/qwen-code": "0.26.3"
14
23
  },
15
24
  "scripts": {
@@ -21,6 +30,7 @@
21
30
  },
22
31
  "license": "Apache-2.0",
23
32
  "overrides": {
33
+ "esbuild": "0.28.2",
24
34
  "sharp": "0.35.5"
25
35
  },
26
36
  "files": [
@@ -35,17 +45,27 @@
35
45
  "model-command.mjs",
36
46
  "providers.mjs",
37
47
  "provider-cli.mjs",
38
- "startup-menu.mjs"
48
+ "startup-menu.mjs",
49
+ "legacy-compat.mjs",
50
+ "legacy"
39
51
  ],
40
52
  "repository": {
41
53
  "type": "git",
42
- "url": "git+https://github.com/farijarifriyanto-debug/bccli-qwen-engine.git"
54
+ "url": "git+https://github.com/farijarifriyanto-debug/bccli-runtime.git"
43
55
  },
44
56
  "homepage": "https://github.com/farijarifriyanto-debug/bccli#readme",
45
57
  "bugs": {
46
58
  "url": "https://github.com/farijarifriyanto-debug/bccli/issues"
47
59
  },
48
60
  "author": "BICO Inovasi Digital / BotConnector",
61
+ "main": "./legacy/dist/sdk.js",
62
+ "types": "./legacy/dist/sdk.d.ts",
63
+ "exports": {
64
+ ".": {
65
+ "types": "./legacy/dist/sdk.d.ts",
66
+ "default": "./legacy/dist/sdk.js"
67
+ }
68
+ },
49
69
  "publishConfig": {
50
70
  "access": "public",
51
71
  "tag": "next"
package/provider-cli.mjs CHANGED
@@ -57,8 +57,7 @@ export async function runProviderCommand(words, {
57
57
  }
58
58
  providerDefinition(id)
59
59
  if (action === 'add') {
60
- if (id === 'botconnector') throw Error('For BotConnector, use the existing "bccli-legacy login bc-cloud".')
61
- out('Provider: ' + PROVIDERS[id].name + ' (direct API; provider billing and terms apply).')
60
+ out('Provider: ' + PROVIDERS[id].name + ' (API use may incur charges; entering a key does not send an AI prompt).')
62
61
  const key = await getKey()
63
62
  save(id, key)
64
63
  select(id,{env})
package/providers.mjs CHANGED
@@ -40,7 +40,7 @@ function ownerDirectory(root, { create = false } = {}) {
40
40
  return true
41
41
  }
42
42
  function keyPath(root, id) {
43
- if (!ID.test(id) || id === 'botconnector') throw Error('Provider key storage requires a supported non-BotConnector provider.')
43
+ if (!ID.test(id)) throw Error('Provider key storage requires a supported provider.')
44
44
  return join(root, 'provider-' + id + '.dpapi')
45
45
  }
46
46
  function dpapi(script, input, { platform = process.platform, runner = spawnSync } = {}) {
@@ -54,7 +54,6 @@ function dpapi(script, input, { platform = process.platform, runner = spawnSync
54
54
  }
55
55
  export function saveProviderKey(id, key, { root = providerRoot(), platform = process.platform, runner = spawnSync } = {}) {
56
56
  providerDefinition(id)
57
- if (id === 'botconnector') throw Error('Use bccli-legacy login bc-cloud or BOTCONNECTOR_API_KEY for BotConnector credentials.')
58
57
  if (typeof key !== 'string' || !key.trim() || key.length > 8192 || /[\r\n]/.test(key)) throw Error('API key cannot be empty, multiline, or oversized.')
59
58
  if (platform !== 'win32') throw Error('On non-Windows systems, use the provider API key environment variable.')
60
59
  ownerDirectory(root, { create: true })
@@ -70,7 +69,6 @@ export function saveProviderKey(id, key, { root = providerRoot(), platform = pro
70
69
  }
71
70
  export function storedProviderKey(id, { root = providerRoot(), platform = process.platform, runner = spawnSync } = {}) {
72
71
  providerDefinition(id)
73
- if (id === 'botconnector') throw Error('BotConnector credentials are managed by public BCCLI.')
74
72
  if (!ownerDirectory(root)) throw Error('Provider not configured. Run provider add.')
75
73
  const path = keyPath(root, id)
76
74
  let info
@@ -83,7 +81,16 @@ export function storedProviderKey(id, { root = providerRoot(), platform = proces
83
81
  }
84
82
  export function isConfigured(id, { env = process.env, root = providerRoot({env}) } = {}) {
85
83
  providerDefinition(id)
86
- if (id === 'botconnector') return botConnectorCredentialStatus({ env }).configured
84
+ if (id === 'botconnector') {
85
+ if (env.BOTCONNECTOR_API_KEY?.trim()) return true
86
+ try {
87
+ if (ownerDirectory(root)) {
88
+ const info = lstatSync(keyPath(root, id))
89
+ if (info.isFile() && !info.isSymbolicLink() && info.size <= MAX_BLOB_BYTES) return true
90
+ }
91
+ } catch {}
92
+ return botConnectorCredentialStatus({ env }).configured
93
+ }
87
94
  if (env[PROVIDERS[id].env]?.trim()) return true
88
95
  try { return ownerDirectory(root) && lstatSync(keyPath(root, id)).isFile() && !lstatSync(keyPath(root, id)).isSymbolicLink() }
89
96
  catch { return false }
@@ -138,7 +145,6 @@ export function useProvider(id, { env = process.env, root = providerRoot({env})
138
145
  }
139
146
  export function removeProvider(id, { env = process.env, root = providerRoot({env}) } = {}) {
140
147
  providerDefinition(id)
141
- if (id === 'botconnector') throw Error('BotConnector credentials are managed by bccli-legacy or BOTCONNECTOR_API_KEY.')
142
148
  if (!ownerDirectory(root)) return
143
149
  const path = keyPath(root, id)
144
150
  if (existsSync(path)) {
@@ -152,7 +158,14 @@ export function removeProvider(id, { env = process.env, root = providerRoot({env
152
158
  }
153
159
  export function resolveProvider(id, { env = process.env, root = providerRoot({env}) } = {}) {
154
160
  const info = providerDefinition(id)
155
- if (id === 'botconnector') return { id, ...info, key: botConnectorKey({env}) }
161
+ if (id === 'botconnector') {
162
+ // Saved keys take precedence; legacy credentials are read only as fallback.
163
+ try { return { id, ...info, key: storedProviderKey(id, { root }) } }
164
+ catch (error) {
165
+ if (error?.message !== 'Provider not configured. Run provider add.') throw error
166
+ return { id, ...info, key: botConnectorKey({ env }) }
167
+ }
168
+ }
156
169
  // An explicit key saved through "provider add" must win over stale environment
157
170
  // variables. Never silently fall back if the encrypted file is corrupted.
158
171
  let key
package/startup-menu.mjs CHANGED
@@ -74,22 +74,19 @@ export async function startupMenu({
74
74
  providerId = selectedName(value, ids, active)
75
75
  if (!providerId) { out('Pilihan provider tidak dikenal.'); continue }
76
76
  replace = value.endsWith('!')
77
- if (providerId === 'botconnector' && replace) {
78
- out('Gunakan bccli-legacy login bc-cloud untuk mengubah key BotConnector lama; key tidak akan diubah dari menu ini.')
79
- continue
80
- }
81
77
  break
82
78
  }
83
- if ((replace || !configured(providerId)) && providerId !== 'botconnector') {
79
+ let pendingKey
80
+ if (replace || !configured(providerId)) {
84
81
  out('Masukkan API key ' + PROVIDERS[providerId].name + ' (input tersembunyi).')
85
- const key = await askSecret()
86
- // A cancel or missing key cannot silently select an unusable provider.
87
- if (!key) throw Error('Tidak ada API key; provider tidak diubah.')
88
- saveKey(providerId, key)
89
- out('API key disimpan terlindungi untuk akun Windows. Tidak ada request AI.')
82
+ pendingKey = await askSecret()
83
+ if (!pendingKey) throw Error('Tidak ada API key; provider tidak diubah.')
90
84
  }
91
- if (!configured(providerId)) throw Error('Provider ' + providerId + ' belum memiliki API key. Tidak ada permintaan AI.')
92
- const profile = resolve(providerId)
85
+ if (!pendingKey && !configured(providerId)) throw Error('Provider ' + providerId + ' belum memiliki API key. Tidak ada permintaan AI.')
86
+ // Validate before saving so a bad replacement never destroys a working key.
87
+ const profile = pendingKey
88
+ ? { id: providerId, key: pendingKey, endpoint: PROVIDERS[providerId].endpoint }
89
+ : resolve(providerId)
93
90
  const models = await list({ key: profile.key, endpoint: profile.endpoint })
94
91
  if (!models.length) throw Error('Tidak ada model untuk provider ini.')
95
92
  const all = [...models].sort((a, b) => a.id.localeCompare(b.id))
@@ -112,7 +109,11 @@ export async function startupMenu({
112
109
  const candidate = !value ? preferred : /^[0-9]+$/.test(value) ? filtered[Number(value) - 1]?.id : value
113
110
  if (!candidate || !all.some(m => m.id === candidate)) { out('Pilih nomor atau ID model yang tampil.'); continue }
114
111
  selectModel(all, candidate)
115
- // Save active provider only after valid credentials and model are confirmed.
112
+ // Save only after authenticated model discovery and selection.
113
+ if (pendingKey) {
114
+ saveKey(providerId, pendingKey)
115
+ out('API key tersimpan terlindungi. Tidak ada permintaan chat AI.')
116
+ }
116
117
  use(providerId)
117
118
  saveModel(providerId, candidate)
118
119
  return { providerId, model: candidate, key: profile.key, endpoint: profile.endpoint }