@botconnector/bccli 0.4.0-beta.9 → 0.4.1
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.
- package/README.en.md +253 -0
- package/README.md +93 -6
- package/dist/abort.d.ts +5 -0
- package/dist/acp-3PHBAI64.js +182 -0
- package/dist/acp.d.ts +27 -0
- package/dist/agent.d.ts +143 -0
- package/dist/agents.d.ts +4 -0
- package/dist/args-W6BNKY7K.js +13 -0
- package/dist/args.d.ts +51 -0
- package/dist/budget.d.ts +23 -0
- package/dist/checkpoints.d.ts +34 -0
- package/dist/{chunk-BFZXRKW5.js → chunk-24JN75FZ.js} +29 -7
- package/dist/chunk-45SDLMTV.js +341 -0
- package/dist/chunk-75BECX4F.js +56 -0
- package/dist/{chunk-SJH3YLW4.js → chunk-BC7UXFE4.js} +69 -6
- package/dist/chunk-BGXNR74O.js +733 -0
- package/dist/chunk-BKGFA2PT.js +8 -0
- package/dist/{chunk-CLN4Z5E3.js → chunk-BLOYFIAD.js} +6 -3
- package/dist/chunk-BXQQCUAC.js +145 -0
- package/dist/chunk-FLQPE4S5.js +373 -0
- package/dist/chunk-GN4W3T6E.js +76 -0
- package/dist/{chunk-7S6JXQCH.js → chunk-JHPGOAXU.js} +2 -1
- package/dist/chunk-MFHCQQEN.js +201 -0
- package/dist/chunk-MHIYLXJC.js +143 -0
- package/dist/chunk-PSO7R5AD.js +10 -0
- package/dist/chunk-PZRKU4UM.js +2143 -0
- package/dist/chunk-QZKLZELZ.js +64 -0
- package/dist/chunk-R5RE46CW.js +231 -0
- package/dist/chunk-UZWJQF67.js +500 -0
- package/dist/{chunk-DX7CZTBB.js → chunk-WI22M2ID.js} +14 -9
- package/dist/chunk-WNACAUDK.js +946 -0
- package/dist/chunk-XV7ZVL4J.js +235 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +150 -1382
- package/dist/clipboardImage.d.ts +17 -0
- package/dist/commands.d.ts +16 -0
- package/dist/completion-HPPFDAPB.js +125 -0
- package/dist/completion.d.ts +14 -0
- package/dist/{config-YUNKPCNB.js → config-DIDLDONU.js} +5 -2
- package/dist/config-IU76CPK2.js +34 -0
- package/dist/config.d.ts +97 -0
- package/dist/context.d.ts +13 -0
- package/dist/contextWindow.d.ts +3 -0
- package/dist/diff.d.ts +7 -0
- package/dist/exit.d.ts +9 -0
- package/dist/extensions.d.ts +34 -0
- package/dist/ghBotCli-E3VPHNZY.js +677 -0
- package/dist/ghBotCli.d.ts +9 -0
- package/dist/ghBotDispatch.d.ts +68 -0
- package/dist/ghBotRunner.d.ts +82 -0
- package/dist/ghBotServe.d.ts +21 -0
- package/dist/ghBotWebhook.d.ts +45 -0
- package/dist/hooks.d.ts +27 -0
- package/dist/i18n/id.d.ts +1 -0
- package/dist/i18n.d.ts +16 -0
- package/dist/integrationCli-Q2YPDTPG.js +685 -0
- package/dist/integrationCli.d.ts +9 -0
- package/dist/keybinds.d.ts +22 -0
- package/dist/login-FP3BB44A.js +16 -0
- package/dist/login.d.ts +19 -0
- package/dist/lsp.d.ts +47 -0
- package/dist/mcp/catalog.d.ts +14 -0
- package/dist/mcp/config.d.ts +36 -0
- package/dist/mcp/manager.d.ts +34 -0
- package/dist/mcp/oauth.d.ts +82 -0
- package/dist/{mcpCli-DZWQPTOS.js → mcpCli-SNBDUWJN.js} +42 -9
- package/dist/mcpCli.d.ts +3 -0
- package/dist/mcpServer/serve.d.ts +8 -0
- package/dist/mcpServer/server.d.ts +19 -0
- package/dist/models.d.ts +13 -0
- package/dist/modelsCli-CZ2HNGKR.js +36 -0
- package/dist/modelsCli.d.ts +15 -0
- package/dist/npmRunner-LU43GM3W.js +46 -0
- package/dist/npmRunner.d.ts +21 -0
- package/dist/oauthLogin-6FWQABJ7.js +210 -0
- package/dist/oauthLogin.d.ts +47 -0
- package/dist/permissions.d.ts +30 -0
- package/dist/pluginSpec.d.ts +25 -0
- package/dist/plugins.d.ts +45 -0
- package/dist/pluginsCli-LPIUUDWB.js +284 -0
- package/dist/pluginsCli.d.ts +16 -0
- package/dist/prCli-5IMV3VNQ.js +78 -0
- package/dist/prCli.d.ts +33 -0
- package/dist/presets.d.ts +8 -0
- package/dist/print.d.ts +14 -0
- package/dist/provider.d.ts +140 -0
- package/dist/providerCli-NMUFXAAS.js +15 -0
- package/dist/providerCli.d.ts +17 -0
- package/dist/providers.d.ts +6 -0
- package/dist/reasoning.d.ts +12 -0
- package/dist/repomap.d.ts +10 -0
- package/dist/sdk.d.ts +57 -0
- package/dist/sdk.js +5571 -0
- package/dist/serve-KWOECK5W.js +178 -0
- package/dist/serve-X2ZV4S5U.js +521 -0
- package/dist/serve.d.ts +37 -0
- package/dist/serveHeartbeat.d.ts +14 -0
- package/dist/serveProtocol.d.ts +47 -0
- package/dist/session-GKEXDJKZ.js +12 -0
- package/dist/session.d.ts +30 -0
- package/dist/setup-OV3NWQN5.js +24 -0
- package/dist/setup.d.ts +74 -0
- package/dist/slash/copy.d.ts +1 -0
- package/dist/slash/diff.d.ts +1 -0
- package/dist/slash/doctor.d.ts +18 -0
- package/dist/slash/export.d.ts +2 -0
- package/dist/slash/info.d.ts +45 -0
- package/dist/slash/memory.d.ts +10 -0
- package/dist/statsCli-CF3R7ATL.js +353 -0
- package/dist/statsCli.d.ts +62 -0
- package/dist/textToolCalls.d.ts +6 -0
- package/dist/thinking.d.ts +17 -0
- package/dist/tokenBudget.d.ts +22 -0
- package/dist/tools/bash.d.ts +52 -0
- package/dist/tools/diagnostics.d.ts +5 -0
- package/dist/tools/edit.d.ts +7 -0
- package/dist/tools/fetch.d.ts +41 -0
- package/dist/tools/glob.d.ts +5 -0
- package/dist/tools/globSafety.d.ts +7 -0
- package/dist/tools/grep.d.ts +13 -0
- package/dist/tools/ignore.d.ts +2 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/memory.d.ts +13 -0
- package/dist/tools/paths.d.ts +2 -0
- package/dist/tools/plan.d.ts +10 -0
- package/dist/tools/read.d.ts +16 -0
- package/dist/tools/repomap.d.ts +2 -0
- package/dist/tools/sandbox.d.ts +12 -0
- package/dist/tools/shell.d.ts +18 -0
- package/dist/tools/skill.d.ts +3 -0
- package/dist/tools/task.d.ts +28 -0
- package/dist/tools/todo.d.ts +13 -0
- package/dist/tools/types.d.ts +52 -0
- package/dist/tools/websearch.d.ts +9 -0
- package/dist/tools/write.d.ts +5 -0
- package/dist/transferCli-YUFPI6AH.js +71 -0
- package/dist/transferCli.d.ts +23 -0
- package/dist/ui/App.d.ts +7 -0
- package/dist/ui/DiffView.d.ts +3 -0
- package/dist/ui/EditableText.d.ts +7 -0
- package/dist/ui/LinePrompt.d.ts +6 -0
- package/dist/ui/ListPicker.d.ts +11 -0
- package/dist/ui/Markdown.d.ts +8 -0
- package/dist/ui/McpMenu.d.ts +11 -0
- package/dist/ui/ModelPicker.d.ts +6 -0
- package/dist/ui/PermissionPrompt.d.ts +5 -0
- package/dist/ui/PlanApproval.d.ts +5 -0
- package/dist/ui/PromptInput.d.ts +18 -0
- package/dist/ui/ProviderMenu.d.ts +10 -0
- package/dist/ui/Spinner.d.ts +4 -0
- package/dist/ui/StatusBar.d.ts +9 -0
- package/dist/ui/TodoList.d.ts +4 -0
- package/dist/ui/ToolBlock.d.ts +14 -0
- package/dist/ui/complete.d.ts +1 -0
- package/dist/ui/focusReport.d.ts +1 -0
- package/dist/ui/index.d.ts +7 -0
- package/dist/ui/lineEdit.d.ts +42 -0
- package/dist/ui/replay.d.ts +4 -0
- package/dist/ui/table.d.ts +20 -0
- package/dist/ui/theme.d.ts +5 -0
- package/dist/ui/thinkingMouse.d.ts +33 -0
- package/dist/ui/transcript.d.ts +55 -0
- package/dist/ui/vim.d.ts +26 -0
- package/dist/ui-IVIFNUCC.js +2367 -0
- package/dist/update-TZ2G6KQ3.js +145 -0
- package/dist/update.d.ts +30 -0
- package/dist/verify.d.ts +16 -0
- package/dist/version.d.ts +1 -0
- package/dist/webBudget.d.ts +25 -0
- package/dist/worktree.d.ts +12 -0
- package/package.json +42 -8
- package/dist/args-JPVSGCGY.js +0 -12
- package/dist/chunk-2MGINW4O.js +0 -114
- package/dist/chunk-ELML4KVQ.js +0 -76
- package/dist/chunk-ISGBGFSU.js +0 -52
- package/dist/chunk-KV27KTOX.js +0 -254
- package/dist/chunk-U7GK73CK.js +0 -341
- package/dist/chunk-ZYCYTEKD.js +0 -96
- package/dist/integrationCli-BGT5UZU4.js +0 -223
- package/dist/login-6K32PIXV.js +0 -9
- package/dist/providerCli-Q7LRM2NP.js +0 -12
- package/dist/session-7UB77CEG.js +0 -9
- package/dist/ui-6GTQUJOE.js +0 -1415
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
t
|
|
4
|
+
} from "./chunk-UZWJQF67.js";
|
|
2
5
|
|
|
3
6
|
// src/reasoning.ts
|
|
4
7
|
var REASONING_LEVELS = ["auto", "off", "low", "medium", "high", "max"];
|
|
@@ -9,7 +12,7 @@ function isReasoningLevel(value) {
|
|
|
9
12
|
}
|
|
10
13
|
function parseReasoningLevel(value, label = "reasoning") {
|
|
11
14
|
if (!isReasoningLevel(value)) {
|
|
12
|
-
throw new Error(
|
|
15
|
+
throw new Error(t("{label} must be one of: {levels}", { label, levels: REASONING_LEVELS.join(", ") }));
|
|
13
16
|
}
|
|
14
17
|
return value;
|
|
15
18
|
}
|
|
@@ -32,11 +35,11 @@ function assertReasoningSupported(providerId, level) {
|
|
|
32
35
|
if (level === "auto") return;
|
|
33
36
|
const capability = reasoningCapability(providerId);
|
|
34
37
|
if (!capability) {
|
|
35
|
-
throw new Error("
|
|
38
|
+
throw new Error(t("This provider has no manual reasoning setting yet. Use Auto."));
|
|
36
39
|
}
|
|
37
40
|
if (!capability.levels.includes(level)) {
|
|
38
41
|
const supported = capability.levels.filter((x) => x !== "auto").map(capitalize).join("/");
|
|
39
|
-
throw new Error(
|
|
42
|
+
throw new Error(t("This model/provider does not support reasoning level '{level}'. Use Auto{supported}.", { level, supported: supported ? `/${supported}` : "" }));
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
function reasoningPayload(providerId, level) {
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
defaultOpen
|
|
4
|
+
} from "./chunk-MFHCQQEN.js";
|
|
5
|
+
import {
|
|
6
|
+
removeCredential,
|
|
7
|
+
saveCredential
|
|
8
|
+
} from "./chunk-FLQPE4S5.js";
|
|
9
|
+
import {
|
|
10
|
+
t
|
|
11
|
+
} from "./chunk-UZWJQF67.js";
|
|
12
|
+
|
|
13
|
+
// src/login.ts
|
|
14
|
+
function readSecret(prompt) {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
process.stdout.write(prompt);
|
|
17
|
+
const stdin = process.stdin;
|
|
18
|
+
if (!stdin.isTTY) {
|
|
19
|
+
let data = "";
|
|
20
|
+
stdin.on("data", (d) => {
|
|
21
|
+
data += d;
|
|
22
|
+
});
|
|
23
|
+
stdin.on("end", () => resolve(data.trim()));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
let value = "";
|
|
27
|
+
stdin.setRawMode(true);
|
|
28
|
+
stdin.resume();
|
|
29
|
+
const onData = (buffer) => {
|
|
30
|
+
for (const ch of buffer.toString()) {
|
|
31
|
+
if (ch === "\r" || ch === "\n") {
|
|
32
|
+
stdin.setRawMode(false);
|
|
33
|
+
stdin.pause();
|
|
34
|
+
stdin.off("data", onData);
|
|
35
|
+
process.stdout.write("\n");
|
|
36
|
+
resolve(value.trim());
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (ch === "") process.exit(130);
|
|
40
|
+
if (ch === "\x7F" || ch === "\b") value = value.slice(0, -1);
|
|
41
|
+
else value += ch;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
stdin.on("data", onData);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function readLine(prompt) {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
process.stdout.write(prompt);
|
|
50
|
+
const stdin = process.stdin;
|
|
51
|
+
if (!stdin.isTTY) {
|
|
52
|
+
let data = "";
|
|
53
|
+
stdin.on("data", (d) => {
|
|
54
|
+
data += d;
|
|
55
|
+
});
|
|
56
|
+
stdin.on("end", () => resolve(data.trim()));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
let value = "";
|
|
60
|
+
stdin.setRawMode(true);
|
|
61
|
+
stdin.resume();
|
|
62
|
+
const onData = (buffer) => {
|
|
63
|
+
for (const ch of buffer.toString()) {
|
|
64
|
+
const code = ch.charCodeAt(0);
|
|
65
|
+
if (code === 13 || code === 10) {
|
|
66
|
+
stdin.setRawMode(false);
|
|
67
|
+
stdin.pause();
|
|
68
|
+
stdin.off("data", onData);
|
|
69
|
+
process.stdout.write("\n");
|
|
70
|
+
resolve(value.trim());
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
if (code === 3) process.exit(130);
|
|
74
|
+
if (code === 127 || code === 8) value = value.slice(0, -1);
|
|
75
|
+
else value += ch;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
stdin.on("data", onData);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function defaultLoginDeps() {
|
|
82
|
+
return {
|
|
83
|
+
out: (s) => console.log(s),
|
|
84
|
+
err: (s) => console.error(s),
|
|
85
|
+
env: process.env,
|
|
86
|
+
isTTY: process.stdin.isTTY ?? false,
|
|
87
|
+
open: (url) => defaultOpen(new URL(url)),
|
|
88
|
+
readLine,
|
|
89
|
+
saveCredential: (id, key) => saveCredential(id, key, process.env),
|
|
90
|
+
removeCredential: (id) => removeCredential(id, process.env),
|
|
91
|
+
fetch: globalThis.fetch,
|
|
92
|
+
// Wiring NYATA jalur lama: persis runLogin lama (provider add + prompt key).
|
|
93
|
+
legacyAdd: async (providerId, targetCwd) => {
|
|
94
|
+
const { runProviderCommand } = await import("./providerCli-NMUFXAAS.js");
|
|
95
|
+
const { parseCliArgs: parse } = await import("./args-W6BNKY7K.js");
|
|
96
|
+
return runProviderCommand(parse(["provider", "add", providerId]), {
|
|
97
|
+
env: process.env,
|
|
98
|
+
cwd: targetCwd,
|
|
99
|
+
out: (s) => console.log(s),
|
|
100
|
+
err: (s) => console.error(s),
|
|
101
|
+
readSecret
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
async function runLogin(providerId, cwd, args, deps) {
|
|
107
|
+
if (args.key && args.logout) {
|
|
108
|
+
deps.err(t("Use either --key or --logout, not both."));
|
|
109
|
+
return 1;
|
|
110
|
+
}
|
|
111
|
+
if (args.logout) {
|
|
112
|
+
if (providerId !== "openrouter") {
|
|
113
|
+
deps.err(
|
|
114
|
+
t("Logout is not supported for {id}; use bccli provider remove {id} to delete its stored key and config.", {
|
|
115
|
+
id: providerId
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
const { runOauthLogout } = await import("./oauthLogin-6FWQABJ7.js");
|
|
121
|
+
return runOauthLogout({ out: deps.out, err: deps.err, env: deps.env, removeCredential: deps.removeCredential });
|
|
122
|
+
}
|
|
123
|
+
if (args.key) return deps.legacyAdd(providerId, cwd);
|
|
124
|
+
if (providerId === "openrouter") {
|
|
125
|
+
const { runOauthLogin } = await import("./oauthLogin-6FWQABJ7.js");
|
|
126
|
+
return runOauthLogin({
|
|
127
|
+
out: deps.out,
|
|
128
|
+
err: deps.err,
|
|
129
|
+
env: deps.env,
|
|
130
|
+
isTTY: deps.isTTY,
|
|
131
|
+
open: deps.open,
|
|
132
|
+
readLine: deps.readLine,
|
|
133
|
+
saveCredential: deps.saveCredential,
|
|
134
|
+
fetch: deps.fetch
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return deps.legacyAdd(providerId, cwd);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export {
|
|
141
|
+
readSecret,
|
|
142
|
+
readLine,
|
|
143
|
+
defaultLoginDeps,
|
|
144
|
+
runLogin
|
|
145
|
+
};
|
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
t
|
|
4
|
+
} from "./chunk-UZWJQF67.js";
|
|
5
|
+
|
|
6
|
+
// src/config.ts
|
|
7
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "fs";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { dirname, join } from "path";
|
|
10
|
+
|
|
11
|
+
// src/presets.ts
|
|
12
|
+
var PRESETS = [
|
|
13
|
+
{ id: "bc-cloud", name: "BotConnector Cloud", baseURL: "https://api.botconnector.id/v1", apiKeyEnv: "BOTCONNECTOR_API_KEY" },
|
|
14
|
+
{ id: "openrouter", name: "OpenRouter", baseURL: "https://openrouter.ai/api/v1", apiKeyEnv: "OPENROUTER_API_KEY" },
|
|
15
|
+
{ id: "openai", name: "OpenAI", baseURL: "https://api.openai.com/v1", apiKeyEnv: "OPENAI_API_KEY" },
|
|
16
|
+
{ id: "gemini", name: "Google Gemini", baseURL: "https://generativelanguage.googleapis.com/v1beta/openai", apiKeyEnv: "GEMINI_API_KEY" },
|
|
17
|
+
{ id: "deepseek", name: "DeepSeek", baseURL: "https://api.deepseek.com/v1", apiKeyEnv: "DEEPSEEK_API_KEY" },
|
|
18
|
+
{ id: "groq", name: "Groq", baseURL: "https://api.groq.com/openai/v1", apiKeyEnv: "GROQ_API_KEY" },
|
|
19
|
+
{ id: "ollama-cloud", name: "Ollama Cloud", baseURL: "https://ollama.com/v1", apiKeyEnv: "OLLAMA_API_KEY" },
|
|
20
|
+
{ id: "ollama", name: "Ollama lokal", baseURL: "http://127.0.0.1:11434/v1", local: true },
|
|
21
|
+
{ id: "lmstudio", name: "LM Studio lokal", baseURL: "http://127.0.0.1:1234/v1", local: true }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
// src/keybinds.ts
|
|
25
|
+
var KEYBIND_ACTIONS = ["thinking", "toolOutput", "pasteImage"];
|
|
26
|
+
var DEFAULT_KEYBINDS = {
|
|
27
|
+
thinking: "ctrl+t",
|
|
28
|
+
toolOutput: "ctrl+o",
|
|
29
|
+
pasteImage: "alt+v"
|
|
30
|
+
};
|
|
31
|
+
function defaultKeybinds() {
|
|
32
|
+
return Object.fromEntries(KEYBIND_ACTIONS.map((action) => [action, parseKeybind(DEFAULT_KEYBINDS[action])]));
|
|
33
|
+
}
|
|
34
|
+
function parseKeybind(raw) {
|
|
35
|
+
const spec = raw.trim().toLowerCase();
|
|
36
|
+
const parts = spec.split("+");
|
|
37
|
+
const key = parts.pop() ?? "";
|
|
38
|
+
if (!/^[a-z]$/.test(key)) return void 0;
|
|
39
|
+
if (!parts.length || new Set(parts).size !== parts.length) return void 0;
|
|
40
|
+
if (parts.some((p) => p !== "ctrl" && p !== "alt")) return void 0;
|
|
41
|
+
return { spec, ctrl: parts.includes("ctrl"), meta: parts.includes("alt"), key };
|
|
42
|
+
}
|
|
43
|
+
function matchKeybind(kb, input, key) {
|
|
44
|
+
return kb.ctrl === !!key.ctrl && kb.meta === !!key.meta && input.toLowerCase() === kb.key;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/ui/theme.ts
|
|
48
|
+
var THEMES = ["default", "blue", "amber", "magenta", "mono"];
|
|
49
|
+
var REMAP = {
|
|
50
|
+
blue: { green: "blue" },
|
|
51
|
+
amber: { green: "yellow" },
|
|
52
|
+
magenta: { green: "magenta" }
|
|
53
|
+
};
|
|
54
|
+
var theme = "default";
|
|
55
|
+
function setTheme(name) {
|
|
56
|
+
theme = name;
|
|
57
|
+
}
|
|
58
|
+
function color(name) {
|
|
59
|
+
if (process.env.NO_COLOR) return void 0;
|
|
60
|
+
if (theme === "mono") return void 0;
|
|
61
|
+
return REMAP[theme]?.[name] ?? name;
|
|
62
|
+
}
|
|
63
|
+
var ACCENT = "green";
|
|
64
|
+
|
|
65
|
+
// src/config.ts
|
|
66
|
+
var ConfigError = class extends Error {
|
|
67
|
+
};
|
|
68
|
+
var DEFAULT_MAX_STEPS = null;
|
|
69
|
+
var DEFAULT_CONFIG = {
|
|
70
|
+
model: "bc-cloud/glm-5.3-flash",
|
|
71
|
+
permissionMode: "default",
|
|
72
|
+
reasoning: "auto",
|
|
73
|
+
vision: true,
|
|
74
|
+
networkPolicy: "allow",
|
|
75
|
+
keybinds: defaultKeybinds(),
|
|
76
|
+
verifyCommands: [],
|
|
77
|
+
repoMap: false,
|
|
78
|
+
theme: "default",
|
|
79
|
+
editor: "emacs",
|
|
80
|
+
sandbox: "off",
|
|
81
|
+
providers: Object.fromEntries(PRESETS.map((p) => [p.id, { baseURL: p.baseURL, apiKeyEnv: p.apiKeyEnv }])),
|
|
82
|
+
allow: []
|
|
83
|
+
};
|
|
84
|
+
function bccliHome(env = process.env) {
|
|
85
|
+
return env.BCCLI_HOME ?? join(homedir(), ".bccli");
|
|
86
|
+
}
|
|
87
|
+
function readJsonConfig(path) {
|
|
88
|
+
if (!existsSync(path)) return {};
|
|
89
|
+
try {
|
|
90
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw new ConfigError(t("Invalid config: {path} ({error})", { path, error: error.message }));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function resolvedMaxSteps(global) {
|
|
96
|
+
const value = global.maxSteps;
|
|
97
|
+
if (value === void 0) return DEFAULT_MAX_STEPS;
|
|
98
|
+
if (value === null || value === 0) return null;
|
|
99
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
100
|
+
throw new ConfigError(t("maxSteps must be a positive number, or 0/null to disable the limit."));
|
|
101
|
+
}
|
|
102
|
+
return Math.max(1, Math.floor(value));
|
|
103
|
+
}
|
|
104
|
+
function resolvedSessionRetentionDays(global) {
|
|
105
|
+
const value = global.sessionRetentionDays;
|
|
106
|
+
if (value === void 0 || value === null || value === 0) return null;
|
|
107
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
108
|
+
throw new ConfigError(t("sessionRetentionDays must be a positive number of days, or 0/null to keep sessions."));
|
|
109
|
+
}
|
|
110
|
+
return Math.max(1, Math.floor(value));
|
|
111
|
+
}
|
|
112
|
+
function resolvedNetworkPolicy(global) {
|
|
113
|
+
const value = global.networkPolicy ?? "allow";
|
|
114
|
+
if (value !== "allow" && value !== "offline") {
|
|
115
|
+
throw new ConfigError(t('networkPolicy must be "allow" or "offline".'));
|
|
116
|
+
}
|
|
117
|
+
return value;
|
|
118
|
+
}
|
|
119
|
+
function resolvedPlugins(global) {
|
|
120
|
+
const value = global.plugins;
|
|
121
|
+
if (value === void 0) return [];
|
|
122
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
123
|
+
throw new ConfigError(t("plugins must be an array of strings."));
|
|
124
|
+
}
|
|
125
|
+
return [...value];
|
|
126
|
+
}
|
|
127
|
+
function resolvedUpdateCheck(global) {
|
|
128
|
+
const value = global.updateCheck;
|
|
129
|
+
if (value === void 0) return void 0;
|
|
130
|
+
if (value !== "on" && value !== "off") throw new ConfigError(t('updateCheck must be "on" or "off".'));
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
function resolvedUsageCap(global) {
|
|
134
|
+
const cap = global.usageCap;
|
|
135
|
+
if (cap === void 0) return void 0;
|
|
136
|
+
if (typeof cap !== "object" || cap === null || Array.isArray(cap)) {
|
|
137
|
+
throw new ConfigError(t('usageCap must be an object, e.g. { "tokens": 2000000 }.'));
|
|
138
|
+
}
|
|
139
|
+
const { tokens, usd, prices } = cap;
|
|
140
|
+
const positive = (v) => typeof v === "number" && Number.isFinite(v) && v > 0;
|
|
141
|
+
const nonNegative = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0;
|
|
142
|
+
if (tokens !== void 0 && !positive(tokens)) throw new ConfigError(t("usageCap.tokens must be a number greater than 0."));
|
|
143
|
+
if (usd !== void 0 && !positive(usd)) throw new ConfigError(t("usageCap.usd must be a number greater than 0."));
|
|
144
|
+
if (usd !== void 0 && (prices === void 0 || !Object.keys(prices).length)) {
|
|
145
|
+
throw new ConfigError(t("usageCap.usd requires usageCap.prices (USD per 1M tokens)."));
|
|
146
|
+
}
|
|
147
|
+
if (prices !== void 0) {
|
|
148
|
+
for (const [key, price] of Object.entries(prices)) {
|
|
149
|
+
if (!price || !nonNegative(price.input) || !nonNegative(price.output)) {
|
|
150
|
+
throw new ConfigError(t('usageCap.prices["{key}"] must have numeric input and output (USD per 1M tokens).', { key }));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (tokens === void 0 && usd === void 0) throw new ConfigError(t("usageCap needs at least one of: tokens, usd."));
|
|
155
|
+
return { ...tokens !== void 0 ? { tokens } : {}, ...usd !== void 0 ? { usd } : {}, ...prices !== void 0 ? { prices } : {} };
|
|
156
|
+
}
|
|
157
|
+
function resolvedLsp(global) {
|
|
158
|
+
if (global.lsp === void 0) return void 0;
|
|
159
|
+
const lsp = global.lsp;
|
|
160
|
+
if (typeof global.lsp !== "object" || global.lsp === null || !Array.isArray(lsp.servers)) {
|
|
161
|
+
throw new ConfigError(t('lsp must be an object with a "servers" array.'));
|
|
162
|
+
}
|
|
163
|
+
for (const s of lsp.servers) {
|
|
164
|
+
const server = s;
|
|
165
|
+
if (typeof server?.command !== "string" || !server.command || !Array.isArray(server.extensions) || server.extensions.length === 0 || server.extensions.some((e) => typeof e !== "string" || !e.startsWith(".")) || server.args !== void 0 && (!Array.isArray(server.args) || server.args.some((a) => typeof a !== "string"))) {
|
|
166
|
+
throw new ConfigError(t('Each lsp server needs { "extensions": [".ts"], "command": "...", "args": ["..."] }.'));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { servers: lsp.servers };
|
|
170
|
+
}
|
|
171
|
+
function resolvedSandbox(global) {
|
|
172
|
+
const value = global.sandbox ?? "off";
|
|
173
|
+
if (value !== "off" && value !== "on") throw new ConfigError(t('sandbox must be "off" or "on".'));
|
|
174
|
+
return value;
|
|
175
|
+
}
|
|
176
|
+
function resolvedEditor(global) {
|
|
177
|
+
const value = global.editor ?? "emacs";
|
|
178
|
+
if (value !== "emacs" && value !== "vim") throw new ConfigError(t('editor must be "emacs" or "vim".'));
|
|
179
|
+
return value;
|
|
180
|
+
}
|
|
181
|
+
function resolvedTheme(global) {
|
|
182
|
+
const value = global.theme ?? "default";
|
|
183
|
+
if (typeof value !== "string" || !THEMES.includes(value)) {
|
|
184
|
+
throw new ConfigError(t("theme must be one of: {themes}.", { themes: THEMES.join(", ") }));
|
|
185
|
+
}
|
|
186
|
+
return value;
|
|
187
|
+
}
|
|
188
|
+
function resolvedRepoMap(global) {
|
|
189
|
+
const value = global.repoMap ?? false;
|
|
190
|
+
if (typeof value !== "boolean") throw new ConfigError(t("repoMap must be true or false."));
|
|
191
|
+
return value;
|
|
192
|
+
}
|
|
193
|
+
function resolvedVerifyCommands(global) {
|
|
194
|
+
const value = global.verifyCommands;
|
|
195
|
+
if (value === void 0) return [];
|
|
196
|
+
if (!Array.isArray(value) || value.some((c) => typeof c !== "string" || !c.trim())) {
|
|
197
|
+
throw new ConfigError(t("verifyCommands must be an array of non-empty strings."));
|
|
198
|
+
}
|
|
199
|
+
return value.map((c) => c.trim());
|
|
200
|
+
}
|
|
201
|
+
function resolvedKeybinds(global) {
|
|
202
|
+
const raw = global.keybinds ?? {};
|
|
203
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
204
|
+
throw new ConfigError(t("keybinds must be an object mapping action to binding."));
|
|
205
|
+
}
|
|
206
|
+
const out = {};
|
|
207
|
+
for (const action of KEYBIND_ACTIONS) {
|
|
208
|
+
const spec = raw[action];
|
|
209
|
+
const value = spec === void 0 ? DEFAULT_KEYBINDS[action] : spec;
|
|
210
|
+
if (typeof value !== "string") {
|
|
211
|
+
throw new ConfigError(t('Keybind for "{action}" must be a string like "ctrl+t".', { action }));
|
|
212
|
+
}
|
|
213
|
+
const parsed = parseKeybind(value);
|
|
214
|
+
if (!parsed) {
|
|
215
|
+
throw new ConfigError(t('Keybind "{spec}" for "{action}" is invalid: use ctrl or alt plus one letter, e.g. "ctrl+t".', { spec: value, action }));
|
|
216
|
+
}
|
|
217
|
+
out[action] = parsed;
|
|
218
|
+
}
|
|
219
|
+
for (const key of Object.keys(raw)) {
|
|
220
|
+
if (!KEYBIND_ACTIONS.includes(key)) {
|
|
221
|
+
throw new ConfigError(t('Unknown keybind action "{action}". Known actions: {list}.', { action: key, list: KEYBIND_ACTIONS.join(", ") }));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
function untrustedProviders(project, known) {
|
|
227
|
+
const out = {};
|
|
228
|
+
for (const [id, provider] of Object.entries(project.providers ?? {})) {
|
|
229
|
+
if (known[id] || typeof provider?.baseURL !== "string") continue;
|
|
230
|
+
out[id] = { baseURL: provider.baseURL };
|
|
231
|
+
}
|
|
232
|
+
return out;
|
|
233
|
+
}
|
|
234
|
+
function loadConfig(cwd, env = process.env) {
|
|
235
|
+
const global = readJsonConfig(join(bccliHome(env), "config.json"));
|
|
236
|
+
const project = readJsonConfig(join(cwd, ".bccli", "config.json"));
|
|
237
|
+
const known = { ...DEFAULT_CONFIG.providers, ...global.providers };
|
|
238
|
+
const fromProject = untrustedProviders(project, known);
|
|
239
|
+
return {
|
|
240
|
+
model: project.model ?? global.model ?? DEFAULT_CONFIG.model,
|
|
241
|
+
permissionMode: global.permissionMode ?? DEFAULT_CONFIG.permissionMode,
|
|
242
|
+
reasoning: global.reasoning ?? DEFAULT_CONFIG.reasoning,
|
|
243
|
+
thinkingVisible: global.thinkingVisible === true,
|
|
244
|
+
thinkingClickCollapse: global.thinkingClickCollapse === true,
|
|
245
|
+
sessionRetentionDays: resolvedSessionRetentionDays(global),
|
|
246
|
+
maxSteps: resolvedMaxSteps(global),
|
|
247
|
+
hooks: global.hooks,
|
|
248
|
+
vision: global.vision ?? DEFAULT_CONFIG.vision,
|
|
249
|
+
networkPolicy: resolvedNetworkPolicy(global),
|
|
250
|
+
plugins: resolvedPlugins(global),
|
|
251
|
+
keybinds: resolvedKeybinds(global),
|
|
252
|
+
verifyCommands: resolvedVerifyCommands(global),
|
|
253
|
+
repoMap: resolvedRepoMap(global),
|
|
254
|
+
theme: resolvedTheme(global),
|
|
255
|
+
editor: resolvedEditor(global),
|
|
256
|
+
sandbox: resolvedSandbox(global),
|
|
257
|
+
lsp: resolvedLsp(global),
|
|
258
|
+
usageCap: resolvedUsageCap(global),
|
|
259
|
+
updateCheck: resolvedUpdateCheck(global),
|
|
260
|
+
providers: { ...known, ...fromProject },
|
|
261
|
+
projectProviders: Object.keys(fromProject),
|
|
262
|
+
allow: [...global.allow ?? []]
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function globalConfigPath(env = process.env) {
|
|
266
|
+
return join(bccliHome(env), "config.json");
|
|
267
|
+
}
|
|
268
|
+
function readGlobalPlugins(env = process.env) {
|
|
269
|
+
const raw = readJsonConfig(globalConfigPath(env));
|
|
270
|
+
const value = raw.plugins;
|
|
271
|
+
if (value === void 0) return [];
|
|
272
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
|
|
273
|
+
throw new ConfigError(t("plugins must be an array of strings."));
|
|
274
|
+
}
|
|
275
|
+
return value;
|
|
276
|
+
}
|
|
277
|
+
function writeFileAtomic(path, data, mode) {
|
|
278
|
+
const tmp = `${path}.tmp-${process.pid}-${Date.now()}`;
|
|
279
|
+
try {
|
|
280
|
+
writeFileSync(tmp, data, mode === void 0 ? void 0 : { mode });
|
|
281
|
+
renameSync(tmp, path);
|
|
282
|
+
if (mode !== void 0) chmodSync(path, mode);
|
|
283
|
+
} catch (error) {
|
|
284
|
+
try {
|
|
285
|
+
rmSync(tmp, { force: true });
|
|
286
|
+
} catch {
|
|
287
|
+
}
|
|
288
|
+
throw error;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function quarantineCorruptFile(path, error) {
|
|
292
|
+
const stamp = Date.now();
|
|
293
|
+
let dest = `${path}.corrupt-${stamp}`;
|
|
294
|
+
for (let n = 1; existsSync(dest); n++) dest = `${path}.corrupt-${stamp}-${n}`;
|
|
295
|
+
try {
|
|
296
|
+
renameSync(path, dest);
|
|
297
|
+
} catch {
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
console.error(`${path} is not valid JSON (${error.message}); moved it to ${dest} instead of overwriting it.`);
|
|
301
|
+
return dest;
|
|
302
|
+
}
|
|
303
|
+
function updateGlobalConfig(env, mutate) {
|
|
304
|
+
const path = globalConfigPath(env);
|
|
305
|
+
let cfg = {};
|
|
306
|
+
if (existsSync(path)) {
|
|
307
|
+
cfg = JSON.parse(readFileSync(path, "utf8"));
|
|
308
|
+
}
|
|
309
|
+
mutate(cfg);
|
|
310
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
311
|
+
writeFileAtomic(path, `${JSON.stringify(cfg, null, 2)}
|
|
312
|
+
`);
|
|
313
|
+
}
|
|
314
|
+
function readCredentials(env = process.env) {
|
|
315
|
+
const path = join(bccliHome(env), "credentials");
|
|
316
|
+
if (!existsSync(path)) return {};
|
|
317
|
+
try {
|
|
318
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
319
|
+
} catch (error) {
|
|
320
|
+
quarantineCorruptFile(path, error);
|
|
321
|
+
return {};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function saveCredential(providerId, key, env = process.env) {
|
|
325
|
+
const home = bccliHome(env);
|
|
326
|
+
mkdirSync(home, { recursive: true });
|
|
327
|
+
writeFileAtomic(join(home, "credentials"), JSON.stringify({ ...readCredentials(env), [providerId]: key }, null, 2), 384);
|
|
328
|
+
}
|
|
329
|
+
function resolveModel(config, modelRef, env = process.env) {
|
|
330
|
+
const slash = modelRef.indexOf("/");
|
|
331
|
+
if (slash <= 0 || slash === modelRef.length - 1) {
|
|
332
|
+
throw new ConfigError(t("Model must be in provider/model format, e.g. bc-cloud/glm-5.3-flash (got: {ref})", { ref: modelRef }));
|
|
333
|
+
}
|
|
334
|
+
const providerId = modelRef.slice(0, slash);
|
|
335
|
+
const provider = config.providers[providerId];
|
|
336
|
+
if (!provider) {
|
|
337
|
+
throw new ConfigError(t('Provider "{id}" is not in the config. Available: {list}', { id: providerId, list: Object.keys(config.providers).join(", ") }));
|
|
338
|
+
}
|
|
339
|
+
const apiKey = readCredentials(env)[providerId] || (provider.apiKeyEnv ? env[provider.apiKeyEnv] : void 0) || void 0;
|
|
340
|
+
if (provider.apiKeyEnv && !apiKey) {
|
|
341
|
+
throw new ConfigError(t("No API key for {id} yet. Run `bccli login` or set the env var {env}.", { id: providerId, env: provider.apiKeyEnv }));
|
|
342
|
+
}
|
|
343
|
+
return { providerId, model: modelRef.slice(slash + 1), baseURL: provider.baseURL.replace(/\/+$/, ""), apiKey };
|
|
344
|
+
}
|
|
345
|
+
function removeCredential(providerId, env = process.env) {
|
|
346
|
+
const creds = readCredentials(env);
|
|
347
|
+
if (!(providerId in creds)) return false;
|
|
348
|
+
delete creds[providerId];
|
|
349
|
+
writeFileAtomic(join(bccliHome(env), "credentials"), JSON.stringify(creds, null, 2), 384);
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
export {
|
|
354
|
+
PRESETS,
|
|
355
|
+
matchKeybind,
|
|
356
|
+
setTheme,
|
|
357
|
+
color,
|
|
358
|
+
ACCENT,
|
|
359
|
+
ConfigError,
|
|
360
|
+
DEFAULT_MAX_STEPS,
|
|
361
|
+
bccliHome,
|
|
362
|
+
readJsonConfig,
|
|
363
|
+
loadConfig,
|
|
364
|
+
globalConfigPath,
|
|
365
|
+
readGlobalPlugins,
|
|
366
|
+
writeFileAtomic,
|
|
367
|
+
quarantineCorruptFile,
|
|
368
|
+
updateGlobalConfig,
|
|
369
|
+
readCredentials,
|
|
370
|
+
saveCredential,
|
|
371
|
+
resolveModel,
|
|
372
|
+
removeCredential
|
|
373
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
listAllModels
|
|
4
|
+
} from "./chunk-75BECX4F.js";
|
|
5
|
+
import {
|
|
6
|
+
hasKey,
|
|
7
|
+
providerName,
|
|
8
|
+
removeGlobalProvider,
|
|
9
|
+
writeGlobalConfig
|
|
10
|
+
} from "./chunk-JHPGOAXU.js";
|
|
11
|
+
import {
|
|
12
|
+
PRESETS,
|
|
13
|
+
loadConfig,
|
|
14
|
+
saveCredential
|
|
15
|
+
} from "./chunk-FLQPE4S5.js";
|
|
16
|
+
import {
|
|
17
|
+
t
|
|
18
|
+
} from "./chunk-UZWJQF67.js";
|
|
19
|
+
|
|
20
|
+
// src/providerCli.ts
|
|
21
|
+
async function addProviderKey(providerId, key, deps) {
|
|
22
|
+
const config = loadConfig(deps.cwd, deps.env);
|
|
23
|
+
const [group] = await listAllModels(config, deps.env, { only: providerId, fetch: deps.fetch, keyOverride: key || void 0 });
|
|
24
|
+
if (!group || group.error) return { ok: false, error: t("The API key was rejected or {provider} could not be reached.", { provider: providerName(config, providerId) }) };
|
|
25
|
+
if (key) saveCredential(providerId, key, deps.env);
|
|
26
|
+
return { ok: true, models: group.models.length };
|
|
27
|
+
}
|
|
28
|
+
async function runProviderCommand(args, deps) {
|
|
29
|
+
const [action = "list", id] = args.subArgs;
|
|
30
|
+
const config = loadConfig(deps.cwd, deps.env);
|
|
31
|
+
if (action === "list") {
|
|
32
|
+
for (const pid of Object.keys(config.providers)) {
|
|
33
|
+
const mark = hasKey(config, pid, deps.env) ? "\u2713" : "\u25CB";
|
|
34
|
+
deps.out(`${mark} ${pid.padEnd(14)} ${providerName(config, pid).padEnd(20)} ${config.providers[pid].baseURL}`);
|
|
35
|
+
}
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
if (!id) {
|
|
39
|
+
deps.err(t("Usage: bccli provider {action} <id>", { action }));
|
|
40
|
+
return 1;
|
|
41
|
+
}
|
|
42
|
+
if (action === "remove") {
|
|
43
|
+
removeGlobalProvider(id, deps.env);
|
|
44
|
+
deps.out(t("{id} removed.", { id }));
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
if (action !== "add") {
|
|
48
|
+
deps.err(t("Unknown action: {action}. Use list, add, or remove.", { action }));
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
const preset = PRESETS.find((p) => p.id === id);
|
|
52
|
+
if (!preset && !config.providers[id]) {
|
|
53
|
+
if (!args.url) {
|
|
54
|
+
deps.err(t("{id} is not a preset. For a custom provider add --url <OpenAI-compatible base URL>.", { id }));
|
|
55
|
+
return 1;
|
|
56
|
+
}
|
|
57
|
+
writeGlobalConfig(
|
|
58
|
+
{ providers: { [id]: { baseURL: args.url, ...args.name ? { name: args.name } : {}, ...args.keyEnv ? { apiKeyEnv: args.keyEnv } : {} } } },
|
|
59
|
+
deps.env
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const needsKey = !!loadConfig(deps.cwd, deps.env).providers[id].apiKeyEnv || !preset;
|
|
63
|
+
const key = needsKey ? await deps.readSecret(t("API key for {id} (leave empty if not needed): ", { id })) : "";
|
|
64
|
+
const result = await addProviderKey(id, key, deps);
|
|
65
|
+
if (!result.ok) {
|
|
66
|
+
deps.err(result.error);
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
deps.out(t("{id} is ready. {n} models available.", { id, n: result.models }));
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export {
|
|
74
|
+
addProviderKey,
|
|
75
|
+
runProviderCommand
|
|
76
|
+
};
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
readCredentials,
|
|
6
6
|
readJsonConfig,
|
|
7
7
|
removeCredential
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-FLQPE4S5.js";
|
|
9
9
|
|
|
10
10
|
// src/providers.ts
|
|
11
11
|
import { mkdirSync, writeFileSync } from "fs";
|
|
@@ -45,6 +45,7 @@ function removeGlobalProvider(id, env = process.env) {
|
|
|
45
45
|
export {
|
|
46
46
|
providerName,
|
|
47
47
|
hasKey,
|
|
48
|
+
readGlobalConfigFile,
|
|
48
49
|
writeGlobalConfig,
|
|
49
50
|
removeGlobalProvider
|
|
50
51
|
};
|