fraim-framework 2.0.55 → 2.0.57

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/CHANGELOG.md +10 -0
  2. package/dist/src/cli/commands/init-project.js +10 -4
  3. package/dist/src/cli/setup/mcp-config-generator.js +23 -15
  4. package/dist/src/local-mcp-server/stdio-server.js +207 -0
  5. package/dist/src/utils/validate-workflows.js +101 -0
  6. package/dist/src/utils/workflow-parser.js +81 -0
  7. package/package.json +16 -11
  8. package/registry/scripts/pdf-styles.css +172 -0
  9. package/registry/scripts/prep-issue.sh +46 -4
  10. package/registry/scripts/profile-server.ts +131 -130
  11. package/registry/stubs/workflows/customer-development/user-survey-dispatch.md +1 -1
  12. package/registry/stubs/workflows/customer-development/users-to-target.md +1 -1
  13. package/registry/stubs/workflows/product-building/design.md +1 -1
  14. package/registry/stubs/workflows/product-building/implement.md +1 -1
  15. package/Claude.md +0 -1
  16. package/dist/registry/ai-manager-rules/design-phases/design-completeness-review.md +0 -73
  17. package/dist/registry/ai-manager-rules/design-phases/design-design.md +0 -145
  18. package/dist/registry/ai-manager-rules/implement-phases/implement-code.md +0 -283
  19. package/dist/registry/ai-manager-rules/implement-phases/implement-completeness-review.md +0 -120
  20. package/dist/registry/ai-manager-rules/implement-phases/implement-regression.md +0 -173
  21. package/dist/registry/ai-manager-rules/implement-phases/implement-repro.md +0 -104
  22. package/dist/registry/ai-manager-rules/implement-phases/implement-scoping.md +0 -100
  23. package/dist/registry/ai-manager-rules/implement-phases/implement-smoke.md +0 -237
  24. package/dist/registry/ai-manager-rules/implement-phases/implement-spike.md +0 -121
  25. package/dist/registry/ai-manager-rules/implement-phases/implement-validate.md +0 -375
  26. package/dist/registry/ai-manager-rules/retrospective.md +0 -116
  27. package/dist/registry/ai-manager-rules/shared-phases/address-pr-feedback.md +0 -188
  28. package/dist/registry/ai-manager-rules/shared-phases/submit-pr.md +0 -202
  29. package/dist/registry/ai-manager-rules/shared-phases/wait-for-pr-review.md +0 -170
  30. package/dist/registry/ai-manager-rules/spec-phases/spec-competitor-analysis.md +0 -105
  31. package/dist/registry/ai-manager-rules/spec-phases/spec-completeness-review.md +0 -66
  32. package/dist/registry/ai-manager-rules/spec-phases/spec-spec.md +0 -139
  33. package/dist/registry/providers/ado.json +0 -19
  34. package/dist/registry/providers/github.json +0 -19
  35. package/dist/registry/scripts/cleanup-branch.js +0 -287
  36. package/dist/registry/scripts/evaluate-code-quality.js +0 -66
  37. package/dist/registry/scripts/exec-with-timeout.js +0 -142
  38. package/dist/registry/scripts/generate-engagement-emails.js +0 -705
  39. package/dist/registry/scripts/newsletter-helpers.js +0 -671
  40. package/dist/registry/scripts/profile-server.js +0 -388
  41. package/dist/registry/scripts/run-thank-you-workflow.js +0 -92
  42. package/dist/registry/scripts/send-newsletter-simple.js +0 -85
  43. package/dist/registry/scripts/send-thank-you-emails.js +0 -54
  44. package/dist/registry/scripts/validate-openapi-limits.js +0 -311
  45. package/dist/registry/scripts/validate-test-coverage.js +0 -262
  46. package/dist/registry/scripts/verify-test-coverage.js +0 -66
  47. package/dist/scripts/build-stub-registry.js +0 -108
  48. package/dist/src/ai-manager/ai-manager.js +0 -482
  49. package/dist/src/ai-manager/phase-flow.js +0 -357
  50. package/dist/src/ai-manager/types.js +0 -5
  51. package/dist/src/fraim-mcp-server.js +0 -1885
  52. package/dist/tests/debug-tools.js +0 -80
  53. package/dist/tests/shared-server-utils.js +0 -57
  54. package/dist/tests/test-add-ide.js +0 -283
  55. package/dist/tests/test-ai-coach-edge-cases.js +0 -420
  56. package/dist/tests/test-ai-coach-mcp-integration.js +0 -450
  57. package/dist/tests/test-ai-coach-performance.js +0 -328
  58. package/dist/tests/test-ai-coach-phase-content.js +0 -264
  59. package/dist/tests/test-ai-coach-workflows.js +0 -514
  60. package/dist/tests/test-cli.js +0 -228
  61. package/dist/tests/test-client-scripts-validation.js +0 -167
  62. package/dist/tests/test-complete-setup-flow.js +0 -110
  63. package/dist/tests/test-config-system.js +0 -279
  64. package/dist/tests/test-debug-session.js +0 -134
  65. package/dist/tests/test-end-to-end-hybrid-validation.js +0 -328
  66. package/dist/tests/test-enhanced-session-init.js +0 -188
  67. package/dist/tests/test-first-run-journey.js +0 -368
  68. package/dist/tests/test-fraim-issues.js +0 -59
  69. package/dist/tests/test-genericization.js +0 -44
  70. package/dist/tests/test-hybrid-script-execution.js +0 -340
  71. package/dist/tests/test-ide-detector.js +0 -46
  72. package/dist/tests/test-improved-setup.js +0 -121
  73. package/dist/tests/test-mcp-config-generator.js +0 -99
  74. package/dist/tests/test-mcp-connection.js +0 -107
  75. package/dist/tests/test-mcp-issue-integration.js +0 -156
  76. package/dist/tests/test-mcp-lifecycle-methods.js +0 -240
  77. package/dist/tests/test-mcp-shared-server.js +0 -308
  78. package/dist/tests/test-mcp-template-processing.js +0 -160
  79. package/dist/tests/test-modular-issue-tracking.js +0 -165
  80. package/dist/tests/test-node-compatibility.js +0 -95
  81. package/dist/tests/test-npm-install.js +0 -68
  82. package/dist/tests/test-package-size.js +0 -108
  83. package/dist/tests/test-pr-review-workflow.js +0 -307
  84. package/dist/tests/test-prep-issue.js +0 -129
  85. package/dist/tests/test-productivity-integration.js +0 -157
  86. package/dist/tests/test-script-location-independence.js +0 -198
  87. package/dist/tests/test-script-sync.js +0 -557
  88. package/dist/tests/test-server-utils.js +0 -32
  89. package/dist/tests/test-session-rehydration.js +0 -148
  90. package/dist/tests/test-setup-integration.js +0 -98
  91. package/dist/tests/test-setup-scenarios.js +0 -322
  92. package/dist/tests/test-standalone.js +0 -143
  93. package/dist/tests/test-stub-registry.js +0 -136
  94. package/dist/tests/test-sync-stubs.js +0 -143
  95. package/dist/tests/test-sync-version-update.js +0 -93
  96. package/dist/tests/test-telemetry.js +0 -193
  97. package/dist/tests/test-token-validator.js +0 -30
  98. package/dist/tests/test-user-journey.js +0 -236
  99. package/dist/tests/test-users-to-target-workflow.js +0 -253
  100. package/dist/tests/test-utils.js +0 -109
  101. package/dist/tests/test-wizard.js +0 -71
  102. package/dist/tests/test-workflow-discovery.js +0 -242
  103. package/labels.json +0 -52
  104. package/registry/agent-guardrails.md +0 -63
  105. package/registry/fraim.md +0 -48
  106. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase1-customer-profiling.md +0 -11
  107. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase1-survey-scoping.md +0 -11
  108. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase2-platform-discovery.md +0 -11
  109. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase2-survey-build-linkedin.md +0 -11
  110. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase3-prospect-qualification.md +0 -11
  111. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase3-survey-build-reddit.md +0 -11
  112. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase4-inventory-compilation.md +0 -11
  113. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase4-survey-build-x.md +0 -11
  114. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase5-survey-build-facebook.md +0 -11
  115. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase6-survey-build-custom.md +0 -11
  116. package/registry/stubs/workflows/customer-development/ai-coach-phases/phase7-survey-dispatch.md +0 -11
  117. package/registry/stubs/workflows/customer-development/templates/customer-persona-template.md +0 -11
  118. package/registry/stubs/workflows/customer-development/templates/search-strategy-template.md +0 -11
  119. package/setup.js +0 -171
  120. package/tsconfig.json +0 -23
