@ekzs/cli 0.3.3 → 0.3.5

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 CHANGED
@@ -1,43 +1,59 @@
1
1
  # @ekzs/cli
2
2
 
3
+ **Version:** 0.3.5
4
+
3
5
  **Ekz** is a local coding agent for e-Kwanza Pagamento Integrado v2.4 — like Claude Code, but specialized for `@ekzs/connect`, GPO, EMIS ref, and Ticket integrations.
4
6
 
5
- Runs **in your repo** with Composer 2.5 Fast. Edits files directly.
7
+ Runs **in your repo**. Edits files directly in **agent** or **plan** mode. You bring your own LLM key (BYOK) — Cursor, OpenAI, Anthropic, DeepSeek, and more.
6
8
 
7
9
  ## Install
8
10
 
9
11
  ```bash
10
12
  npm install -g @ekzs/cli
13
+ ekz setup
11
14
  ```
12
15
 
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
- ```
16
+ Clean install (~15 packages). Keys are configured locally via `ekz setup` or `ekz providers` — never committed to git.
20
17
 
21
- Or install both at once:
18
+ **Cursor:** if you pick Cursor during `ekz setup`, Ekz checks for `@cursor/sdk` **before** asking for your API key and offers to run `npm install -g @cursor/sdk` for you. You can also install it yourself beforehand:
22
19
 
23
20
  ```bash
24
21
  npm install -g @ekzs/cli @cursor/sdk
25
22
  ```
26
23
 
27
- ## Setup (BYOK)
24
+ Other providers (OpenAI, Anthropic, etc.) need no extra packages.
28
25
 
29
- First run:
26
+ ## First run — `ekz setup`
30
27
 
31
28
  ```bash
32
29
  ekz setup
33
30
  # aliases: ekz configurar · ekz 设置
34
31
  ```
35
32
 
36
- Wizard steps: **language** → **your name** → **Configurar provedor** (or offline).
33
+ Wizard steps:
34
+
35
+ 1. **Language** — Português · English · 中文
36
+ 2. **Your name** — shown on the splash screen
37
+ 3. **Provider or offline** — configure one LLM key, or skip to doctor/scan-only mode
38
+
39
+ When you configure a provider:
37
40
 
38
- Stored in `~/.ekz/config.json` (name, locale) and `~/.ekz/providers.json` (API keys).
41
+ - Pick from the **numbered provider list**
42
+ - **Cursor only:** Ekz checks for `@cursor/sdk` and offers to install it **before** the API key step
43
+ - Paste your **API key**
44
+ - Pick a **model from a predefined list** (Enter = default)
45
+ - Setup **finishes and exits** — then run `ekz` to start the agent
39
46
 
40
- Without a provider, `ekz` shows:
47
+ No base URL prompts — endpoints are predefined per provider.
48
+
49
+ Stored in:
50
+
51
+ | File | Contents |
52
+ |------|----------|
53
+ | `~/.ekz/config.json` | locale, name, offline flag, setup complete |
54
+ | `~/.ekz/providers.json` | active provider, API keys, models (mode `600`) |
55
+
56
+ Without setup or a provider, `ekz` shows:
41
57
 
42
58
  ```
43
59
  Setup required · Configuração necessária · 需要设置
@@ -47,78 +63,78 @@ Setup required · Configuração necessária · 需要设置
47
63
  zh: 运行 `ekz setup` 或 `ekz --offline`
48
64
  ```
49
65
 
50
- Supported providers: **Cursor**, **OpenAI**, **Anthropic**, **DeepSeek**, **Kimi**, **Groq**, **Mistral**, **Google Gemini**, **Ollama**.
51
-
52
- | Mode | BYOK behavior |
53
- |------|----------------|
54
- | `agent` | Cursor → local SDK; others → tool loop (read/write/run) |
55
- | `plan` | Same, plan-first instructions |
56
- | `ask` | Chat only (Cursor uses lightweight Cursor agent) |
57
-
58
- **Localized commands:** `ekz provedores` / `/provedores` (PT) · `ekz 供应商` / `/供应商` (ZH)
66
+ ### Supported providers
59
67
 
60
- Optional: `--api-key crsr_...` for a one-off Cursor override in agent/plan mode.
68
+ | Provider | Example models |
69
+ |----------|----------------|
70
+ | **Cursor** | Requires `@cursor/sdk` (offered during setup) · `composer-2.5-fast`, `gpt-5.3-codex`, … |
71
+ | **OpenAI** | `gpt-4o`, `gpt-4o-mini`, `o1`, `o3-mini`, … |
72
+ | **Anthropic** | `claude-sonnet-4-20250514`, `claude-opus-4-20250514`, … |
73
+ | **DeepSeek** | `deepseek-chat`, `deepseek-reasoner` |
74
+ | **Kimi (Moonshot)** | `moonshot-v1-8k`, `moonshot-v1-32k`, … |
75
+ | **Groq** | `llama-3.3-70b-versatile`, … |
76
+ | **Mistral** | `mistral-large-latest`, `codestral-latest`, … |
77
+ | **Google Gemini** | `gemini-2.0-flash`, `gemini-1.5-pro`, … |
78
+ | **Ollama** | `llama3.2`, `qwen2.5`, … (local) |
61
79
 
62
- ### Run from this repo
80
+ Manage keys anytime:
63
81
 
64
82
  ```bash
