@ekzs/cli 0.2.0 → 0.3.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.
- package/README.md +49 -13
- package/dist/commands/agent.d.ts +0 -4
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +0 -6
- package/dist/commands/ask.d.ts +3 -5
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +50 -36
- package/dist/commands/local-agent.d.ts +0 -2
- package/dist/commands/local-agent.d.ts.map +1 -1
- package/dist/commands/local-agent.js +292 -131
- package/dist/commands/providers.d.ts +4 -0
- package/dist/commands/providers.d.ts.map +1 -0
- package/dist/commands/providers.js +6 -0
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +4 -0
- package/dist/index.js +41 -20
- package/dist/lib/commands-i18n.d.ts +12 -1
- package/dist/lib/commands-i18n.d.ts.map +1 -1
- package/dist/lib/commands-i18n.js +78 -14
- package/dist/lib/composer-model.d.ts +1 -1
- package/dist/lib/composer-model.d.ts.map +1 -1
- package/dist/lib/composer-model.js +2 -2
- package/dist/lib/global-config.d.ts +19 -0
- package/dist/lib/global-config.d.ts.map +1 -0
- package/dist/lib/global-config.js +52 -0
- package/dist/lib/help.d.ts.map +1 -1
- package/dist/lib/help.js +16 -12
- package/dist/lib/locale.d.ts.map +1 -1
- package/dist/lib/locale.js +2 -1
- package/dist/lib/onboarding.d.ts +20 -0
- package/dist/lib/onboarding.d.ts.map +1 -0
- package/dist/lib/onboarding.js +129 -0
- package/dist/lib/providers/agent-runner.d.ts +12 -0
- package/dist/lib/providers/agent-runner.d.ts.map +1 -0
- package/dist/lib/providers/agent-runner.js +167 -0
- package/dist/lib/providers/catalog.d.ts +15 -0
- package/dist/lib/providers/catalog.d.ts.map +1 -0
- package/dist/lib/providers/catalog.js +93 -0
- package/dist/lib/providers/chat.d.ts +10 -0
- package/dist/lib/providers/chat.d.ts.map +1 -0
- package/dist/lib/providers/chat.js +121 -0
- package/dist/lib/providers/credentials.d.ts +28 -0
- package/dist/lib/providers/credentials.d.ts.map +1 -0
- package/dist/lib/providers/credentials.js +70 -0
- package/dist/lib/providers/cursor-runner.d.ts +13 -0
- package/dist/lib/providers/cursor-runner.d.ts.map +1 -0
- package/dist/lib/providers/cursor-runner.js +89 -0
- package/dist/lib/providers/cursor-sdk.d.ts +8 -0
- package/dist/lib/providers/cursor-sdk.d.ts.map +1 -0
- package/dist/lib/providers/cursor-sdk.js +25 -0
- package/dist/lib/providers/store.d.ts +21 -0
- package/dist/lib/providers/store.d.ts.map +1 -0
- package/dist/lib/providers/store.js +84 -0
- package/dist/lib/providers/tools.d.ts +80 -0
- package/dist/lib/providers/tools.d.ts.map +1 -0
- package/dist/lib/providers/tools.js +145 -0
- package/dist/lib/providers/ui.d.ts +4 -0
- package/dist/lib/providers/ui.d.ts.map +1 -0
- package/dist/lib/providers/ui.js +188 -0
- package/dist/lib/setup-messages.d.ts +4 -0
- package/dist/lib/setup-messages.d.ts.map +1 -0
- package/dist/lib/setup-messages.js +13 -0
- package/dist/lib/ui/splash.d.ts.map +1 -1
- package/dist/lib/ui/splash.js +4 -0
- package/package.json +10 -2
- package/skills/ekz-sdk-cli/SKILL.md +6 -4
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import readline from "readline";
|
|
2
|
+
import { ok, warn } from "../output.js";
|
|
3
|
+
import { c, muted } from "../theme.js";
|
|
4
|
+
import { askWithPlaceholder, formatInputPrompt } from "../ui/prompt.js";
|
|
5
|
+
import { PROVIDER_CATALOG, getProviderDefinition } from "./catalog.js";
|
|
6
|
+
import { listProviderRows, resolveActiveCredentials, } from "./credentials.js";
|
|
7
|
+
import { maskApiKey, providersFilePath, removeStoredProvider, setActiveProvider, setStoredProvider, } from "./store.js";
|
|
8
|
+
function providersTitle(locale) {
|
|
9
|
+
if (locale === "pt")
|
|
10
|
+
return "Provedores (BYOK)";
|
|
11
|
+
if (locale === "zh")
|
|
12
|
+
return "供应商 (BYOK)";
|
|
13
|
+
return "Providers (BYOK)";
|
|
14
|
+
}
|
|
15
|
+
function t(locale, pt, en, zh) {
|
|
16
|
+
if (locale === "pt")
|
|
17
|
+
return pt;
|
|
18
|
+
if (locale === "zh")
|
|
19
|
+
return zh;
|
|
20
|
+
return en;
|
|
21
|
+
}
|
|
22
|
+
async function askLine(prompt, hidden = false) {
|
|
23
|
+
if (!hidden || !process.stdin.isTTY) {
|
|
24
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
25
|
+
return new Promise((resolve) => {
|
|
26
|
+
rl.question(prompt, (answer) => {
|
|
27
|
+
rl.close();
|
|
28
|
+
resolve(answer.trim());
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
process.stdout.write(prompt);
|
|
33
|
+
return new Promise((resolve) => {
|
|
34
|
+
const stdin = process.stdin;
|
|
35
|
+
const stdout = process.stdout;
|
|
36
|
+
readline.emitKeypressEvents(stdin);
|
|
37
|
+
stdin.setRawMode(true);
|
|
38
|
+
stdin.resume();
|
|
39
|
+
let value = "";
|
|
40
|
+
const cleanup = () => {
|
|
41
|
+
stdin.setRawMode(false);
|
|
42
|
+
stdin.removeListener("keypress", onKeypress);
|
|
43
|
+
stdout.write("\n");
|
|
44
|
+
};
|
|
45
|
+
const onKeypress = (_char, key) => {
|
|
46
|
+
if (!key)
|
|
47
|
+
return;
|
|
48
|
+
if (key.name === "return") {
|
|
49
|
+
cleanup();
|
|
50
|
+
resolve(value.trim());
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (key.name === "c" && key.ctrl) {
|
|
54
|
+
cleanup();
|
|
55
|
+
process.exit(130);
|
|
56
|
+
}
|
|
57
|
+
if (key.name === "backspace") {
|
|
58
|
+
if (value.length > 0) {
|
|
59
|
+
value = value.slice(0, -1);
|
|
60
|
+
stdout.write("\b \b");
|
|
61
|
+
}
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const ch = key.sequence;
|
|
65
|
+
if (!ch || key.ctrl || key.meta)
|
|
66
|
+
return;
|
|
67
|
+
value += ch;
|
|
68
|
+
stdout.write("*");
|
|
69
|
+
};
|
|
70
|
+
stdin.on("keypress", onKeypress);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function printProviderList(rows, locale) {
|
|
74
|
+
console.log("");
|
|
75
|
+
console.log(` ${c.bold}${providersTitle(locale)}${c.reset}`);
|
|
76
|
+
console.log(` ${muted(providersFilePath())}`);
|
|
77
|
+
console.log("");
|
|
78
|
+
rows.forEach((row, index) => {
|
|
79
|
+
const n = index + 1;
|
|
80
|
+
const bullet = row.active ? `${c.green}●${c.reset}` : " ";
|
|
81
|
+
const keyCol = row.maskedKey ? muted(row.maskedKey) : muted("—");
|
|
82
|
+
const activeTag = row.active
|
|
83
|
+
? muted(t(locale, " activo", " active", " 当前"))
|
|
84
|
+
: "";
|
|
85
|
+
console.log(` ${bullet} ${c.bold}${String(n).padStart(2)}.${c.reset} ${row.definition.name.padEnd(14)} ${keyCol} ${muted(row.model)}${activeTag}`);
|
|
86
|
+
});
|
|
87
|
+
console.log("");
|
|
88
|
+
console.log(` ${muted(t(locale, "[número] configurar · u usar · r remover · q sair", "[number] configure · u use · r remove · q quit", "[数字] 配置 · u 使用 · r 删除 · q 退出"))}`);
|
|
89
|
+
console.log("");
|
|
90
|
+
}
|
|
91
|
+
async function configureProvider(id, locale) {
|
|
92
|
+
const def = getProviderDefinition(id);
|
|
93
|
+
if (!def)
|
|
94
|
+
return;
|
|
95
|
+
console.log("");
|
|
96
|
+
console.log(` ${c.bold}${def.name}${c.reset} — ${muted(def.description)}`);
|
|
97
|
+
console.log(` ${muted(def.docsUrl)}`);
|
|
98
|
+
console.log("");
|
|
99
|
+
const keyPrompt = t(locale, `Cola a API key (${def.keyHint}): `, `Paste API key (${def.keyHint}): `, `粘贴 API key (${def.keyHint}): `);
|
|
100
|
+
const apiKey = await askLine(keyPrompt, true);
|
|
101
|
+
if (!apiKey) {
|
|
102
|
+
warn(t(locale, "Chave vazia — cancelado.", "Empty key — cancelled.", "密钥为空,已取消。"));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const modelDefault = def.defaultModel;
|
|
106
|
+
const modelPrompt = t(locale, `Modelo [${modelDefault}]: `, `Model [${modelDefault}]: `, `模型 [${modelDefault}]: `);
|
|
107
|
+
const modelInput = await askLine(modelPrompt);
|
|
108
|
+
const model = modelInput || modelDefault;
|
|
109
|
+
let baseUrl;
|
|
110
|
+
if (def.kind === "openai-compatible" || def.defaultBaseUrl) {
|
|
111
|
+
const urlPrompt = t(locale, `Base URL [${def.defaultBaseUrl ?? "(default)"}]: `, `Base URL [${def.defaultBaseUrl ?? "(default)"}]: `, `Base URL [${def.defaultBaseUrl ?? "(default)"}]: `);
|
|
112
|
+
const urlInput = await askLine(urlPrompt);
|
|
113
|
+
if (urlInput)
|
|
114
|
+
baseUrl = urlInput;
|
|
115
|
+
}
|
|
116
|
+
setStoredProvider(id, { apiKey, model, baseUrl });
|
|
117
|
+
ok(t(locale, `Guardado ${def.name} (${maskApiKey(apiKey)})`, `Saved ${def.name} (${maskApiKey(apiKey)})`, `已保存 ${def.name} (${maskApiKey(apiKey)})`));
|
|
118
|
+
const active = resolveActiveCredentials();
|
|
119
|
+
if (!active || active.id !== id) {
|
|
120
|
+
const useNow = await askLine(t(locale, "Usar como provider activo? [Y/n]: ", "Use as active provider? [Y/n]: ", "设为当前 provider? [Y/n]: "), false);
|
|
121
|
+
if (!useNow || useNow.toLowerCase() === "y" || useNow.toLowerCase() === "s" || useNow === "") {
|
|
122
|
+
setActiveProvider(id);
|
|
123
|
+
ok(t(locale, `${def.name} activo.`, `${def.name} is now active.`, `${def.name} 已激活。`));
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function resolveRowTarget(rows, target) {
|
|
128
|
+
const trimmed = target.trim().toLowerCase();
|
|
129
|
+
const num = Number(trimmed);
|
|
130
|
+
if (Number.isInteger(num) && num >= 1 && num <= rows.length) {
|
|
131
|
+
return rows[num - 1];
|
|
132
|
+
}
|
|
133
|
+
return rows.find((r) => r.definition.id === trimmed);
|
|
134
|
+
}
|
|
135
|
+
export async function runProvidersInteractive(locale = "pt") {
|
|
136
|
+
while (true) {
|
|
137
|
+
const rows = listProviderRows();
|
|
138
|
+
printProviderList(rows, locale);
|
|
139
|
+
const choice = (await askWithPlaceholder({
|
|
140
|
+
prompt: formatInputPrompt(),
|
|
141
|
+
placeholder: t(locale, "número ou u/r/q", "number or u/r/q", "数字或 u/r/q"),
|
|
142
|
+
})).trim();
|
|
143
|
+
if (!choice || choice.toLowerCase() === "q" || choice === "sair" || choice === "退出")
|
|
144
|
+
break;
|
|
145
|
+
const num = Number(choice);
|
|
146
|
+
if (Number.isInteger(num) && num >= 1 && num <= rows.length) {
|
|
147
|
+
await configureProvider(rows[num - 1].definition.id, locale);
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const parts = choice.split(/\s+/);
|
|
151
|
+
const cmd = parts[0]?.toLowerCase();
|
|
152
|
+
if (cmd === "u" || cmd === "use" || cmd === "usar" || cmd === "使用") {
|
|
153
|
+
const target = parts[1] ?? (await askLine(t(locale, "Número ou id: ", "Number or id: ", "编号或 id: ")));
|
|
154
|
+
const row = resolveRowTarget(rows, target);
|
|
155
|
+
if (!row?.stored?.apiKey) {
|
|
156
|
+
warn(t(locale, "Provider sem chave.", "Provider has no key.", "该 provider 未配置密钥。"));
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
setActiveProvider(row.definition.id);
|
|
160
|
+
ok(t(locale, `${row.definition.name} activo.`, `${row.definition.name} active.`, `${row.definition.name} 已激活。`));
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (cmd === "r" || cmd === "remove" || cmd === "remover" || cmd === "删除") {
|
|
164
|
+
const target = parts[1] ?? (await askLine(t(locale, "Número ou id: ", "Number or id: ", "编号或 id: ")));
|
|
165
|
+
const row = resolveRowTarget(rows, target);
|
|
166
|
+
if (!row) {
|
|
167
|
+
warn(t(locale, "Provider inválido.", "Invalid provider.", "无效的 provider。"));
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
removeStoredProvider(row.definition.id);
|
|
171
|
+
ok(t(locale, `${row.definition.name} removido.`, `${row.definition.name} removed.`, `${row.definition.name} 已删除。`));
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const byId = PROVIDER_CATALOG.find((p) => p.id === choice.toLowerCase());
|
|
175
|
+
if (byId) {
|
|
176
|
+
await configureProvider(byId.id, locale);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
warn(t(locale, "Opção inválida.", "Invalid option.", "无效选项。"));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export function formatActiveProviderLabel(locale) {
|
|
183
|
+
const creds = resolveActiveCredentials();
|
|
184
|
+
if (!creds) {
|
|
185
|
+
return t(locale, "sem provider", "no provider", "未配置");
|
|
186
|
+
}
|
|
187
|
+
return `${creds.definition.name} · ${creds.maskedKey}`;
|
|
188
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup-messages.d.ts","sourceRoot":"","sources":["../../src/lib/setup-messages.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,wBAAgB,0BAA0B,IAAI,MAAM,CAQnD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3D"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Shown when agent needs provider but setup was not run (all locales). */
|
|
2
|
+
export function formatSetupRequiredMessage() {
|
|
3
|
+
return [
|
|
4
|
+
"Setup required · Configuração necessária · 需要设置",
|
|
5
|
+
"",
|
|
6
|
+
" pt: Corre `ekz setup` ou `ekz --offline`",
|
|
7
|
+
" en: Run `ekz setup` or `ekz --offline`",
|
|
8
|
+
" zh: 运行 `ekz setup` 或 `ekz --offline`",
|
|
9
|
+
].join("\n");
|
|
10
|
+
}
|
|
11
|
+
export function providersSetupHint(_locale) {
|
|
12
|
+
return formatSetupRequiredMessage();
|
|
13
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"splash.d.ts","sourceRoot":"","sources":["../../../src/lib/ui/splash.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"splash.d.ts","sourceRoot":"","sources":["../../../src/lib/ui/splash.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,cAAc,CAAC;AACzD,OAAO,EAAe,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAMvD,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AA6GF,wBAAgB,YAAY,CAAC,IAAI,EAAE,aAAa,GAAG,IAAI,CAQtD;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAG1D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,SAAI,GAAG,MAAM,CAIrE"}
|
package/dist/lib/ui/splash.js
CHANGED
|
@@ -4,6 +4,7 @@ import { readFileSync } from "fs";
|
|
|
4
4
|
import { dirname, join } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { formatSplashTool, splashHelpHint, splashSectionTitle, SPLASH_TOOLS, splashSessionLines, welcomeMessage, } from "../commands-i18n.js";
|
|
7
|
+
import { getGlobalUserName } from "../global-config.js";
|
|
7
8
|
import { uiStrings } from "../locale.js";
|
|
8
9
|
import { MODE_LABELS } from "../mode.js";
|
|
9
10
|
import { detectShellEnvironment, shellLabel } from "../shell.js";
|
|
@@ -24,6 +25,9 @@ function packageVersion() {
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
function displayName() {
|
|
28
|
+
const fromConfig = getGlobalUserName();
|
|
29
|
+
if (fromConfig)
|
|
30
|
+
return fromConfig.split(/\s+/)[0] ?? fromConfig;
|
|
27
31
|
const fromEnv = process.env.EKZ_USER_NAME?.trim();
|
|
28
32
|
if (fromEnv)
|
|
29
33
|
return fromEnv.split(/\s+/)[0] ?? fromEnv;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ekzs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "CLI agent for e-Kwanza v2.4 — health checks, code scan, webhook tests, AI assistance",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -17,12 +17,20 @@
|
|
|
17
17
|
"prepublishOnly": "npm run build"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@cursor/sdk": "^1.0.13",
|
|
21
20
|
"@ekzs/connect": "^0.1.0",
|
|
22
21
|
"commander": "^12.1.0",
|
|
23
22
|
"dotenv": "^16.4.7"
|
|
24
23
|
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@cursor/sdk": "^1.0.13"
|
|
26
|
+
},
|
|
27
|
+
"peerDependenciesMeta": {
|
|
28
|
+
"@cursor/sdk": {
|
|
29
|
+
"optional": true
|
|
30
|
+
}
|
|
31
|
+
},
|
|
25
32
|
"devDependencies": {
|
|
33
|
+
"@cursor/sdk": "^1.0.13",
|
|
26
34
|
"@types/node": "^20",
|
|
27
35
|
"typescript": "^5"
|
|
28
36
|
},
|
|
@@ -35,15 +35,17 @@ Model env: `EKZ_AGENT_MODEL=composer-2.5-fast` → `composer-2.5` + `fast: true`
|
|
|
35
35
|
| `ekz doctor` | Validate `.env`, live OAuth (free) |
|
|
36
36
|
| `ekz scan` | Common integration mistakes (free) |
|
|
37
37
|
| `ekz webhook <url>` | POST sample payload (free) |
|
|
38
|
-
| `ekz
|
|
38
|
+
| `ekz health` | OAuth + rail health (free) |
|
|
39
|
+
| `ekz providers` | Configure LLM API keys — BYOK (`ekz provedores` / `ekz 供应商`) |
|
|
40
|
+
| `ekz agent` | Local coding agent (BYOK — configure via `ekz providers`) |
|
|
39
41
|
| `ekz fix` | Auto-audit + fix integration |
|
|
40
|
-
| `ekz ask` |
|
|
42
|
+
| `ekz ask` | BYOK Q&A only (no file edits) |
|
|
41
43
|
|
|
42
44
|
Modes: `--mode agent` (default) | `--mode plan` | `--mode ask`. Shorthand: `--plan` = plan mode.
|
|
43
|
-
In the REPL: `/help`, `/mode agent|plan|ask`, `/doctor`, `/scan`, `/lang`, `/save`, `/resume`.
|
|
45
|
+
In the REPL: `/help`, `/providers` (`/provedores`, `/供应商`), `/mode agent|plan|ask`, `/doctor`, `/scan`, `/lang`, `/save`, `/resume`.
|
|
44
46
|
|
|
45
47
|
```bash
|
|
46
|
-
|
|
48
|
+
ekz providers # BYOK keys → ~/.ekz/providers.json
|
|
47
49
|
ekz # interactive REPL (agent mode)
|
|
48
50
|
ekz --mode plan # plan before edits
|
|
49
51
|
ekz --mode ask # remote Q&A, no edits
|