@@ -0,0 +1,172 @@
1
+ /* Professional PDF Styling for Business Documents */
2
+
3
+ body {
4
+ font-family: 'Times New Roman', 'Georgia', serif;
5
+ font-size: 11pt;
6
+ line-height: 1.4;
7
+ color: #333;
8
+ margin: 0;
9
+ padding: 0;
10
+ }
11
+
12
+ /* Headers */
13
+ h1 {
14
+ font-size: 18pt;
15
+ font-weight: bold;
16
+ color: #2c3e50;
17
+ margin: 20pt 0 12pt 0;
18
+ border-bottom: 2px solid #3498db;
19
+ padding-bottom: 6pt;
20
+ }
21
+
22
+ h2 {
23
+ font-size: 14pt;
24
+ font-weight: bold;
25
+ color: #34495e;
26
+ margin: 16pt 0 8pt 0;
27
+ }
28
+
29
+ h3 {
30
+ font-size: 12pt;
31
+ font-weight: bold;
32
+ color: #34495e;
33
+ margin: 12pt 0 6pt 0;
34
+ }
35
+
36
+ /* Paragraphs */
37
+ p {
38
+ margin: 6pt 0;
39
+ text-align: justify;
40
+ }
41
+
42
+ /* Lists */
43
+ ul, ol {
44
+ margin: 6pt 0;
45
+ padding-left: 20pt;
46
+ }
47
+
48
+ li {
49
+ margin: 3pt 0;
50
+ }
51
+
52
+ /* Tables */
53
+ table {
54
+ border-collapse: collapse;
55
+ width: 100%;
56
+ margin: 12pt 0;
57
+ font-size: 10pt;
58
+ }
59
+
60
+ th, td {
61
+ border: 1px solid #ddd;
62
+ padding: 6pt 8pt;
63
+ text-align: left;
64
+ }
65
+
66
+ th {
67
+ background-color: #f8f9fa;
68
+ font-weight: bold;
69
+ }
70
+
71
+ /* Code blocks */
72
+ code {
73
+ font-family: 'Courier New', monospace;
74
+ font-size: 9pt;
75
+ background-color: #f8f9fa;
76
+ padding: 2pt 4pt;
77
+ border: 1px solid #e9ecef;
78
+ border-radius: 2pt;
79
+ }
80
+
81
+ pre {
82
+ font-family: 'Courier New', monospace;
83
+ font-size: 9pt;
84
+ background-color: #f8f9fa;
85
+ padding: 8pt;
86
+ border: 1px solid #e9ecef;
87
+ border-radius: 4pt;
88
+ overflow-x: auto;
89
+ margin: 8pt 0;
90
+ }
91
+
92
+ /* Blockquotes */
93
+ blockquote {
94
+ margin: 8pt 0;
95
+ padding: 8pt 12pt;
96
+ border-left: 4pt solid #3498db;
97
+ background-color: #f8f9fa;
98
+ font-style: italic;
99
+ }
100
+
101
+ /* Horizontal rules */
102
+ hr {
103
+ border: none;
104
+ border-top: 1px solid #bdc3c7;
105
+ margin: 16pt 0;
106
+ }
107
+
108
+ /* Strong and emphasis */
109
+ strong, b {
110
+ font-weight: bold;
111
+ }
112
+
113
+ em, i {
114
+ font-style: italic;
115
+ }
116
+
117
+ /* Page settings */
118
+ @page {
119
+ size: A4;
120
+ margin: 1in;
121
+
122
+ @top-center {
123
+ content: "";
124
+ font-size: 9pt;
125
+ color: #7f8c8d;
126
+ }
127
+
128
+ @bottom-center {
129
+ content: "Page " counter(page) " of " counter(pages);
130
+ font-size: 9pt;
131
+ color: #7f8c8d;
132
+ }
133
+ }
134
+
135
+ /* Print-specific adjustments */
136
+ @media print {
137
+ body {
138
+ font-size: 10pt;
139
+ }
140
+
141
+ h1 {
142
+ font-size: 16pt;
143
+ }
144
+
145
+ h2 {
146
+ font-size: 13pt;
147
+ }
148
+
149
+ h3 {
150
+ font-size: 11pt;
151
+ }
152
+ }
153
+
154
+ /* Signature sections */
155
+ .signature-section {
156
+ margin-top: 24pt;
157
+ page-break-inside: avoid;
158
+ }
159
+
160
+ /* Contact information */
161
+ .contact-info {
162
+ font-size: 10pt;
163
+ margin-top: 16pt;
164
+ }
165
+
166
+ /* Legal text formatting */
167
+ .legal-text {
168
+ font-size: 10pt;
169
+ text-transform: uppercase;
170
+ font-weight: bold;
171
+ margin: 12pt 0;
172
+ }
@@ -153,21 +153,63 @@ try {
153
153
  } else {
154
154
  // Extract defaultBranch from repository config
155
155
  defaultBranch = repo.defaultBranch || 'master';
156
+ // Ensure URL is present - construct it if missing
157
+ if (!repo.url && repo.owner && repo.name) {
158
+ repo.url = \`https://github.com/\${repo.owner}/\${repo.name}.git\`;
159
+ }
156
160
  }
157
161
 
158
- if (!repo || !repo.owner || !repo.name || !repo.url) {
162
+ if (!repo || !repo.owner || !repo.name) {
163
+ console.error('āŒ Config validation failed: Missing required repository fields');
164
+ console.error(' Required: repository.owner, repository.name');
165
+ console.error(' Found in config:', JSON.stringify(repo, null, 2));
159
166
  process.exit(1);
160
167
  }
168
+
169
+ if (!repo.url) {
170
+ console.error('āŒ Config validation failed: Missing repository.url field');
171
+ console.error(' This field is required for cloning the repository');
172
+ console.error(' Expected format: https://github.com/owner/repo.git');
173
+ console.error(' Current config:', JSON.stringify(repo, null, 2));
174
+ console.error('');
175
+ console.error('šŸ’” To fix this:');
176
+ console.error(' 1. Add \"url\": \"https://github.com/\${repo.owner}/\${repo.name}.git\" to your .fraim/config.json');
177
+ console.error(' 2. Or run: fraim init-project (if using FRAIM CLI)');
178
+ process.exit(1);
179
+ }
180
+
161
181
  console.log(\`\${repo.owner}|\${repo.name}|\${repo.url}|\${defaultBranch}\`);
162
182
  } catch (e) {
183
+ console.error('āŒ Failed to parse .fraim/config.json');
184
+ console.error(' Error:', e.message);
185
+ console.error('');
186
+ console.error('šŸ’” Common issues:');
187
+ console.error(' • Invalid JSON syntax');
188
+ console.error(' • Missing required fields: repository.owner, repository.name, repository.url');
189
+ console.error(' • File encoding issues');
190
+ console.error('');
191
+ console.error('šŸ“ Expected config format:');
192
+ console.error('{');
193
+ console.error(' \"repository\": {');
194
+ console.error(' \"provider\": \"github\",');
195
+ console.error(' \"owner\": \"your-username\",');
196
+ console.error(' \"name\": \"your-repo\",');
197
+ console.error(' \"url\": \"https://github.com/your-username/your-repo.git\",');
198
+ console.error(' \"defaultBranch\": \"main\"');
199
+ console.error(' }');
200
+ console.error('}');
163
201
  process.exit(1);
164
202
  }
165
203
  "
166
204
 
167
- REPO_INFO=$(cat "$CONFIG_FILE" | node -e "$NODE_SCRIPT")
205
+ REPO_INFO=$(cat "$CONFIG_FILE" | node -e "$NODE_SCRIPT" 2>&1)
168
206
  if [ $? -ne 0 ]; then
169
- echo "Error: Failed to parse repository config from $CONFIG_FILE" >&2
170
- echo "Required: repository.owner, repository.name, repository.url" >&2
207
+ echo "$REPO_INFO" >&2
208
+ echo "" >&2
209
+ echo "šŸ”§ To fix this issue:" >&2
210
+ echo " 1. Check your .fraim/config.json file" >&2
211
+ echo " 2. Ensure it has the required repository fields" >&2
212
+ echo " 3. Run 'fraim init-project' to regenerate the config" >&2
171
213
  exit 1
172
214
  fi
173
215
 
@@ -45,9 +45,9 @@ function getEnvOr(keys: string[], fallback: string): string {
45
45
  }
46
46
 
47
47
  interface ProfilingConfig {
48
- env: 'prod' | 'preprod' | 'local';
49
- appName: string;
50
- resourceGroup: string;
48
+ env: 'prod' | 'preprod' | 'local';
49
+ appName: string;
50
+ resourceGroup: string;
51
51
  }
52
52
 
53
53
  function checkAzureCLI(): boolean {
@@ -71,7 +71,7 @@ function checkAzureAuth(): boolean {
71
71
  async function getAppServicePlanMetrics(appName: string, resourceGroup: string): Promise<void> {
72
72
  try {
73
73
  console.log('\n--- ā˜ļø App Service Plan Metrics ---');
74
-
74
+
75
75
  // Get App Service Plan ID
76
76
  const planIdCmd = `az webapp show --name ${appName} --resource-group ${resourceGroup} --query "appServicePlanId" -o tsv`;
77
77
  const planId = execSync(planIdCmd, { encoding: 'utf8' }).trim();
@@ -81,10 +81,10 @@ async function getAppServicePlanMetrics(appName: string, resourceGroup: string):
81
81
  // Get CPU and Memory metrics for last 5 minutes
82
82
  console.log('šŸ“Š Fetching CPU and Memory metrics (last 5 minutes)...');
83
83
  const metricsCmd = `az monitor metrics list --resource "${planId}" --metrics CpuPercentage MemoryPercentage --interval PT1M --query "value[].{name:name.value, data:timeseries[0].data}" -o json`;
84
-
84
+
85
85
  const metricsOutput = execSync(metricsCmd, { encoding: 'utf8' });
86
86
  const metrics = JSON.parse(metricsOutput);
87
-
87
+
88
88
  for (const metric of metrics) {
89
89
  const latestData = metric.data[metric.data.length - 1];
90
90
  const value = latestData?.average || latestData?.total || 'N/A';
@@ -98,24 +98,25 @@ async function getAppServicePlanMetrics(appName: string, resourceGroup: string):
98
98
  async function getAppServiceMetrics(appName: string, resourceGroup: string): Promise<void> {
99
99
  try {
100
100
  console.log('\n--- šŸ–„ļø App Service Metrics ---');
101
-
101
+
102
102
  // Get subscription ID
103
103
  const subscriptionId = execSync('az account show --query id -o tsv', { encoding: 'utf8' }).trim();
104
-
104
+
105
105
  // Get app-level metrics using Azure CLI
106
106
  console.log('šŸ“Š Fetching App Service metrics (last 5 minutes)...');
107
- const appResourceId = `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Web/sites/${appName}`;
108
-
107
+ const root = '/';
108
+ const appResourceId = `${root}subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}/providers/Microsoft.Web/sites/${appName}`;
109
+
109
110
  const appMetricsCmd = `az monitor metrics list --resource "${appResourceId}" --metrics CpuTime Requests Http2xx Http4xx Http5xx --interval PT1M --query "value[].{name:name.value, data:timeseries[0].data}" -o json`;
110
-
111
+
111
112
  const appMetricsOutput = execSync(appMetricsCmd, { encoding: 'utf8' });
112
113
  const appMetrics = JSON.parse(appMetricsOutput);
113
-
114
+
114
115
  for (const metric of appMetrics) {
115
116
  const latestData = metric.data[metric.data.length - 1];
116
117
  const value = latestData?.total || latestData?.average || 'N/A';
117
118
  let displayValue = value;
118
-
119
+
119
120
  if (typeof value === 'number') {
120
121
  if (metric.name === 'CpuTime') {
121
122
  displayValue = `${value.toFixed(2)}s`;
@@ -123,7 +124,7 @@ async function getAppServiceMetrics(appName: string, resourceGroup: string): Pro
123
124
  displayValue = value.toString();
124
125
  }
125
126
  }
126
-
127
+
127
128
  console.log(` ${metric.name}: ${displayValue}`);
128
129
  }
129
130
  } catch (error: any) {
@@ -134,41 +135,41 @@ async function getAppServiceMetrics(appName: string, resourceGroup: string): Pro
134
135
  async function getProcessInformation(appName: string): Promise<void> {
135
136
  try {
136
137
  console.log('\n--- āš™ļø Process Information (Kudu API) ---');
137
-
138
+
138
139
  // Get access token for Kudu API
139
140
  const token = execSync('az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv', { encoding: 'utf8' }).trim();
140
-
141
+
141
142
  // Import axios dynamically
142
143
  const axios = (await import('axios')).default;
143
-
144
+
144
145
  // Get process list from Kudu
145
146
  console.log('šŸ“‹ Fetching running processes...');
146
147
  const processResponse = await axios.get(`https://${appName}.scm.azurewebsites.net/api/processes`, {
147
148
  headers: { Authorization: `Bearer ${token}` },
148
149
  timeout: 10000
149
150
  });
