nowaikit 3.0.0 → 4.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/LICENSE +10 -10
- package/README.md +8 -6
- package/desktop/renderer/dist/assets/index-D46KUWoj.js +49 -0
- package/desktop/renderer/dist/index.html +5 -5
- package/desktop/serve.cjs +134 -10
- package/dist/a2a/agent-card.d.ts +8 -0
- package/dist/a2a/agent-card.d.ts.map +1 -0
- package/dist/a2a/agent-card.js +82 -0
- package/dist/a2a/agent-card.js.map +1 -0
- package/dist/a2a/index.d.ts +13 -0
- package/dist/a2a/index.d.ts.map +1 -0
- package/dist/a2a/index.js +74 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/a2a/task-manager.d.ts +20 -0
- package/dist/a2a/task-manager.d.ts.map +1 -0
- package/dist/a2a/task-manager.js +148 -0
- package/dist/a2a/task-manager.js.map +1 -0
- package/dist/a2a/types.d.ts +73 -0
- package/dist/a2a/types.d.ts.map +1 -0
- package/dist/a2a/types.js +6 -0
- package/dist/a2a/types.js.map +1 -0
- package/dist/api/index.d.ts +16 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +106 -0
- package/dist/api/index.js.map +1 -0
- package/dist/cli/config-store.d.ts +31 -0
- package/dist/cli/config-store.d.ts.map +1 -1
- package/dist/cli/config-store.js +44 -1
- package/dist/cli/config-store.js.map +1 -1
- package/dist/cli/index.js +115 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +669 -49
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/shortcuts.d.ts +2 -0
- package/dist/cli/shortcuts.d.ts.map +1 -0
- package/dist/cli/shortcuts.js +122 -0
- package/dist/cli/shortcuts.js.map +1 -0
- package/dist/dashboard/index.d.ts +7 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +111 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/direct/llm-client.d.ts +1 -1
- package/dist/direct/llm-client.d.ts.map +1 -1
- package/dist/direct/llm-client.js +9 -4
- package/dist/direct/llm-client.js.map +1 -1
- package/dist/prompts/capabilities/build-app.d.ts.map +1 -1
- package/dist/prompts/capabilities/build-app.js +2 -0
- package/dist/prompts/capabilities/build-app.js.map +1 -1
- package/dist/prompts/capabilities/build-atf-suite.d.ts +4 -0
- package/dist/prompts/capabilities/build-atf-suite.d.ts.map +1 -0
- package/dist/prompts/capabilities/build-atf-suite.js +143 -0
- package/dist/prompts/capabilities/build-atf-suite.js.map +1 -0
- package/dist/prompts/capabilities/build-business-rule.d.ts.map +1 -1
- package/dist/prompts/capabilities/build-business-rule.js +2 -0
- package/dist/prompts/capabilities/build-business-rule.js.map +1 -1
- package/dist/prompts/capabilities/build-catalog.d.ts.map +1 -1
- package/dist/prompts/capabilities/build-catalog.js +2 -0
- package/dist/prompts/capabilities/build-catalog.js.map +1 -1
- package/dist/prompts/capabilities/build-client-script.d.ts.map +1 -1
- package/dist/prompts/capabilities/build-client-script.js +2 -0
- package/dist/prompts/capabilities/build-client-script.js.map +1 -1
- package/dist/prompts/capabilities/build-flow.d.ts.map +1 -1
- package/dist/prompts/capabilities/build-flow.js +2 -0
- package/dist/prompts/capabilities/build-flow.js.map +1 -1
- package/dist/prompts/capabilities/build-portal.d.ts.map +1 -1
- package/dist/prompts/capabilities/build-portal.js +2 -0
- package/dist/prompts/capabilities/build-portal.js.map +1 -1
- package/dist/prompts/capabilities/build-rest-api.d.ts.map +1 -1
- package/dist/prompts/capabilities/build-rest-api.js +2 -0
- package/dist/prompts/capabilities/build-rest-api.js.map +1 -1
- package/dist/prompts/capabilities/build-test-plan.d.ts.map +1 -1
- package/dist/prompts/capabilities/build-test-plan.js +2 -0
- package/dist/prompts/capabilities/build-test-plan.js.map +1 -1
- package/dist/prompts/capabilities/build-uib.d.ts.map +1 -1
- package/dist/prompts/capabilities/build-uib.js +2 -0
- package/dist/prompts/capabilities/build-uib.js.map +1 -1
- package/dist/prompts/capabilities/docs-app.d.ts.map +1 -1
- package/dist/prompts/capabilities/docs-app.js +2 -0
- package/dist/prompts/capabilities/docs-app.js.map +1 -1
- package/dist/prompts/capabilities/docs-release.d.ts.map +1 -1
- package/dist/prompts/capabilities/docs-release.js +2 -0
- package/dist/prompts/capabilities/docs-release.js.map +1 -1
- package/dist/prompts/capabilities/docs-runbook.d.ts.map +1 -1
- package/dist/prompts/capabilities/docs-runbook.js +2 -0
- package/dist/prompts/capabilities/docs-runbook.js.map +1 -1
- package/dist/prompts/capabilities/docs-script.d.ts.map +1 -1
- package/dist/prompts/capabilities/docs-script.js +2 -0
- package/dist/prompts/capabilities/docs-script.js.map +1 -1
- package/dist/prompts/capabilities/ops-deploy.d.ts.map +1 -1
- package/dist/prompts/capabilities/ops-deploy.js +2 -0
- package/dist/prompts/capabilities/ops-deploy.js.map +1 -1
- package/dist/prompts/capabilities/ops-risk.d.ts.map +1 -1
- package/dist/prompts/capabilities/ops-risk.js +2 -0
- package/dist/prompts/capabilities/ops-risk.js.map +1 -1
- package/dist/prompts/capabilities/ops-triage.d.ts.map +1 -1
- package/dist/prompts/capabilities/ops-triage.js +2 -0
- package/dist/prompts/capabilities/ops-triage.js.map +1 -1
- package/dist/prompts/capabilities/review-acls.d.ts.map +1 -1
- package/dist/prompts/capabilities/review-acls.js +2 -0
- package/dist/prompts/capabilities/review-acls.js.map +1 -1
- package/dist/prompts/capabilities/review-code.d.ts.map +1 -1
- package/dist/prompts/capabilities/review-code.js +2 -0
- package/dist/prompts/capabilities/review-code.js.map +1 -1
- package/dist/prompts/capabilities/review-flows.d.ts.map +1 -1
- package/dist/prompts/capabilities/review-flows.js +2 -0
- package/dist/prompts/capabilities/review-flows.js.map +1 -1
- package/dist/prompts/capabilities/review-scripts.d.ts.map +1 -1
- package/dist/prompts/capabilities/review-scripts.js +2 -0
- package/dist/prompts/capabilities/review-scripts.js.map +1 -1
- package/dist/prompts/capabilities/scan-automation.d.ts.map +1 -1
- package/dist/prompts/capabilities/scan-automation.js +2 -0
- package/dist/prompts/capabilities/scan-automation.js.map +1 -1
- package/dist/prompts/capabilities/scan-cmdb.d.ts.map +1 -1
- package/dist/prompts/capabilities/scan-cmdb.js +2 -0
- package/dist/prompts/capabilities/scan-cmdb.js.map +1 -1
- package/dist/prompts/capabilities/scan-debt.d.ts.map +1 -1
- package/dist/prompts/capabilities/scan-debt.js +2 -0
- package/dist/prompts/capabilities/scan-debt.js.map +1 -1
- package/dist/prompts/capabilities/scan-health.d.ts.map +1 -1
- package/dist/prompts/capabilities/scan-health.js +2 -0
- package/dist/prompts/capabilities/scan-health.js.map +1 -1
- package/dist/prompts/capabilities/scan-security.d.ts.map +1 -1
- package/dist/prompts/capabilities/scan-security.js +2 -0
- package/dist/prompts/capabilities/scan-security.js.map +1 -1
- package/dist/prompts/capabilities/scan-upgrade.d.ts.map +1 -1
- package/dist/prompts/capabilities/scan-upgrade.js +2 -0
- package/dist/prompts/capabilities/scan-upgrade.js.map +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +47 -8
- package/dist/prompts/index.js.map +1 -1
- package/dist/reports/brand.d.ts +79 -0
- package/dist/reports/brand.d.ts.map +1 -0
- package/dist/reports/brand.js +204 -0
- package/dist/reports/brand.js.map +1 -0
- package/dist/reports/charts.d.ts +11 -0
- package/dist/reports/charts.d.ts.map +1 -0
- package/dist/reports/charts.js +91 -0
- package/dist/reports/charts.js.map +1 -0
- package/dist/reports/index.d.ts +13 -0
- package/dist/reports/index.d.ts.map +1 -0
- package/dist/reports/index.js +65 -0
- package/dist/reports/index.js.map +1 -0
- package/dist/reports/parser.d.ts +13 -0
- package/dist/reports/parser.d.ts.map +1 -0
- package/dist/reports/parser.js +202 -0
- package/dist/reports/parser.js.map +1 -0
- package/dist/reports/pdf-generator.d.ts +8 -0
- package/dist/reports/pdf-generator.d.ts.map +1 -0
- package/dist/reports/pdf-generator.js +244 -0
- package/dist/reports/pdf-generator.js.map +1 -0
- package/dist/reports/pptx-generator.d.ts +8 -0
- package/dist/reports/pptx-generator.d.ts.map +1 -0
- package/dist/reports/pptx-generator.js +273 -0
- package/dist/reports/pptx-generator.js.map +1 -0
- package/dist/reports/types.d.ts +60 -0
- package/dist/reports/types.d.ts.map +1 -0
- package/dist/reports/types.js +7 -0
- package/dist/reports/types.js.map +1 -0
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +10 -0
- package/dist/resources/index.js.map +1 -1
- package/dist/resources/query-syntax.d.ts +6 -0
- package/dist/resources/query-syntax.d.ts.map +1 -0
- package/dist/resources/query-syntax.js +113 -0
- package/dist/resources/query-syntax.js.map +1 -0
- package/dist/sdk/index.d.ts +51 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +55 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/server.d.ts +2 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +103 -92
- package/dist/server.js.map +1 -1
- package/dist/servicenow/client.js +1 -1
- package/dist/servicenow/client.js.map +1 -1
- package/dist/tools/ai-agents.d.ts +145 -0
- package/dist/tools/ai-agents.d.ts.map +1 -0
- package/dist/tools/ai-agents.js +185 -0
- package/dist/tools/ai-agents.js.map +1 -0
- package/dist/tools/cmdb-reconciliation.d.ts +112 -0
- package/dist/tools/cmdb-reconciliation.d.ts.map +1 -0
- package/dist/tools/cmdb-reconciliation.js +267 -0
- package/dist/tools/cmdb-reconciliation.js.map +1 -0
- package/dist/tools/discovery.d.ts +34 -0
- package/dist/tools/discovery.d.ts.map +1 -0
- package/dist/tools/discovery.js +168 -0
- package/dist/tools/discovery.js.map +1 -0
- package/dist/tools/fluent.d.ts +105 -11
- package/dist/tools/fluent.d.ts.map +1 -1
- package/dist/tools/fluent.js +118 -1
- package/dist/tools/fluent.js.map +1 -1
- package/dist/tools/index.d.ts +840 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +44 -6
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/ml.d.ts +104 -0
- package/dist/tools/ml.d.ts.map +1 -1
- package/dist/tools/ml.js +139 -0
- package/dist/tools/ml.js.map +1 -1
- package/dist/tools/now-assist-skills.d.ts +129 -0
- package/dist/tools/now-assist-skills.d.ts.map +1 -0
- package/dist/tools/now-assist-skills.js +128 -0
- package/dist/tools/now-assist-skills.js.map +1 -0
- package/dist/tools/orchestration.d.ts +132 -0
- package/dist/tools/orchestration.d.ts.map +1 -0
- package/dist/tools/orchestration.js +320 -0
- package/dist/tools/orchestration.js.map +1 -0
- package/dist/tools/reporting.d.ts +127 -1
- package/dist/tools/reporting.d.ts.map +1 -1
- package/dist/tools/reporting.js +64 -0
- package/dist/tools/reporting.js.map +1 -1
- package/dist/tools/schema-cache.d.ts +44 -0
- package/dist/tools/schema-cache.d.ts.map +1 -0
- package/dist/tools/schema-cache.js +127 -0
- package/dist/tools/schema-cache.js.map +1 -0
- package/dist/tools-manifest.json +1250 -25
- package/dist/transport/auth-middleware.d.ts +16 -0
- package/dist/transport/auth-middleware.d.ts.map +1 -0
- package/dist/transport/auth-middleware.js +31 -0
- package/dist/transport/auth-middleware.js.map +1 -0
- package/dist/transport/http-server.d.ts +44 -0
- package/dist/transport/http-server.d.ts.map +1 -0
- package/dist/transport/http-server.js +172 -0
- package/dist/transport/http-server.js.map +1 -0
- package/dist/transport/index.d.ts +19 -0
- package/dist/transport/index.d.ts.map +1 -0
- package/dist/transport/index.js +105 -0
- package/dist/transport/index.js.map +1 -0
- package/dist/utils/permissions.d.ts +2 -0
- package/dist/utils/permissions.d.ts.map +1 -1
- package/dist/utils/permissions.js +8 -0
- package/dist/utils/permissions.js.map +1 -1
- package/package.json +27 -4
- package/desktop/renderer/dist/assets/index-B-6BYnh8.js +0 -49
package/dist/cli/setup.js
CHANGED
|
@@ -2,19 +2,24 @@
|
|
|
2
2
|
* Interactive setup wizard — `nowaikit setup`
|
|
3
3
|
*
|
|
4
4
|
* Walks the user through:
|
|
5
|
-
* 1.
|
|
6
|
-
* 2.
|
|
7
|
-
* 3.
|
|
8
|
-
* 4.
|
|
9
|
-
* 5.
|
|
10
|
-
* 6.
|
|
11
|
-
* 7.
|
|
5
|
+
* 1. ServiceNow instance (with reachability auto-detect)
|
|
6
|
+
* 2. Auth method (Basic / OAuth)
|
|
7
|
+
* 3. Credentials
|
|
8
|
+
* 4. Connection test (with auto-fix suggestions on failure)
|
|
9
|
+
* 5. Permission tier / tool package
|
|
10
|
+
* 6. Component selection — MCP Server / SDK / Apex AI Skills (checkbox multi-select)
|
|
11
|
+
* 7. AI Provider — Ollama/LM Studio auto-detect, cloud API key (shown when Apex enabled)
|
|
12
|
+
* 8. Power Tools & Capabilities overview
|
|
13
|
+
* 9. Prompts, Shortcuts & Resources
|
|
14
|
+
* 10. AI Client Installation
|
|
15
|
+
* 11. Auto-Configuration (npm link, starter file, client config)
|
|
12
16
|
*/
|
|
13
17
|
import { input, password, select, checkbox, confirm } from '@inquirer/prompts';
|
|
14
18
|
import chalk from 'chalk';
|
|
15
19
|
import ora from 'ora';
|
|
16
20
|
import { execSync } from 'child_process';
|
|
17
21
|
import { fileURLToPath } from 'url';
|
|
22
|
+
import { writeFileSync, existsSync } from 'fs';
|
|
18
23
|
import path from 'path';
|
|
19
24
|
import { addInstance, loadConfig } from './config-store.js';
|
|
20
25
|
import { detectClients } from './detect-clients.js';
|
|
@@ -32,10 +37,11 @@ const accent = teal; // accent (AI highlight)
|
|
|
32
37
|
const success = chalk.hex('#10B981'); // emerald-500
|
|
33
38
|
const warn = chalk.hex('#FF6B35'); // amber/orange
|
|
34
39
|
const err = chalk.hex('#E8466A'); // pink-500
|
|
40
|
+
const gray = chalk.hex('#8B949E'); // muted gray — AI contrast in banner
|
|
35
41
|
const dim = chalk.gray; // terminal-adaptive dim text
|
|
36
42
|
const white = chalk.bold; // terminal-adaptive primary text (works on light + dark)
|
|
37
43
|
const subtle = chalk.dim; // terminal-adaptive secondary text
|
|
38
|
-
const TOTAL_STEPS =
|
|
44
|
+
const TOTAL_STEPS = 11;
|
|
39
45
|
const TOOL_PACKAGES = [
|
|
40
46
|
{ value: 'full', name: `${brand('full')} ${dim('— all 400+ tools')}` },
|
|
41
47
|
{ value: 'service_desk', name: `${brand('service_desk')} ${dim('— help desk agents')}` },
|
|
@@ -87,10 +93,13 @@ function logoText() {
|
|
|
87
93
|
}
|
|
88
94
|
function banner() {
|
|
89
95
|
console.log('');
|
|
90
|
-
// ASCII art logo — "NowAIKit" in
|
|
91
|
-
console.log(
|
|
92
|
-
console.log(teal('
|
|
93
|
-
console.log(
|
|
96
|
+
// ASCII art logo — "NowAIKit" in block thick style, NOW/KIT teal, AI gray
|
|
97
|
+
console.log(teal.bold(' ███╗ ██╗ ██████╗ ██╗ ██╗') + ' ' + gray(' █████╗ ██╗') + ' ' + teal.bold('██╗ ██╗██╗████████╗'));
|
|
98
|
+
console.log(teal.bold(' ████╗ ██║██╔═══██╗██║ ██║') + ' ' + gray('██╔══██╗██║') + ' ' + teal.bold('██║ ██╔╝██║╚══██╔══╝'));
|
|
99
|
+
console.log(teal.bold(' ██╔██╗██║██║ ██║██║ █╗ ██║') + ' ' + gray('███████║██║') + ' ' + teal.bold('█████╔╝ ██║ ██║'));
|
|
100
|
+
console.log(teal.bold(' ██║╚████║██║ ██║██║███╗██║') + ' ' + gray('██╔══██║██║') + ' ' + teal.bold('██╔═██╗ ██║ ██║'));
|
|
101
|
+
console.log(teal.bold(' ██║ ╚███║╚██████╔╝╚███╔███╔╝') + ' ' + gray('██║ ██║██║') + ' ' + teal.bold('██║ ██╗██║ ██║'));
|
|
102
|
+
console.log(teal.bold(' ╚═╝ ╚══╝ ╚═════╝ ╚══╝╚══╝') + ' ' + gray('╚═╝ ╚═╝╚═╝') + ' ' + teal.bold('╚═╝ ╚═╝╚═╝ ╚═╝') + ' ' + teal('✦'));
|
|
94
103
|
console.log('');
|
|
95
104
|
console.log(` ${logoText()} ${dim('—')} ${subtle('Setup Wizard')}`);
|
|
96
105
|
console.log('');
|
|
@@ -140,6 +149,22 @@ function extractFetchError(error) {
|
|
|
140
149
|
}
|
|
141
150
|
return error.message;
|
|
142
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Quick HEAD check — returns true if the URL is reachable (any HTTP response).
|
|
154
|
+
* Does not throw; returns false on any network / DNS error.
|
|
155
|
+
*/
|
|
156
|
+
async function isUrlReachable(url) {
|
|
157
|
+
try {
|
|
158
|
+
const controller = new AbortController();
|
|
159
|
+
const timeout = setTimeout(() => controller.abort(), 8000);
|
|
160
|
+
await fetch(url, { method: 'HEAD', signal: controller.signal, redirect: 'follow' });
|
|
161
|
+
clearTimeout(timeout);
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
143
168
|
async function testConnection(instanceUrl, authMethod, creds) {
|
|
144
169
|
// Pre-flight: verify the hostname is reachable before attempting auth
|
|
145
170
|
const spinner = ora({
|
|
@@ -235,6 +260,41 @@ async function ensureGlobalCommand() {
|
|
|
235
260
|
}
|
|
236
261
|
}
|
|
237
262
|
}
|
|
263
|
+
async function detectLocalProviders() {
|
|
264
|
+
const result = {
|
|
265
|
+
ollama: { running: false, models: [] },
|
|
266
|
+
lmstudio: { running: false, models: [] },
|
|
267
|
+
};
|
|
268
|
+
// Ollama: GET http://localhost:11434/api/tags
|
|
269
|
+
try {
|
|
270
|
+
const controller = new AbortController();
|
|
271
|
+
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
272
|
+
const res = await fetch('http://localhost:11434/api/tags', { signal: controller.signal });
|
|
273
|
+
clearTimeout(timeout);
|
|
274
|
+
if (res.ok) {
|
|
275
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
276
|
+
const data = await res.json();
|
|
277
|
+
result.ollama.running = true;
|
|
278
|
+
result.ollama.models = (data.models || []).map((m) => m.name.replace(/:latest$/, ''));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
catch { /* not running */ }
|
|
282
|
+
// LM Studio: GET http://localhost:1234/v1/models
|
|
283
|
+
try {
|
|
284
|
+
const controller = new AbortController();
|
|
285
|
+
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
286
|
+
const res = await fetch('http://localhost:1234/v1/models', { signal: controller.signal });
|
|
287
|
+
clearTimeout(timeout);
|
|
288
|
+
if (res.ok) {
|
|
289
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
290
|
+
const data = await res.json();
|
|
291
|
+
result.lmstudio.running = true;
|
|
292
|
+
result.lmstudio.models = (data.data || []).map((m) => m.id);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch { /* not running */ }
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
238
298
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
239
299
|
// MAIN SETUP FLOW
|
|
240
300
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -259,32 +319,91 @@ export async function runSetup(options = {}) {
|
|
|
259
319
|
sectionLabel('Enter your instance name — just the subdomain, not the full URL');
|
|
260
320
|
console.log(dim(' Example: if your URL is https://acme.service-now.com, enter ') + brand('acme'));
|
|
261
321
|
console.log('');
|
|
262
|
-
const instanceId = await input({
|
|
263
|
-
message: brand('?') + ' Instance name ' + dim('(e.g. acme, dev12345)') + brand(':'),
|
|
264
|
-
validate: (v) => {
|
|
265
|
-
if (!v.trim())
|
|
266
|
-
return 'Instance name is required';
|
|
267
|
-
if (/\s/.test(v))
|
|
268
|
-
return 'No spaces allowed';
|
|
269
|
-
return true;
|
|
270
|
-
},
|
|
271
|
-
});
|
|
272
322
|
let instanceUrl;
|
|
273
|
-
let trimmed
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
323
|
+
let trimmed;
|
|
324
|
+
// eslint-disable-next-line no-constant-condition
|
|
325
|
+
while (true) {
|
|
326
|
+
const instanceId = await input({
|
|
327
|
+
message: brand('?') + ' Instance name ' + dim('(e.g. acme, dev12345)') + brand(':'),
|
|
328
|
+
validate: (v) => {
|
|
329
|
+
if (!v.trim())
|
|
330
|
+
return 'Instance name is required';
|
|
331
|
+
if (/\s/.test(v))
|
|
332
|
+
return 'No spaces allowed';
|
|
333
|
+
return true;
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
trimmed = instanceId.trim().toLowerCase();
|
|
337
|
+
// Strip full URL if user pasted one
|
|
338
|
+
if (trimmed.startsWith('https://')) {
|
|
339
|
+
instanceUrl = trimmed.replace(/\/+$/, '');
|
|
340
|
+
trimmed = instanceUrl.replace('https://', '').replace('.service-now.com', '');
|
|
341
|
+
}
|
|
342
|
+
else if (trimmed.includes('.service-now.com')) {
|
|
343
|
+
trimmed = trimmed.replace('.service-now.com', '').replace(/\/+$/, '');
|
|
344
|
+
instanceUrl = `https://${trimmed}.service-now.com`;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
instanceUrl = `https://${trimmed}.service-now.com`;
|
|
348
|
+
}
|
|
349
|
+
console.log(` ${success('→')} ${dim('URL:')} ${accent(instanceUrl)}`);
|
|
350
|
+
console.log('');
|
|
351
|
+
// Auto-detect reachability before proceeding
|
|
352
|
+
const reachSpinner = ora({ text: dim(' Checking if instance is reachable…'), color: 'cyan' }).start();
|
|
353
|
+
const reachable = await isUrlReachable(instanceUrl);
|
|
354
|
+
if (reachable) {
|
|
355
|
+
reachSpinner.succeed(success(' Instance is reachable'));
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
// Not reachable — offer auto-fix options
|
|
359
|
+
reachSpinner.warn(warn(' Instance not reachable — it may be offline or the name may be wrong'));
|
|
360
|
+
console.log('');
|
|
361
|
+
const fixAction = await select({
|
|
362
|
+
message: warn('?') + ' Auto-fix options' + brand(':'),
|
|
363
|
+
choices: [
|
|
364
|
+
{ name: `${brand('↻')} Try again with same URL`, value: 'retry' },
|
|
365
|
+
{ name: `${accent('/')} Try with /api prefix ${dim(`(${instanceUrl}/api)`)}`, value: 'try_api' },
|
|
366
|
+
{ name: `${brand('🔒')} Try HTTPS variant ${dim('(already HTTPS — refresh)')}`, value: 'try_https' },
|
|
367
|
+
{ name: `${dim('✏')} Re-enter instance name`, value: 'reenter' },
|
|
368
|
+
{ name: `${subtle('→')} Continue anyway ${dim('(fix manually later)')}`, value: 'continue' },
|
|
369
|
+
],
|
|
370
|
+
});
|
|
371
|
+
if (fixAction === 'continue') {
|
|
372
|
+
console.log(` ${dim('→')} Continuing with unreachable instance — you can fix this in your config later.`);
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
if (fixAction === 'try_api') {
|
|
376
|
+
const apiUrl = `${instanceUrl}/api`;
|
|
377
|
+
const apiReachable = await isUrlReachable(apiUrl);
|
|
378
|
+
if (apiReachable) {
|
|
379
|
+
console.log(` ${success('✓')} Reachable with /api prefix — continuing`);
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
console.log(` ${warn('→')} Still not reachable. Try re-entering the instance name.`);
|
|
383
|
+
}
|
|
384
|
+
if (fixAction === 'try_https' || fixAction === 'retry') {
|
|
385
|
+
const recheck = await isUrlReachable(instanceUrl);
|
|
386
|
+
if (recheck) {
|
|
387
|
+
console.log(` ${success('✓')} Instance is now reachable`);
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
console.log(` ${warn('→')} Still not reachable.`);
|
|
391
|
+
}
|
|
392
|
+
// 'reenter' falls through to top of loop naturally
|
|
393
|
+
if (fixAction !== 'reenter') {
|
|
394
|
+
// For all non-reenter choices that didn't break, offer to continue or reenter
|
|
395
|
+
const nextAction = await select({
|
|
396
|
+
message: warn('?') + ' What next' + brand(':'),
|
|
397
|
+
choices: [
|
|
398
|
+
{ name: `${dim('→')} Continue anyway`, value: 'continue' },
|
|
399
|
+
{ name: `${accent('✏')} Re-enter instance name`, value: 'reenter' },
|
|
400
|
+
],
|
|
401
|
+
});
|
|
402
|
+
if (nextAction === 'continue')
|
|
403
|
+
break;
|
|
404
|
+
}
|
|
405
|
+
// loop continues for 'reenter'
|
|
285
406
|
}
|
|
286
|
-
console.log(` ${success('→')} ${dim('URL:')} ${accent(instanceUrl)}`);
|
|
287
|
-
console.log('');
|
|
288
407
|
const instanceName = await input({
|
|
289
408
|
message: brand('?') + ' Short name ' + dim('(e.g. prod, dev, acme)') + brand(':'),
|
|
290
409
|
default: trimmed.replace(/\.service-now\.com$/, '').replace(/[^a-z0-9_-]/gi, '-'),
|
|
@@ -377,10 +496,12 @@ export async function runSetup(options = {}) {
|
|
|
377
496
|
}
|
|
378
497
|
console.log('');
|
|
379
498
|
const action = await select({
|
|
380
|
-
message: warn('?') + '
|
|
499
|
+
message: warn('?') + ' Connection failed — what would you like to do?' + brand(':'),
|
|
381
500
|
choices: [
|
|
382
501
|
{ name: `${brand('↻')} Retry connection`, value: 'retry' },
|
|
383
502
|
{ name: `${accent('✏')} Re-enter credentials`, value: 'creds' },
|
|
503
|
+
{ name: `${brand('/')} Auto-fix: try with /api prefix`, value: 'fix_api' },
|
|
504
|
+
{ name: `${brand('🔒')} Auto-fix: switch to HTTPS`, value: 'fix_https' },
|
|
384
505
|
{ name: `${subtle('💾')} Save config anyway ${dim('(fix later)')}`, value: 'save' },
|
|
385
506
|
{ name: `${err('✕')} Cancel setup`, value: 'cancel' },
|
|
386
507
|
],
|
|
@@ -393,6 +514,29 @@ export async function runSetup(options = {}) {
|
|
|
393
514
|
}
|
|
394
515
|
if (action === 'save')
|
|
395
516
|
break;
|
|
517
|
+
if (action === 'fix_api') {
|
|
518
|
+
// Try appending /api to the URL
|
|
519
|
+
const apiUrl = instanceUrl.endsWith('/api') ? instanceUrl : `${instanceUrl}/api`;
|
|
520
|
+
console.log(` ${dim('→')} Trying ${accent(apiUrl)}…`);
|
|
521
|
+
const reachable = await isUrlReachable(apiUrl);
|
|
522
|
+
if (reachable) {
|
|
523
|
+
instanceUrl = apiUrl;
|
|
524
|
+
console.log(` ${success('✓')} URL updated to ${accent(instanceUrl)}`);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
console.log(` ${warn('→')} /api prefix did not help. Try re-entering credentials.`);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
if (action === 'fix_https') {
|
|
531
|
+
// Ensure URL uses HTTPS
|
|
532
|
+
if (instanceUrl.startsWith('http://')) {
|
|
533
|
+
instanceUrl = instanceUrl.replace('http://', 'https://');
|
|
534
|
+
console.log(` ${success('✓')} Switched to HTTPS: ${accent(instanceUrl)}`);
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
console.log(` ${dim('→')} URL is already HTTPS: ${accent(instanceUrl)}`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
396
540
|
if (action === 'creds') {
|
|
397
541
|
console.log('');
|
|
398
542
|
if (authMethod === 'basic') {
|
|
@@ -416,15 +560,302 @@ export async function runSetup(options = {}) {
|
|
|
416
560
|
choices: TOOL_PACKAGES,
|
|
417
561
|
});
|
|
418
562
|
const writeEnabled = await confirm({
|
|
419
|
-
message: brand('?') + ' Enable write operations ' + dim('(create/update/delete)') + brand('?'),
|
|
563
|
+
message: brand('?') + ' Enable write operations ' + dim('(create/update/delete records)') + brand('?'),
|
|
420
564
|
default: false,
|
|
421
565
|
});
|
|
566
|
+
let scriptingEnabled = false;
|
|
567
|
+
let cmdbWriteEnabled = false;
|
|
568
|
+
let atfEnabled = false;
|
|
569
|
+
if (writeEnabled) {
|
|
570
|
+
console.log('');
|
|
571
|
+
sectionLabel('Advanced write permissions');
|
|
572
|
+
console.log(dim(' These unlock powerful features but should be used carefully in production.'));
|
|
573
|
+
console.log('');
|
|
574
|
+
scriptingEnabled = await confirm({
|
|
575
|
+
message: brand('?') + ' Enable script execution ' + dim('(Background Scripts, server-side JS)') + brand('?'),
|
|
576
|
+
default: false,
|
|
577
|
+
});
|
|
578
|
+
cmdbWriteEnabled = await confirm({
|
|
579
|
+
message: brand('?') + ' Enable CMDB write operations ' + dim('(create/update CIs and relationships)') + brand('?'),
|
|
580
|
+
default: false,
|
|
581
|
+
});
|
|
582
|
+
atfEnabled = await confirm({
|
|
583
|
+
message: brand('?') + ' Enable ATF test execution ' + dim('(run Automated Test Framework suites)') + brand('?'),
|
|
584
|
+
default: false,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
422
587
|
const nowAssistEnabled = await confirm({
|
|
423
588
|
message: brand('?') + ' Enable Now Assist / AI features' + brand('?'),
|
|
424
589
|
default: false,
|
|
425
590
|
});
|
|
426
|
-
// ─── Step 6:
|
|
427
|
-
step(6, '
|
|
591
|
+
// ─── Step 6: Component Selection (checkbox multi-select) ───────────────────
|
|
592
|
+
step(6, 'Component Selection');
|
|
593
|
+
sectionLabel('Choose which NowAIKit components to enable');
|
|
594
|
+
console.log(dim(' Use Space to toggle, Enter to confirm. At least one must be selected.'));
|
|
595
|
+
console.log('');
|
|
596
|
+
let selectedComponents = [];
|
|
597
|
+
while (true) {
|
|
598
|
+
selectedComponents = await checkbox({
|
|
599
|
+
message: brand('?') + ' Enable components ' + dim('(space to toggle, enter to confirm)') + brand(':'),
|
|
600
|
+
choices: [
|
|
601
|
+
{
|
|
602
|
+
name: `${brand('MCP Server')} ${dim('— AI clients discover and call tools automatically')}`,
|
|
603
|
+
value: 'mcp',
|
|
604
|
+
checked: true,
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
name: `${accent('TypeScript SDK')} ${dim('— import NowAIKit directly in your code')}`,
|
|
608
|
+
value: 'sdk',
|
|
609
|
+
checked: false,
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
name: `${teal('AI Skills (Apex)')} ${dim('— 26 expert capabilities (scan, review, build, ops, docs)')}`,
|
|
613
|
+
value: 'apex',
|
|
614
|
+
checked: true,
|
|
615
|
+
},
|
|
616
|
+
],
|
|
617
|
+
});
|
|
618
|
+
if (selectedComponents.length > 0)
|
|
619
|
+
break;
|
|
620
|
+
console.log(` ${warn('!')} At least one component must be selected.`);
|
|
621
|
+
console.log('');
|
|
622
|
+
}
|
|
623
|
+
const mcpEnabled = selectedComponents.includes('mcp');
|
|
624
|
+
const sdkEnabled = selectedComponents.includes('sdk');
|
|
625
|
+
const apexEnabled = selectedComponents.includes('apex');
|
|
626
|
+
// Compute legacy integrationMode for backward compat
|
|
627
|
+
let integrationMode;
|
|
628
|
+
if (mcpEnabled && sdkEnabled)
|
|
629
|
+
integrationMode = 'both';
|
|
630
|
+
else if (sdkEnabled)
|
|
631
|
+
integrationMode = 'sdk';
|
|
632
|
+
else
|
|
633
|
+
integrationMode = 'mcp';
|
|
634
|
+
console.log('');
|
|
635
|
+
if (mcpEnabled)
|
|
636
|
+
console.log(` ${success('✓')} MCP Server — AI clients will auto-discover your 400+ tools`);
|
|
637
|
+
if (sdkEnabled)
|
|
638
|
+
console.log(` ${success('✓')} TypeScript SDK — import NowAIKit in your project`);
|
|
639
|
+
if (apexEnabled)
|
|
640
|
+
console.log(` ${success('✓')} AI Skills (Apex) — 26 capabilities enabled`);
|
|
641
|
+
if (!apexEnabled)
|
|
642
|
+
console.log(` ${dim('✗')} Apex AI Skills disabled — only MCP tools and ITSM prompts active`);
|
|
643
|
+
console.log('');
|
|
644
|
+
if (sdkEnabled) {
|
|
645
|
+
box([
|
|
646
|
+
white('SDK mode selected'),
|
|
647
|
+
dim('Import NowAIKit in your TypeScript/JavaScript code:'),
|
|
648
|
+
'',
|
|
649
|
+
brand(" import { ServiceNowClient } from 'nowaikit/sdk';"),
|
|
650
|
+
brand(" import { executeDirectly } from 'nowaikit/sdk';"),
|
|
651
|
+
'',
|
|
652
|
+
...(mcpEnabled ? [] : [dim('MCP client configuration will be skipped.')]),
|
|
653
|
+
]);
|
|
654
|
+
console.log('');
|
|
655
|
+
}
|
|
656
|
+
if (mcpEnabled && sdkEnabled) {
|
|
657
|
+
console.log(` ${success('→')} ${dim('Both MCP server and SDK imports will be available.')}`);
|
|
658
|
+
console.log('');
|
|
659
|
+
}
|
|
660
|
+
// ─── Step 7: AI Provider (for Apex capabilities) ──────────────────────────
|
|
661
|
+
let aiProvider;
|
|
662
|
+
let aiModel;
|
|
663
|
+
let aiApiKey;
|
|
664
|
+
let aiBaseUrl;
|
|
665
|
+
if (apexEnabled) {
|
|
666
|
+
step(7, 'AI Provider');
|
|
667
|
+
sectionLabel('Configure the LLM for Apex AI capabilities (direct mode)');
|
|
668
|
+
console.log(dim(' Apex capabilities like scan-health need an LLM for analysis.'));
|
|
669
|
+
console.log(dim(' MCP mode (Claude Desktop, Cursor) uses the host AI — no key needed there.'));
|
|
670
|
+
console.log('');
|
|
671
|
+
const detectSpinner = ora({ text: dim(' Detecting local AI providers…'), color: 'cyan' }).start();
|
|
672
|
+
const localProviders = await detectLocalProviders();
|
|
673
|
+
detectSpinner.stop();
|
|
674
|
+
if (localProviders.ollama.running) {
|
|
675
|
+
const modelList = localProviders.ollama.models.length > 0
|
|
676
|
+
? localProviders.ollama.models.slice(0, 5).join(', ')
|
|
677
|
+
: 'no models pulled yet';
|
|
678
|
+
console.log(` ${success('✓')} Ollama detected at localhost:11434 (${dim(modelList)})`);
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
console.log(` ${dim('✗')} Ollama not detected`);
|
|
682
|
+
}
|
|
683
|
+
if (localProviders.lmstudio.running) {
|
|
684
|
+
const modelList = localProviders.lmstudio.models.length > 0
|
|
685
|
+
? localProviders.lmstudio.models.slice(0, 3).join(', ')
|
|
686
|
+
: 'model loaded';
|
|
687
|
+
console.log(` ${success('✓')} LM Studio detected at localhost:1234 (${dim(modelList)})`);
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
console.log(` ${dim('✗')} LM Studio not detected`);
|
|
691
|
+
}
|
|
692
|
+
console.log('');
|
|
693
|
+
const providerChoices = [];
|
|
694
|
+
if (localProviders.ollama.running) {
|
|
695
|
+
providerChoices.push({
|
|
696
|
+
name: `${brand('Ollama')} ${dim('(local — detected, running)')}`,
|
|
697
|
+
value: 'ollama',
|
|
698
|
+
});
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
providerChoices.push({
|
|
702
|
+
name: `${dim('Ollama')} ${dim('(local — not detected)')}`,
|
|
703
|
+
value: 'ollama',
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
if (localProviders.lmstudio.running) {
|
|
707
|
+
providerChoices.push({
|
|
708
|
+
name: `${brand('LM Studio')} ${dim('(local — detected, running)')}`,
|
|
709
|
+
value: 'lmstudio',
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
providerChoices.push({
|
|
714
|
+
name: `${dim('LM Studio')} ${dim('(local — not detected)')}`,
|
|
715
|
+
value: 'lmstudio',
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
providerChoices.push({ name: `${accent('Anthropic')} ${dim('(cloud — requires API key)')}`, value: 'anthropic' }, { name: `${accent('OpenAI')} ${dim('(cloud — requires API key)')}`, value: 'openai' }, { name: `${subtle('Skip')} ${dim('(configure later with --provider flag or env var)')}`, value: 'skip' });
|
|
719
|
+
const selectedProvider = await select({
|
|
720
|
+
message: brand('?') + ' Select AI provider' + brand(':'),
|
|
721
|
+
choices: providerChoices,
|
|
722
|
+
default: localProviders.ollama.running ? 'ollama'
|
|
723
|
+
: localProviders.lmstudio.running ? 'lmstudio'
|
|
724
|
+
: undefined,
|
|
725
|
+
});
|
|
726
|
+
if (selectedProvider !== 'skip') {
|
|
727
|
+
aiProvider = selectedProvider;
|
|
728
|
+
// Model selection for local providers with detected models
|
|
729
|
+
if (selectedProvider === 'ollama' && localProviders.ollama.models.length > 0) {
|
|
730
|
+
const modelChoices = localProviders.ollama.models.map(m => ({
|
|
731
|
+
name: brand(m),
|
|
732
|
+
value: m,
|
|
733
|
+
}));
|
|
734
|
+
aiModel = await select({
|
|
735
|
+
message: brand('?') + ' Select Ollama model' + brand(':'),
|
|
736
|
+
choices: modelChoices,
|
|
737
|
+
});
|
|
738
|
+
console.log(` ${success('✓')} Ollama configured with ${accent(aiModel)} — no API key needed`);
|
|
739
|
+
}
|
|
740
|
+
else if (selectedProvider === 'lmstudio' && localProviders.lmstudio.models.length > 0) {
|
|
741
|
+
const modelChoices = localProviders.lmstudio.models.map(m => ({
|
|
742
|
+
name: brand(m),
|
|
743
|
+
value: m,
|
|
744
|
+
}));
|
|
745
|
+
aiModel = await select({
|
|
746
|
+
message: brand('?') + ' Select LM Studio model' + brand(':'),
|
|
747
|
+
choices: modelChoices,
|
|
748
|
+
});
|
|
749
|
+
console.log(` ${success('✓')} LM Studio configured with ${accent(aiModel)} — no API key needed`);
|
|
750
|
+
}
|
|
751
|
+
else if (selectedProvider === 'ollama') {
|
|
752
|
+
aiModel = await input({
|
|
753
|
+
message: brand('?') + ' Ollama model name ' + dim('(e.g. llama3.3, codellama)') + brand(':'),
|
|
754
|
+
default: 'llama3.3',
|
|
755
|
+
});
|
|
756
|
+
console.log(` ${success('✓')} Ollama configured with ${accent(aiModel)} — no API key needed`);
|
|
757
|
+
}
|
|
758
|
+
else if (selectedProvider === 'lmstudio') {
|
|
759
|
+
console.log(` ${success('✓')} LM Studio configured — uses whatever model is loaded`);
|
|
760
|
+
}
|
|
761
|
+
else if (selectedProvider === 'anthropic') {
|
|
762
|
+
aiApiKey = await password({
|
|
763
|
+
message: brand('?') + ' Anthropic API key ' + dim('(sk-ant-...)') + brand(':'),
|
|
764
|
+
mask: '•',
|
|
765
|
+
});
|
|
766
|
+
aiModel = await input({
|
|
767
|
+
message: brand('?') + ' Model ' + dim('(Enter for default)') + brand(':'),
|
|
768
|
+
default: 'claude-sonnet-4-6-20250514',
|
|
769
|
+
});
|
|
770
|
+
console.log(` ${success('✓')} Anthropic configured with ${accent(aiModel)}`);
|
|
771
|
+
}
|
|
772
|
+
else if (selectedProvider === 'openai') {
|
|
773
|
+
aiApiKey = await password({
|
|
774
|
+
message: brand('?') + ' OpenAI API key ' + dim('(sk-...)') + brand(':'),
|
|
775
|
+
mask: '•',
|
|
776
|
+
});
|
|
777
|
+
aiModel = await input({
|
|
778
|
+
message: brand('?') + ' Model ' + dim('(Enter for default)') + brand(':'),
|
|
779
|
+
default: 'gpt-5.4',
|
|
780
|
+
});
|
|
781
|
+
console.log(` ${success('✓')} OpenAI configured with ${accent(aiModel)}`);
|
|
782
|
+
}
|
|
783
|
+
// Custom base URL option for advanced users
|
|
784
|
+
const useCustomUrl = await confirm({
|
|
785
|
+
message: brand('?') + ' Use a custom API endpoint URL' + brand('?'),
|
|
786
|
+
default: false,
|
|
787
|
+
});
|
|
788
|
+
if (useCustomUrl) {
|
|
789
|
+
aiBaseUrl = await input({
|
|
790
|
+
message: brand('?') + ' Custom endpoint URL' + brand(':'),
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
console.log(` ${dim('→')} AI provider skipped — use --provider flag or environment variables when running capabilities.`);
|
|
796
|
+
}
|
|
797
|
+
console.log('');
|
|
798
|
+
}
|
|
799
|
+
// ─── Step 8: Power Tools & Capabilities ─────────────────────────────────────
|
|
800
|
+
step(8, 'Power Tools & Capabilities');
|
|
801
|
+
console.log(` ${accent('▸')} ${white('Power Tools')} ${dim('— advanced features included in NowAIKit v3.0')}`);
|
|
802
|
+
console.log('');
|
|
803
|
+
console.log(` ${brand('fluent_query')} ${dim('GlideQuery-style queries from your AI — structured,')}`);
|
|
804
|
+
console.log(` ${dim('no scripts needed. Filter, aggregate, group, sort.')}`);
|
|
805
|
+
console.log('');
|
|
806
|
+
console.log(` ${brand('batch_request')} ${dim('Bundle up to 50 API calls in one request.')}`);
|
|
807
|
+
console.log(` ${dim('Dramatically faster for bulk operations.')}`);
|
|
808
|
+
console.log('');
|
|
809
|
+
console.log(` ${brand('execute_script')} ${dim('Run server-side JavaScript directly on your instance.')}`);
|
|
810
|
+
console.log(` ${dim('Requires scripting permission (Step 5).')}`);
|
|
811
|
+
console.log('');
|
|
812
|
+
divider();
|
|
813
|
+
console.log('');
|
|
814
|
+
if (apexEnabled) {
|
|
815
|
+
console.log(` ${accent('▸')} ${white('26 AI Capabilities')} ${dim('— run directly from terminal (no MCP client needed)')}`);
|
|
816
|
+
console.log('');
|
|
817
|
+
const capCategories = [
|
|
818
|
+
{ icon: '🔍', label: 'Scan & Monitor', items: ['health', 'security', 'debt', 'upgrade', 'cmdb', 'automation'] },
|
|
819
|
+
{ icon: '📋', label: 'Review & Audit', items: ['code', 'acls', 'scripts', 'flows'] },
|
|
820
|
+
{ icon: '🔨', label: 'Build & Generate', items: ['business-rule', 'client-script', 'test-plan', 'app', 'flow', 'portal', 'uib', 'catalog', 'rest-api'] },
|
|
821
|
+
{ icon: '⚡', label: 'Operations', items: ['triage', 'deploy', 'risk'] },
|
|
822
|
+
{ icon: '📄', label: 'Documentation', items: ['app', 'release', 'runbook', 'script'] },
|
|
823
|
+
];
|
|
824
|
+
for (const cat of capCategories) {
|
|
825
|
+
const cmds = cat.items.map(i => brand('/' + cat.label.split(' ')[0].toLowerCase() + '-' + i)).join(dim(', '));
|
|
826
|
+
console.log(` ${cat.icon} ${white(cat.label)}: ${cmds}`);
|
|
827
|
+
}
|
|
828
|
+
console.log('');
|
|
829
|
+
console.log(dim(' Run any capability with: ') + brand('npx nowaikit run <capability>'));
|
|
830
|
+
console.log(dim(' Supports: markdown, ') + accent('PDF (branded)') + dim(', ') + accent('PPTX (slide deck)') + dim(' output'));
|
|
831
|
+
console.log(dim(' Generate reports: ') + brand('npx nowaikit report scan-health --format pdf'));
|
|
832
|
+
console.log(dim(' Supports: ') + accent('Anthropic') + dim(', ') + accent('OpenAI') + dim(', ') + accent('Ollama') + dim(' (BYOK — bring your own key)'));
|
|
833
|
+
console.log('');
|
|
834
|
+
const showMoreCaps = await confirm({
|
|
835
|
+
message: brand('?') + ' Would you like to list all 26 capabilities in detail' + brand('?'),
|
|
836
|
+
default: false,
|
|
837
|
+
});
|
|
838
|
+
if (showMoreCaps) {
|
|
839
|
+
console.log('');
|
|
840
|
+
try {
|
|
841
|
+
const { getCapabilityMeta } = await import('../prompts/index.js');
|
|
842
|
+
const caps = getCapabilityMeta();
|
|
843
|
+
for (const c of caps) {
|
|
844
|
+
console.log(` ${brand('/' + c.name.padEnd(24))} ${dim(c.description)}`);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
catch {
|
|
848
|
+
console.log(dim(' (Capability metadata not available — run ') + brand('npx nowaikit capabilities') + dim(' to see the full list)'));
|
|
849
|
+
}
|
|
850
|
+
console.log('');
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
else {
|
|
854
|
+
console.log(` ${dim('▸')} ${dim('Apex AI Skills disabled — 26 capabilities not loaded.')}`);
|
|
855
|
+
console.log('');
|
|
856
|
+
}
|
|
857
|
+
// ─── Step 9: Prompts, Shortcuts & Resources ────────────────────────────────
|
|
858
|
+
step(9, 'Prompts, Shortcuts & Resources');
|
|
428
859
|
console.log(` ${accent('▸')} ${white('Slash Commands')} ${dim('(type / in your AI client)')}`);
|
|
429
860
|
console.log('');
|
|
430
861
|
const commands = [
|
|
@@ -471,8 +902,19 @@ export async function runSetup(options = {}) {
|
|
|
471
902
|
clientSecret,
|
|
472
903
|
authMode,
|
|
473
904
|
writeEnabled,
|
|
905
|
+
scriptingEnabled,
|
|
906
|
+
cmdbWriteEnabled,
|
|
907
|
+
atfEnabled,
|
|
474
908
|
toolPackage,
|
|
475
909
|
nowAssistEnabled,
|
|
910
|
+
integrationMode,
|
|
911
|
+
mcpEnabled,
|
|
912
|
+
sdkEnabled,
|
|
913
|
+
apexEnabled,
|
|
914
|
+
aiProvider,
|
|
915
|
+
aiModel,
|
|
916
|
+
aiApiKey,
|
|
917
|
+
aiBaseUrl,
|
|
476
918
|
group: group || undefined,
|
|
477
919
|
environment,
|
|
478
920
|
addedAt: new Date().toISOString(),
|
|
@@ -482,8 +924,26 @@ export async function runSetup(options = {}) {
|
|
|
482
924
|
success(`✓ Instance "${instance.name}" saved`),
|
|
483
925
|
dim(` ~/.config/nowaikit/instances.json`),
|
|
484
926
|
], success);
|
|
485
|
-
// ─── Step
|
|
486
|
-
step(
|
|
927
|
+
// ─── Step 10: AI Client Installation ──────────────────────────────────────
|
|
928
|
+
step(10, 'Install into AI Client(s)');
|
|
929
|
+
// SDK-only mode: skip MCP client configuration
|
|
930
|
+
if (!mcpEnabled) {
|
|
931
|
+
console.log(` ${dim('Skipping AI client installation — MCP Server not enabled.')}`);
|
|
932
|
+
console.log('');
|
|
933
|
+
box([
|
|
934
|
+
white('SDK / Apex mode — no MCP client needed'),
|
|
935
|
+
'',
|
|
936
|
+
dim(' Use NowAIKit directly in your code:'),
|
|
937
|
+
brand(" import { ServiceNowClient } from 'nowaikit/sdk';"),
|
|
938
|
+
'',
|
|
939
|
+
dim(' Or run capabilities from the terminal:'),
|
|
940
|
+
brand(' npx nowaikit run scan-health'),
|
|
941
|
+
]);
|
|
942
|
+
await ensureGlobalCommand();
|
|
943
|
+
await runAutoConfiguration(instance, mcpEnabled, sdkEnabled, []);
|
|
944
|
+
printSummary(instance);
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
487
947
|
const clients = detectClients();
|
|
488
948
|
const detected = clients.filter(c => c.detected);
|
|
489
949
|
const notDetected = clients.filter(c => !c.detected);
|
|
@@ -493,6 +953,7 @@ export async function runSetup(options = {}) {
|
|
|
493
953
|
const result = writeClientConfig(dotenvClient, instance);
|
|
494
954
|
console.log(result.success ? success(` ✓ ${result.message}`) : err(` ✗ ${result.message}`));
|
|
495
955
|
await ensureGlobalCommand();
|
|
956
|
+
await runAutoConfiguration(instance, mcpEnabled, sdkEnabled, []);
|
|
496
957
|
printSummary(instance);
|
|
497
958
|
return;
|
|
498
959
|
}
|
|
@@ -512,6 +973,7 @@ export async function runSetup(options = {}) {
|
|
|
512
973
|
if (chosen.length === 0) {
|
|
513
974
|
console.log(warn('\n No clients selected. Nothing written.'));
|
|
514
975
|
await ensureGlobalCommand();
|
|
976
|
+
await runAutoConfiguration(instance, mcpEnabled, sdkEnabled, []);
|
|
515
977
|
printSummary(instance);
|
|
516
978
|
return;
|
|
517
979
|
}
|
|
@@ -531,16 +993,130 @@ export async function runSetup(options = {}) {
|
|
|
531
993
|
}
|
|
532
994
|
}
|
|
533
995
|
await ensureGlobalCommand();
|
|
996
|
+
await runAutoConfiguration(instance, mcpEnabled, sdkEnabled, chosen);
|
|
534
997
|
printSummary(instance);
|
|
535
998
|
}
|
|
999
|
+
// ─── Step 11: Auto-Configuration ──────────────────────────────────────────────
|
|
1000
|
+
async function runAutoConfiguration(instance, mcpEnabled, sdkEnabled, chosenClientIds) {
|
|
1001
|
+
step(11, 'Auto-Configuration');
|
|
1002
|
+
// npm link (already handled by ensureGlobalCommand, but we surface it here)
|
|
1003
|
+
console.log(` ${accent('▸')} ${white('Global command')} ${dim('— already handled by npm link above')}`);
|
|
1004
|
+
console.log('');
|
|
1005
|
+
// MCP: detect ALL clients and offer to configure any that weren't already chosen
|
|
1006
|
+
if (mcpEnabled) {
|
|
1007
|
+
const clients = detectClients();
|
|
1008
|
+
const allDetected = clients.filter(c => c.detected && c.id !== 'dotenv');
|
|
1009
|
+
const unconfigured = allDetected.filter(c => !chosenClientIds.includes(c.id));
|
|
1010
|
+
if (unconfigured.length > 0) {
|
|
1011
|
+
console.log(` ${accent('▸')} ${white('Additional AI clients found')}`);
|
|
1012
|
+
console.log('');
|
|
1013
|
+
unconfigured.forEach(c => console.log(` ${warn('○')} ${white(c.name)} ${dim('— not yet configured')}`));
|
|
1014
|
+
console.log('');
|
|
1015
|
+
const configureAll = await confirm({
|
|
1016
|
+
message: brand('?') + ' Auto-configure these additional clients too' + brand('?'),
|
|
1017
|
+
default: false,
|
|
1018
|
+
});
|
|
1019
|
+
if (configureAll) {
|
|
1020
|
+
console.log('');
|
|
1021
|
+
for (const client of unconfigured) {
|
|
1022
|
+
const result = writeClientConfig(client, instance);
|
|
1023
|
+
console.log(result.success
|
|
1024
|
+
? ` ${success('✓')} ${white(client.name)}: ${dim(result.message)}`
|
|
1025
|
+
: ` ${err('✗')} ${white(client.name)}: ${err(result.message)}`);
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
else {
|
|
1030
|
+
console.log(` ${success('✓')} All detected AI clients are already configured`);
|
|
1031
|
+
}
|
|
1032
|
+
console.log('');
|
|
1033
|
+
}
|
|
1034
|
+
// SDK: create starter nowaikit-example.ts in cwd
|
|
1035
|
+
if (sdkEnabled) {
|
|
1036
|
+
const examplePath = path.join(process.cwd(), 'nowaikit-example.ts');
|
|
1037
|
+
if (existsSync(examplePath)) {
|
|
1038
|
+
console.log(` ${dim('→')} ${dim('Starter file already exists:')} ${accent(examplePath)}`);
|
|
1039
|
+
}
|
|
1040
|
+
else {
|
|
1041
|
+
const shouldCreate = await confirm({
|
|
1042
|
+
message: brand('?') + ' Create a starter ' + brand('nowaikit-example.ts') + ' in the current directory' + brand('?'),
|
|
1043
|
+
default: true,
|
|
1044
|
+
});
|
|
1045
|
+
if (shouldCreate) {
|
|
1046
|
+
const instanceUrl = instance.instanceUrl;
|
|
1047
|
+
const starter = [
|
|
1048
|
+
`/**`,
|
|
1049
|
+
` * NowAIKit SDK — starter example`,
|
|
1050
|
+
` * Generated by \`nowaikit setup\``,
|
|
1051
|
+
` *`,
|
|
1052
|
+
` * Docs: https://nowaikit.com/docs/sdk`,
|
|
1053
|
+
` */`,
|
|
1054
|
+
`import { ServiceNowClient } from 'nowaikit/sdk';`,
|
|
1055
|
+
``,
|
|
1056
|
+
`const client = new ServiceNowClient({`,
|
|
1057
|
+
` instanceUrl: '${instanceUrl}',`,
|
|
1058
|
+
` authMethod: '${instance.authMethod}',`,
|
|
1059
|
+
instance.authMethod === 'basic'
|
|
1060
|
+
? ` basic: { username: process.env.SN_USERNAME!, password: process.env.SN_PASSWORD! },`
|
|
1061
|
+
: ` oauth: { clientId: process.env.SN_CLIENT_ID!, clientSecret: process.env.SN_CLIENT_SECRET!, username: process.env.SN_USERNAME!, password: process.env.SN_PASSWORD! },`,
|
|
1062
|
+
`});`,
|
|
1063
|
+
``,
|
|
1064
|
+
`// Query open P1 incidents`,
|
|
1065
|
+
`const incidents = await client.queryRecords({`,
|
|
1066
|
+
` table: 'incident',`,
|
|
1067
|
+
` query: 'priority=1^state!=6',`,
|
|
1068
|
+
` fields: 'number,short_description,assigned_to,state',`,
|
|
1069
|
+
` limit: 10,`,
|
|
1070
|
+
`});`,
|
|
1071
|
+
``,
|
|
1072
|
+
`console.log('Open P1 incidents:', incidents.records);`,
|
|
1073
|
+
``,
|
|
1074
|
+
`// Create an incident`,
|
|
1075
|
+
`const newIncident = await client.createRecord('incident', {`,
|
|
1076
|
+
` short_description: 'Test incident from NowAIKit SDK',`,
|
|
1077
|
+
` priority: '3',`,
|
|
1078
|
+
` category: 'software',`,
|
|
1079
|
+
`});`,
|
|
1080
|
+
``,
|
|
1081
|
+
`console.log('Created incident:', newIncident.sys_id);`,
|
|
1082
|
+
].join('\n');
|
|
1083
|
+
writeFileSync(examplePath, starter, 'utf8');
|
|
1084
|
+
console.log(` ${success('✓')} Created starter file: ${accent(examplePath)}`);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
console.log('');
|
|
1088
|
+
}
|
|
1089
|
+
// Getting Started summary
|
|
1090
|
+
console.log(` ${accent('▸')} ${white('Getting Started')}`);
|
|
1091
|
+
console.log('');
|
|
1092
|
+
if (mcpEnabled) {
|
|
1093
|
+
console.log(` ${brand('1.')} Restart your AI client (Claude Desktop, Cursor, etc.)`);
|
|
1094
|
+
console.log(` ${brand('2.')} Ask: ${accent('"List my 5 most recent open incidents"')}`);
|
|
1095
|
+
console.log(` ${brand('3.')} Try a slash command: ${accent('/morning-standup')}`);
|
|
1096
|
+
}
|
|
1097
|
+
else if (sdkEnabled) {
|
|
1098
|
+
console.log(` ${brand('1.')} Install dependencies: ${accent('npm install nowaikit')}`);
|
|
1099
|
+
console.log(` ${brand('2.')} Run the example: ${accent('npx tsx nowaikit-example.ts')}`);
|
|
1100
|
+
console.log(` ${brand('3.')} Explore capabilities: ${accent('npx nowaikit caps')}`);
|
|
1101
|
+
}
|
|
1102
|
+
else {
|
|
1103
|
+
console.log(` ${brand('1.')} Explore capabilities: ${accent('npx nowaikit caps')}`);
|
|
1104
|
+
console.log(` ${brand('2.')} Run a capability: ${accent('npx nowaikit run scan-health')}`);
|
|
1105
|
+
console.log(` ${brand('3.')} See all commands: ${accent('npx nowaikit shortcuts')}`);
|
|
1106
|
+
}
|
|
1107
|
+
console.log('');
|
|
1108
|
+
}
|
|
536
1109
|
// ─── Final summary ──────────────────────────────────────────────────────────
|
|
537
1110
|
function printSummary(instance) {
|
|
538
1111
|
console.log('');
|
|
539
1112
|
divider();
|
|
540
1113
|
console.log('');
|
|
541
|
-
console.log(
|
|
542
|
-
console.log(teal('
|
|
543
|
-
console.log(
|
|
1114
|
+
console.log(teal.bold(' ███╗ ██╗ ██████╗ ██╗ ██╗') + ' ' + gray(' █████╗ ██╗') + ' ' + teal.bold('██╗ ██╗██╗████████╗'));
|
|
1115
|
+
console.log(teal.bold(' ████╗ ██║██╔═══██╗██║ ██║') + ' ' + gray('██╔══██╗██║') + ' ' + teal.bold('██║ ██╔╝██║╚══██╔══╝'));
|
|
1116
|
+
console.log(teal.bold(' ██╔██╗██║██║ ██║██║ █╗ ██║') + ' ' + gray('███████║██║') + ' ' + teal.bold('█████╔╝ ██║ ██║'));
|
|
1117
|
+
console.log(teal.bold(' ██║╚████║██║ ██║██║███╗██║') + ' ' + gray('██╔══██║██║') + ' ' + teal.bold('██╔═██╗ ██║ ██║'));
|
|
1118
|
+
console.log(teal.bold(' ██║ ╚███║╚██████╔╝╚███╔███╔╝') + ' ' + gray('██║ ██║██║') + ' ' + teal.bold('██║ ██╗██║ ██║'));
|
|
1119
|
+
console.log(teal.bold(' ╚═╝ ╚══╝ ╚═════╝ ╚══╝╚══╝') + ' ' + gray('╚═╝ ╚═╝╚═╝') + ' ' + teal.bold('╚═╝ ╚═╝╚═╝ ╚═╝') + ' ' + teal('✦'));
|
|
544
1120
|
console.log('');
|
|
545
1121
|
box([
|
|
546
1122
|
success(' Setup Complete!'),
|
|
@@ -551,20 +1127,64 @@ function printSummary(instance) {
|
|
|
551
1127
|
...(instance.group ? [`${dim(' Group:')} ${white(instance.group)}`] : []),
|
|
552
1128
|
`${dim(' Tools:')} ${white(instance.toolPackage || 'full')}`,
|
|
553
1129
|
`${dim(' Write:')} ${instance.writeEnabled ? success('enabled') : dim('disabled')}`,
|
|
1130
|
+
...(instance.scriptingEnabled ? [`${dim(' Scripting:')} ${success('enabled')}`] : []),
|
|
1131
|
+
...(instance.cmdbWriteEnabled ? [`${dim(' CMDB Write:')} ${success('enabled')}`] : []),
|
|
1132
|
+
...(instance.atfEnabled ? [`${dim(' ATF:')} ${success('enabled')}`] : []),
|
|
554
1133
|
`${dim(' NowAssist:')} ${instance.nowAssistEnabled ? success('enabled') : dim('disabled')}`,
|
|
1134
|
+
`${dim(' MCP:')} ${instance.mcpEnabled !== false ? success('enabled') : dim('disabled')}`,
|
|
1135
|
+
`${dim(' SDK:')} ${instance.sdkEnabled ? success('enabled') : dim('disabled')}`,
|
|
1136
|
+
`${dim(' Apex:')} ${instance.apexEnabled !== false ? success('enabled') : dim('disabled')}`,
|
|
1137
|
+
...(instance.aiProvider ? [`${dim(' AI:')} ${success(instance.aiProvider)}${instance.aiModel ? dim(' / ' + instance.aiModel) : ''}`] : []),
|
|
555
1138
|
], brand);
|
|
1139
|
+
// ── What's Next ──────────────────────────────────────────────────────────
|
|
556
1140
|
console.log('');
|
|
557
|
-
console.log(` ${accent('▸')} ${white('
|
|
1141
|
+
console.log(` ${accent('▸')} ${white("What's Next")} ${dim('— 3 recommended first actions:')}`);
|
|
1142
|
+
console.log('');
|
|
1143
|
+
if (instance.mcpEnabled !== false) {
|
|
1144
|
+
console.log(` ${brand('1.')} ${white('Restart your AI client')} ${dim('so it picks up the new MCP server config')}`);
|
|
1145
|
+
console.log(` ${brand('2.')} ${white('Ask your AI')} ${dim('→')} ${accent('"Show me my open P1 incidents"')}`);
|
|
1146
|
+
console.log(` ${brand('3.')} ${white('Try a slash command')} ${dim('→')} ${accent('/morning-standup')}`);
|
|
1147
|
+
}
|
|
1148
|
+
else if (instance.sdkEnabled) {
|
|
1149
|
+
console.log(` ${brand('1.')} ${white('Install')} ${dim('→')} ${accent('npm install nowaikit')}`);
|
|
1150
|
+
console.log(` ${brand('2.')} ${white('Run the starter')} ${dim('→')} ${accent('npx tsx nowaikit-example.ts')}`);
|
|
1151
|
+
console.log(` ${brand('3.')} ${white('Explore capabilities')} ${dim('→')} ${accent('nowaikit caps')}`);
|
|
1152
|
+
}
|
|
1153
|
+
else {
|
|
1154
|
+
console.log(` ${brand('1.')} ${white('Explore capabilities')} ${dim('→')} ${accent('nowaikit caps')}`);
|
|
1155
|
+
console.log(` ${brand('2.')} ${white('Run a capability')} ${dim('→')} ${accent('nowaikit run scan-health')}`);
|
|
1156
|
+
console.log(` ${brand('3.')} ${white('See all shortcuts')} ${dim('→')} ${accent('nowaikit shortcuts')}`);
|
|
1157
|
+
}
|
|
1158
|
+
console.log('');
|
|
1159
|
+
console.log(` ${accent('▸')} ${white('Power Tools')} ${dim('(available to your AI automatically):')}`);
|
|
1160
|
+
console.log('');
|
|
1161
|
+
console.log(` ${brand('fluent_query')} ${dim('Structured queries — no scripts needed')}`);
|
|
1162
|
+
console.log(` ${brand('batch_request')} ${dim('Up to 50 API calls in one request')}`);
|
|
1163
|
+
if (instance.scriptingEnabled) {
|
|
1164
|
+
console.log(` ${brand('execute_script')} ${dim('Run server-side JS on your instance')}`);
|
|
1165
|
+
}
|
|
1166
|
+
console.log('');
|
|
1167
|
+
if (instance.sdkEnabled) {
|
|
1168
|
+
console.log(` ${accent('▸')} ${white('SDK Mode')} ${dim('(import in your TypeScript/JavaScript code):')}`);
|
|
1169
|
+
console.log('');
|
|
1170
|
+
console.log(` ${brand("import { ServiceNowClient } from 'nowaikit/sdk';")} `);
|
|
1171
|
+
console.log(` ${brand("import { executeDirectly } from 'nowaikit/sdk';")} `);
|
|
1172
|
+
console.log(` ${brand("import { ServiceNowClient } from 'nowaikit/client';")} ${dim('// just the client')}`);
|
|
1173
|
+
console.log('');
|
|
1174
|
+
}
|
|
1175
|
+
console.log(` ${accent('▸')} ${white('Direct Mode')} ${dim('(run capabilities from terminal — no MCP client):')}`);
|
|
558
1176
|
console.log('');
|
|
559
|
-
console.log(` ${brand('
|
|
560
|
-
console.log(` ${brand('
|
|
561
|
-
console.log(` ${brand('
|
|
1177
|
+
console.log(` ${brand('npx nowaikit capabilities')} ${dim('List all 26 capabilities')}`);
|
|
1178
|
+
console.log(` ${brand('npx nowaikit run scan-health')} ${dim('Run a capability directly')}`);
|
|
1179
|
+
console.log(` ${brand('npx nowaikit report scan-health')} ${dim('Generate branded PDF report')}`);
|
|
1180
|
+
console.log(` ${brand('nowaikit shortcuts')} ${dim('Show all commands & keyboard shortcuts')}`);
|
|
562
1181
|
console.log('');
|
|
563
1182
|
console.log(` ${accent('▸')} ${white('Manage from the terminal:')}`);
|
|
564
1183
|
console.log('');
|
|
565
1184
|
console.log(` ${brand('nowaikit setup --add')} ${dim('Add another instance')}`);
|
|
566
1185
|
console.log(` ${brand('nowaikit instances list')} ${dim('Show configured instances')}`);
|
|
567
1186
|
console.log(` ${brand('nowaikit instances remove')} ${dim('Remove an instance')}`);
|
|
1187
|
+
console.log(` ${brand('nowaikit web')} ${dim('Open web dashboard')}`);
|
|
568
1188
|
if (instance.authMode === 'per-user') {
|
|
569
1189
|
console.log(` ${brand('nowaikit auth login')} ${dim('Authenticate as yourself')}`);
|
|
570
1190
|
}
|