@@ -3,12 +3,21 @@ import path from 'node:path'
33import { logger } from '@socketsecurity/registry/lib/logger'
44
55import { handleScanReach } from './handle-scan-reach.mts'
6+ import { reachabilityFlags } from './reachability-flags.mts'
7+ import { suggestTarget } from './suggest_target.mts'
68import constants from '../../constants.mts'
7- import { commonFlags , outputFlags } from '../../flags.mts'
9+ import { type MeowFlags , commonFlags , outputFlags } from '../../flags.mts'
810import { checkCommandInput } from '../../utils/check-input.mts'
11+ import { cmdFlagValueToArray } from '../../utils/cmd.mts'
12+ import { determineOrgSlug } from '../../utils/determine-org-slug.mts'
13+ import {
14+ type EcosystemString ,
15+ getEcosystemChoicesForMeow ,
16+ } from '../../utils/ecosystem.mts'
917import { getOutputKind } from '../../utils/get-output-kind.mts'
1018import { meowOrExit } from '../../utils/meow-with-subcommands.mts'
1119import { getFlagListOutput } from '../../utils/output-formatting.mts'
20+ import { hasDefaultToken } from '../../utils/sdk.mts'
1221
1322import type { CliCommandConfig } from '../../utils/meow-with-subcommands.mts'
1423
@@ -21,18 +30,43 @@ const config: CliCommandConfig = {
2130 flags : {
2231 ...commonFlags ,
2332 ...outputFlags ,
33+ cwd : {
34+ type : 'string' ,
35+ description : 'working directory, defaults to process.cwd()' ,
36+ } ,
37+ org : {
38+ type : 'string' ,
39+ description :
40+ 'Force override the organization slug, overrides the default org from config' ,
41+ } ,
42+ ...reachabilityFlags ,
2443 } ,
25- help : ( command , config ) => `
44+ help : ( command , config ) => {
45+ const allFlags = config . flags || { }
46+ const generalFlags : MeowFlags = { }
47+
48+ // Separate general flags from reachability flags
49+ for ( const [ key , value ] of Object . entries ( allFlags ) ) {
50+ if ( ! reachabilityFlags [ key ] ) {
51+ generalFlags [ key ] = value
52+ }
53+ }
54+
55+ return `
2656 Usage
2757 $ ${ command } [options] [CWD=.]
2858
2959 Options
30- ${ getFlagListOutput ( config . flags ) }
60+ ${ getFlagListOutput ( generalFlags ) }
61+
62+ Reachability Options
63+ ${ getFlagListOutput ( reachabilityFlags ) }
3164
3265 Examples
3366 $ ${ command }
3467 $ ${ command } ./proj
35- ` ,
68+ `
69+ } ,
3670}
3771
3872export const cmdScanReach = {
@@ -53,30 +87,108 @@ async function run(
5387 parentName,
5488 } )
5589
56- const { dryRun, json, markdown } = cli . flags
90+ const {
91+ cwd : cwdOverride ,
92+ dryRun = false ,
93+ interactive = true ,
94+ json,
95+ markdown,
96+ org : orgFlag ,
97+ reachAnalysisMemoryLimit,
98+ reachAnalysisTimeout,
99+ reachContinueOnFailingProjects,
100+ reachDisableAnalytics,
101+ } = cli . flags as {
102+ cwd : string
103+ dryRun : boolean
104+ interactive : boolean
105+ json : boolean
106+ markdown : boolean
107+ org : string
108+ reachAnalysisTimeout ?: number
109+ reachAnalysisMemoryLimit ?: number
110+ reachContinueOnFailingProjects : boolean
111+ reachDisableAnalytics : boolean
112+ }
113+
114+ // Process comma-separated values for isMultiple flags
115+ const reachEcosystemsRaw = cmdFlagValueToArray ( cli . flags [ 'reachEcosystems' ] )
116+ const reachExcludePaths = cmdFlagValueToArray ( cli . flags [ 'reachExcludePaths' ] )
117+
118+ // Validate ecosystem values
119+ const validEcosystems = getEcosystemChoicesForMeow ( )
120+ const reachEcosystems : EcosystemString [ ] = [ ]
121+ for ( const ecosystem of reachEcosystemsRaw ) {
122+ if ( ! validEcosystems . includes ( ecosystem ) ) {
123+ throw new Error (
124+ `Invalid ecosystem: "${ ecosystem } ". Valid values are: ${ validEcosystems . join ( ', ' ) } ` ,
125+ )
126+ }
127+ reachEcosystems . push ( ecosystem as EcosystemString )
128+ }
57129
58130 const outputKind = getOutputKind ( json , markdown )
59131
60- const wasValidInput = checkCommandInput ( outputKind )
61- if ( ! wasValidInput ) {
62- return
63- }
132+ const cwd =
133+ cwdOverride && cwdOverride !== 'process.cwd()'
134+ ? path . resolve ( process . cwd ( ) , String ( cwdOverride ) )
135+ : process . cwd ( )
64136
137+ // Accept zero or more paths. Default to cwd() if none given.
138+ let targets = cli . input || [ cwd ]
139+
140+ // Check if we're in dry-run mode first
65141 if ( dryRun ) {
66142 logger . log ( DRY_RUN_BAILING_NOW )
67143 return
68144 }
69145
70- const { unknownFlags } = cli
146+ // Use suggestTarget if no targets specified and in interactive mode
147+ if ( ! targets . length && ! dryRun && interactive ) {
148+ targets = await suggestTarget ( )
149+ }
150+
151+ // Determine org slug
152+ const [ orgSlug ] = await determineOrgSlug (
153+ String ( orgFlag || '' ) ,
154+ interactive ,
155+ dryRun ,
156+ )
157+
158+ const hasApiToken = hasDefaultToken ( )
71159
72- let [ cwd = '.' ] = cli . input
73- // Note: path.resolve vs .join:
74- // If given path is absolute then cwd should not affect it.
75- cwd = path . resolve ( process . cwd ( ) , cwd )
160+ const wasValidInput = checkCommandInput (
161+ outputKind ,
162+ {
163+ nook : true ,
164+ test : ! ! orgSlug ,
165+ message : 'Org name by default setting, --org, or auto-discovered' ,
166+ fail : 'missing' ,
167+ } ,
168+ {
169+ nook : true ,
170+ test : hasApiToken ,
171+ message : 'This command requires an API token for access' ,
172+ fail : 'missing (try `socket login`)' ,
173+ } ,
174+ )
175+ if ( ! wasValidInput ) {
176+ return
177+ }
76178
77179 await handleScanReach ( {
78180 cwd,
181+ orgSlug,
79182 outputKind,
80- unknownFlags,
183+ targets,
184+ interactive,
185+ reachabilityOptions : {
186+ reachContinueOnFailingProjects : Boolean ( reachContinueOnFailingProjects ) ,
187+ reachDisableAnalytics : Boolean ( reachDisableAnalytics ) ,
188+ reachAnalysisTimeout : Number ( reachAnalysisTimeout ) ,
189+ reachAnalysisMemoryLimit : Number ( reachAnalysisMemoryLimit ) ,
190+ reachEcosystems,
191+ reachExcludePaths,
192+ } ,
81193 } )
82194}
0 commit comments