verbalcoding 0.2.9 → 0.2.10
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/app-node/cli_install.test.mjs +7 -0
- package/app-node/install_config.mjs +2 -1
- package/package.json +1 -1
- package/scripts/cli.mjs +7 -0
- package/scripts/doctor.mjs +1 -1
- package/scripts/install.mjs +65 -0
|
@@ -30,7 +30,9 @@ test('CLI includes npm-friendly setup and start commands', () => {
|
|
|
30
30
|
const cli = fs.readFileSync(path.join(ROOT, 'scripts', 'cli.mjs'), 'utf8');
|
|
31
31
|
|
|
32
32
|
assert.match(cli, /vc setup \[--yes\]/);
|
|
33
|
+
assert.match(cli, /vc setup token \[bot-token\]/);
|
|
33
34
|
assert.match(cli, /command === 'setup'/);
|
|
35
|
+
assert.match(cli, /install\.mjs'\), \.\.\.argv\.slice\(1\)/);
|
|
34
36
|
assert.match(cli, /VERBALCODING_SKIP_CLI_LINK/);
|
|
35
37
|
assert.match(cli, /command === 'start'/);
|
|
36
38
|
assert.match(cli, /run\.sh/);
|
|
@@ -53,6 +55,9 @@ test('npm setup supports non-interactive --yes mode', () => {
|
|
|
53
55
|
const config = fs.readFileSync(path.join(ROOT, 'app-node', 'install_config.mjs'), 'utf8');
|
|
54
56
|
|
|
55
57
|
assert.match(installer, /args\.includes\('--yes'\)/);
|
|
58
|
+
assert.match(installer, /configureDiscordToken/);
|
|
59
|
+
assert.match(installer, /DISCORD_BOT_TOKEN: token/);
|
|
60
|
+
assert.match(installer, /vc setup token/);
|
|
56
61
|
assert.match(installer, /normalizeInstallAnswers\(process\.env\)/);
|
|
57
62
|
assert.match(config, /vc start/);
|
|
58
63
|
assert.doesNotMatch(config, /npm install -g \.\s+#/);
|
|
@@ -86,6 +91,7 @@ test('doctor auto-bootstraps fixable prerequisites by default', () => {
|
|
|
86
91
|
assert.match(doctor, /NousResearch\/hermes-agent\/main\/scripts\/install\.sh/);
|
|
87
92
|
assert.match(doctor, /VERBALCODING_DOCTOR_INSTALL_HERMES/);
|
|
88
93
|
assert.match(doctor, /Discord bot setup:/);
|
|
94
|
+
assert.match(doctor, /vc setup token/);
|
|
89
95
|
assert.match(doctor, /discord\.com\/developers\/applications/);
|
|
90
96
|
assert.match(cli, /doctor\.mjs'\), \.\.\.argv\.slice\(1\)/);
|
|
91
97
|
});
|
|
@@ -99,6 +105,7 @@ test('setup summary guides Discord app creation and records client id', () => {
|
|
|
99
105
|
assert.match(config, /Discord app setup:/);
|
|
100
106
|
assert.match(config, /https:\/\/discord\.com\/developers\/applications/);
|
|
101
107
|
assert.match(config, /vc bot invite <client-id>/);
|
|
108
|
+
assert.match(config, /vc setup token/);
|
|
102
109
|
assert.match(config, /buildDiscordBotInviteUrl\(\{ clientId: values\.DISCORD_CLIENT_ID \}\)/);
|
|
103
110
|
});
|
|
104
111
|
|
|
@@ -252,9 +252,10 @@ export function renderInstallSummary(values = {}) {
|
|
|
252
252
|
'Discord app setup:',
|
|
253
253
|
' 1. Create an app: https://discord.com/developers/applications',
|
|
254
254
|
' 2. Bot tab: Add Bot, enable Message Content Intent, copy/reset the token.',
|
|
255
|
-
' 3.
|
|
255
|
+
' 3. Register the token with `vc setup token` (or `vc setup token <token>`).',
|
|
256
256
|
inviteUrl ? ` 4. Invite URL: ${inviteUrl}` : ' 4. Invite URL: vc bot invite <client-id>',
|
|
257
257
|
' 5. Make sure the bot can read/send text and connect/speak in voice.',
|
|
258
|
+
'You may skip the token now and run `vc setup token` anytime later.',
|
|
258
259
|
'',
|
|
259
260
|
'Next commands:',
|
|
260
261
|
' vc doctor',
|
package/package.json
CHANGED
package/scripts/cli.mjs
CHANGED
|
@@ -31,6 +31,7 @@ function usage() {
|
|
|
31
31
|
|
|
32
32
|
Usage:
|
|
33
33
|
vc setup [--yes] [--no-wizard] [--skip-system] [--skip-model] [--skip-edge-tts]
|
|
34
|
+
vc setup token [bot-token] [--client-id <client-id>]
|
|
34
35
|
vc start
|
|
35
36
|
vc status
|
|
36
37
|
vc language <ko|en|auto>
|
|
@@ -48,6 +49,7 @@ Usage:
|
|
|
48
49
|
Examples:
|
|
49
50
|
npx verbalcoding setup --yes
|
|
50
51
|
vc setup --yes
|
|
52
|
+
vc setup token
|
|
51
53
|
vc start
|
|
52
54
|
vc language en
|
|
53
55
|
vc language ko
|
|
@@ -275,6 +277,11 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
275
277
|
}
|
|
276
278
|
if (command === 'setup' || command === 'install') {
|
|
277
279
|
const { spawnSync } = await import('node:child_process');
|
|
280
|
+
if (argv[1] === 'token' || argv[1] === 'discord' || argv[1] === 'bot-token') {
|
|
281
|
+
const result = spawnSync(process.execPath, [path.join(ROOT, 'scripts', 'install.mjs'), ...argv.slice(1)], { stdio: 'inherit', cwd: ROOT });
|
|
282
|
+
process.exitCode = result.status ?? 1;
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
278
285
|
const script = path.join(ROOT, 'scripts', 'install.sh');
|
|
279
286
|
const result = spawnSync('bash', [script, ...argv.slice(1)], {
|
|
280
287
|
stdio: 'inherit',
|
package/scripts/doctor.mjs
CHANGED
|
@@ -141,7 +141,7 @@ function discordSetupGuidance(env) {
|
|
|
141
141
|
'Discord bot setup:',
|
|
142
142
|
' 1. Open https://discord.com/developers/applications and create an application.',
|
|
143
143
|
' 2. Bot tab: Add Bot, enable Message Content Intent, then Reset/Copy Token.',
|
|
144
|
-
` 3.
|
|
144
|
+
` 3. Register it with \`vc setup token\` (or \`vc setup token <token>\`).`,
|
|
145
145
|
' 4. OAuth2 tab: copy the Application/Client ID.',
|
|
146
146
|
clientId
|
|
147
147
|
? ` 5. Invite the bot: vc bot invite ${clientId}`
|
package/scripts/install.mjs
CHANGED
|
@@ -15,8 +15,73 @@ async function ask(question, fallback = '', options = {}) {
|
|
|
15
15
|
return answer || fallback;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function quoteEnv(value) {
|
|
19
|
+
return JSON.stringify(String(value ?? ''));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function upsertEnvFile(file, updates) {
|
|
23
|
+
const existing = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : '';
|
|
24
|
+
const seen = new Set();
|
|
25
|
+
const lines = existing.split(/\r?\n/).map(raw => {
|
|
26
|
+
const line = raw.trim();
|
|
27
|
+
if (!line || line.startsWith('#') || !line.includes('=')) return raw;
|
|
28
|
+
const idx = line.indexOf('=');
|
|
29
|
+
const key = line.slice(0, idx).trim().replace(/^export\s+/, '');
|
|
30
|
+
if (!(key in updates)) return raw;
|
|
31
|
+
seen.add(key);
|
|
32
|
+
return `${key}=${quoteEnv(updates[key])}`;
|
|
33
|
+
});
|
|
34
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
35
|
+
if (!seen.has(key)) lines.push(`${key}=${quoteEnv(value)}`);
|
|
36
|
+
}
|
|
37
|
+
const text = `${lines.filter((line, index, arr) => line !== '' || index < arr.length - 1).join('\n')}\n`;
|
|
38
|
+
fs.writeFileSync(file, text, { mode: 0o600 });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function argValue(args, name) {
|
|
42
|
+
const idx = args.indexOf(name);
|
|
43
|
+
if (idx < 0) return '';
|
|
44
|
+
const value = args[idx + 1] || '';
|
|
45
|
+
return value.startsWith('--') ? '' : value;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function configureDiscordToken(args) {
|
|
49
|
+
const envPath = path.join(ROOT, '.env');
|
|
50
|
+
const tokenArg = args.find((arg, idx) => idx > 0 && !arg.startsWith('--')) || argValue(args, '--token');
|
|
51
|
+
const clientIdArg = argValue(args, '--client-id') || argValue(args, '--application-id');
|
|
52
|
+
let token = tokenArg || process.env.DISCORD_BOT_TOKEN || '';
|
|
53
|
+
let clientId = clientIdArg || process.env.DISCORD_CLIENT_ID || '';
|
|
54
|
+
if (!token) {
|
|
55
|
+
globalThis.__rl = readline.createInterface({ input, output });
|
|
56
|
+
try {
|
|
57
|
+
console.log('Discord bot token setup');
|
|
58
|
+
console.log('Create/copy the token at https://discord.com/developers/applications → your app → Bot.');
|
|
59
|
+
token = await ask('Discord bot token (DISCORD_BOT_TOKEN)', '', { fallbackLabel: '' });
|
|
60
|
+
clientId = await ask('Discord application/client ID for invite URL (optional)', clientId, { fallbackLabel: clientId ? 'keep existing' : 'skip' });
|
|
61
|
+
} finally {
|
|
62
|
+
globalThis.__rl.close();
|
|
63
|
+
globalThis.__rl = null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!token) {
|
|
67
|
+
console.error('No Discord bot token provided. Nothing changed.');
|
|
68
|
+
process.exitCode = 2;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const updates = { DISCORD_BOT_TOKEN: token };
|
|
72
|
+
if (clientId) updates.DISCORD_CLIENT_ID = clientId;
|
|
73
|
+
upsertEnvFile(envPath, updates);
|
|
74
|
+
console.log(`Updated ${envPath}`);
|
|
75
|
+
console.log('Discord bot token saved. Run `vc doctor` to verify. You can update it anytime with `vc setup token`.');
|
|
76
|
+
if (clientId) console.log(`Invite URL: vc bot invite ${clientId}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
18
79
|
async function main() {
|
|
19
80
|
const args = process.argv.slice(2);
|
|
81
|
+
if (args[0] === 'token' || args[0] === 'discord' || args[0] === 'bot-token') {
|
|
82
|
+
await configureDiscordToken(args);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
20
85
|
const yes = args.includes('--yes') || args.includes('-y');
|
|
21
86
|
if (args[0] === 'instance' || args.includes('--instance')) {
|
|
22
87
|
const { spawnSync } = await import('node:child_process');
|