@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.
- package/dist/bin.js +1397 -786
- package/dist/bin.js.map +1 -1
- package/dist/skills/self-update.md +76 -0
- package/dist/skills/vault-setup.md +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
-
|
|
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
|
|