65
- npm run build:cli
66
-
67
- # Option A — wrapper (no global install)
68
- ./ekz # Git Bash / macOS / Linux
69
- .\ekz # PowerShell / cmd (resolves to ekz.cmd)
70
- npx ekz # any shell, from repo root
71
-
72
- # Option B — npm script
73
- npm run ekz # interactive REPL
74
- npm run ekz -- agent "fix webhook"
75
-
76
- # Option C — global command (once)
77
- npm run setup:cli # build + npm link
78
- ekz # works everywhere
83
+ ekz providers # full menu — configure, switch, remove
84
+ ekz provedores # PT
85
+ ekz 供应商 # ZH
79
86
  ```
80
87
 
81
- ### Windows / PowerShell
88
+ In the REPL: `/providers`, `/provedores`, `/供应商`
82
89
 
83
- From the repo root (after `npm run build:cli`):
90
+ ### Modes
84
91
 
85
- ```powershell
86
- .\ekz # same as ekz.cmd — no extension needed
87
- npx ekz # works in PowerShell, cmd, Git Bash
88
- npm run setup:cli # then `ekz` works globally
89
- ```
92
+ | Mode | Aliases | Behavior |
93
+ |------|---------|----------|
94
+ | **agent** | default, `agente`, `代理` | Edits files and runs commands |
95
+ | **plan** | `plano`, `计划` | Plans before editing |
96
+ | **ask** | `perguntar`, `问答`, `chat` | Q&A only — no file edits |
90
97
 
91
- Shell type is **detected at launch** (Cursor terminal settings, Git Bash, or Windows default PowerShell) and injected into every agent turn. Override with `EKZ_AGENT_SHELL=powershell|bash|cmd` if needed.
92
-
93
- When testing HTTP yourself in PowerShell, use `curl.exe -s "http://localhost:3000/..."` bare `curl` is an alias for `Invoke-WebRequest` and will prompt for `Uri:`.
98
+ | Mode | BYOK routing |
99
+ |------|----------------|
100
+ | **agent / plan** | Cursor `@cursor/sdk`; others tool loop (`read_file`, `write_file`, `run_command`) |
101
+ | **ask** | Chat API (Cursor uses lightweight Cursor agent) |
94
102
 
95
- Prefer built-in CLI helpers over manual curls: `ekz doctor`, `ekz health`, `ekz webhook <url>`.
103
+ Optional one-off override: `--api-key crsr_...` in agent/plan mode.
96
104
 
97
105
  ## Commands
98
106
 
99
107
  | Command | Description |
100
108
  |---------|-------------|
101
- | `ekz` / `ekz agent` | Interactive REPL (default mode: agent) |
109
+ | `ekz setup` | First-run wizard language, name, provider or offline |
110
+ | `ekz` / `ekz agent` | Interactive REPL (default: agent mode) |
102
111
  | `ekz --mode plan` | Plan before editing files |
103
112
  | `ekz --mode ask` | BYOK Q&A, no file edits |
113
+ | `ekz --offline` | Doctor/scan answers only — no LLM |
104
114
  | `ekz agent "fix webhook"` | One-shot task with file edits |
105
115
  | `ekz fix` | Full integration audit + auto-fix pass |
106
116
  | `ekz ask "…"` | BYOK Q&A only |
107
117
  | `ekz ask -i` | Interactive ask REPL |
108
- | `ekz providers` | Configure LLM API keys (BYOK) |
118
+ | `ekz providers` | Manage LLM API keys (BYOK) |
109
119
  | `ekz agent --resume` | Continue last session (`.ekz/session.json`) |
110
- | `ekz doctor` | Env + OAuth health (free) |
111
- | `ekz health` | OAuth + rail health (free) |
112
- | `ekz scan` | Lint common mistakes (free) |
113
- | `ekz webhook <url>` | POST sample webhook (free) |
120
+ | `ekz doctor` | Env + credential checks (**free**) |
121
+ | `ekz health` | OAuth + rail health (**free**) |
122
+ | `ekz scan` | Lint common integration mistakes (**free**) |
123
+ | `ekz webhook <url>` | POST sample webhook (**free**) |
114
124
 
115
125
  ### REPL shortcuts
116
126
 
117
- Type `/help` in the REPL for the full list (modes, CLI commands, flags). Also: `/providers`, `/mode agent|plan|ask`, `/doctor`, `/scan`, `/lang pt|en|zh`, `/save`, `/resume`.
127
+ Type `/help` in the REPL for the full list. Also:
128
+
129
+ `/providers` · `/mode agent|plan|ask` · `/doctor` · `/scan` · `/lang pt|en|zh` · `/save` · `/resume`
118
130
 
119
131
  ## Examples
120
132
 
121
133
  ```bash
134
+ # First time
135
+ ekz setup
136
+ ekz
137
+
122
138
  # Interactive — stay in your project and iterate
123
139
  ekz agent
124
140
 
@@ -130,19 +146,55 @@ ekz fix "Ticket webhook returns 401 on x-signature"
130
146
 
