delimit-cli 1.0.0 ā 2.1.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/workflows/api-governance.yml +43 -0
- package/README.md +70 -113
- package/adapters/codex-skill.js +87 -0
- package/adapters/cursor-extension.js +190 -0
- package/adapters/gemini-action.js +93 -0
- package/adapters/openai-function.js +112 -0
- package/adapters/xai-plugin.js +151 -0
- package/bin/delimit-cli.js +921 -0
- package/bin/delimit.js +237 -1
- package/delimit.yml +19 -0
- package/hooks/evidence-status.sh +12 -0
- package/hooks/git/commit-msg +4 -0
- package/hooks/git/pre-commit +4 -0
- package/hooks/git/pre-push +4 -0
- package/hooks/install-hooks.sh +583 -0
- package/hooks/message-auth-hook.js +9 -0
- package/hooks/message-governance-hook.js +9 -0
- package/hooks/models/claude-post.js +4 -0
- package/hooks/models/claude-pre.js +4 -0
- package/hooks/models/codex-post.js +4 -0
- package/hooks/models/codex-pre.js +4 -0
- package/hooks/models/cursor-post.js +4 -0
- package/hooks/models/cursor-pre.js +4 -0
- package/hooks/models/gemini-post.js +4 -0
- package/hooks/models/gemini-pre.js +4 -0
- package/hooks/models/openai-post.js +4 -0
- package/hooks/models/openai-pre.js +4 -0
- package/hooks/models/windsurf-post.js +4 -0
- package/hooks/models/windsurf-pre.js +4 -0
- package/hooks/models/xai-post.js +4 -0
- package/hooks/models/xai-pre.js +4 -0
- package/hooks/post-bash-hook.js +13 -0
- package/hooks/post-mcp-hook.js +13 -0
- package/hooks/post-response-hook.js +4 -0
- package/hooks/post-tool-hook.js +126 -0
- package/hooks/post-write-hook.js +13 -0
- package/hooks/pre-bash-hook.js +30 -0
- package/hooks/pre-mcp-hook.js +13 -0
- package/hooks/pre-read-hook.js +13 -0
- package/hooks/pre-search-hook.js +13 -0
- package/hooks/pre-submit-hook.js +4 -0
- package/hooks/pre-task-hook.js +13 -0
- package/hooks/pre-tool-hook.js +121 -0
- package/hooks/pre-web-hook.js +13 -0
- package/hooks/pre-write-hook.js +31 -0
- package/hooks/test-hooks.sh +12 -0
- package/hooks/update-delimit.sh +6 -0
- package/lib/agent.js +509 -0
- package/lib/api-engine.js +156 -0
- package/lib/auth-setup.js +891 -0
- package/lib/decision-engine.js +474 -0
- package/lib/hooks-installer.js +416 -0
- package/lib/platform-adapters.js +353 -0
- package/lib/proxy-handler.js +114 -0
- package/package.json +38 -30
- package/scripts/infect.js +128 -0
- package/test-decision-engine.js +181 -0
- package/test-hook.js +27 -0
- package/dist/commands/validate.d.ts +0 -2
- package/dist/commands/validate.d.ts.map +0 -1
- package/dist/commands/validate.js +0 -106
- package/dist/commands/validate.js.map +0 -1
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -71
- package/dist/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -39
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +0 -1
- package/dist/utils/api.d.ts +0 -3
- package/dist/utils/api.d.ts.map +0 -1
- package/dist/utils/api.js +0 -64
- package/dist/utils/api.js.map +0 -1
- package/dist/utils/file.d.ts +0 -7
- package/dist/utils/file.d.ts.map +0 -1
- package/dist/utils/file.js +0 -69
- package/dist/utils/file.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -14
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -28
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/masker.d.ts +0 -14
- package/dist/utils/masker.d.ts.map +0 -1
- package/dist/utils/masker.js +0 -89
- package/dist/utils/masker.js.map +0 -1
- package/src/commands/validate.ts +0 -150
- package/src/index.ts +0 -80
- package/src/types/index.ts +0 -41
- package/src/utils/api.ts +0 -68
- package/src/utils/file.ts +0 -71
- package/src/utils/logger.ts +0 -27
- package/src/utils/masker.ts +0 -101
- package/test-sensitive.yaml +0 -109
- package/tsconfig.json +0 -23
|
@@ -0,0 +1,891 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Delimit Authentication Setup
|
|
5
|
+
* Handles secure credential collection and storage for new users
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const readline = require('readline');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const crypto = require('crypto');
|
|
12
|
+
const { execSync } = require('child_process');
|
|
13
|
+
const chalk = require('chalk');
|
|
14
|
+
|
|
15
|
+
class DelimitAuthSetup {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.configDir = path.join(process.env.HOME, '.delimit');
|
|
18
|
+
this.credentialsFile = path.join(this.configDir, 'credentials.enc');
|
|
19
|
+
this.authConfigFile = path.join(this.configDir, 'auth.json');
|
|
20
|
+
|
|
21
|
+
// Ensure config directory exists
|
|
22
|
+
if (!fs.existsSync(this.configDir)) {
|
|
23
|
+
fs.mkdirSync(this.configDir, { recursive: true, mode: 0o700 });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async setup(options = {}) {
|
|
28
|
+
console.log(chalk.blue.bold('\nš Delimit Authentication Setup\n'));
|
|
29
|
+
|
|
30
|
+
const credentials = {};
|
|
31
|
+
|
|
32
|
+
// Check for existing credentials
|
|
33
|
+
if (fs.existsSync(this.credentialsFile) && !options.force) {
|
|
34
|
+
const overwrite = await this.prompt(
|
|
35
|
+
'Existing credentials found. Overwrite? (y/N): ',
|
|
36
|
+
'n'
|
|
37
|
+
);
|
|
38
|
+
if (overwrite.toLowerCase() !== 'y') {
|
|
39
|
+
console.log(chalk.yellow('Using existing credentials.'));
|
|
40
|
+
return this.loadCredentials();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Collect credentials based on what's installed
|
|
45
|
+
console.log(chalk.cyan('\nš Detecting installed tools...\n'));
|
|
46
|
+
|
|
47
|
+
// GitHub credentials
|
|
48
|
+
if (this.isInstalled('git')) {
|
|
49
|
+
console.log(chalk.yellow('GitHub Configuration:'));
|
|
50
|
+
credentials.github = await this.setupGitHub();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// AI Tool credentials
|
|
54
|
+
const aiTools = {
|
|
55
|
+
'claude': 'Anthropic Claude',
|
|
56
|
+
'openai': 'OpenAI GPT',
|
|
57
|
+
'gemini': 'Google Gemini',
|
|
58
|
+
'codex': 'GitHub Copilot'
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
for (const [tool, name] of Object.entries(aiTools)) {
|
|
62
|
+
if (this.isInstalled(tool) || options.all) {
|
|
63
|
+
const setup = await this.prompt(
|
|
64
|
+
`\nSetup ${name}? (y/N): `,
|
|
65
|
+
'n'
|
|
66
|
+
);
|
|
67
|
+
if (setup.toLowerCase() === 'y') {
|
|
68
|
+
console.log(chalk.yellow(`\n${name} Configuration:`));
|
|
69
|
+
credentials[tool] = await this.setupAITool(tool, name);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// MCP Server credentials
|
|
75
|
+
if (this.isInstalled('claude') || options.all) {
|
|
76
|
+
const setupMcp = await this.prompt(
|
|
77
|
+
'\nSetup MCP server authentication? (y/N): ',
|
|
78
|
+
'n'
|
|
79
|
+
);
|
|
80
|
+
if (setupMcp.toLowerCase() === 'y') {
|
|
81
|
+
console.log(chalk.yellow('\nMCP Server Configuration:'));
|
|
82
|
+
credentials.mcp = await this.setupMCP();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Cloud Provider credentials
|
|
87
|
+
const setupCloud = await this.prompt(
|
|
88
|
+
'\nSetup cloud provider credentials? (y/N): ',
|
|
89
|
+
'n'
|
|
90
|
+
);
|
|
91
|
+
if (setupCloud.toLowerCase() === 'y') {
|
|
92
|
+
console.log(chalk.yellow('\nCloud Provider Configuration:'));
|
|
93
|
+
credentials.cloud = await this.setupCloudProviders();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Database credentials
|
|
97
|
+
const setupDb = await this.prompt(
|
|
98
|
+
'\nSetup database connections? (y/N): ',
|
|
99
|
+
'n'
|
|
100
|
+
);
|
|
101
|
+
if (setupDb.toLowerCase() === 'y') {
|
|
102
|
+
console.log(chalk.yellow('\nDatabase Configuration:'));
|
|
103
|
+
credentials.databases = await this.setupDatabases();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Container registries
|
|
107
|
+
const setupRegistry = await this.prompt(
|
|
108
|
+
'\nSetup container registries? (y/N): ',
|
|
109
|
+
'n'
|
|
110
|
+
);
|
|
111
|
+
if (setupRegistry.toLowerCase() === 'y') {
|
|
112
|
+
console.log(chalk.yellow('\nContainer Registry Configuration:'));
|
|
113
|
+
credentials.registries = await this.setupRegistries();
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Package managers
|
|
117
|
+
const setupPackages = await this.prompt(
|
|
118
|
+
'\nSetup package manager credentials? (y/N): ',
|
|
119
|
+
'n'
|
|
120
|
+
);
|
|
121
|
+
if (setupPackages.toLowerCase() === 'y') {
|
|
122
|
+
console.log(chalk.yellow('\nPackage Manager Configuration:'));
|
|
123
|
+
credentials.packages = await this.setupPackageManagers();
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Monitoring and observability
|
|
127
|
+
const setupMonitoring = await this.prompt(
|
|
128
|
+
'\nSetup monitoring services? (y/N): ',
|
|
129
|
+
'n'
|
|
130
|
+
);
|
|
131
|
+
if (setupMonitoring.toLowerCase() === 'y') {
|
|
132
|
+
console.log(chalk.yellow('\nMonitoring Configuration:'));
|
|
133
|
+
credentials.monitoring = await this.setupMonitoring();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Organization settings
|
|
137
|
+
const setupOrg = await this.prompt(
|
|
138
|
+
'\nSetup organization policies? (y/N): ',
|
|
139
|
+
'n'
|
|
140
|
+
);
|
|
141
|
+
if (setupOrg.toLowerCase() === 'y') {
|
|
142
|
+
console.log(chalk.yellow('\nOrganization Configuration:'));
|
|
143
|
+
credentials.organization = await this.setupOrganization();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Save credentials securely
|
|
147
|
+
await this.saveCredentials(credentials);
|
|
148
|
+
|
|
149
|
+
// Configure Git globally if GitHub was setup
|
|
150
|
+
if (credentials.github) {
|
|
151
|
+
await this.configureGit(credentials.github);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Create environment file
|
|
155
|
+
await this.createEnvironmentFile(credentials);
|
|
156
|
+
|
|
157
|
+
console.log(chalk.green.bold('\nā
Authentication setup complete!\n'));
|
|
158
|
+
this.printSummary(credentials);
|
|
159
|
+
|
|
160
|
+
return credentials;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async setupGitHub() {
|
|
164
|
+
const github = {};
|
|
165
|
+
|
|
166
|
+
// Check for existing Git config
|
|
167
|
+
try {
|
|
168
|
+
github.username = execSync('git config --global user.name', { encoding: 'utf8' }).trim();
|
|
169
|
+
github.email = execSync('git config --global user.email', { encoding: 'utf8' }).trim();
|
|
170
|
+
console.log(chalk.gray(` Found: ${github.username} <${github.email}>`));
|
|
171
|
+
} catch (e) {
|
|
172
|
+
github.username = await this.prompt(' GitHub username: ');
|
|
173
|
+
github.email = await this.prompt(' GitHub email: ');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// GitHub Personal Access Token
|
|
177
|
+
const needToken = await this.prompt(' Add GitHub Personal Access Token? (y/N): ', 'n');
|
|
178
|
+
if (needToken.toLowerCase() === 'y') {
|
|
179
|
+
github.token = await this.promptSecret(' GitHub PAT: ');
|
|
180
|
+
|
|
181
|
+
// Token scopes
|
|
182
|
+
console.log(chalk.gray('\n Required scopes for full functionality:'));
|
|
183
|
+
console.log(chalk.gray(' ⢠repo (Full control of private repositories)'));
|
|
184
|
+
console.log(chalk.gray(' ⢠workflow (Update GitHub Action workflows)'));
|
|
185
|
+
console.log(chalk.gray(' ⢠write:packages (Upload packages to GitHub Package Registry)'));
|
|
186
|
+
console.log(chalk.gray(' ⢠read:org (Read org and team membership)'));
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// SSH Key setup
|
|
190
|
+
const needSsh = await this.prompt(' Setup SSH key for GitHub? (y/N): ', 'n');
|
|
191
|
+
if (needSsh.toLowerCase() === 'y') {
|
|
192
|
+
github.sshKey = await this.setupSSHKey(github.email);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// GitHub CLI token (for gh command)
|
|
196
|
+
if (this.isInstalled('gh')) {
|
|
197
|
+
const needGhToken = await this.prompt(' Setup GitHub CLI (gh) authentication? (y/N): ', 'n');
|
|
198
|
+
if (needGhToken.toLowerCase() === 'y') {
|
|
199
|
+
github.ghToken = await this.promptSecret(' GitHub CLI token: ');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return github;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async setupAITool(tool, name) {
|
|
207
|
+
const config = {};
|
|
208
|
+
|
|
209
|
+
switch (tool) {
|
|
210
|
+
case 'claude':
|
|
211
|
+
config.apiKey = await this.promptSecret(` Anthropic API key: `);
|
|
212
|
+
config.model = await this.prompt(` Default model (claude-3-opus-20240229): `, 'claude-3-opus-20240229');
|
|
213
|
+
break;
|
|
214
|
+
|
|
215
|
+
case 'openai':
|
|
216
|
+
config.apiKey = await this.promptSecret(` OpenAI API key: `);
|
|
217
|
+
config.organization = await this.prompt(` Organization ID (optional): `, '');
|
|
218
|
+
config.model = await this.prompt(` Default model (gpt-4): `, 'gpt-4');
|
|
219
|
+
break;
|
|
220
|
+
|
|
221
|
+
case 'gemini':
|
|
222
|
+
config.apiKey = await this.promptSecret(` Google AI API key: `);
|
|
223
|
+
config.projectId = await this.prompt(` GCP Project ID (optional): `, '');
|
|
224
|
+
break;
|
|
225
|
+
|
|
226
|
+
case 'codex':
|
|
227
|
+
config.token = await this.promptSecret(` GitHub Copilot token: `);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Rate limits and safety
|
|
232
|
+
config.maxTokens = await this.prompt(` Max tokens per request (4000): `, '4000');
|
|
233
|
+
config.rateLimit = await this.prompt(` Requests per minute (10): `, '10');
|
|
234
|
+
|
|
235
|
+
return config;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
async setupMCP() {
|
|
239
|
+
const mcp = {};
|
|
240
|
+
|
|
241
|
+
console.log(chalk.gray('\n MCP servers can require authentication for:'));
|
|
242
|
+
console.log(chalk.gray(' ⢠Database connections'));
|
|
243
|
+
console.log(chalk.gray(' ⢠API endpoints'));
|
|
244
|
+
console.log(chalk.gray(' ⢠Cloud services'));
|
|
245
|
+
|
|
246
|
+
const servers = [
|
|
247
|
+
'delimit-gov',
|
|
248
|
+
'delimit-mem',
|
|
249
|
+
'delimit-os',
|
|
250
|
+
'delimit-vault',
|
|
251
|
+
'delimit-deploy'
|
|
252
|
+
];
|
|
253
|
+
|
|
254
|
+
for (const server of servers) {
|
|
255
|
+
const needAuth = await this.prompt(`\n Setup ${server} authentication? (y/N): `, 'n');
|
|
256
|
+
if (needAuth.toLowerCase() === 'y') {
|
|
257
|
+
mcp[server] = {};
|
|
258
|
+
|
|
259
|
+
// Check what type of auth is needed
|
|
260
|
+
const authType = await this.prompt(' Auth type (token/credentials/oauth): ', 'token');
|
|
261
|
+
|
|
262
|
+
switch (authType) {
|
|
263
|
+
case 'token':
|
|
264
|
+
mcp[server].token = await this.promptSecret(' API token: ');
|
|
265
|
+
break;
|
|
266
|
+
case 'credentials':
|
|
267
|
+
mcp[server].username = await this.prompt(' Username: ');
|
|
268
|
+
mcp[server].password = await this.promptSecret(' Password: ');
|
|
269
|
+
break;
|
|
270
|
+
case 'oauth':
|
|
271
|
+
mcp[server].clientId = await this.prompt(' Client ID: ');
|
|
272
|
+
mcp[server].clientSecret = await this.promptSecret(' Client secret: ');
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
mcp[server].endpoint = await this.prompt(' Endpoint URL (http://localhost:8080): ', 'http://localhost:8080');
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return mcp;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async setupCloudProviders() {
|
|
284
|
+
const cloud = {};
|
|
285
|
+
|
|
286
|
+
// AWS
|
|
287
|
+
const setupAws = await this.prompt(' Setup AWS credentials? (y/N): ', 'n');
|
|
288
|
+
if (setupAws.toLowerCase() === 'y') {
|
|
289
|
+
cloud.aws = {};
|
|
290
|
+
cloud.aws.accessKeyId = await this.prompt(' AWS Access Key ID: ');
|
|
291
|
+
cloud.aws.secretAccessKey = await this.promptSecret(' AWS Secret Access Key: ');
|
|
292
|
+
cloud.aws.region = await this.prompt(' Default region (us-east-1): ', 'us-east-1');
|
|
293
|
+
|
|
294
|
+
const needMfa = await this.prompt(' MFA device ARN (optional): ', '');
|
|
295
|
+
if (needMfa) cloud.aws.mfaDevice = needMfa;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Google Cloud
|
|
299
|
+
const setupGcp = await this.prompt(' Setup Google Cloud credentials? (y/N): ', 'n');
|
|
300
|
+
if (setupGcp.toLowerCase() === 'y') {
|
|
301
|
+
cloud.gcp = {};
|
|
302
|
+
cloud.gcp.projectId = await this.prompt(' GCP Project ID: ');
|
|
303
|
+
|
|
304
|
+
const keyFile = await this.prompt(' Service account key file path: ');
|
|
305
|
+
if (keyFile && fs.existsSync(keyFile)) {
|
|
306
|
+
cloud.gcp.keyFile = keyFile;
|
|
307
|
+
} else {
|
|
308
|
+
cloud.gcp.clientEmail = await this.prompt(' Service account email: ');
|
|
309
|
+
cloud.gcp.privateKey = await this.promptSecret(' Private key (paste entire key): ');
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Azure
|
|
314
|
+
const setupAzure = await this.prompt(' Setup Azure credentials? (y/N): ', 'n');
|
|
315
|
+
if (setupAzure.toLowerCase() === 'y') {
|
|
316
|
+
cloud.azure = {};
|
|
317
|
+
cloud.azure.tenantId = await this.prompt(' Azure Tenant ID: ');
|
|
318
|
+
cloud.azure.clientId = await this.prompt(' Client ID: ');
|
|
319
|
+
cloud.azure.clientSecret = await this.promptSecret(' Client Secret: ');
|
|
320
|
+
cloud.azure.subscriptionId = await this.prompt(' Subscription ID: ');
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// DigitalOcean
|
|
324
|
+
const setupDo = await this.prompt(' Setup DigitalOcean credentials? (y/N): ', 'n');
|
|
325
|
+
if (setupDo.toLowerCase() === 'y') {
|
|
326
|
+
cloud.digitalocean = {};
|
|
327
|
+
cloud.digitalocean.token = await this.promptSecret(' DigitalOcean API Token: ');
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Cloudflare
|
|
331
|
+
const setupCf = await this.prompt(' Setup Cloudflare credentials? (y/N): ', 'n');
|
|
332
|
+
if (setupCf.toLowerCase() === 'y') {
|
|
333
|
+
cloud.cloudflare = {};
|
|
334
|
+
cloud.cloudflare.email = await this.prompt(' Cloudflare email: ');
|
|
335
|
+
cloud.cloudflare.apiKey = await this.promptSecret(' Global API Key: ');
|
|
336
|
+
cloud.cloudflare.zoneId = await this.prompt(' Zone ID (optional): ', '');
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return cloud;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
async setupDatabases() {
|
|
343
|
+
const databases = {};
|
|
344
|
+
|
|
345
|
+
// PostgreSQL
|
|
346
|
+
const setupPg = await this.prompt(' Setup PostgreSQL? (y/N): ', 'n');
|
|
347
|
+
if (setupPg.toLowerCase() === 'y') {
|
|
348
|
+
databases.postgresql = {};
|
|
349
|
+
databases.postgresql.host = await this.prompt(' Host (localhost): ', 'localhost');
|
|
350
|
+
databases.postgresql.port = await this.prompt(' Port (5432): ', '5432');
|
|
351
|
+
databases.postgresql.database = await this.prompt(' Database name: ');
|
|
352
|
+
databases.postgresql.username = await this.prompt(' Username: ');
|
|
353
|
+
databases.postgresql.password = await this.promptSecret(' Password: ');
|
|
354
|
+
databases.postgresql.ssl = await this.prompt(' Use SSL? (Y/n): ', 'y');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// MySQL
|
|
358
|
+
const setupMysql = await this.prompt(' Setup MySQL? (y/N): ', 'n');
|
|
359
|
+
if (setupMysql.toLowerCase() === 'y') {
|
|
360
|
+
databases.mysql = {};
|
|
361
|
+
databases.mysql.host = await this.prompt(' Host (localhost): ', 'localhost');
|
|
362
|
+
databases.mysql.port = await this.prompt(' Port (3306): ', '3306');
|
|
363
|
+
databases.mysql.database = await this.prompt(' Database name: ');
|
|
364
|
+
databases.mysql.username = await this.prompt(' Username: ');
|
|
365
|
+
databases.mysql.password = await this.promptSecret(' Password: ');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// MongoDB
|
|
369
|
+
const setupMongo = await this.prompt(' Setup MongoDB? (y/N): ', 'n');
|
|
370
|
+
if (setupMongo.toLowerCase() === 'y') {
|
|
371
|
+
databases.mongodb = {};
|
|
372
|
+
const useUri = await this.prompt(' Use connection URI? (Y/n): ', 'y');
|
|
373
|
+
if (useUri.toLowerCase() === 'y') {
|
|
374
|
+
databases.mongodb.uri = await this.promptSecret(' MongoDB URI: ');
|
|
375
|
+
} else {
|
|
376
|
+
databases.mongodb.host = await this.prompt(' Host (localhost): ', 'localhost');
|
|
377
|
+
databases.mongodb.port = await this.prompt(' Port (27017): ', '27017');
|
|
378
|
+
databases.mongodb.database = await this.prompt(' Database name: ');
|
|
379
|
+
databases.mongodb.username = await this.prompt(' Username: ');
|
|
380
|
+
databases.mongodb.password = await this.promptSecret(' Password: ');
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Redis
|
|
385
|
+
const setupRedis = await this.prompt(' Setup Redis? (y/N): ', 'n');
|
|
386
|
+
if (setupRedis.toLowerCase() === 'y') {
|
|
387
|
+
databases.redis = {};
|
|
388
|
+
databases.redis.host = await this.prompt(' Host (localhost): ', 'localhost');
|
|
389
|
+
databases.redis.port = await this.prompt(' Port (6379): ', '6379');
|
|
390
|
+
databases.redis.password = await this.promptSecret(' Password (optional): ');
|
|
391
|
+
databases.redis.db = await this.prompt(' Database number (0): ', '0');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return databases;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
async setupRegistries() {
|
|
398
|
+
const registries = {};
|
|
399
|
+
|
|
400
|
+
// Docker Hub
|
|
401
|
+
const setupDocker = await this.prompt(' Setup Docker Hub? (y/N): ', 'n');
|
|
402
|
+
if (setupDocker.toLowerCase() === 'y') {
|
|
403
|
+
registries.dockerhub = {};
|
|
404
|
+
registries.dockerhub.username = await this.prompt(' Docker Hub username: ');
|
|
405
|
+
registries.dockerhub.password = await this.promptSecret(' Docker Hub password: ');
|
|
406
|
+
registries.dockerhub.email = await this.prompt(' Email: ');
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// GitHub Container Registry
|
|
410
|
+
const setupGhcr = await this.prompt(' Setup GitHub Container Registry? (y/N): ', 'n');
|
|
411
|
+
if (setupGhcr.toLowerCase() === 'y') {
|
|
412
|
+
registries.ghcr = {};
|
|
413
|
+
registries.ghcr.username = await this.prompt(' GitHub username: ');
|
|
414
|
+
registries.ghcr.token = await this.promptSecret(' Personal Access Token: ');
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// AWS ECR
|
|
418
|
+
const setupEcr = await this.prompt(' Setup AWS ECR? (y/N): ', 'n');
|
|
419
|
+
if (setupEcr.toLowerCase() === 'y') {
|
|
420
|
+
registries.ecr = {};
|
|
421
|
+
registries.ecr.region = await this.prompt(' AWS Region: ');
|
|
422
|
+
registries.ecr.registryId = await this.prompt(' Registry ID: ');
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Google Artifact Registry
|
|
426
|
+
const setupGar = await this.prompt(' Setup Google Artifact Registry? (y/N): ', 'n');
|
|
427
|
+
if (setupGar.toLowerCase() === 'y') {
|
|
428
|
+
registries.gar = {};
|
|
429
|
+
registries.gar.location = await this.prompt(' Location: ');
|
|
430
|
+
registries.gar.repository = await this.prompt(' Repository name: ');
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Private registry
|
|
434
|
+
const setupPrivate = await this.prompt(' Setup private registry? (y/N): ', 'n');
|
|
435
|
+
if (setupPrivate.toLowerCase() === 'y') {
|
|
436
|
+
registries.private = {};
|
|
437
|
+
registries.private.url = await this.prompt(' Registry URL: ');
|
|
438
|
+
registries.private.username = await this.prompt(' Username: ');
|
|
439
|
+
registries.private.password = await this.promptSecret(' Password: ');
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return registries;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
async setupPackageManagers() {
|
|
446
|
+
const packages = {};
|
|
447
|
+
|
|
448
|
+
// NPM
|
|
449
|
+
const setupNpm = await this.prompt(' Setup NPM registry? (y/N): ', 'n');
|
|
450
|
+
if (setupNpm.toLowerCase() === 'y') {
|
|
451
|
+
packages.npm = {};
|
|
452
|
+
packages.npm.registry = await this.prompt(' Registry URL (https://registry.npmjs.org/): ', 'https://registry.npmjs.org/');
|
|
453
|
+
packages.npm.token = await this.promptSecret(' Auth token: ');
|
|
454
|
+
|
|
455
|
+
const needScope = await this.prompt(' Scoped packages (@org)? (y/N): ', 'n');
|
|
456
|
+
if (needScope.toLowerCase() === 'y') {
|
|
457
|
+
packages.npm.scope = await this.prompt(' Scope name: ');
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// PyPI
|
|
462
|
+
const setupPypi = await this.prompt(' Setup PyPI? (y/N): ', 'n');
|
|
463
|
+
if (setupPypi.toLowerCase() === 'y') {
|
|
464
|
+
packages.pypi = {};
|
|
465
|
+
packages.pypi.username = await this.prompt(' PyPI username: ');
|
|
466
|
+
packages.pypi.password = await this.promptSecret(' PyPI password: ');
|
|
467
|
+
packages.pypi.repository = await this.prompt(' Repository (pypi): ', 'pypi');
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Maven
|
|
471
|
+
const setupMaven = await this.prompt(' Setup Maven? (y/N): ', 'n');
|
|
472
|
+
if (setupMaven.toLowerCase() === 'y') {
|
|
473
|
+
packages.maven = {};
|
|
474
|
+
packages.maven.repository = await this.prompt(' Repository URL: ');
|
|
475
|
+
packages.maven.username = await this.prompt(' Username: ');
|
|
476
|
+
packages.maven.password = await this.promptSecret(' Password: ');
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// RubyGems
|
|
480
|
+
const setupGem = await this.prompt(' Setup RubyGems? (y/N): ', 'n');
|
|
481
|
+
if (setupGem.toLowerCase() === 'y') {
|
|
482
|
+
packages.rubygems = {};
|
|
483
|
+
packages.rubygems.apiKey = await this.promptSecret(' API Key: ');
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Cargo (Rust)
|
|
487
|
+
const setupCargo = await this.prompt(' Setup Cargo? (y/N): ', 'n');
|
|
488
|
+
if (setupCargo.toLowerCase() === 'y') {
|
|
489
|
+
packages.cargo = {};
|
|
490
|
+
packages.cargo.token = await this.promptSecret(' Crates.io token: ');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return packages;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async setupMonitoring() {
|
|
497
|
+
const monitoring = {};
|
|
498
|
+
|
|
499
|
+
// Datadog
|
|
500
|
+
const setupDatadog = await this.prompt(' Setup Datadog? (y/N): ', 'n');
|
|
501
|
+
if (setupDatadog.toLowerCase() === 'y') {
|
|
502
|
+
monitoring.datadog = {};
|
|
503
|
+
monitoring.datadog.apiKey = await this.promptSecret(' API Key: ');
|
|
504
|
+
monitoring.datadog.appKey = await this.promptSecret(' Application Key: ');
|
|
505
|
+
monitoring.datadog.site = await this.prompt(' Site (datadoghq.com): ', 'datadoghq.com');
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// New Relic
|
|
509
|
+
const setupNewrelic = await this.prompt(' Setup New Relic? (y/N): ', 'n');
|
|
510
|
+
if (setupNewrelic.toLowerCase() === 'y') {
|
|
511
|
+
monitoring.newrelic = {};
|
|
512
|
+
monitoring.newrelic.accountId = await this.prompt(' Account ID: ');
|
|
513
|
+
monitoring.newrelic.apiKey = await this.promptSecret(' API Key: ');
|
|
514
|
+
monitoring.newrelic.licenseKey = await this.promptSecret(' License Key: ');
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
// Sentry
|
|
518
|
+
const setupSentry = await this.prompt(' Setup Sentry? (y/N): ', 'n');
|
|
519
|
+
if (setupSentry.toLowerCase() === 'y') {
|
|
520
|
+
monitoring.sentry = {};
|
|
521
|
+
monitoring.sentry.dsn = await this.promptSecret(' DSN: ');
|
|
522
|
+
monitoring.sentry.org = await this.prompt(' Organization slug: ');
|
|
523
|
+
monitoring.sentry.project = await this.prompt(' Project slug: ');
|
|
524
|
+
monitoring.sentry.authToken = await this.promptSecret(' Auth token: ');
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// PagerDuty
|
|
528
|
+
const setupPager = await this.prompt(' Setup PagerDuty? (y/N): ', 'n');
|
|
529
|
+
if (setupPager.toLowerCase() === 'y') {
|
|
530
|
+
monitoring.pagerduty = {};
|
|
531
|
+
monitoring.pagerduty.apiKey = await this.promptSecret(' API Key: ');
|
|
532
|
+
monitoring.pagerduty.integrationKey = await this.promptSecret(' Integration Key: ');
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// Prometheus/Grafana
|
|
536
|
+
const setupProm = await this.prompt(' Setup Prometheus/Grafana? (y/N): ', 'n');
|
|
537
|
+
if (setupProm.toLowerCase() === 'y') {
|
|
538
|
+
monitoring.prometheus = {};
|
|
539
|
+
monitoring.prometheus.url = await this.prompt(' Prometheus URL: ');
|
|
540
|
+
monitoring.grafana = {};
|
|
541
|
+
monitoring.grafana.url = await this.prompt(' Grafana URL: ');
|
|
542
|
+
monitoring.grafana.apiKey = await this.promptSecret(' Grafana API Key: ');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return monitoring;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
async setupOrganization() {
|
|
549
|
+
const org = {};
|
|
550
|
+
|
|
551
|
+
org.name = await this.prompt(' Organization name: ');
|
|
552
|
+
org.domain = await this.prompt(' Organization domain: ');
|
|
553
|
+
|
|
554
|
+
// SSO/SAML
|
|
555
|
+
const hasSso = await this.prompt(' Use SSO/SAML? (y/N): ', 'n');
|
|
556
|
+
if (hasSso.toLowerCase() === 'y') {
|
|
557
|
+
org.sso = {};
|
|
558
|
+
org.sso.provider = await this.prompt(' Provider (okta/auth0/azure): ');
|
|
559
|
+
org.sso.domain = await this.prompt(' SSO domain: ');
|
|
560
|
+
org.sso.clientId = await this.prompt(' Client ID: ');
|
|
561
|
+
org.sso.clientSecret = await this.promptSecret(' Client secret: ');
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// Policy server
|
|
565
|
+
const hasPolicyServer = await this.prompt(' Use organization policy server? (y/N): ', 'n');
|
|
566
|
+
if (hasPolicyServer.toLowerCase() === 'y') {
|
|
567
|
+
org.policyUrl = await this.prompt(' Policy server URL: ');
|
|
568
|
+
org.policyToken = await this.promptSecret(' Policy server token: ');
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// VPN/Proxy
|
|
572
|
+
const hasVpn = await this.prompt(' Configure VPN/Proxy? (y/N): ', 'n');
|
|
573
|
+
if (hasVpn.toLowerCase() === 'y') {
|
|
574
|
+
org.proxy = {};
|
|
575
|
+
org.proxy.http = await this.prompt(' HTTP proxy: ');
|
|
576
|
+
org.proxy.https = await this.prompt(' HTTPS proxy: ');
|
|
577
|
+
org.proxy.noProxy = await this.prompt(' No proxy (localhost,127.0.0.1): ', 'localhost,127.0.0.1');
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Audit requirements
|
|
581
|
+
org.auditLevel = await this.prompt(' Audit level (basic/detailed/verbose): ', 'detailed');
|
|
582
|
+
org.requireApproval = await this.prompt(' Require approval for enforce mode? (Y/n): ', 'y');
|
|
583
|
+
|
|
584
|
+
return org;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
async setupSSHKey(email) {
|
|
588
|
+
const sshDir = path.join(process.env.HOME, '.ssh');
|
|
589
|
+
const keyPath = path.join(sshDir, 'id_ed25519_delimit');
|
|
590
|
+
|
|
591
|
+
// Check for existing key
|
|
592
|
+
if (fs.existsSync(keyPath)) {
|
|
593
|
+
console.log(chalk.gray(` Found existing SSH key: ${keyPath}`));
|
|
594
|
+
return keyPath;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Generate new key
|
|
598
|
+
console.log(chalk.cyan(' Generating new SSH key...'));
|
|
599
|
+
|
|
600
|
+
if (!fs.existsSync(sshDir)) {
|
|
601
|
+
fs.mkdirSync(sshDir, { mode: 0o700 });
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
try {
|
|
605
|
+
execSync(`ssh-keygen -t ed25519 -C "${email}" -f ${keyPath} -N ""`, { stdio: 'pipe' });
|
|
606
|
+
|
|
607
|
+
// Set proper permissions
|
|
608
|
+
fs.chmodSync(keyPath, 0o600);
|
|
609
|
+
fs.chmodSync(`${keyPath}.pub`, 0o644);
|
|
610
|
+
|
|
611
|
+
// Display public key
|
|
612
|
+
const publicKey = fs.readFileSync(`${keyPath}.pub`, 'utf8');
|
|
613
|
+
console.log(chalk.green('\n SSH key generated successfully!'));
|
|
614
|
+
console.log(chalk.cyan('\n Add this public key to GitHub:'));
|
|
615
|
+
console.log(chalk.white(` ${publicKey}`));
|
|
616
|
+
console.log(chalk.gray('\n https://github.com/settings/keys'));
|
|
617
|
+
|
|
618
|
+
// Add to SSH agent
|
|
619
|
+
try {
|
|
620
|
+
execSync(`ssh-add ${keyPath}`, { stdio: 'pipe' });
|
|
621
|
+
console.log(chalk.green(' Added to SSH agent'));
|
|
622
|
+
} catch (e) {
|
|
623
|
+
console.log(chalk.yellow(' Could not add to SSH agent (start ssh-agent first)'));
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return keyPath;
|
|
627
|
+
} catch (e) {
|
|
628
|
+
console.log(chalk.red(' Failed to generate SSH key:', e.message));
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
async configureGit(github) {
|
|
634
|
+
console.log(chalk.cyan('\nš§ Configuring Git...'));
|
|
635
|
+
|
|
636
|
+
try {
|
|
637
|
+
// Set user info
|
|
638
|
+
if (github.username) {
|
|
639
|
+
execSync(`git config --global user.name "${github.username}"`, { stdio: 'pipe' });
|
|
640
|
+
}
|
|
641
|
+
if (github.email) {
|
|
642
|
+
execSync(`git config --global user.email "${github.email}"`, { stdio: 'pipe' });
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// Set up credential helper
|
|
646
|
+
if (github.token) {
|
|
647
|
+
// Create credentials file for HTTPS
|
|
648
|
+
const credFile = path.join(this.configDir, 'git-credentials');
|
|
649
|
+
const credContent = `https://${github.username}:${github.token}@github.com\n`;
|
|
650
|
+
fs.writeFileSync(credFile, credContent, { mode: 0o600 });
|
|
651
|
+
|
|
652
|
+
execSync(`git config --global credential.helper "store --file=${credFile}"`, { stdio: 'pipe' });
|
|
653
|
+
console.log(chalk.green(' ā Git credentials configured'));
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Configure Delimit hooks
|
|
657
|
+
const hooksPath = path.join(this.configDir, 'hooks');
|
|
658
|
+
execSync(`git config --global core.hooksPath ${hooksPath}`, { stdio: 'pipe' });
|
|
659
|
+
console.log(chalk.green(' ā Delimit hooks configured'));
|
|
660
|
+
|
|
661
|
+
} catch (e) {
|
|
662
|
+
console.log(chalk.yellow(' Warning: Could not configure Git:', e.message));
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
async createEnvironmentFile(credentials) {
|
|
667
|
+
const envFile = path.join(this.configDir, 'env.sh');
|
|
668
|
+
let envContent = '#!/bin/bash\n# Delimit Environment Configuration\n\n';
|
|
669
|
+
|
|
670
|
+
// GitHub
|
|
671
|
+
if (credentials.github?.token) {
|
|
672
|
+
envContent += `export GITHUB_TOKEN="${credentials.github.token}"\n`;
|
|
673
|
+
}
|
|
674
|
+
if (credentials.github?.ghToken) {
|
|
675
|
+
envContent += `export GH_TOKEN="${credentials.github.ghToken}"\n`;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// AI Tools
|
|
679
|
+
if (credentials.claude?.apiKey) {
|
|
680
|
+
envContent += `export ANTHROPIC_API_KEY="${credentials.claude.apiKey}"\n`;
|
|
681
|
+
}
|
|
682
|
+
if (credentials.openai?.apiKey) {
|
|
683
|
+
envContent += `export OPENAI_API_KEY="${credentials.openai.apiKey}"\n`;
|
|
684
|
+
}
|
|
685
|
+
if (credentials.gemini?.apiKey) {
|
|
686
|
+
envContent += `export GOOGLE_AI_API_KEY="${credentials.gemini.apiKey}"\n`;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Organization
|
|
690
|
+
if (credentials.organization?.policyUrl) {
|
|
691
|
+
envContent += `export DELIMIT_ORG_POLICY_URL="${credentials.organization.policyUrl}"\n`;
|
|
692
|
+
envContent += `export DELIMIT_ORG_POLICY_TOKEN="${credentials.organization.policyToken}"\n`;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Delimit settings
|
|
696
|
+
envContent += `\n# Delimit Settings\n`;
|
|
697
|
+
envContent += `export DELIMIT_CONFIGURED=true\n`;
|
|
698
|
+
envContent += `export DELIMIT_AUTH_FILE="${this.credentialsFile}"\n`;
|
|
699
|
+
|
|
700
|
+
fs.writeFileSync(envFile, envContent, { mode: 0o600 });
|
|
701
|
+
|
|
702
|
+
// Add to bashrc if not present
|
|
703
|
+
const bashrcPath = path.join(process.env.HOME, '.bashrc');
|
|
704
|
+
const sourceLine = `source ${envFile}`;
|
|
705
|
+
|
|
706
|
+
if (fs.existsSync(bashrcPath)) {
|
|
707
|
+
const bashrc = fs.readFileSync(bashrcPath, 'utf8');
|
|
708
|
+
if (!bashrc.includes(sourceLine)) {
|
|
709
|
+
fs.appendFileSync(bashrcPath, `\n# Delimit Authentication\n${sourceLine}\n`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
async saveCredentials(credentials) {
|
|
715
|
+
// Generate encryption key from machine ID
|
|
716
|
+
const machineId = this.getMachineId();
|
|
717
|
+
const key = crypto.createHash('sha256').update(machineId).digest();
|
|
718
|
+
|
|
719
|
+
// Encrypt credentials
|
|
720
|
+
const iv = crypto.randomBytes(16);
|
|
721
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
|
722
|
+
|
|
723
|
+
const encrypted = Buffer.concat([
|
|
724
|
+
iv,
|
|
725
|
+
cipher.update(JSON.stringify(credentials, null, 2)),
|
|
726
|
+
cipher.final()
|
|
727
|
+
]);
|
|
728
|
+
|
|
729
|
+
// Save encrypted file
|
|
730
|
+
fs.writeFileSync(this.credentialsFile, encrypted, { mode: 0o600 });
|
|
731
|
+
|
|
732
|
+
// Save auth config (non-sensitive)
|
|
733
|
+
const authConfig = {
|
|
734
|
+
configured: true,
|
|
735
|
+
timestamp: new Date().toISOString(),
|
|
736
|
+
tools: Object.keys(credentials)
|
|
737
|
+
};
|
|
738
|
+
fs.writeFileSync(this.authConfigFile, JSON.stringify(authConfig, null, 2));
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
async loadCredentials() {
|
|
742
|
+
if (!fs.existsSync(this.credentialsFile)) {
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
try {
|
|
747
|
+
// Generate decryption key
|
|
748
|
+
const machineId = this.getMachineId();
|
|
749
|
+
const key = crypto.createHash('sha256').update(machineId).digest();
|
|
750
|
+
|
|
751
|
+
// Read encrypted file
|
|
752
|
+
const encrypted = fs.readFileSync(this.credentialsFile);
|
|
753
|
+
const iv = encrypted.slice(0, 16);
|
|
754
|
+
const data = encrypted.slice(16);
|
|
755
|
+
|
|
756
|
+
// Decrypt
|
|
757
|
+
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
|
|
758
|
+
const decrypted = Buffer.concat([
|
|
759
|
+
decipher.update(data),
|
|
760
|
+
decipher.final()
|
|
761
|
+
]);
|
|
762
|
+
|
|
763
|
+
return JSON.parse(decrypted.toString());
|
|
764
|
+
} catch (e) {
|
|
765
|
+
console.log(chalk.red('Failed to load credentials:', e.message));
|
|
766
|
+
return null;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
getMachineId() {
|
|
771
|
+
// Get a unique machine identifier
|
|
772
|
+
try {
|
|
773
|
+
const hostname = require('os').hostname();
|
|
774
|
+
const cpuInfo = require('os').cpus()[0].model;
|
|
775
|
+
return `${hostname}-${cpuInfo}`;
|
|
776
|
+
} catch (e) {
|
|
777
|
+
return 'default-machine-id';
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
isInstalled(tool) {
|
|
782
|
+
try {
|
|
783
|
+
execSync(`which ${tool}`, { stdio: 'ignore' });
|
|
784
|
+
return true;
|
|
785
|
+
} catch {
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
prompt(question, defaultValue = '') {
|
|
791
|
+
const rl = readline.createInterface({
|
|
792
|
+
input: process.stdin,
|
|
793
|
+
output: process.stdout
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
return new Promise((resolve) => {
|
|
797
|
+
rl.question(question, (answer) => {
|
|
798
|
+
rl.close();
|
|
799
|
+
resolve(answer || defaultValue);
|
|
800
|
+
});
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
promptSecret(question) {
|
|
805
|
+
const rl = readline.createInterface({
|
|
806
|
+
input: process.stdin,
|
|
807
|
+
output: process.stdout
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
return new Promise((resolve) => {
|
|
811
|
+
// Hide input
|
|
812
|
+
process.stdout.write(question);
|
|
813
|
+
|
|
814
|
+
let secret = '';
|
|
815
|
+
process.stdin.setRawMode(true);
|
|
816
|
+
process.stdin.resume();
|
|
817
|
+
process.stdin.setEncoding('utf8');
|
|
818
|
+
|
|
819
|
+
const onData = (char) => {
|
|
820
|
+
char = char.toString('utf8');
|
|
821
|
+
|
|
822
|
+
switch (char) {
|
|
823
|
+
case '\n':
|
|
824
|
+
case '\r':
|
|
825
|
+
case '\u0004':
|
|
826
|
+
process.stdin.setRawMode(false);
|
|
827
|
+
process.stdin.pause();
|
|
828
|
+
process.stdin.removeListener('data', onData);
|
|
829
|
+
process.stdout.write('\n');
|
|
830
|
+
rl.close();
|
|
831
|
+
resolve(secret);
|
|
832
|
+
break;
|
|
833
|
+
case '\u0003':
|
|
834
|
+
process.exit();
|
|
835
|
+
break;
|
|
836
|
+
case '\u007f':
|
|
837
|
+
if (secret.length > 0) {
|
|
838
|
+
secret = secret.slice(0, -1);
|
|
839
|
+
process.stdout.write('\b \b');
|
|
840
|
+
}
|
|
841
|
+
break;
|
|
842
|
+
default:
|
|
843
|
+
secret += char;
|
|
844
|
+
process.stdout.write('*');
|
|
845
|
+
break;
|
|
846
|
+
}
|
|
847
|
+
};
|
|
848
|
+
|
|
849
|
+
process.stdin.on('data', onData);
|
|
850
|
+
});
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
printSummary(credentials) {
|
|
854
|
+
console.log(chalk.cyan('š Configuration Summary:'));
|
|
855
|
+
|
|
856
|
+
if (credentials.github) {
|
|
857
|
+
console.log(chalk.white(` ⢠GitHub: ${credentials.github.username} <${credentials.github.email}>`));
|
|
858
|
+
if (credentials.github.token) console.log(chalk.gray(' - Personal Access Token configured'));
|
|
859
|
+
if (credentials.github.sshKey) console.log(chalk.gray(' - SSH key configured'));
|
|
860
|
+
if (credentials.github.ghToken) console.log(chalk.gray(' - GitHub CLI configured'));
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
for (const tool of ['claude', 'openai', 'gemini', 'codex']) {
|
|
864
|
+
if (credentials[tool]) {
|
|
865
|
+
console.log(chalk.white(` ⢠${tool}: Configured`));
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
if (credentials.mcp && Object.keys(credentials.mcp).length > 0) {
|
|
870
|
+
console.log(chalk.white(` ⢠MCP Servers: ${Object.keys(credentials.mcp).length} configured`));
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
if (credentials.organization) {
|
|
874
|
+
console.log(chalk.white(` ⢠Organization: ${credentials.organization.name}`));
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
console.log(chalk.cyan('\nšÆ Next Steps:'));
|
|
878
|
+
console.log(chalk.white(' 1. Restart your shell to load environment'));
|
|
879
|
+
console.log(chalk.white(' 2. Run "delimit test auth" to verify credentials'));
|
|
880
|
+
console.log(chalk.white(' 3. Run "delimit install --hooks all" to complete setup'));
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// Export for use as module
|
|
885
|
+
module.exports = DelimitAuthSetup;
|
|
886
|
+
|
|
887
|
+
// Run if executed directly
|
|
888
|
+
if (require.main === module) {
|
|
889
|
+
const setup = new DelimitAuthSetup();
|
|
890
|
+
setup.setup({ all: process.argv.includes('--all') }).catch(console.error);
|
|
891
|
+
}
|