Skip to content

Commit 40a3cb6

Browse files
committed
fix: stabilize dynamic dependency probe and CI bootstrap
1 parent 0753d62 commit 40a3cb6

5 files changed

Lines changed: 105 additions & 11 deletions

File tree

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ jobs:
3737
- name: Install JavaScript dependencies
3838
run: npm ci
3939

40+
- name: Install baseline Python dependencies
41+
run: python -m pip install -r requirements.txt
42+
4043
- name: Build TypeScript
4144
run: npm run build
4245

.github/workflows/publish-npm.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ jobs:
3737
- name: Install JavaScript dependencies
3838
run: npm ci
3939

40+
- name: Install baseline Python dependencies
41+
run: python -m pip install -r requirements.txt
42+
4043
- name: Build TypeScript
4144
run: npm run build
4245

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@
5959
],
6060
"author": "",
6161
"license": "MIT",
62+
"repository": {
63+
"type": "git",
64+
"url": "git+https://github.com/Last-emo-boy/windows-exe-decompiler-mcp-server.git"
65+
},
66+
"homepage": "https://github.com/Last-emo-boy/windows-exe-decompiler-mcp-server",
67+
"bugs": {
68+
"url": "https://github.com/Last-emo-boy/windows-exe-decompiler-mcp-server/issues"
69+
},
6270
"publishConfig": {
6371
"access": "public"
6472
},

src/tools/dynamic-dependencies.ts

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,56 @@ interface WorkerResponse {
8080
metrics: Record<string, unknown>
8181
}
8282

83+
interface DynamicDependenciesDependencies {
84+
callWorker?: (request: WorkerRequest) => Promise<WorkerResponse>
85+
}
86+
87+
function buildBootstrapFallback(startTime: number, errorMessage: string): WorkerResult {
88+
return {
89+
ok: true,
90+
data: {
91+
status: 'bootstrap_required',
92+
available_components: [],
93+
components: {
94+
speakeasy: {
95+
available: false,
96+
version: null,
97+
distribution: null,
98+
api_available: false,
99+
warnings: [],
100+
error: errorMessage,
101+
},
102+
frida: {
103+
available: false,
104+
version: null,
105+
error: errorMessage,
106+
},
107+
psutil: {
108+
available: false,
109+
version: null,
110+
error: errorMessage,
111+
},
112+
worker: {
113+
available: false,
114+
error: errorMessage,
115+
},
116+
},
117+
recommendations: [
118+
'Install baseline Python dependencies first: pip install -r requirements.txt',
119+
'Install FLARE Speakeasy emulator for PE user-mode emulation: pip install speakeasy-emulator',
120+
'Install frida for runtime API tracing: pip install frida',
121+
'Install psutil for process telemetry collection: pip install psutil',
122+
],
123+
checked_at: new Date().toISOString(),
124+
},
125+
warnings: [`dynamic.dependencies probe degraded: ${errorMessage}`],
126+
metrics: {
127+
elapsed_ms: Date.now() - startTime,
128+
tool: TOOL_NAME,
129+
},
130+
}
131+
}
132+
83133
async function callStaticWorker(request: WorkerRequest): Promise<WorkerResponse> {
84134
return new Promise((resolve, reject) => {
85135
const workerPath = resolvePackagePath('workers', 'static_worker.py')
@@ -134,10 +184,12 @@ async function callStaticWorker(request: WorkerRequest): Promise<WorkerResponse>
134184

135185
export function createDynamicDependenciesHandler(
136186
workspaceManager: WorkspaceManager,
137-
database: DatabaseManager
187+
database: DatabaseManager,
188+
dependencies?: DynamicDependenciesDependencies
138189
) {
139190
return async (args: ToolArgs): Promise<WorkerResult> => {
140191
const startTime = Date.now()
192+
const runWorker = dependencies?.callWorker || callStaticWorker
141193

142194
try {
143195
const input = DynamicDependenciesInputSchema.parse(args)
@@ -185,17 +237,18 @@ export function createDynamicDependenciesHandler(
185237
},
186238
}
187239

188-
const workerResponse = await callStaticWorker(workerRequest)
240+
let workerResponse: WorkerResponse
241+
try {
242+
workerResponse = await runWorker(workerRequest)
243+
} catch (error) {
244+
return buildBootstrapFallback(startTime, (error as Error).message)
245+
}
246+
189247
if (!workerResponse.ok) {
190-
return {
191-
ok: false,
192-
errors: workerResponse.errors,
193-
warnings: workerResponse.warnings,
194-
metrics: {
195-
elapsed_ms: Date.now() - startTime,
196-
tool: TOOL_NAME,
197-
},
198-
}
248+
return buildBootstrapFallback(
249+
startTime,
250+
workerResponse.errors.join('; ') || 'dynamic dependency probe failed'
251+
)
199252
}
200253

201254
return {

tests/unit/dynamic-dependencies.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,31 @@ describe('dynamic.dependencies tool', () => {
6060
expect(data.recommendations.join(' ')).toContain('pip uninstall speakeasy')
6161
}
6262
})
63+
64+
test('should degrade to bootstrap_required when the worker probe fails', async () => {
65+
const handler = createDynamicDependenciesHandler(workspaceManager, database, {
66+
callWorker: async () => {
67+
throw new Error('Python worker exited with code 1')
68+
},
69+
})
70+
71+
const result = await handler({})
72+
73+
expect(result.ok).toBe(true)
74+
const data = result.data as {
75+
status: string
76+
components: {
77+
worker?: {
78+
available?: boolean
79+
error?: string
80+
}
81+
}
82+
recommendations: string[]
83+
}
84+
85+
expect(data.status).toBe('bootstrap_required')
86+
expect(data.components.worker?.available).toBe(false)
87+
expect(data.components.worker?.error).toContain('Python worker exited with code 1')
88+
expect(data.recommendations.join(' ')).toContain('pip install -r requirements.txt')
89+
})
6390
})

0 commit comments

Comments
 (0)