bc-telemetry-buddy-mcp 2.0.4 → 2.0.5
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/CHANGELOG.md +7 -0
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.5] - 2025-11-19
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **Build Process**: Added pretest script to generate version.ts before running tests
|
|
15
|
+
- **CI Pipeline**: Fixed test compilation errors in CI environment
|
|
16
|
+
|
|
10
17
|
## [2.0.4] - 2025-11-19
|
|
11
18
|
|
|
12
19
|
### Fixed
|
package/dist/cli.js
CHANGED
|
@@ -248,7 +248,7 @@ ${l}
|
|
|
248
248
|
Shutting down gracefully...`),process.exit(0)}),process.on("SIGTERM",()=>{console.log(`
|
|
249
249
|
Shutting down gracefully...`),process.exit(0)})}async startStdio(){if(console.log("BC Telemetry Buddy MCP Server starting in stdio mode"),this.configErrors.length===0)try{await this.auth.authenticate(),console.log("Authentication successful")}catch(t){console.error("Authentication failed:",t.message)}else console.log("Skipping authentication (configuration incomplete)");let e="";process.stdin.setEncoding("utf8"),process.stdin.on("data",async t=>{e+=t;let r=e.split(`
|
|
250
250
|
`);e=r.pop()||"";for(let i of r)if(i.trim())try{let o=JSON.parse(i),a=await this.handleStdioJSONRPC(o);process.stdout.write(JSON.stringify(a)+`
|
|
251
|
-
`)}catch(o){console.error("Failed to process request:",o.message)}}),process.stdin.on("end",()=>{console.log("Stdin closed, shutting down"),process.exit(0)}),process.on("SIGINT",()=>{console.log("Shutting down gracefully..."),process.exit(0)}),process.on("SIGTERM",()=>{console.log("Shutting down gracefully..."),process.exit(0)})}async handleStdioJSONRPC(e){let{id:t,method:r,params:i}=e;try{let o;switch(r){case"initialize":o={protocolVersion:"2024-11-05",serverInfo:{name:"BC Telemetry Buddy",version:"0.1.0"},capabilities:{tools:{}}};break;case"tools/list":o={tools:[{name:"query_telemetry",description:"Execute a KQL query against Business Central telemetry data. CRITICAL PREREQUISITE: You MUST call get_event_catalog() FIRST to discover available event IDs, then call get_event_field_samples() to understand field structure BEFORE constructing any KQL query. DO NOT use this tool without completing the discovery flow first.",inputSchema:{type:"object",properties:{kql:{type:"string",description:"KQL query string constructed using event IDs from get_event_catalog() and field names from get_event_field_samples()"},useContext:{type:"boolean",description:"Use saved queries as examples",default:!0},includeExternal:{type:"boolean",description:"Include external reference queries",default:!0}},required:["kql"]}},{name:"get_saved_queries",description:"List all saved telemetry queries in the workspace",inputSchema:{type:"object",properties:{tags:{type:"array",items:{type:"string"},description:"Filter by tags (optional)"}}}},{name:"search_queries",description:"Search saved queries by keywords",inputSchema:{type:"object",properties:{searchTerms:{type:"array",items:{type:"string"},description:"Search terms"}},required:["searchTerms"]}},{name:"save_query",description:"Save a telemetry query for future reference",inputSchema:{type:"object",properties:{name:{type:"string",description:"Query name"},kql:{type:"string",description:"KQL query string"},purpose:{type:"string",description:"Query purpose"},useCase:{type:"string",description:"When to use this query"},tags:{type:"array",items:{type:"string"},description:"Tags for categorization"},category:{type:"string",description:"Category/folder for organization"}},required:["name","kql"]}},{name:"get_categories",description:"List all query categories in the workspace",inputSchema:{type:"object",properties:{}}},{name:"get_recommendations",description:"Get query optimization recommendations",inputSchema:{type:"object",properties:{kql:{type:"string",description:"KQL query to analyze"},results:{type:"object",description:"Query results to analyze"}}}},{name:"get_external_queries",description:"Get example queries from external references",inputSchema:{type:"object",properties:{}}},{name:"get_event_catalog",description:"Discover available Business Central telemetry event IDs with descriptions, status, and documentation URLs. Optionally includes analysis of common fields that appear across multiple events. Results are limited to top events by count to prevent overwhelming responses.",inputSchema:{type:"object",properties:{daysBack:{type:"number",description:"Number of days to look back (default: 10)",default:10},status:{type:"string",description:"Filter by status: all, success, error, too slow, unknown (default: all)",default:"all"},minCount:{type:"number",description:"Minimum occurrence count to include (default: 1)",default:1},maxResults:{type:"number",description:"Maximum number of events to return (default: 50, max: 200)",default:50},includeCommonFields:{type:"boolean",description:"Include analysis of common customDimensions fields that appear across multiple events (default: false)",default:!1}}}},{name:"get_event_schema",description:"Get schema details for a specific event ID including all customDimensions fields with examples",inputSchema:{type:"object",properties:{eventId:{type:"string",description:"Event ID to analyze (e.g., RT0005, LC0012)"},sampleSize:{type:"number",description:"Number of samples to analyze (default: 100)",default:100}},required:["eventId"]}},{name:"get_event_field_samples",description:"RECOMMENDED: Get detailed field analysis from real telemetry events including data types, occurrence rates, and sample values. Also provides event category information (Performance, Lifecycle, Security, etc.) dynamically fetched from Microsoft Learn documentation. Returns ready-to-use example query with proper type conversions and documentation links. Use this to understand the exact structure of customDimensions before writing queries.",inputSchema:{type:"object",properties:{eventId:{type:"string",description:"Event ID to analyze (e.g., RT0005, LC0011)"},sampleCount:{type:"number",description:"Number of events to sample for analysis",default:10},daysBack:{type:"number",description:"How many days back to search for events",default:30}},required:["eventId"]}},{name:"get_tenant_mapping",description:"IMPORTANT: BC telemetry uses aadTenantId (not company names) for filtering. Use this tool to map company/customer names to tenant IDs before querying. Always call this first when user asks about a specific customer/company.",inputSchema:{type:"object",properties:{daysBack:{type:"number",description:"Number of days to look back for mappings (default: 10)",default:10},companyNameFilter:{type:"string",description:"Optional: Filter for specific company name (partial match)"}}}},{name:"list_profiles",description:"List all available telemetry profiles in the workspace configuration. Shows the currently active profile and all other available profiles. Each profile represents a different customer/environment with separate credentials and App Insights configuration. Use this to understand which profiles are available before querying data.",inputSchema:{type:"object",properties:{}}}]};break;case"tools/call":let a=i?.name,s=i?.arguments||{},c=await this.executeToolCall(a,s);o={content:[{type:"text",text:typeof c=="string"?c:JSON.stringify(c,null,2)}]};break;case"query_telemetry":let l=i.kql;if(!l||l.trim()==="")throw new Error("kql parameter is required. Use get_event_catalog() to discover events and get_event_field_samples() to understand field structure.");o=await this.executeQuery(l,i.useContext||!1,i.includeExternal||!1);break;case"get_saved_queries":o=this.queries.getAllQueries();break;case"search_queries":o=this.queries.searchQueries(i.searchTerms||[]);break;case"save_query":o={filePath:this.queries.saveQuery(i.name,i.kql,i.purpose,i.useCase,i.tags,i.category,i.companyName)};break;case"get_categories":o=this.queries.getCategories();break;case"get_recommendations":o=await this.generateRecommendations(i.kql,i.results);break;case"get_external_queries":o=await this.references.getAllExternalQueries();break;case"get_event_catalog":o=await this.getEventCatalog(i?.daysBack||10,i?.status||"all",i?.minCount||1,i?.includeCommonFields||!1,i?.maxResults||50);break;case"get_event_schema":if(!i?.eventId)throw new Error("eventId parameter is required");o=await this.getEventSchema(i.eventId,i?.sampleSize||100);break;case"get_event_field_samples":if(!i?.eventId)throw new Error("eventId parameter is required");o=await this.getEventFieldSamples(i.eventId,i?.sampleCount||10,i?.daysBack||30);break;case"get_tenant_mapping":o=await this.getTenantMapping(i?.daysBack||10,i?.companyNameFilter);break;case"get_cache_stats":o=this.cache.getStats();break;case"clear_cache":this.cache.clear(),o={success:!0,message:"Cache cleared successfully"};break;case"cleanup_cache":this.cache.cleanupExpired();let p=this.cache.getStats();o={success:!0,message:`Cleaned up expired entries. ${p.totalEntries} entries remaining`,stats:p};break;default:throw new Error(`Unknown method: ${r}`)}return{jsonrpc:"2.0",id:t,result:o}}catch(o){return{jsonrpc:"2.0",id:t,error:{code:-32603,message:o.message}}}}async executeToolCall(e,t){switch(e){case"query_telemetry":let r=t.kql;if(!r||r.trim()==="")throw new Error("kql parameter is required. PREREQUISITE: Call get_event_catalog() to discover events and get_event_field_samples() to understand field structure BEFORE constructing queries.");return await this.executeQuery(r,t.useContext||!1,t.includeExternal||!1);case"get_saved_queries":return this.queries.getAllQueries();case"search_queries":return this.queries.searchQueries(t.searchTerms||[]);case"save_query":return{filePath:this.queries.saveQuery(t.name,t.kql,t.purpose,t.useCase,t.tags,t.category,t.companyName)};case"get_categories":return this.queries.getCategories();case"get_recommendations":return await this.generateRecommendations(t.kql,t.results);case"get_external_queries":return await this.references.getAllExternalQueries();case"get_event_catalog":return await this.getEventCatalog(t?.daysBack||10,t?.status||"all",t?.minCount||1,t?.includeCommonFields||!1);case"get_event_schema":if(!t?.eventId)throw new Error("eventId parameter is required");return await this.getEventSchema(t.eventId,t?.sampleSize||100);case"get_event_field_samples":if(!t?.eventId)throw new Error("eventId parameter is required");return await this.getEventFieldSamples(t.eventId,t?.sampleCount||20,t?.daysBack||7);case"get_tenant_mapping":return await this.getTenantMapping(t?.daysBack||10,t?.companyNameFilter);case"get_cache_stats":return this.cache.getStats();case"clear_cache":return this.cache.clear(),{success:!0,message:"Cache cleared successfully"};case"list_profiles":return this.listProfiles();case"cleanup_cache":this.cache.cleanupExpired();let a=this.cache.getStats();return{success:!0,message:`Cleaned up expired entries. ${a.totalEntries} entries remaining`,stats:a};default:throw new Error(`Unknown tool: ${e}`)}}};require.main===module&&L6()});var fE=un(dE(),1),{program:_te,createCommand:Ate,createArgument:Ste,createOption:Ite,CommanderError:Rte,InvalidArgumentError:kte,InvalidOptionArgumentError:Ote,Command:hE,Argument:Nte,Option:Pte,Help:Mte}=fE.default;Ih();var q6=un(wv()),Ml=un(require("fs"));var $R="2.0.4";var xi=new hE;xi.name("bctb-mcp").description("BC Telemetry Buddy MCP Server").version($R);xi.command("start").description("Start the MCP server").option("-c, --config <path>","Path to config file").option("--stdio","Use stdio mode (default)",!0).option("--http","Use HTTP mode").option("-p, --profile <name>","Profile name to use (for multi-profile configs)").action(async n=>{try{let e=Ci(n.config,n.profile),t=ba(e);t.length>0&&(console.error("Configuration errors:"),t.forEach(i=>console.error(` - ${i}`)),process.exit(1));let{startServer:r}=await Promise.resolve().then(()=>(U6(),F6));n.http?await r(e,"http"):await r(e,"stdio")}catch(e){console.error("Failed to start server:",e.message),process.exit(1)}});xi.command("init").description("Create a config file template").option("-o, --output <path>","Output path",".bctb-config.json").action(n=>{try{Ml.existsSync(n.output)&&(console.error(`File already exists: ${n.output}`),console.error("Use a different path or delete the existing file."),process.exit(1)),vE(n.output),console.log(`\u2713 Created config template: ${n.output}`),console.log(`
|
|
251
|
+
`)}catch(o){console.error("Failed to process request:",o.message)}}),process.stdin.on("end",()=>{console.log("Stdin closed, shutting down"),process.exit(0)}),process.on("SIGINT",()=>{console.log("Shutting down gracefully..."),process.exit(0)}),process.on("SIGTERM",()=>{console.log("Shutting down gracefully..."),process.exit(0)})}async handleStdioJSONRPC(e){let{id:t,method:r,params:i}=e;try{let o;switch(r){case"initialize":o={protocolVersion:"2024-11-05",serverInfo:{name:"BC Telemetry Buddy",version:"0.1.0"},capabilities:{tools:{}}};break;case"tools/list":o={tools:[{name:"query_telemetry",description:"Execute a KQL query against Business Central telemetry data. CRITICAL PREREQUISITE: You MUST call get_event_catalog() FIRST to discover available event IDs, then call get_event_field_samples() to understand field structure BEFORE constructing any KQL query. DO NOT use this tool without completing the discovery flow first.",inputSchema:{type:"object",properties:{kql:{type:"string",description:"KQL query string constructed using event IDs from get_event_catalog() and field names from get_event_field_samples()"},useContext:{type:"boolean",description:"Use saved queries as examples",default:!0},includeExternal:{type:"boolean",description:"Include external reference queries",default:!0}},required:["kql"]}},{name:"get_saved_queries",description:"List all saved telemetry queries in the workspace",inputSchema:{type:"object",properties:{tags:{type:"array",items:{type:"string"},description:"Filter by tags (optional)"}}}},{name:"search_queries",description:"Search saved queries by keywords",inputSchema:{type:"object",properties:{searchTerms:{type:"array",items:{type:"string"},description:"Search terms"}},required:["searchTerms"]}},{name:"save_query",description:"Save a telemetry query for future reference",inputSchema:{type:"object",properties:{name:{type:"string",description:"Query name"},kql:{type:"string",description:"KQL query string"},purpose:{type:"string",description:"Query purpose"},useCase:{type:"string",description:"When to use this query"},tags:{type:"array",items:{type:"string"},description:"Tags for categorization"},category:{type:"string",description:"Category/folder for organization"}},required:["name","kql"]}},{name:"get_categories",description:"List all query categories in the workspace",inputSchema:{type:"object",properties:{}}},{name:"get_recommendations",description:"Get query optimization recommendations",inputSchema:{type:"object",properties:{kql:{type:"string",description:"KQL query to analyze"},results:{type:"object",description:"Query results to analyze"}}}},{name:"get_external_queries",description:"Get example queries from external references",inputSchema:{type:"object",properties:{}}},{name:"get_event_catalog",description:"Discover available Business Central telemetry event IDs with descriptions, status, and documentation URLs. Optionally includes analysis of common fields that appear across multiple events. Results are limited to top events by count to prevent overwhelming responses.",inputSchema:{type:"object",properties:{daysBack:{type:"number",description:"Number of days to look back (default: 10)",default:10},status:{type:"string",description:"Filter by status: all, success, error, too slow, unknown (default: all)",default:"all"},minCount:{type:"number",description:"Minimum occurrence count to include (default: 1)",default:1},maxResults:{type:"number",description:"Maximum number of events to return (default: 50, max: 200)",default:50},includeCommonFields:{type:"boolean",description:"Include analysis of common customDimensions fields that appear across multiple events (default: false)",default:!1}}}},{name:"get_event_schema",description:"Get schema details for a specific event ID including all customDimensions fields with examples",inputSchema:{type:"object",properties:{eventId:{type:"string",description:"Event ID to analyze (e.g., RT0005, LC0012)"},sampleSize:{type:"number",description:"Number of samples to analyze (default: 100)",default:100}},required:["eventId"]}},{name:"get_event_field_samples",description:"RECOMMENDED: Get detailed field analysis from real telemetry events including data types, occurrence rates, and sample values. Also provides event category information (Performance, Lifecycle, Security, etc.) dynamically fetched from Microsoft Learn documentation. Returns ready-to-use example query with proper type conversions and documentation links. Use this to understand the exact structure of customDimensions before writing queries.",inputSchema:{type:"object",properties:{eventId:{type:"string",description:"Event ID to analyze (e.g., RT0005, LC0011)"},sampleCount:{type:"number",description:"Number of events to sample for analysis",default:10},daysBack:{type:"number",description:"How many days back to search for events",default:30}},required:["eventId"]}},{name:"get_tenant_mapping",description:"IMPORTANT: BC telemetry uses aadTenantId (not company names) for filtering. Use this tool to map company/customer names to tenant IDs before querying. Always call this first when user asks about a specific customer/company.",inputSchema:{type:"object",properties:{daysBack:{type:"number",description:"Number of days to look back for mappings (default: 10)",default:10},companyNameFilter:{type:"string",description:"Optional: Filter for specific company name (partial match)"}}}},{name:"list_profiles",description:"List all available telemetry profiles in the workspace configuration. Shows the currently active profile and all other available profiles. Each profile represents a different customer/environment with separate credentials and App Insights configuration. Use this to understand which profiles are available before querying data.",inputSchema:{type:"object",properties:{}}}]};break;case"tools/call":let a=i?.name,s=i?.arguments||{},c=await this.executeToolCall(a,s);o={content:[{type:"text",text:typeof c=="string"?c:JSON.stringify(c,null,2)}]};break;case"query_telemetry":let l=i.kql;if(!l||l.trim()==="")throw new Error("kql parameter is required. Use get_event_catalog() to discover events and get_event_field_samples() to understand field structure.");o=await this.executeQuery(l,i.useContext||!1,i.includeExternal||!1);break;case"get_saved_queries":o=this.queries.getAllQueries();break;case"search_queries":o=this.queries.searchQueries(i.searchTerms||[]);break;case"save_query":o={filePath:this.queries.saveQuery(i.name,i.kql,i.purpose,i.useCase,i.tags,i.category,i.companyName)};break;case"get_categories":o=this.queries.getCategories();break;case"get_recommendations":o=await this.generateRecommendations(i.kql,i.results);break;case"get_external_queries":o=await this.references.getAllExternalQueries();break;case"get_event_catalog":o=await this.getEventCatalog(i?.daysBack||10,i?.status||"all",i?.minCount||1,i?.includeCommonFields||!1,i?.maxResults||50);break;case"get_event_schema":if(!i?.eventId)throw new Error("eventId parameter is required");o=await this.getEventSchema(i.eventId,i?.sampleSize||100);break;case"get_event_field_samples":if(!i?.eventId)throw new Error("eventId parameter is required");o=await this.getEventFieldSamples(i.eventId,i?.sampleCount||10,i?.daysBack||30);break;case"get_tenant_mapping":o=await this.getTenantMapping(i?.daysBack||10,i?.companyNameFilter);break;case"get_cache_stats":o=this.cache.getStats();break;case"clear_cache":this.cache.clear(),o={success:!0,message:"Cache cleared successfully"};break;case"cleanup_cache":this.cache.cleanupExpired();let p=this.cache.getStats();o={success:!0,message:`Cleaned up expired entries. ${p.totalEntries} entries remaining`,stats:p};break;default:throw new Error(`Unknown method: ${r}`)}return{jsonrpc:"2.0",id:t,result:o}}catch(o){return{jsonrpc:"2.0",id:t,error:{code:-32603,message:o.message}}}}async executeToolCall(e,t){switch(e){case"query_telemetry":let r=t.kql;if(!r||r.trim()==="")throw new Error("kql parameter is required. PREREQUISITE: Call get_event_catalog() to discover events and get_event_field_samples() to understand field structure BEFORE constructing queries.");return await this.executeQuery(r,t.useContext||!1,t.includeExternal||!1);case"get_saved_queries":return this.queries.getAllQueries();case"search_queries":return this.queries.searchQueries(t.searchTerms||[]);case"save_query":return{filePath:this.queries.saveQuery(t.name,t.kql,t.purpose,t.useCase,t.tags,t.category,t.companyName)};case"get_categories":return this.queries.getCategories();case"get_recommendations":return await this.generateRecommendations(t.kql,t.results);case"get_external_queries":return await this.references.getAllExternalQueries();case"get_event_catalog":return await this.getEventCatalog(t?.daysBack||10,t?.status||"all",t?.minCount||1,t?.includeCommonFields||!1);case"get_event_schema":if(!t?.eventId)throw new Error("eventId parameter is required");return await this.getEventSchema(t.eventId,t?.sampleSize||100);case"get_event_field_samples":if(!t?.eventId)throw new Error("eventId parameter is required");return await this.getEventFieldSamples(t.eventId,t?.sampleCount||20,t?.daysBack||7);case"get_tenant_mapping":return await this.getTenantMapping(t?.daysBack||10,t?.companyNameFilter);case"get_cache_stats":return this.cache.getStats();case"clear_cache":return this.cache.clear(),{success:!0,message:"Cache cleared successfully"};case"list_profiles":return this.listProfiles();case"cleanup_cache":this.cache.cleanupExpired();let a=this.cache.getStats();return{success:!0,message:`Cleaned up expired entries. ${a.totalEntries} entries remaining`,stats:a};default:throw new Error(`Unknown tool: ${e}`)}}};require.main===module&&L6()});var fE=un(dE(),1),{program:_te,createCommand:Ate,createArgument:Ste,createOption:Ite,CommanderError:Rte,InvalidArgumentError:kte,InvalidOptionArgumentError:Ote,Command:hE,Argument:Nte,Option:Pte,Help:Mte}=fE.default;Ih();var q6=un(wv()),Ml=un(require("fs"));var $R="2.0.5";var xi=new hE;xi.name("bctb-mcp").description("BC Telemetry Buddy MCP Server").version($R);xi.command("start").description("Start the MCP server").option("-c, --config <path>","Path to config file").option("--stdio","Use stdio mode (default)",!0).option("--http","Use HTTP mode").option("-p, --profile <name>","Profile name to use (for multi-profile configs)").action(async n=>{try{let e=Ci(n.config,n.profile),t=ba(e);t.length>0&&(console.error("Configuration errors:"),t.forEach(i=>console.error(` - ${i}`)),process.exit(1));let{startServer:r}=await Promise.resolve().then(()=>(U6(),F6));n.http?await r(e,"http"):await r(e,"stdio")}catch(e){console.error("Failed to start server:",e.message),process.exit(1)}});xi.command("init").description("Create a config file template").option("-o, --output <path>","Output path",".bctb-config.json").action(n=>{try{Ml.existsSync(n.output)&&(console.error(`File already exists: ${n.output}`),console.error("Use a different path or delete the existing file."),process.exit(1)),vE(n.output),console.log(`\u2713 Created config template: ${n.output}`),console.log(`
|
|
252
252
|
Next steps:`),console.log("1. Edit the config file with your Application Insights details"),console.log("2. Run: bctb-mcp validate"),console.log("3. Run: bctb-mcp start")}catch(e){console.error("Failed to create config:",e.message),process.exit(1)}});xi.command("validate").description("Validate a config file").option("-c, --config <path>","Path to config file",".bctb-config.json").option("-p, --profile <name>","Profile name to validate (for multi-profile configs)").action(n=>{try{let e=Ci(n.config,n.profile),t=ba(e);t.length===0?(console.log("\u2713 Configuration is valid"),console.log(` Connection: ${e.connectionName}`),console.log(` Auth flow: ${e.authFlow}`),console.log(` App Insights: ${e.applicationInsightsAppId||"Not configured"}`)):(console.error("\u2717 Configuration errors:"),t.forEach(r=>console.error(` - ${r}`)),process.exit(1))}catch(e){console.error("\u2717 Failed to load config:",e.message),process.exit(1)}});xi.command("test-auth").description("Test authentication").option("-c, --config <path>","Path to config file",".bctb-config.json").option("-p, --profile <name>","Profile name to test (for multi-profile configs)").action(async n=>{try{let e=Ci(n.config,n.profile),t=new q6.AuthService(e);console.log(`Testing authentication for: ${e.connectionName}`),console.log(`Auth flow: ${e.authFlow}
|
|
253
253
|
`);let r=await t.authenticate();r.authenticated?(console.log("\u2713 Authentication successful"),console.log(` User: ${r.user}`),r.expiresOn&&console.log(` Expires: ${r.expiresOn.toISOString()}`)):(console.error("\u2717 Authentication failed"),process.exit(1))}catch(e){console.error("\u2717 Authentication failed:",e.message),process.exit(1)}});xi.command("list-profiles").description("List all available profiles").option("-c, --config <path>","Path to config file",".bctb-config.json").action(n=>{try{let e=n.config||".bctb-config.json";Ml.existsSync(e)||(console.error(`Config file not found: ${e}`),process.exit(1));let t=JSON.parse(Ml.readFileSync(e,"utf-8"));if(!t.profiles){console.log("No profiles found (single config mode)"),console.log(`Connection: ${t.connectionName||"Unnamed"}`);return}console.log(`Available profiles:
|
|
254
254
|
`),Object.entries(t.profiles).forEach(([r,i])=>{let a=r===t.defaultProfile?"\u2713":" ",s=r.startsWith("_")?"(base profile)":"";console.log(` [${a}] ${r}`),console.log(` ${i.connectionName||"Unnamed"} ${s}`),i.extends&&console.log(` Extends: ${i.extends}`),console.log("")}),t.defaultProfile&&console.log(`Default profile: ${t.defaultProfile}`)}catch(e){console.error("Failed to list profiles:",e.message),process.exit(1)}});xi.parse();
|