131
147
  # Plan before editing
132
148
  ekz --mode plan "Add emis_ref rail to checkout"
133
- # or shorthand:
134
- ekz agent --plan "Add emis_ref rail to checkout"
135
149
 
136
150
  # Ask mode (no local edits)
137
151
  ekz --mode ask "What env vars am I missing for Ticket?"
138
152
 
153
+ # Free checks before touching the agent
154
+ ekz doctor
155
+ ekz scan
156
+ ekz webhook http://localhost:3000/api/webhooks/ekwanza
157
+
139
158
  # Resume yesterday's session
140
159
  ekz agent --resume
141
160
  ```
142
161
 
143
162
  ## Billing
144
163
 
145
- You bring your own keys (BYOK). Usage bills through **your** provider account (Cursor, OpenAI, Anthropic, etc.). Free commands: `doctor`, `health`, `scan`, `webhook`.
164
+ **BYOK only** usage bills through your provider account (Cursor, OpenAI, Anthropic, etc.). Ekz does not resell LLM credits.
165
+
166
+ Free commands (no API key): `doctor`, `health`, `scan`, `webhook`.
167
+
168
+ ## Run from this repo
169
+
170
+ ```bash
171
+ npm run build:cli
172
+
173
+ # Option A — wrapper (no global install)
174
+ ./ekz # Git Bash / macOS / Linux
175
+ .\ekz # PowerShell / cmd
176
+ npx ekz # any shell, from repo root
177
+
178
+ # Option B — npm script
179
+ npm run ekz
180
+ npm run ekz -- agent "fix webhook"
181
+
182
+ # Option C — global command
183
+ npm run setup:cli # build + npm link
184
+ ekz
185
+ ```
186
+
187
+ ### Windows / PowerShell
188
+
189
+ ```powershell
190
+ .\ekz # resolves to ekz.cmd
191
+ npx ekz
192
+ npm run setup:cli # then `ekz` works globally
193
+ ```
194
+
195
+ Shell type is detected at launch and injected into agent turns. Override with `EKZ_AGENT_SHELL=powershell|bash|cmd`.
196
+
197
+ In PowerShell, use `curl.exe` for manual HTTP tests — bare `curl` is an alias for `Invoke-WebRequest`. Prefer `ekz doctor`, `ekz health`, `ekz webhook <url>`.
146
198
 
147
199
  ## Session file
148
200
 
@@ -150,35 +202,42 @@ You bring your own keys (BYOK). Usage bills through **your** provider account (C
150
202
 
151
203
  ## Agent skills
152
204
 
153
- Ekz ships a skill pack under `packages/cli/skills/` (orchestrator + architecture sub-skills). On build, skills sync to `.cursor/skills/` so Cursor picks them up locally.
205
+ 15 skills under `packages/cli/skills/`. On build, they sync to `.cursor/skills/` for local Cursor use.
154
206
 
155
207
  | Skill | Use when |
156
208
  |-------|----------|
157
- | `ekz-connect` | Orchestrator — always loaded |
158
- | `ekz-payment-core-architecture` | Payment primitives, state machines, ledger, idempotency |
159
- | `ekz-ekwanza-provider-adapter` | e-Kwanza auth, config, GPO, EMIS ref, Ticket API |
160
- | `ekz-webhook-normalization` | Callback normalization, x-signature, dedupe, routing |
161
- | `ekz-one-time-product-payments` | Products, orders, invoices, checkout links, fulfillment |
162
- | `ekz-ticket-invite-selling` | Reservations, invite/ticket issuance, QR/code ownership |
163
- | `ekz-subscription-billing` | Recurring one-time payment requests, grace, entitlements |
164
- | `ekz-overage-billing` | Metered usage, overage charges, settlement |
165
- | `ekz-integration-playbook` | Adapting payment patterns into any existing codebase |
209
+ | `ekz-connect` | Orchestrator — SDK entry points |
210
+ | `ekz-payment-core-architecture` | Primitives, state machines, idempotency |
211
+ | `ekz-ekwanza-provider-adapter` | Auth, config, GPO, EMIS ref, Ticket API |
212
+ | `ekz-webhook-normalization` | Callbacks, x-signature, dedupe, routing |
213
+ | `ekz-one-time-product-payments` | Products, checkout links, fulfillment |
214
+ | `ekz-ticket-invite-selling` | Reservations, invite/ticket issuance |
215
+ | `ekz-subscription-billing` | Recurring payments, grace, entitlements |
216
+ | `ekz-overage-billing` | Metered usage, overage charges |
217
+ | `ekz-integration-playbook` | Adapting patterns into existing codebases |
166
218
  | `ekz-sdk-cli` | `@ekzs/connect`, `ekz doctor`, CLI |
219
+ | `ekz-data-layer-design` | Store abstraction choices |
220
+ | `ekz-data-postgres` | Supabase / Postgres |
221
+ | `ekz-data-mysql` | MySQL |
222
+ | `ekz-data-sqlite` | SQLite |
223
+ | `ekz-data-mongo` | MongoDB |
167
224
 
168
225
  ```bash
