vibe-weaver 1.0.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/README.md +229 -0
- package/bin/vibe-weaver.js +8 -0
- package/dist/bin/vibe-weaver.js +8 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2018 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
- package/src/agent/deploy.ts +171 -0
- package/src/agent/swarm.ts +167 -0
- package/src/agent/tasks.ts +205 -0
- package/src/auth/index.ts +273 -0
- package/src/config/index.ts +86 -0
- package/src/index.ts +797 -0
- package/src/lib/code-parser.ts +150 -0
- package/src/lib/vibe-api.ts +197 -0
- package/src/mcp/index.ts +273 -0
- package/src/tools/files.ts +279 -0
- package/src/tools/git.ts +498 -0
- package/src/tools/shell.ts +248 -0
- package/tsconfig.json +25 -0
- package/tsup.config.ts +34 -0
- package/vibe-weaver-1.0.0.tgz +0 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth module for Vibe-Weaver CLI
|
|
3
|
+
* Handles API key and GitHub token storage via keytar with plaintext fallback
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
import inquirer from 'inquirer';
|
|
8
|
+
import open from 'open';
|
|
9
|
+
import { loadConfig, saveConfig, getSupabaseUrl, getConfigPath } from '../config/index.js';
|
|
10
|
+
|
|
11
|
+
const SERVICE_NAME = 'vibe-weaver';
|
|
12
|
+
const API_KEY_ACCOUNT = 'api-key';
|
|
13
|
+
const GITHUB_TOKEN_ACCOUNT = 'github-token';
|
|
14
|
+
|
|
15
|
+
// Try to use keytar for secure storage
|
|
16
|
+
let keytar: any = null;
|
|
17
|
+
try {
|
|
18
|
+
keytar = require('keytar');
|
|
19
|
+
} catch {
|
|
20
|
+
// keytar not available, will use plaintext fallback
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getPlaintextPath(): string {
|
|
24
|
+
return getConfigPath();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Try to get a secret from keytar, fall back to config file
|
|
29
|
+
*/
|
|
30
|
+
async function getSecret(account: string): Promise<string | null> {
|
|
31
|
+
if (keytar) {
|
|
32
|
+
try {
|
|
33
|
+
const value = await keytar.getPassword(SERVICE_NAME, account);
|
|
34
|
+
if (value) return value;
|
|
35
|
+
} catch {
|
|
36
|
+
// Keytar failed, fall through to plaintext
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Fallback to config file
|
|
41
|
+
const config = loadConfig();
|
|
42
|
+
if (account === API_KEY_ACCOUNT) return config.apiKey || null;
|
|
43
|
+
if (account === GITHUB_TOKEN_ACCOUNT) return config.githubToken || null;
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Store a secret in keytar, fall back to config file
|
|
49
|
+
*/
|
|
50
|
+
async function setSecret(account: string, value: string): Promise<void> {
|
|
51
|
+
if (keytar) {
|
|
52
|
+
try {
|
|
53
|
+
await keytar.setPassword(SERVICE_NAME, account, value);
|
|
54
|
+
// Also save to config as backup
|
|
55
|
+
const config = loadConfig();
|
|
56
|
+
if (account === API_KEY_ACCOUNT) config.apiKey = value;
|
|
57
|
+
if (account === GITHUB_TOKEN_ACCOUNT) config.githubToken = value;
|
|
58
|
+
saveConfig(config);
|
|
59
|
+
return;
|
|
60
|
+
} catch {
|
|
61
|
+
// Keytar failed, fall through to plaintext
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Fallback to config file
|
|
66
|
+
const config = loadConfig();
|
|
67
|
+
if (account === API_KEY_ACCOUNT) config.apiKey = value;
|
|
68
|
+
if (account === GITHUB_TOKEN_ACCOUNT) config.githubToken = value;
|
|
69
|
+
saveConfig(config);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Remove a secret from keytar and config
|
|
74
|
+
*/
|
|
75
|
+
async function deleteSecret(account: string): Promise<void> {
|
|
76
|
+
if (keytar) {
|
|
77
|
+
try {
|
|
78
|
+
await keytar.deletePassword(SERVICE_NAME, account);
|
|
79
|
+
} catch {
|
|
80
|
+
// Ignore errors
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const config = loadConfig();
|
|
85
|
+
if (account === API_KEY_ACCOUNT) {
|
|
86
|
+
config.apiKey = undefined;
|
|
87
|
+
}
|
|
88
|
+
if (account === GITHUB_TOKEN_ACCOUNT) {
|
|
89
|
+
config.githubToken = undefined;
|
|
90
|
+
}
|
|
91
|
+
saveConfig(config);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Check if user is logged in (has API key)
|
|
96
|
+
*/
|
|
97
|
+
export async function isLoggedIn(): Promise<boolean> {
|
|
98
|
+
const apiKey = await getSecret(API_KEY_ACCOUNT);
|
|
99
|
+
return !!apiKey;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get current API key
|
|
104
|
+
*/
|
|
105
|
+
export async function getApiKey(): Promise<string | null> {
|
|
106
|
+
return getSecret(API_KEY_ACCOUNT);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get current GitHub token
|
|
111
|
+
*/
|
|
112
|
+
export async function getGithubToken(): Promise<string | null> {
|
|
113
|
+
return getSecret(GITHUB_TOKEN_ACCOUNT);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Interactive login - accept API key or open web page
|
|
118
|
+
*/
|
|
119
|
+
export async function login(options: { apiKey?: string; openBrowser?: boolean } = {}): Promise<void> {
|
|
120
|
+
const supabaseUrl = getSupabaseUrl();
|
|
121
|
+
|
|
122
|
+
if (options.apiKey) {
|
|
123
|
+
// Direct API key provided
|
|
124
|
+
await setSecret(API_KEY_ACCOUNT, options.apiKey);
|
|
125
|
+
console.log(chalk.green('✓ API key saved securely'));
|
|
126
|
+
|
|
127
|
+
// Verify the key works
|
|
128
|
+
const userInfo = await getUserInfo();
|
|
129
|
+
if (userInfo) {
|
|
130
|
+
console.log(chalk.green(`✓ Logged in as ${userInfo.email} (${userInfo.plan} plan)`));
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (options.openBrowser) {
|
|
136
|
+
// Open the web page for API key creation
|
|
137
|
+
const apiPageUrl = `${supabaseUrl}/dashboard/api`;
|
|
138
|
+
console.log(chalk.blue('Opening browser to create API key...'));
|
|
139
|
+
await open(apiPageUrl);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Interactive prompt for API key
|
|
143
|
+
const answers = await inquirer.prompt([
|
|
144
|
+
{
|
|
145
|
+
type: 'input',
|
|
146
|
+
name: 'apiKey',
|
|
147
|
+
message: 'Enter your Vibe-Weaver API key (vw_...):',
|
|
148
|
+
validate: (input: string) => {
|
|
149
|
+
if (!input.startsWith('vw_') && input.length < 10) {
|
|
150
|
+
return 'Please enter a valid API key (starts with vw_)';
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
]);
|
|
156
|
+
|
|
157
|
+
await setSecret(API_KEY_ACCOUNT, answers.apiKey);
|
|
158
|
+
console.log(chalk.green('✓ API key saved securely'));
|
|
159
|
+
|
|
160
|
+
// Verify the key works
|
|
161
|
+
const userInfo = await getUserInfo();
|
|
162
|
+
if (userInfo) {
|
|
163
|
+
console.log(chalk.green(`✓ Logged in as ${userInfo.email} (${userInfo.plan} plan)`));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Login with GitHub token
|
|
169
|
+
*/
|
|
170
|
+
export async function loginGithub(token?: string): Promise<void> {
|
|
171
|
+
if (!token) {
|
|
172
|
+
const answers = await inquirer.prompt([
|
|
173
|
+
{
|
|
174
|
+
type: 'password',
|
|
175
|
+
name: 'token',
|
|
176
|
+
message: 'Enter your GitHub Personal Access Token:',
|
|
177
|
+
validate: (input: string) => {
|
|
178
|
+
if (input.length < 10) {
|
|
179
|
+
return 'Please enter a valid GitHub token';
|
|
180
|
+
}
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
]);
|
|
185
|
+
token = answers.token;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
await setSecret(GITHUB_TOKEN_ACCOUNT, token);
|
|
189
|
+
console.log(chalk.green('✓ GitHub token saved securely'));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Logout - clear all stored credentials
|
|
194
|
+
*/
|
|
195
|
+
export async function logout(): Promise<void> {
|
|
196
|
+
await deleteSecret(API_KEY_ACCOUNT);
|
|
197
|
+
await deleteSecret(GITHUB_TOKEN_ACCOUNT);
|
|
198
|
+
console.log(chalk.green('✓ Logged out successfully'));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Get user info from the API
|
|
203
|
+
*/
|
|
204
|
+
export async function getUserInfo(): Promise<{ email: string; plan: string; credits: number } | null> {
|
|
205
|
+
const apiKey = await getApiKey();
|
|
206
|
+
if (!apiKey) return null;
|
|
207
|
+
|
|
208
|
+
const supabaseUrl = getSupabaseUrl();
|
|
209
|
+
|
|
210
|
+
try {
|
|
211
|
+
const response = await fetch(`${supabaseUrl}/rest/v1/profiles?select=email,plan,credits`, {
|
|
212
|
+
headers: {
|
|
213
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
214
|
+
'apikey': `${supabaseUrl}/rest/v1/apikey`
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (response.ok) {
|
|
219
|
+
const data = await response.json();
|
|
220
|
+
if (Array.isArray(data) && data.length > 0) {
|
|
221
|
+
return {
|
|
222
|
+
email: data[0].email || 'Unknown',
|
|
223
|
+
plan: data[0].plan || 'free',
|
|
224
|
+
credits: data[0].credits || 0
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.warn('Warning: Could not fetch user info:', error);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Show current user info (whoami)
|
|
237
|
+
*/
|
|
238
|
+
export async function whoami(): Promise<void> {
|
|
239
|
+
const apiKey = await getApiKey();
|
|
240
|
+
if (!apiKey) {
|
|
241
|
+
console.log(chalk.yellow('Not logged in. Run "vibe-weaver login" first.'));
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const userInfo = await getUserInfo();
|
|
246
|
+
if (userInfo) {
|
|
247
|
+
console.log(chalk.bold('Logged in as:'));
|
|
248
|
+
console.log(` Email: ${userInfo.email}`);
|
|
249
|
+
console.log(` Plan: ${chalk.cyan(userInfo.plan)}`);
|
|
250
|
+
console.log(` Credits: ${chalk.green(userInfo.credits.toString())}`);
|
|
251
|
+
} else {
|
|
252
|
+
console.log(chalk.yellow('API key is set but could not fetch user info.'));
|
|
253
|
+
console.log(` API Key: ${chalk.grey(apiKey.substring(0, 10))}...`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const githubToken = await getGithubToken();
|
|
257
|
+
if (githubToken) {
|
|
258
|
+
console.log(chalk.green('✓ GitHub token configured'));
|
|
259
|
+
} else {
|
|
260
|
+
console.log(chalk.grey(' GitHub: Not configured (run "vibe-weaver login --github" to add)'));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export default {
|
|
265
|
+
login,
|
|
266
|
+
logout,
|
|
267
|
+
whoami,
|
|
268
|
+
loginGithub,
|
|
269
|
+
isLoggedIn,
|
|
270
|
+
getApiKey,
|
|
271
|
+
getGithubToken,
|
|
272
|
+
getUserInfo
|
|
273
|
+
};
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration module for Vibe-Weaver CLI
|
|
3
|
+
* Manages config storage in ~/.vibe-weaver/config.json
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import envPaths from 'env-paths';
|
|
7
|
+
import fs from 'fs';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import os from 'os';
|
|
10
|
+
|
|
11
|
+
export interface CLIConfig {
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
githubToken?: string;
|
|
14
|
+
supabaseUrl?: string;
|
|
15
|
+
defaultPlatform?: string;
|
|
16
|
+
defaultMode?: string;
|
|
17
|
+
autoApprove?: boolean;
|
|
18
|
+
autoDeploy?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface UserInfo {
|
|
22
|
+
email?: string;
|
|
23
|
+
plan?: string;
|
|
24
|
+
credits?: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const CONFIG_FILE_NAME = 'config.json';
|
|
28
|
+
|
|
29
|
+
function getConfigDir(): string {
|
|
30
|
+
const paths = envPaths('vibe-weaver', { suffix: '' });
|
|
31
|
+
if (!fs.existsSync(paths.config)) {
|
|
32
|
+
fs.mkdirSync(paths.config, { recursive: true });
|
|
33
|
+
}
|
|
34
|
+
// Set restrictive permissions
|
|
35
|
+
fs.chmodSync(paths.config, 0o700);
|
|
36
|
+
return paths.config;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getConfigPath(): string {
|
|
40
|
+
return path.join(getConfigDir(), CONFIG_FILE_NAME);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function loadConfig(): CLIConfig {
|
|
44
|
+
const configPath = getConfigPath();
|
|
45
|
+
try {
|
|
46
|
+
if (fs.existsSync(configPath)) {
|
|
47
|
+
const data = fs.readFileSync(configPath, 'utf-8');
|
|
48
|
+
return JSON.parse(data);
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.warn('Warning: Failed to load config:', error);
|
|
52
|
+
}
|
|
53
|
+
return {};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function saveConfig(config: CLIConfig): void {
|
|
57
|
+
const configPath = getConfigPath();
|
|
58
|
+
try {
|
|
59
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), {
|
|
60
|
+
mode: 0o600
|
|
61
|
+
});
|
|
62
|
+
} catch (error) {
|
|
63
|
+
throw new Error(`Failed to save config: ${error}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function clearConfig(): void {
|
|
68
|
+
const configPath = getConfigPath();
|
|
69
|
+
try {
|
|
70
|
+
if (fs.existsSync(configPath)) {
|
|
71
|
+
fs.unlinkSync(configPath);
|
|
72
|
+
}
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw new Error(`Failed to clear config: ${error}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getDefaultSupabaseUrl(): string {
|
|
79
|
+
// Default to vibe-weaver's Supabase instance
|
|
80
|
+
return process.env.VW_SUPABASE_URL || 'https://mmtsoqelnsjleusqyknc.supabase.co';
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function getSupabaseUrl(): string {
|
|
84
|
+
const config = loadConfig();
|
|
85
|
+
return config.supabaseUrl || getDefaultSupabaseUrl();
|
|
86
|
+
}
|