@ekzs/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -11
- package/dist/commands/agent.d.ts +0 -4
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +0 -6
- package/dist/commands/ask.d.ts +3 -5
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +36 -35
- package/dist/commands/local-agent.d.ts +0 -2
- package/dist/commands/local-agent.d.ts.map +1 -1
- package/dist/commands/local-agent.js +200 -125
- package/dist/commands/providers.d.ts +4 -0
- package/dist/commands/providers.d.ts.map +1 -0
- package/dist/commands/providers.js +6 -0
- package/dist/index.js +25 -20
- package/dist/lib/commands-i18n.d.ts +12 -1
- package/dist/lib/commands-i18n.d.ts.map +1 -1
- package/dist/lib/commands-i18n.js +69 -14
- package/dist/lib/composer-model.d.ts +1 -1
- package/dist/lib/composer-model.d.ts.map +1 -1
- package/dist/lib/composer-model.js +2 -2
- package/dist/lib/help.js +13 -12
- package/dist/lib/providers/agent-runner.d.ts +12 -0
- package/dist/lib/providers/agent-runner.d.ts.map +1 -0
- package/dist/lib/providers/agent-runner.js +167 -0
- package/dist/lib/providers/catalog.d.ts +15 -0
- package/dist/lib/providers/catalog.d.ts.map +1 -0
- package/dist/lib/providers/catalog.js +93 -0
- package/dist/lib/providers/chat.d.ts +10 -0
- package/dist/lib/providers/chat.d.ts.map +1 -0
- package/dist/lib/providers/chat.js +121 -0
- package/dist/lib/providers/credentials.d.ts +26 -0
- package/dist/lib/providers/credentials.d.ts.map +1 -0
- package/dist/lib/providers/credentials.js +54 -0
- package/dist/lib/providers/cursor-runner.d.ts +13 -0
- package/dist/lib/providers/cursor-runner.d.ts.map +1 -0
- package/dist/lib/providers/cursor-runner.js +87 -0
- package/dist/lib/providers/store.d.ts +21 -0
- package/dist/lib/providers/store.d.ts.map +1 -0
- package/dist/lib/providers/store.js +81 -0
- package/dist/lib/providers/tools.d.ts +80 -0
- package/dist/lib/providers/tools.d.ts.map +1 -0
- package/dist/lib/providers/tools.js +145 -0
- package/dist/lib/providers/ui.d.ts +4 -0
- package/dist/lib/providers/ui.d.ts.map +1 -0
- package/dist/lib/providers/ui.js +188 -0
- package/package.json +1 -1
- package/skills/ekz-sdk-cli/SKILL.md +6 -4
package/README.md
CHANGED
|
@@ -12,15 +12,26 @@ npm install -g @ekzs/cli
|
|
|
12
12
|
npm run build --prefix packages/cli
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
## Setup
|
|
15
|
+
## Setup (BYOK)
|
|
16
|
+
|
|
17
|
+
Configure your LLM API keys once — stored in `~/.ekz/providers.json` (not in `.env`):
|
|
16
18
|
|
|
17
19
|
```bash
|
|
18
|
-
|
|
19
|
-
# or
|
|
20
|
-
export EKZ_AGENT_MODEL=composer-2.5-fast # optional; maps to fast param
|
|
20
|
+
ekz providers
|
|
21
|
+
# or inside the REPL: /providers
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
Supported providers: **Cursor**, **OpenAI**, **Anthropic**, **DeepSeek**, **Kimi**, **Groq**, **Mistral**, **Google Gemini**, **Ollama**.
|
|
25
|
+
|
|
26
|
+
| Mode | BYOK behavior |
|
|
27
|
+
|------|----------------|
|
|
28
|
+
| `agent` | Cursor → local SDK; others → tool loop (read/write/run) |
|
|
29
|
+
| `plan` | Same, plan-first instructions |
|
|
30
|
+
| `ask` | Chat only (Cursor uses lightweight Cursor agent) |
|
|
31
|
+
|
|
32
|
+
**Localized commands:** `ekz provedores` / `/provedores` (PT) · `ekz 供应商` / `/供应商` (ZH)
|
|
33
|
+
|
|
34
|
+
Optional: `--api-key crsr_...` for a one-off Cursor override in agent/plan mode.
|
|
24
35
|
|
|
25
36
|
### Run from this repo
|
|
26
37
|
|
|
@@ -63,11 +74,12 @@ Prefer built-in CLI helpers over manual curls: `ekz doctor`, `ekz health`, `ekz
|
|
|
63
74
|
|---------|-------------|
|
|
64
75
|
| `ekz` / `ekz agent` | Interactive REPL (default mode: agent) |
|
|
65
76
|
| `ekz --mode plan` | Plan before editing files |
|
|
66
|
-
| `ekz --mode ask` |
|
|
77
|
+
| `ekz --mode ask` | BYOK Q&A, no file edits |
|
|
67
78
|
| `ekz agent "fix webhook"` | One-shot task with file edits |
|
|
68
79
|
| `ekz fix` | Full integration audit + auto-fix pass |
|
|
69
|
-
| `ekz ask "…"` |
|
|
80
|
+
| `ekz ask "…"` | BYOK Q&A only |
|
|
70
81
|
| `ekz ask -i` | Interactive ask REPL |
|
|
82
|
+
| `ekz providers` | Configure LLM API keys (BYOK) |
|
|
71
83
|
| `ekz agent --resume` | Continue last session (`.ekz/session.json`) |
|
|
72
84
|
| `ekz doctor` | Env + OAuth health (free) |
|
|
73
85
|
| `ekz health` | OAuth + rail health (free) |
|
|
@@ -76,7 +88,7 @@ Prefer built-in CLI helpers over manual curls: `ekz doctor`, `ekz health`, `ekz
|
|
|
76
88
|
|
|
77
89
|
### REPL shortcuts
|
|
78
90
|
|
|
79
|
-
Type `/help` in the REPL for the full list (modes, CLI commands, flags). Also: `/mode agent|plan|ask`, `/doctor`, `/scan`, `/lang pt|en|zh`, `/save`, `/resume`.
|
|
91
|
+
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`.
|
|
80
92
|
|
|
81
93
|
## Examples
|
|
82
94
|
|
|
@@ -104,9 +116,7 @@ ekz agent --resume
|
|
|
104
116
|
|
|
105
117
|
## Billing
|
|
106
118
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
Remote `ekz ask` uses **EKZ_AGENT_API_KEY** credits on Ekz Connect.
|
|
119
|
+
You bring your own keys (BYOK). Usage bills through **your** provider account (Cursor, OpenAI, Anthropic, etc.). Free commands: `doctor`, `health`, `scan`, `webhook`.
|
|
110
120
|
|
|
111
121
|
## Session file
|
|
112
122
|
|
package/dist/commands/agent.d.ts
CHANGED
|
@@ -4,8 +4,6 @@ export declare function runAgentCommand(opts: {
|
|
|
4
4
|
cwd: string;
|
|
5
5
|
question?: string;
|
|
6
6
|
apiKey?: string;
|
|
7
|
-
askApiKey?: string;
|
|
8
|
-
askApiUrl?: string;
|
|
9
7
|
askOffline?: boolean;
|
|
10
8
|
resume?: boolean;
|
|
11
9
|
fresh?: boolean;
|
|
@@ -17,8 +15,6 @@ export declare function runAgentCommand(opts: {
|
|
|
17
15
|
export declare function runFixCommand(opts: {
|
|
18
16
|
cwd: string;
|
|
19
17
|
apiKey?: string;
|
|
20
|
-
askApiKey?: string;
|
|
21
|
-
askApiUrl?: string;
|
|
22
18
|
askOffline?: boolean;
|
|
23
19
|
resume?: boolean;
|
|
24
20
|
fresh?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK3C,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/commands/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK3C,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,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,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,iBA6BA;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,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,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,iBAiBA"}
|
package/dist/commands/agent.js
CHANGED
|
@@ -8,8 +8,6 @@ export async function runAgentCommand(opts) {
|
|
|
8
8
|
await runInteractiveAgent({
|
|
9
9
|
cwd: opts.cwd,
|
|
10
10
|
apiKey: opts.apiKey,
|
|
11
|
-
askApiKey: opts.askApiKey,
|
|
12
|
-
askApiUrl: opts.askApiUrl,
|
|
13
11
|
askOffline: opts.askOffline,
|
|
14
12
|
resume: opts.resume,
|
|
15
13
|
fresh: opts.fresh,
|
|
@@ -23,8 +21,6 @@ export async function runAgentCommand(opts) {
|
|
|
23
21
|
cwd: opts.cwd,
|
|
24
22
|
task: opts.question.trim(),
|
|
25
23
|
apiKey: opts.apiKey,
|
|
26
|
-
askApiKey: opts.askApiKey,
|
|
27
|
-
askApiUrl: opts.askApiUrl,
|
|
28
24
|
askOffline: opts.askOffline,
|
|
29
25
|
resume: opts.resume,
|
|
30
26
|
fresh: opts.fresh,
|
|
@@ -42,8 +38,6 @@ export async function runFixCommand(opts) {
|
|
|
42
38
|
cwd: opts.cwd,
|
|
43
39
|
task,
|
|
44
40
|
apiKey: opts.apiKey,
|
|
45
|
-
askApiKey: opts.askApiKey,
|
|
46
|
-
askApiUrl: opts.askApiUrl,
|
|
47
41
|
askOffline: opts.askOffline,
|
|
48
42
|
resume: opts.resume,
|
|
49
43
|
fresh: opts.fresh,
|
package/dist/commands/ask.d.ts
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
+
import { type EkzLocale } from "../lib/locale.js";
|
|
1
2
|
export type AskOptions = {
|
|
2
3
|
cwd: string;
|
|
3
4
|
question: string;
|
|
4
|
-
apiKey?: string;
|
|
5
|
-
apiUrl?: string;
|
|
6
5
|
offline?: boolean;
|
|
7
6
|
quiet?: boolean;
|
|
7
|
+
locale?: EkzLocale;
|
|
8
8
|
};
|
|
9
|
-
/**
|
|
9
|
+
/** Q&A via BYOK provider (no local file edits). */
|
|
10
10
|
export declare function executeAskTurn(options: AskOptions): Promise<boolean>;
|
|
11
11
|
export declare function runAsk(options: Omit<AskOptions, "quiet">): Promise<void>;
|
|
12
12
|
export declare function runInteractiveAsk(opts: {
|
|
13
13
|
cwd: string;
|
|
14
14
|
locale?: string;
|
|
15
|
-
apiKey?: string;
|
|
16
|
-
apiUrl?: string;
|
|
17
15
|
offline?: boolean;
|
|
18
16
|
}): Promise<void>;
|
|
19
17
|
export declare function resolveQuestion(arg?: string): Promise<string | undefined>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../src/commands/ask.ts"],"names":[],"mappings":"
|
|
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,iBA2DA;AAED,wBAAsB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAM/E"}
|
package/dist/commands/ask.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { printHelp, printWelcome } from "../lib/banner.js";
|
|
2
|
-
import { resolveReplMetaCommand } from "../lib/commands-i18n.js";
|
|
2
|
+
import { isProvidersMetaCommand, providersCommandLabels, resolveReplMetaCommand } from "../lib/commands-i18n.js";
|
|
3
3
|
import { buildBootstrapContext } from "../lib/context.js";
|
|
4
4
|
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 { executeByokAskTurn } from "../lib/providers/chat.js";
|
|
9
|
+
import { executeCursorAskTurn } from "../lib/providers/cursor-runner.js";
|
|
10
|
+
import { isCursorProvider, requireActiveCredentials, ProviderSetupError, } from "../lib/providers/credentials.js";
|
|
11
|
+
import { runProvidersInteractive } from "../lib/providers/ui.js";
|
|
8
12
|
import { redactText } from "../lib/redact.js";
|
|
9
13
|
import { askWithPlaceholder, formatInputPrompt } from "../lib/ui/prompt.js";
|
|
10
14
|
import { inputPlaceholder } from "../lib/ui/splash.js";
|
|
11
|
-
function deterministicAnswer(question, ctx) {
|
|
15
|
+
function deterministicAnswer(question, ctx, locale) {
|
|
16
|
+
const p = providersCommandLabels(locale);
|
|
12
17
|
const lines = ["## Ekz assistant (offline)", "", question, ""];
|
|
13
18
|
if (ctx.doctor.issues.length) {
|
|
14
19
|
lines.push("### Issues", ...ctx.doctor.issues.map((i) => `- ${i}`), "");
|
|
@@ -16,48 +21,41 @@ function deterministicAnswer(question, ctx) {
|
|
|
16
21
|
if (ctx.scan.length) {
|
|
17
22
|
lines.push("### Scan", ...ctx.scan.slice(0, 8).map((f) => `- \`${f.file}:${f.line}\` · ${f.message}`), "");
|
|
18
23
|
}
|
|
19
|
-
lines.push("### Next steps",
|
|
24
|
+
lines.push("### Next steps", `- Run \`${p.cli}\` or \`${p.repl}\` and paste your LLM API key (BYOK)`, "- Or run `ekz --mode agent` with your provider configured");
|
|
20
25
|
return lines.join("\n");
|
|
21
26
|
}
|
|
22
|
-
/**
|
|
27
|
+
/** Q&A via BYOK provider (no local file edits). */
|
|
23
28
|
export async function executeAskTurn(options) {
|
|
24
|
-
const { cwd, question,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const baseUrl = apiUrl ?? process.env.EKZ_AGENT_API_URL ?? process.env.EKZ_CONNECT_URL ?? "http://localhost:3000";
|
|
28
|
-
if (offline || !key) {
|
|
29
|
+
const { cwd, question, offline, quiet, locale = "pt" } = options;
|
|
30
|
+
if (offline) {
|
|
31
|
+
const ctx = await buildBootstrapContext(cwd);
|
|
29
32
|
if (!quiet)
|
|
30
33
|
info("Ask mode (offline) · no file edits.\n");
|
|
31
|
-
console.log("\n" + deterministicAnswer(question, ctx));
|
|
34
|
+
console.log("\n" + deterministicAnswer(question, ctx, locale));
|
|
32
35
|
return true;
|
|
33
36
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
});
|
|
45
|
-
const data = (await res.json().catch(() => ({})));
|
|
46
|
-
if (!res.ok) {
|
|
47
|
-
fail(data.error ?? `API error (${res.status})`);
|
|
48
|
-
return false;
|
|
37
|
+
let creds;
|
|
38
|
+
try {
|
|
39
|
+
creds = requireActiveCredentials(locale);
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
if (err instanceof ProviderSetupError) {
|
|
43
|
+
fail(err.message);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
throw err;
|
|
49
47
|
}
|
|
50
|
-
if (
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
console.log("\n" + (data.answer ?? ""));
|
|
55
|
-
return true;
|
|
48
|
+
if (isCursorProvider(creds)) {
|
|
49
|
+
return executeCursorAskTurn({ cwd, question, creds, locale });
|
|
50
|
+
}
|
|
51
|
+
return executeByokAskTurn({ cwd, question, locale, creds, quiet });
|
|
56
52
|
}
|
|
57
53
|
export async function runAsk(options) {
|
|
54
|
+
const locale = options.locale ?? "pt";
|
|
55
|
+
const p = providersCommandLabels(locale);
|
|
58
56
|
heading("Ekz ask");
|
|
59
|
-
info(
|
|
60
|
-
const okResult = await executeAskTurn({ ...options, quiet: false });
|
|
57
|
+
info(`BYOK Q&A · configure keys with \`${p.cli}\` or \`${p.repl}\`.\n`);
|
|
58
|
+
const okResult = await executeAskTurn({ ...options, quiet: false, locale });
|
|
61
59
|
if (!okResult)
|
|
62
60
|
process.exitCode = 1;
|
|
63
61
|
}
|
|
@@ -109,6 +107,10 @@ export async function runInteractiveAsk(opts) {
|
|
|
109
107
|
})).trim();
|
|
110
108
|
if (!line)
|
|
111
109
|
continue;
|
|
110
|
+
if (isProvidersMetaCommand(line.trim().split(/\s+/)[0] ?? "")) {
|
|
111
|
+
await runProvidersInteractive(currentLocale);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
112
114
|
const meta = handleAskMeta(line, currentLocale);
|
|
113
115
|
if (meta?.kind === "handled")
|
|
114
116
|
continue;
|
|
@@ -132,10 +134,9 @@ export async function runInteractiveAsk(opts) {
|
|
|
132
134
|
await executeAskTurn({
|
|
133
135
|
cwd: opts.cwd,
|
|
134
136
|
question: line,
|
|
135
|
-
apiKey: opts.apiKey,
|
|
136
|
-
apiUrl: opts.apiUrl,
|
|
137
137
|
offline: opts.offline,
|
|
138
138
|
quiet: true,
|
|
139
|
+
locale: currentLocale,
|
|
139
140
|
});
|
|
140
141
|
console.log("");
|
|
141
142
|
turn++;
|
|
@@ -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;
|
|
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;AAkPF,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,iBAuG1D;AAED,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,aAAa,CAAC,iBAwE9F"}
|