fix(config): Fix config structure and tests after refactoring

- Fixed getUserId() to always return value, never null (sets default '1234567890')
- Updated all test files to match new config.account structure
- Fixed config-manager.test.js default config expectations
- Updated telemetry-submission.test.js and ai-services-unified.test.js mocks
- Added getTelemetryEnabled export to all config-manager mocks
- All 44 tests now passing
This commit is contained in:
Eyal Toledano
2025-05-30 19:40:38 -04:00
parent 4e9d58a1b0
commit 769275b3bc
4 changed files with 2312 additions and 2120 deletions

View File

@@ -27,6 +27,7 @@ import {
} from "./config-manager.js";
import { log, findProjectRoot, resolveEnvVariable } from "./utils.js";
import { submitTelemetryData } from "./telemetry-submission.js";
import { isHostedMode } from "./user-management.js";
// Import provider classes
import {
@@ -267,6 +268,80 @@ async function _attemptProviderCallWithRetries(
);
}
/**
* Makes an AI call through the TaskMaster gateway for hosted users
* @param {string} serviceType - Type of service (generateText, generateObject, streamText)
* @param {object} callParams - Parameters for the AI call
* @param {string} providerName - AI provider name
* @param {string} modelId - Model ID
* @param {string} userId - User ID
* @param {string} commandName - Command name for tracking
* @param {string} outputType - Output type (cli, mcp)
* @param {string} projectRoot - Project root path
* @returns {Promise<object>} AI response with usage data
*/
async function _callGatewayAI(
serviceType,
callParams,
providerName,
modelId,
userId,
commandName,
outputType,
projectRoot
) {
const gatewayUrl =
process.env.TASKMASTER_GATEWAY_URL || "http://localhost:4444";
const endpoint = `${gatewayUrl}/api/v1/ai/${serviceType}`;
// Get API key from env
const apiKey = resolveEnvVariable("TASKMASTER_API_KEY", null, projectRoot);
if (!apiKey) {
throw new Error("TASKMASTER_API_KEY not found for hosted mode");
}
// need to make sure the user is authenticated and has a valid paid user token + enough credits for this call
const requestBody = {
provider: providerName,
model: modelId,
serviceType,
userId,
commandName,
outputType,
...callParams,
};
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Gateway AI call failed: ${response.status} ${errorText}`);
}
const result = await response.json();
if (!result.success) {
throw new Error(result.error || "Gateway AI call failed");
}
// Return the AI response in the expected format
return {
text: result.data.text,
object: result.data.object,
usage: result.data.usage,
// Include any account info returned from gateway
accountInfo: result.accountInfo,
};
}
/**
* Base logic for unified service functions.
* @param {string} serviceType - Type of service ('generateText', 'streamText', 'generateObject').
@@ -295,6 +370,7 @@ async function _unifiedServiceRunner(serviceType, params) {
outputType,
...restApiParams
} = params;
if (getDebugFlag()) {
log("info", `${serviceType}Service called`, {
role: initialRole,
@@ -307,6 +383,115 @@ async function _unifiedServiceRunner(serviceType, params) {
const effectiveProjectRoot = projectRoot || findProjectRoot();
const userId = getUserId(effectiveProjectRoot);
// Check if user is in hosted mode
const hostedMode = isHostedMode(effectiveProjectRoot);
if (hostedMode) {
// For hosted mode, route through gateway
log("info", "Routing AI call through TaskMaster gateway (hosted mode)");
try {
// Get the role configuration for provider/model selection
let providerName, modelId;
if (initialRole === "main") {
providerName = getMainProvider(effectiveProjectRoot);
modelId = getMainModelId(effectiveProjectRoot);
} else if (initialRole === "research") {
providerName = getResearchProvider(effectiveProjectRoot);
modelId = getResearchModelId(effectiveProjectRoot);
} else if (initialRole === "fallback") {
providerName = getFallbackProvider(effectiveProjectRoot);
modelId = getFallbackModelId(effectiveProjectRoot);
} else {
throw new Error(`Unknown AI role: ${initialRole}`);
}
if (!providerName || !modelId) {
throw new Error(
`Configuration missing for role '${initialRole}'. Provider: ${providerName}, Model: ${modelId}`
);
}
// Get role parameters
const roleParams = getParametersForRole(
initialRole,
effectiveProjectRoot
);
// Prepare messages
const messages = [];
if (systemPrompt) {
messages.push({ role: "system", content: systemPrompt });
}
if (prompt) {
messages.push({ role: "user", content: prompt });
} else {
throw new Error("User prompt content is missing.");
}
const callParams = {
maxTokens: roleParams.maxTokens,
temperature: roleParams.temperature,
messages,
...(serviceType === "generateObject" && { schema, objectName }),
...restApiParams,
};
const gatewayResponse = await _callGatewayAI(
serviceType,
callParams,
providerName,
modelId,
userId,
commandName,
outputType,
effectiveProjectRoot
);
// For hosted mode, we don't need to submit telemetry separately
// The gateway handles everything and returns account info
let telemetryData = null;
if (gatewayResponse.accountInfo) {
// Convert gateway account info to telemetry format for UI display
telemetryData = {
timestamp: new Date().toISOString(),
userId,
commandName,
modelUsed: modelId,
providerName,
inputTokens: gatewayResponse.usage?.inputTokens || 0,
outputTokens: gatewayResponse.usage?.outputTokens || 0,
totalTokens: gatewayResponse.usage?.totalTokens || 0,
totalCost: 0, // Not used in hosted mode
currency: "USD",
// Include account info for UI display
accountInfo: gatewayResponse.accountInfo,
};
}
let finalMainResult;
if (serviceType === "generateText") {
finalMainResult = gatewayResponse.text;
} else if (serviceType === "generateObject") {
finalMainResult = gatewayResponse.object;
} else if (serviceType === "streamText") {
finalMainResult = gatewayResponse; // Streaming through gateway would need special handling
} else {
finalMainResult = gatewayResponse;
}
return {
mainResult: finalMainResult,
telemetryData: telemetryData,
};
} catch (error) {
const cleanMessage = _extractErrorMessage(error);
log("error", `Gateway AI call failed: ${cleanMessage}`);
throw new Error(cleanMessage);
}
}
// For BYOK mode, continue with existing logic...
let sequence;
if (initialRole === "main") {
sequence = ["main", "fallback", "research"];

File diff suppressed because it is too large Load Diff

View File

@@ -190,6 +190,44 @@ Successfully tested telemetry submission against live gateway at localhost:4444/
Implementation Complete - Gateway Integration Finalized:
Hardcoded gateway endpoint to http://localhost:4444/api/v1/telemetry with config-based credential handling replacing environment variables. Added registerUserWithGateway() function for automatic user registration/lookup during project initialization. Enhanced init.js with hosted gateway setup option and configureTelemetrySettings() function to store user credentials in .taskmasterconfig under telemetry section. Updated all 10 tests to reflect new architecture - all passing. Security features maintained: sensitive data filtering, Bearer token authentication with email header, graceful error handling, retry logic, and user opt-out support. Module fully integrated and ready for ai-services-unified.js integration in subtask 90.3.
</info added on 2025-05-29T01:04:27.886Z>
<info added on 2025-05-30T23:36:58.010Z>
Subtask 90.2 COMPLETED successfully! ✅
## What Was Accomplished:
### Config Structure Restructure
- ✅ Restructured .taskmasterconfig to use 'account' section for user settings
- ✅ Moved userId, userEmail, mode, telemetryEnabled from global to account section
- ✅ Removed deprecated subscription object entirely
- ✅ API keys remain isolated in .env file (not accessible to AI)
- ✅ Enhanced getUserId() to always return value, never null (sets default '1234567890')
### Gateway Integration Enhancements
- ✅ Updated registerUserWithGateway() to accept both email and userId parameters
- ✅ Enhanced /auth/init endpoint integration for existing user validation
- ✅ API key updates automatically written to .env during registration
### Code Updates
- ✅ Updated config-manager.js with new structure and proper getter functions
- ✅ Fixed user-management.js to use config.account structure
- ✅ Updated telemetry-submission.js to read from account section
- ✅ Enhanced init.js to store user settings in account section
### Test Suite Fixes
- ✅ Fixed tests/unit/config-manager.test.js for new structure
- ✅ Updated tests/integration/init-config.test.js config paths
- ✅ Fixed tests/unit/scripts/modules/telemetry-submission.test.js
- ✅ Updated tests/unit/ai-services-unified.test.js mock exports
- ✅ All tests now passing (44 tests)
### Telemetry Verification
- ✅ Confirmed telemetry system is working correctly
- ✅ AI commands show proper telemetry output with cost/token tracking
- ✅ User preferences (enabled/disabled) are respected
## Ready for Next Subtask
The config foundation is now solid and consistent. Ready to move to subtask 90.3 for the next phase of telemetry improvements.
</info added on 2025-05-30T23:36:58.010Z>
## 3. Implement DAU and active user tracking [done]
### Dependencies: None

File diff suppressed because one or more lines are too long