169
- npm run sync:skills --prefix packages/cli # copy to .cursor/skills/
226
+ npm run sync:skills --prefix packages/cli
170
227
  ```
171
228
 
172
- The local agent loads project skills via `settingSources: ["project"]` and injects task-relevant skills into the prompt.
229
+ ## Language
173
230
 
174
- ### Language
231
+ Default replies: **Portuguese (Angola)**. Switch in the REPL with `/lang pt`, `/lang en`, `/lang zh`, or start with `ekz --lang en`.
175
232
 
176
- Default replies are in **Portuguese (Angola)**. Switch anytime in the REPL:
233
+ ## Publish (maintainers)
177
234
 
235
+ ```bash
236
+ npm run build:cli
237
+ npm run publish:cli
178
238
  ```
179
- /lang pt
180
- /lang en
181
- /ajuda
182
- ```
183
239
 
184
- Or set `EKZ_LANG=pt` in `.env.local`, or start with `./ekz --lang en`.
240
+ ## Related
241
+
242
+ - [`@ekzs/connect`](../sdk/README.md) — TypeScript SDK for v2.4
243
+ - [Full report](../../docs/EKZ-CONNECT-FULL-REPORT.md) — product + agent reference
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import { runScan } from "./commands/scan.js";
13
13
  import { runProvidersCommand } from "./commands/providers.js";
14
14
  import { runSetupCommand } from "./commands/setup.js";
15
15
  import { runWebhookTest } from "./commands/webhook.js";
