fraim-hub 2.0.204

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.
Files changed (120) hide show
  1. package/README.md +467 -0
  2. package/bin/fraim-hub.js +17 -0
  3. package/dist/src/ai-hub/brand-store.js +17 -0
  4. package/dist/src/ai-hub/catalog.js +412 -0
  5. package/dist/src/ai-hub/cert-store.js +70 -0
  6. package/dist/src/ai-hub/cli.js +212 -0
  7. package/dist/src/ai-hub/configured-agents.js +283 -0
  8. package/dist/src/ai-hub/conversation-store.js +428 -0
  9. package/dist/src/ai-hub/desktop-main.js +449 -0
  10. package/dist/src/ai-hub/hosts.js +1669 -0
  11. package/dist/src/ai-hub/hub-latest-version.js +52 -0
  12. package/dist/src/ai-hub/hub-launch-decision.js +57 -0
  13. package/dist/src/ai-hub/hub-runtime-file.js +43 -0
  14. package/dist/src/ai-hub/managed-browser.js +269 -0
  15. package/dist/src/ai-hub/manager-turns.js +64 -0
  16. package/dist/src/ai-hub/office-sideload.js +156 -0
  17. package/dist/src/ai-hub/openclaw-bridge.js +250 -0
  18. package/dist/src/ai-hub/preferences.js +201 -0
  19. package/dist/src/ai-hub/remote-hub-gateway.js +88 -0
  20. package/dist/src/ai-hub/server.js +4013 -0
  21. package/dist/src/ai-hub/types.js +2 -0
  22. package/dist/src/ai-hub/url-safety.js +19 -0
  23. package/dist/src/api/admin/payments.js +33 -0
  24. package/dist/src/api/admin/sales-leads.js +21 -0
  25. package/dist/src/api/payment/create-session.js +338 -0
  26. package/dist/src/api/payment/dashboard-link.js +149 -0
  27. package/dist/src/api/payment/session-details.js +31 -0
  28. package/dist/src/api/payment/webhook.js +587 -0
  29. package/dist/src/api/sales/contact.js +44 -0
  30. package/dist/src/cli/api/get-provider-client.js +41 -0
  31. package/dist/src/cli/api/provider-client.js +107 -0
  32. package/dist/src/cli/commands/add-ide.js +462 -0
  33. package/dist/src/cli/fraim-hub.js +50 -0
  34. package/dist/src/cli/internal/device-flow-service.js +83 -0
  35. package/dist/src/cli/mcp/command-resolution.js +81 -0
  36. package/dist/src/cli/mcp/fraim-mcp-latest-launcher.js +136 -0
  37. package/dist/src/cli/mcp/ide-formats.js +317 -0
  38. package/dist/src/cli/mcp/mcp-server-builder.js +48 -0
  39. package/dist/src/cli/mcp/mcp-server-registry.js +173 -0
  40. package/dist/src/cli/providers/local-provider-registry.js +165 -0
  41. package/dist/src/cli/providers/provider-registry.js +230 -0
  42. package/dist/src/cli/setup/claude-code-telemetry.js +59 -0
  43. package/dist/src/cli/setup/codex-local-config.js +37 -0
  44. package/dist/src/cli/setup/ide-detector.js +383 -0
  45. package/dist/src/cli/setup/ide-global-integration.js +100 -0
  46. package/dist/src/cli/setup/ide-invocation-surfaces.js +160 -0
  47. package/dist/src/cli/setup/mcp-config-generator.js +225 -0
  48. package/dist/src/cli/setup/provider-prompts.js +339 -0
  49. package/dist/src/cli/utils/managed-agent-paths.js +118 -0
  50. package/dist/src/cli/utils/script-sync-utils.js +221 -0
  51. package/dist/src/cli/utils/user-config.js +100 -0
  52. package/dist/src/cli/utils/version-utils.js +35 -0
  53. package/dist/src/config/ai-manager-hiring.js +121 -0
  54. package/dist/src/config/feature-flags.js +25 -0
  55. package/dist/src/config/persona-capability-bundles.js +277 -0
  56. package/dist/src/config/persona-hiring.js +270 -0
  57. package/dist/src/config/portfolio-slug-overrides.js +17 -0
  58. package/dist/src/config/pricing.js +37 -0
  59. package/dist/src/config/stripe.js +43 -0
  60. package/dist/src/core/brand-store.js +232 -0
  61. package/dist/src/core/quality-evidence.js +331 -0
  62. package/dist/src/core/utils/git-utils.js +194 -0
  63. package/dist/src/core/utils/ports.js +32 -0
  64. package/dist/src/core/utils/project-fraim-paths.js +220 -0
  65. package/dist/src/db/payment-repository.js +61 -0
  66. package/dist/src/first-run/types.js +93 -0
  67. package/dist/src/fraim/db-service.js +2409 -0
  68. package/dist/src/local-mcp-server/agent-token-prices.js +167 -0
  69. package/dist/src/local-mcp-server/learning-context-builder.js +975 -0
  70. package/dist/src/middleware/rate-limit.js +110 -0
  71. package/dist/src/models/payment.js +2 -0
  72. package/dist/src/routes/payment-routes.js +186 -0
  73. package/dist/src/routes/persona-catalog-routes.js +84 -0
  74. package/dist/src/services/dashboard-access.js +27 -0
  75. package/dist/src/services/email-service.js +951 -0
  76. package/dist/src/services/persona-entitlement-service.js +352 -0
  77. package/dist/src/services/workspace-identity.js +21 -0
  78. package/dist/src/types/analytics.js +2 -0
  79. package/dist/src/utils/payment-calculator.js +52 -0
  80. package/extensions/office-word/favicon.ico +0 -0
  81. package/extensions/office-word/icon-64.png +0 -0
  82. package/extensions/office-word/manifest.xml +33 -0
  83. package/extensions/office-word/taskpane.html +242 -0
  84. package/index.js +85 -0
  85. package/package.json +106 -0
  86. package/public/ai-hub/index.html +910 -0
  87. package/public/ai-hub/powerpoint-taskpane/icon-64.png +0 -0
  88. package/public/ai-hub/powerpoint-taskpane/index.html +236 -0
  89. package/public/ai-hub/powerpoint-taskpane/manifest.xml +30 -0
  90. package/public/ai-hub/review.css +444 -0
  91. package/public/ai-hub/script.js +11737 -0
  92. package/public/ai-hub/styles.css +5102 -0
  93. package/public/first-run/error-frame.js +100 -0
  94. package/public/first-run/index.html +35 -0
  95. package/public/first-run/script.js +739 -0
  96. package/public/first-run/styles.css +929 -0
  97. package/public/portfolio/ashley.html +523 -0
  98. package/public/portfolio/auditya.html +83 -0
  99. package/public/portfolio/banke.html +83 -0
  100. package/public/portfolio/beza.html +659 -0
  101. package/public/portfolio/careena.html +632 -0
  102. package/public/portfolio/casey.html +568 -0
  103. package/public/portfolio/celia.html +490 -0
  104. package/public/portfolio/deidre.html +642 -0
  105. package/public/portfolio/gautam.html +597 -0
  106. package/public/portfolio/hari.html +469 -0
  107. package/public/portfolio/huxley.html +1354 -0
  108. package/public/portfolio/index.html +741 -0
  109. package/public/portfolio/maestro.html +518 -0
  110. package/public/portfolio/mandy.html +590 -0
  111. package/public/portfolio/mona.html +597 -0
  112. package/public/portfolio/pam.html +887 -0
  113. package/public/portfolio/procella.html +107 -0
  114. package/public/portfolio/qasm.html +569 -0
  115. package/public/portfolio/ricardo.html +489 -0
  116. package/public/portfolio/sade.html +560 -0
  117. package/public/portfolio/sam.html +654 -0
  118. package/public/portfolio/sechar.html +580 -0
  119. package/public/portfolio/sreya.html +599 -0
  120. package/public/portfolio/swen.html +601 -0
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateMCPConfig = exports.generateWindsurfMCPServers = exports.generateCopilotCliMCPServers = exports.generateGeminiCliMCPServers = exports.generateVSCodeMCPServers = exports.generateGrokMCPServers = exports.generateCodexMCPServers = exports.generateKiroMCPServers = exports.generateClaudeCodeMCPServers = exports.generateClaudeMCPServers = exports.generateStandardMCPServers = exports.mergeTomlMCPServers = exports.extractTomlMcpServerBlock = void 0;
4
+ const mcp_server_builder_1 = require("../mcp/mcp-server-builder");
5
+ const ide_formats_1 = require("../mcp/ide-formats");
6
+ const normalizeTokens = (tokenInput) => {
7
+ if (!tokenInput)
8
+ return {};
9
+ if (typeof tokenInput === 'string') {
10
+ return tokenInput ? { github: tokenInput } : {};
11
+ }
12
+ return tokenInput;
13
+ };
14
+ const findTomlServerBlockRange = (content, server) => {
15
+ const lines = content.split(/\r?\n/);
16
+ const serverSection = `mcp_servers.${server}`;
17
+ const serverHeader = `[${serverSection}]`;
18
+ const start = lines.findIndex(line => line.trim() === serverHeader);
19
+ if (start === -1) {
20
+ return null;
21
+ }
22
+ let end = lines.length;
23
+ for (let i = start + 1; i < lines.length; i++) {
24
+ const sectionMatch = lines[i].trim().match(/^\[([^\]]+)\]$/);
25
+ if (!sectionMatch)
26
+ continue;
27
+ const sectionName = sectionMatch[1];
28
+ if (sectionName === serverSection || sectionName.startsWith(`${serverSection}.`)) {
29
+ continue;
30
+ }
31
+ end = i;
32
+ break;
33
+ }
34
+ return { start, end };
35
+ };
36
+ const extractTomlMcpServerBlock = (content, server) => {
37
+ const range = findTomlServerBlockRange(content, server);
38
+ if (!range)
39
+ return null;
40
+ const lines = content.split(/\r?\n/);
41
+ return lines.slice(range.start, range.end).join('\n').trim();
42
+ };
43
+ exports.extractTomlMcpServerBlock = extractTomlMcpServerBlock;
44
+ const removeTomlMcpServerBlock = (content, server) => {
45
+ const range = findTomlServerBlockRange(content, server);
46
+ if (!range)
47
+ return content;
48
+ const lines = content.split(/\r?\n/);
49
+ const updated = [...lines.slice(0, range.start), ...lines.slice(range.end)].join('\n');
50
+ return updated.replace(/\n{3,}/g, '\n\n').trimEnd();
51
+ };
52
+ const appendTomlBlock = (content, block) => {
53
+ const trimmedContent = content.trimEnd();
54
+ const trimmedBlock = block.trim();
55
+ if (!trimmedBlock)
56
+ return trimmedContent;
57
+ if (!trimmedContent)
58
+ return `${trimmedBlock}\n`;
59
+ return `${trimmedContent}\n\n${trimmedBlock}\n`;
60
+ };
61
+ const mergeTomlMCPServers = (existingContent, generatedContent, servers) => {
62
+ let merged = existingContent || '';
63
+ const addedServers = [];
64
+ const replacedServers = [];
65
+ const skippedServers = [];
66
+ for (const server of servers) {
67
+ const newBlock = (0, exports.extractTomlMcpServerBlock)(generatedContent, server);
68
+ if (!newBlock) {
69
+ skippedServers.push(server);
70
+ continue;
71
+ }
72
+ const existingBlock = (0, exports.extractTomlMcpServerBlock)(merged, server);
73
+ if (existingBlock && existingBlock.trim() === newBlock.trim()) {
74
+ skippedServers.push(server);
75
+ continue;
76
+ }
77
+ if (existingBlock) {
78
+ merged = removeTomlMcpServerBlock(merged, server);
79
+ merged = appendTomlBlock(merged, newBlock);
80
+ replacedServers.push(server);
81
+ continue;
82
+ }
83
+ merged = appendTomlBlock(merged, newBlock);
84
+ addedServers.push(server);
85
+ }
86
+ return {
87
+ content: merged.trimEnd() + '\n',
88
+ addedServers,
89
+ replacedServers,
90
+ skippedServers
91
+ };
92
+ };
93
+ exports.mergeTomlMCPServers = mergeTomlMCPServers;
94
+ // Helper function to add all provider servers with their configs
95
+ const addProviderServers = async (builder, tokens, providerConfigs) => {
96
+ for (const [providerId, token] of Object.entries(tokens)) {
97
+ if (token) {
98
+ // Get provider-specific config if it exists
99
+ const config = providerConfigs?.[providerId];
100
+ await builder.addProvider(providerId, token, config);
101
+ }
102
+ }
103
+ };
104
+ const generateStandardMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
105
+ const tokens = normalizeTokens(tokenInput);
106
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
107
+ builder.addBaseServers(fraimKey);
108
+ // Dynamically add provider servers based on available tokens
109
+ await addProviderServers(builder, tokens, providerConfigs);
110
+ const format = (0, ide_formats_1.getIDEFormat)('standard');
111
+ return format.transform(builder.getServers());
112
+ };
113
+ exports.generateStandardMCPServers = generateStandardMCPServers;
114
+ const generateClaudeMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
115
+ const tokens = normalizeTokens(tokenInput);
116
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
117
+ builder.addBaseServers(fraimKey);
118
+ // Claude Desktop does not support HTTPS MCP servers (GitHub, GitLab, Jira)
119
+ // These cause Claude Desktop to break on boot
120
+ // See: https://github.com/mathursrus/FRAIM/issues/132
121
+ // The ClaudeFormat adapter will filter out HTTP servers
122
+ await addProviderServers(builder, tokens, providerConfigs);
123
+ const format = (0, ide_formats_1.getIDEFormat)('claude');
124
+ return format.transform(builder.getServers());
125
+ };
126
+ exports.generateClaudeMCPServers = generateClaudeMCPServers;
127
+ const generateClaudeCodeMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
128
+ const tokens = normalizeTokens(tokenInput);
129
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
130
+ builder.addBaseServers(fraimKey);
131
+ await addProviderServers(builder, tokens, providerConfigs);
132
+ const format = (0, ide_formats_1.getIDEFormat)('claude-code');
133
+ return format.transform(builder.getServers());
134
+ };
135
+ exports.generateClaudeCodeMCPServers = generateClaudeCodeMCPServers;
136
+ const generateKiroMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
137
+ const tokens = normalizeTokens(tokenInput);
138
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
139
+ builder.addBaseServers(fraimKey);
140
+ await addProviderServers(builder, tokens, providerConfigs);
141
+ const format = (0, ide_formats_1.getIDEFormat)('kiro');
142
+ return format.transform(builder.getServers());
143
+ };
144
+ exports.generateKiroMCPServers = generateKiroMCPServers;
145
+ const generateCodexMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
146
+ const tokens = normalizeTokens(tokenInput);
147
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
148
+ builder.addBaseServers(fraimKey);
149
+ await addProviderServers(builder, tokens, providerConfigs);
150
+ const format = (0, ide_formats_1.getIDEFormat)('codex');
151
+ return format.transform(builder.getServers());
152
+ };
153
+ exports.generateCodexMCPServers = generateCodexMCPServers;
154
+ const generateGrokMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
155
+ const tokens = normalizeTokens(tokenInput);
156
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
157
+ builder.addBaseServers(fraimKey);
158
+ await addProviderServers(builder, tokens, providerConfigs);
159
+ const format = (0, ide_formats_1.getIDEFormat)('grok');
160
+ return format.transform(builder.getServers());
161
+ };
162
+ exports.generateGrokMCPServers = generateGrokMCPServers;
163
+ const generateVSCodeMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
164
+ const tokens = normalizeTokens(tokenInput);
165
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
166
+ builder.addBaseServers(fraimKey);
167
+ await addProviderServers(builder, tokens, providerConfigs);
168
+ const format = (0, ide_formats_1.getIDEFormat)('vscode');
169
+ return format.transform(builder.getServers());
170
+ };
171
+ exports.generateVSCodeMCPServers = generateVSCodeMCPServers;
172
+ const generateGeminiCliMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
173
+ const tokens = normalizeTokens(tokenInput);
174
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
175
+ builder.addBaseServers(fraimKey);
176
+ await addProviderServers(builder, tokens, providerConfigs);
177
+ const format = (0, ide_formats_1.getIDEFormat)('gemini-cli');
178
+ return format.transform(builder.getServers());
179
+ };
180
+ exports.generateGeminiCliMCPServers = generateGeminiCliMCPServers;
181
+ const generateCopilotCliMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
182
+ const tokens = normalizeTokens(tokenInput);
183
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
184
+ builder.addBaseServers(fraimKey);
185
+ await addProviderServers(builder, tokens, providerConfigs);
186
+ const format = (0, ide_formats_1.getIDEFormat)('copilot-cli');
187
+ return format.transform(builder.getServers());
188
+ };
189
+ exports.generateCopilotCliMCPServers = generateCopilotCliMCPServers;
190
+ const generateWindsurfMCPServers = async (fraimKey, tokenInput, providerConfigs) => {
191
+ const tokens = normalizeTokens(tokenInput);
192
+ const builder = new mcp_server_builder_1.MCPServerBuilder();
193
+ builder.addBaseServers(fraimKey);
194
+ await addProviderServers(builder, tokens, providerConfigs);
195
+ const format = (0, ide_formats_1.getIDEFormat)('windsurf');
196
+ return format.transform(builder.getServers());
197
+ };
198
+ exports.generateWindsurfMCPServers = generateWindsurfMCPServers;
199
+ const generateMCPConfig = async (configType, fraimKey, tokenInput, providerConfigs) => {
200
+ switch (configType) {
201
+ case 'standard':
202
+ return await (0, exports.generateStandardMCPServers)(fraimKey, tokenInput, providerConfigs);
203
+ case 'claude':
204
+ return await (0, exports.generateClaudeMCPServers)(fraimKey, tokenInput, providerConfigs);
205
+ case 'claude-code':
206
+ return await (0, exports.generateClaudeCodeMCPServers)(fraimKey, tokenInput, providerConfigs);
207
+ case 'kiro':
208
+ return await (0, exports.generateKiroMCPServers)(fraimKey, tokenInput, providerConfigs);
209
+ case 'vscode':
210
+ return await (0, exports.generateVSCodeMCPServers)(fraimKey, tokenInput, providerConfigs);
211
+ case 'gemini-cli':
212
+ return await (0, exports.generateGeminiCliMCPServers)(fraimKey, tokenInput, providerConfigs);
213
+ case 'copilot-cli':
214
+ return await (0, exports.generateCopilotCliMCPServers)(fraimKey, tokenInput, providerConfigs);
215
+ case 'codex':
216
+ return await (0, exports.generateCodexMCPServers)(fraimKey, tokenInput, providerConfigs);
217
+ case 'grok':
218
+ return await (0, exports.generateGrokMCPServers)(fraimKey, tokenInput, providerConfigs);
219
+ case 'windsurf':
220
+ return await (0, exports.generateWindsurfMCPServers)(fraimKey, tokenInput, providerConfigs);
221
+ default:
222
+ throw new Error(`Unsupported config type: ${configType}`);
223
+ }
224
+ };
225
+ exports.generateMCPConfig = generateMCPConfig;
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ // Generic provider prompt system - no hardcoded provider knowledge
3
+ // All provider information comes from the server via ProviderClient
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
16
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
17
+ }) : function(o, v) {
18
+ o["default"] = v;
19
+ });
20
+ var __importStar = (this && this.__importStar) || (function () {
21
+ var ownKeys = function(o) {
22
+ ownKeys = Object.getOwnPropertyNames || function (o) {
23
+ var ar = [];
24
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
25
+ return ar;
26
+ };
27
+ return ownKeys(o);
28
+ };
29
+ return function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
36
+ })();
37
+ var __importDefault = (this && this.__importDefault) || function (mod) {
38
+ return (mod && mod.__esModule) ? mod : { "default": mod };
39
+ };
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.promptForProviders = promptForProviders;
42
+ exports.promptForSingleProvider = promptForSingleProvider;
43
+ exports.promptForProviderToken = promptForProviderToken;
44
+ exports.promptForProviderConfig = promptForProviderConfig;
45
+ exports.promptForProviderCredentials = promptForProviderCredentials;
46
+ const chalk_1 = __importDefault(require("chalk"));
47
+ const prompts_1 = __importDefault(require("prompts"));
48
+ /**
49
+ * Prompt user to select multiple providers
50
+ */
51
+ async function promptForProviders(client, preselectedIds) {
52
+ console.log(chalk_1.default.blue('\n🔧 Platform Selection'));
53
+ console.log(chalk_1.default.gray('Which platforms do you want to integrate with?\n'));
54
+ // Get providers that support integrated mode
55
+ const integratedProviders = await client.getProvidersWithCapability('integrated');
56
+ // Default to first integrated provider (typically GitHub)
57
+ const defaultProviderId = integratedProviders[0]?.id;
58
+ const choices = integratedProviders.map(provider => ({
59
+ title: provider.displayName,
60
+ value: provider.id,
61
+ selected: preselectedIds?.includes(provider.id) ?? provider.id === defaultProviderId
62
+ }));
63
+ if (process.env.FRAIM_NON_INTERACTIVE) {
64
+ console.log(chalk_1.default.yellow(`\nℹ️ Non-interactive mode: defaulting to ${integratedProviders[0]?.displayName || 'first available provider'}`));
65
+ return [defaultProviderId];
66
+ }
67
+ const response = await (0, prompts_1.default)({
68
+ type: 'multiselect',
69
+ name: 'providers',
70
+ message: 'Select platforms (Space to select, Enter to confirm)',
71
+ choices,
72
+ min: 1
73
+ });
74
+ if (!response.providers || response.providers.length === 0) {
75
+ const defaultProvider = await client.getProvider(defaultProviderId);
76
+ console.log(chalk_1.default.yellow(`\nℹ️ No platforms selected, defaulting to ${defaultProvider?.displayName || 'first available provider'}`));
77
+ return [defaultProviderId];
78
+ }
79
+ console.log(chalk_1.default.blue('\n✓ Selected platforms:'));
80
+ for (const id of response.providers) {
81
+ const provider = await client.getProvider(id);
82
+ if (provider) {
83
+ console.log(chalk_1.default.gray(` - ${provider.displayName}`));
84
+ }
85
+ }
86
+ console.log();
87
+ return response.providers;
88
+ }
89
+ /**
90
+ * Prompt user to select a single provider (for code repo or issue tracking)
91
+ */
92
+ async function promptForSingleProvider(client, purpose, availableIds) {
93
+ const purposeLabel = purpose === 'code' ? 'Code Repository' : 'Issue Tracking';
94
+ const purposeDesc = purpose === 'code' ? 'code hosting' : 'issue tracking';
95
+ console.log(chalk_1.default.blue(`\n📦 ${purposeLabel} Platform`));
96
+ console.log(chalk_1.default.gray(`Select the platform for ${purposeDesc}:\n`));
97
+ // Use capability-based filtering if no explicit filter provided
98
+ let providers;
99
+ if (availableIds) {
100
+ providers = [];
101
+ for (const id of availableIds) {
102
+ const provider = await client.getProvider(id);
103
+ if (provider)
104
+ providers.push(provider);
105
+ }
106
+ }
107
+ else {
108
+ providers = await client.getProvidersWithCapability(purpose);
109
+ }
110
+ // Default to first available provider
111
+ const defaultProviderId = providers[0]?.id;
112
+ const choices = providers.map(provider => ({
113
+ title: provider.displayName,
114
+ value: provider.id
115
+ }));
116
+ if (process.env.FRAIM_NON_INTERACTIVE) {
117
+ console.log(chalk_1.default.yellow(`\nℹ️ Non-interactive mode: defaulting to ${providers[0]?.displayName || 'first available provider'}`));
118
+ return defaultProviderId;
119
+ }
120
+ const response = await (0, prompts_1.default)({
121
+ type: 'select',
122
+ name: 'provider',
123
+ message: `Select ${purposeDesc} platform`,
124
+ choices,
125
+ initial: 0
126
+ });
127
+ if (!response.provider) {
128
+ const defaultProvider = await client.getProvider(defaultProviderId);
129
+ console.log(chalk_1.default.yellow(`\nℹ️ No platform selected, defaulting to ${defaultProvider?.displayName || 'first available provider'}`));
130
+ return defaultProviderId;
131
+ }
132
+ const provider = await client.getProvider(response.provider);
133
+ if (provider) {
134
+ console.log(chalk_1.default.blue(`\n✓ ${purposeLabel}: ${provider.displayName}\n`));
135
+ }
136
+ return response.provider;
137
+ }
138
+ /**
139
+ * Prompt for a provider's token
140
+ */
141
+ async function promptForProviderToken(client, providerId) {
142
+ let provider;
143
+ try {
144
+ provider = await client.getProvider(providerId);
145
+ }
146
+ catch (error) {
147
+ // Server unavailable - use local fallback
148
+ const { getLocalProvider } = await Promise.resolve().then(() => __importStar(require('../providers/local-provider-registry')));
149
+ provider = getLocalProvider(providerId);
150
+ }
151
+ if (!provider) {
152
+ throw new Error(`Unknown provider: ${providerId}`);
153
+ }
154
+ console.log(chalk_1.default.blue(`\n🔧 ${provider.displayName} Integration Setup`));
155
+ console.log(`FRAIM requires a ${provider.displayName} token for integration.\n`);
156
+ if (process.env.FRAIM_NON_INTERACTIVE) {
157
+ throw new Error(`Non-interactive mode: ${provider.displayName} token is missing and cannot prompt.`);
158
+ }
159
+ const hasToken = await (0, prompts_1.default)({
160
+ type: 'confirm',
161
+ name: 'hasToken',
162
+ message: `Do you have a ${provider.displayName} token?`,
163
+ initial: false
164
+ });
165
+ if (!hasToken.hasToken) {
166
+ if (providerId === 'github') {
167
+ const loginChoice = await (0, prompts_1.default)({
168
+ type: 'confirm',
169
+ name: 'login',
170
+ message: `Would you like to login to ${provider.displayName} now using your browser? (Recommended)`,
171
+ initial: true
172
+ });
173
+ if (loginChoice.login) {
174
+ const { DeviceFlowService } = await Promise.resolve().then(() => __importStar(require('../internal/device-flow-service')));
175
+ if (!provider.deviceFlowConfig) {
176
+ throw new Error(`Device flow configuration not found for provider: ${providerId}`);
177
+ }
178
+ const deviceFlow = new DeviceFlowService(provider.deviceFlowConfig);
179
+ try {
180
+ return await deviceFlow.login();
181
+ }
182
+ catch (e) {
183
+ console.log(chalk_1.default.yellow('\nBrowser login failed or was cancelled. Fallback to manual token entry.'));
184
+ }
185
+ }
186
+ }
187
+ console.log(chalk_1.default.yellow(`\n📝 To create a ${provider.displayName} token:`));
188
+ console.log(chalk_1.default.gray(` ${provider.setupInstructions}`));
189
+ console.log(chalk_1.default.gray(` Visit: ${provider.docsUrl}\n`));
190
+ }
191
+ let token = null;
192
+ let attempts = 0;
193
+ const maxAttempts = 3;
194
+ while (!token && attempts < maxAttempts) {
195
+ const tokenResponse = await (0, prompts_1.default)({
196
+ type: 'password',
197
+ name: 'token',
198
+ message: attempts === 0
199
+ ? `Enter your ${provider.displayName} token`
200
+ : `Enter your ${provider.displayName} token (attempt ${attempts + 1}/${maxAttempts})`,
201
+ validate: (value) => {
202
+ if (!value)
203
+ return `${provider.displayName} token is required`;
204
+ return true;
205
+ }
206
+ });
207
+ if (!tokenResponse.token) {
208
+ console.log(chalk_1.default.red(`\n❌ ${provider.displayName} token is required.`));
209
+ attempts++;
210
+ if (attempts < maxAttempts) {
211
+ const retry = await (0, prompts_1.default)({
212
+ type: 'confirm',
213
+ name: 'retry',
214
+ message: 'Would you like to try entering the token again?',
215
+ initial: true
216
+ });
217
+ if (!retry.retry)
218
+ break;
219
+ }
220
+ continue;
221
+ }
222
+ token = tokenResponse.token;
223
+ console.log(chalk_1.default.green(`✅ ${provider.displayName} token received\n`));
224
+ }
225
+ if (!token) {
226
+ throw new Error(`Failed to get ${provider.displayName} token`);
227
+ }
228
+ return token;
229
+ }
230
+ /**
231
+ * Prompt for additional provider configuration (e.g., Jira URL and email)
232
+ */
233
+ async function promptForProviderConfig(client, providerId) {
234
+ let provider;
235
+ let schema;
236
+ try {
237
+ provider = await client.getProvider(providerId);
238
+ schema = await client.getProviderSchema(providerId);
239
+ }
240
+ catch (error) {
241
+ // Server unavailable - use local fallback
242
+ const { getLocalProvider, getLocalProviderConfigRequirements } = await Promise.resolve().then(() => __importStar(require('../providers/local-provider-registry')));
243
+ provider = getLocalProvider(providerId);
244
+ if (!provider) {
245
+ throw new Error(`Unknown provider: ${providerId}`);
246
+ }
247
+ schema = { configRequirements: getLocalProviderConfigRequirements(providerId) };
248
+ }
249
+ if (!provider) {
250
+ throw new Error(`Unknown provider: ${providerId}`);
251
+ }
252
+ const requirements = schema.configRequirements;
253
+ if (requirements.length === 0) {
254
+ return {};
255
+ }
256
+ console.log(chalk_1.default.blue(`\n🔧 ${provider.displayName} Configuration`));
257
+ console.log(`Additional configuration required for ${provider.displayName}.\n`);
258
+ const config = {};
259
+ for (const req of requirements) {
260
+ if (process.env.FRAIM_NON_INTERACTIVE) {
261
+ if (req.required) {
262
+ throw new Error(`Non-interactive mode: Required configuration "${req.displayName}" for ${provider.displayName} is missing.`);
263
+ }
264
+ console.log(chalk_1.default.yellow(`\nℹ️ Non-interactive mode: skipping optional configuration "${req.displayName}"`));
265
+ continue;
266
+ }
267
+ const response = await (0, prompts_1.default)({
268
+ type: req.type === 'email' ? 'text' : req.type === 'url' ? 'text' : 'text',
269
+ name: 'value',
270
+ message: `Enter ${req.displayName}`,
271
+ validate: (value) => {
272
+ if (req.required && !value) {
273
+ return `${req.displayName} is required`;
274
+ }
275
+ if (req.type === 'email' && value && !value.includes('@')) {
276
+ return 'Please enter a valid email address';
277
+ }
278
+ if (req.type === 'url' && value) {
279
+ try {
280
+ new URL(value.startsWith('http') ? value : `https://${value}`);
281
+ }
282
+ catch {
283
+ return 'Please enter a valid URL';
284
+ }
285
+ }
286
+ return true;
287
+ }
288
+ });
289
+ if (!response.value && req.required) {
290
+ throw new Error(`${req.displayName} is required`);
291
+ }
292
+ config[req.key] = response.value;
293
+ }
294
+ console.log(chalk_1.default.green(`✅ ${provider.displayName} configuration received\n`));
295
+ return config;
296
+ }
297
+ /**
298
+ * Get token and config for a provider (handles both token and additional config)
299
+ */
300
+ async function promptForProviderCredentials(client, providerId, existingToken, existingConfig) {
301
+ let provider;
302
+ let schema;
303
+ try {
304
+ provider = await client.getProvider(providerId);
305
+ schema = await client.getProviderSchema(providerId);
306
+ }
307
+ catch (error) {
308
+ // Server unavailable - use local fallback
309
+ const { getLocalProvider, getLocalProviderConfigRequirements } = await Promise.resolve().then(() => __importStar(require('../providers/local-provider-registry')));
310
+ provider = getLocalProvider(providerId);
311
+ if (!provider) {
312
+ throw new Error(`Unknown provider: ${providerId}`);
313
+ }
314
+ schema = { configRequirements: getLocalProviderConfigRequirements(providerId) };
315
+ }
316
+ if (!provider) {
317
+ throw new Error(`Unknown provider: ${providerId}`);
318
+ }
319
+ // Get token if not provided
320
+ const token = existingToken || await promptForProviderToken(client, providerId);
321
+ // Get additional config if needed
322
+ const requirements = schema.configRequirements;
323
+ let config;
324
+ if (requirements.length > 0) {
325
+ // Check if we have all required config
326
+ const hasAllConfig = existingConfig && requirements.every(req => !req.required || existingConfig[req.key]);
327
+ if (!hasAllConfig) {
328
+ config = await promptForProviderConfig(client, providerId);
329
+ // Merge with existing config
330
+ if (existingConfig) {
331
+ config = { ...existingConfig, ...config };
332
+ }
333
+ }
334
+ else {
335
+ config = existingConfig;
336
+ }
337
+ }
338
+ return { token, config };
339
+ }
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getManagedNodeRoot = getManagedNodeRoot;
7
+ exports.getPortableNodeBinPath = getPortableNodeBinPath;
8
+ exports.getManagedAgentBinDirs = getManagedAgentBinDirs;
9
+ exports.stripManagedAgentBinDirsFromPath = stripManagedAgentBinDirsFromPath;
10
+ exports.appendBinDirsToPath = appendBinDirsToPath;
11
+ exports.getNpmGlobalBinDirsFromPrefix = getNpmGlobalBinDirsFromPrefix;
12
+ exports.resolveNpmGlobalBinDirs = resolveNpmGlobalBinDirs;
13
+ exports.buildPathWithManagedAgentBins = buildPathWithManagedAgentBins;
14
+ exports.appendManagedAgentBinDirsToProcessPath = appendManagedAgentBinDirsToProcessPath;
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const child_process_1 = require("child_process");
18
+ const script_sync_utils_1 = require("./script-sync-utils");
19
+ function getManagedNodeRoot() {
20
+ return path_1.default.join((0, script_sync_utils_1.getUserFraimDir)(), 'node');
21
+ }
22
+ function getPortableNodeBinPath() {
23
+ const nodeRoot = getManagedNodeRoot();
24
+ if (process.platform === 'win32') {
25
+ if (fs_1.default.existsSync(nodeRoot)) {
26
+ const extractedDir = fs_1.default.readdirSync(nodeRoot, { withFileTypes: true })
27
+ .filter((entry) => entry.isDirectory() && entry.name.startsWith('node-v'))
28
+ .sort((a, b) => b.name.localeCompare(a.name))[0];
29
+ if (extractedDir) {
30
+ return path_1.default.join(nodeRoot, extractedDir.name);
31
+ }
32
+ }
33
+ return nodeRoot;
34
+ }
35
+ return path_1.default.join(nodeRoot, 'bin');
36
+ }
37
+ function getManagedAgentBinDirs() {
38
+ const nodeRoot = getManagedNodeRoot();
39
+ const portableNodeBin = getPortableNodeBinPath();
40
+ const candidates = process.platform === 'win32'
41
+ ? [nodeRoot, portableNodeBin]
42
+ : [nodeRoot, path_1.default.join(nodeRoot, 'bin'), portableNodeBin];
43
+ return [...new Set(candidates.filter(Boolean))];
44
+ }
45
+ function normalizePathEntry(entry) {
46
+ const resolved = path_1.default.resolve(entry.trim());
47
+ return process.platform === 'win32' ? resolved.toLowerCase() : resolved;
48
+ }
49
+ function uniquePathEntries(entries) {
50
+ const seen = new Set();
51
+ const result = [];
52
+ for (const entry of entries.filter(Boolean)) {
53
+ const normalized = normalizePathEntry(entry);
54
+ if (seen.has(normalized))
55
+ continue;
56
+ seen.add(normalized);
57
+ result.push(entry);
58
+ }
59
+ return result;
60
+ }
61
+ function stripManagedAgentBinDirsFromPath(basePath) {
62
+ const managed = new Set(getManagedAgentBinDirs().map(normalizePathEntry));
63
+ return (basePath ?? process.env.PATH ?? '')
64
+ .split(path_1.default.delimiter)
65
+ .filter(Boolean)
66
+ .filter((entry) => !managed.has(normalizePathEntry(entry)))
67
+ .join(path_1.default.delimiter);
68
+ }
69
+ function appendBinDirsToPath(basePath, binDirs) {
70
+ const existing = (basePath ?? '').split(path_1.default.delimiter).filter(Boolean);
71
+ return uniquePathEntries([...existing, ...binDirs]).join(path_1.default.delimiter);
72
+ }
73
+ function getNpmGlobalBinDirsFromPrefix(prefix) {
74
+ const trimmed = prefix.trim();
75
+ if (!trimmed)
76
+ return [];
77
+ const resolved = path_1.default.resolve(trimmed);
78
+ const candidates = process.platform === 'win32'
79
+ ? [resolved, path_1.default.join(resolved, 'bin')]
80
+ : [path_1.default.join(resolved, 'bin')];
81
+ return uniquePathEntries(candidates);
82
+ }
83
+ function buildChildEnv(env) {
84
+ const childEnv = { ...process.env };
85
+ for (const [key, value] of Object.entries(env || {})) {
86
+ if (value === undefined)
87
+ delete childEnv[key];
88
+ else
89
+ childEnv[key] = value;
90
+ }
91
+ return childEnv;
92
+ }
93
+ function resolveNpmGlobalBinDirs(basePath, env) {
94
+ const childEnv = buildChildEnv({
95
+ ...env,
96
+ ...(basePath === undefined ? {} : { PATH: basePath }),
97
+ });
98
+ const executable = process.platform === 'win32' ? 'cmd.exe' : 'npm';
99
+ const args = process.platform === 'win32'
100
+ ? ['/d', '/s', '/c', 'npm', 'prefix', '-g']
101
+ : ['prefix', '-g'];
102
+ const result = (0, child_process_1.spawnSync)(executable, args, {
103
+ encoding: 'utf8',
104
+ timeout: 5000,
105
+ env: childEnv,
106
+ });
107
+ if (result.status !== 0 || result.error)
108
+ return [];
109
+ const output = (result.stdout || '').trim() || (result.stderr || '').trim();
110
+ const prefix = output.split(/\r?\n/)[0]?.trim() || '';
111
+ return getNpmGlobalBinDirsFromPrefix(prefix);
112
+ }
113
+ function buildPathWithManagedAgentBins(basePath) {
114
+ return appendBinDirsToPath(stripManagedAgentBinDirsFromPath(basePath), getManagedAgentBinDirs());
115
+ }
116
+ function appendManagedAgentBinDirsToProcessPath() {
117
+ process.env.PATH = buildPathWithManagedAgentBins(process.env.PATH);
118
+ }