@deepdub/agent-plugin 0.1.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/.agents/plugins/marketplace.json +20 -0
- package/.agents/skills/deepdub-tts/SKILL.md +10 -0
- package/.claude-plugin/marketplace.json +28 -0
- package/.cursor/skills/deepdub-tts/SKILL.md +10 -0
- package/AGENTS.md +14 -0
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/bin/deepdub-agent-kit.js +419 -0
- package/examples/js/.env.example +4 -0
- package/examples/js/package.json +9 -0
- package/examples/js/tts.mjs +20 -0
- package/examples/python/.env.example +4 -0
- package/examples/python/requirements.txt +1 -0
- package/examples/python/tts.py +24 -0
- package/package.json +53 -0
- package/plugin/.claude-plugin/plugin.json +45 -0
- package/plugin/.codex-plugin/plugin.json +40 -0
- package/plugin/skills/deepdub-tts/SKILL.md +161 -0
- package/plugin/skills/deepdub-tts/agents/openai.yaml +7 -0
- package/plugin/skills/deepdub-tts/examples.md +149 -0
- package/plugin/skills/deepdub-tts/reference.md +123 -0
- package/plugin/skills/deepdub-tts/scripts/scaffold-deepdub.js +129 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "deepdub-agent-plugins",
|
|
3
|
+
"interface": {
|
|
4
|
+
"displayName": "Deepdub Agent Plugins"
|
|
5
|
+
},
|
|
6
|
+
"plugins": [
|
|
7
|
+
{
|
|
8
|
+
"name": "deepdub-tts",
|
|
9
|
+
"source": {
|
|
10
|
+
"source": "local",
|
|
11
|
+
"path": "./plugin"
|
|
12
|
+
},
|
|
13
|
+
"policy": {
|
|
14
|
+
"installation": "AVAILABLE",
|
|
15
|
+
"authentication": "ON_INSTALL"
|
|
16
|
+
},
|
|
17
|
+
"category": "Productivity"
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deepdub-tts
|
|
3
|
+
description: Add Deepdub text-to-speech, voice cloning, streaming speech, voice presets, or Deepdub SDK/API usage to JavaScript, Python, REST, or CLI projects. Use when the user mentions Deepdub, TTS, text-to-speech, speech synthesis, voice generation, voicePromptId, voiceReference, audio streaming, or generating audio files.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Deepdub TTS
|
|
7
|
+
|
|
8
|
+
This repository keeps the canonical skill at `plugin/skills/deepdub-tts/SKILL.md`.
|
|
9
|
+
|
|
10
|
+
When this skill is selected, read and follow `plugin/skills/deepdub-tts/SKILL.md`. Use `plugin/skills/deepdub-tts/reference.md` for API details and `plugin/skills/deepdub-tts/examples.md` for snippets.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/claude-code-plugin-marketplace.json",
|
|
3
|
+
"name": "deepdub-agent-plugins",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "Deepdub",
|
|
6
|
+
"email": "support@deepdub.ai"
|
|
7
|
+
},
|
|
8
|
+
"description": "Deepdub plugins and skills for adding expressive text-to-speech to software projects.",
|
|
9
|
+
"version": "0.1.0",
|
|
10
|
+
"plugins": [
|
|
11
|
+
{
|
|
12
|
+
"name": "deepdub-tts",
|
|
13
|
+
"description": "Add Deepdub text-to-speech to JavaScript, Python, CLI, and REST projects.",
|
|
14
|
+
"version": "0.1.0",
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "Deepdub",
|
|
17
|
+
"email": "support@deepdub.ai"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://docs.deepdub.ai",
|
|
20
|
+
"repository": "https://github.com/deepdub-ai/deepdub-agent-plugin",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"keywords": ["deepdub", "tts", "voice-ai", "speech-synthesis"],
|
|
23
|
+
"category": "AI",
|
|
24
|
+
"tags": ["tts", "audio", "javascript", "python", "cli"],
|
|
25
|
+
"source": "./plugin"
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: deepdub-tts
|
|
3
|
+
description: Add Deepdub text-to-speech, voice cloning, streaming speech, voice presets, or Deepdub SDK/API usage to JavaScript, Python, REST, or CLI projects. Use when the user mentions Deepdub, TTS, text-to-speech, speech synthesis, voice generation, voicePromptId, voiceReference, audio streaming, or generating audio files.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Deepdub TTS
|
|
7
|
+
|
|
8
|
+
This repository keeps the canonical skill at `plugin/skills/deepdub-tts/SKILL.md`.
|
|
9
|
+
|
|
10
|
+
When this skill is selected, read and follow `plugin/skills/deepdub-tts/SKILL.md`. Use `plugin/skills/deepdub-tts/reference.md` for API details and `plugin/skills/deepdub-tts/examples.md` for snippets.
|
package/AGENTS.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Deepdub Agent Plugin Repository
|
|
2
|
+
|
|
3
|
+
This repository packages Deepdub TTS guidance for Claude Code, Codex, and Cursor.
|
|
4
|
+
|
|
5
|
+
Keep these surfaces in sync when changing behavior:
|
|
6
|
+
|
|
7
|
+
- `plugin/skills/deepdub-tts/` is the canonical portable skill.
|
|
8
|
+
- `plugin/.claude-plugin/plugin.json` is the Claude Code plugin manifest.
|
|
9
|
+
- `plugin/.codex-plugin/plugin.json` is the Codex plugin manifest.
|
|
10
|
+
- `.claude-plugin/marketplace.json` and `.agents/plugins/marketplace.json` are local marketplace catalogs.
|
|
11
|
+
- `.cursor/skills/deepdub-tts/SKILL.md` and `.agents/skills/deepdub-tts/SKILL.md` are repository discovery wrappers.
|
|
12
|
+
- `bin/deepdub-agent-kit.js` is the npm CLI for local install, scaffolding, and terminal TTS.
|
|
13
|
+
|
|
14
|
+
Do not commit real Deepdub API keys. Use `DEEPDUB_API_KEY` in examples and `.env.example` files.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Deepdub
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Deepdub Agent Plugin
|
|
2
|
+
|
|
3
|
+
Claude Code, Codex, and Cursor skills for adding Deepdub text-to-speech to JavaScript, Python, REST, and CLI projects.
|
|
4
|
+
|
|
5
|
+
The goal is simple: ask your coding agent to add speech generation, and it should know the right SDK, env vars, voice presets, streaming tradeoffs, and error handling without making you paste docs.
|
|
6
|
+
|
|
7
|
+
## What Is Included
|
|
8
|
+
|
|
9
|
+
- Claude Code plugin: `plugin/.claude-plugin/plugin.json`
|
|
10
|
+
- Codex plugin: `plugin/.codex-plugin/plugin.json`
|
|
11
|
+
- Portable skill: `plugin/skills/deepdub-tts/SKILL.md`
|
|
12
|
+
- Cursor project skill wrapper: `.cursor/skills/deepdub-tts/SKILL.md`
|
|
13
|
+
- Codex project skill wrapper: `.agents/skills/deepdub-tts/SKILL.md`
|
|
14
|
+
- Marketplace catalogs for local testing:
|
|
15
|
+
- Claude Code: `.claude-plugin/marketplace.json`
|
|
16
|
+
- Codex: `.agents/plugins/marketplace.json`
|
|
17
|
+
- npm CLI: `deepdub-agent-kit`
|
|
18
|
+
- Runnable JavaScript and Python examples under `examples/`
|
|
19
|
+
|
|
20
|
+
## Fastest Use
|
|
21
|
+
|
|
22
|
+
Run a TTS demo from the terminal:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx @deepdub/agent-plugin tts \
|
|
26
|
+
--text "Hello from Deepdub" \
|
|
27
|
+
--out hello.mp3
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
If `DEEPDUB_API_KEY` is not set, the CLI uses Deepdub's public rate-limited trial key for that demo. For real projects:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
export DEEPDUB_API_KEY=dd-your-api-key
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Install Skills Locally
|
|
37
|
+
|
|
38
|
+
Install the skill into the current project for Cursor, Codex, and Claude Code:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx @deepdub/agent-plugin install --agent all --scope project
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Install globally for your user:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx @deepdub/agent-plugin install --agent all --scope user
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Use `--link` while developing this repository, or `--force` to replace an existing installed copy.
|
|
51
|
+
|
|
52
|
+
## Scaffold A Tiny App
|
|
53
|
+
|
|
54
|
+
JavaScript:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx @deepdub/agent-plugin scaffold --language js --out ./deepdub-demo
|
|
58
|
+
cd deepdub-demo
|
|
59
|
+
npm install
|
|
60
|
+
DEEPDUB_API_KEY=dd-your-api-key npm run tts -- "Welcome to Deepdub"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Python:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx @deepdub/agent-plugin scaffold --language python --out ./deepdub-demo
|
|
67
|
+
cd deepdub-demo
|
|
68
|
+
pip install -r requirements.txt
|
|
69
|
+
DEEPDUB_API_KEY=dd-your-api-key python tts.py "Welcome to Deepdub"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Claude Code Marketplace
|
|
73
|
+
|
|
74
|
+
Install from the public GitHub repository:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
claude plugin marketplace add https://github.com/deepdub-ai/deepdub-agent-plugin
|
|
78
|
+
claude plugin install deepdub-tts@deepdub-agent-plugins
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
During local development, add this repository as a marketplace from disk:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
claude plugin marketplace add ./path/to/deepdub-agent-plugin
|
|
85
|
+
claude plugin install deepdub-tts@deepdub-agent-plugins
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Or test the plugin directly:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
claude --plugin-dir ./plugin
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
After installation, use prompts like:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
Use Deepdub TTS to add a /tts endpoint that returns MP3 bytes.
|
|
98
|
+
Use Deepdub TTS to create a CLI command that saves speech to a file.
|
|
99
|
+
Use Deepdub TTS to stream WAV chunks from a WebSocket connection.
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Codex Marketplace
|
|
103
|
+
|
|
104
|
+
Codex can read the repo marketplace at `.agents/plugins/marketplace.json`. For a local marketplace:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
codex plugin marketplace add ./path/to/deepdub-agent-plugin
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
You can also use the repo skill directly from `.agents/skills/deepdub-tts/`.
|
|
111
|
+
|
|
112
|
+
## Cursor Skill
|
|
113
|
+
|
|
114
|
+
Cursor discovers project skills from `.cursor/skills/`. This repo includes a wrapper skill that points to the canonical skill in `plugin/skills/deepdub-tts/`.
|
|
115
|
+
|
|
116
|
+
For use in another project:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx @deepdub/agent-plugin install --agent cursor --scope project --project /path/to/project
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## CLI Reference
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
deepdub-agent-kit --help
|
|
126
|
+
deepdub-agent-kit doctor
|
|
127
|
+
deepdub-agent-kit install --agent cursor,codex --scope project
|
|
128
|
+
deepdub-agent-kit scaffold --language js --out ./deepdub-demo
|
|
129
|
+
deepdub-agent-kit voices --api-key dd-your-api-key
|
|
130
|
+
deepdub-agent-kit tts --text "Hello" --voice-prompt-id 50a537cf-1ec8-4714-b07e-c589ab76be4b --out output.mp3
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Development
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npm install
|
|
137
|
+
npm run smoke
|
|
138
|
+
npm run doctor
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Validate Claude Code metadata if you have Claude Code installed:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
claude plugin validate ./plugin
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Deepdub Docs
|
|
148
|
+
|
|
149
|
+
- Documentation index: https://docs.deepdub.ai/llms.txt
|
|
150
|
+
- Quickstart: https://docs.deepdub.ai/quickstart.md
|
|
151
|
+
- JavaScript SDK: https://www.npmjs.com/package/@deepdub/node
|
|
152
|
+
- Python SDK: https://docs.deepdub.ai/sdk.md
|
|
153
|
+
- Voice presets: https://docs.deepdub.ai/voice-presets.md
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const fs = require("node:fs");
|
|
6
|
+
const os = require("node:os");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
|
|
9
|
+
const PACKAGE_ROOT = path.resolve(__dirname, "..");
|
|
10
|
+
const SKILL_SOURCE = path.join(PACKAGE_ROOT, "plugin", "skills", "deepdub-tts");
|
|
11
|
+
const TRIAL_API_KEY = "dd-00000000000000000000000065c9cbfe";
|
|
12
|
+
const DEFAULT_VOICE_PROMPT_ID = "50a537cf-1ec8-4714-b07e-c589ab76be4b";
|
|
13
|
+
|
|
14
|
+
function printHelp() {
|
|
15
|
+
console.log(`Deepdub Agent Kit
|
|
16
|
+
|
|
17
|
+
Usage:
|
|
18
|
+
deepdub-agent-kit install [--agent all|cursor,codex,claude] [--scope project|user] [--project .] [--force] [--link]
|
|
19
|
+
deepdub-agent-kit scaffold --language js|python [--out ./deepdub-demo]
|
|
20
|
+
deepdub-agent-kit tts --text "Hello" [--voice-prompt-id ID] [--out output.mp3] [--api-key KEY] [--eu]
|
|
21
|
+
deepdub-agent-kit voices [--api-key KEY] [--eu]
|
|
22
|
+
deepdub-agent-kit doctor
|
|
23
|
+
|
|
24
|
+
Aliases:
|
|
25
|
+
dd-agent
|
|
26
|
+
|
|
27
|
+
Examples:
|
|
28
|
+
npx @deepdub/agent-plugin install --agent all --scope project
|
|
29
|
+
npx @deepdub/agent-plugin scaffold --language js --out ./deepdub-demo
|
|
30
|
+
npx @deepdub/agent-plugin tts --text "Hello from Deepdub" --out hello.mp3
|
|
31
|
+
`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function parseArgs(argv) {
|
|
35
|
+
const args = { _: [] };
|
|
36
|
+
|
|
37
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
38
|
+
const token = argv[index];
|
|
39
|
+
|
|
40
|
+
if (!token.startsWith("--")) {
|
|
41
|
+
args._.push(token);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const [rawKey, inlineValue] = token.slice(2).split(/=(.*)/s, 2);
|
|
46
|
+
const key = rawKey.trim();
|
|
47
|
+
|
|
48
|
+
if (inlineValue !== undefined) {
|
|
49
|
+
args[key] = inlineValue;
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const next = argv[index + 1];
|
|
54
|
+
if (!next || next.startsWith("--")) {
|
|
55
|
+
args[key] = true;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
args[key] = next;
|
|
60
|
+
index += 1;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return args;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getArg(args, ...keys) {
|
|
67
|
+
for (const key of keys) {
|
|
68
|
+
if (args[key] !== undefined) return args[key];
|
|
69
|
+
}
|
|
70
|
+
return undefined;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function listArg(value, fallback) {
|
|
74
|
+
if (!value || value === true) return fallback;
|
|
75
|
+
return String(value)
|
|
76
|
+
.split(",")
|
|
77
|
+
.map((item) => item.trim().toLowerCase())
|
|
78
|
+
.filter(Boolean);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function ensureDir(dirPath) {
|
|
82
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function copyOrLinkSkill(destination, options) {
|
|
86
|
+
ensureDir(path.dirname(destination));
|
|
87
|
+
|
|
88
|
+
if (fs.existsSync(destination)) {
|
|
89
|
+
if (!options.force) {
|
|
90
|
+
console.log(
|
|
91
|
+
`skip ${destination} (already exists; pass --force to replace)`
|
|
92
|
+
);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (options.link) {
|
|
99
|
+
fs.symlinkSync(SKILL_SOURCE, destination, "dir");
|
|
100
|
+
console.log(`linked ${destination}`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fs.cpSync(SKILL_SOURCE, destination, { recursive: true });
|
|
105
|
+
console.log(`installed ${destination}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function install(args) {
|
|
109
|
+
const requested = listArg(getArg(args, "agent", "agents"), ["all"]);
|
|
110
|
+
const agents = requested.includes("all")
|
|
111
|
+
? ["cursor", "codex", "claude"]
|
|
112
|
+
: requested;
|
|
113
|
+
const scope = String(getArg(args, "scope") || "project").toLowerCase();
|
|
114
|
+
const projectRoot = path.resolve(
|
|
115
|
+
String(getArg(args, "project") || process.cwd())
|
|
116
|
+
);
|
|
117
|
+
const options = {
|
|
118
|
+
force: Boolean(args.force),
|
|
119
|
+
link: Boolean(args.link),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const destinations = {
|
|
123
|
+
cursor:
|
|
124
|
+
scope === "user"
|
|
125
|
+
? path.join(os.homedir(), ".cursor", "skills", "deepdub-tts")
|
|
126
|
+
: path.join(projectRoot, ".cursor", "skills", "deepdub-tts"),
|
|
127
|
+
codex:
|
|
128
|
+
scope === "user"
|
|
129
|
+
? path.join(os.homedir(), ".agents", "skills", "deepdub-tts")
|
|
130
|
+
: path.join(projectRoot, ".agents", "skills", "deepdub-tts"),
|
|
131
|
+
claude:
|
|
132
|
+
scope === "user"
|
|
133
|
+
? path.join(os.homedir(), ".claude", "skills", "deepdub-tts")
|
|
134
|
+
: path.join(projectRoot, ".claude", "skills", "deepdub-tts"),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
for (const agent of agents) {
|
|
138
|
+
if (!destinations[agent]) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Unknown agent "${agent}". Use cursor, codex, claude, or all.`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
copyOrLinkSkill(destinations[agent], options);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function detectLanguage(args) {
|
|
148
|
+
const explicit = getArg(args, "language", "lang");
|
|
149
|
+
if (explicit) return String(explicit).toLowerCase();
|
|
150
|
+
if (fs.existsSync(path.join(process.cwd(), "pyproject.toml")))
|
|
151
|
+
return "python";
|
|
152
|
+
if (fs.existsSync(path.join(process.cwd(), "requirements.txt")))
|
|
153
|
+
return "python";
|
|
154
|
+
return "js";
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function writeIfMissing(filePath, content, force) {
|
|
158
|
+
ensureDir(path.dirname(filePath));
|
|
159
|
+
if (fs.existsSync(filePath) && !force) {
|
|
160
|
+
console.log(`skip ${filePath} (already exists; pass --force to replace)`);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
fs.writeFileSync(filePath, content);
|
|
164
|
+
console.log(`wrote ${filePath}`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function jsScaffold() {
|
|
168
|
+
return `import { writeFile } from "node:fs/promises";
|
|
169
|
+
import { DeepdubClient } from "@deepdub/node";
|
|
170
|
+
|
|
171
|
+
const deepdub = new DeepdubClient(process.env.DEEPDUB_API_KEY, {
|
|
172
|
+
protocol: "http",
|
|
173
|
+
eu: process.env.DD_EU === "1",
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const text = process.argv.slice(2).join(" ") || "Hello from Deepdub.";
|
|
177
|
+
const voicePromptId =
|
|
178
|
+
process.env.DEEPDUB_VOICE_PROMPT_ID ||
|
|
179
|
+
"${DEFAULT_VOICE_PROMPT_ID}";
|
|
180
|
+
|
|
181
|
+
const audio = await deepdub.tts(text, {
|
|
182
|
+
voicePromptId,
|
|
183
|
+
locale: process.env.DEEPDUB_LOCALE || "en-US",
|
|
184
|
+
format: "mp3",
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await writeFile("output.mp3", audio);
|
|
188
|
+
console.log(\`Generated output.mp3 (\${audio.length} bytes)\`);
|
|
189
|
+
`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function pythonScaffold() {
|
|
193
|
+
return `import os
|
|
194
|
+
import sys
|
|
195
|
+
from pathlib import Path
|
|
196
|
+
|
|
197
|
+
from deepdub import DeepdubClient
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
client = DeepdubClient()
|
|
201
|
+
|
|
202
|
+
text = " ".join(sys.argv[1:]) or "Hello from Deepdub."
|
|
203
|
+
voice_prompt_id = os.getenv(
|
|
204
|
+
"DEEPDUB_VOICE_PROMPT_ID",
|
|
205
|
+
"${DEFAULT_VOICE_PROMPT_ID}",
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
audio = client.tts(
|
|
209
|
+
text=text,
|
|
210
|
+
voice_prompt_id=voice_prompt_id,
|
|
211
|
+
locale=os.getenv("DEEPDUB_LOCALE", "en-US"),
|
|
212
|
+
format="mp3",
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
Path("output.mp3").write_bytes(audio)
|
|
216
|
+
print(f"Generated output.mp3 ({len(audio)} bytes)")
|
|
217
|
+
`;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function scaffold(args) {
|
|
221
|
+
const language = detectLanguage(args);
|
|
222
|
+
const outDir = path.resolve(String(getArg(args, "out") || "./deepdub-demo"));
|
|
223
|
+
const force = Boolean(args.force);
|
|
224
|
+
|
|
225
|
+
if (
|
|
226
|
+
language === "js" ||
|
|
227
|
+
language === "javascript" ||
|
|
228
|
+
language === "ts" ||
|
|
229
|
+
language === "typescript"
|
|
230
|
+
) {
|
|
231
|
+
writeIfMissing(
|
|
232
|
+
path.join(outDir, "package.json"),
|
|
233
|
+
`${JSON.stringify(
|
|
234
|
+
{
|
|
235
|
+
type: "module",
|
|
236
|
+
scripts: {
|
|
237
|
+
tts: "node ./tts.mjs",
|
|
238
|
+
},
|
|
239
|
+
dependencies: {
|
|
240
|
+
"@deepdub/node": "^2.0.0",
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
null,
|
|
244
|
+
2
|
|
245
|
+
)}\n`,
|
|
246
|
+
force
|
|
247
|
+
);
|
|
248
|
+
writeIfMissing(path.join(outDir, "tts.mjs"), jsScaffold(), force);
|
|
249
|
+
writeIfMissing(
|
|
250
|
+
path.join(outDir, ".env.example"),
|
|
251
|
+
`DEEPDUB_API_KEY=dd-your-api-key
|
|
252
|
+
DEEPDUB_VOICE_PROMPT_ID=${DEFAULT_VOICE_PROMPT_ID}
|
|
253
|
+
DEEPDUB_LOCALE=en-US
|
|
254
|
+
DD_EU=0
|
|
255
|
+
`,
|
|
256
|
+
force
|
|
257
|
+
);
|
|
258
|
+
console.log(
|
|
259
|
+
`\nNext: cd ${outDir} && npm install && DEEPDUB_API_KEY=... npm run tts -- "Hello"`
|
|
260
|
+
);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (language === "py" || language === "python") {
|
|
265
|
+
writeIfMissing(path.join(outDir, "requirements.txt"), "deepdub\n", force);
|
|
266
|
+
writeIfMissing(path.join(outDir, "tts.py"), pythonScaffold(), force);
|
|
267
|
+
writeIfMissing(
|
|
268
|
+
path.join(outDir, ".env.example"),
|
|
269
|
+
`DEEPDUB_API_KEY=dd-your-api-key
|
|
270
|
+
DEEPDUB_VOICE_PROMPT_ID=${DEFAULT_VOICE_PROMPT_ID}
|
|
271
|
+
DEEPDUB_LOCALE=en-US
|
|
272
|
+
DD_EU=0
|
|
273
|
+
`,
|
|
274
|
+
force
|
|
275
|
+
);
|
|
276
|
+
console.log(
|
|
277
|
+
`\nNext: cd ${outDir} && pip install -r requirements.txt && DEEPDUB_API_KEY=... python tts.py "Hello"`
|
|
278
|
+
);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
throw new Error(`Unsupported language "${language}". Use js or python.`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function loadDeepdubSdk() {
|
|
286
|
+
try {
|
|
287
|
+
return require("@deepdub/node");
|
|
288
|
+
} catch (error) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
"Missing @deepdub/node. Run npm install, or use npx @deepdub/agent-plugin."
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function apiKeyFromArgs(args) {
|
|
296
|
+
const key = getArg(args, "api-key", "apiKey") || process.env.DEEPDUB_API_KEY;
|
|
297
|
+
if (key) return String(key);
|
|
298
|
+
if (args["no-trial"]) {
|
|
299
|
+
throw new Error("Set DEEPDUB_API_KEY or pass --api-key.");
|
|
300
|
+
}
|
|
301
|
+
console.warn(
|
|
302
|
+
"DEEPDUB_API_KEY is not set; using Deepdub's public rate-limited trial key for this demo."
|
|
303
|
+
);
|
|
304
|
+
return TRIAL_API_KEY;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function tts(args) {
|
|
308
|
+
const text = getArg(args, "text", "t") || args._.slice(1).join(" ");
|
|
309
|
+
if (!text) throw new Error('Pass text with --text "Hello".');
|
|
310
|
+
|
|
311
|
+
const { DeepdubClient } = loadDeepdubSdk();
|
|
312
|
+
const format = String(getArg(args, "format") || "mp3");
|
|
313
|
+
const out = path.resolve(
|
|
314
|
+
String(getArg(args, "out", "output") || `deepdub-output.${format}`)
|
|
315
|
+
);
|
|
316
|
+
const client = new DeepdubClient(apiKeyFromArgs(args), {
|
|
317
|
+
protocol: "http",
|
|
318
|
+
eu: Boolean(args.eu) || process.env.DD_EU === "1",
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
const params = {
|
|
322
|
+
locale: String(
|
|
323
|
+
getArg(args, "locale") || process.env.DEEPDUB_LOCALE || "en-US"
|
|
324
|
+
),
|
|
325
|
+
format,
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
const voiceReference = getArg(args, "voice-reference", "voiceReference");
|
|
329
|
+
if (voiceReference) {
|
|
330
|
+
params.voiceReference = String(voiceReference);
|
|
331
|
+
} else {
|
|
332
|
+
params.voicePromptId = String(
|
|
333
|
+
getArg(args, "voice-prompt-id", "voicePromptId") ||
|
|
334
|
+
process.env.DEEPDUB_VOICE_PROMPT_ID ||
|
|
335
|
+
DEFAULT_VOICE_PROMPT_ID
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const sampleRate = getArg(args, "sample-rate", "sampleRate");
|
|
340
|
+
if (sampleRate) params.sampleRate = Number(sampleRate);
|
|
341
|
+
|
|
342
|
+
const model = getArg(args, "model");
|
|
343
|
+
if (model) params.model = String(model);
|
|
344
|
+
|
|
345
|
+
const audio = await client.tts(String(text), params);
|
|
346
|
+
ensureDir(path.dirname(out));
|
|
347
|
+
fs.writeFileSync(out, audio);
|
|
348
|
+
console.log(`Generated ${out} (${audio.length} bytes)`);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
async function voices(args) {
|
|
352
|
+
const { DeepdubClient } = loadDeepdubSdk();
|
|
353
|
+
const client = new DeepdubClient(apiKeyFromArgs(args), {
|
|
354
|
+
protocol: "http",
|
|
355
|
+
eu: Boolean(args.eu) || process.env.DD_EU === "1",
|
|
356
|
+
});
|
|
357
|
+
const response = await client.listVoices();
|
|
358
|
+
const voicesList = response.voicePrompts || [];
|
|
359
|
+
|
|
360
|
+
if (args.json) {
|
|
361
|
+
console.log(JSON.stringify(response, null, 2));
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
for (const voice of voicesList) {
|
|
366
|
+
console.log(`${voice.id}: ${voice.name || voice.title || "Untitled"}`);
|
|
367
|
+
}
|
|
368
|
+
console.log(`\n${voicesList.length} voices`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function doctor() {
|
|
372
|
+
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
373
|
+
console.log(`node: ${process.version}`);
|
|
374
|
+
console.log(
|
|
375
|
+
`skill: ${fs.existsSync(SKILL_SOURCE) ? "ok" : "missing"} (${SKILL_SOURCE})`
|
|
376
|
+
);
|
|
377
|
+
console.log(
|
|
378
|
+
`DEEPDUB_API_KEY: ${process.env.DEEPDUB_API_KEY ? "set" : "not set"}`
|
|
379
|
+
);
|
|
380
|
+
console.log(`Node 18+: ${nodeMajor >= 18 ? "ok" : "upgrade required"}`);
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
require.resolve("@deepdub/node");
|
|
384
|
+
console.log("@deepdub/node: installed");
|
|
385
|
+
} catch (error) {
|
|
386
|
+
console.log("@deepdub/node: not installed yet");
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async function main() {
|
|
391
|
+
const args = parseArgs(process.argv.slice(2));
|
|
392
|
+
const command = args._[0];
|
|
393
|
+
|
|
394
|
+
if (!command || command === "help" || args.help || args.h) {
|
|
395
|
+
printHelp();
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (command === "install") {
|
|
400
|
+
install(args);
|
|
401
|
+
} else if (command === "scaffold") {
|
|
402
|
+
scaffold(args);
|
|
403
|
+
} else if (command === "tts") {
|
|
404
|
+
await tts(args);
|
|
405
|
+
} else if (command === "voices") {
|
|
406
|
+
await voices(args);
|
|
407
|
+
} else if (command === "doctor") {
|
|
408
|
+
doctor();
|
|
409
|
+
} else {
|
|
410
|
+
throw new Error(
|
|
411
|
+
`Unknown command "${command}". Run deepdub-agent-kit --help.`
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
main().catch((error) => {
|
|
417
|
+
console.error(`error: ${error.message}`);
|
|
418
|
+
process.exit(1);
|
|
419
|
+
});
|