16
+ import { packageVersion } from "./lib/version.js";
16
17
  const SUBCOMMANDS = new Set([
17
18
  "doctor",
18
19
  "health",
@@ -40,7 +41,7 @@ const program = new Command();
40
41
  program
41
42
  .name("ekz")
42
43
  .description("EKZ CONNECT — e-Kwanza v2.4 coding agent by Alberto Moisés")
43
- .version("0.3.0");
44
+ .version(packageVersion());
44
45
  program
45
46
  .command("doctor")
46
47
  .description("Validate .env and run live credential checks (free)")
@@ -5,7 +5,7 @@ import { parseLocale } from "./locale.js";
5
5
  import { formatSetupRequiredMessage } from "./setup-messages.js";
6
6
  import { info, ok } from "./output.js";
7
7
  import { ProviderSetupError, resolveActiveCredentials, resolveCredentialsWithOverride, } from "./providers/credentials.js";
8
- import { runProvidersInteractive } from "./providers/ui.js";
8
+ import { runSetupProviderWizard } from "./providers/ui.js";
9
9
  import { c, muted } from "./theme.js";
10
10
  function t(locale, pt, en, zh) {
11
11
  if (locale === "pt")
@@ -80,8 +80,8 @@ async function providerStep(locale) {
80
80
  return "offline";
81
81
  }
82
82
  if (choice === "1" || choice === "p" || choice === "providers" || choice === "provedores" || choice === "供应商") {
83
- await runProvidersInteractive(locale);
84
- if (resolveActiveCredentials()) {
83
+ const configured = await runSetupProviderWizard(locale);
84
+ if (configured && resolveActiveCredentials()) {
85
85
  clearGlobalOffline();
86
86
  ok(t(locale, "Provedor configurado.", "Provider configured.", "Provider 已配置。"));
87
87
  return "configured";
@@ -5,11 +5,16 @@ export type ProviderDefinition = {
5
5
  name: string;
6
6
  kind: ProviderKind;
7
7
  description: string;
8
+ /** Default model when user presses Enter */
8
9
  defaultModel: string;
10
+ /** Predefined API base — never shown in UI */
9
11
  defaultBaseUrl?: string;
12
+ /** Selectable models in setup / providers menu */
13
+ models: string[];
10
14
  keyHint: string;
11
15
  docsUrl: string;
12
16
  };
13
17
  export declare const PROVIDER_CATALOG: ProviderDefinition[];
14
18
  export declare function getProviderDefinition(id: string): ProviderDefinition | undefined;
19
+ export declare function resolveProviderModel(def: ProviderDefinition, storedModel?: string): string;
15
20
  //# sourceMappingURL=catalog.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,mBAAmB,GAAG,WAAW,CAAC;AAExE,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,UAAU,GACV,MAAM,GACN,MAAM,GACN,SAAS,GACT,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,UAAU,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,kBAAkB,EAyFhD,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAEhF"}
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/catalog.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,mBAAmB,GAAG,WAAW,CAAC;AAExE,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,UAAU,GACV,MAAM,GACN,MAAM,GACN,SAAS,GACT,QAAQ,GACR,QAAQ,CAAC;AAEb,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,UAAU,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,YAAY,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,8CAA8C;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,kDAAkD;IAClD,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,kBAAkB,EAkHhD,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAEhF;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,kBAAkB,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAK1F"}
@@ -5,6 +5,19 @@ export const PROVIDER_CATALOG = [
5
5
  kind: "cursor",
6
6
  description: "Composer local agent (@cursor/sdk)",
7
7
  defaultModel: "composer-2.5-fast",
8
+ models: [
9
+ "composer-2.5-fast",
10
+ "composer-2-fast",
11
+ "composer-2.5",
12
+ "composer-2",
13
+ "claude-4.6-sonnet-medium-thinking",
14
+ "claude-opus-4-7-thinking-xhigh",
15
+ "gemini-3-flash",
16
+ "gemini-3.1-pro",
17
+ "gpt-5.3-codex",
18
+ "gpt-5.5-medium",
19
+ "kimi-k2.5",
20
+ ],
8
21
  keyHint: "crsr_…",
9
22
  docsUrl: "https://cursor.com/dashboard/integrations",
10
23
  },
@@ -15,6 +28,7 @@ export const PROVIDER_CATALOG = [
15
28
  description: "GPT-4o, o-series",
16
29
  defaultModel: "gpt-4o",
17
30
  defaultBaseUrl: "https://api.openai.com/v1",
31
+ models: ["gpt-4o", "gpt-4o-mini", "gpt-4.1", "gpt-4.1-mini", "o1", "o1-mini", "o3-mini"],
18
32
  keyHint: "sk-…",
19
33
  docsUrl: "https://platform.openai.com/api-keys",
20
34
  },
@@ -24,6 +38,11 @@ export const PROVIDER_CATALOG = [
24
38
  kind: "anthropic",
25
39
  description: "Claude Sonnet / Opus",
26
40
  defaultModel: "claude-sonnet-4-20250514",
41
+ models: [
42
+ "claude-sonnet-4-20250514",
43
+ "claude-opus-4-20250514",
44
+ "claude-3-5-haiku-20241022",
45
+ ],
27
46
  keyHint: "sk-ant-…",
28
47
  docsUrl: "https://console.anthropic.com/settings/keys",
29
48
  },
@@ -34,6 +53,7 @@ export const PROVIDER_CATALOG = [
34
53
  description: "DeepSeek Chat / Reasoner",
35
54
  defaultModel: "deepseek-chat",
36
55
  defaultBaseUrl: "https://api.deepseek.com",
56
+ models: ["deepseek-chat", "deepseek-reasoner"],
37
57
  keyHint: "sk-…",
38
58
  docsUrl: "https://platform.deepseek.com/api_keys",
39
59
  },
@@ -44,6 +64,7 @@ export const PROVIDER_CATALOG = [
44
64
  description: "Moonshot AI",
45
65
  defaultModel: "moonshot-v1-8k",
46
66
  defaultBaseUrl: "https://api.moonshot.cn/v1",
67
+ models: ["moonshot-v1-8k", "moonshot-v1-32k", "moonshot-v1-128k"],
47
68
  keyHint: "sk-…",
48
69
  docsUrl: "https://platform.moonshot.cn/console/api-keys",
49
70
  },
@@ -54,6 +75,7 @@ export const PROVIDER_CATALOG = [
54
75
  description: "Fast inference",
55
76
  defaultModel: "llama-3.3-70b-versatile",
56
77
  defaultBaseUrl: "https://api.groq.com/openai/v1",
78
+ models: ["llama-3.3-70b-versatile", "llama-3.1-8b-instant", "mixtral-8x7b-32768"],
57
79
  keyHint: "gsk_…",
58
80
  docsUrl: "https://console.groq.com/keys",
59
81
  },
@@ -64,6 +86,7 @@ export const PROVIDER_CATALOG = [
64
86
  description: "Mistral Large",
65
87
  defaultModel: "mistral-large-latest",
66
88
  defaultBaseUrl: "https://api.mistral.ai/v1",
89
+ models: ["mistral-large-latest", "mistral-small-latest", "codestral-latest"],
67
90
  keyHint: "…",
68
91
  docsUrl: "https://console.mistral.ai/api-keys",
69
92
  },
@@ -71,9 +94,10 @@ export const PROVIDER_CATALOG = [
71
94
  id: "google",
72
95
  name: "Google Gemini",
73
96
  kind: "openai-compatible",
74
- description: "Gemini via OpenAI-compatible endpoint",
97
+ description: "Gemini models",
75
98
  defaultModel: "gemini-2.0-flash",
76
99
  defaultBaseUrl: "https://generativelanguage.googleapis.com/v1beta/openai",
100
+ models: ["gemini-2.0-flash", "gemini-2.0-flash-lite", "gemini-1.5-pro", "gemini-1.5-flash"],
77
101
  keyHint: "AI…",
78
102
  docsUrl: "https://aistudio.google.com/apikey",
79
103
  },
@@ -84,6 +108,7 @@ export const PROVIDER_CATALOG = [
84
108
  description: "Local models",
85
109
  defaultModel: "llama3.2",
86
110
  defaultBaseUrl: "http://127.0.0.1:11434/v1",
111
+ models: ["llama3.2", "llama3.1", "qwen2.5", "mistral", "deepseek-r1"],
87
112
  keyHint: "ollama (optional)",
88
113
  docsUrl: "https://ollama.com",
89
114
  },
@@ -91,3 +116,11 @@ export const PROVIDER_CATALOG = [
91
116
  export function getProviderDefinition(id) {
92
117
  return PROVIDER_CATALOG.find((p) => p.id === id);
93
118
  }
119
+ export function resolveProviderModel(def, storedModel) {
120
+ const trimmed = storedModel?.trim();
121
+ if (trimmed && def.models.includes(trimmed))
122
+ return trimmed;
123
+ if (trimmed && !def.models.includes(trimmed))
124
+ return trimmed;
125
+ return def.defaultModel;
126
+ }
@@ -3,6 +3,13 @@ export declare class CursorSdkMissingError extends Error {
3
3
  constructor(locale?: EkzLocale);
4
4
  }
5
5
  type CursorSdkModule = typeof import("@cursor/sdk");
6
+ export declare function clearCursorSdkCache(): void;
7
+ export declare function probeCursorSdk(): Promise<boolean>;
6
8
  export declare function loadCursorSdk(locale?: EkzLocale): Promise<CursorSdkModule>;
9
+ /**
10
+ * Before Cursor API key entry: ensure @cursor/sdk is installed.
11
+ * Offers to run `npm install -g @cursor/sdk` when missing.
12
+ */
13
+ export declare function ensureCursorSdkReady(locale: EkzLocale): Promise<boolean>;
7
14
  export type { SDKAgent } from "@cursor/sdk";
8
15
  //# sourceMappingURL=cursor-sdk.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"cursor-sdk.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/cursor-sdk.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAI9C,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,MAAM,GAAE,SAAgB;CAWrC;AAED,KAAK,eAAe,GAAG,cAAc,aAAa,CAAC,CAAC;AAoBpD,wBAAgB,mBAAmB,SAElC;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,CAAC,CAOvD;AAED,wBAAsB,aAAa,CAAC,MAAM,GAAE,SAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAQtF;AAcD;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAwE9E;AAED,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC"}
@@ -1,4 +1,8 @@
1
+ import { spawn } from "child_process";
2
+ import readline from "readline";
1
3
  import { providersCommandLabels } from "../commands-i18n.js";
4
+ import { info, ok, warn } from "../output.js";
5
+ import { muted } from "../theme.js";
2
6
  export class CursorSdkMissingError extends Error {
3
7
  constructor(locale = "en") {
4
8
  const p = providersCommandLabels(locale);
@@ -12,6 +16,34 @@ export class CursorSdkMissingError extends Error {
12
16
  }
13
17
  }
14
18
  let cached = null;
19
+ function t(locale, pt, en, zh) {
20
+ if (locale === "pt")
21
+ return pt;
22
+ if (locale === "zh")
23
+ return zh;
24
+ return en;
25
+ }
26
+ async function askLine(prompt) {
27
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
28
+ return new Promise((resolve) => {
29
+ rl.question(prompt, (answer) => {
30
+ rl.close();
31
+ resolve(answer.trim());
32
+ });
33
+ });
34
+ }
35
+ export function clearCursorSdkCache() {
36
+ cached = null;
37
+ }
38
+ export async function probeCursorSdk() {
39
+ try {
40
+ await import("@cursor/sdk");
41
+ return true;
42
+ }
43
+ catch {
44
+ return false;
45
+ }
46
+ }
15
47
  export async function loadCursorSdk(locale = "en") {
16
48
  if (cached)
17
49
  return cached;
@@ -23,3 +55,42 @@ export async function loadCursorSdk(locale = "en") {
23
55
  throw new CursorSdkMissingError(locale);
24
56
  }
25
57
  }
58
+ async function runNpmInstallGlobal(packageName) {
59
+ const npm = process.platform === "win32" ? "npm.cmd" : "npm";
60
+ return new Promise((resolve) => {
61
+ const child = spawn(npm, ["install", "-g", packageName], {
62
+ stdio: "inherit",
63
+ shell: process.platform === "win32",
64
+ });
65
+ child.on("error", () => resolve(false));
66
+ child.on("close", (code) => resolve(code === 0));
67
+ });
68
+ }
69
+ /**
70
+ * Before Cursor API key entry: ensure @cursor/sdk is installed.
71
+ * Offers to run `npm install -g @cursor/sdk` when missing.
72
+ */
73
+ export async function ensureCursorSdkReady(locale) {
74
+ clearCursorSdkCache();
75
+ if (await probeCursorSdk()) {
76
+ return true;
77
+ }
78
+ console.log("");
79
+ info(t(locale, "Cursor usa o pacote @cursor/sdk para correr o agente local.", "Cursor uses @cursor/sdk to run the local agent.", "Cursor 使用 @cursor/sdk 运行本地 agent。"));
80
+ console.log(` ${muted("npm install -g @cursor/sdk")}`);
81
+ console.log("");
82
+ const answer = await askLine(t(locale, " Instalar agora? [Y/n]: ", " Install now? [Y/n]: ", " 现在安装? [Y/n]: "));
83
+ if (answer && !["y", "yes", "s", "sim", "是", ""].includes(answer.toLowerCase())) {
84
+ warn(t(locale, "Instalação cancelada — escolhe outro provider ou corre npm install -g @cursor/sdk.", "Install skipped — pick another provider or run npm install -g @cursor/sdk.", "已跳过安装 — 请选择其他 provider 或运行 npm install -g @cursor/sdk。"));
85
+ return false;
86
+ }
87
+ info(t(locale, "A instalar @cursor/sdk…", "Installing @cursor/sdk…", "正在安装 @cursor/sdk…"));
88
+ const installed = await runNpmInstallGlobal("@cursor/sdk");
89
+ clearCursorSdkCache();
90
+ if (!installed || !(await probeCursorSdk())) {
91
+ warn(t(locale, "Instalação falhou. Tenta manualmente: npm install -g @cursor/sdk", "Install failed. Try manually: npm install -g @cursor/sdk", "安装失败。请手动运行: npm install -g @cursor/sdk"));
92
+ return false;
93
+ }
94
+ ok(t(locale, "@cursor/sdk instalado.", "@cursor/sdk installed.", "@cursor/sdk 已安装。"));
95
+ return true;
96
+ }
@@ -17,5 +17,5 @@ export declare function setStoredProvider(id: ProviderId, entry: StoredProvider)
17
17
  export declare function removeStoredProvider(id: ProviderId): void;
18
18
  export declare function setActiveProvider(id: ProviderId): void;
19
19
  export declare function resolveModel(id: ProviderId, stored?: StoredProvider): string;
20
- export declare function resolveBaseUrl(id: ProviderId, stored?: StoredProvider): string | undefined;
20
+ export declare function resolveBaseUrl(id: ProviderId, _stored?: StoredProvider): string | undefined;
21
21
  //# sourceMappingURL=store.d.ts.map
@@ -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;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"}
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,QAQtE;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,CAM5E;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,MAAM,GAAG,SAAS,CAE3F"}
@@ -50,9 +50,9 @@ export function getStoredProvider(id) {
50
50
  }
51
51
  export function setStoredProvider(id, entry) {
52
52
  const file = loadProvidersFile();
53
- file.providers[id] = entry;
54
- if (!file.active)
55
- file.active = id;
53
+ const { baseUrl: _ignored, ...rest } = entry;
54
+ file.providers[id] = rest;
55
+ file.active = id;
56
56
  saveProvidersFile(file);
57
57
  clearGlobalOffline();
58
58
  markSetupComplete();
@@ -75,10 +75,13 @@ export function setActiveProvider(id) {
75
75
  }
76
76
  export function resolveModel(id, stored) {
77
77
  const def = getProviderDefinition(id);
78
- return stored?.model?.trim() || def?.defaultModel || "gpt-4o";
78
+ if (!def)
79
+ return stored?.model?.trim() || "gpt-4o";
80
+ const trimmed = stored?.model?.trim();
81
+ if (trimmed)
82
+ return trimmed;
83
+ return def.defaultModel;
79
84
  }
80
- export function resolveBaseUrl(id, stored) {
81
- if (stored?.baseUrl?.trim())
82
- return stored.baseUrl.trim();
85
+ export function resolveBaseUrl(id, _stored) {
83
86
  return getProviderDefinition(id)?.defaultBaseUrl;
84
87
  }
@@ -1,4 +1,6 @@
1
1
  import { type EkzLocale } from "../locale.js";
2
+ /** One-shot provider pick + configure — used by `ekz setup`. */
3
+ export declare function runSetupProviderWizard(locale?: EkzLocale): Promise<boolean>;
2
4
  export declare function runProvidersInteractive(locale?: EkzLocale): Promise<void>;
3
5
  export declare function formatActiveProviderLabel(locale: EkzLocale): string;
4
6
  //# sourceMappingURL=ui.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/ui.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AA+L9C,wBAAsB,uBAAuB,CAAC,MAAM,GAAE,SAAgB,iBAuDrE;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAMnE"}
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../../src/lib/providers/ui.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AA2N9C,gEAAgE;AAChE,wBAAsB,sBAAsB,CAAC,MAAM,GAAE,SAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAoBvF;AAED,wBAAsB,uBAAuB,CAAC,MAAM,GAAE,SAAgB,iBAuDrE;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,GAAG,MAAM,CAMnE"}
@@ -5,6 +5,7 @@ import { askWithPlaceholder, formatInputPrompt } from "../ui/prompt.js";
5
5
  import { PROVIDER_CATALOG, getProviderDefinition } from "./catalog.js";
6
6
  import { listProviderRows, resolveActiveCredentials, } from "./credentials.js";
7
7
  import { maskApiKey, providersFilePath, removeStoredProvider, setActiveProvider, setStoredProvider, } from "./store.js";
8
+ import { ensureCursorSdkReady } from "./cursor-sdk.js";
8
9
  function providersTitle(locale) {
9
10
  if (locale === "pt")
10
11
  return "Provedores (BYOK)";
@@ -88,41 +89,67 @@ function printProviderList(rows, locale) {
88
89
  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
90
  console.log("");
90
91
  }
92
+ function printCatalogPicker(locale) {
93
+ console.log("");
94
+ console.log(` ${c.bold}${t(locale, "Escolhe o provedor", "Choose provider", "选择 provider")}${c.reset}`);
95
+ console.log("");
96
+ PROVIDER_CATALOG.forEach((def, index) => {
97
+ console.log(` ${c.bold}${String(index + 1).padStart(2)}.${c.reset} ${def.name.padEnd(16)} ${muted(def.description)}`);
98
+ });
99
+ console.log("");
100
+ }
101
+ async function pickModel(id, locale) {
102
+ const def = getProviderDefinition(id);
103
+ if (!def)
104
+ return null;
105
+ const defaultIndex = Math.max(0, def.models.findIndex((m) => m === def.defaultModel));
106
+ const defaultNum = defaultIndex + 1;
107
+ console.log("");
108
+ console.log(` ${c.bold}${t(locale, "Modelo", "Model", "模型")}${c.reset}`);
109
+ def.models.forEach((model, index) => {
110
+ const n = index + 1;
111
+ const tag = model === def.defaultModel
112
+ ? muted(t(locale, " · predefinição", " · default", " · 默认"))
113
+ : "";
114
+ console.log(` ${c.bold}${String(n).padStart(2)}.${c.reset} ${model}${tag}`);
115
+ });
116
+ console.log("");
117
+ const prompt = t(locale, ` Escolha [${defaultNum}]: `, ` Choice [${defaultNum}]: `, ` 选择 [${defaultNum}]: `);
118
+ const choice = await askLine(prompt);
119
+ if (!choice)
120
+ return def.defaultModel;
121
+ const num = Number(choice);
122
+ if (Number.isInteger(num) && num >= 1 && num <= def.models.length) {
123
+ return def.models[num - 1];
124
+ }
125
+ warn(t(locale, "Opção inválida — a usar predefinição.", "Invalid choice — using default.", "无效选项 — 使用默认。"));
126
+ return def.defaultModel;
127
+ }
91
128
  async function configureProvider(id, locale) {
92
129
  const def = getProviderDefinition(id);
93
130
  if (!def)
94
- return;
131
+ return false;
95
132
  console.log("");
96
133
  console.log(` ${c.bold}${def.name}${c.reset} — ${muted(def.description)}`);
97
134
  console.log(` ${muted(def.docsUrl)}`);
98
135
  console.log("");
136
+ if (id === "cursor") {
137
+ const sdkReady = await ensureCursorSdkReady(locale);
138
+ if (!sdkReady)
139
+ return false;
140
+ }
99
141
  const keyPrompt = t(locale, `Cola a API key (${def.keyHint}): `, `Paste API key (${def.keyHint}): `, `粘贴 API key (${def.keyHint}): `);
100
142
  const apiKey = await askLine(keyPrompt, true);
101
143
  if (!apiKey) {
102
144
  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
- }
145
+ return false;
125
146
  }
147
+ const model = await pickModel(id, locale);
148
+ if (!model)
149
+ return false;
150
+ setStoredProvider(id, { apiKey, model });
151
+ ok(t(locale, `Guardado ${def.name} · ${model} (${maskApiKey(apiKey)})`, `Saved ${def.name} · ${model} (${maskApiKey(apiKey)})`, `已保存 ${def.name} · ${model} (${maskApiKey(apiKey)})`));
152
+ return true;
126
153
  }
127
154
  function resolveRowTarget(rows, target) {
128
155
  const trimmed = target.trim().toLowerCase();
@@ -132,6 +159,21 @@ function resolveRowTarget(rows, target) {
132
159
  }
133
160
  return rows.find((r) => r.definition.id === trimmed);
134
161
  }
162
+ /** One-shot provider pick + configure — used by `ekz setup`. */
163
+ export async function runSetupProviderWizard(locale = "pt") {
164
+ printCatalogPicker(locale);
165
+ const prompt = t(locale, " Número do provedor (Enter cancelar): ", " Provider number (Enter to cancel): ", " Provider 编号 (Enter 取消): ");
166
+ const choice = (await askLine(prompt)).trim();
167
+ if (!choice)
168
+ return false;
169
+ const num = Number(choice);
170
+ if (!Number.isInteger(num) || num < 1 || num > PROVIDER_CATALOG.length) {
171
+ warn(t(locale, "Opção inválida.", "Invalid option.", "无效选项。"));
172
+ return false;
173
+ }
174
+ const def = PROVIDER_CATALOG[num - 1];
175
+ return configureProvider(def.id, locale);
176
+ }
135
177
  export async function runProvidersInteractive(locale = "pt") {
136
178
  while (true) {
137
179
  const rows = listProviderRows();
@@ -184,5 +226,5 @@ export function formatActiveProviderLabel(locale) {
184
226
  if (!creds) {
185
227
  return t(locale, "sem provider", "no provider", "未配置");
186
228
  }
187
- return `${creds.definition.name} · ${creds.maskedKey}`;
229
+ return `${creds.definition.name} · ${creds.model} · ${creds.maskedKey}`;
188
230
  }
@@ -0,0 +1,2 @@
1
+ export declare function packageVersion(): string;
2
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/lib/version.ts"],"names":[],"mappings":"AAMA,wBAAgB,cAAc,IAAI,MAAM,CASvC"}
@@ -0,0 +1,13 @@
1
+ import { readFileSync } from "fs";
2
+ import { dirname, join } from "path";
3
+ import { fileURLToPath } from "url";
4
+ const here = dirname(fileURLToPath(import.meta.url));
5
+ export function packageVersion() {
6
+ try {
7
+ const pkg = JSON.parse(readFileSync(join(here, "../../package.json"), "utf8"));
8
+ return pkg.version ?? "0.0.0";
9
+ }
10
+ catch {
11
+ return "0.0.0";
12
+ }
13
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ekzs/cli",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
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": {