Skip to content

Commit 636a517

Browse files
committed
refactor(dlx): add binaryName support and rename to details
- Add binaryName field to DlxPackageSpec type - Pass binaryName to dlxPackage() in spawnDlx() - Specify binary names in resolve functions (coana, cdxgen, synp) - Rename packageSpec property to details in BinaryResolution type for clarity This fixes the issue where @coana-tech/cli was incorrectly resolved as 'coana-tech/cli' during scan reach command execution.
1 parent f20d695 commit 636a517

File tree

4 files changed

+69
-111
lines changed

4 files changed

+69
-111
lines changed

packages/cli/src/constants/env.mts

Lines changed: 37 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -106,20 +106,20 @@ export {
106106
// IMPORTANT: esbuild's define plugin can only replace direct process.env['KEY'] references.
107107
// If we imported these from env modules, esbuild couldn't inline the values at build time.
108108
// This is critical for embedding version info, build tags, and feature flags into the binary.
109-
export function getCliVersion(): string | undefined {
110-
return process.env['INLINED_SOCKET_CLI_VERSION']
109+
export function getCliVersion(): string {
110+
return process.env['INLINED_SOCKET_CLI_VERSION']!
111111
}
112112

113-
export function getCliVersionHash(): string | undefined {
114-
return process.env['INLINED_SOCKET_CLI_VERSION_HASH']
113+
export function getCliVersionHash(): string {
114+
return process.env['INLINED_SOCKET_CLI_VERSION_HASH']!
115115
}
116116

117-
export function getCliHomepage(): string | undefined {
118-
return process.env['INLINED_SOCKET_CLI_HOMEPAGE']
117+
export function getCliHomepage(): string {
118+
return process.env['INLINED_SOCKET_CLI_HOMEPAGE']!
119119
}
120120

121-
export function getCliName(): string | undefined {
122-
return process.env['INLINED_SOCKET_CLI_NAME']
121+
export function getCliName(): string {
122+
return process.env['INLINED_SOCKET_CLI_NAME']!
123123
}
124124

125125
export function isPublishedBuild(): boolean {
@@ -134,32 +134,32 @@ export function isSentryBuild(): boolean {
134134
return envAsBoolean(process.env['INLINED_SOCKET_CLI_SENTRY_BUILD'])
135135
}
136136

137-
export function getCliAiVersion(): string | undefined {
138-
return process.env['INLINED_SOCKET_CLI_AI_VERSION']
137+
export function getCliAiVersion(): string {
138+
return process.env['INLINED_SOCKET_CLI_AI_VERSION']!
139139
}
140140

141-
export function getCoanaVersion(): string | undefined {
142-
return process.env['INLINED_SOCKET_CLI_COANA_VERSION']
141+
export function getCoanaVersion(): string {
142+
return process.env['INLINED_SOCKET_CLI_COANA_VERSION']!
143143
}
144144

145-
export function getCdxgenVersion(): string | undefined {
146-
return process.env['INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION']
145+
export function getCdxgenVersion(): string {
146+
return process.env['INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION']!
147147
}
148148

149-
export function getSynpVersion(): string | undefined {
150-
return process.env['INLINED_SOCKET_CLI_SYNP_VERSION']
149+
export function getSynpVersion(): string {
150+
return process.env['INLINED_SOCKET_CLI_SYNP_VERSION']!
151151
}
152152

153-
export function getPythonVersion(): string | undefined {
154-
return process.env['INLINED_SOCKET_CLI_PYTHON_VERSION']
153+
export function getPythonVersion(): string {
154+
return process.env['INLINED_SOCKET_CLI_PYTHON_VERSION']!
155155
}
156156

157-
export function getPythonBuildTag(): string | undefined {
158-
return process.env['INLINED_SOCKET_CLI_PYTHON_BUILD_TAG']
157+
export function getPythonBuildTag(): string {
158+
return process.env['INLINED_SOCKET_CLI_PYTHON_BUILD_TAG']!
159159
}
160160

161-
export function getPyCliVersion(): string | undefined {
162-
return process.env['INLINED_SOCKET_CLI_PYCLI_VERSION']
161+
export function getPyCliVersion(): string {
162+
return process.env['INLINED_SOCKET_CLI_PYCLI_VERSION']!
163163
}
164164

165165
// Export processEnv for backward compatibility with shadow npm integration.
@@ -222,32 +222,32 @@ const envSnapshot = {
222222
GITHUB_REPOSITORY: env['GITHUB_REPOSITORY'],
223223
SOCKET_CLI_ORG_SLUG: env['SOCKET_CLI_ORG_SLUG'],
224224
// Build metadata (inlined by esbuild define).
225-
INLINED_SOCKET_CLI_AI_VERSION: process.env['INLINED_SOCKET_CLI_AI_VERSION'],
225+
INLINED_SOCKET_CLI_AI_VERSION: process.env['INLINED_SOCKET_CLI_AI_VERSION']!,
226226
INLINED_SOCKET_CLI_CDXGEN_VERSION:
227-
process.env['INLINED_SOCKET_CLI_CDXGEN_VERSION'],
227+
process.env['INLINED_SOCKET_CLI_CDXGEN_VERSION']!,
228228
INLINED_SOCKET_CLI_COANA_VERSION:
229-
process.env['INLINED_SOCKET_CLI_COANA_VERSION'],
229+
process.env['INLINED_SOCKET_CLI_COANA_VERSION']!,
230230
INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION:
231-
process.env['INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION'],
232-
INLINED_SOCKET_CLI_HOMEPAGE: process.env['INLINED_SOCKET_CLI_HOMEPAGE'],
231+
process.env['INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION']!,
232+
INLINED_SOCKET_CLI_HOMEPAGE: process.env['INLINED_SOCKET_CLI_HOMEPAGE']!,
233233
INLINED_SOCKET_CLI_LEGACY_BUILD:
234-
process.env['INLINED_SOCKET_CLI_LEGACY_BUILD'],
235-
INLINED_SOCKET_CLI_NAME: process.env['INLINED_SOCKET_CLI_NAME'],
234+
process.env['INLINED_SOCKET_CLI_LEGACY_BUILD']!,
235+
INLINED_SOCKET_CLI_NAME: process.env['INLINED_SOCKET_CLI_NAME']!,
236236
INLINED_SOCKET_CLI_PUBLISHED_BUILD:
237-
process.env['INLINED_SOCKET_CLI_PUBLISHED_BUILD'],
237+
process.env['INLINED_SOCKET_CLI_PUBLISHED_BUILD']!,
238238
INLINED_SOCKET_CLI_PYTHON_BUILD_TAG:
239-
process.env['INLINED_SOCKET_CLI_PYTHON_BUILD_TAG'],
239+
process.env['INLINED_SOCKET_CLI_PYTHON_BUILD_TAG']!,
240240
INLINED_SOCKET_CLI_PYTHON_VERSION:
241-
process.env['INLINED_SOCKET_CLI_PYTHON_VERSION'],
241+
process.env['INLINED_SOCKET_CLI_PYTHON_VERSION']!,
242242
INLINED_SOCKET_CLI_PYCLI_VERSION:
243-
process.env['INLINED_SOCKET_CLI_PYCLI_VERSION'],
243+
process.env['INLINED_SOCKET_CLI_PYCLI_VERSION']!,
244244
INLINED_SOCKET_CLI_SENTRY_BUILD:
245-
process.env['INLINED_SOCKET_CLI_SENTRY_BUILD'],
245+
process.env['INLINED_SOCKET_CLI_SENTRY_BUILD']!,
246246
INLINED_SOCKET_CLI_SYNP_VERSION:
247-
process.env['INLINED_SOCKET_CLI_SYNP_VERSION'],
248-
INLINED_SOCKET_CLI_VERSION: process.env['INLINED_SOCKET_CLI_VERSION'],
247+
process.env['INLINED_SOCKET_CLI_SYNP_VERSION']!,
248+
INLINED_SOCKET_CLI_VERSION: process.env['INLINED_SOCKET_CLI_VERSION']!,
249249
INLINED_SOCKET_CLI_VERSION_HASH:
250-
process.env['INLINED_SOCKET_CLI_VERSION_HASH'],
250+
process.env['INLINED_SOCKET_CLI_VERSION_HASH']!,
251251
}
252252

253253
// Create a Proxy that uses live process.env in VITEST mode and snapshot in production.

packages/cli/src/utils/dlx/resolve-binary.mts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type { DlxPackageSpec } from './spawn.mjs'
1313
*/
1414
export type BinaryResolution =
1515
| { type: 'local'; path: string }
16-
| { type: 'dlx'; packageSpec: DlxPackageSpec }
16+
| { type: 'dlx'; details: DlxPackageSpec }
1717

1818
/**
1919
* Resolve path for Coana CLI binary.
@@ -27,9 +27,10 @@ export function resolveCoana(): BinaryResolution {
2727

2828
return {
2929
type: 'dlx',
30-
packageSpec: {
30+
details: {
3131
name: '@coana-tech/cli',
3232
version: `~${ENV.INLINED_SOCKET_CLI_COANA_VERSION}`,
33+
binaryName: 'coana',
3334
},
3435
}
3536
}
@@ -46,9 +47,10 @@ export function resolveCdxgen(): BinaryResolution {
4647

4748
return {
4849
type: 'dlx',
49-
packageSpec: {
50+
details: {
5051
name: '@cyclonedx/cdxgen',
51-
version: `${ENV.INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION}`,
52+
version: ENV.INLINED_SOCKET_CLI_CYCLONEDX_CDXGEN_VERSION,
53+
binaryName: 'cdxgen',
5254
},
5355
}
5456
}
@@ -88,9 +90,10 @@ export function resolveSfw(): BinaryResolution | { type: 'npx' } {
8890
export function resolveSynp(): BinaryResolution {
8991
return {
9092
type: 'dlx',
91-
packageSpec: {
93+
details: {
9294
name: 'synp',
93-
version: `${ENV.INLINED_SOCKET_CLI_SYNP_VERSION}`,
95+
version: ENV.INLINED_SOCKET_CLI_SYNP_VERSION,
96+
binaryName: 'synp',
9497
},
9598
}
9699
}

packages/cli/src/utils/dlx/spawn.mts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export type DlxOptions = ShadowBinOptions & {
4343
}
4444

4545
export type DlxPackageSpec = {
46+
binaryName?: string | undefined
4647
name: string
4748
version: string
4849
}
@@ -65,8 +66,9 @@ export async function spawnDlx(
6566
const result = await dlxPackage(
6667
args,
6768
{
68-
force,
6969
package: packageString,
70+
binaryName: packageSpec.binaryName,
71+
force,
7072
spawnOptions: shadowOptions,
7173
},
7274
spawnExtra,
@@ -78,7 +80,7 @@ export async function spawnDlx(
7880
}
7981

8082
/**
81-
* Helper to spawn coana with dlx.
83+
* Helper to spawn Coana with dlx.
8284
* Returns a CResult with stdout extraction for backward compatibility.
8385
*
8486
* If SOCKET_CLI_COANA_LOCAL_PATH environment variable is set, uses the local
@@ -148,7 +150,7 @@ export async function spawnCoanaDlx(
148150

149151
// Use dlx version.
150152
const result = await spawnDlx(
151-
resolution.packageSpec,
153+
resolution.details,
152154
args,
153155
{
154156
force: true,
@@ -222,7 +224,7 @@ export async function spawnCdxgenDlx(
222224

223225
// Use dlx version.
224226
return await spawnDlx(
225-
resolution.packageSpec,
227+
resolution.details,
226228
args,
227229
{ force: false, ...options },
228230
spawnExtra,

packages/cli/src/utils/preflight/downloads.mts

Lines changed: 17 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@
77
* This runs asynchronously and never blocks the main CLI execution.
88
*/
99

10-
import { existsSync, promises as fs } from 'node:fs'
10+
import { existsSync } from 'node:fs'
1111
import path from 'node:path'
1212

13-
import { spawn } from '@socketsecurity/lib/spawn'
13+
import { downloadPackage } from '@socketsecurity/lib/dlx-package'
1414

1515
import ENV from '../../constants/env.mts'
1616
import { getSocketHomePath } from '../dlx/binary.mts'
17-
import { detectPackageManager } from '../shadow/runner.mts'
1817

1918
/**
2019
* Check if a package is already cached by the package manager.
@@ -32,60 +31,6 @@ function isPackageCached(packageName: string): boolean {
3231
return existsSync(cacheMarker)
3332
}
3433

35-
/**
36-
* Mark a package as cached to avoid redundant downloads.
37-
*/
38-
async function markPackageCached(packageName: string): Promise<void> {
39-
const socketHome = getSocketHomePath()
40-
const cacheDir = path.join(socketHome, 'cache', 'preflight')
41-
const cacheMarker = path.join(cacheDir, packageName.replace(/[/@]/g, '-'))
42-
43-
try {
44-
await fs.mkdir(cacheDir, { recursive: true })
45-
await fs.writeFile(cacheMarker, new Date().toISOString())
46-
} catch {
47-
// Silently fail - not critical.
48-
}
49-
}
50-
51-
/**
52-
* Download a package via package manager dlx in the background.
53-
*/
54-
async function downloadPackage(packageSpec: string): Promise<void> {
55-
try {
56-
const agent = await detectPackageManager()
57-
58-
let dlxCommand: string
59-
let dlxArgs: string[]
60-
61-
switch (agent) {
62-
case 'pnpm':
63-
dlxCommand = 'pnpm'
64-
dlxArgs = ['dlx', packageSpec, '--help']
65-
break
66-
case 'yarn':
67-
dlxCommand = 'yarn'
68-
dlxArgs = ['dlx', packageSpec, '--help']
69-
break
70-
default:
71-
dlxCommand = 'npx'
72-
dlxArgs = [packageSpec, '--help']
73-
break
74-
}
75-
76-
// Run in background with output discarded.
77-
await spawn(dlxCommand, dlxArgs, {
78-
stdio: 'ignore',
79-
timeout: 30_000, // 30 second timeout.
80-
})
81-
82-
// Mark as cached.
83-
await markPackageCached(packageSpec)
84-
} catch {
85-
// Silently fail - downloads will happen on-demand if needed.
86-
}
87-
}
88-
8934
/**
9035
* Run preflight downloads in the background.
9136
* This never blocks or throws errors.
@@ -98,14 +43,14 @@ export function runPreflightDownloads(): void {
9843

9944
// Run asynchronously in the background.
10045
void (async () => {
101-
const downloads: Array<{ name: string; spec: string }> = []
46+
const downloads: Array<{ packageSpec: string; binaryName?: string }> = []
10247

10348
// @coana-tech/cli preflight.
10449
const coanaVersion = ENV.INLINED_SOCKET_CLI_COANA_VERSION
10550
if (coanaVersion) {
10651
const coanaSpec = `@coana-tech/cli@~${coanaVersion}`
10752
if (!isPackageCached(coanaSpec)) {
108-
downloads.push({ name: '@coana-tech/cli', spec: coanaSpec })
53+
downloads.push({ packageSpec: coanaSpec, binaryName: 'coana' })
10954
}
11055
}
11156

@@ -114,13 +59,21 @@ export function runPreflightDownloads(): void {
11459
if (cliAiVersion) {
11560
const cliAiSpec = `@socketbin/cli-ai@^${cliAiVersion}`
11661
if (!isPackageCached(cliAiSpec)) {
117-
downloads.push({ name: '@socketbin/cli-ai', spec: cliAiSpec })
62+
downloads.push({ packageSpec: cliAiSpec, binaryName: 'cli-ai' })
11863
}
11964
}
12065

121-
// Download in background (fire and forget).
122-
for (const pkg of downloads) {
123-
void downloadPackage(pkg.spec)
124-
}
66+
try {
67+
// Download in background (fire and forget).
68+
await Promise.all(
69+
downloads.map(p =>
70+
downloadPackage({
71+
package: p.packageSpec,
72+
binaryName: p.binaryName,
73+
force: false,
74+
})
75+
)
76+
)
77+
} catch {}
12578
})()
12679
}

0 commit comments

Comments
 (0)