snow-flow 8.5.7 → 8.5.8
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/CLAUDE.md +52 -4
- package/README.md +99 -30
- package/{OPENCODE-SETUP.md → SNOWCODE-SETUP.md} +47 -21
- package/{OPENCODE-TROUBLESHOOTING.md → SNOWCODE-TROUBLESHOOTING.md} +18 -18
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +526 -280
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +528 -250
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/claude-md-template.d.ts +1 -1
- package/dist/templates/claude-md-template.js +1 -1
- package/dist/templates/readme-template.d.ts +1 -1
- package/dist/templates/readme-template.js +18 -18
- package/dist/utils/artifact-local-sync.d.ts +1 -1
- package/dist/utils/artifact-local-sync.js +4 -4
- package/dist/utils/snow-oauth.d.ts +11 -5
- package/dist/utils/snow-oauth.d.ts.map +1 -1
- package/dist/utils/snow-oauth.js +337 -90
- package/dist/utils/snow-oauth.js.map +1 -1
- package/dist/utils/{opencode-output-interceptor.d.ts → snowcode-output-interceptor.d.ts} +7 -7
- package/dist/utils/{opencode-output-interceptor.d.ts.map → snowcode-output-interceptor.d.ts.map} +1 -1
- package/dist/utils/{opencode-output-interceptor.js → snowcode-output-interceptor.js} +10 -10
- package/dist/utils/{opencode-output-interceptor.js.map → snowcode-output-interceptor.js.map} +1 -1
- package/package.json +20 -9
- package/scripts/{start-opencode.sh → start-snowcode.sh} +28 -28
- package/scripts/verify-snowcode-fork.sh +141 -0
- package/templates/snowcode-package.json +5 -0
- package/THEMES.md +0 -223
- package/bin/opencode +0 -17
- package/dist/mcp/servicenow-mcp-unified/config/tool-definitions.json +0 -3935
- package/dist/mcp/servicenow-mcp-unified/tools/automation/snow_automation_discover.js +0 -164
- package/dist/mcp/servicenow-mcp-unified/tools/deployment/snow_artifact_transfer.js +0 -282
- package/dist/mcp/servicenow-mcp-unified/tools/filters/snow_build_filter.js +0 -171
- package/dist/mcp/servicenow-mcp-unified/tools/formatters/snow_format_value.js +0 -164
- package/dist/mcp/servicenow-mcp-unified/tools/knowledge/index.js.bak +0 -45
- package/dist/mcp/servicenow-mcp-unified/tools/local-sync/snow_artifact_sync.js +0 -172
- package/dist/mcp/servicenow-mcp-unified/tools/system-properties/index.js +0 -36
- package/dist/mcp/servicenow-mcp-unified/tools/ui-builder/snow_discover_uib.js +0 -296
- package/dist/mcp/servicenow-mcp-unified/tools/workspace/snow_create_ux_component.js +0 -292
- package/dist/memory/session-memory.d.ts +0 -80
- package/dist/memory/session-memory.d.ts.map +0 -1
- package/dist/memory/session-memory.js +0 -468
- package/dist/memory/session-memory.js.map +0 -1
- package/dist/templates/opencode-agents-template.d.ts +0 -2
- package/dist/templates/opencode-agents-template.d.ts.map +0 -1
- package/dist/templates/opencode-agents-template.js +0 -469
- package/dist/templates/opencode-agents-template.js.map +0 -1
- package/scripts/bulk-optimize-tools.js +0 -486
- package/scripts/optimize-mcp-tools.ts +0 -410
- package/themes/README.md +0 -83
- package/themes/servicenow.json +0 -117
- /package/{opencode-config.example.json → snowcode-config.example.json} +0 -0
package/dist/cli/auth.js
CHANGED
|
@@ -32,12 +32,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
32
32
|
return result;
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
-
};
|
|
38
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
36
|
exports.registerAuthCommands = registerAuthCommands;
|
|
40
|
-
const
|
|
37
|
+
const prompts = __importStar(require("@clack/prompts"));
|
|
41
38
|
const snow_oauth_js_1 = require("../utils/snow-oauth.js");
|
|
42
39
|
const servicenow_client_js_1 = require("../utils/servicenow-client.js");
|
|
43
40
|
const logger_js_1 = require("../utils/logger.js");
|
|
@@ -51,44 +48,44 @@ function registerAuthCommands(program) {
|
|
|
51
48
|
.option('-p, --provider <provider>', 'Provider to list models for (anthropic, openai, google, ollama)')
|
|
52
49
|
.action(async (options) => {
|
|
53
50
|
const { getAllProviderModels, getProviderModels } = await Promise.resolve().then(() => __importStar(require('../utils/dynamic-models.js')));
|
|
54
|
-
|
|
51
|
+
prompts.log.step('Available LLM Models');
|
|
55
52
|
if (options.provider) {
|
|
56
53
|
// List models for specific provider
|
|
57
|
-
|
|
54
|
+
prompts.log.info(`${options.provider.toUpperCase()}:`);
|
|
58
55
|
const models = await getProviderModels(options.provider);
|
|
59
56
|
if (models.length > 0) {
|
|
60
57
|
models.forEach((model, i) => {
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
prompts.log.message(` ${i + 1}. ${model.name}`);
|
|
59
|
+
prompts.log.message(` ID: ${model.value}`);
|
|
63
60
|
if (model.contextWindow) {
|
|
64
|
-
|
|
61
|
+
prompts.log.message(` Context: ${model.contextWindow.toLocaleString()} tokens`);
|
|
65
62
|
}
|
|
66
63
|
console.log();
|
|
67
64
|
});
|
|
68
65
|
}
|
|
69
66
|
else {
|
|
70
|
-
|
|
67
|
+
prompts.log.warn(' No models available for this provider');
|
|
71
68
|
}
|
|
72
69
|
}
|
|
73
70
|
else {
|
|
74
71
|
// List all providers
|
|
75
72
|
const allModels = await getAllProviderModels();
|
|
76
73
|
for (const [provider, models] of Object.entries(allModels)) {
|
|
77
|
-
|
|
74
|
+
prompts.log.info(`${provider.toUpperCase()}:`);
|
|
78
75
|
if (models.length > 0) {
|
|
79
76
|
models.forEach((model, i) => {
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
prompts.log.message(` ${i + 1}. ${model.name}`);
|
|
78
|
+
prompts.log.message(` ID: ${model.value}`);
|
|
82
79
|
console.log();
|
|
83
80
|
});
|
|
84
81
|
}
|
|
85
82
|
else {
|
|
86
|
-
|
|
83
|
+
prompts.log.warn(' No models available');
|
|
87
84
|
}
|
|
88
85
|
}
|
|
89
86
|
}
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
prompts.log.message('Tip: Use --provider to see models for a specific provider');
|
|
88
|
+
prompts.log.message('Example: snow-flow auth models --provider anthropic');
|
|
92
89
|
});
|
|
93
90
|
auth
|
|
94
91
|
.command('login')
|
|
@@ -99,49 +96,46 @@ function registerAuthCommands(program) {
|
|
|
99
96
|
const path = require('path');
|
|
100
97
|
const inquirer = require('inquirer');
|
|
101
98
|
console.log(); // Empty line for spacing
|
|
102
|
-
// Step
|
|
103
|
-
console.log(chalk_1.default.blue('╭────────────────────────────────────────────╮'));
|
|
104
|
-
console.log(chalk_1.default.blue('│ Add LLM credential │'));
|
|
105
|
-
console.log(chalk_1.default.blue('╰────────────────────────────────────────────╯\n'));
|
|
99
|
+
// Step 0: Check if we need LLM authentication
|
|
106
100
|
let provider = process.env.DEFAULT_LLM_PROVIDER;
|
|
107
|
-
// Check if ANY provider API key is
|
|
101
|
+
// Check if ANY provider API key is configured
|
|
108
102
|
const hasApiKey = (process.env.ANTHROPIC_API_KEY && process.env.ANTHROPIC_API_KEY.trim() !== '') ||
|
|
109
103
|
(process.env.OPENAI_API_KEY && process.env.OPENAI_API_KEY.trim() !== '') ||
|
|
110
104
|
(process.env.GOOGLE_API_KEY && process.env.GOOGLE_API_KEY.trim() !== '') ||
|
|
111
105
|
(process.env.GROQ_API_KEY && process.env.GROQ_API_KEY.trim() !== '') ||
|
|
112
106
|
(process.env.MISTRAL_API_KEY && process.env.MISTRAL_API_KEY.trim() !== '');
|
|
113
|
-
//
|
|
107
|
+
// Only do SnowCode auth if no API key is configured
|
|
114
108
|
if (!hasApiKey) {
|
|
115
|
-
// Check if
|
|
109
|
+
// Check if snowcode is installed
|
|
116
110
|
try {
|
|
117
|
-
execSync('which
|
|
111
|
+
execSync('which snowcode', { stdio: 'ignore' });
|
|
118
112
|
}
|
|
119
113
|
catch {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
114
|
+
prompts.log.error('SnowCode is not installed');
|
|
115
|
+
prompts.log.warn('Please install SnowCode first: npm install -g @groeimetai/snowcode');
|
|
116
|
+
prompts.log.info('Or configure an API key in .env: ANTHROPIC_API_KEY=your-key');
|
|
123
117
|
return;
|
|
124
118
|
}
|
|
125
|
-
|
|
126
|
-
// Fix common
|
|
119
|
+
prompts.intro('Starting authentication');
|
|
120
|
+
// Fix common SnowCode directory issue (agents vs agent) in ALL possible directories
|
|
127
121
|
try {
|
|
128
122
|
const fs = require('fs');
|
|
129
123
|
const path = require('path');
|
|
130
124
|
const directoriesToFix = [
|
|
131
|
-
// Fix 1: Global ~/.
|
|
132
|
-
process.env.HOME + '/.
|
|
125
|
+
// Fix 1: Global ~/.snowcode directory
|
|
126
|
+
process.env.HOME + '/.snowcode',
|
|
133
127
|
// Fix 2: Current working directory
|
|
134
|
-
path.join(process.cwd(), '.
|
|
128
|
+
path.join(process.cwd(), '.snowcode'),
|
|
135
129
|
// Fix 3: Parent directory (in case we're in a subdirectory)
|
|
136
|
-
path.join(process.cwd(), '..', '.
|
|
130
|
+
path.join(process.cwd(), '..', '.snowcode'),
|
|
137
131
|
// Fix 4: Snow-flow package directory (for development)
|
|
138
|
-
path.join(__dirname, '..', '..', '.
|
|
132
|
+
path.join(__dirname, '..', '..', '.snowcode')
|
|
139
133
|
];
|
|
140
|
-
for (const
|
|
141
|
-
const agentsDir = path.join(
|
|
142
|
-
const agentDir = path.join(
|
|
134
|
+
for (const snowcodeDir of directoriesToFix) {
|
|
135
|
+
const agentsDir = path.join(snowcodeDir, 'agents');
|
|
136
|
+
const agentDir = path.join(snowcodeDir, 'agent');
|
|
143
137
|
if (fs.existsSync(agentsDir) && !fs.existsSync(agentDir)) {
|
|
144
|
-
|
|
138
|
+
prompts.log.message(` Fixing SnowCode directory structure in ${snowcodeDir}...`);
|
|
145
139
|
try {
|
|
146
140
|
fs.renameSync(agentsDir, agentDir);
|
|
147
141
|
}
|
|
@@ -152,265 +146,514 @@ function registerAuthCommands(program) {
|
|
|
152
146
|
}
|
|
153
147
|
}
|
|
154
148
|
catch (dirError) {
|
|
155
|
-
// Ignore directory fix errors -
|
|
156
|
-
|
|
149
|
+
// Ignore directory fix errors - SnowCode will handle it
|
|
150
|
+
prompts.log.message(' (Directory fix skipped - will auto-correct)');
|
|
157
151
|
}
|
|
158
152
|
try {
|
|
159
|
-
// Run
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
choices: [
|
|
169
|
-
{ name: 'Anthropic (Claude)', value: 'anthropic' },
|
|
170
|
-
{ name: 'OpenAI (GPT)', value: 'openai' },
|
|
171
|
-
{ name: 'Google (Gemini)', value: 'google' },
|
|
172
|
-
{ name: 'Ollama (Local)', value: 'ollama' },
|
|
173
|
-
{ name: 'Other', value: 'other' }
|
|
174
|
-
]
|
|
175
|
-
}]);
|
|
176
|
-
if (selectedProvider !== 'other') {
|
|
177
|
-
// Dynamically fetch latest models from provider APIs
|
|
178
|
-
const { getProviderModels } = await Promise.resolve().then(() => __importStar(require('../utils/dynamic-models.js')));
|
|
179
|
-
console.log(chalk_1.default.dim(` Fetching latest ${selectedProvider} models...`));
|
|
180
|
-
const models = await getProviderModels(selectedProvider);
|
|
181
|
-
// Ask for preferred model
|
|
182
|
-
let selectedModel = '';
|
|
183
|
-
if (models && models.length > 0) {
|
|
184
|
-
const choices = models.map(m => ({
|
|
185
|
-
name: m.name,
|
|
186
|
-
value: m.value
|
|
187
|
-
}));
|
|
188
|
-
const { chosenModel } = await inquirer.prompt([{
|
|
189
|
-
type: 'list',
|
|
190
|
-
name: 'chosenModel',
|
|
191
|
-
message: `Which ${selectedProvider} model do you want to use by default?`,
|
|
192
|
-
choices: choices
|
|
193
|
-
}]);
|
|
194
|
-
selectedModel = chosenModel;
|
|
195
|
-
}
|
|
196
|
-
else {
|
|
197
|
-
console.log(chalk_1.default.yellow(` ⚠️ Could not fetch models for ${selectedProvider}, skipping model selection`));
|
|
198
|
-
}
|
|
199
|
-
// Save provider AND model to .env file
|
|
200
|
-
const envPath = path.join(process.cwd(), '.env');
|
|
201
|
-
let envContent = '';
|
|
202
|
-
try {
|
|
203
|
-
envContent = fs.readFileSync(envPath, 'utf8');
|
|
204
|
-
}
|
|
205
|
-
catch {
|
|
206
|
-
// .env doesn't exist yet, use .env.example as template
|
|
207
|
-
const examplePath = path.join(process.cwd(), '.env.example');
|
|
208
|
-
if (fs.existsSync(examplePath)) {
|
|
209
|
-
envContent = fs.readFileSync(examplePath, 'utf8');
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
// Update DEFAULT_LLM_PROVIDER in .env content
|
|
213
|
-
if (envContent.includes('DEFAULT_LLM_PROVIDER=')) {
|
|
214
|
-
envContent = envContent.replace(/DEFAULT_LLM_PROVIDER=.*/g, `DEFAULT_LLM_PROVIDER=${selectedProvider}`);
|
|
215
|
-
}
|
|
216
|
-
else {
|
|
217
|
-
envContent += `\nDEFAULT_LLM_PROVIDER=${selectedProvider}\n`;
|
|
218
|
-
}
|
|
219
|
-
// Update DEFAULT_MODEL in .env content
|
|
220
|
-
if (selectedModel) {
|
|
221
|
-
if (envContent.includes('DEFAULT_MODEL=')) {
|
|
222
|
-
envContent = envContent.replace(/DEFAULT_MODEL=.*/g, `DEFAULT_MODEL=${selectedModel}`);
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
envContent += `DEFAULT_MODEL=${selectedModel}\n`;
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
fs.writeFileSync(envPath, envContent);
|
|
229
|
-
console.log(chalk_1.default.green(`✅ Provider saved: ${selectedProvider}`));
|
|
230
|
-
if (selectedModel) {
|
|
231
|
-
console.log(chalk_1.default.green(`✅ Default model saved: ${selectedModel}\n`));
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
console.log();
|
|
235
|
-
}
|
|
236
|
-
provider = selectedProvider;
|
|
237
|
-
process.env.DEFAULT_LLM_PROVIDER = provider;
|
|
238
|
-
if (selectedModel) {
|
|
239
|
-
process.env.DEFAULT_MODEL = selectedModel;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
153
|
+
// Run SnowCode auth login - prefer local installation (has platform binaries)
|
|
154
|
+
const localSnowCode = path.join(process.cwd(), 'node_modules', '@groeimetai', 'snowcode', 'bin', 'snowcode');
|
|
155
|
+
let snowcodeCommand = 'snowcode'; // fallback to global
|
|
156
|
+
if (fs.existsSync(localSnowCode)) {
|
|
157
|
+
prompts.log.message(' Using local SnowCode installation (with platform binaries)');
|
|
158
|
+
snowcodeCommand = `"${localSnowCode}"`;
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
prompts.log.message(' Using global SnowCode installation');
|
|
242
162
|
}
|
|
163
|
+
execSync(`${snowcodeCommand} auth login`, { stdio: 'inherit' });
|
|
243
164
|
}
|
|
244
165
|
catch (error) {
|
|
245
|
-
|
|
246
|
-
// Check if it's the
|
|
166
|
+
prompts.log.error('Authentication failed');
|
|
167
|
+
// Check if it's the bun dependency error
|
|
247
168
|
const errorMsg = error?.message || error?.toString() || '';
|
|
248
|
-
if (errorMsg.includes('
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
169
|
+
if (errorMsg.includes('Cannot find package \'bun\'') || errorMsg.includes('ERR_MODULE_NOT_FOUND')) {
|
|
170
|
+
prompts.log.warn('SnowCode dependency issue detected');
|
|
171
|
+
prompts.log.message(' This is a known issue with SnowCode versions 0.15.18 and earlier');
|
|
172
|
+
prompts.log.message(' Please update SnowCode to the latest version:');
|
|
173
|
+
prompts.log.message(' npm update -g @groeimetai/snowcode');
|
|
174
|
+
prompts.log.message(' Alternatively, use an API key instead:');
|
|
175
|
+
prompts.log.message(' 1. Add to .env: ANTHROPIC_API_KEY=your-api-key');
|
|
176
|
+
prompts.log.message(' 2. Then run: snow-flow auth login');
|
|
177
|
+
}
|
|
178
|
+
else if (errorMsg.includes('agents') && errorMsg.includes('agent')) {
|
|
179
|
+
prompts.log.warn('SnowCode directory issue detected');
|
|
180
|
+
prompts.log.message(' Run this fix: mv ~/.snowcode/agents ~/.snowcode/agent');
|
|
181
|
+
prompts.log.message(' Then try: snow-flow auth login');
|
|
252
182
|
}
|
|
253
183
|
else {
|
|
254
|
-
|
|
255
|
-
|
|
184
|
+
prompts.log.warn('You can try again later or use an API key instead');
|
|
185
|
+
prompts.log.message(' Add to .env: ANTHROPIC_API_KEY=your-api-key');
|
|
256
186
|
}
|
|
257
187
|
return;
|
|
258
188
|
}
|
|
259
189
|
}
|
|
260
|
-
//
|
|
261
|
-
console.log(chalk_1.default.blue('🔐 Authenticating with ServiceNow...'));
|
|
262
|
-
const oauth = new snow_oauth_js_1.ServiceNowOAuth();
|
|
190
|
+
// ServiceNow setup - continue the flow (maintain @clack/prompts styling)
|
|
263
191
|
// Read credentials from .env file
|
|
264
192
|
let instance = process.env.SNOW_INSTANCE;
|
|
265
|
-
let
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
// If
|
|
272
|
-
if (!
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
}
|
|
193
|
+
let authMethod = process.env.SNOW_AUTH_METHOD;
|
|
194
|
+
// Check if we have complete credentials for either method
|
|
195
|
+
const hasOAuthCreds = process.env.SNOW_CLIENT_ID && process.env.SNOW_CLIENT_SECRET;
|
|
196
|
+
const hasBasicCreds = process.env.SNOW_USERNAME && process.env.SNOW_PASSWORD;
|
|
197
|
+
const hasCompleteCredentials = instance && instance.includes('.service-now.com') &&
|
|
198
|
+
((authMethod === 'oauth' && hasOAuthCreds) || (authMethod === 'basic' && hasBasicCreds));
|
|
199
|
+
// If no complete credentials, ask for auth method FIRST
|
|
200
|
+
if (!hasCompleteCredentials) {
|
|
201
|
+
const method = await prompts.select({
|
|
202
|
+
message: 'Choose authentication method',
|
|
203
|
+
options: [
|
|
204
|
+
{ value: 'oauth', label: 'OAuth 2.0', hint: 'recommended' },
|
|
205
|
+
{ value: 'basic', label: 'Basic Auth', hint: 'username/password' }
|
|
206
|
+
]
|
|
207
|
+
});
|
|
208
|
+
if (prompts.isCancel(method)) {
|
|
209
|
+
prompts.cancel('Setup cancelled');
|
|
210
|
+
process.exit(0);
|
|
284
211
|
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
console.log(' SNOW_CLIENT_SECRET=your-client-secret');
|
|
304
|
-
console.log('\n Then run: snow-flow auth login');
|
|
305
|
-
return;
|
|
212
|
+
authMethod = method;
|
|
213
|
+
// Ask for ServiceNow instance (common for both methods)
|
|
214
|
+
instance = await prompts.text({
|
|
215
|
+
message: 'ServiceNow instance',
|
|
216
|
+
placeholder: 'dev12345.service-now.com',
|
|
217
|
+
defaultValue: instance || '',
|
|
218
|
+
validate: (value) => {
|
|
219
|
+
if (!value || value.trim() === '')
|
|
220
|
+
return 'Instance URL is required';
|
|
221
|
+
const cleaned = value.replace(/^https?:\/\//, '').replace(/\/$/, '');
|
|
222
|
+
if (!cleaned.includes('.service-now.com')) {
|
|
223
|
+
return 'Must be a ServiceNow domain (e.g., dev12345.service-now.com)';
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
if (prompts.isCancel(instance)) {
|
|
228
|
+
prompts.cancel('Setup cancelled');
|
|
229
|
+
process.exit(0);
|
|
306
230
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
},
|
|
326
|
-
filter: (input) => input.replace(/^https?:\/\//, '').replace(/\/$/, '')
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
type: 'input',
|
|
330
|
-
name: 'clientId',
|
|
331
|
-
message: 'OAuth Client ID:',
|
|
332
|
-
default: clientId,
|
|
333
|
-
validate: (input) => {
|
|
334
|
-
if (!input || input.trim() === '') {
|
|
231
|
+
instance = instance.replace(/^https?:\/\//, '').replace(/\/$/, '');
|
|
232
|
+
}
|
|
233
|
+
// OAuth 2.0 Flow
|
|
234
|
+
if (authMethod === 'oauth') {
|
|
235
|
+
const oauth = new snow_oauth_js_1.ServiceNowOAuth();
|
|
236
|
+
let clientId = process.env.SNOW_CLIENT_ID;
|
|
237
|
+
let clientSecret = process.env.SNOW_CLIENT_SECRET;
|
|
238
|
+
// Check if OAuth credentials need to be collected
|
|
239
|
+
const needsCredentials = !clientId || !clientSecret || clientId.length < 32 || clientSecret.length < 32;
|
|
240
|
+
// If credentials are missing, prompt for them
|
|
241
|
+
if (needsCredentials) {
|
|
242
|
+
// OAuth Client ID
|
|
243
|
+
clientId = await prompts.text({
|
|
244
|
+
message: 'OAuth Client ID',
|
|
245
|
+
placeholder: '32-character hex string from ServiceNow',
|
|
246
|
+
defaultValue: clientId || '',
|
|
247
|
+
validate: (value) => {
|
|
248
|
+
if (!value || value.trim() === '')
|
|
335
249
|
return 'Client ID is required';
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
return 'Client ID seems too short. Expected a 32-character hex string from ServiceNow';
|
|
339
|
-
}
|
|
340
|
-
return true;
|
|
250
|
+
if (value.length < 32)
|
|
251
|
+
return 'Client ID too short (expected 32+ characters)';
|
|
341
252
|
}
|
|
342
|
-
}
|
|
343
|
-
{
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
253
|
+
});
|
|
254
|
+
if (prompts.isCancel(clientId)) {
|
|
255
|
+
prompts.cancel('Setup cancelled');
|
|
256
|
+
process.exit(0);
|
|
257
|
+
}
|
|
258
|
+
// OAuth Client Secret
|
|
259
|
+
clientSecret = await prompts.password({
|
|
260
|
+
message: 'OAuth Client Secret',
|
|
261
|
+
validate: (value) => {
|
|
262
|
+
if (!value || value.trim() === '')
|
|
350
263
|
return 'Client Secret is required';
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return 'Client Secret too short. Expected 32+ character random string from ServiceNow';
|
|
354
|
-
}
|
|
355
|
-
return true;
|
|
264
|
+
if (value.length < 32)
|
|
265
|
+
return 'Client Secret too short (expected 32+ characters)';
|
|
356
266
|
}
|
|
267
|
+
});
|
|
268
|
+
if (prompts.isCancel(clientSecret)) {
|
|
269
|
+
prompts.cancel('Setup cancelled');
|
|
270
|
+
process.exit(0);
|
|
357
271
|
}
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
272
|
+
// Save to .env file
|
|
273
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
274
|
+
let envContent = '';
|
|
275
|
+
try {
|
|
276
|
+
envContent = fs.readFileSync(envPath, 'utf8');
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
const examplePath = path.join(process.cwd(), '.env.example');
|
|
280
|
+
if (fs.existsSync(examplePath)) {
|
|
281
|
+
envContent = fs.readFileSync(examplePath, 'utf8');
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Update credentials
|
|
285
|
+
const updates = [
|
|
286
|
+
{ key: 'SNOW_INSTANCE', value: instance },
|
|
287
|
+
{ key: 'SNOW_AUTH_METHOD', value: 'oauth' },
|
|
288
|
+
{ key: 'SNOW_CLIENT_ID', value: clientId },
|
|
289
|
+
{ key: 'SNOW_CLIENT_SECRET', value: clientSecret }
|
|
290
|
+
];
|
|
291
|
+
for (const { key, value } of updates) {
|
|
292
|
+
if (envContent.includes(`${key}=`)) {
|
|
293
|
+
envContent = envContent.replace(new RegExp(`${key}=.*`, 'g'), `${key}=${value}`);
|
|
294
|
+
}
|
|
295
|
+
else {
|
|
296
|
+
envContent += `\n${key}=${value}\n`;
|
|
297
|
+
}
|
|
373
298
|
}
|
|
299
|
+
fs.writeFileSync(envPath, envContent);
|
|
300
|
+
process.env.SNOW_INSTANCE = instance;
|
|
301
|
+
process.env.SNOW_AUTH_METHOD = 'oauth';
|
|
302
|
+
process.env.SNOW_CLIENT_ID = clientId;
|
|
303
|
+
process.env.SNOW_CLIENT_SECRET = clientSecret;
|
|
374
304
|
}
|
|
375
|
-
//
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if (
|
|
383
|
-
|
|
305
|
+
// Start OAuth flow with localhost callback server
|
|
306
|
+
const result = await oauth.authenticate(instance, clientId, clientSecret);
|
|
307
|
+
if (result.success) {
|
|
308
|
+
prompts.log.success('ServiceNow authentication successful');
|
|
309
|
+
// Test connection
|
|
310
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
311
|
+
const testResult = await client.testConnection();
|
|
312
|
+
if (testResult.success) {
|
|
313
|
+
prompts.log.success(`Logged in as: ${testResult.data.name} (${testResult.data.user_name})`);
|
|
384
314
|
}
|
|
385
|
-
|
|
386
|
-
|
|
315
|
+
// 🔧 Auto-refresh MCP configuration with new credentials
|
|
316
|
+
try {
|
|
317
|
+
const { setupMCPConfig } = await Promise.resolve().then(() => __importStar(require('../cli.js')));
|
|
318
|
+
const spinner2 = prompts.spinner();
|
|
319
|
+
spinner2.start('Updating MCP configuration');
|
|
320
|
+
await setupMCPConfig(process.cwd(), instance, clientId, clientSecret, true);
|
|
321
|
+
spinner2.stop('MCP servers ready for SnowCode/Claude Code');
|
|
322
|
+
}
|
|
323
|
+
catch (error) {
|
|
324
|
+
prompts.log.warn('Could not update MCP config - run "snow-flow init" to set up');
|
|
387
325
|
}
|
|
326
|
+
prompts.outro('Setup complete!');
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
prompts.cancel(result.error || 'Unknown error');
|
|
330
|
+
process.exit(1);
|
|
388
331
|
}
|
|
389
|
-
fs.writeFileSync(envPath, envContent);
|
|
390
|
-
console.log(chalk_1.default.green('\n✅ Credentials saved to .env file\n'));
|
|
391
|
-
// Reload environment variables
|
|
392
|
-
process.env.SNOW_INSTANCE = instance;
|
|
393
|
-
process.env.SNOW_CLIENT_ID = clientId;
|
|
394
|
-
process.env.SNOW_CLIENT_SECRET = clientSecret;
|
|
395
332
|
}
|
|
396
|
-
//
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
//
|
|
333
|
+
// Basic Auth Flow
|
|
334
|
+
else if (authMethod === 'basic') {
|
|
335
|
+
let username = process.env.SNOW_USERNAME;
|
|
336
|
+
let password = process.env.SNOW_PASSWORD;
|
|
337
|
+
// Check if Basic Auth credentials need to be collected
|
|
338
|
+
const needsCredentials = !username || !password || username.trim() === '' || password.trim() === '';
|
|
339
|
+
if (needsCredentials) {
|
|
340
|
+
// Username
|
|
341
|
+
username = await prompts.text({
|
|
342
|
+
message: 'ServiceNow username',
|
|
343
|
+
placeholder: 'admin',
|
|
344
|
+
defaultValue: username || '',
|
|
345
|
+
validate: (value) => {
|
|
346
|
+
if (!value || value.trim() === '')
|
|
347
|
+
return 'Username is required';
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
if (prompts.isCancel(username)) {
|
|
351
|
+
prompts.cancel('Setup cancelled');
|
|
352
|
+
process.exit(0);
|
|
353
|
+
}
|
|
354
|
+
// Password
|
|
355
|
+
password = await prompts.password({
|
|
356
|
+
message: 'ServiceNow password',
|
|
357
|
+
validate: (value) => {
|
|
358
|
+
if (!value || value.trim() === '')
|
|
359
|
+
return 'Password is required';
|
|
360
|
+
}
|
|
361
|
+
});
|
|
362
|
+
if (prompts.isCancel(password)) {
|
|
363
|
+
prompts.cancel('Setup cancelled');
|
|
364
|
+
process.exit(0);
|
|
365
|
+
}
|
|
366
|
+
// Save to .env file
|
|
367
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
368
|
+
let envContent = '';
|
|
369
|
+
try {
|
|
370
|
+
envContent = fs.readFileSync(envPath, 'utf8');
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
const examplePath = path.join(process.cwd(), '.env.example');
|
|
374
|
+
if (fs.existsSync(examplePath)) {
|
|
375
|
+
envContent = fs.readFileSync(examplePath, 'utf8');
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// Update credentials
|
|
379
|
+
const updates = [
|
|
380
|
+
{ key: 'SNOW_INSTANCE', value: instance },
|
|
381
|
+
{ key: 'SNOW_AUTH_METHOD', value: 'basic' },
|
|
382
|
+
{ key: 'SNOW_USERNAME', value: username },
|
|
383
|
+
{ key: 'SNOW_PASSWORD', value: password }
|
|
384
|
+
];
|
|
385
|
+
for (const { key, value } of updates) {
|
|
386
|
+
if (envContent.includes(`${key}=`)) {
|
|
387
|
+
envContent = envContent.replace(new RegExp(`${key}=.*`, 'g'), `${key}=${value}`);
|
|
388
|
+
}
|
|
389
|
+
else {
|
|
390
|
+
envContent += `\n${key}=${value}\n`;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
fs.writeFileSync(envPath, envContent);
|
|
394
|
+
process.env.SNOW_INSTANCE = instance;
|
|
395
|
+
process.env.SNOW_AUTH_METHOD = 'basic';
|
|
396
|
+
process.env.SNOW_USERNAME = username;
|
|
397
|
+
process.env.SNOW_PASSWORD = password;
|
|
398
|
+
}
|
|
399
|
+
// Test Basic Auth connection
|
|
400
|
+
const spinner = prompts.spinner();
|
|
401
|
+
spinner.start('Authenticating with ServiceNow');
|
|
401
402
|
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
402
403
|
const testResult = await client.testConnection();
|
|
403
404
|
if (testResult.success) {
|
|
404
|
-
|
|
405
|
+
spinner.stop('ServiceNow authentication successful');
|
|
406
|
+
prompts.log.success(`Logged in as: ${testResult.data.name} (${testResult.data.user_name})`);
|
|
407
|
+
// 🔧 Auto-refresh MCP configuration with new credentials
|
|
408
|
+
try {
|
|
409
|
+
const { setupMCPConfig } = await Promise.resolve().then(() => __importStar(require('../cli.js')));
|
|
410
|
+
const spinner2 = prompts.spinner();
|
|
411
|
+
spinner2.start('Updating MCP configuration');
|
|
412
|
+
await setupMCPConfig(process.cwd(), instance, username, password, true);
|
|
413
|
+
spinner2.stop('MCP servers ready for SnowCode/Claude Code');
|
|
414
|
+
}
|
|
415
|
+
catch (error) {
|
|
416
|
+
prompts.log.warn('Could not update MCP config - run "snow-flow init" to set up');
|
|
417
|
+
}
|
|
418
|
+
prompts.outro('Setup complete!');
|
|
419
|
+
}
|
|
420
|
+
else {
|
|
421
|
+
spinner.stop('Authentication failed');
|
|
422
|
+
prompts.cancel(testResult.error || 'Invalid credentials');
|
|
423
|
+
process.exit(1);
|
|
405
424
|
}
|
|
406
|
-
console.log(chalk_1.default.blue('\n🎉 Ready to start developing!'));
|
|
407
|
-
console.log(chalk_1.default.cyan(' snow-flow swarm "create incident dashboard"'));
|
|
408
|
-
console.log(chalk_1.default.dim(' or: ') + chalk_1.default.cyan('opencode\n'));
|
|
409
425
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
426
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
427
|
+
// 🚀 ENTERPRISE FEATURES SETUP (Optional)
|
|
428
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
429
|
+
console.log(); // Spacing
|
|
430
|
+
prompts.log.step('Enterprise Features (Optional)');
|
|
431
|
+
const hasEnterprise = await prompts.confirm({
|
|
432
|
+
message: 'Do you have a Snow-Flow Enterprise license?',
|
|
433
|
+
initialValue: false
|
|
434
|
+
});
|
|
435
|
+
if (prompts.isCancel(hasEnterprise) || !hasEnterprise) {
|
|
436
|
+
prompts.log.info('Skipping enterprise setup - you can add it later with "snow-flow auth login"');
|
|
437
|
+
prompts.outro('Setup complete!');
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
// Enterprise license key
|
|
441
|
+
const licenseKey = await prompts.text({
|
|
442
|
+
message: 'Enterprise license key',
|
|
443
|
+
placeholder: 'SNOW-ENT-YOURCOMPANY-20261231-ABC123',
|
|
444
|
+
validate: (value) => {
|
|
445
|
+
if (!value || value.trim() === '')
|
|
446
|
+
return 'License key is required';
|
|
447
|
+
if (!value.startsWith('SNOW-'))
|
|
448
|
+
return 'Invalid format (should start with SNOW-)';
|
|
449
|
+
const parts = value.split('-');
|
|
450
|
+
if (parts.length !== 5)
|
|
451
|
+
return 'Invalid format (expected: SNOW-TIER-ORG-DATE-HASH)';
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
if (prompts.isCancel(licenseKey)) {
|
|
455
|
+
prompts.log.info('Enterprise setup skipped');
|
|
456
|
+
prompts.outro('Setup complete!');
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
459
|
+
// License server URL (with sensible default)
|
|
460
|
+
const licenseServerUrl = await prompts.text({
|
|
461
|
+
message: 'Enterprise license server URL',
|
|
462
|
+
placeholder: 'https://license.snow-flow.dev',
|
|
463
|
+
defaultValue: 'https://license.snow-flow.dev',
|
|
464
|
+
validate: (value) => {
|
|
465
|
+
if (!value || value.trim() === '')
|
|
466
|
+
return 'URL is required';
|
|
467
|
+
if (!value.startsWith('https://'))
|
|
468
|
+
return 'Must be HTTPS URL';
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
if (prompts.isCancel(licenseServerUrl)) {
|
|
472
|
+
prompts.log.info('Enterprise setup skipped');
|
|
473
|
+
prompts.outro('Setup complete!');
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
// Ask which integrations to configure
|
|
477
|
+
const integrations = await prompts.multiselect({
|
|
478
|
+
message: 'Configure integrations (optional)',
|
|
479
|
+
options: [
|
|
480
|
+
{ value: 'jira', label: 'Jira', hint: 'Atlassian Jira Cloud' },
|
|
481
|
+
{ value: 'azdo', label: 'Azure DevOps', hint: 'Microsoft Azure DevOps' },
|
|
482
|
+
{ value: 'confluence', label: 'Confluence', hint: 'Atlassian Confluence' }
|
|
483
|
+
],
|
|
484
|
+
required: false
|
|
485
|
+
});
|
|
486
|
+
const enterpriseEnv = {
|
|
487
|
+
SNOW_LICENSE_KEY: licenseKey,
|
|
488
|
+
SNOW_ENTERPRISE_URL: licenseServerUrl
|
|
489
|
+
};
|
|
490
|
+
// Jira credentials
|
|
491
|
+
if (integrations && integrations.includes('jira')) {
|
|
492
|
+
prompts.log.message('Jira Configuration');
|
|
493
|
+
const jiraHost = await prompts.text({
|
|
494
|
+
message: 'Jira host',
|
|
495
|
+
placeholder: 'yourcompany.atlassian.net',
|
|
496
|
+
validate: (v) => v && v.includes('.') ? undefined : 'Invalid host'
|
|
497
|
+
});
|
|
498
|
+
if (!prompts.isCancel(jiraHost)) {
|
|
499
|
+
const jiraEmail = await prompts.text({
|
|
500
|
+
message: 'Jira email',
|
|
501
|
+
placeholder: 'you@company.com',
|
|
502
|
+
validate: (v) => v && v.includes('@') ? undefined : 'Invalid email'
|
|
503
|
+
});
|
|
504
|
+
if (!prompts.isCancel(jiraEmail)) {
|
|
505
|
+
const jiraToken = await prompts.password({
|
|
506
|
+
message: 'Jira API token',
|
|
507
|
+
validate: (v) => v && v.length > 10 ? undefined : 'Token required'
|
|
508
|
+
});
|
|
509
|
+
if (!prompts.isCancel(jiraToken)) {
|
|
510
|
+
enterpriseEnv.JIRA_BASE_URL = `https://${jiraHost}`;
|
|
511
|
+
enterpriseEnv.JIRA_EMAIL = jiraEmail;
|
|
512
|
+
enterpriseEnv.JIRA_API_TOKEN = jiraToken;
|
|
513
|
+
prompts.log.success('Jira configured');
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// Azure DevOps credentials
|
|
519
|
+
if (integrations && integrations.includes('azdo')) {
|
|
520
|
+
prompts.log.message('Azure DevOps Configuration');
|
|
521
|
+
const azdoOrg = await prompts.text({
|
|
522
|
+
message: 'Azure DevOps organization',
|
|
523
|
+
placeholder: 'yourcompany',
|
|
524
|
+
validate: (v) => v && v.length > 0 ? undefined : 'Organization required'
|
|
525
|
+
});
|
|
526
|
+
if (!prompts.isCancel(azdoOrg)) {
|
|
527
|
+
const azdoPat = await prompts.password({
|
|
528
|
+
message: 'Azure DevOps Personal Access Token (PAT)',
|
|
529
|
+
validate: (v) => v && v.length > 20 ? undefined : 'PAT required'
|
|
530
|
+
});
|
|
531
|
+
if (!prompts.isCancel(azdoPat)) {
|
|
532
|
+
enterpriseEnv.AZDO_ORG_URL = `https://dev.azure.com/${azdoOrg}`;
|
|
533
|
+
enterpriseEnv.AZDO_PAT = azdoPat;
|
|
534
|
+
prompts.log.success('Azure DevOps configured');
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
// Confluence credentials
|
|
539
|
+
if (integrations && integrations.includes('confluence')) {
|
|
540
|
+
prompts.log.message('Confluence Configuration');
|
|
541
|
+
const confluenceHost = await prompts.text({
|
|
542
|
+
message: 'Confluence host',
|
|
543
|
+
placeholder: 'yourcompany.atlassian.net',
|
|
544
|
+
validate: (v) => v && v.includes('.') ? undefined : 'Invalid host'
|
|
545
|
+
});
|
|
546
|
+
if (!prompts.isCancel(confluenceHost)) {
|
|
547
|
+
const confluenceEmail = await prompts.text({
|
|
548
|
+
message: 'Confluence email',
|
|
549
|
+
placeholder: 'you@company.com',
|
|
550
|
+
validate: (v) => v && v.includes('@') ? undefined : 'Invalid email'
|
|
551
|
+
});
|
|
552
|
+
if (!prompts.isCancel(confluenceEmail)) {
|
|
553
|
+
const confluenceToken = await prompts.password({
|
|
554
|
+
message: 'Confluence API token',
|
|
555
|
+
validate: (v) => v && v.length > 10 ? undefined : 'Token required'
|
|
556
|
+
});
|
|
557
|
+
if (!prompts.isCancel(confluenceToken)) {
|
|
558
|
+
enterpriseEnv.CONFLUENCE_BASE_URL = `https://${confluenceHost}`;
|
|
559
|
+
enterpriseEnv.CONFLUENCE_EMAIL = confluenceEmail;
|
|
560
|
+
enterpriseEnv.CONFLUENCE_API_TOKEN = confluenceToken;
|
|
561
|
+
prompts.log.success('Confluence configured');
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// Save enterprise credentials to .env
|
|
567
|
+
const envPath = path.join(process.cwd(), '.env');
|
|
568
|
+
let envContent = '';
|
|
569
|
+
try {
|
|
570
|
+
envContent = fs.readFileSync(envPath, 'utf8');
|
|
571
|
+
}
|
|
572
|
+
catch {
|
|
573
|
+
// File doesn't exist, will create
|
|
574
|
+
}
|
|
575
|
+
for (const [key, value] of Object.entries(enterpriseEnv)) {
|
|
576
|
+
if (envContent.includes(`${key}=`)) {
|
|
577
|
+
envContent = envContent.replace(new RegExp(`${key}=.*`, 'g'), `${key}=${value}`);
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
envContent += `\n${key}=${value}\n`;
|
|
581
|
+
}
|
|
582
|
+
process.env[key] = value;
|
|
583
|
+
}
|
|
584
|
+
fs.writeFileSync(envPath, envContent);
|
|
585
|
+
prompts.log.success('Enterprise credentials saved to .env');
|
|
586
|
+
// Configure enterprise MCP proxy
|
|
587
|
+
const spinner3 = prompts.spinner();
|
|
588
|
+
spinner3.start('Configuring enterprise MCP proxy');
|
|
589
|
+
try {
|
|
590
|
+
// Find the enterprise proxy path
|
|
591
|
+
const enterpriseProxyPath = path.join(process.cwd(), '..', 'snow-flow-enterprise', 'mcp-proxy', 'dist', 'enterprise-proxy.js');
|
|
592
|
+
// Check if proxy exists
|
|
593
|
+
if (!fs.existsSync(enterpriseProxyPath)) {
|
|
594
|
+
spinner3.stop('Enterprise proxy not found - will use remote server only');
|
|
595
|
+
prompts.log.warn('Enterprise proxy not built yet');
|
|
596
|
+
prompts.log.message(' Run: cd ../snow-flow-enterprise/mcp-proxy && npm run build');
|
|
597
|
+
}
|
|
598
|
+
else {
|
|
599
|
+
// Configure for SnowCode (prioritize SnowCode config)
|
|
600
|
+
const snowcodeConfigPath = path.join(process.env.HOME || '', '.snowcode', 'config.json');
|
|
601
|
+
const snowcodeConfigDirPath = path.join(process.env.HOME || '', '.snowcode');
|
|
602
|
+
if (!fs.existsSync(snowcodeConfigDirPath)) {
|
|
603
|
+
fs.mkdirSync(snowcodeConfigDirPath, { recursive: true });
|
|
604
|
+
}
|
|
605
|
+
let snowcodeConfig = { mcpServers: {} };
|
|
606
|
+
if (fs.existsSync(snowcodeConfigPath)) {
|
|
607
|
+
try {
|
|
608
|
+
snowcodeConfig = JSON.parse(fs.readFileSync(snowcodeConfigPath, 'utf8'));
|
|
609
|
+
}
|
|
610
|
+
catch {
|
|
611
|
+
snowcodeConfig = { mcpServers: {} };
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
if (!snowcodeConfig.mcpServers) {
|
|
615
|
+
snowcodeConfig.mcpServers = {};
|
|
616
|
+
}
|
|
617
|
+
// Add enterprise MCP server
|
|
618
|
+
snowcodeConfig.mcpServers['snow-flow-enterprise'] = {
|
|
619
|
+
command: 'node',
|
|
620
|
+
args: [enterpriseProxyPath],
|
|
621
|
+
env: enterpriseEnv
|
|
622
|
+
};
|
|
623
|
+
fs.writeFileSync(snowcodeConfigPath, JSON.stringify(snowcodeConfig, null, 2));
|
|
624
|
+
spinner3.stop('Enterprise MCP proxy configured for SnowCode');
|
|
625
|
+
prompts.log.success(`Config saved: ${snowcodeConfigPath}`);
|
|
626
|
+
// Also try Claude Code config
|
|
627
|
+
const claudeConfigPath = path.join(process.env.HOME || '', '.claude', 'settings.json');
|
|
628
|
+
if (fs.existsSync(path.dirname(claudeConfigPath))) {
|
|
629
|
+
try {
|
|
630
|
+
let claudeConfig = { mcpServers: {} };
|
|
631
|
+
if (fs.existsSync(claudeConfigPath)) {
|
|
632
|
+
claudeConfig = JSON.parse(fs.readFileSync(claudeConfigPath, 'utf8'));
|
|
633
|
+
}
|
|
634
|
+
if (!claudeConfig.mcpServers) {
|
|
635
|
+
claudeConfig.mcpServers = {};
|
|
636
|
+
}
|
|
637
|
+
claudeConfig.mcpServers['snow-flow-enterprise'] = {
|
|
638
|
+
command: 'node',
|
|
639
|
+
args: [enterpriseProxyPath],
|
|
640
|
+
env: enterpriseEnv
|
|
641
|
+
};
|
|
642
|
+
fs.writeFileSync(claudeConfigPath, JSON.stringify(claudeConfig, null, 2));
|
|
643
|
+
prompts.log.success('Also configured for Claude Code');
|
|
644
|
+
}
|
|
645
|
+
catch {
|
|
646
|
+
// Claude Code config optional
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
catch (error) {
|
|
652
|
+
spinner3.stop('Enterprise proxy configuration failed');
|
|
653
|
+
prompts.log.warn(`Error: ${error.message}`);
|
|
654
|
+
prompts.log.message('You can configure manually later');
|
|
413
655
|
}
|
|
656
|
+
prompts.outro('🎉 Enterprise setup complete! Restart your AI coding assistant to use enterprise features.');
|
|
414
657
|
});
|
|
415
658
|
auth
|
|
416
659
|
.command('logout')
|
|
@@ -418,45 +661,48 @@ function registerAuthCommands(program) {
|
|
|
418
661
|
.action(async () => {
|
|
419
662
|
const oauth = new snow_oauth_js_1.ServiceNowOAuth();
|
|
420
663
|
await oauth.logout();
|
|
421
|
-
|
|
664
|
+
prompts.log.success('Logged out successfully');
|
|
422
665
|
});
|
|
423
666
|
auth
|
|
424
667
|
.command('status')
|
|
425
668
|
.description('Show ServiceNow authentication status')
|
|
426
669
|
.action(async () => {
|
|
427
670
|
const oauth = new snow_oauth_js_1.ServiceNowOAuth();
|
|
428
|
-
|
|
671
|
+
prompts.log.step('ServiceNow Authentication Status');
|
|
429
672
|
const isAuthenticated = await oauth.isAuthenticated();
|
|
430
673
|
const credentials = await oauth.loadCredentials();
|
|
431
674
|
if (isAuthenticated && credentials) {
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
675
|
+
prompts.log.success('Status: Authenticated');
|
|
676
|
+
prompts.log.info(`Instance: ${credentials.instance}`);
|
|
677
|
+
prompts.log.info('Method: OAuth 2.0');
|
|
678
|
+
prompts.log.info(`Client ID: ${credentials.clientId}`);
|
|
436
679
|
if (credentials.expiresAt) {
|
|
437
680
|
const expiresAt = new Date(credentials.expiresAt);
|
|
438
|
-
|
|
681
|
+
prompts.log.info(`Token expires: ${expiresAt.toLocaleString()}`);
|
|
439
682
|
}
|
|
440
683
|
// Test connection
|
|
684
|
+
const spinner = prompts.spinner();
|
|
685
|
+
spinner.start('Testing connection');
|
|
441
686
|
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
442
687
|
const testResult = await client.testConnection();
|
|
443
688
|
if (testResult.success) {
|
|
444
|
-
|
|
689
|
+
spinner.stop('Connection test successful');
|
|
445
690
|
if (testResult.data.message) {
|
|
446
|
-
|
|
691
|
+
prompts.log.message(` ${testResult.data.message}`);
|
|
447
692
|
}
|
|
448
|
-
|
|
693
|
+
prompts.log.info(`Instance: ${testResult.data.email || credentials.instance}`);
|
|
449
694
|
}
|
|
450
695
|
else {
|
|
451
|
-
|
|
452
|
-
|
|
696
|
+
spinner.stop('Connection test failed');
|
|
697
|
+
prompts.log.error(`Error: ${testResult.error}`);
|
|
453
698
|
}
|
|
454
699
|
}
|
|
455
700
|
else {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
701
|
+
prompts.log.error('Status: Not authenticated');
|
|
702
|
+
prompts.log.message('Instance: Not configured');
|
|
703
|
+
prompts.log.message('Method: Not set');
|
|
704
|
+
prompts.log.message('');
|
|
705
|
+
prompts.log.info('Create .env file and run "snow-flow auth login"');
|
|
460
706
|
}
|
|
461
707
|
});
|
|
462
708
|
}
|