@ekzs/cli 0.3.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 +31 -5
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +16 -3
- package/dist/commands/local-agent.d.ts.map +1 -1
- package/dist/commands/local-agent.js +146 -60
- 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 +16 -0
- package/dist/lib/commands-i18n.d.ts +2 -2
- package/dist/lib/commands-i18n.d.ts.map +1 -1
- package/dist/lib/commands-i18n.js +21 -12
- 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 +3 -0
- 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/credentials.d.ts +2 -0
- package/dist/lib/providers/credentials.d.ts.map +1 -1
- package/dist/lib/providers/credentials.js +19 -3
- package/dist/lib/providers/cursor-runner.d.ts +2 -2
- package/dist/lib/providers/cursor-runner.d.ts.map +1 -1
- package/dist/lib/providers/cursor-runner.js +7 -5
- 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.map +1 -1
- package/dist/lib/providers/store.js +3 -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/README.md
CHANGED
|
@@ -8,17 +8,43 @@ Runs **in your repo** with Composer 2.5 Fast. Edits files directly.
|
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
npm install -g @ekzs/cli
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Clean install (~15 packages) — BYOK with OpenAI, Anthropic, DeepSeek, etc. Keys via `ekz providers`.
|
|
14
|
+
|
|
15
|
+
**Cursor provider only** (optional — pulls native deps + npm deprecation noise from `@cursor/sdk`):
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install -g @cursor/sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Or install both at once:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install -g @ekzs/cli @cursor/sdk
|
|
13
25
|
```
|
|
14
26
|
|
|
15
27
|
## Setup (BYOK)
|
|
16
28
|
|
|
17
|
-
|
|
29
|
+
First run:
|
|
18
30
|
|
|
19
31
|
```bash
|
|
20
|
-
ekz
|
|
21
|
-
#
|
|
32
|
+
ekz setup
|
|
33
|
+
# aliases: ekz configurar · ekz 设置
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Wizard steps: **language** → **your name** → **Configurar provedor** (or offline).
|
|
37
|
+
|
|
38
|
+
Stored in `~/.ekz/config.json` (name, locale) and `~/.ekz/providers.json` (API keys).
|
|
39
|
+
|
|
40
|
+
Without a provider, `ekz` shows:
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
Setup required · Configuração necessária · 需要设置
|
|
44
|
+
|
|
45
|
+
pt: Corre `ekz setup` ou `ekz --offline`
|
|
46
|
+
en: Run `ekz setup` or `ekz --offline`
|
|
47
|
+
zh: 运行 `ekz setup` 或 `ekz --offline`
|
|
22
48
|
```
|
|
23
49
|
|
|
24
50
|
Supported providers: **Cursor**, **OpenAI**, **Anthropic**, **DeepSeek**, **Kimi**, **Groq**, **Mistral**, **Google Gemini**, **Ollama**.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAA0B,MAAM,kBAAkB,CAAC;AAyC1E,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,CAAC;AAEF,mDAAmD;AACnD,wBAAsB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CA0B1E;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,iBAO9D;AAgDD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,
|
|
1
|
+
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAA0B,MAAM,kBAAkB,CAAC;AAyC1E,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB,CAAC;AAEF,mDAAmD;AACnD,wBAAsB,cAAc,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CA0B1E;AAED,wBAAsB,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,iBAO9D;AAgDD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,iBAwEA;AAED,wBAAsB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAM/E"}
|
package/dist/commands/ask.js
CHANGED
|
@@ -5,6 +5,7 @@ import { parseLocale, uiStrings } from "../lib/locale.js";
|
|
|
5
5
|
import { parseMode } from "../lib/mode.js";
|
|
6
6
|
import { fail, heading, info, ok } from "../lib/output.js";
|
|
7
7
|
import { loadPreferences, savePreferences } from "../lib/preferences.js";
|
|
8
|
+
import { ensureAgentReady, offlineModelLabel } from "../lib/onboarding.js";
|
|
8
9
|
import { executeByokAskTurn } from "../lib/providers/chat.js";
|
|
9
10
|
import { executeCursorAskTurn } from "../lib/providers/cursor-runner.js";
|
|
10
11
|
import { isCursorProvider, requireActiveCredentials, ProviderSetupError, } from "../lib/providers/credentials.js";
|
|
@@ -13,7 +14,6 @@ import { redactText } from "../lib/redact.js";
|
|
|
13
14
|
import { askWithPlaceholder, formatInputPrompt } from "../lib/ui/prompt.js";
|
|
14
15
|
import { inputPlaceholder } from "../lib/ui/splash.js";
|
|
15
16
|
function deterministicAnswer(question, ctx, locale) {
|
|
16
|
-
const p = providersCommandLabels(locale);
|
|
17
17
|
const lines = ["## Ekz assistant (offline)", "", question, ""];
|
|
18
18
|
if (ctx.doctor.issues.length) {
|
|
19
19
|
lines.push("### Issues", ...ctx.doctor.issues.map((i) => `- ${i}`), "");
|
|
@@ -21,7 +21,7 @@ function deterministicAnswer(question, ctx, locale) {
|
|
|
21
21
|
if (ctx.scan.length) {
|
|
22
22
|
lines.push("### Scan", ...ctx.scan.slice(0, 8).map((f) => `- \`${f.file}:${f.line}\` · ${f.message}`), "");
|
|
23
23
|
}
|
|
24
|
-
lines.push("### Next steps",
|
|
24
|
+
lines.push("### Next steps", "- Run `ekz setup` to configure language, name, and provider", "- Or `ekz --offline` for doctor/scan only");
|
|
25
25
|
return lines.join("\n");
|
|
26
26
|
}
|
|
27
27
|
/** Q&A via BYOK provider (no local file edits). */
|
|
@@ -94,10 +94,23 @@ function handleAskMeta(line, locale) {
|
|
|
94
94
|
export async function runInteractiveAsk(opts) {
|
|
95
95
|
const prefs = loadPreferences(opts.cwd, opts.locale, "ask");
|
|
96
96
|
let currentLocale = prefs.locale;
|
|
97
|
+
let askOffline = Boolean(opts.offline);
|
|
98
|
+
try {
|
|
99
|
+
const ready = await ensureAgentReady({
|
|
100
|
+
locale: currentLocale,
|
|
101
|
+
offlineFlag: opts.offline,
|
|
102
|
+
});
|
|
103
|
+
askOffline = ready.offline || askOffline;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
97
109
|
printWelcome({
|
|
98
110
|
cwd: opts.cwd,
|
|
99
111
|
locale: currentLocale,
|
|
100
112
|
mode: "ask",
|
|
113
|
+
model: askOffline ? offlineModelLabel(currentLocale) : undefined,
|
|
101
114
|
});
|
|
102
115
|
let turn = 0;
|
|
103
116
|
while (true) {
|
|
@@ -134,7 +147,7 @@ export async function runInteractiveAsk(opts) {
|
|
|
134
147
|
await executeAskTurn({
|
|
135
148
|
cwd: opts.cwd,
|
|
136
149
|
question: line,
|
|
137
|
-
offline:
|
|
150
|
+
offline: askOffline,
|
|
138
151
|
quiet: true,
|
|
139
152
|
locale: currentLocale,
|
|
140
153
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"local-agent.d.ts","sourceRoot":"","sources":["../../src/commands/local-agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAAsC,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAsBlF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;
|
|
1
|
+
{"version":3,"file":"local-agent.d.ts","sourceRoot":"","sources":["../../src/commands/local-agent.ts"],"names":[],"mappings":"AAWA,OAAO,EAAsC,KAAK,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAsBlF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AA2PF,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,iBA0I1D;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,aAAa,CAAC,iBAmG9F"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { resolve } from "path";
|
|
2
|
-
import { Agent, CursorAgentError } from "@cursor/sdk";
|
|
3
2
|
import { executeAskTurn } from "../commands/ask.js";
|
|
4
3
|
import { runDoctor } from "../commands/doctor.js";
|
|
5
4
|
import { runScan } from "../commands/scan.js";
|
|
6
5
|
import { inputPlaceholder, printHelp, printLangSwitch, printModeSwitch, printWelcome } from "../lib/banner.js";
|
|
7
6
|
import { loadProjectEnv } from "../lib/env.js";
|
|
7
|
+
import { ensureAgentReady, offlineAgentHint, offlineModelLabel } from "../lib/onboarding.js";
|
|
8
8
|
import { resolveReplMetaCommand, isProvidersMetaCommand } from "../lib/commands-i18n.js";
|
|
9
9
|
import { buildAgentPrompt, buildBootstrapContext } from "../lib/context.js";
|
|
10
10
|
import { resolveComposerModel } from "../lib/composer-model.js";
|
|
@@ -12,29 +12,31 @@ import { parseLocale, uiStrings } from "../lib/locale.js";
|
|
|
12
12
|
import { modeRequiresCursorAgent, parseMode } from "../lib/mode.js";
|
|
13
13
|
import { loadPreferences, savePreferences } from "../lib/preferences.js";
|
|
14
14
|
import { runByokAgentTurn, usesCursorSdk } from "../lib/providers/agent-runner.js";
|
|
15
|
+
import { CursorSdkMissingError, loadCursorSdk } from "../lib/providers/cursor-sdk.js";
|
|
15
16
|
import { disposeAgent, streamRun } from "../lib/providers/cursor-runner.js";
|
|
16
|
-
import {
|
|
17
|
-
import { requireActiveCredentials, } from "../lib/providers/credentials.js";
|
|
17
|
+
import { resolveActiveCredentials, resolveCredentialsWithOverride, } from "../lib/providers/credentials.js";
|
|
18
18
|
import { runProvidersInteractive, formatActiveProviderLabel } from "../lib/providers/ui.js";
|
|
19
|
-
import { maskApiKey } from "../lib/providers/store.js";
|
|
20
19
|
import { clearSession, getSessionByIndex, loadSession, persistSession, saveNamedSession, } from "../lib/session.js";
|
|
21
20
|
import { fail, info, ok, warn } from "../lib/output.js";
|
|
22
21
|
import { turnDivider } from "../lib/theme.js";
|
|
23
22
|
import { askWithPlaceholder, formatInputPrompt } from "../lib/ui/prompt.js";
|
|
24
|
-
function
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
23
|
+
async function prepareAgentSession(opts, prefs) {
|
|
24
|
+
try {
|
|
25
|
+
const ready = await ensureAgentReady({
|
|
26
|
+
locale: prefs.locale,
|
|
27
|
+
offlineFlag: opts.askOffline,
|
|
28
|
+
apiKey: opts.apiKey,
|
|
29
|
+
});
|
|
30
|
+
return { offline: ready.offline, creds: ready.creds };
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
34
|
+
process.exitCode = 1;
|
|
35
|
+
return null;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
}
|
|
38
|
+
function resolveAgentCredentials(override, locale = "pt") {
|
|
39
|
+
return resolveCredentialsWithOverride(override, locale);
|
|
38
40
|
}
|
|
39
41
|
function resolveCursorModel(creds) {
|
|
40
42
|
return resolveComposerModel(process.env, creds.model);
|
|
@@ -53,12 +55,12 @@ async function runAgentTurn(options) {
|
|
|
53
55
|
const ctx = await buildBootstrapContext(options.cwd);
|
|
54
56
|
const prompt = buildAgentPrompt(options.task, ctx, options.locale);
|
|
55
57
|
try {
|
|
56
|
-
await streamRun(options.cursorAgent, prompt);
|
|
58
|
+
await streamRun(options.cursorAgent, prompt, options.locale);
|
|
57
59
|
console.log("\n");
|
|
58
60
|
return { cursorAgent: options.cursorAgent, ok: true };
|
|
59
61
|
}
|
|
60
62
|
catch (err) {
|
|
61
|
-
if (err instanceof CursorAgentError) {
|
|
63
|
+
if (err instanceof Error && err.name === "CursorAgentError") {
|
|
62
64
|
fail(err.message);
|
|
63
65
|
return { cursorAgent: options.cursorAgent, ok: false };
|
|
64
66
|
}
|
|
@@ -77,6 +79,7 @@ async function runAgentTurn(options) {
|
|
|
77
79
|
async function createOrResumeAgent(opts, locale = "pt") {
|
|
78
80
|
const cwd = resolve(opts.cwd);
|
|
79
81
|
const creds = resolveAgentCredentials(opts.apiKey, locale);
|
|
82
|
+
const { Agent } = await loadCursorSdk(locale);
|
|
80
83
|
const apiKey = creds.apiKey;
|
|
81
84
|
const model = resolveCursorModel(creds);
|
|
82
85
|
const usePlan = opts.mode === "plan" || Boolean(opts.plan);
|
|
@@ -195,6 +198,7 @@ async function resumeAgentInRepl(cwd, current, agentId, locale, apiKeyOverride)
|
|
|
195
198
|
? "恢复会话仅适用于 Cursor provider。"
|
|
196
199
|
: "Resume session is Cursor-only.");
|
|
197
200
|
}
|
|
201
|
+
const { Agent } = await loadCursorSdk(locale);
|
|
198
202
|
const model = resolveCursorModel(creds);
|
|
199
203
|
const agent = await Agent.resume(agentId, {
|
|
200
204
|
apiKey: creds.apiKey,
|
|
@@ -210,24 +214,24 @@ export async function runLocalAgent(opts) {
|
|
|
210
214
|
loadProjectEnv(cwd);
|
|
211
215
|
const prefs = loadPreferences(cwd, opts.locale, opts.mode, opts.plan);
|
|
212
216
|
const mode = prefs.mode;
|
|
213
|
-
const
|
|
217
|
+
const session = await prepareAgentSession(opts, prefs);
|
|
218
|
+
if (!session)
|
|
219
|
+
return;
|
|
220
|
+
const { offline, creds: sessionCreds } = session;
|
|
221
|
+
const askOffline = offline || Boolean(opts.askOffline);
|
|
222
|
+
const providerLabel = askOffline
|
|
223
|
+
? offlineModelLabel(prefs.locale)
|
|
224
|
+
: sessionCreds
|
|
225
|
+
? formatActiveProviderLabel(prefs.locale)
|
|
226
|
+
: formatActiveProviderLabel(prefs.locale);
|
|
214
227
|
if (mode === "ask") {
|
|
215
|
-
let askCreds;
|
|
216
|
-
try {
|
|
217
|
-
askCreds = resolveAgentCredentials(opts.apiKey, prefs.locale);
|
|
218
|
-
}
|
|
219
|
-
catch (err) {
|
|
220
|
-
fail(err instanceof Error ? err.message : String(err));
|
|
221
|
-
process.exitCode = 1;
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
228
|
printWelcome({ cwd, locale: prefs.locale, mode: "ask", model: providerLabel });
|
|
225
229
|
if (opts.task.trim()) {
|
|
226
230
|
info(`${uiStrings(prefs.locale).workingOn}: ${opts.task.slice(0, 120)}${opts.task.length > 120 ? "…" : ""}\n`);
|
|
227
231
|
await executeAskTurn({
|
|
228
232
|
cwd,
|
|
229
233
|
question: opts.task.trim(),
|
|
230
|
-
offline:
|
|
234
|
+
offline: askOffline,
|
|
231
235
|
quiet: true,
|
|
232
236
|
locale: prefs.locale,
|
|
233
237
|
});
|
|
@@ -240,23 +244,54 @@ export async function runLocalAgent(opts) {
|
|
|
240
244
|
locale: prefs.locale,
|
|
241
245
|
mode: "ask",
|
|
242
246
|
apiKey: opts.apiKey,
|
|
243
|
-
askOffline
|
|
244
|
-
creds:
|
|
247
|
+
askOffline,
|
|
248
|
+
creds: sessionCreds,
|
|
249
|
+
offline: askOffline,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (askOffline) {
|
|
255
|
+
printWelcome({ cwd, locale: prefs.locale, mode, model: providerLabel });
|
|
256
|
+
savePreferences(cwd, { mode });
|
|
257
|
+
if (opts.task.trim()) {
|
|
258
|
+
info(`${uiStrings(prefs.locale).workingOn}: ${opts.task.slice(0, 120)}${opts.task.length > 120 ? "…" : ""}\n`);
|
|
259
|
+
warn(offlineAgentHint(prefs.locale));
|
|
260
|
+
}
|
|
261
|
+
if (opts.interactive !== false) {
|
|
262
|
+
await runRepl({
|
|
263
|
+
agent: null,
|
|
264
|
+
cwd,
|
|
265
|
+
locale: prefs.locale,
|
|
266
|
+
mode,
|
|
267
|
+
apiKey: opts.apiKey,
|
|
268
|
+
askOffline: true,
|
|
269
|
+
creds: null,
|
|
270
|
+
offline: true,
|
|
245
271
|
});
|
|
246
272
|
}
|
|
247
273
|
return;
|
|
248
274
|
}
|
|
275
|
+
if (!sessionCreds) {
|
|
276
|
+
fail(offlineAgentHint(prefs.locale));
|
|
277
|
+
process.exitCode = 1;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
249
280
|
let agent = null;
|
|
250
281
|
let resumed = false;
|
|
251
|
-
|
|
282
|
+
const creds = sessionCreds;
|
|
252
283
|
try {
|
|
253
|
-
creds = resolveAgentCredentials(opts.apiKey, prefs.locale);
|
|
254
284
|
if (usesCursorSdk(creds)) {
|
|
255
285
|
({ agent, resumed } = await createOrResumeAgent({ ...opts, mode }, prefs.locale));
|
|
256
286
|
}
|
|
257
287
|
}
|
|
258
288
|
catch (err) {
|
|
259
|
-
if (err instanceof
|
|
289
|
+
if (err instanceof CursorSdkMissingError) {
|
|
290
|
+
fail(err.message);
|
|
291
|
+
process.exitCode = 1;
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (err instanceof Error && err.name === "CursorAgentError") {
|
|
260
295
|
fail(`Agent startup failed: ${err.message}`);
|
|
261
296
|
process.exitCode = 1;
|
|
262
297
|
return;
|
|
@@ -298,8 +333,9 @@ export async function runLocalAgent(opts) {
|
|
|
298
333
|
locale: prefs.locale,
|
|
299
334
|
mode,
|
|
300
335
|
apiKey: opts.apiKey,
|
|
301
|
-
askOffline
|
|
336
|
+
askOffline,
|
|
302
337
|
creds,
|
|
338
|
+
offline: false,
|
|
303
339
|
});
|
|
304
340
|
}
|
|
305
341
|
else {
|
|
@@ -316,17 +352,15 @@ export async function runInteractiveAgent(opts) {
|
|
|
316
352
|
loadProjectEnv(cwd);
|
|
317
353
|
const prefs = loadPreferences(cwd, opts.locale, opts.mode, opts.plan);
|
|
318
354
|
const mode = prefs.mode;
|
|
319
|
-
const
|
|
355
|
+
const session = await prepareAgentSession({ ...opts, task: "" }, prefs);
|
|
356
|
+
if (!session)
|
|
357
|
+
return;
|
|
358
|
+
const { offline, creds: sessionCreds } = session;
|
|
359
|
+
const askOffline = offline || Boolean(opts.askOffline);
|
|
360
|
+
const providerLabel = askOffline
|
|
361
|
+
? offlineModelLabel(prefs.locale)
|
|
362
|
+
: formatActiveProviderLabel(prefs.locale);
|
|
320
363
|
if (mode === "ask") {
|
|
321
|
-
let creds;
|
|
322
|
-
try {
|
|
323
|
-
creds = resolveAgentCredentials(opts.apiKey, prefs.locale);
|
|
324
|
-
}
|
|
325
|
-
catch (err) {
|
|
326
|
-
fail(err instanceof Error ? err.message : String(err));
|
|
327
|
-
process.exitCode = 1;
|
|
328
|
-
return;
|
|
329
|
-
}
|
|
330
364
|
printWelcome({ cwd, locale: prefs.locale, mode: "ask", model: providerLabel });
|
|
331
365
|
await runRepl({
|
|
332
366
|
agent: null,
|
|
@@ -334,22 +368,47 @@ export async function runInteractiveAgent(opts) {
|
|
|
334
368
|
locale: prefs.locale,
|
|
335
369
|
mode: "ask",
|
|
336
370
|
apiKey: opts.apiKey,
|
|
337
|
-
askOffline
|
|
338
|
-
creds,
|
|
371
|
+
askOffline,
|
|
372
|
+
creds: sessionCreds,
|
|
373
|
+
offline: askOffline,
|
|
339
374
|
});
|
|
340
375
|
return;
|
|
341
376
|
}
|
|
377
|
+
if (askOffline) {
|
|
378
|
+
printWelcome({ cwd, locale: prefs.locale, mode, model: providerLabel });
|
|
379
|
+
savePreferences(cwd, { mode });
|
|
380
|
+
await runRepl({
|
|
381
|
+
agent: null,
|
|
382
|
+
cwd,
|
|
383
|
+
locale: prefs.locale,
|
|
384
|
+
mode,
|
|
385
|
+
apiKey: opts.apiKey,
|
|
386
|
+
askOffline: true,
|
|
387
|
+
creds: null,
|
|
388
|
+
offline: true,
|
|
389
|
+
});
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
if (!sessionCreds) {
|
|
393
|
+
fail(offlineAgentHint(prefs.locale));
|
|
394
|
+
process.exitCode = 1;
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
342
397
|
let agent = null;
|
|
343
398
|
let resumed = false;
|
|
344
|
-
|
|
399
|
+
const creds = sessionCreds;
|
|
345
400
|
try {
|
|
346
|
-
creds = resolveAgentCredentials(opts.apiKey, prefs.locale);
|
|
347
401
|
if (usesCursorSdk(creds)) {
|
|
348
402
|
({ agent, resumed } = await createOrResumeAgent({ ...opts, task: "", mode }, prefs.locale));
|
|
349
403
|
}
|
|
350
404
|
}
|
|
351
405
|
catch (err) {
|
|
352
|
-
if (err instanceof
|
|
406
|
+
if (err instanceof CursorSdkMissingError) {
|
|
407
|
+
fail(err.message);
|
|
408
|
+
process.exitCode = 1;
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
if (err instanceof Error && err.name === "CursorAgentError") {
|
|
353
412
|
fail(`Agent startup failed: ${err.message}`);
|
|
354
413
|
process.exitCode = 1;
|
|
355
414
|
return;
|
|
@@ -376,8 +435,9 @@ export async function runInteractiveAgent(opts) {
|
|
|
376
435
|
locale: prefs.locale,
|
|
377
436
|
mode,
|
|
378
437
|
apiKey: opts.apiKey,
|
|
379
|
-
askOffline
|
|
438
|
+
askOffline,
|
|
380
439
|
creds,
|
|
440
|
+
offline: false,
|
|
381
441
|
});
|
|
382
442
|
}
|
|
383
443
|
finally {
|
|
@@ -390,16 +450,31 @@ async function runRepl(opts) {
|
|
|
390
450
|
let currentMode = opts.mode;
|
|
391
451
|
let currentAgent = opts.agent;
|
|
392
452
|
let currentCreds = opts.creds;
|
|
453
|
+
let currentOffline = opts.offline;
|
|
393
454
|
let turn = 0;
|
|
394
455
|
const t = () => uiStrings(currentLocale);
|
|
395
456
|
const resolveCreds = () => {
|
|
457
|
+
if (currentOffline) {
|
|
458
|
+
try {
|
|
459
|
+
const live = resolveActiveCredentials();
|
|
460
|
+
if (live) {
|
|
461
|
+
currentOffline = false;
|
|
462
|
+
currentCreds = live;
|
|
463
|
+
return live;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
catch {
|
|
467
|
+
/* stay offline */
|
|
468
|
+
}
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
396
471
|
try {
|
|
397
472
|
currentCreds = resolveAgentCredentials(opts.apiKey, currentLocale);
|
|
473
|
+
return currentCreds;
|
|
398
474
|
}
|
|
399
475
|
catch {
|
|
400
|
-
|
|
476
|
+
return currentCreds;
|
|
401
477
|
}
|
|
402
|
-
return currentCreds;
|
|
403
478
|
};
|
|
404
479
|
while (true) {
|
|
405
480
|
const line = (await askWithPlaceholder({
|
|
@@ -410,7 +485,11 @@ async function runRepl(opts) {
|
|
|
410
485
|
break;
|
|
411
486
|
if (isProvidersMetaCommand(line.trim().split(/\s+/)[0] ?? "")) {
|
|
412
487
|
await runProvidersInteractive(currentLocale);
|
|
413
|
-
|
|
488
|
+
const live = resolveActiveCredentials();
|
|
489
|
+
if (live) {
|
|
490
|
+
currentOffline = false;
|
|
491
|
+
currentCreds = live;
|
|
492
|
+
}
|
|
414
493
|
continue;
|
|
415
494
|
}
|
|
416
495
|
const meta = handleMetaCommand(line, currentLocale, opts.cwd, currentMode);
|
|
@@ -432,7 +511,7 @@ async function runRepl(opts) {
|
|
|
432
511
|
}
|
|
433
512
|
else {
|
|
434
513
|
const creds = resolveCreds();
|
|
435
|
-
if (usesCursorSdk(creds)) {
|
|
514
|
+
if (creds && usesCursorSdk(creds)) {
|
|
436
515
|
currentAgent = await recreateAgentForMode(opts.cwd, currentAgent, currentMode, opts.apiKey, currentLocale);
|
|
437
516
|
}
|
|
438
517
|
else if (currentAgent) {
|
|
@@ -447,7 +526,8 @@ async function runRepl(opts) {
|
|
|
447
526
|
continue;
|
|
448
527
|
}
|
|
449
528
|
if (meta?.kind === "resume") {
|
|
450
|
-
|
|
529
|
+
const resumeCreds = resolveCreds();
|
|
530
|
+
if (currentMode === "ask" || !resumeCreds || !usesCursorSdk(resumeCreds)) {
|
|
451
531
|
warn(currentLocale === "pt"
|
|
452
532
|
? "Sessões guardadas só com provider Cursor."
|
|
453
533
|
: currentLocale === "zh"
|
|
@@ -468,7 +548,8 @@ async function runRepl(opts) {
|
|
|
468
548
|
continue;
|
|
469
549
|
}
|
|
470
550
|
if (meta?.kind === "save") {
|
|
471
|
-
|
|
551
|
+
const saveCreds = resolveCreds();
|
|
552
|
+
if (!currentAgent || !saveCreds || !usesCursorSdk(saveCreds)) {
|
|
472
553
|
warn(currentLocale === "pt"
|
|
473
554
|
? "Guardar sessão só com provider Cursor."
|
|
474
555
|
: currentLocale === "zh"
|
|
@@ -492,7 +573,7 @@ async function runRepl(opts) {
|
|
|
492
573
|
await executeAskTurn({
|
|
493
574
|
cwd: opts.cwd,
|
|
494
575
|
question: line,
|
|
495
|
-
offline: opts.askOffline,
|
|
576
|
+
offline: currentOffline || Boolean(opts.askOffline),
|
|
496
577
|
quiet: true,
|
|
497
578
|
locale: currentLocale,
|
|
498
579
|
});
|
|
@@ -500,7 +581,12 @@ async function runRepl(opts) {
|
|
|
500
581
|
turn++;
|
|
501
582
|
continue;
|
|
502
583
|
}
|
|
503
|
-
|
|
584
|
+
if (currentOffline || !resolveCreds()) {
|
|
585
|
+
warn(offlineAgentHint(currentLocale));
|
|
586
|
+
turn++;
|
|
587
|
+
continue;
|
|
588
|
+
}
|
|
589
|
+
const creds = currentCreds;
|
|
504
590
|
if (usesCursorSdk(creds) && !currentAgent) {
|
|
505
591
|
fail(currentLocale === "pt" ? "Sem agente Cursor activo." : "No active Cursor agent.");
|
|
506
592
|
continue;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/commands/setup.ts"],"names":[],"mappings":"AAEA,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,iBAE/E"}
|
package/dist/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { runFixCommand } from "./commands/agent.js";
|
|
|
11
11
|
import { runHealth } from "./commands/health.js";
|
|
12
12
|
import { runScan } from "./commands/scan.js";
|
|
13
13
|
import { runProvidersCommand } from "./commands/providers.js";
|
|
14
|
+
import { runSetupCommand } from "./commands/setup.js";
|
|
14
15
|
import { runWebhookTest } from "./commands/webhook.js";
|
|
15
16
|
const SUBCOMMANDS = new Set([
|
|
16
17
|
"doctor",
|
|
@@ -21,6 +22,7 @@ const SUBCOMMANDS = new Set([
|
|
|
21
22
|
"fix",
|
|
22
23
|
"ask",
|
|
23
24
|
"providers",
|
|
25
|
+
"setup",
|
|
24
26
|
]);
|
|
25
27
|
/** Bare `ekz` or `ekz "natural language task"` → agent. Resolves PT/zh CLI aliases. */
|
|
26
28
|
function normalizeArgv(argv) {
|
|
@@ -134,6 +136,20 @@ program
|
|
|
134
136
|
process.exitCode = 1;
|
|
135
137
|
}
|
|
136
138
|
});
|
|
139
|
+
program
|
|
140
|
+
.command("setup")
|
|
141
|
+
.alias("configurar")
|
|
142
|
+
.description("First-run setup — language, name, provider or offline")
|
|
143
|
+
.option("-l, --lang <code>", "Skip language pick: pt, en, or zh")
|
|
144
|
+
.action(async (opts) => {
|
|
145
|
+
try {
|
|
146
|
+
await runSetupCommand({ locale: opts.lang });
|
|
147
|
+
}
|
|
148
|
+
catch (err) {
|
|
149
|
+
console.error(err instanceof Error ? err.message : err);
|
|
150
|
+
process.exitCode = 1;
|
|
151
|
+
}
|
|
152
|
+
});
|
|
137
153
|
program
|
|
138
154
|
.command("providers")
|
|
139
155
|
.alias("provedores")
|
|
@@ -6,9 +6,9 @@ export type ProvidersCommandLabels = {
|
|
|
6
6
|
replAliases: string[];
|
|
7
7
|
};
|
|
8
8
|
export declare function providersCommandLabels(locale: EkzLocale): ProvidersCommandLabels;
|
|
9
|
-
export declare function providersSetupHint(
|
|
9
|
+
export declare function providersSetupHint(_locale?: EkzLocale): string;
|
|
10
10
|
export declare function isProvidersMetaCommand(token: string): boolean;
|
|
11
|
-
export type CliCommandId = "doctor" | "health" | "scan" | "webhook" | "agent" | "fix" | "ask" | "providers";
|
|
11
|
+
export type CliCommandId = "doctor" | "health" | "scan" | "webhook" | "agent" | "fix" | "ask" | "providers" | "setup";
|
|
12
12
|
export declare function resolveCliSubcommand(token: string): string;
|
|
13
13
|
export declare function isKnownSubcommand(token: string): boolean;
|
|
14
14
|
export declare const SPLASH_TOOLS: Record<EkzLocale, Array<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands-i18n.d.ts","sourceRoot":"","sources":["../../src/lib/commands-i18n.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"commands-i18n.d.ts","sourceRoot":"","sources":["../../src/lib/commands-i18n.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,GAAG,sBAAsB,CAuBhF;AAED,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,CAE9D;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,SAAS,GACT,OAAO,GACP,KAAK,GACL,KAAK,GACL,WAAW,GACX,OAAO,CAAC;AA2DZ,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAK1D;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAC/B,SAAS,EACT,KAAK,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAmCxC,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAGlG;AAED,uDAAuD;AACvD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAE/D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,EAAE,CAgB9D;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAIxD;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAQxF;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,GAAG,UAAU,GAAG,MAAM,CAMvF;AAED,wDAAwD;AACxD,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAkCpD,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI5D"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatSetupRequiredMessage } from "./setup-messages.js";
|
|
1
2
|
export function providersCommandLabels(locale) {
|
|
2
3
|
if (locale === "pt") {
|
|
3
4
|
return {
|
|
@@ -22,15 +23,8 @@ export function providersCommandLabels(locale) {
|
|
|
22
23
|
replAliases: ["/provider"],
|
|
23
24
|
};
|
|
24
25
|
}
|
|
25
|
-
export function providersSetupHint(
|
|
26
|
-
|
|
27
|
-
if (locale === "pt") {
|
|
28
|
-
return `Nenhum provider activo. Corre \`${labels.cli}\` ou \`${labels.repl}\` e cola a tua API key.`;
|
|
29
|
-
}
|
|
30
|
-
if (locale === "zh") {
|
|
31
|
-
return `未配置 provider。运行 \`${labels.cli}\` 或 \`${labels.repl}\` 并粘贴 API key。`;
|
|
32
|
-
}
|
|
33
|
-
return `No active provider. Run \`${labels.cli}\` or \`${labels.repl}\` and paste your API key.`;
|
|
26
|
+
export function providersSetupHint(_locale) {
|
|
27
|
+
return formatSetupRequiredMessage();
|
|
34
28
|
}
|
|
35
29
|
export function isProvidersMetaCommand(token) {
|
|
36
30
|
return resolveReplMetaCommand(token) === "providers";
|
|
@@ -44,6 +38,7 @@ const EN_SUBCOMMANDS = new Set([
|
|
|
44
38
|
"fix",
|
|
45
39
|
"ask",
|
|
46
40
|
"providers",
|
|
41
|
+
"setup",
|
|
47
42
|
]);
|
|
48
43
|
/** Map localized CLI tokens (and English) to canonical subcommand ids. */
|
|
49
44
|
const CLI_ALIASES = {
|
|
@@ -76,6 +71,12 @@ const CLI_ALIASES = {
|
|
|
76
71
|
供应商: "providers",
|
|
77
72
|
提供商: "providers",
|
|
78
73
|
模型: "providers",
|
|
74
|
+
setup: "setup",
|
|
75
|
+
configurar: "setup",
|
|
76
|
+
configuracao: "setup",
|
|
77
|
+
"configuração": "setup",
|
|
78
|
+
设置: "setup",
|
|
79
|
+
配置: "setup",
|
|
79
80
|
诊断: "doctor",
|
|
80
81
|
凭证: "health",
|
|
81
82
|
扫描: "scan",
|
|
@@ -104,6 +105,7 @@ export const SPLASH_TOOLS = {
|
|
|
104
105
|
{ label: "agente", hint: "agent" },
|
|
105
106
|
{ label: "perguntar", hint: "ask" },
|
|
106
107
|
{ label: "provedores", hint: "BYOK" },
|
|
108
|
+
{ label: "setup", hint: "configurar" },
|
|
107
109
|
],
|
|
108
110
|
en: [
|
|
109
111
|
{ label: "doctor" },
|
|
@@ -114,6 +116,7 @@ export const SPLASH_TOOLS = {
|
|
|
114
116
|
{ label: "agent" },
|
|
115
117
|
{ label: "ask" },
|
|
116
118
|
{ label: "providers", hint: "BYOK" },
|
|
119
|
+
{ label: "setup" },
|
|
117
120
|
],
|
|
118
121
|
zh: [
|
|
119
122
|
{ label: "诊断", hint: "doctor" },
|
|
@@ -124,6 +127,7 @@ export const SPLASH_TOOLS = {
|
|
|
124
127
|
{ label: "代理", hint: "agent" },
|
|
125
128
|
{ label: "问答", hint: "ask" },
|
|
126
129
|
{ label: "供应商", hint: "BYOK" },
|
|
130
|
+
{ label: "设置", hint: "setup" },
|
|
127
131
|
],
|
|
128
132
|
};
|
|
129
133
|
export function formatSplashTool(locale, tool) {
|
|
@@ -137,14 +141,19 @@ export function formatCliToolsSummary(locale) {
|
|
|
137
141
|
}
|
|
138
142
|
export function splashSessionLines(locale) {
|
|
139
143
|
const p = providersCommandLabels(locale);
|
|
144
|
+
const setupLine = locale === "pt"
|
|
145
|
+
? "ekz setup · ekz configurar"
|
|
146
|
+
: locale === "zh"
|
|
147
|
+
? "ekz setup · ekz 设置"
|
|
148
|
+
: "ekz setup";
|
|
140
149
|
const providersLine = `${p.repl} · ${p.cli}`;
|
|
141
150
|
if (locale === "pt") {
|
|
142
|
-
return [providersLine, "/guardar <nome>", "/retomar · /retomar 2", "ekz --resume", "ekz --fresh"];
|
|
151
|
+
return [setupLine, providersLine, "/guardar <nome>", "/retomar · /retomar 2", "ekz --resume", "ekz --fresh"];
|
|
143
152
|
}
|
|
144
153
|
if (locale === "zh") {
|
|
145
|
-
return [providersLine, "/保存 <名称>", "/恢复 · /恢复 2", "ekz --resume", "ekz --fresh"];
|
|
154
|
+
return [setupLine, providersLine, "/保存 <名称>", "/恢复 · /恢复 2", "ekz --resume", "ekz --fresh"];
|
|
146
155
|
}
|
|
147
|
-
return [providersLine, "/save <name>", "/resume · /resume 2", "ekz --resume", "ekz --fresh"];
|
|
156
|
+
return [setupLine, providersLine, "/save <name>", "/resume · /resume 2", "ekz --resume", "ekz --fresh"];
|
|
148
157
|
}
|
|
149
158
|
export function splashHelpHint(locale) {
|
|
150
159
|
if (locale === "pt")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { EkzLocale } from "./locale.js";
|
|
2
|
+
export type EkzGlobalConfig = {
|
|
3
|
+
locale?: EkzLocale;
|
|
4
|
+
userName?: string;
|
|
5
|
+
/** User chose offline mode (no LLM provider). */
|
|
6
|
+
offline?: boolean;
|
|
7
|
+
setupComplete?: boolean;
|
|
8
|
+
};
|
|
9
|
+
export declare function globalConfigPath(): string;
|
|
10
|
+
export declare function loadGlobalConfig(): EkzGlobalConfig;
|
|
11
|
+
export declare function saveGlobalConfig(patch: Partial<EkzGlobalConfig>): void;
|
|
12
|
+
export declare function getGlobalLocale(): EkzLocale | undefined;
|
|
13
|
+
export declare function getGlobalUserName(): string | undefined;
|
|
14
|
+
export declare function isGlobalOffline(): boolean;
|
|
15
|
+
export declare function setGlobalOffline(offline: boolean): void;
|
|
16
|
+
export declare function clearGlobalOffline(): void;
|
|
17
|
+
export declare function isSetupComplete(): boolean;
|
|
18
|
+
export declare function markSetupComplete(): void;
|
|
19
|
+
//# sourceMappingURL=global-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"global-config.d.ts","sourceRoot":"","sources":["../../src/lib/global-config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAK7C,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAMF,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,gBAAgB,IAAI,eAAe,CAQlD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,eAAe,CAAC,QAI/D;AAED,wBAAgB,eAAe,IAAI,SAAS,GAAG,SAAS,CAGvD;AAED,wBAAgB,iBAAiB,IAAI,MAAM,GAAG,SAAS,CAEtD;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,QAEhD;AAED,wBAAgB,kBAAkB,SAGjC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAEzC;AAED,wBAAgB,iBAAiB,SAEhC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
const EKZ_HOME = join(homedir(), ".ekz");
|
|
5
|
+
const CONFIG_PATH = join(EKZ_HOME, "config.json");
|
|
6
|
+
function ensureEkzHome() {
|
|
7
|
+
if (!existsSync(EKZ_HOME))
|
|
8
|
+
mkdirSync(EKZ_HOME, { recursive: true, mode: 0o700 });
|
|
9
|
+
}
|
|
10
|
+
export function globalConfigPath() {
|
|
11
|
+
return CONFIG_PATH;
|
|
12
|
+
}
|
|
13
|
+
export function loadGlobalConfig() {
|
|
14
|
+
ensureEkzHome();
|
|
15
|
+
if (!existsSync(CONFIG_PATH))
|
|
16
|
+
return {};
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(readFileSync(CONFIG_PATH, "utf8"));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function saveGlobalConfig(patch) {
|
|
25
|
+
ensureEkzHome();
|
|
26
|
+
const next = { ...loadGlobalConfig(), ...patch };
|
|
27
|
+
writeFileSync(CONFIG_PATH, JSON.stringify(next, null, 2) + "\n", { mode: 0o600 });
|
|
28
|
+
}
|
|
29
|
+
export function getGlobalLocale() {
|
|
30
|
+
const l = loadGlobalConfig().locale;
|
|
31
|
+
return l === "pt" || l === "en" || l === "zh" ? l : undefined;
|
|
32
|
+
}
|
|
33
|
+
export function getGlobalUserName() {
|
|
34
|
+
return loadGlobalConfig().userName?.trim() || undefined;
|
|
35
|
+
}
|
|
36
|
+
export function isGlobalOffline() {
|
|
37
|
+
return loadGlobalConfig().offline === true;
|
|
38
|
+
}
|
|
39
|
+
export function setGlobalOffline(offline) {
|
|
40
|
+
saveGlobalConfig({ offline, setupComplete: true });
|
|
41
|
+
}
|
|
42
|
+
export function clearGlobalOffline() {
|
|
43
|
+
const cfg = loadGlobalConfig();
|
|
44
|
+
if (cfg.offline)
|
|
45
|
+
saveGlobalConfig({ offline: false });
|
|
46
|
+
}
|
|
47
|
+
export function isSetupComplete() {
|
|
48
|
+
return loadGlobalConfig().setupComplete === true;
|
|
49
|
+
}
|
|
50
|
+
export function markSetupComplete() {
|
|
51
|
+
saveGlobalConfig({ setupComplete: true });
|
|
52
|
+
}
|
package/dist/lib/help.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/lib/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAe,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAGtD,KAAK,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAatD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/lib/help.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAe,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAGtD,KAAK,WAAW,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAatD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,CA+HzF;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,OAAO,QAezE"}
|
package/dist/lib/help.js
CHANGED
|
@@ -44,6 +44,7 @@ export function buildHelpSections(locale, currentMode) {
|
|
|
44
44
|
? [
|
|
45
45
|
"Escreve perguntas ou tarefas em linguagem natural.",
|
|
46
46
|
`${p.repl} · ${p.cli} (BYOK)`,
|
|
47
|
+
"ekz setup · ekz configurar",
|
|
47
48
|
"/ajuda · /help",
|
|
48
49
|
"/modo agente|plan|ask · /mode agent|plan|ask",
|
|
49
50
|
"/idioma pt · /lang pt|en|zh",
|
|
@@ -55,6 +56,7 @@ export function buildHelpSections(locale, currentMode) {
|
|
|
55
56
|
? [
|
|
56
57
|
"用自然语言提问或描述任务。",
|
|
57
58
|
`${p.repl} · ${p.cli} (BYOK)`,
|
|
59
|
+
"ekz setup · ekz 设置",
|
|
58
60
|
"/帮助 · /help · /ajuda",
|
|
59
61
|
"/模式 代理|plan|ask · /mode agent|plan|ask",
|
|
60
62
|
"/语言 pt · /lang pt|en|zh",
|
|
@@ -65,6 +67,7 @@ export function buildHelpSections(locale, currentMode) {
|
|
|
65
67
|
: [
|
|
66
68
|
"Type questions or tasks in natural language.",
|
|
67
69
|
`${p.repl} · ${p.cli} (BYOK)`,
|
|
70
|
+
"ekz setup",
|
|
68
71
|
"/help · /ajuda · /帮助",
|
|
69
72
|
"/mode agent|plan|ask",
|
|
70
73
|
"/lang pt · /lang en · /lang zh",
|
package/dist/lib/locale.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locale.d.ts","sourceRoot":"","sources":["../../src/lib/locale.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"locale.d.ts","sourceRoot":"","sources":["../../src/lib/locale.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAInD,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAIlD,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAOnE;AAED,sEAAsE;AACtE,wBAAgB,aAAa,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,IAAI,GAAG,SAAS,CAE3F;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAgB7D;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS;;;;;;;;;;;;;;;;;;;sBAqBnB,MAAM;;;;;;;;;;EA4H7B"}
|
package/dist/lib/locale.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getGlobalLocale } from "./global-config.js";
|
|
1
2
|
export const LOCALE_LABELS = {
|
|
2
3
|
pt: "Português (Angola)",
|
|
3
4
|
en: "English",
|
|
@@ -22,7 +23,7 @@ export function parseLocale(input) {
|
|
|
22
23
|
}
|
|
23
24
|
/** Default: Portuguese (Angola). Override with EKZ_LANG or --lang. */
|
|
24
25
|
export function resolveLocale(explicit, saved) {
|
|
25
|
-
return parseLocale(explicit) ?? parseLocale(process.env.EKZ_LANG) ?? saved ?? "pt";
|
|
26
|
+
return parseLocale(explicit) ?? parseLocale(process.env.EKZ_LANG) ?? saved ?? getGlobalLocale() ?? "pt";
|
|
26
27
|
}
|
|
27
28
|
export function languageInstruction(locale) {
|
|
28
29
|
if (locale === "pt") {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { type EkzLocale } from "./locale.js";
|
|
2
|
+
import { type ResolvedCredentials } from "./providers/credentials.js";
|
|
3
|
+
export type AgentReadyState = {
|
|
4
|
+
offline: boolean;
|
|
5
|
+
creds: ResolvedCredentials | null;
|
|
6
|
+
};
|
|
7
|
+
export declare function offlineModelLabel(locale: EkzLocale): string;
|
|
8
|
+
/** Full first-run wizard: language → name → provider/offline. */
|
|
9
|
+
export declare function runSetupWizard(opts?: {
|
|
10
|
+
locale?: string;
|
|
11
|
+
force?: boolean;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
/** Ensures the agent can start — provider, offline, or throws setup hint. */
|
|
14
|
+
export declare function ensureAgentReady(options: {
|
|
15
|
+
locale: EkzLocale;
|
|
16
|
+
offlineFlag?: boolean;
|
|
17
|
+
apiKey?: string;
|
|
18
|
+
}): Promise<AgentReadyState>;
|
|
19
|
+
export declare function offlineAgentHint(locale: EkzLocale): string;
|
|
20
|
+
//# sourceMappingURL=onboarding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../src/lib/onboarding.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,aAAa,CAAC;AAG1D,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AAyBpC,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAC;CACnC,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAI3D;AAoFD,iEAAiE;AACjE,wBAAsB,cAAc,CAAC,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,iBA6B/E;AAED,6EAA6E;AAC7E,wBAAsB,gBAAgB,CAAC,OAAO,EAAE;IAC9C,MAAM,EAAE,SAAS,CAAC;IAClB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,eAAe,CAAC,CAgB3B;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAQ1D"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import readline from "readline";
|
|
2
|
+
import { providersCommandLabels } from "./commands-i18n.js";
|
|
3
|
+
import { markSetupComplete, saveGlobalConfig, setGlobalOffline, clearGlobalOffline, isGlobalOffline, } from "./global-config.js";
|
|
4
|
+
import { parseLocale } from "./locale.js";
|
|
5
|
+
import { formatSetupRequiredMessage } from "./setup-messages.js";
|
|
6
|
+
import { info, ok } from "./output.js";
|
|
7
|
+
import { ProviderSetupError, resolveActiveCredentials, resolveCredentialsWithOverride, } from "./providers/credentials.js";
|
|
8
|
+
import { runProvidersInteractive } from "./providers/ui.js";
|
|
9
|
+
import { c, muted } from "./theme.js";
|
|
10
|
+
function t(locale, pt, en, zh) {
|
|
11
|
+
if (locale === "pt")
|
|
12
|
+
return pt;
|
|
13
|
+
if (locale === "zh")
|
|
14
|
+
return zh;
|
|
15
|
+
return en;
|
|
16
|
+
}
|
|
17
|
+
async function askLine(prompt) {
|
|
18
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
rl.question(prompt, (answer) => {
|
|
21
|
+
rl.close();
|
|
22
|
+
resolve(answer.trim());
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function hasProvider(apiKey) {
|
|
27
|
+
if (apiKey?.trim())
|
|
28
|
+
return true;
|
|
29
|
+
return Boolean(resolveActiveCredentials());
|
|
30
|
+
}
|
|
31
|
+
export function offlineModelLabel(locale) {
|
|
32
|
+
if (locale === "pt")
|
|
33
|
+
return "offline · doctor/scan";
|
|
34
|
+
if (locale === "zh")
|
|
35
|
+
return "离线 · 诊断/扫描";
|
|
36
|
+
return "offline · doctor/scan";
|
|
37
|
+
}
|
|
38
|
+
async function pickLanguage() {
|
|
39
|
+
console.log("");
|
|
40
|
+
console.log(` ${c.bold}Ekz Connect — Setup${c.reset}`);
|
|
41
|
+
console.log("");
|
|
42
|
+
console.log(` ${muted("Choose language · Escolhe idioma · 选择语言")}`);
|
|
43
|
+
console.log("");
|
|
44
|
+
console.log(` ${c.bold}1.${c.reset} Português`);
|
|
45
|
+
console.log(` ${c.bold}2.${c.reset} English`);
|
|
46
|
+
console.log(` ${c.bold}3.${c.reset} 中文`);
|
|
47
|
+
console.log("");
|
|
48
|
+
const choice = (await askLine(" [1/2/3]: ")).toLowerCase();
|
|
49
|
+
if (choice === "2" || choice === "en" || choice === "english")
|
|
50
|
+
return "en";
|
|
51
|
+
if (choice === "3" || choice === "zh" || choice === "中文")
|
|
52
|
+
return "zh";
|
|
53
|
+
return "pt";
|
|
54
|
+
}
|
|
55
|
+
async function askUserName(locale) {
|
|
56
|
+
console.log("");
|
|
57
|
+
const prompt = t(locale, "Como devo te chamar? ", "What should we call you? (first name): ", "怎么称呼您?(名字): ");
|
|
58
|
+
const name = await askLine(` ${prompt}`);
|
|
59
|
+
if (!name)
|
|
60
|
+
return t(locale, "Developer", "Developer", "开发者");
|
|
61
|
+
return name.split(/\s+/)[0] ?? name;
|
|
62
|
+
}
|
|
63
|
+
async function providerStep(locale) {
|
|
64
|
+
const p = providersCommandLabels(locale);
|
|
65
|
+
console.log("");
|
|
66
|
+
console.log(` ${muted(t(locale, "LLM (BYOK) — opcional agora", "LLM (BYOK) — optional for now", "LLM (BYOK) — 可选"))}`);
|
|
67
|
+
console.log("");
|
|
68
|
+
console.log(` ${c.bold}1.${c.reset} ${t(locale, "Configurar provedor", "Configure provider", "配置 provider")}`);
|
|
69
|
+
console.log(` ${muted(t(locale, p.cli, p.cli, p.cli))}`);
|
|
70
|
+
console.log("");
|
|
71
|
+
console.log(` ${c.bold}2.${c.reset} ${t(locale, "Continuar offline", "Continue offline", "离线模式")}`);
|
|
72
|
+
console.log(` ${muted(t(locale, "doctor · scan · sem AI", "doctor · scan · no AI", "诊断 · 扫描 · 无 AI"))}`);
|
|
73
|
+
console.log("");
|
|
74
|
+
console.log(` ${muted("Enter · skip")}`);
|
|
75
|
+
console.log("");
|
|
76
|
+
const choice = (await askLine(t(locale, " Escolha [1/2/Enter]: ", " Choice [1/2/Enter]: ", " 选择 [1/2/Enter]: "))).toLowerCase();
|
|
77
|
+
if (choice === "2" || choice === "offline" || choice === "离线") {
|
|
78
|
+
setGlobalOffline(true);
|
|
79
|
+
ok(t(locale, "Modo offline activo.", "Offline mode enabled.", "已启用离线模式。"));
|
|
80
|
+
return "offline";
|
|
81
|
+
}
|
|
82
|
+
if (choice === "1" || choice === "p" || choice === "providers" || choice === "provedores" || choice === "供应商") {
|
|
83
|
+
await runProvidersInteractive(locale);
|
|
84
|
+
if (resolveActiveCredentials()) {
|
|
85
|
+
clearGlobalOffline();
|
|
86
|
+
ok(t(locale, "Provedor configurado.", "Provider configured.", "Provider 已配置。"));
|
|
87
|
+
return "configured";
|
|
88
|
+
}
|
|
89
|
+
info(t(locale, "Provedor não configurado.", "No provider configured.", "未配置 provider。"));
|
|
90
|
+
return "skip";
|
|
91
|
+
}
|
|
92
|
+
return "skip";
|
|
93
|
+
}
|
|
94
|
+
/** Full first-run wizard: language → name → provider/offline. */
|
|
95
|
+
export async function runSetupWizard(opts) {
|
|
96
|
+
if (!process.stdin.isTTY) {
|
|
97
|
+
console.error(formatSetupRequiredMessage());
|
|
98
|
+
process.exitCode = 1;
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
let locale = parseLocale(opts?.locale) ?? (await pickLanguage());
|
|
102
|
+
saveGlobalConfig({ locale });
|
|
103
|
+
const userName = await askUserName(locale);
|
|
104
|
+
saveGlobalConfig({ userName });
|
|
105
|
+
ok(t(locale, `Olá ${userName}!`, `Hello ${userName}!`, `你好 ${userName}!`));
|
|
106
|
+
await providerStep(locale);
|
|
107
|
+
markSetupComplete();
|
|
108
|
+
console.log("");
|
|
109
|
+
ok(t(locale, "Setup concluído. Corre `ekz` para começar.", "Setup complete. Run `ekz` to start.", "设置完成。运行 `ekz` 开始。"));
|
|
110
|
+
}
|
|
111
|
+
/** Ensures the agent can start — provider, offline, or throws setup hint. */
|
|
112
|
+
export async function ensureAgentReady(options) {
|
|
113
|
+
const { locale, offlineFlag, apiKey } = options;
|
|
114
|
+
if (offlineFlag || isGlobalOffline()) {
|
|
115
|
+
return { offline: true, creds: null };
|
|
116
|
+
}
|
|
117
|
+
if (hasProvider(apiKey)) {
|
|
118
|
+
clearGlobalOffline();
|
|
119
|
+
if (apiKey?.trim()) {
|
|
120
|
+
return { offline: false, creds: resolveCredentialsWithOverride(apiKey, locale) };
|
|
121
|
+
}
|
|
122
|
+
return { offline: false, creds: resolveActiveCredentials() };
|
|
123
|
+
}
|
|
124
|
+
throw new ProviderSetupError(formatSetupRequiredMessage());
|
|
125
|
+
}
|
|
126
|
+
export function offlineAgentHint(locale) {
|
|
127
|
+
const p = providersCommandLabels(locale);
|
|
128
|
+
return t(locale, `Modo offline — sem AI. Usa \`ekz setup\`, ${p.repl}, /diagnóstico ou /analisar.`, `Offline mode — no AI. Use \`ekz setup\`, ${p.repl}, /doctor, or /scan.`, `离线模式 — 无 AI。使用 \`ekz setup\`、${p.repl}、/诊断 或 /扫描。`);
|
|
129
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ProviderDefinition, type ProviderId } from "./catalog.js";
|
|
2
|
+
import type { EkzLocale } from "../locale.js";
|
|
2
3
|
import { type StoredProvider } from "./store.js";
|
|
3
4
|
export type ResolvedCredentials = {
|
|
4
5
|
id: ProviderId;
|
|
@@ -23,4 +24,5 @@ export declare function getActiveProviderId(): ProviderId | undefined;
|
|
|
23
24
|
export declare function resolveActiveCredentials(): ResolvedCredentials | null;
|
|
24
25
|
export declare function requireActiveCredentials(locale?: "pt" | "en" | "zh"): ResolvedCredentials;
|
|
25
26
|
export declare function isCursorProvider(creds: ResolvedCredentials): boolean;
|
|
27
|
+
export declare function resolveCredentialsWithOverride(override?: string, locale?: EkzLocale): ResolvedCredentials;
|
|
26
28
|
//# sourceMappingURL=credentials.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2C,KAAK,kBAAkB,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAGjH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAKL,KAAK,cAAc,EACpB,MAAM,YAAY,CAAC;AAEpB,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,UAAU,CAAC;IACf,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,gBAAgB,IAAI,WAAW,EAAE,CAYhD;AAED,wBAAgB,mBAAmB,IAAI,UAAU,GAAG,SAAS,CAE5D;AAED,wBAAgB,wBAAwB,IAAI,mBAAmB,GAAG,IAAI,CAgBrE;AAED,wBAAgB,wBAAwB,CAAC,MAAM,GAAE,IAAI,GAAG,IAAI,GAAG,IAAW,GAAG,mBAAmB,CAI/F;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAEpE;AAED,wBAAgB,8BAA8B,CAC5C,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,GAAE,SAAgB,GACvB,mBAAmB,CAarB"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { PROVIDER_CATALOG } from "./catalog.js";
|
|
2
|
-
import {
|
|
1
|
+
import { PROVIDER_CATALOG, getProviderDefinition } from "./catalog.js";
|
|
2
|
+
import { formatSetupRequiredMessage } from "../setup-messages.js";
|
|
3
|
+
import { resolveComposerModel } from "../composer-model.js";
|
|
3
4
|
import { loadProvidersFile, maskApiKey, resolveBaseUrl, resolveModel, } from "./store.js";
|
|
4
5
|
export class ProviderSetupError extends Error {
|
|
5
6
|
constructor(message) {
|
|
@@ -47,8 +48,23 @@ export function requireActiveCredentials(locale = "en") {
|
|
|
47
48
|
const creds = resolveActiveCredentials();
|
|
48
49
|
if (creds)
|
|
49
50
|
return creds;
|
|
50
|
-
throw new ProviderSetupError(
|
|
51
|
+
throw new ProviderSetupError(formatSetupRequiredMessage());
|
|
51
52
|
}
|
|
52
53
|
export function isCursorProvider(creds) {
|
|
53
54
|
return creds.definition.kind === "cursor";
|
|
54
55
|
}
|
|
56
|
+
export function resolveCredentialsWithOverride(override, locale = "en") {
|
|
57
|
+
if (override?.trim()) {
|
|
58
|
+
const def = getProviderDefinition("cursor");
|
|
59
|
+
if (!def)
|
|
60
|
+
throw new Error("Cursor provider missing from catalog.");
|
|
61
|
+
return {
|
|
62
|
+
id: "cursor",
|
|
63
|
+
definition: def,
|
|
64
|
+
apiKey: override.trim(),
|
|
65
|
+
model: resolveComposerModel().id,
|
|
66
|
+
maskedKey: maskApiKey(override.trim()),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return requireActiveCredentials(locale);
|
|
70
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { type SDKAgent } from "@cursor/sdk";
|
|
2
1
|
import { type EkzLocale } from "../locale.js";
|
|
2
|
+
import { type SDKAgent } from "./cursor-sdk.js";
|
|
3
3
|
import { type ResolvedCredentials } from "./credentials.js";
|
|
4
4
|
declare function disposeAgent(agent: SDKAgent): Promise<void>;
|
|
5
|
-
declare function streamRun(agent: SDKAgent, prompt: string): Promise<void>;
|
|
5
|
+
declare function streamRun(agent: SDKAgent, prompt: string, locale: EkzLocale): Promise<void>;
|
|
6
6
|
export declare function executeCursorAskTurn(options: {
|
|
7
7
|
cwd: string;
|
|
8
8
|
question: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/cursor-runner.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cursor-runner.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/cursor-runner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,SAAS,EAAuB,MAAM,cAAc,CAAC;AAInE,OAAO,EAAiB,KAAK,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,iBAAe,YAAY,CAAC,KAAK,EAAE,QAAQ,iBAI1C;AAED,iBAAe,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC1F;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,mBAAmB,CAAC;IAC3B,MAAM,EAAE,SAAS,CAAC;CACnB,GAAG,OAAO,CAAC,OAAO,CAAC,CAmCnB;AAED,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { resolve } from "path";
|
|
2
|
-
import { Agent, AgentBusyError, CursorAgentError } from "@cursor/sdk";
|
|
3
2
|
import { buildBootstrapContext } from "../context.js";
|
|
4
3
|
import { resolveComposerModel } from "../composer-model.js";
|
|
5
|
-
import { fail } from "../output.js";
|
|
6
|
-
import { toolDone, toolError, toolRunning } from "../theme.js";
|
|
7
4
|
import { languageInstruction } from "../locale.js";
|
|
5
|
+
import { fail } from "../output.js";
|
|
8
6
|
import { agentScopeBlock } from "../scope.js";
|
|
7
|
+
import { toolDone, toolError, toolRunning } from "../theme.js";
|
|
8
|
+
import { loadCursorSdk } from "./cursor-sdk.js";
|
|
9
9
|
async function disposeAgent(agent) {
|
|
10
10
|
const disposer = agent[Symbol.asyncDispose];
|
|
11
11
|
if (typeof disposer === "function")
|
|
@@ -13,7 +13,8 @@ async function disposeAgent(agent) {
|
|
|
13
13
|
else
|
|
14
14
|
agent.close();
|
|
15
15
|
}
|
|
16
|
-
async function streamRun(agent, prompt) {
|
|
16
|
+
async function streamRun(agent, prompt, locale) {
|
|
17
|
+
const { AgentBusyError, CursorAgentError } = await loadCursorSdk(locale);
|
|
17
18
|
const sendOptions = {
|
|
18
19
|
onDelta: ({ update }) => {
|
|
19
20
|
if (update.type === "text-delta" && update.text)
|
|
@@ -52,6 +53,7 @@ export async function executeCursorAskTurn(options) {
|
|
|
52
53
|
const cwd = resolve(options.cwd);
|
|
53
54
|
const ctx = await buildBootstrapContext(cwd);
|
|
54
55
|
const model = resolveComposerModel(process.env, options.creds.model);
|
|
56
|
+
const { Agent } = await loadCursorSdk(options.locale);
|
|
55
57
|
const prompt = `${agentScopeBlock(options.locale)}
|
|
56
58
|
|
|
57
59
|
${languageInstruction(options.locale)}
|
|
@@ -71,7 +73,7 @@ ${options.question}`;
|
|
|
71
73
|
model: { id: model.id, params: model.params },
|
|
72
74
|
local: { cwd, settingSources: ["project"] },
|
|
73
75
|
});
|
|
74
|
-
await streamRun(agent, prompt);
|
|
76
|
+
await streamRun(agent, prompt, options.locale);
|
|
75
77
|
console.log("");
|
|
76
78
|
return true;
|
|
77
79
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { EkzLocale } from "../locale.js";
|
|
2
|
+
export declare class CursorSdkMissingError extends Error {
|
|
3
|
+
constructor(locale?: EkzLocale);
|
|
4
|
+
}
|
|
5
|
+
type CursorSdkModule = typeof import("@cursor/sdk");
|
|
6
|
+
export declare function loadCursorSdk(locale?: EkzLocale): Promise<CursorSdkModule>;
|
|
7
|
+
export type { SDKAgent } from "@cursor/sdk";
|
|
8
|
+
//# sourceMappingURL=cursor-sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor-sdk.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/cursor-sdk.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,MAAM,GAAE,SAAgB;CAWrC;AAED,KAAK,eAAe,GAAG,cAAc,aAAa,CAAC,CAAC;AAIpD,wBAAsB,aAAa,CAAC,MAAM,GAAE,SAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAQtF;AAED,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { providersCommandLabels } from "../commands-i18n.js";
|
|
2
|
+
export class CursorSdkMissingError extends Error {
|
|
3
|
+
constructor(locale = "en") {
|
|
4
|
+
const p = providersCommandLabels(locale);
|
|
5
|
+
const msg = locale === "pt"
|
|
6
|
+
? `Provider Cursor requer @cursor/sdk. Instala: npm install -g @cursor/sdk\nOu usa outro provider em ${p.repl}.`
|
|
7
|
+
: locale === "zh"
|
|
8
|
+
? `Cursor provider 需要 @cursor/sdk。安装: npm install -g @cursor/sdk\n或在 ${p.repl} 选择其他 provider。`
|
|
9
|
+
: `Cursor provider requires @cursor/sdk. Run: npm install -g @cursor/sdk\nOr pick another provider via ${p.repl}.`;
|
|
10
|
+
super(msg);
|
|
11
|
+
this.name = "CursorSdkMissingError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
let cached = null;
|
|
15
|
+
export async function loadCursorSdk(locale = "en") {
|
|
16
|
+
if (cached)
|
|
17
|
+
return cached;
|
|
18
|
+
try {
|
|
19
|
+
cached = await import("@cursor/sdk");
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
throw new CursorSdkMissingError(locale);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/store.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAyB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/store.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,UAAU,EAAyB,MAAM,cAAc,CAAC;AAGtE,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC,CAAC;CACxD,CAAC;AAWF,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C;AAED,wBAAgB,iBAAiB,IAAI,aAAa,CAcjD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,QAQpD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,UAAU,GAAG,cAAc,GAAG,SAAS,CAE5E;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,cAAc,QAOtE;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,UAAU,QAOlD;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,UAAU,QAO/C;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,CAG5E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAG1F"}
|
|
@@ -2,6 +2,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "f
|
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { getProviderDefinition } from "./catalog.js";
|
|
5
|
+
import { clearGlobalOffline, markSetupComplete } from "../global-config.js";
|
|
5
6
|
const EKZ_HOME = join(homedir(), ".ekz");
|
|
6
7
|
const PROVIDERS_PATH = join(EKZ_HOME, "providers.json");
|
|
7
8
|
function ensureEkzHome() {
|
|
@@ -53,6 +54,8 @@ export function setStoredProvider(id, entry) {
|
|
|
53
54
|
if (!file.active)
|
|
54
55
|
file.active = id;
|
|
55
56
|
saveProvidersFile(file);
|
|
57
|
+
clearGlobalOffline();
|
|
58
|
+
markSetupComplete();
|
|
56
59
|
}
|
|
57
60
|
export function removeStoredProvider(id) {
|
|
58
61
|
const file = loadProvidersFile();
|
|
@@ -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.
|
|
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
|
},
|