edsger 0.31.1 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/app-store.d.ts +76 -0
- package/dist/api/app-store.js +148 -0
- package/dist/api/github.js +16 -23
- package/dist/api/mcp-client.js +5 -10
- package/dist/auth/login.js +18 -18
- package/dist/commands/agent-workflow/index.js +6 -6
- package/dist/commands/agent-workflow/processor.d.ts +1 -1
- package/dist/commands/agent-workflow/processor.js +34 -6
- package/dist/commands/analyze-logs/index.d.ts +7 -0
- package/dist/commands/analyze-logs/index.js +34 -0
- package/dist/commands/app-store/index.d.ts +12 -0
- package/dist/commands/app-store/index.js +60 -0
- package/dist/commands/code-review/reviewer.js +14 -13
- package/dist/commands/growth-analysis/index.js +14 -1
- package/dist/commands/init/index.js +4 -4
- package/dist/commands/refactor/refactor.js +4 -4
- package/dist/commands/workflow/core/__tests__/feature-filter.test.d.ts +5 -0
- package/dist/commands/workflow/core/__tests__/feature-filter.test.js +325 -0
- package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.d.ts +4 -0
- package/dist/commands/workflow/core/__tests__/pipeline-evaluator.test.js +406 -0
- package/dist/commands/workflow/core/__tests__/state-manager.test.d.ts +4 -0
- package/dist/commands/workflow/core/__tests__/state-manager.test.js +384 -0
- package/dist/commands/workflow/core/workflow-logger.js +6 -6
- package/dist/commands/workflow/executors/phase-executor.js +19 -18
- package/dist/config/__tests__/config.test.d.ts +4 -0
- package/dist/config/__tests__/config.test.js +286 -0
- package/dist/config.js +12 -0
- package/dist/errors/__tests__/index.test.d.ts +4 -0
- package/dist/errors/__tests__/index.test.js +349 -0
- package/dist/errors/index.d.ts +62 -0
- package/dist/errors/index.js +116 -0
- package/dist/index.js +48 -0
- package/dist/phases/analyze-logs/agent.d.ts +11 -0
- package/dist/phases/analyze-logs/agent.js +63 -0
- package/dist/phases/analyze-logs/index.d.ts +23 -0
- package/dist/phases/analyze-logs/index.js +131 -0
- package/dist/phases/analyze-logs/prompts.d.ts +3 -0
- package/dist/phases/analyze-logs/prompts.js +59 -0
- package/dist/phases/app-store-generation/__tests__/agent.test.d.ts +5 -0
- package/dist/phases/app-store-generation/__tests__/agent.test.js +144 -0
- package/dist/phases/app-store-generation/__tests__/context.test.d.ts +4 -0
- package/dist/phases/app-store-generation/__tests__/context.test.js +280 -0
- package/dist/phases/app-store-generation/__tests__/prompts.test.d.ts +4 -0
- package/dist/phases/app-store-generation/__tests__/prompts.test.js +165 -0
- package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.d.ts +5 -0
- package/dist/phases/app-store-generation/__tests__/screenshot-composer.test.js +407 -0
- package/dist/phases/app-store-generation/agent.d.ts +2 -0
- package/dist/phases/app-store-generation/agent.js +97 -0
- package/dist/phases/app-store-generation/context.d.ts +21 -0
- package/dist/phases/app-store-generation/context.js +77 -0
- package/dist/phases/app-store-generation/index.d.ts +19 -0
- package/dist/phases/app-store-generation/index.js +119 -0
- package/dist/phases/app-store-generation/prompts.d.ts +43 -0
- package/dist/phases/app-store-generation/prompts.js +134 -0
- package/dist/phases/app-store-generation/screenshot-composer.d.ts +42 -0
- package/dist/phases/app-store-generation/screenshot-composer.js +319 -0
- package/dist/phases/app-store-generation/uploader.d.ts +18 -0
- package/dist/phases/app-store-generation/uploader.js +119 -0
- package/dist/phases/autonomous/index.js +3 -7
- package/dist/phases/branch-planning/index.js +3 -7
- package/dist/phases/bug-fixing/analyzer.js +3 -7
- package/dist/phases/bug-fixing/mcp-server.js +2 -1
- package/dist/phases/chat-processor/index.js +5 -13
- package/dist/phases/code-implementation/index.d.ts +2 -2
- package/dist/phases/code-implementation/index.js +63 -86
- package/dist/phases/code-implementation-verification/agent.js +3 -7
- package/dist/phases/code-refine/context.js +19 -52
- package/dist/phases/code-refine/index.js +3 -7
- package/dist/phases/code-refine/retry-handler.js +9 -15
- package/dist/phases/code-review/context.js +16 -45
- package/dist/phases/code-review/index.js +2 -4
- package/dist/phases/code-testing/analyzer.js +7 -11
- package/dist/phases/feature-analysis/agent.js +3 -7
- package/dist/phases/feature-analysis-verification/agent.js +2 -4
- package/dist/phases/functional-testing/analyzer.js +3 -7
- package/dist/phases/functional-testing/mcp-server.js +3 -2
- package/dist/phases/functional-testing/test-retry-handler.js +8 -15
- package/dist/phases/growth-analysis/agent.js +3 -7
- package/dist/phases/growth-analysis/index.d.ts +6 -0
- package/dist/phases/growth-analysis/index.js +142 -2
- package/dist/phases/growth-analysis/prompts.js +70 -3
- package/dist/phases/pr-execution/index.js +3 -7
- package/dist/phases/pr-splitting/index.js +3 -7
- package/dist/phases/pull-request/creator.js +23 -66
- package/dist/phases/pull-request/handler.js +13 -32
- package/dist/phases/task/agent.js +3 -7
- package/dist/phases/technical-design/index.d.ts +4 -4
- package/dist/phases/technical-design/index.js +3 -7
- package/dist/phases/technical-design-verification/agent.js +2 -4
- package/dist/phases/test-cases-analysis/agent.js +3 -7
- package/dist/phases/user-stories-analysis/agent.js +3 -7
- package/dist/services/branches.js +12 -11
- package/dist/services/checklist.js +18 -17
- package/dist/services/feedbacks.js +11 -10
- package/dist/services/product-logs.d.ts +31 -0
- package/dist/services/product-logs.js +33 -0
- package/dist/services/pull-requests.js +10 -9
- package/dist/services/video/__tests__/video-pipeline.test.d.ts +6 -0
- package/dist/services/video/__tests__/video-pipeline.test.js +231 -0
- package/dist/services/video/device-frames.d.ts +30 -0
- package/dist/services/video/device-frames.js +422 -0
- package/dist/services/video/index.d.ts +66 -0
- package/dist/services/video/index.js +226 -0
- package/dist/services/video/retry.d.ts +20 -0
- package/dist/services/video/retry.js +73 -0
- package/dist/services/video/screenshot-generator.d.ts +67 -0
- package/dist/services/video/screenshot-generator.js +254 -0
- package/dist/services/video/tts-generator.d.ts +40 -0
- package/dist/services/video/tts-generator.js +121 -0
- package/dist/services/video/video-assembler.d.ts +45 -0
- package/dist/services/video/video-assembler.js +308 -0
- package/dist/services/video/video-uploader.d.ts +28 -0
- package/dist/services/video/video-uploader.js +105 -0
- package/dist/types/features.d.ts +2 -2
- package/dist/types/index.d.ts +48 -28
- package/dist/types/llm-responses.d.ts +127 -0
- package/dist/types/llm-responses.js +9 -0
- package/dist/types/pipeline.d.ts +1 -1
- package/dist/utils/logger.d.ts +4 -0
- package/dist/utils/logger.js +11 -0
- package/dist/utils/pipeline-logger.js +11 -8
- package/package.json +1 -1
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export interface AppStoreConfig {
|
|
2
|
+
id: string;
|
|
3
|
+
product_id: string;
|
|
4
|
+
store_type: 'apple_app_store' | 'google_play';
|
|
5
|
+
credentials: Record<string, unknown>;
|
|
6
|
+
app_identifier: string | null;
|
|
7
|
+
listings: Record<string, AppStoreListing>;
|
|
8
|
+
screenshots: AppStoreScreenshot[];
|
|
9
|
+
current_version: string | null;
|
|
10
|
+
submission_status: string;
|
|
11
|
+
submitted_at: string | null;
|
|
12
|
+
released_at: string | null;
|
|
13
|
+
rejection_reason: string | null;
|
|
14
|
+
is_active: boolean;
|
|
15
|
+
created_by: string;
|
|
16
|
+
created_at: string;
|
|
17
|
+
updated_at: string;
|
|
18
|
+
}
|
|
19
|
+
export interface AppStoreListing {
|
|
20
|
+
app_name: string;
|
|
21
|
+
subtitle?: string;
|
|
22
|
+
short_description?: string;
|
|
23
|
+
description: string;
|
|
24
|
+
keywords?: string;
|
|
25
|
+
whats_new?: string;
|
|
26
|
+
privacy_policy_url?: string;
|
|
27
|
+
support_url?: string;
|
|
28
|
+
marketing_url?: string;
|
|
29
|
+
primary_category?: string;
|
|
30
|
+
secondary_category?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface AppStoreScreenshot {
|
|
33
|
+
device_type: string;
|
|
34
|
+
display_order: number;
|
|
35
|
+
storage_url: string;
|
|
36
|
+
storage_path: string;
|
|
37
|
+
width: number;
|
|
38
|
+
height: number;
|
|
39
|
+
caption: string;
|
|
40
|
+
background_gradient: string;
|
|
41
|
+
device_frame: string;
|
|
42
|
+
locale: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get app store configs for a product via MCP
|
|
46
|
+
*/
|
|
47
|
+
export declare function getAppStoreConfigs(productId: string, verbose?: boolean): Promise<AppStoreConfig[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Get a single app store config via MCP
|
|
50
|
+
*/
|
|
51
|
+
export declare function getAppStoreConfig(configId: string, verbose?: boolean): Promise<AppStoreConfig | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Create or update an app store config via MCP
|
|
54
|
+
*/
|
|
55
|
+
export declare function upsertAppStoreConfig(params: {
|
|
56
|
+
product_id: string;
|
|
57
|
+
store_type: string;
|
|
58
|
+
credentials?: Record<string, unknown>;
|
|
59
|
+
app_identifier?: string;
|
|
60
|
+
}, verbose?: boolean): Promise<AppStoreConfig | null>;
|
|
61
|
+
/**
|
|
62
|
+
* Save listings to an app store config via MCP
|
|
63
|
+
*/
|
|
64
|
+
export declare function saveAppStoreListings(configId: string, listings: Record<string, AppStoreListing>, verbose?: boolean): Promise<AppStoreConfig | null>;
|
|
65
|
+
/**
|
|
66
|
+
* Save screenshots to an app store config via MCP
|
|
67
|
+
*/
|
|
68
|
+
export declare function saveAppStoreScreenshots(configId: string, screenshots: AppStoreScreenshot[], verbose?: boolean): Promise<AppStoreConfig | null>;
|
|
69
|
+
/**
|
|
70
|
+
* Update submission status via MCP
|
|
71
|
+
*/
|
|
72
|
+
export declare function updateAppStoreStatus(configId: string, updates: {
|
|
73
|
+
submission_status?: string;
|
|
74
|
+
current_version?: string;
|
|
75
|
+
rejection_reason?: string;
|
|
76
|
+
}, verbose?: boolean): Promise<AppStoreConfig | null>;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { logInfo, logError } from '../utils/logger.js';
|
|
2
|
+
import { callMcpEndpoint } from './mcp-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Get app store configs for a product via MCP
|
|
5
|
+
*/
|
|
6
|
+
export async function getAppStoreConfigs(productId, verbose) {
|
|
7
|
+
if (verbose) {
|
|
8
|
+
logInfo(`Fetching app store configs for product: ${productId}`);
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const result = (await callMcpEndpoint('app_store/configs/list', {
|
|
12
|
+
product_id: productId,
|
|
13
|
+
}));
|
|
14
|
+
const text = result.content?.[0]?.text || '[]';
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(text);
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (verbose) {
|
|
24
|
+
logError(`Failed to fetch app store configs: ${error instanceof Error ? error.message : String(error)}`);
|
|
25
|
+
}
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Get a single app store config via MCP
|
|
31
|
+
*/
|
|
32
|
+
export async function getAppStoreConfig(configId, verbose) {
|
|
33
|
+
try {
|
|
34
|
+
const result = (await callMcpEndpoint('app_store/configs/get', {
|
|
35
|
+
config_id: configId,
|
|
36
|
+
}));
|
|
37
|
+
const text = result.content?.[0]?.text || 'null';
|
|
38
|
+
try {
|
|
39
|
+
return JSON.parse(text);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
if (verbose) {
|
|
47
|
+
logError(`Failed to fetch app store config: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create or update an app store config via MCP
|
|
54
|
+
*/
|
|
55
|
+
export async function upsertAppStoreConfig(params, verbose) {
|
|
56
|
+
if (verbose) {
|
|
57
|
+
logInfo(`Upserting app store config for ${params.store_type}`);
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const result = (await callMcpEndpoint('app_store/configs/upsert', params));
|
|
61
|
+
const text = result.content?.[0]?.text || 'null';
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(text);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
logError(`Failed to upsert app store config: ${error instanceof Error ? error.message : String(error)}`);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Save listings to an app store config via MCP
|
|
76
|
+
*/
|
|
77
|
+
export async function saveAppStoreListings(configId, listings, verbose) {
|
|
78
|
+
if (verbose) {
|
|
79
|
+
logInfo(`Saving listings for config: ${configId}`);
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const result = (await callMcpEndpoint('app_store/configs/save_listings', {
|
|
83
|
+
config_id: configId,
|
|
84
|
+
listings,
|
|
85
|
+
}));
|
|
86
|
+
const text = result.content?.[0]?.text || 'null';
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(text);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
logError(`Failed to save listings: ${error instanceof Error ? error.message : String(error)}`);
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Save screenshots to an app store config via MCP
|
|
101
|
+
*/
|
|
102
|
+
export async function saveAppStoreScreenshots(configId, screenshots, verbose) {
|
|
103
|
+
if (verbose) {
|
|
104
|
+
logInfo(`Saving ${screenshots.length} screenshots for config: ${configId}`);
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const result = (await callMcpEndpoint('app_store/configs/save_screenshots', {
|
|
108
|
+
config_id: configId,
|
|
109
|
+
screenshots,
|
|
110
|
+
}));
|
|
111
|
+
const text = result.content?.[0]?.text || 'null';
|
|
112
|
+
try {
|
|
113
|
+
return JSON.parse(text);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
logError(`Failed to save screenshots: ${error instanceof Error ? error.message : String(error)}`);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Update submission status via MCP
|
|
126
|
+
*/
|
|
127
|
+
export async function updateAppStoreStatus(configId, updates, verbose) {
|
|
128
|
+
if (verbose) {
|
|
129
|
+
logInfo(`Updating status for config: ${configId}`);
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const result = (await callMcpEndpoint('app_store/configs/update_status', {
|
|
133
|
+
config_id: configId,
|
|
134
|
+
...updates,
|
|
135
|
+
}));
|
|
136
|
+
const text = result.content?.[0]?.text || 'null';
|
|
137
|
+
try {
|
|
138
|
+
return JSON.parse(text);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
logError(`Failed to update status: ${error instanceof Error ? error.message : String(error)}`);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
}
|
package/dist/api/github.js
CHANGED
|
@@ -3,31 +3,30 @@
|
|
|
3
3
|
* Uses product developer configuration from the database
|
|
4
4
|
*/
|
|
5
5
|
import { callMcpEndpoint } from './mcp-client.js';
|
|
6
|
+
import { logDebug, logSuccess, logWarning, logError } from '../utils/logger.js';
|
|
6
7
|
/**
|
|
7
8
|
* Get GitHub developer configuration for a feature
|
|
8
9
|
* Returns installation_id, repository info from product_developers
|
|
9
10
|
*/
|
|
10
11
|
export async function getGitHubDeveloperConfig(featureId, verbose) {
|
|
11
|
-
|
|
12
|
-
console.log(`🔍 Fetching GitHub developer config for feature: ${featureId}`);
|
|
13
|
-
}
|
|
12
|
+
logDebug(`Fetching GitHub developer config for feature: ${featureId}`, verbose);
|
|
14
13
|
try {
|
|
15
14
|
const result = (await callMcpEndpoint('github/developer_config', {
|
|
16
15
|
feature_id: featureId,
|
|
17
16
|
}));
|
|
18
17
|
if (verbose) {
|
|
19
18
|
if (result.configured) {
|
|
20
|
-
|
|
19
|
+
logSuccess(`GitHub configured: ${result.repository_full_name}`);
|
|
21
20
|
}
|
|
22
21
|
else {
|
|
23
|
-
|
|
22
|
+
logWarning(`GitHub not configured: ${result.message}`);
|
|
24
23
|
}
|
|
25
24
|
}
|
|
26
25
|
return result;
|
|
27
26
|
}
|
|
28
27
|
catch (error) {
|
|
29
28
|
if (verbose) {
|
|
30
|
-
|
|
29
|
+
logError(`Failed to get GitHub developer config: ${error instanceof Error ? error.message : String(error)}`);
|
|
31
30
|
}
|
|
32
31
|
return {
|
|
33
32
|
configured: false,
|
|
@@ -39,22 +38,20 @@ export async function getGitHubDeveloperConfig(featureId, verbose) {
|
|
|
39
38
|
* Get installation access token for GitHub API operations
|
|
40
39
|
*/
|
|
41
40
|
export async function getGitHubInstallationToken(installationId, featureId, verbose) {
|
|
42
|
-
|
|
43
|
-
console.log(`🔑 Fetching GitHub installation token for: ${installationId}`);
|
|
44
|
-
}
|
|
41
|
+
logDebug(`Fetching GitHub installation token for: ${installationId}`, verbose);
|
|
45
42
|
try {
|
|
46
43
|
const result = (await callMcpEndpoint('github/installation_token', {
|
|
47
44
|
installation_id: installationId,
|
|
48
45
|
feature_id: featureId,
|
|
49
46
|
}));
|
|
50
47
|
if (verbose) {
|
|
51
|
-
|
|
48
|
+
logSuccess(`GitHub token obtained, expires: ${result.expires_at}`);
|
|
52
49
|
}
|
|
53
50
|
return result;
|
|
54
51
|
}
|
|
55
52
|
catch (error) {
|
|
56
53
|
if (verbose) {
|
|
57
|
-
|
|
54
|
+
logError(`Failed to get GitHub installation token: ${error instanceof Error ? error.message : String(error)}`);
|
|
58
55
|
}
|
|
59
56
|
return null;
|
|
60
57
|
}
|
|
@@ -64,19 +61,17 @@ export async function getGitHubInstallationToken(installationId, featureId, verb
|
|
|
64
61
|
* Used for product-level operations like growth analysis.
|
|
65
62
|
*/
|
|
66
63
|
export async function getGitHubConfigByProduct(productId, verbose) {
|
|
67
|
-
|
|
68
|
-
console.log(`🔍 Fetching GitHub config for product: ${productId}`);
|
|
69
|
-
}
|
|
64
|
+
logDebug(`Fetching GitHub config for product: ${productId}`, verbose);
|
|
70
65
|
try {
|
|
71
66
|
const result = (await callMcpEndpoint('github/config_and_token_by_product', {
|
|
72
67
|
product_id: productId,
|
|
73
68
|
}));
|
|
74
69
|
if (verbose) {
|
|
75
70
|
if (result.configured) {
|
|
76
|
-
|
|
71
|
+
logSuccess(`GitHub ready: ${result.repository_full_name}`);
|
|
77
72
|
}
|
|
78
73
|
else {
|
|
79
|
-
|
|
74
|
+
logWarning(`GitHub not configured: ${result.message}`);
|
|
80
75
|
}
|
|
81
76
|
}
|
|
82
77
|
if (result.configured && result.token && result.owner && result.repo) {
|
|
@@ -95,7 +90,7 @@ export async function getGitHubConfigByProduct(productId, verbose) {
|
|
|
95
90
|
}
|
|
96
91
|
catch (error) {
|
|
97
92
|
if (verbose) {
|
|
98
|
-
|
|
93
|
+
logError(`Failed to get GitHub config for product: ${error instanceof Error ? error.message : String(error)}`);
|
|
99
94
|
}
|
|
100
95
|
return {
|
|
101
96
|
configured: false,
|
|
@@ -108,19 +103,17 @@ export async function getGitHubConfigByProduct(productId, verbose) {
|
|
|
108
103
|
* This is the main entry point for getting GitHub config
|
|
109
104
|
*/
|
|
110
105
|
export async function getGitHubConfig(featureId, verbose) {
|
|
111
|
-
|
|
112
|
-
console.log(`🔍 Fetching GitHub config for feature: ${featureId}`);
|
|
113
|
-
}
|
|
106
|
+
logDebug(`Fetching GitHub config for feature: ${featureId}`, verbose);
|
|
114
107
|
try {
|
|
115
108
|
const result = (await callMcpEndpoint('github/config_and_token', {
|
|
116
109
|
feature_id: featureId,
|
|
117
110
|
}));
|
|
118
111
|
if (verbose) {
|
|
119
112
|
if (result.configured) {
|
|
120
|
-
|
|
113
|
+
logSuccess(`GitHub ready: ${result.repository_full_name}`);
|
|
121
114
|
}
|
|
122
115
|
else {
|
|
123
|
-
|
|
116
|
+
logWarning(`GitHub not configured: ${result.message}`);
|
|
124
117
|
}
|
|
125
118
|
}
|
|
126
119
|
if (result.configured && result.token && result.owner && result.repo) {
|
|
@@ -139,7 +132,7 @@ export async function getGitHubConfig(featureId, verbose) {
|
|
|
139
132
|
}
|
|
140
133
|
catch (error) {
|
|
141
134
|
if (verbose) {
|
|
142
|
-
|
|
135
|
+
logError(`Failed to get GitHub config: ${error instanceof Error ? error.message : String(error)}`);
|
|
143
136
|
}
|
|
144
137
|
return {
|
|
145
138
|
configured: false,
|
package/dist/api/mcp-client.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* Environment variables take precedence over stored auth.
|
|
8
8
|
*/
|
|
9
9
|
import { getMcpServerUrl, getMcpToken } from '../auth/auth-store.js';
|
|
10
|
+
import { logDebug, logError } from '../utils/logger.js';
|
|
10
11
|
/**
|
|
11
12
|
* Helper function to make HTTP requests to the MCP server
|
|
12
13
|
* Uses stored auth from `edsger login` or environment variables
|
|
@@ -44,7 +45,7 @@ export async function callMcpEndpoint(method, params) {
|
|
|
44
45
|
return data.result;
|
|
45
46
|
}
|
|
46
47
|
catch (error) {
|
|
47
|
-
|
|
48
|
+
logError(`MCP call failed for ${method}: ${error instanceof Error ? error.message : String(error)}`);
|
|
48
49
|
throw error;
|
|
49
50
|
}
|
|
50
51
|
}
|
|
@@ -53,21 +54,15 @@ export async function callMcpEndpoint(method, params) {
|
|
|
53
54
|
*/
|
|
54
55
|
export async function makeMcpRequest(options) {
|
|
55
56
|
const { method, params, verbose } = options;
|
|
56
|
-
|
|
57
|
-
console.log(`Making MCP request: ${method}`);
|
|
58
|
-
}
|
|
57
|
+
logDebug(`Making MCP request: ${method}`, verbose);
|
|
59
58
|
try {
|
|
60
59
|
const result = await callMcpEndpoint(method, params);
|
|
61
|
-
|
|
62
|
-
console.log(`MCP request successful: ${method}`);
|
|
63
|
-
}
|
|
60
|
+
logDebug(`MCP request successful: ${method}`, verbose);
|
|
64
61
|
return result;
|
|
65
62
|
}
|
|
66
63
|
catch (error) {
|
|
67
64
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
68
|
-
|
|
69
|
-
console.error(`MCP request failed: ${method} - ${errorMessage}`);
|
|
70
|
-
}
|
|
65
|
+
logDebug(`MCP request failed: ${method} - ${errorMessage}`, verbose);
|
|
71
66
|
throw error;
|
|
72
67
|
}
|
|
73
68
|
}
|
package/dist/auth/login.js
CHANGED
|
@@ -8,7 +8,7 @@ import { createInterface } from 'readline';
|
|
|
8
8
|
import { execSync } from 'child_process';
|
|
9
9
|
import { platform } from 'os';
|
|
10
10
|
import { saveAuth, loadAuth, clearAuth, getEdsgerBaseUrl, getAuthFilePath } from './auth-store.js';
|
|
11
|
-
import { logInfo, logSuccess, logError, logWarning } from '../utils/logger.js';
|
|
11
|
+
import { logInfo, logSuccess, logError, logWarning, logRaw } from '../utils/logger.js';
|
|
12
12
|
/**
|
|
13
13
|
* Open a URL in the user's default browser
|
|
14
14
|
*/
|
|
@@ -65,7 +65,7 @@ export async function runLogin() {
|
|
|
65
65
|
const baseUrl = getEdsgerBaseUrl();
|
|
66
66
|
const authUrl = `${baseUrl}/cli/auth`;
|
|
67
67
|
logInfo('Starting Edsger CLI login...');
|
|
68
|
-
|
|
68
|
+
logRaw('');
|
|
69
69
|
// Check if already logged in
|
|
70
70
|
const existingAuth = loadAuth();
|
|
71
71
|
if (existingAuth) {
|
|
@@ -77,19 +77,19 @@ export async function runLogin() {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
// Open browser
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
logInfo(`Opening browser to: ${authUrl}`);
|
|
81
|
+
logRaw('');
|
|
82
82
|
const opened = openBrowser(authUrl);
|
|
83
83
|
if (!opened) {
|
|
84
84
|
logWarning(`Could not open browser automatically.`);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
logInfo('Please open this URL manually:');
|
|
86
|
+
logRaw(` ${authUrl}`);
|
|
87
|
+
logRaw('');
|
|
88
88
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
logInfo('1. Sign in to edsger.ai (if not already signed in)');
|
|
90
|
+
logInfo('2. Click "Generate CLI Token" on the page');
|
|
91
|
+
logInfo('3. Copy the CLI Token and paste it below');
|
|
92
|
+
logRaw('');
|
|
93
93
|
const mcpServerUrl = process.env.EDSGER_MCP_SERVER_URL || MCP_SERVER_URL;
|
|
94
94
|
// Read token
|
|
95
95
|
const mcpToken = await readLine('CLI Token: ');
|
|
@@ -130,19 +130,19 @@ export async function runLogin() {
|
|
|
130
130
|
loggedInAt: new Date().toISOString(),
|
|
131
131
|
edsgerBaseUrl: baseUrl,
|
|
132
132
|
});
|
|
133
|
-
|
|
133
|
+
logRaw('');
|
|
134
134
|
logSuccess('Login successful!');
|
|
135
135
|
logInfo(`Credentials saved to ${getAuthFilePath()}`);
|
|
136
136
|
// Show available products
|
|
137
137
|
const products = data.result?.products || [];
|
|
138
138
|
if (products.length > 0) {
|
|
139
|
-
|
|
139
|
+
logRaw('');
|
|
140
140
|
logInfo(`You have access to ${products.length} product(s):`);
|
|
141
141
|
products.forEach((p, i) => {
|
|
142
|
-
|
|
142
|
+
logRaw(` ${i + 1}. ${p.name} (${p.id})`);
|
|
143
143
|
});
|
|
144
144
|
}
|
|
145
|
-
|
|
145
|
+
logRaw('');
|
|
146
146
|
logInfo('You can now run `edsger` to start processing features.');
|
|
147
147
|
}
|
|
148
148
|
catch (error) {
|
|
@@ -173,7 +173,7 @@ export async function runStatus() {
|
|
|
173
173
|
return;
|
|
174
174
|
}
|
|
175
175
|
logInfo('Login status:');
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
176
|
+
logRaw(` Server: ${auth.mcpServerUrl}`);
|
|
177
|
+
logRaw(` Logged in at: ${auth.loggedInAt}`);
|
|
178
|
+
logRaw(` Token: ${auth.mcpToken.substring(0, 8)}...${auth.mcpToken.substring(auth.mcpToken.length - 4)}`);
|
|
179
179
|
}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 6. Responds to web dashboard commands (pause/resume/stop)
|
|
11
11
|
*/
|
|
12
12
|
import { AgentWorkflowProcessor, MAX_CONCURRENCY } from './processor.js';
|
|
13
|
-
import { logInfo, logError, logSuccess, logWarning } from '../../utils/logger.js';
|
|
13
|
+
import { logInfo, logError, logSuccess, logWarning, logRaw } from '../../utils/logger.js';
|
|
14
14
|
import { isLoggedIn } from '../../auth/auth-store.js';
|
|
15
15
|
import { startAutoUpdateChecker, stopAutoUpdateChecker } from '../../updater/auto-updater.js';
|
|
16
16
|
import { startSleepPrevention, stopSleepPrevention } from '../../system/sleep-prevention.js';
|
|
@@ -27,10 +27,10 @@ export async function runAgentWorkflow(options) {
|
|
|
27
27
|
}
|
|
28
28
|
const config = validateConfiguration(options);
|
|
29
29
|
const version = getVersion();
|
|
30
|
-
|
|
30
|
+
logRaw('');
|
|
31
31
|
logInfo(`Edsger Agent v${version}`);
|
|
32
32
|
logInfo('Cross-product workflow mode');
|
|
33
|
-
|
|
33
|
+
logRaw('');
|
|
34
34
|
// Set up workspace directory
|
|
35
35
|
const workspaceRoot = ensureWorkspaceDir();
|
|
36
36
|
logInfo(`Workspace: ${workspaceRoot}`);
|
|
@@ -44,9 +44,9 @@ export async function runAgentWorkflow(options) {
|
|
|
44
44
|
startSleepMonitor();
|
|
45
45
|
// Start heartbeat
|
|
46
46
|
startHeartbeat();
|
|
47
|
-
|
|
47
|
+
logRaw('');
|
|
48
48
|
logSuccess('Agent started. Watching for features...');
|
|
49
|
-
|
|
49
|
+
logRaw('');
|
|
50
50
|
// Create and start the processor
|
|
51
51
|
let maxConcurrent = 3;
|
|
52
52
|
if (options.concurrency && options.concurrency > 0) {
|
|
@@ -71,7 +71,7 @@ export async function runAgentWorkflow(options) {
|
|
|
71
71
|
processor.stop();
|
|
72
72
|
// Show stats
|
|
73
73
|
const stats = processor.getStats();
|
|
74
|
-
|
|
74
|
+
logRaw('');
|
|
75
75
|
logInfo('Session Statistics:');
|
|
76
76
|
logInfo(` Total processed: ${stats.totalProcessed}`);
|
|
77
77
|
logInfo(` Completed: ${stats.completed}`);
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
import { EdsgerConfig } from '../../types/index.js';
|
|
11
11
|
import { type WorkflowStats } from '../workflow/core/state-manager.js';
|
|
12
12
|
/**
|
|
13
|
-
* Maximum allowed concurrency.
|
|
13
|
+
* Maximum allowed concurrency (hard cap).
|
|
14
14
|
* Limits are due to:
|
|
15
15
|
* - MCP API rate limits (per-minute/per-hour per token)
|
|
16
16
|
* - AI API rate limits (Claude/OpenAI RPM/TPM)
|
|
@@ -15,9 +15,10 @@ import { getGitHubConfig } from '../../api/github.js';
|
|
|
15
15
|
import { logInfo, logWarning, logError, logSuccess } from '../../utils/logger.js';
|
|
16
16
|
import { shouldProcess, sendHeartbeat } from '../../system/session-manager.js';
|
|
17
17
|
import { cloneFeatureRepo } from '../../workspace/workspace-manager.js';
|
|
18
|
+
import { WorkerTimeoutError } from '../../errors/index.js';
|
|
18
19
|
import { createInitialState, updateFeatureState, createProcessingState, createCompletedState, createFailedState, calculateStats, } from '../workflow/core/state-manager.js';
|
|
19
20
|
/**
|
|
20
|
-
* Maximum allowed concurrency.
|
|
21
|
+
* Maximum allowed concurrency (hard cap).
|
|
21
22
|
* Limits are due to:
|
|
22
23
|
* - MCP API rate limits (per-minute/per-hour per token)
|
|
23
24
|
* - AI API rate limits (Claude/OpenAI RPM/TPM)
|
|
@@ -41,10 +42,11 @@ export class AgentWorkflowProcessor {
|
|
|
41
42
|
/** Chat worker subprocess — runs in parallel, handles chat messages and phase events */
|
|
42
43
|
chatWorker;
|
|
43
44
|
constructor(options, config) {
|
|
45
|
+
const wf = config.workflow;
|
|
44
46
|
this.options = {
|
|
45
|
-
pollInterval:
|
|
46
|
-
maxConcurrent:
|
|
47
|
-
maxRetries:
|
|
47
|
+
pollInterval: options.pollInterval ?? wf.pollInterval,
|
|
48
|
+
maxConcurrent: Math.min(options.maxConcurrent ?? wf.maxConcurrency, MAX_CONCURRENCY),
|
|
49
|
+
maxRetries: options.maxRetries ?? wf.maxRetries,
|
|
48
50
|
verbose: false,
|
|
49
51
|
...options,
|
|
50
52
|
};
|
|
@@ -152,6 +154,8 @@ export class AgentWorkflowProcessor {
|
|
|
152
154
|
// Kill all active feature workers gracefully
|
|
153
155
|
for (const [featureId, worker] of this.activeWorkers) {
|
|
154
156
|
logInfo(`Stopping worker for feature: ${featureId}`);
|
|
157
|
+
if (worker.timeoutTimer)
|
|
158
|
+
clearTimeout(worker.timeoutTimer);
|
|
155
159
|
try {
|
|
156
160
|
worker.process.kill('SIGTERM');
|
|
157
161
|
}
|
|
@@ -186,7 +190,7 @@ export class AgentWorkflowProcessor {
|
|
|
186
190
|
return;
|
|
187
191
|
}
|
|
188
192
|
// Filter out features already being processed or recently failed
|
|
189
|
-
const
|
|
193
|
+
const cooldownMs = this.config.workflow.retryCooldown;
|
|
190
194
|
const newFeatures = features.filter((f) => {
|
|
191
195
|
if (this.activeWorkers.has(f.id))
|
|
192
196
|
return false;
|
|
@@ -199,7 +203,7 @@ export class AgentWorkflowProcessor {
|
|
|
199
203
|
if (state.retryCount >= this.options.maxRetries)
|
|
200
204
|
return false;
|
|
201
205
|
// Skip if within cooldown period
|
|
202
|
-
if (Date.now() - state.lastAttempt.getTime() <
|
|
206
|
+
if (Date.now() - state.lastAttempt.getTime() < cooldownMs)
|
|
203
207
|
return false;
|
|
204
208
|
}
|
|
205
209
|
return true;
|
|
@@ -319,6 +323,10 @@ export class AgentWorkflowProcessor {
|
|
|
319
323
|
});
|
|
320
324
|
// Handle worker exit (crash, killed, etc.)
|
|
321
325
|
worker.on('exit', (code) => {
|
|
326
|
+
// Clear timeout timer
|
|
327
|
+
const w = this.activeWorkers.get(featureId);
|
|
328
|
+
if (w?.timeoutTimer)
|
|
329
|
+
clearTimeout(w.timeoutTimer);
|
|
322
330
|
this.activeWorkers.delete(featureId);
|
|
323
331
|
// If the worker exited without sending a result message, treat as failure
|
|
324
332
|
if (!resultReceived) {
|
|
@@ -328,6 +336,26 @@ export class AgentWorkflowProcessor {
|
|
|
328
336
|
this.handleWorkerResult(featureId, feature.name, false, `Worker exited with code ${code}`);
|
|
329
337
|
}
|
|
330
338
|
});
|
|
339
|
+
// Set up worker timeout
|
|
340
|
+
const workerTimeout = this.config.workflow.workerTimeout;
|
|
341
|
+
activeWorker.timeoutTimer = setTimeout(() => {
|
|
342
|
+
logError(`Worker for ${feature.name} timed out after ${Math.round(workerTimeout / 1000)}s, killing...`);
|
|
343
|
+
try {
|
|
344
|
+
worker.kill('SIGTERM');
|
|
345
|
+
// Force kill after 10s if SIGTERM didn't work
|
|
346
|
+
setTimeout(() => {
|
|
347
|
+
try {
|
|
348
|
+
worker.kill('SIGKILL');
|
|
349
|
+
}
|
|
350
|
+
catch { /* ignore */ }
|
|
351
|
+
}, 10_000);
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
// Worker may already be dead
|
|
355
|
+
}
|
|
356
|
+
const err = new WorkerTimeoutError(featureId, workerTimeout);
|
|
357
|
+
this.handleWorkerResult(featureId, feature.name, false, err.message);
|
|
358
|
+
}, workerTimeout);
|
|
331
359
|
// Send start message to worker
|
|
332
360
|
worker.send({
|
|
333
361
|
type: 'start',
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { CliOptions } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Run AI-powered log analysis for a product.
|
|
4
|
+
* Analyzes pending user logs (inactive >1 hour) and creates
|
|
5
|
+
* product feedback for any issues or improvements found.
|
|
6
|
+
*/
|
|
7
|
+
export declare const runAnalyzeLogs: (options: CliOptions) => Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { logInfo, logError, logSuccess } from '../../utils/logger.js';
|
|
2
|
+
import { analyzeLogs } from '../../phases/analyze-logs/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Run AI-powered log analysis for a product.
|
|
5
|
+
* Analyzes pending user logs (inactive >1 hour) and creates
|
|
6
|
+
* product feedback for any issues or improvements found.
|
|
7
|
+
*/
|
|
8
|
+
export const runAnalyzeLogs = async (options) => {
|
|
9
|
+
const productId = options.analyzeLogs;
|
|
10
|
+
if (!productId) {
|
|
11
|
+
throw new Error('Product ID is required for log analysis');
|
|
12
|
+
}
|
|
13
|
+
logInfo(`Starting log analysis for product: ${productId}`);
|
|
14
|
+
try {
|
|
15
|
+
const result = await analyzeLogs({
|
|
16
|
+
productId,
|
|
17
|
+
verbose: options.verbose,
|
|
18
|
+
});
|
|
19
|
+
if (result.status === 'success') {
|
|
20
|
+
logSuccess('Log analysis completed!');
|
|
21
|
+
logInfo(` Groups processed: ${result.groupsProcessed}`);
|
|
22
|
+
logInfo(` Feedbacks created: ${result.feedbacksCreated}`);
|
|
23
|
+
logInfo(` Logs analyzed: ${result.logsAnalyzed}`);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
logError(`Log analysis failed: ${result.summary}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
logError(`Log analysis failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { CliOptions } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Run AI-powered app store asset generation for a product.
|
|
4
|
+
* Generates professional screenshots and optimized listing content.
|
|
5
|
+
*/
|
|
6
|
+
export declare const runAppStoreGeneration: (options: CliOptions & {
|
|
7
|
+
appStoreProductId: string;
|
|
8
|
+
store?: string;
|
|
9
|
+
locale?: string;
|
|
10
|
+
screenshotsOnly?: boolean;
|
|
11
|
+
listingsOnly?: boolean;
|
|
12
|
+
}) => Promise<void>;
|