150
-
151
+
151
152
  const processes = processResponse.data;
152
-
153
+
153
154
  // Sort by CPU usage and show top processes
154
155
  const sortedProcesses = processes
155
156
  .filter((p: any) => p.cpu_usage !== undefined)
156
157
  .sort((a: any, b: any) => (b.cpu_usage || 0) - (a.cpu_usage || 0))
157
158
  .slice(0, 10);
158
-
159
+
159
160
  console.log('\nšŸ”„ Top 10 Processes by CPU Usage:');
160
161
  console.log('PID\tCPU%\tMemory(MB)\tName');
161
162
  console.log('---\t----\t---------\t----');
162
-
163
+
163
164
  for (const process of sortedProcesses) {
164
165
  const pid = process.id || 'N/A';
165
166
  const cpu = process.cpu_usage ? process.cpu_usage.toFixed(2) : '0.00';
166
167
  const memory = process.working_set ? (process.working_set / 1024 / 1024).toFixed(1) : 'N/A';
167
168
  const name = process.name || 'Unknown';
168
-
169
+
169
170
  console.log(`${pid}\t${cpu}\t${memory}\t\t${name.substring(0, 30)}`);
170
171
  }
171
-
172
+
172
173
  } catch (error: any) {
173
174
  console.log(`āš ļø Could not fetch process information: ${error.message.split('\n')[0]}`);
174
175
  console.log(' This might be due to authentication issues or Kudu API being unavailable.');
@@ -178,39 +179,39 @@ async function getProcessInformation(appName: string): Promise<void> {
178
179
  async function getSystemInformation(appName: string): Promise<void> {
179
180
  try {
180
181
  console.log('\n--- šŸ–„ļø System Information ---');
181
-
182
+
182
183
  const token = execSync('az account get-access-token --resource https://management.azure.com/ --query accessToken -o tsv', { encoding: 'utf8' }).trim();
183
184
  const axios = (await import('axios')).default;
184
-
185
+
185
186
  // Get system info using Kudu command API
186
187
  console.log('šŸ“Š Fetching system information...');
187
-
188
+
188
189
  const commands = [
189
190
  { name: 'Memory Info', cmd: 'cat /proc/meminfo | head -10' },
190
191
  { name: 'CPU Info', cmd: 'cat /proc/cpuinfo | grep "model name" | head -1' },
191
192
  { name: 'Disk Usage', cmd: 'df -h | head -5' },
192
193
  { name: 'Load Average', cmd: 'uptime' }
193
194
  ];
194
-
195
+
195
196
  for (const command of commands) {
196
197
  try {
197
198
  const response = await axios.post(`https://${appName}.scm.azurewebsites.net/api/command`, {
198
199
  command: command.cmd,
199
- dir: path.join('/', 'home')
200
+ dir: path.join(path.sep, 'home')
200
201
  }, {
201
202
  headers: { Authorization: `Bearer ${token}` },
202
203
  timeout: 5000
203
204
  });
204
-
205
+
205
206
  console.log(`\n${command.name}:`);
206
207
  const output = response.data.Output || response.data.Error || 'No output';
207
208
  console.log(output.split('\n').map((line: string) => ` ${line}`).join('\n'));
208
-
209
+
209
210
  } catch (cmdError: any) {
210
211
  console.log(`\n${command.name}: Could not retrieve (${cmdError.message.split('\n')[0]})`);
211
212
  }
212
213
  }
213
-
214
+
214
215
  } catch (error: any) {
215
216
  console.log(`āš ļø Could not fetch system information: ${error.message.split('\n')[0]}`);
216
217
  }
@@ -219,11 +220,11 @@ async function getSystemInformation(appName: string): Promise<void> {
219
220
  async function getApplicationLogs(appName: string, resourceGroup: string): Promise<void> {
220
221
  try {
221
222
  console.log('\n--- šŸ“‹ Recent Application Logs ---');
222
-
223
+
223
224
  // Get recent logs using Azure CLI
224
225
  console.log('šŸ“„ Fetching recent application logs...');
225
226
  const logsCmd = `az webapp log tail --name ${appName} --resource-group ${resourceGroup} --provider application --timeout 5`;
226
-
227
+
227
228
  try {
228
229
  const logs = execSync(logsCmd, { encoding: 'utf8', timeout: 10000 });
229
230
  console.log('Recent logs:');
@@ -232,7 +233,7 @@ async function getApplicationLogs(appName: string, resourceGroup: string): Promi
232
233
  console.log('āš ļø Could not fetch logs - they may not be enabled or available');
233
234
  console.log(' Enable application logging in Azure portal for better diagnostics');
234
235
  }
235
-
236
+
236
237
  } catch (error: any) {
237
238
  console.log(`āš ļø Could not fetch application logs: ${error.message.split('\n')[0]}`);
238
239
  }
@@ -240,11 +241,11 @@ async function getApplicationLogs(appName: string, resourceGroup: string): Promi
240
241
 
241
242
  async function profileLocalEnvironment(): Promise<void> {
242
243
  console.log('\n--- šŸ  Local Environment Profiling ---');
243
-
244
+
244
245
  try {
245
246
  // Basic system information
246
247
  console.log('šŸ’» System Information:');
247
-
248
+
248
249
  if (process.platform === 'win32') {
249
250
  try {
250
251
  const systemInfo = execSync('systeminfo | findstr /C:"Total Physical Memory" /C:"Available Physical Memory" /C:"Processor"', { encoding: 'utf8' });
@@ -264,7 +265,7 @@ async function profileLocalEnvironment(): Promise<void> {
264
265
  console.log(' Could not retrieve Linux system info');
265
266
  }
266
267
  }
267
-
268
+
268
269
  // Node.js process information
269
270
  console.log('\n🟢 Node.js Process Information:');
270
271
  console.log(` Node Version: ${process.version}`);
@@ -276,49 +277,49 @@ async function profileLocalEnvironment(): Promise<void> {
276
277
  console.log(` Heap Used: ${(memUsage.heapUsed / 1024 / 1024).toFixed(2)} MB`);
277
278
  console.log(` Heap Total: ${(memUsage.heapTotal / 1024 / 1024).toFixed(2)} MB`);
278
279
  console.log(` External: ${(memUsage.external / 1024 / 1024).toFixed(2)} MB`);
279
-
280
+
280
281
  } catch (error: any) {
281
282
  console.log(`āš ļø Error profiling local environment: ${error.message}`);
282
283
  }
283
284
  }
284
285
 
285
286
  async function main() {
286
- const config = loadClientConfig();
287
-
288
- // Azure configuration with environment variable fallbacks
289
- const azureConfig = {
290
- prodAppName: config.azure?.prodAppName || getEnvOr(['FRAIM_AZURE_PROD_APP_NAME'], 'fraim-app-prod'),
291
- prodResourceGroup: config.azure?.prodResourceGroup || getEnvOr(['FRAIM_AZURE_PROD_RESOURCE_GROUP'], 'fraim-prod-rg'),
292
- preprodAppName: config.azure?.preprodAppName || getEnvOr(['FRAIM_AZURE_PREPROD_APP_NAME'], 'fraim-app-pre-prod'),
293
- preprodResourceGroup: config.azure?.preprodResourceGroup || getEnvOr(['FRAIM_AZURE_PREPROD_RESOURCE_GROUP'], 'fraim-pre-prod-rg'),
294
- localAppName: config.azure?.localAppName || getEnvOr(['FRAIM_AZURE_LOCAL_APP_NAME'], 'local'),
295
- localResourceGroup: config.azure?.localResourceGroup || getEnvOr(['FRAIM_AZURE_LOCAL_RESOURCE_GROUP'], 'local')
296
- };
297
-
298
- const CONFIGS: Record<string, ProfilingConfig> = {
299
- prod: {
300
- env: 'prod',
301
- appName: azureConfig.prodAppName,
302
- resourceGroup: azureConfig.prodResourceGroup
303
- },
304
- preprod: {
305
- env: 'preprod',
306
- appName: azureConfig.preprodAppName,
307
- resourceGroup: azureConfig.preprodResourceGroup
308
- },
309
- local: {
310
- env: 'local',
311
- appName: azureConfig.localAppName,
312
- resourceGroup: azureConfig.localResourceGroup
313
- }
314
- };
287
+ const config = loadClientConfig();
288
+
289
+ // Azure configuration with environment variable fallbacks
290
+ const azureConfig = {
291
+ prodAppName: config.azure?.prodAppName || getEnvOr(['FRAIM_AZURE_PROD_APP_NAME'], 'fraim-app-prod'),
292
+ prodResourceGroup: config.azure?.prodResourceGroup || getEnvOr(['FRAIM_AZURE_PROD_RESOURCE_GROUP'], 'fraim-prod-rg'),
293
+ preprodAppName: config.azure?.preprodAppName || getEnvOr(['FRAIM_AZURE_PREPROD_APP_NAME'], 'fraim-app-pre-prod'),
294
+ preprodResourceGroup: config.azure?.preprodResourceGroup || getEnvOr(['FRAIM_AZURE_PREPROD_RESOURCE_GROUP'], 'fraim-pre-prod-rg'),
295
+ localAppName: config.azure?.localAppName || getEnvOr(['FRAIM_AZURE_LOCAL_APP_NAME'], 'local'),
296
+ localResourceGroup: config.azure?.localResourceGroup || getEnvOr(['FRAIM_AZURE_LOCAL_RESOURCE_GROUP'], 'local')
297
+ };
298
+
299
+ const CONFIGS: Record<string, ProfilingConfig> = {
300
+ prod: {
301
+ env: 'prod',
302
+ appName: azureConfig.prodAppName,
303
+ resourceGroup: azureConfig.prodResourceGroup
304
+ },
305
+ preprod: {
306
+ env: 'preprod',
307
+ appName: azureConfig.preprodAppName,
308
+ resourceGroup: azureConfig.preprodResourceGroup
309
+ },
310
+ local: {
311
+ env: 'local',
312
+ appName: azureConfig.localAppName,
313
+ resourceGroup: azureConfig.localResourceGroup
314
+ }
315
+ };
315
316
 
316
- const args = process.argv.slice(2);
317
- let profileConfig: ProfilingConfig = CONFIGS.local;
318
- const includeLogs = args.includes('--logs');
317
+ const args = process.argv.slice(2);
318
+ let profileConfig: ProfilingConfig = CONFIGS.local;
319
+ const includeLogs = args.includes('--logs');
319
320
 
320
- if (args.includes('--help') || args.includes('-h')) {
321
- console.log(`
321
+ if (args.includes('--help') || args.includes('-h')) {
322
+ console.log(`
322
323
  šŸ” Azure App Service Profiler
323
324
 
324
325
  Usage:
@@ -353,72 +354,72 @@ Configuration:
353
354
  - FRAIM_AZURE_PREPROD_APP_NAME
354
355
  - FRAIM_AZURE_PREPROD_RESOURCE_GROUP
355
356
  `);
356
- return;
357
- }
357
+ return;
358
+ }
358
359
 
359
- if (args.includes('--prod')) profileConfig = CONFIGS.prod;
360
- else if (args.includes('--preprod')) profileConfig = CONFIGS.preprod;
361
- else if (args.includes('--local')) profileConfig = CONFIGS.local;
360
+ if (args.includes('--prod')) profileConfig = CONFIGS.prod;
361
+ else if (args.includes('--preprod')) profileConfig = CONFIGS.preprod;
362
+ else if (args.includes('--local')) profileConfig = CONFIGS.local;
362
363
 
363
- console.log(`\nšŸš€ Starting Server Profiling`);
364
- console.log(`šŸ“Š Environment: ${profileConfig.env.toUpperCase()}`);
365
- console.log(`šŸ·ļø App: ${profileConfig.appName}`);
366
- console.log(`šŸ“ Resource Group: ${profileConfig.resourceGroup}`);
364
+ console.log(`\nšŸš€ Starting Server Profiling`);
365
+ console.log(`šŸ“Š Environment: ${profileConfig.env.toUpperCase()}`);
366
+ console.log(`šŸ·ļø App: ${profileConfig.appName}`);
367
+ console.log(`šŸ“ Resource Group: ${profileConfig.resourceGroup}`);
367
368
 
368
- if (profileConfig.env === 'local') {
369
- await profileLocalEnvironment();
370
- console.log('\nāœ… Local profiling completed');
371
- return;
372
- }
369
+ if (profileConfig.env === 'local') {
370
+ await profileLocalEnvironment();
371
+ console.log('\nāœ… Local profiling completed');
372
+ return;
373
+ }
373
374
 
374
- // Check prerequisites for Azure profiling
375
- console.log('\nšŸ” Checking prerequisites...');
376
-
377
- if (!checkAzureCLI()) {
378
- console.error('āŒ Azure CLI not found. Please install Azure CLI first.');
379
- console.error(' Download from: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli');
380
- process.exit(1);
381
- }
382
- console.log('āœ… Azure CLI found');
375
+ // Check prerequisites for Azure profiling
376
+ console.log('\nšŸ” Checking prerequisites...');
383
377
 
384
- if (!checkAzureAuth()) {
385
- console.error('āŒ Not authenticated with Azure. Please run: az login');
386
- process.exit(1);
387
- }
388
- console.log('āœ… Azure authentication verified');
378
+ if (!checkAzureCLI()) {
379
+ console.error('āŒ Azure CLI not found. Please install Azure CLI first.');
380
+ console.error(' Download from: https://docs.microsoft.com/en-us/cli/azure/install-azure-cli');
381
+ process.exit(1);
382
+ }
383
+ console.log('āœ… Azure CLI found');
389
384
 
390
- try {
391
- // Verify app exists
392
- console.log(`\nšŸ” Verifying App Service: ${profileConfig.appName}`);
393
- execSync(`az webapp show --name ${profileConfig.appName} --resource-group ${profileConfig.resourceGroup} --query name -o tsv`, { stdio: 'pipe' });
394
- console.log('āœ… App Service found');
395
-
396
- // Run profiling analysis
397
- await getAppServicePlanMetrics(profileConfig.appName, profileConfig.resourceGroup);
398
- await getAppServiceMetrics(profileConfig.appName, profileConfig.resourceGroup);
399
- await getProcessInformation(profileConfig.appName);
400
- await getSystemInformation(profileConfig.appName);
401
-
402
- if (includeLogs) {
403
- await getApplicationLogs(profileConfig.appName, profileConfig.resourceGroup);
404
- }
385
+ if (!checkAzureAuth()) {
386
+ console.error('āŒ Not authenticated with Azure. Please run: az login');
387
+ process.exit(1);
388
+ }
389
+ console.log('āœ… Azure authentication verified');
405
390
 
406
- console.log('\nāœ… Server profiling completed successfully');
407
- console.log('\nšŸ’” Tips:');
408
- console.log(' - Use --logs flag to include application logs');
409
- console.log(' - High CPU usage may indicate performance bottlenecks');
410
- console.log(' - High memory usage may indicate memory leaks');
411
- console.log(' - Check process list for unexpected or resource-heavy processes');
412
-
413
- } catch (error: any) {
414
- console.error(`\nāŒ Profiling failed: ${error.message}`);
415
- console.error('\nTroubleshooting:');
416
- console.error(' - Verify app name and resource group are correct');
417
- console.error(' - Ensure you have proper permissions (Contributor role)');
418
- console.error(' - Check if the App Service is running');
419
- console.error(' - Try: az webapp list --query "[].{name:name, resourceGroup:resourceGroup}"');
420
- process.exit(1);
391
+ try {
392
+ // Verify app exists
393
+ console.log(`\nšŸ” Verifying App Service: ${profileConfig.appName}`);
394
+ execSync(`az webapp show --name ${profileConfig.appName} --resource-group ${profileConfig.resourceGroup} --query name -o tsv`, { stdio: 'pipe' });
395
+ console.log('āœ… App Service found');
396
+
397
+ // Run profiling analysis
398
+ await getAppServicePlanMetrics(profileConfig.appName, profileConfig.resourceGroup);
399
+ await getAppServiceMetrics(profileConfig.appName, profileConfig.resourceGroup);
400
+ await getProcessInformation(profileConfig.appName);
401
+ await getSystemInformation(profileConfig.appName);
402
+
403
+ if (includeLogs) {
404
+ await getApplicationLogs(profileConfig.appName, profileConfig.resourceGroup);
421
405
  }
406
+
407
+ console.log('\nāœ… Server profiling completed successfully');
408
+ console.log('\nšŸ’” Tips:');
409
+ console.log(' - Use --logs flag to include application logs');
410
+ console.log(' - High CPU usage may indicate performance bottlenecks');
411
+ console.log(' - High memory usage may indicate memory leaks');
412
+ console.log(' - Check process list for unexpected or resource-heavy processes');
413
+
414
+ } catch (error: any) {
415
+ console.error(`\nāŒ Profiling failed: ${error.message}`);
416
+ console.error('\nTroubleshooting:');
417
+ console.error(' - Verify app name and resource group are correct');
418
+ console.error(' - Ensure you have proper permissions (Contributor role)');
419
+ console.error(' - Check if the App Service is running');
420
+ console.error(' - Try: az webapp list --query "[].{name:name, resourceGroup:resourceGroup}"');
421
+ process.exit(1);
422
+ }
422
423
  }
423
424
 
424
425
  // Run the main function