@moxxy/cli 0.1.6 → 0.2.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.
@@ -126,6 +126,82 @@ export default definePlugin({
126
126
  });
127
127
  ```
128
128
 
129
+ ### API keys / secrets in a plugin
130
+
131
+ If the plugin needs an API key (e.g. to call a third-party API), **read it at call
132
+ time from the vault via `ctx.getSecret(name)`** — never `process.env`, and never ask
133
+ the user to paste the key into the chat. Tell the user to store it once with
134
+ `/vault set NAME <value>` (or the desktop Secrets UI); the plaintext only ever reaches
135
+ your handler, not the model's context.
136
+
137
+ ```js
138
+ import { definePlugin, defineTool, z } from '@moxxy/sdk';
139
+
140
+ export default definePlugin({
141
+ name: 'elevenlabs',
142
+ version: '0.0.0',
143
+ tools: [
144
+ defineTool({
145
+ name: 'elevenlabs_tts',
146
+ description: 'Synthesize speech from text via ElevenLabs.',
147
+ inputSchema: z.object({ text: z.string() }),
148
+ permission: { action: 'prompt' },
149
+ handler: async ({ text }, ctx) => {
150
+ const key = await ctx.getSecret?.('ELEVENLABS_API_KEY');
151
+ if (!key) throw new Error('Set the key first: /vault set ELEVENLABS_API_KEY <value>');
152
+ const res = await fetch('https://api.elevenlabs.io/v1/text-to-speech/...', {
153
+ method: 'POST',
154
+ headers: { 'xi-api-key': key, 'content-type': 'application/json' },
155
+ body: JSON.stringify({ text }),
156
+ });
157
+ // ... return result
158
+ },
159
+ }),
160
+ ],
161
+ });
162
+ ```
163
+
164
+ ### Text-to-speech (read-aloud) plugins
165
+
166
+ To give the app a new voice (e.g. ElevenLabs) for the "Read aloud" button,
167
+ register a **synthesizer** — `synthesizers: [SynthesizerDef]` — not a tool. The
168
+ `create({ getSecret })` factory reads the API key from the vault the same way;
169
+ return `{ audio: Uint8Array, mimeType }`. A newly registered synthesizer
170
+ **auto-activates**, so read-aloud uses it immediately. The user switches back
171
+ with `set_voice system`, or between voices with `set_voice <name>`.
172
+
173
+ ```js
174
+ import { definePlugin } from '@moxxy/sdk';
175
+
176
+ export default definePlugin({
177
+ name: 'elevenlabs-voice',
178
+ version: '0.0.0',
179
+ synthesizers: [
180
+ {
181
+ name: 'elevenlabs',
182
+ displayName: 'ElevenLabs',
183
+ create: ({ getSecret }) => ({
184
+ name: 'elevenlabs',
185
+ async synthesize(text) {
186
+ const key = await getSecret?.('ELEVENLABS_API_KEY');
187
+ if (!key) throw new Error('Set the key: /vault set ELEVENLABS_API_KEY <value>');
188
+ const res = await fetch(
189
+ 'https://api.elevenlabs.io/v1/text-to-speech/<voiceId>',
190
+ {
191
+ method: 'POST',
192
+ headers: { 'xi-api-key': key, 'content-type': 'application/json' },
193
+ body: JSON.stringify({ text, model_id: 'eleven_multilingual_v2' }),
194
+ },
195
+ );
196
+ if (!res.ok) throw new Error(`ElevenLabs ${res.status}`);
197
+ return { audio: new Uint8Array(await res.arrayBuffer()), mimeType: 'audio/mpeg' };
198
+ },
199
+ }),
200
+ },
201
+ ],
202
+ });
203
+ ```
204
+
129
205
  ## Don't
130
206
 
131
207
  - **Don't skip the transaction.** Always `self_update_begin` before editing so
@@ -31,7 +31,7 @@ Naming: use slug-style names like `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `slack_
31
31
  ## Using a stored secret
32
32
 
33
33
  - In `moxxy.config.ts` / `moxxy.config.yaml`: write `${vault:OPENAI_API_KEY}` anywhere a string is expected. The CLI resolves it on session start.
34
- - For a tool/integration that needs the value at call time: pass the `${vault:NAME}` reference where supported, or let the integration resolve it do not fetch and inline the plaintext.
34
+ - **From plugin/tool code that needs the value at call time:** read it with `const key = await ctx.getSecret('OPENAI_API_KEY')` (the `ToolContext` passed to every tool handler). The plaintext goes straight to your handler — it never enters the model's context or `process.env`. This is the way an authored plugin should consume an API key; do **not** read `process.env`, and do not fetch-and-inline the plaintext anywhere the model can see it.
35
35
 
36
36
  ## Where things live
37
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moxxy/cli",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "description": "moxxy command-line binary. Subcommand dispatcher consuming the moxxy SDK.",
5
5
  "keywords": [
6
6
  "moxxy",