Skip to content

Commit 30280b5

Browse files
committed
all
1 parent db7dedc commit 30280b5

File tree

10 files changed

+634
-1283
lines changed

10 files changed

+634
-1283
lines changed

debugger/dap_debug_service.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,19 @@ function spawnProcess(options: SpawnOptions): Subprocess {
162162
logger.info(`Spawning: ${cmd.join(' ')}`)
163163
}
164164

165+
// Only include essential env vars + caller-provided ones
166+
// Don't inherit all of process.env to keep debugger environment clean
165167
return spawn({
166168
cmd,
167169
cwd: options.cwd || process.cwd(),
168170
stdout: options.stdout || 'pipe',
169171
stderr: options.stderr || 'pipe',
170172
env: {
171-
...process.env,
173+
// Essential system vars
174+
PATH: process.env.PATH || '/usr/bin:/bin',
175+
HOME: process.env.HOME,
176+
// Caller-provided env vars
177+
// Note: WM_BASE_URL is already overridden by BASE_INTERNAL_URL if set
172178
...options.env
173179
}
174180
})
@@ -270,6 +276,7 @@ class PythonDebugSession extends BaseDebugSession {
270276
private variablesRefCounter = 1
271277
private scopesMap = new Map<number, { variablesReference: number }>()
272278
private scriptResult: unknown = undefined
279+
private envVars: Record<string, string> = {}
273280

274281
private nextDebugpySeq(): number {
275282
return this.debugpySeq++
@@ -399,6 +406,7 @@ class PythonDebugSession extends BaseDebugSession {
399406
const scriptDir = import.meta.dir
400407

401408
// Spawn the Python WebSocket DAP server
409+
// Pass env vars to the server - it will forward them to the debugged script
402410
this.process = spawnProcess({
403411
cmd: [
404412
config.pythonPath,
@@ -408,7 +416,7 @@ class PythonDebugSession extends BaseDebugSession {
408416
'--host', '127.0.0.1'
409417
],
410418
cwd,
411-
env: { PYTHONUNBUFFERED: '1' }
419+
env: { PYTHONUNBUFFERED: '1', ...this.envVars }
412420
})
413421

414422
// Read stderr to capture startup messages
@@ -643,6 +651,16 @@ class PythonDebugSession extends BaseDebugSession {
643651
const cwd = (args.cwd as string) || process.cwd()
644652
this.callMain = (args.callMain as boolean) || false
645653
this.mainArgs = (args.args as Record<string, unknown>) || {}
654+
this.envVars = (args.env as Record<string, string>) || {}
655+
656+
// If BASE_INTERNAL_URL is set on the server, use it to override WM_BASE_URL
657+
if (process.env.BASE_INTERNAL_URL) {
658+
this.envVars.WM_BASE_URL = process.env.BASE_INTERNAL_URL
659+
}
660+
661+
if (Object.keys(this.envVars).length > 0) {
662+
logger.info(`Python launch with env vars: ${Object.keys(this.envVars).join(', ')}`)
663+
}
646664

647665
if (!this.scriptPath && !code) {
648666
this.sendResponse(request, false, {}, 'No program or code specified')
@@ -699,7 +717,8 @@ sys.stdout.flush()
699717
code: code,
700718
args: this.mainArgs,
701719
cwd: cwd,
702-
callMain: this.callMain
720+
callMain: this.callMain,
721+
env: this.envVars
703722
})
704723
} catch (error) {
705724
this.sendEvent('output', { category: 'stderr', output: `Failed to start Python: ${error}\n` })

debugger/dap_websocket_server.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,10 @@ async def handle_launch(self, request: dict) -> None:
425425
cwd = args.get("cwd", os.getcwd())
426426
self._call_main = args.get("callMain", False)
427427
self._main_args = args.get("args", {})
428+
self._env_vars = args.get("env", {})
429+
430+
if self._env_vars:
431+
logger.info(f"Launch with env vars: {list(self._env_vars.keys())}")
428432

429433
if not self.script_path and not code:
430434
await self.send_response(
@@ -486,6 +490,14 @@ def _run_script(self, script_path: str, cwd: str) -> None:
486490
old_argv = sys.argv
487491
old_stdout = sys.stdout
488492
old_stderr = sys.stderr
493+
old_env = {}
494+
495+
# Set environment variables for the script
496+
if hasattr(self, '_env_vars') and self._env_vars:
497+
for key, value in self._env_vars.items():
498+
old_env[key] = os.environ.get(key)
499+
os.environ[key] = str(value)
500+
logger.info(f"Set {len(self._env_vars)} env vars for script")
489501

490502
# Create a streaming output wrapper that sends output events in real-time
491503
session = self
@@ -573,6 +585,12 @@ def flush(self):
573585
sys.argv = old_argv
574586
sys.stdout = old_stdout
575587
sys.stderr = old_stderr
588+
# Restore environment variables
589+
for key, old_value in old_env.items():
590+
if old_value is None:
591+
os.environ.pop(key, None)
592+
else:
593+
os.environ[key] = old_value
576594
self._cleanup_temp_file()
577595

578596
def _cleanup_temp_file(self) -> None:

debugger/dap_websocket_server_bun.ts

Lines changed: 139 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,9 @@ export class DebugSession {
298298
// Track if current pause is due to a breakpoint (for line number correction)
299299
private pausedAtBreakpoint = false
300300

301+
// Environment variables to pass to the debugger subprocess (e.g., WM_WORKSPACE, WM_TOKEN, etc.)
302+
private envVars: Record<string, string> = {}
303+
301304
// Nsjail configuration for sandboxed execution
302305
private nsjailConfig?: NsjailConfig
303306

@@ -704,6 +707,83 @@ export class DebugSession {
704707
})
705708
}
706709

710+
/**
711+
* Format a console parameter for output.
712+
* Handles primitives, objects with preview, arrays, etc.
713+
*/
714+
private formatConsoleParameter(p: {
715+
type: string
716+
value?: unknown
717+
description?: string
718+
subtype?: string
719+
className?: string
720+
objectId?: string
721+
preview?: {
722+
type: string
723+
subtype?: string
724+
description?: string
725+
overflow?: boolean
726+
properties?: Array<{ name: string; type: string; value?: string; subtype?: string }>
727+
}
728+
}): string {
729+
// Handle primitives with direct values
730+
if (p.value !== undefined) {
731+
if (typeof p.value === 'string') return p.value
732+
return String(p.value)
733+
}
734+
735+
// Handle null
736+
if (p.subtype === 'null') return 'null'
737+
738+
// Handle undefined
739+
if (p.type === 'undefined') return 'undefined'
740+
741+
// Handle objects with preview (this is the key fix for process.env, etc.)
742+
if (p.type === 'object' && p.preview && p.preview.properties) {
743+
const props = p.preview.properties
744+
const isArray = p.subtype === 'array' || p.preview.subtype === 'array'
745+
746+
if (isArray) {
747+
// Format as array: [val1, val2, ...]
748+
const items = props.map((prop) => this.formatPreviewValue(prop))
749+
const suffix = p.preview.overflow ? ', ...' : ''
750+
return `[${items.join(', ')}${suffix}]`
751+
} else {
752+
// Format as object: { key1: val1, key2: val2, ... }
753+
const items = props.map((prop) => `${prop.name}: ${this.formatPreviewValue(prop)}`)
754+
const suffix = p.preview.overflow ? ', ...' : ''
755+
return `{ ${items.join(', ')}${suffix} }`
756+
}
757+
}
758+
759+
// Handle functions
760+
if (p.type === 'function') {
761+
return p.description || '[Function]'
762+
}
763+
764+
// Fallback to description (for other object types without preview)
765+
if (p.description) return p.description
766+
767+
return ''
768+
}
769+
770+
/**
771+
* Format a single property from an object preview.
772+
*/
773+
private formatPreviewValue(prop: { name: string; type: string; value?: string; subtype?: string }): string {
774+
if (prop.subtype === 'null') return 'null'
775+
if (prop.type === 'undefined') return 'undefined'
776+
if (prop.type === 'string') return `"${prop.value ?? ''}"`
777+
if (prop.type === 'number' || prop.type === 'boolean') return prop.value ?? ''
778+
if (prop.type === 'function') return '[Function]'
779+
if (prop.type === 'object') {
780+
// Nested objects just show their type/value preview
781+
if (prop.subtype === 'array') return prop.value || '[]'
782+
return prop.value || '{...}'
783+
}
784+
return prop.value ?? ''
785+
}
786+
707787
/**
708788
* Handle console output (WebKit/Bun format - Console.messageAdded).
709789
*/
@@ -716,19 +796,29 @@ export class DebugSession {
716796
line?: number
717797
column?: number
718798
url?: string
719-
parameters?: Array<{ type: string; value?: unknown; description?: string }>
799+
parameters?: Array<{
800+
type: string
801+
value?: unknown
802+
description?: string
803+
subtype?: string
804+
className?: string
805+
objectId?: string
806+
preview?: {
807+
type: string
808+
subtype?: string
809+
description?: string
810+
overflow?: boolean
811+
properties?: Array<{ name: string; type: string; value?: string; subtype?: string }>
812+
}
813+
}>
720814
}
721815

722816
if (!message) return
723817

724818
let output: string
725819
if (message.parameters && message.parameters.length > 0) {
726820
output = message.parameters
727-
.map((p) => {
728-
if (p.value !== undefined) return String(p.value)
729-
if (p.description) return p.description
730-
return ''
731-
})
821+
.map((p) => this.formatConsoleParameter(p))
732822
.join(' ')
733823
} else {
734824
output = message.text || ''
@@ -906,6 +996,16 @@ export class DebugSession {
906996
const cwd = (args.cwd as string) || process.cwd()
907997
this.callMain = (args.callMain as boolean) || false
908998
this.mainArgs = (args.args as Record<string, unknown>) || {}
999+
this.envVars = (args.env as Record<string, string>) || {}
1000+
1001+
// If BASE_INTERNAL_URL is set on the server, use it to override WM_BASE_URL
1002+
if (process.env.BASE_INTERNAL_URL) {
1003+
this.envVars.WM_BASE_URL = process.env.BASE_INTERNAL_URL
1004+
}
1005+
1006+
if (Object.keys(this.envVars).length > 0) {
1007+
logger.info(`Launch with env vars: ${Object.keys(this.envVars).join(', ')}`)
1008+
}
9091009

9101010
if (!this.scriptPath && !code) {
9111011
this.sendResponse(request, false, {}, 'No program or code specified')
@@ -1017,14 +1117,20 @@ export class DebugSession {
10171117
}, 10000)
10181118
})
10191119

1120+
// Only include essential env vars + client-provided ones
1121+
// Don't inherit all of process.env to keep debugger environment clean
10201122
this.process = spawn({
10211123
cmd,
10221124
cwd,
10231125
stdout: 'pipe',
10241126
stderr: 'pipe',
10251127
env: {
1026-
...process.env,
1027-
NODE_ENV: 'development'
1128+
// Essential system vars
1129+
PATH: process.env.PATH || '/usr/bin:/bin',
1130+
HOME: process.env.HOME,
1131+
// Client-provided env vars (WM_WORKSPACE, WM_TOKEN, etc.)
1132+
// Note: WM_BASE_URL is already overridden by BASE_INTERNAL_URL if set
1133+
...this.envVars
10281134
}
10291135
})
10301136

@@ -1385,6 +1491,14 @@ export class DebugSession {
13851491
description?: string
13861492
objectId?: string
13871493
subtype?: string
1494+
className?: string
1495+
preview?: {
1496+
type: string
1497+
subtype?: string
1498+
description?: string
1499+
overflow?: boolean
1500+
properties?: Array<{ name: string; type: string; value?: string; subtype?: string }>
1501+
}
13881502
}
13891503
configurable?: boolean
13901504
enumerable?: boolean
@@ -1424,7 +1538,7 @@ export class DebugSession {
14241538
let value: string
14251539
let varRef = 0
14261540
let displayType = prop.value.type
1427-
const className = (prop.value as { className?: string }).className
1541+
const className = prop.value.className
14281542

14291543
// Log for debugging variable parsing issues
14301544
logger.debug(`Variable ${prop.name}: type=${prop.value.type}, className=${className}, description=${prop.value.description}, value=${JSON.stringify(prop.value.value)}`)
@@ -1445,7 +1559,22 @@ export class DebugSession {
14451559
// Regular object - create a reference for nested inspection
14461560
varRef = this.nextVarRef()
14471561
this.objectsMap.set(varRef, prop.value.objectId)
1448-
value = prop.value.description || `[${prop.value.subtype || className || 'Object'}]`
1562+
// Use preview data to show object contents instead of just "Object"
1563+
if (prop.value.preview && prop.value.preview.properties) {
1564+
const previewProps = prop.value.preview.properties
1565+
const isArray = prop.value.subtype === 'array' || prop.value.preview.subtype === 'array'
1566+
if (isArray) {
1567+
const items = previewProps.map((p) => this.formatPreviewValue(p))
1568+
const suffix = prop.value.preview.overflow ? ', ...' : ''
1569+
value = `[${items.join(', ')}${suffix}]`
1570+
} else {
1571+
const items = previewProps.map((p) => `${p.name}: ${this.formatPreviewValue(p)}`)
1572+
const suffix = prop.value.preview.overflow ? ', ...' : ''
1573+
value = `{ ${items.join(', ')}${suffix} }`
1574+
}
1575+
} else {
1576+
value = prop.value.description || `[${prop.value.subtype || className || 'Object'}]`
1577+
}
14491578
}
14501579
} else if (prop.value.type === 'string') {
14511580
// Handle primitive strings - they have value property

0 commit comments

Comments
 (0)