@useconductor/conductor 1.0.0 → 1.0.1
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/.github/README.md +374 -7
- package/.github/workflows/ci.yml +3 -1
- package/.github/workflows/claude-code-review.yml +1 -15
- package/.github/workflows/publish.yml +43 -0
- package/README.md +290 -121
- package/dist/cli/commands/audit.d.ts +40 -0
- package/dist/cli/commands/audit.d.ts.map +1 -0
- package/dist/cli/commands/audit.js +272 -0
- package/dist/cli/commands/audit.js.map +1 -0
- package/dist/cli/commands/circuit.d.ts +13 -0
- package/dist/cli/commands/circuit.d.ts.map +1 -0
- package/dist/cli/commands/circuit.js +53 -0
- package/dist/cli/commands/circuit.js.map +1 -0
- package/dist/cli/commands/config.d.ts +31 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +152 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/init.d.ts +5 -8
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +86 -123
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/marketplace.js +1 -1
- package/dist/cli/commands/onboard.d.ts.map +1 -1
- package/dist/cli/commands/onboard.js +33 -11
- package/dist/cli/commands/onboard.js.map +1 -1
- package/dist/cli/commands/release.d.ts.map +1 -1
- package/dist/cli/commands/release.js +1 -1
- package/dist/cli/commands/release.js.map +1 -1
- package/dist/cli/index.js +146 -10
- package/dist/cli/index.js.map +1 -1
- package/dist/core/audit.d.ts.map +1 -1
- package/dist/core/audit.js +5 -2
- package/dist/core/audit.js.map +1 -1
- package/dist/core/conductor.d.ts.map +1 -1
- package/dist/core/conductor.js +12 -0
- package/dist/core/conductor.js.map +1 -1
- package/dist/core/config.d.ts +3 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +46 -2
- package/dist/core/config.js.map +1 -1
- package/dist/core/database.d.ts +3 -0
- package/dist/core/database.d.ts.map +1 -1
- package/dist/core/database.js +26 -0
- package/dist/core/database.js.map +1 -1
- package/dist/core/encryption.d.ts +34 -0
- package/dist/core/encryption.d.ts.map +1 -0
- package/dist/core/encryption.js +96 -0
- package/dist/core/encryption.js.map +1 -0
- package/dist/core/zero-config.d.ts.map +1 -1
- package/dist/core/zero-config.js +1 -4
- package/dist/core/zero-config.js.map +1 -1
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +112 -16
- package/dist/dashboard/server.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +30 -2
- package/dist/mcp/server.js.map +1 -1
- package/dist/plugins/builtin/aws.d.ts +31 -0
- package/dist/plugins/builtin/aws.d.ts.map +1 -0
- package/dist/plugins/builtin/aws.js +149 -0
- package/dist/plugins/builtin/aws.js.map +1 -0
- package/dist/plugins/builtin/database.d.ts +1 -0
- package/dist/plugins/builtin/database.d.ts.map +1 -1
- package/dist/plugins/builtin/database.js +26 -1
- package/dist/plugins/builtin/database.js.map +1 -1
- package/dist/plugins/builtin/docker.d.ts +4 -0
- package/dist/plugins/builtin/docker.d.ts.map +1 -1
- package/dist/plugins/builtin/docker.js +20 -1
- package/dist/plugins/builtin/docker.js.map +1 -1
- package/dist/plugins/builtin/gcp.d.ts +28 -0
- package/dist/plugins/builtin/gcp.d.ts.map +1 -0
- package/dist/plugins/builtin/gcp.js +135 -0
- package/dist/plugins/builtin/gcp.js.map +1 -0
- package/dist/plugins/builtin/index.d.ts.map +1 -1
- package/dist/plugins/builtin/index.js +4 -0
- package/dist/plugins/builtin/index.js.map +1 -1
- package/dist/plugins/builtin/jira.d.ts.map +1 -1
- package/dist/plugins/builtin/jira.js +4 -2
- package/dist/plugins/builtin/jira.js.map +1 -1
- package/dist/plugins/builtin/linear.js +1 -1
- package/dist/plugins/builtin/linear.js.map +1 -1
- package/dist/plugins/builtin/shell.js +1 -1
- package/dist/plugins/builtin/shell.js.map +1 -1
- package/dist/plugins/builtin/slack.d.ts +1 -0
- package/dist/plugins/builtin/slack.d.ts.map +1 -1
- package/dist/plugins/builtin/slack.js +9 -1
- package/dist/plugins/builtin/slack.js.map +1 -1
- package/dist/plugins/builtin/spotify.js +1 -1
- package/dist/plugins/builtin/spotify.js.map +1 -1
- package/dist/plugins/builtin/vercel.d.ts.map +1 -1
- package/dist/plugins/builtin/vercel.js +3 -1
- package/dist/plugins/builtin/vercel.js.map +1 -1
- package/dist/security/sso.d.ts +37 -0
- package/dist/security/sso.d.ts.map +1 -0
- package/dist/security/sso.js +92 -0
- package/dist/security/sso.js.map +1 -0
- package/docs/deployment.md +201 -0
- package/docs/plugin-sdk.md +212 -0
- package/package.json +11 -8
- package/src/cli/commands/audit.ts +318 -0
- package/src/cli/commands/circuit.ts +63 -0
- package/src/cli/commands/config.ts +176 -0
- package/src/cli/commands/init.ts +87 -145
- package/src/cli/commands/marketplace.ts +1 -1
- package/src/cli/commands/onboard.ts +33 -11
- package/src/cli/commands/release.ts +13 -6
- package/src/cli/index.ts +165 -11
- package/src/core/audit.ts +5 -2
- package/src/core/conductor.ts +11 -0
- package/src/core/config.ts +47 -2
- package/src/core/database.ts +32 -0
- package/src/core/encryption.ts +110 -0
- package/src/core/zero-config.ts +1 -5
- package/src/dashboard/server.ts +135 -16
- package/src/mcp/server.ts +40 -2
- package/src/plugins/builtin/aws.ts +162 -0
- package/src/plugins/builtin/database.ts +19 -1
- package/src/plugins/builtin/docker.ts +17 -1
- package/src/plugins/builtin/gcp.ts +145 -0
- package/src/plugins/builtin/index.ts +4 -0
- package/src/plugins/builtin/jira.ts +23 -19
- package/src/plugins/builtin/linear.ts +1 -1
- package/src/plugins/builtin/shell.ts +1 -1
- package/src/plugins/builtin/slack.ts +6 -1
- package/src/plugins/builtin/spotify.ts +1 -1
- package/src/plugins/builtin/vercel.ts +3 -1
- package/src/security/sso.ts +124 -0
- package/tests/audit.test.ts +185 -0
- package/tests/circuit-breaker.test.ts +125 -0
- package/tests/docker.test.ts +244 -39
- package/tests/errors.test.ts +122 -0
- package/tests/github.test.ts.skip +392 -0
- package/tests/jira.test.ts +310 -0
- package/tests/linear.test.ts +366 -0
- package/tests/mcp.test.ts.skip +243 -0
- package/tests/notion.test.ts +257 -0
- package/tests/retry.test.ts +104 -0
- package/tests/shell.test.ts +262 -30
- package/tests/slack.test.ts +250 -0
- package/tests/stripe.test.ts +272 -0
- package/tests/validation.test.ts +173 -0
- package/tests/vercel.test.ts +368 -0
- package/tests/zero-config.test.ts +566 -0
- package/C.png +0 -0
- package/tests/mcp.test.ts +0 -14
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* conductor config — read and write configuration keys.
|
|
3
|
+
*
|
|
4
|
+
* Commands:
|
|
5
|
+
* conductor config list — show all config keys and values
|
|
6
|
+
* conductor config get <key> — get a specific key
|
|
7
|
+
* conductor config set <key> <val> — set a key
|
|
8
|
+
* conductor config path — print config file path
|
|
9
|
+
* conductor config export — dump config as JSON
|
|
10
|
+
* conductor config reset — reset to defaults (with confirmation)
|
|
11
|
+
* conductor config validate — validate config structure
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { Conductor } from '../../core/conductor.js';
|
|
15
|
+
|
|
16
|
+
function flattenConfig(obj: unknown, prefix = ''): Array<[string, unknown]> {
|
|
17
|
+
if (typeof obj !== 'object' || obj === null) {
|
|
18
|
+
return [[prefix, obj]];
|
|
19
|
+
}
|
|
20
|
+
const result: Array<[string, unknown]> = [];
|
|
21
|
+
for (const [key, val] of Object.entries(obj as Record<string, unknown>)) {
|
|
22
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
23
|
+
if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
|
|
24
|
+
result.push(...flattenConfig(val, fullKey));
|
|
25
|
+
} else {
|
|
26
|
+
result.push([fullKey, val]);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function maskSecret(key: string, value: unknown): unknown {
|
|
33
|
+
if (/token|secret|password|api_key|key_stored/i.test(key)) {
|
|
34
|
+
if (typeof value === 'string' && value.length > 0) return '***';
|
|
35
|
+
}
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function configList(
|
|
40
|
+
conductor: Conductor,
|
|
41
|
+
opts: { json?: boolean; show_secrets?: boolean },
|
|
42
|
+
): Promise<void> {
|
|
43
|
+
await conductor.initialize();
|
|
44
|
+
const config = conductor.getConfig().getConfig();
|
|
45
|
+
|
|
46
|
+
if (opts.json) {
|
|
47
|
+
console.log(JSON.stringify(config, null, 2));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const pairs = flattenConfig(config);
|
|
52
|
+
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log(' ⚙️ Configuration\n');
|
|
55
|
+
console.log(` ${'KEY'.padEnd(40)} VALUE`);
|
|
56
|
+
console.log(' ' + '─'.repeat(70));
|
|
57
|
+
for (const [key, val] of pairs) {
|
|
58
|
+
const display = opts.show_secrets ? val : maskSecret(key, val);
|
|
59
|
+
const str = Array.isArray(val) ? `[${(val as unknown[]).join(', ')}]` : String(display ?? '');
|
|
60
|
+
console.log(` ${key.padEnd(40)} ${str}`);
|
|
61
|
+
}
|
|
62
|
+
console.log('');
|
|
63
|
+
console.log(` Config file: ${conductor.getConfig().getConfigDir()}/config.json\n`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function configGet(conductor: Conductor, key: string, opts: { json?: boolean }): Promise<void> {
|
|
67
|
+
await conductor.initialize();
|
|
68
|
+
const value = conductor.getConfig().get(key);
|
|
69
|
+
|
|
70
|
+
if (value === undefined) {
|
|
71
|
+
console.error(`\n ❌ Key "${key}" not found.\n`);
|
|
72
|
+
console.log(' Run: conductor config list to see all keys.\n');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (opts.json) {
|
|
77
|
+
console.log(JSON.stringify({ key, value }));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(`\n ${key}: ${JSON.stringify(value)}\n`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function configSet(conductor: Conductor, key: string, value: string): Promise<void> {
|
|
85
|
+
await conductor.initialize();
|
|
86
|
+
|
|
87
|
+
// Try to parse as JSON, fall back to string
|
|
88
|
+
let parsed: unknown;
|
|
89
|
+
try {
|
|
90
|
+
parsed = JSON.parse(value);
|
|
91
|
+
} catch {
|
|
92
|
+
parsed = value;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
await conductor.getConfig().set(key, parsed);
|
|
96
|
+
console.log(`\n ✅ Set ${key} = ${JSON.stringify(parsed)}\n`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export async function configPath(conductor: Conductor): Promise<void> {
|
|
100
|
+
await conductor.initialize();
|
|
101
|
+
const dir = conductor.getConfig().getConfigDir();
|
|
102
|
+
console.log(`\n ${dir}/config.json\n`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function configExport(conductor: Conductor, opts: { output?: string; pretty?: boolean }): Promise<void> {
|
|
106
|
+
await conductor.initialize();
|
|
107
|
+
const config = conductor.getConfig().getConfig();
|
|
108
|
+
const content = opts.pretty === false ? JSON.stringify(config) : JSON.stringify(config, null, 2);
|
|
109
|
+
|
|
110
|
+
if (opts.output) {
|
|
111
|
+
const { writeFile } = await import('fs/promises');
|
|
112
|
+
await writeFile(opts.output, content + '\n', 'utf-8');
|
|
113
|
+
console.log(`\n ✅ Config exported to: ${opts.output}\n`);
|
|
114
|
+
} else {
|
|
115
|
+
console.log(content);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function configReset(conductor: Conductor, opts: { yes?: boolean }): Promise<void> {
|
|
120
|
+
if (!opts.yes) {
|
|
121
|
+
const { default: inquirer } = await import('inquirer');
|
|
122
|
+
const { confirm } = await inquirer.prompt([
|
|
123
|
+
{
|
|
124
|
+
type: 'confirm',
|
|
125
|
+
name: 'confirm',
|
|
126
|
+
message: 'Reset configuration to defaults? This cannot be undone.',
|
|
127
|
+
default: false,
|
|
128
|
+
},
|
|
129
|
+
]);
|
|
130
|
+
if (!confirm) {
|
|
131
|
+
console.log('\n Cancelled.\n');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await conductor.initialize();
|
|
137
|
+
const dir = conductor.getConfig().getConfigDir();
|
|
138
|
+
|
|
139
|
+
// Write an empty config to trigger re-initialization to defaults
|
|
140
|
+
const { writeFile } = await import('fs/promises');
|
|
141
|
+
await writeFile(`${dir}/config.json`, '{}', 'utf-8');
|
|
142
|
+
|
|
143
|
+
console.log('\n ✅ Configuration reset to defaults.\n');
|
|
144
|
+
console.log(' Run: conductor init to set up from scratch.\n');
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function configValidate(conductor: Conductor): Promise<void> {
|
|
148
|
+
await conductor.initialize();
|
|
149
|
+
const config = conductor.getConfig().getConfig();
|
|
150
|
+
const issues: string[] = [];
|
|
151
|
+
|
|
152
|
+
// Basic structural checks
|
|
153
|
+
if (config.ai?.provider && !['claude', 'openai', 'gemini', 'ollama'].includes(config.ai.provider)) {
|
|
154
|
+
issues.push(`ai.provider "${config.ai.provider}" is not a recognized provider`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (config.plugins?.enabled && !Array.isArray(config.plugins.enabled)) {
|
|
158
|
+
issues.push('plugins.enabled must be an array');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (config.plugins?.installed && !Array.isArray(config.plugins.installed)) {
|
|
162
|
+
issues.push('plugins.installed must be an array');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
console.log('');
|
|
166
|
+
if (issues.length === 0) {
|
|
167
|
+
console.log(' ✅ Configuration is valid.\n');
|
|
168
|
+
} else {
|
|
169
|
+
console.log(' ❌ Configuration issues found:\n');
|
|
170
|
+
for (const issue of issues) {
|
|
171
|
+
console.log(` • ${issue}`);
|
|
172
|
+
}
|
|
173
|
+
console.log('');
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
}
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* conductor init — First-run setup wizard
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* 3. Plugin onboard TUI (calls the existing onboard() function)
|
|
10
|
-
* 4. MCP client config (Claude Desktop / Cursor / Cline / skip)
|
|
11
|
-
* 5. Final instructions
|
|
4
|
+
* Gets a new user to a working MCP server in under 2 minutes:
|
|
5
|
+
* 1. AI provider setup (Claude / OpenAI / Gemini / Ollama / skip)
|
|
6
|
+
* 2. Plugin onboard TUI
|
|
7
|
+
* 3. MCP client config (Claude Desktop / Cursor / Cline / skip)
|
|
8
|
+
* 4. Final instructions
|
|
12
9
|
*/
|
|
13
10
|
|
|
14
|
-
import chalk from 'chalk';
|
|
15
11
|
import inquirer from 'inquirer';
|
|
16
12
|
import { Conductor } from '../../core/conductor.js';
|
|
17
13
|
import { AIManager } from '../../ai/manager.js';
|
|
@@ -19,65 +15,57 @@ import fs from 'fs/promises';
|
|
|
19
15
|
import path from 'path';
|
|
20
16
|
import { homedir } from 'os';
|
|
21
17
|
|
|
22
|
-
// ──
|
|
18
|
+
// ── Terminal helpers (b/w only) ────────────────────────────────────────────
|
|
23
19
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
console.log(
|
|
28
|
-
chalk.bold.white(' ║') +
|
|
29
|
-
chalk.bold.hex('#FF8C00')(' ♦ Conductor — The AI Tool Hub ') +
|
|
30
|
-
chalk.bold.white('║'),
|
|
31
|
-
);
|
|
32
|
-
console.log(
|
|
33
|
-
chalk.bold.white(' ║') + chalk.dim(' One MCP server. 100+ tools. Any AI. ') + chalk.bold.white('║'),
|
|
34
|
-
);
|
|
35
|
-
console.log(chalk.bold.white(' ╚══════════════════════════════════════════════╝'));
|
|
36
|
-
console.log('');
|
|
37
|
-
console.log(chalk.dim(' This wizard will get you up and running in under 2 minutes.'));
|
|
38
|
-
console.log(chalk.dim(' Press Ctrl+C at any time to exit.'));
|
|
39
|
-
console.log('');
|
|
40
|
-
}
|
|
20
|
+
const B = '\x1b[1m';
|
|
21
|
+
const D = '\x1b[2m';
|
|
22
|
+
const R = '\x1b[0m';
|
|
41
23
|
|
|
42
|
-
|
|
24
|
+
function hr(width = 50) {
|
|
25
|
+
process.stdout.write(' ' + '─'.repeat(width) + '\n');
|
|
26
|
+
}
|
|
43
27
|
|
|
44
28
|
function stepHeader(n: number, total: number, label: string): void {
|
|
45
29
|
console.log('');
|
|
46
|
-
console.log(
|
|
47
|
-
chalk.bold.white(` ── Step ${n}/${total}: ${label} `) + chalk.dim('─'.repeat(Math.max(0, 38 - label.length))),
|
|
48
|
-
);
|
|
30
|
+
console.log(` ${B}── Step ${n}/${total}: ${label}${R}`);
|
|
49
31
|
console.log('');
|
|
50
32
|
}
|
|
51
33
|
|
|
52
|
-
// ──
|
|
53
|
-
|
|
54
|
-
async function setupUserName(conductor: Conductor): Promise<void> {
|
|
55
|
-
stepHeader(1, 4, 'Your name');
|
|
56
|
-
|
|
57
|
-
const existing = conductor.getConfig().get<string>('user.name') || '';
|
|
34
|
+
// ── Banner ─────────────────────────────────────────────────────────────────
|
|
58
35
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
},
|
|
67
|
-
|
|
36
|
+
function printBanner(): void {
|
|
37
|
+
const W = 50;
|
|
38
|
+
const top = ' ┌' + '─'.repeat(W) + '┐';
|
|
39
|
+
const bot = ' └' + '─'.repeat(W) + '┘';
|
|
40
|
+
const blank = ' │' + ' '.repeat(W) + '│';
|
|
41
|
+
const line = (text: string) => {
|
|
42
|
+
const pad = W - text.length - 1;
|
|
43
|
+
return ` │ ${B}${text}${R}` + ' '.repeat(Math.max(0, pad)) + '│';
|
|
44
|
+
};
|
|
45
|
+
const dim = (text: string) => {
|
|
46
|
+
const pad = W - text.length - 1;
|
|
47
|
+
return ` │ ${D}${text}${R}` + ' '.repeat(Math.max(0, pad)) + '│';
|
|
48
|
+
};
|
|
68
49
|
|
|
69
|
-
await conductor.getConfig().set('user.name', name.trim());
|
|
70
50
|
console.log('');
|
|
71
|
-
console.log(
|
|
51
|
+
console.log(top);
|
|
52
|
+
console.log(blank);
|
|
53
|
+
console.log(line('Conductor — The AI Tool Hub'));
|
|
54
|
+
console.log(blank);
|
|
55
|
+
console.log(dim('One MCP server. 100+ tools. Any AI agent.'));
|
|
56
|
+
console.log(dim('Setup takes under 2 minutes.'));
|
|
57
|
+
console.log(blank);
|
|
58
|
+
console.log(bot);
|
|
59
|
+
console.log('');
|
|
72
60
|
}
|
|
73
61
|
|
|
74
|
-
// ── Step
|
|
62
|
+
// ── Step 1: AI Provider ────────────────────────────────────────────────────
|
|
75
63
|
|
|
76
64
|
async function setupAIProvider(conductor: Conductor): Promise<void> {
|
|
77
|
-
stepHeader(
|
|
65
|
+
stepHeader(1, 3, 'AI Provider');
|
|
78
66
|
|
|
79
|
-
console.log(
|
|
80
|
-
console.log(
|
|
67
|
+
console.log(` ${D}Pick the AI provider Conductor uses for its own reasoning.${R}`);
|
|
68
|
+
console.log(` ${D}(Separate from the AI agent that calls Conductor via MCP.)${R}`);
|
|
81
69
|
console.log('');
|
|
82
70
|
|
|
83
71
|
const { provider } = await inquirer.prompt<{ provider: string }>([
|
|
@@ -90,14 +78,14 @@ async function setupAIProvider(conductor: Conductor): Promise<void> {
|
|
|
90
78
|
{ name: 'OpenAI (GPT-4o) — popular & capable', value: 'openai' },
|
|
91
79
|
{ name: 'Gemini (Google) — fast & free tier', value: 'gemini' },
|
|
92
80
|
{ name: 'Ollama (local, private) — no API key needed', value: 'ollama' },
|
|
93
|
-
{ name:
|
|
81
|
+
{ name: `${D}Skip — configure later with: conductor ai setup${R}`, value: 'skip' },
|
|
94
82
|
],
|
|
95
83
|
},
|
|
96
84
|
]);
|
|
97
85
|
|
|
98
86
|
if (provider === 'skip') {
|
|
99
87
|
console.log('');
|
|
100
|
-
console.log(
|
|
88
|
+
console.log(` ${D}Skipped. Run: conductor ai setup${R}`);
|
|
101
89
|
return;
|
|
102
90
|
}
|
|
103
91
|
|
|
@@ -106,77 +94,54 @@ async function setupAIProvider(conductor: Conductor): Promise<void> {
|
|
|
106
94
|
switch (provider) {
|
|
107
95
|
case 'claude': {
|
|
108
96
|
console.log('');
|
|
109
|
-
console.log(
|
|
97
|
+
console.log(` ${D}Get your key at: https://console.anthropic.com${R}`);
|
|
110
98
|
const { apiKey } = await inquirer.prompt<{ apiKey: string }>([
|
|
111
|
-
{
|
|
112
|
-
|
|
113
|
-
name: 'apiKey',
|
|
114
|
-
message: 'Anthropic API key:',
|
|
115
|
-
mask: '*',
|
|
116
|
-
validate: (v: string) => v.trim().length > 0 || 'API key is required',
|
|
117
|
-
},
|
|
99
|
+
{ type: 'password', name: 'apiKey', message: 'Anthropic API key:', mask: '*',
|
|
100
|
+
validate: (v: string) => v.trim().length > 0 || 'API key is required' },
|
|
118
101
|
]);
|
|
119
102
|
await aiManager.setupClaude(apiKey.trim());
|
|
120
|
-
console.log(
|
|
103
|
+
console.log(` ✓ Claude configured`);
|
|
121
104
|
break;
|
|
122
105
|
}
|
|
123
|
-
|
|
124
106
|
case 'openai': {
|
|
125
107
|
console.log('');
|
|
126
|
-
console.log(
|
|
108
|
+
console.log(` ${D}Get your key at: https://platform.openai.com/api-keys${R}`);
|
|
127
109
|
const { apiKey } = await inquirer.prompt<{ apiKey: string }>([
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
name: 'apiKey',
|
|
131
|
-
message: 'OpenAI API key:',
|
|
132
|
-
mask: '*',
|
|
133
|
-
validate: (v: string) => v.trim().length > 0 || 'API key is required',
|
|
134
|
-
},
|
|
110
|
+
{ type: 'password', name: 'apiKey', message: 'OpenAI API key:', mask: '*',
|
|
111
|
+
validate: (v: string) => v.trim().length > 0 || 'API key is required' },
|
|
135
112
|
]);
|
|
136
113
|
await aiManager.setupOpenAI(apiKey.trim());
|
|
137
|
-
console.log(
|
|
114
|
+
console.log(` ✓ OpenAI configured`);
|
|
138
115
|
break;
|
|
139
116
|
}
|
|
140
|
-
|
|
141
117
|
case 'gemini': {
|
|
142
118
|
console.log('');
|
|
143
|
-
console.log(
|
|
119
|
+
console.log(` ${D}Get your key at: https://aistudio.google.com/app/apikey${R}`);
|
|
144
120
|
const { apiKey } = await inquirer.prompt<{ apiKey: string }>([
|
|
145
|
-
{
|
|
146
|
-
|
|
147
|
-
name: 'apiKey',
|
|
148
|
-
message: 'Gemini API key:',
|
|
149
|
-
mask: '*',
|
|
150
|
-
validate: (v: string) => v.trim().length > 0 || 'API key is required',
|
|
151
|
-
},
|
|
121
|
+
{ type: 'password', name: 'apiKey', message: 'Gemini API key:', mask: '*',
|
|
122
|
+
validate: (v: string) => v.trim().length > 0 || 'API key is required' },
|
|
152
123
|
]);
|
|
153
124
|
await aiManager.setupGemini(apiKey.trim());
|
|
154
|
-
console.log(
|
|
125
|
+
console.log(` ✓ Gemini configured`);
|
|
155
126
|
break;
|
|
156
127
|
}
|
|
157
|
-
|
|
158
128
|
case 'ollama': {
|
|
159
129
|
console.log('');
|
|
160
|
-
console.log(
|
|
130
|
+
console.log(` ${D}Make sure Ollama is running: https://ollama.ai${R}`);
|
|
161
131
|
const { model } = await inquirer.prompt<{ model: string }>([
|
|
162
|
-
{
|
|
163
|
-
type: 'input',
|
|
164
|
-
name: 'model',
|
|
165
|
-
message: 'Which Ollama model?',
|
|
166
|
-
default: 'llama3.2',
|
|
167
|
-
},
|
|
132
|
+
{ type: 'input', name: 'model', message: 'Which Ollama model?', default: 'llama3.2' },
|
|
168
133
|
]);
|
|
169
134
|
await aiManager.setupOllama(model.trim());
|
|
170
|
-
console.log(
|
|
135
|
+
console.log(` ✓ Ollama configured (${model.trim()})`);
|
|
171
136
|
break;
|
|
172
137
|
}
|
|
173
138
|
}
|
|
174
139
|
}
|
|
175
140
|
|
|
176
|
-
// ── Step
|
|
141
|
+
// ── Step 2: Plugins ────────────────────────────────────────────────────────
|
|
177
142
|
|
|
178
143
|
async function setupPlugins(conductor: Conductor): Promise<void> {
|
|
179
|
-
stepHeader(
|
|
144
|
+
stepHeader(2, 3, 'Plugins');
|
|
180
145
|
|
|
181
146
|
const { doOnboard } = await inquirer.prompt<{ doOnboard: boolean }>([
|
|
182
147
|
{
|
|
@@ -189,18 +154,16 @@ async function setupPlugins(conductor: Conductor): Promise<void> {
|
|
|
189
154
|
|
|
190
155
|
if (!doOnboard) {
|
|
191
156
|
console.log('');
|
|
192
|
-
console.log(
|
|
157
|
+
console.log(` ${D}Skipped. Run: conductor onboard${R}`);
|
|
193
158
|
return;
|
|
194
159
|
}
|
|
195
160
|
|
|
196
|
-
// Dynamically import onboard to avoid circular deps
|
|
197
161
|
const { onboard } = await import('./onboard.js');
|
|
198
162
|
await onboard(conductor);
|
|
199
163
|
}
|
|
200
164
|
|
|
201
|
-
// ── Step
|
|
165
|
+
// ── Step 3: MCP client config ──────────────────────────────────────────────
|
|
202
166
|
|
|
203
|
-
// Config file paths per client
|
|
204
167
|
const MCP_CONFIG_PATHS: Record<string, string> = {
|
|
205
168
|
claude: path.join(
|
|
206
169
|
homedir(),
|
|
@@ -227,8 +190,8 @@ const MCP_CONFIG_PATHS: Record<string, string> = {
|
|
|
227
190
|
};
|
|
228
191
|
|
|
229
192
|
async function writeClientConfig(configPath: string): Promise<void> {
|
|
230
|
-
|
|
231
|
-
const entry = { command: '
|
|
193
|
+
// Use the conductor binary by name — robust across npm reinstalls and upgrades.
|
|
194
|
+
const entry = { command: 'conductor', args: ['mcp', 'start'] };
|
|
232
195
|
|
|
233
196
|
let config: Record<string, unknown> = {};
|
|
234
197
|
try {
|
|
@@ -238,7 +201,6 @@ async function writeClientConfig(configPath: string): Promise<void> {
|
|
|
238
201
|
// File doesn't exist — start fresh
|
|
239
202
|
}
|
|
240
203
|
|
|
241
|
-
// Both Claude Desktop and Cursor use mcpServers; Cline too
|
|
242
204
|
const servers = (config['mcpServers'] ?? {}) as Record<string, unknown>;
|
|
243
205
|
servers['conductor'] = entry;
|
|
244
206
|
config['mcpServers'] = servers;
|
|
@@ -248,9 +210,9 @@ async function writeClientConfig(configPath: string): Promise<void> {
|
|
|
248
210
|
}
|
|
249
211
|
|
|
250
212
|
async function setupMCPClient(conductor: Conductor): Promise<void> {
|
|
251
|
-
stepHeader(
|
|
213
|
+
stepHeader(3, 3, 'Connect your AI client');
|
|
252
214
|
|
|
253
|
-
console.log(
|
|
215
|
+
console.log(` ${D}Conductor will write the MCP server entry into your client's config.${R}`);
|
|
254
216
|
console.log('');
|
|
255
217
|
|
|
256
218
|
const { client } = await inquirer.prompt<{ client: string }>([
|
|
@@ -262,14 +224,15 @@ async function setupMCPClient(conductor: Conductor): Promise<void> {
|
|
|
262
224
|
{ name: 'Claude Desktop', value: 'claude' },
|
|
263
225
|
{ name: 'Cursor', value: 'cursor' },
|
|
264
226
|
{ name: 'Cline (VS Code extension)', value: 'cline' },
|
|
265
|
-
{ name:
|
|
227
|
+
{ name: `${D}Skip — I'll configure manually${R}`, value: 'skip' },
|
|
266
228
|
],
|
|
267
229
|
},
|
|
268
230
|
]);
|
|
269
231
|
|
|
270
232
|
if (client === 'skip') {
|
|
271
233
|
console.log('');
|
|
272
|
-
console.log(
|
|
234
|
+
console.log(` ${D}Skipped. Run: conductor mcp setup${R}`);
|
|
235
|
+
void conductor;
|
|
273
236
|
return;
|
|
274
237
|
}
|
|
275
238
|
|
|
@@ -277,49 +240,38 @@ async function setupMCPClient(conductor: Conductor): Promise<void> {
|
|
|
277
240
|
|
|
278
241
|
try {
|
|
279
242
|
await writeClientConfig(configPath);
|
|
243
|
+
const clientName = client === 'claude' ? 'Claude Desktop' : client === 'cursor' ? 'Cursor' : 'Cline';
|
|
280
244
|
console.log('');
|
|
281
|
-
console.log(
|
|
282
|
-
|
|
283
|
-
` ✓ ${client === 'claude' ? 'Claude Desktop' : client === 'cursor' ? 'Cursor' : 'Cline'} configured`,
|
|
284
|
-
),
|
|
285
|
-
);
|
|
286
|
-
console.log(chalk.dim(` Config: ${configPath}`));
|
|
245
|
+
console.log(` ✓ ${clientName} configured`);
|
|
246
|
+
console.log(` ${D} Config: ${configPath}${R}`);
|
|
287
247
|
} catch (err: unknown) {
|
|
288
248
|
const msg = err instanceof Error ? err.message : String(err);
|
|
289
249
|
console.log('');
|
|
290
|
-
console.log(
|
|
291
|
-
console.log(
|
|
250
|
+
console.log(` ! Could not write config: ${msg}`);
|
|
251
|
+
console.log(` ${D} Run: conductor mcp setup${R}`);
|
|
292
252
|
}
|
|
293
|
-
|
|
294
|
-
// Silence the conductor parameter — used for future expansion
|
|
295
|
-
void conductor;
|
|
296
253
|
}
|
|
297
254
|
|
|
298
255
|
// ── Final instructions ─────────────────────────────────────────────────────
|
|
299
256
|
|
|
300
|
-
function printFinalInstructions(
|
|
257
|
+
function printFinalInstructions(): void {
|
|
258
|
+
const W = 50;
|
|
259
|
+
console.log('');
|
|
260
|
+
hr(W);
|
|
301
261
|
console.log('');
|
|
302
|
-
console.log(
|
|
303
|
-
console.log(
|
|
304
|
-
chalk.bold.white(' ║') +
|
|
305
|
-
chalk.bold.green(" ✓ You're all set, " + (userName || 'there') + '!') +
|
|
306
|
-
' '.repeat(Math.max(0, 24 - (userName || 'there').length)) +
|
|
307
|
-
chalk.bold.white('║'),
|
|
308
|
-
);
|
|
309
|
-
console.log(chalk.bold.white(' ╚══════════════════════════════════════════════╝'));
|
|
262
|
+
console.log(` ${B}You're all set.${R}`);
|
|
310
263
|
console.log('');
|
|
311
|
-
console.log(
|
|
312
|
-
console.log(` ${
|
|
264
|
+
console.log(` Start the MCP server:`);
|
|
265
|
+
console.log(` ${B}conductor mcp start${R}`);
|
|
313
266
|
console.log('');
|
|
314
|
-
console.log(
|
|
315
|
-
console.log(
|
|
267
|
+
console.log(` ${D}Then restart your AI client — Conductor will appear`);
|
|
268
|
+
console.log(` as a connected MCP server with 100+ tools available.${R}`);
|
|
316
269
|
console.log('');
|
|
317
|
-
console.log(
|
|
318
|
-
console.log(`
|
|
319
|
-
console.log(`
|
|
320
|
-
console.log(` ${chalk.cyan('conductor health')} ${chalk.dim('— system health status')}`);
|
|
270
|
+
console.log(` ${D}conductor dashboard — web UI with metrics and audit log${R}`);
|
|
271
|
+
console.log(` ${D}conductor doctor — diagnose issues${R}`);
|
|
272
|
+
console.log(` ${D}conductor health — system health status${R}`);
|
|
321
273
|
console.log('');
|
|
322
|
-
|
|
274
|
+
hr(W);
|
|
323
275
|
console.log('');
|
|
324
276
|
}
|
|
325
277
|
|
|
@@ -330,19 +282,9 @@ export async function init(conductor: Conductor): Promise<void> {
|
|
|
330
282
|
|
|
331
283
|
printBanner();
|
|
332
284
|
|
|
333
|
-
// Step 1 — user name
|
|
334
|
-
await setupUserName(conductor);
|
|
335
|
-
|
|
336
|
-
// Step 2 — AI provider
|
|
337
285
|
await setupAIProvider(conductor);
|
|
338
|
-
|
|
339
|
-
// Step 3 — plugins
|
|
340
286
|
await setupPlugins(conductor);
|
|
341
|
-
|
|
342
|
-
// Step 4 — MCP client
|
|
343
287
|
await setupMCPClient(conductor);
|
|
344
288
|
|
|
345
|
-
|
|
346
|
-
const userName = conductor.getConfig().get<string>('user.name') || '';
|
|
347
|
-
printFinalInstructions(userName);
|
|
289
|
+
printFinalInstructions();
|
|
348
290
|
}
|
|
@@ -3,7 +3,7 @@ import path from 'path';
|
|
|
3
3
|
import { Conductor } from '../../core/conductor.js';
|
|
4
4
|
|
|
5
5
|
const REGISTRY_URLS = [
|
|
6
|
-
'https://raw.githubusercontent.com/
|
|
6
|
+
'https://raw.githubusercontent.com/useconductor/conductor-plugins/main/registry.json',
|
|
7
7
|
'https://conductor.thealxlabs.ca/registry.json',
|
|
8
8
|
];
|
|
9
9
|
const GITHUB_RAW = 'https://raw.githubusercontent.com';
|
|
@@ -22,25 +22,47 @@ interface _PluginEntry {
|
|
|
22
22
|
|
|
23
23
|
// Category groupings for the TUI picker
|
|
24
24
|
const CATEGORIES: Record<string, string[]> = {
|
|
25
|
-
'Developer Tools': [
|
|
26
|
-
|
|
27
|
-
],
|
|
28
|
-
'Communication': ['slack', 'telegram'],
|
|
25
|
+
'Developer Tools': ['shell', 'docker', 'github', 'git', 'github-actions', 'vercel', 'n8n', 'linear', 'jira'],
|
|
26
|
+
Communication: ['slack', 'telegram'],
|
|
29
27
|
'Google Workspace': ['gmail', 'google-calendar', 'google-drive'],
|
|
30
|
-
|
|
28
|
+
Productivity: ['notes', 'memory', 'notion', 'todoist'],
|
|
31
29
|
'Finance & Commerce': ['stripe'],
|
|
32
|
-
|
|
33
|
-
'calculator',
|
|
34
|
-
'
|
|
30
|
+
Utilities: [
|
|
31
|
+
'calculator',
|
|
32
|
+
'colors',
|
|
33
|
+
'crypto',
|
|
34
|
+
'hash',
|
|
35
|
+
'text-tools',
|
|
36
|
+
'timezone',
|
|
37
|
+
'network',
|
|
38
|
+
'url-tools',
|
|
39
|
+
'fun',
|
|
40
|
+
'system',
|
|
41
|
+
'cron',
|
|
42
|
+
'weather',
|
|
43
|
+
'translate',
|
|
35
44
|
],
|
|
36
45
|
'Media & Social': ['spotify', 'x'],
|
|
37
46
|
'Smart Home': ['homekit'],
|
|
38
47
|
};
|
|
39
48
|
|
|
40
49
|
const ZERO_CONFIG_SET = new Set([
|
|
41
|
-
'calculator',
|
|
42
|
-
'
|
|
43
|
-
'
|
|
50
|
+
'calculator',
|
|
51
|
+
'colors',
|
|
52
|
+
'hash',
|
|
53
|
+
'text-tools',
|
|
54
|
+
'timezone',
|
|
55
|
+
'network',
|
|
56
|
+
'url-tools',
|
|
57
|
+
'fun',
|
|
58
|
+
'system',
|
|
59
|
+
'notes',
|
|
60
|
+
'memory',
|
|
61
|
+
'cron',
|
|
62
|
+
'shell',
|
|
63
|
+
'docker',
|
|
64
|
+
'github',
|
|
65
|
+
'translate',
|
|
44
66
|
'weather',
|
|
45
67
|
'crypto',
|
|
46
68
|
]);
|