37audits-cli 0.0.0-stage → 1.0.2

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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +169 -2
  3. package/dist/cli.cjs +96 -0
  4. package/package.json +52 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 37 Audits
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,170 @@
1
- # Temporary Holding Version
1
+ # 37 Audits CLI
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `37audits` runs audits, reads results and wraps deploys in maintenance
4
+ windows from the terminal or a CI/CD pipeline, against the
5
+ [37 Audits](https://www.37audits.com) API.
6
+
7
+ ```sh
8
+ npm install -g 37audits-cli # or: npx 37audits-cli ...
9
+ 37audits login
10
+ 37audits audit run --url https://www.example.com --wait
11
+ ```
12
+
13
+ Requires Node.js 22 or newer.
14
+
15
+ ## Authentication
16
+
17
+ Two kinds of credential work everywhere:
18
+
19
+ - **Your account** (interactive): `37audits login` opens the browser, you
20
+ approve the CLI on the 37 Audits consent page, and the session is stored
21
+ locally. It refreshes itself; `37audits logout` removes it.
22
+ - **An API key** (CI): create one under *Organization → API keys* on
23
+ 37audits.com, then either set `THIRTYSEVENAUDITS_API_KEY` in the
24
+ pipeline (no login needed), pass `--api-key` on any command, or store it
25
+ once with `37audits login --api-key tsa_live_...`.
26
+
27
+ Precedence: `--api-key`, then `THIRTYSEVENAUDITS_API_KEY`, then the stored
28
+ credential. Credentials live in `~/.config/37audits/credentials.json`
29
+ (`$XDG_CONFIG_HOME/37audits` or `%APPDATA%\37audits`) with mode `0600`.
30
+
31
+ Maintenance windows need an organization **admin**: an admin-role API key,
32
+ or an account with the owner or admin role.
33
+
34
+ `37audits whoami` shows which credential is in use and whom it belongs to.
35
+
36
+ ## Commands
37
+
38
+ Every command accepts `--url-id <uuid>` or `--url <address>` to name a
39
+ monitored URL; `--url` is resolved to the matching monitored URL of your
40
+ organizations. The `mw` commands take `--project <uuid>` instead, or derive
41
+ the project from `--url-id` / `--url`.
42
+
43
+ | Command | What it does |
44
+ | --- | --- |
45
+ | `login [--api-key <key>]` | Browser sign-in, or store an API key |
46
+ | `logout` | Remove the stored credential |
47
+ | `whoami` | Show the current credential and its owner |
48
+ | `audit run [--note <text>] [--auditor <id>]... [--wait] [--fail-on fail\|warning]` | Start a run; `--wait` polls until every auditor finished |
49
+ | `audit latest [--fail-on fail\|warning]` | Each auditor's newest result |
50
+ | `mw start [--reason <text>] [--run-auditors] [--ends-in 30m\|--ends-at <iso>] [--wait]` | Start a maintenance window now, optionally auditing first |
51
+ | `mw end [<id>]` | End the given or the active window (runs the "after" audit) |
52
+ | `mw list [--status scheduled\|active\|ended] [--limit n]` | List a project's windows |
53
+
54
+ `--help` on any command documents its options, JSON output and error
55
+ codes.
56
+
57
+ ### `--wait`, `--timeout`, `--interval`
58
+
59
+ `audit run --wait` and `mw start --run-auditors --wait` poll the run until
60
+ no result is pending, at most `--timeout` (default `120s`, e.g. `3m`),
61
+ every `--interval` (default `5s`). Running out of time is a warning, not an
62
+ error: the run keeps going on the server and the results still land.
63
+
64
+ ### `--fail-on`
65
+
66
+ `audit run --fail-on fail` (implies `--wait`) and `audit latest --fail-on
67
+ fail` exit with code **2** when any auditor reports `fail` or `error`;
68
+ `--fail-on warning` also counts warnings. Pending results never trip the
69
+ gate. With `--fail-on`, a `--wait` timeout is an error (exit 1).
70
+
71
+ ## Output
72
+
73
+ When stdout is a terminal the CLI prints tables and sentences. With
74
+ `--json`, or whenever stdout is not a TTY (CI, pipes), it prints JSON on
75
+ stdout and errors as `{"error":{"message":"...","code":"..."}}` on stderr.
76
+ `-q`/`--quiet` also silences spinners and status lines.
77
+
78
+ Exit codes: `0` success, `1` error, `2` a `--fail-on` gate tripped, `130`
79
+ cancelled with Ctrl-C.
80
+
81
+ ## CI/CD: wrap a deploy in a maintenance window
82
+
83
+ While a maintenance window is active, 37 Audits pauses scheduled audits of
84
+ the project, so a restart mid-deploy does not raise false alerts. Starting
85
+ it with `--run-auditors` records a "before" run, ending it records an
86
+ "after" run, and both carry the `--reason` on the timeline.
87
+
88
+ GitHub Actions:
89
+
90
+ ```yaml
91
+ - name: Start maintenance window
92
+ id: maintenance
93
+ continue-on-error: true # never block a deploy on monitoring
94
+ env:
95
+ THIRTYSEVENAUDITS_API_KEY: ${{ secrets.THIRTYSEVENAUDITS_API_KEY }}
96
+ run: |
97
+ npx -y 37audits-cli@1.0.2 mw start \
98
+ --project "${{ secrets.THIRTYSEVENAUDITS_PROJECT_ID }}" \
99
+ --run-auditors --wait --ends-in 30m \
100
+ --reason "Deploy of my-app at ${GITHUB_REF_NAME}@${GITHUB_SHA::7}"
101
+
102
+ # ... deploy ...
103
+
104
+ - name: End maintenance window
105
+ if: always() && steps.maintenance.outputs.window_id != ''
106
+ continue-on-error: true
107
+ env:
108
+ THIRTYSEVENAUDITS_API_KEY: ${{ secrets.THIRTYSEVENAUDITS_API_KEY }}
109
+ run: npx -y 37audits-cli@1.0.2 mw end "${{ steps.maintenance.outputs.window_id }}" --project "${{ secrets.THIRTYSEVENAUDITS_PROJECT_ID }}"
110
+ ```
111
+
112
+ `mw start` writes `window_id` (and `audit run` writes `group_id`) to
113
+ `$GITHUB_OUTPUT` when it is set, so a later step can read
114
+ `steps.<id>.outputs.window_id`. Always give the window an end
115
+ (`--ends-in`) sized to your deploy: if the pipeline dies, 37 Audits ends
116
+ the window itself and runs the "after" audit.
117
+
118
+ Where there is no post-deploy hook (for example a Vercel build), run only
119
+ `mw start --ends-in 10m` before the build and let the cap end it.
120
+
121
+ ## Environment variables
122
+
123
+ | Variable | Purpose |
124
+ | --- | --- |
125
+ | `THIRTYSEVENAUDITS_API_KEY` | API key used when no `--api-key` is given |
126
+ | `THIRTYSEVENAUDITS_API_BASE_URL` | API base URL (default `https://api.37audits.com`) |
127
+ | `THIRTYSEVENAUDITS_AUTH_URL` | OAuth server for `login` (default: 37 Audits production) |
128
+ | `THIRTYSEVENAUDITS_OAUTH_CLIENT_ID` | Override the CLI's OAuth client id (local development) |
129
+ | `XDG_CONFIG_HOME` | Where the credentials file lives |
130
+ | `CI` / `GITHUB_ACTIONS` | Treated as non-interactive: no prompts, JSON output |
131
+
132
+ ## Development
133
+
134
+ ```sh
135
+ npm ci
136
+ npm run dev -- whoami # run from source (tsx)
137
+ npm test # unit tests (mocked API)
138
+ npm run lint && npm run typecheck
139
+ npm run build && node dist/cli.cjs --help
140
+ THIRTYSEVENAUDITS_API_KEY=tsa_test_... npm run test:e2e # read-only, real API
141
+ ```
142
+
143
+ To use a local 37 Audits stack, point the CLI at it:
144
+
145
+ ```sh
146
+ export THIRTYSEVENAUDITS_API_BASE_URL=http://127.0.0.1:8080
147
+ export THIRTYSEVENAUDITS_AUTH_URL=http://127.0.0.1:54321/auth/v1
148
+ export THIRTYSEVENAUDITS_OAUTH_CLIENT_ID=<a public client registered on local Supabase>
149
+ ```
150
+
151
+ Browser login is an OAuth 2.1 authorization-code flow with PKCE against
152
+ Supabase Auth, the 37 Audits authorization server. The CLI is a public
153
+ client whose redirect URIs are `http://127.0.0.1:37371/callback`, `:37372`
154
+ and `:37373` (the first free one is used; Supabase matches redirect URIs
155
+ exactly, hence fixed ports).
156
+
157
+ ### Releasing
158
+
159
+ Bump `version` in `package.json`, commit, then push a matching tag:
160
+
161
+ ```sh
162
+ git tag v1.0.1 && git push --tags
163
+ ```
164
+
165
+ `release.yml` runs the checks and publishes to npm with provenance through
166
+ npm trusted publishing (no token in the repository).
167
+
168
+ ## License
169
+
170
+ [MIT](./LICENSE)
package/dist/cli.cjs ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ "use strict";var Cr=Object.create;var se=Object.defineProperty;var xr=Object.getOwnPropertyDescriptor;var Or=Object.getOwnPropertyNames;var Ar=Object.getPrototypeOf,vr=Object.prototype.hasOwnProperty;var U=(n,t)=>()=>{try{return t||n((t={exports:{}}).exports,t),t.exports}catch(e){throw t=0,e}};var Er=(n,t,e,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of Or(t))!vr.call(n,i)&&i!==e&&se(n,i,{get:()=>t[i],enumerable:!(r=xr(t,i))||r.enumerable});return n};var B=(n,t,e)=>(e=n!=null?Cr(Ar(n)):{},Er(t||!n||!n.__esModule?se(e,"default",{value:n,enumerable:!0}):e,n));var et=U(kt=>{var st=class extends Error{constructor(t,e,r){super(r),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name,this.code=e,this.exitCode=t,this.nestedError=void 0}},$t=class extends st{constructor(t){super(1,"commander.invalidArgument",t),Error.captureStackTrace(this,this.constructor),this.name=this.constructor.name}};kt.CommanderError=st;kt.InvalidArgumentError=$t});var at=U(Pt=>{var{InvalidArgumentError:Tr}=et(),It=class{constructor(t,e){switch(this.description=e||"",this.variadic=!1,this.parseArg=void 0,this.defaultValue=void 0,this.defaultValueDescription=void 0,this.argChoices=void 0,t[0]){case"<":this.required=!0,this._name=t.slice(1,-1);break;case"[":this.required=!1,this._name=t.slice(1,-1);break;default:this.required=!0,this._name=t;break}this._name.endsWith("...")&&(this.variadic=!0,this._name=this._name.slice(0,-3))}name(){return this._name}_collectValue(t,e){return e===this.defaultValue||!Array.isArray(e)?[t]:(e.push(t),e)}default(t,e){return this.defaultValue=t,this.defaultValueDescription=e,this}argParser(t){return this.parseArg=t,this}choices(t){return this.argChoices=t.slice(),this.parseArg=(e,r)=>{if(!this.argChoices.includes(e))throw new Tr(`Allowed choices are ${this.argChoices.join(", ")}.`);return this.variadic?this._collectValue(e,r):e},this}argRequired(){return this.required=!0,this}argOptional(){return this.required=!1,this}};function Sr(n){let t=n.name()+(n.variadic===!0?"...":"");return n.required?"<"+t+">":"["+t+"]"}Pt.Argument=It;Pt.humanReadableArgName=Sr});var Ht=U(jt=>{var{humanReadableArgName:$r}=at(),Rt=class{constructor(){this.helpWidth=void 0,this.minWidthToWrap=40,this.sortSubcommands=!1,this.sortOptions=!1,this.showGlobalOptions=!1}prepareContext(t){this.helpWidth=this.helpWidth??t.helpWidth??80}visibleCommands(t){let e=t.commands.filter(i=>!i._hidden),r=t._getHelpCommand();return r&&!r._hidden&&e.push(r),this.sortSubcommands&&e.sort((i,o)=>i.name().localeCompare(o.name())),e}compareOptions(t,e){let r=i=>i.short?i.short.replace(/^-/,""):i.long.replace(/^--/,"");return r(t).localeCompare(r(e))}visibleOptions(t){let e=t.options.filter(i=>!i.hidden),r=t._getHelpOption();if(r&&!r.hidden){let i=r.short&&t._findOption(r.short),o=r.long&&t._findOption(r.long);!i&&!o?e.push(r):r.long&&!o?e.push(t.createOption(r.long,r.description)):r.short&&!i&&e.push(t.createOption(r.short,r.description))}return this.sortOptions&&e.sort(this.compareOptions),e}visibleGlobalOptions(t){if(!this.showGlobalOptions)return[];let e=[];for(let r=t.parent;r;r=r.parent){let i=r.options.filter(o=>!o.hidden);e.push(...i)}return this.sortOptions&&e.sort(this.compareOptions),e}visibleArguments(t){return t._argsDescription&&t.registeredArguments.forEach(e=>{e.description=e.description||t._argsDescription[e.name()]||""}),t.registeredArguments.find(e=>e.description)?t.registeredArguments:[]}subcommandTerm(t){let e=t.registeredArguments.map(r=>$r(r)).join(" ");return t._name+(t._aliases[0]?"|"+t._aliases[0]:"")+(t.options.length?" [options]":"")+(e?" "+e:"")}optionTerm(t){return t.flags}argumentTerm(t){return t.name()}longestSubcommandTermLength(t,e){return e.visibleCommands(t).reduce((r,i)=>Math.max(r,this.displayWidth(e.styleSubcommandTerm(e.subcommandTerm(i)))),0)}longestOptionTermLength(t,e){return e.visibleOptions(t).reduce((r,i)=>Math.max(r,this.displayWidth(e.styleOptionTerm(e.optionTerm(i)))),0)}longestGlobalOptionTermLength(t,e){return e.visibleGlobalOptions(t).reduce((r,i)=>Math.max(r,this.displayWidth(e.styleOptionTerm(e.optionTerm(i)))),0)}longestArgumentTermLength(t,e){return e.visibleArguments(t).reduce((r,i)=>Math.max(r,this.displayWidth(e.styleArgumentTerm(e.argumentTerm(i)))),0)}commandUsage(t){let e=t._name;t._aliases[0]&&(e=e+"|"+t._aliases[0]);let r="";for(let i=t.parent;i;i=i.parent)r=i.name()+" "+r;return r+e+" "+t.usage()}commandDescription(t){return t.description()}subcommandDescription(t){return t.summary()||t.description()}optionDescription(t){let e=[];if(t.argChoices&&e.push(`choices: ${t.argChoices.map(r=>JSON.stringify(r)).join(", ")}`),t.defaultValue!==void 0&&(t.required||t.optional||t.isBoolean()&&typeof t.defaultValue=="boolean")&&e.push(`default: ${t.defaultValueDescription||JSON.stringify(t.defaultValue)}`),t.presetArg!==void 0&&t.optional&&e.push(`preset: ${JSON.stringify(t.presetArg)}`),t.envVar!==void 0&&e.push(`env: ${t.envVar}`),e.length>0){let r=`(${e.join(", ")})`;return t.description?`${t.description} ${r}`:r}return t.description}argumentDescription(t){let e=[];if(t.argChoices&&e.push(`choices: ${t.argChoices.map(r=>JSON.stringify(r)).join(", ")}`),t.defaultValue!==void 0&&e.push(`default: ${t.defaultValueDescription||JSON.stringify(t.defaultValue)}`),e.length>0){let r=`(${e.join(", ")})`;return t.description?`${t.description} ${r}`:r}return t.description}formatItemList(t,e,r){return e.length===0?[]:[r.styleTitle(t),...e,""]}groupItems(t,e,r){let i=new Map;return t.forEach(o=>{let s=r(o);i.has(s)||i.set(s,[])}),e.forEach(o=>{let s=r(o);i.has(s)||i.set(s,[]),i.get(s).push(o)}),i}formatHelp(t,e){let r=e.padWidth(t,e),i=e.helpWidth??80;function o(d,m){return e.formatItem(d,r,m,e)}let s=[`${e.styleTitle("Usage:")} ${e.styleUsage(e.commandUsage(t))}`,""],a=e.commandDescription(t);a.length>0&&(s=s.concat([e.boxWrap(e.styleCommandDescription(a),i),""]));let l=e.visibleArguments(t).map(d=>o(e.styleArgumentTerm(e.argumentTerm(d)),e.styleArgumentDescription(e.argumentDescription(d))));if(s=s.concat(this.formatItemList("Arguments:",l,e)),this.groupItems(t.options,e.visibleOptions(t),d=>d.helpGroupHeading??"Options:").forEach((d,m)=>{let p=d.map(f=>o(e.styleOptionTerm(e.optionTerm(f)),e.styleOptionDescription(e.optionDescription(f))));s=s.concat(this.formatItemList(m,p,e))}),e.showGlobalOptions){let d=e.visibleGlobalOptions(t).map(m=>o(e.styleOptionTerm(e.optionTerm(m)),e.styleOptionDescription(e.optionDescription(m))));s=s.concat(this.formatItemList("Global Options:",d,e))}return this.groupItems(t.commands,e.visibleCommands(t),d=>d.helpGroup()||"Commands:").forEach((d,m)=>{let p=d.map(f=>o(e.styleSubcommandTerm(e.subcommandTerm(f)),e.styleSubcommandDescription(e.subcommandDescription(f))));s=s.concat(this.formatItemList(m,p,e))}),s.join(`
3
+ `)}displayWidth(t){return ae(t).length}styleTitle(t){return t}styleUsage(t){return t.split(" ").map(e=>e==="[options]"?this.styleOptionText(e):e==="[command]"?this.styleSubcommandText(e):e[0]==="["||e[0]==="<"?this.styleArgumentText(e):this.styleCommandText(e)).join(" ")}styleCommandDescription(t){return this.styleDescriptionText(t)}styleOptionDescription(t){return this.styleDescriptionText(t)}styleSubcommandDescription(t){return this.styleDescriptionText(t)}styleArgumentDescription(t){return this.styleDescriptionText(t)}styleDescriptionText(t){return t}styleOptionTerm(t){return this.styleOptionText(t)}styleSubcommandTerm(t){return t.split(" ").map(e=>e==="[options]"?this.styleOptionText(e):e[0]==="["||e[0]==="<"?this.styleArgumentText(e):this.styleSubcommandText(e)).join(" ")}styleArgumentTerm(t){return this.styleArgumentText(t)}styleOptionText(t){return t}styleArgumentText(t){return t}styleSubcommandText(t){return t}styleCommandText(t){return t}padWidth(t,e){return Math.max(e.longestOptionTermLength(t,e),e.longestGlobalOptionTermLength(t,e),e.longestSubcommandTermLength(t,e),e.longestArgumentTermLength(t,e))}preformatted(t){return/\n[^\S\r\n]/.test(t)}formatItem(t,e,r,i){let s=" ".repeat(2);if(!r)return s+t;let a=t.padEnd(e+t.length-i.displayWidth(t)),l=2,u=(this.helpWidth??80)-e-l-2,d;return u<this.minWidthToWrap||i.preformatted(r)?d=r:d=i.boxWrap(r,u).replace(/\n/g,`
4
+ `+" ".repeat(e+l)),s+a+" ".repeat(l)+d.replace(/\n/g,`
5
+ ${s}`)}boxWrap(t,e){if(e<this.minWidthToWrap)return t;let r=t.split(/\r\n|\n/),i=/[\s]*[^\s]+/g,o=[];return r.forEach(s=>{let a=s.match(i);if(a===null){o.push("");return}let l=[a.shift()],c=this.displayWidth(l[0]);a.forEach(u=>{let d=this.displayWidth(u);if(c+d<=e){l.push(u),c+=d;return}o.push(l.join(""));let m=u.trimStart();l=[m],c=this.displayWidth(m)}),o.push(l.join(""))}),o.join(`
6
+ `)}};function ae(n){let t=/\x1b\[\d*(;\d*)*m/g;return n.replace(t,"")}jt.Help=Rt;jt.stripColor=ae});var Vt=U(Lt=>{var{InvalidArgumentError:kr}=et(),Ut=class{constructor(t,e){this.flags=t,this.description=e||"",this.required=t.includes("<"),this.optional=t.includes("["),this.variadic=/\w\.\.\.[>\]]$/.test(t),this.mandatory=!1;let r=Ir(t);this.short=r.shortFlag,this.long=r.longFlag,this.negate=!1,this.long&&(this.negate=this.long.startsWith("--no-")),this.defaultValue=void 0,this.defaultValueDescription=void 0,this.presetArg=void 0,this.envVar=void 0,this.parseArg=void 0,this.hidden=!1,this.argChoices=void 0,this.conflictsWith=[],this.implied=void 0,this.helpGroupHeading=void 0}default(t,e){return this.defaultValue=t,this.defaultValueDescription=e,this}preset(t){return this.presetArg=t,this}conflicts(t){return this.conflictsWith=this.conflictsWith.concat(t),this}implies(t){let e=t;return typeof t=="string"&&(e={[t]:!0}),this.implied=Object.assign(this.implied||{},e),this}env(t){return this.envVar=t,this}argParser(t){return this.parseArg=t,this}makeOptionMandatory(t=!0){return this.mandatory=!!t,this}hideHelp(t=!0){return this.hidden=!!t,this}_collectValue(t,e){return e===this.defaultValue||!Array.isArray(e)?[t]:(e.push(t),e)}choices(t){return this.argChoices=t.slice(),this.parseArg=(e,r)=>{if(!this.argChoices.includes(e))throw new kr(`Allowed choices are ${this.argChoices.join(", ")}.`);return this.variadic?this._collectValue(e,r):e},this}name(){return this.long?this.long.replace(/^--/,""):this.short.replace(/^-/,"")}attributeName(){return this.negate?ue(this.name().replace(/^no-/,"")):ue(this.name())}helpGroup(t){return this.helpGroupHeading=t,this}is(t){return this.short===t||this.long===t}isBoolean(){return!this.required&&!this.optional&&!this.negate}},Nt=class{constructor(t){this.positiveOptions=new Map,this.negativeOptions=new Map,this.dualOptions=new Set,t.forEach(e=>{e.negate?this.negativeOptions.set(e.attributeName(),e):this.positiveOptions.set(e.attributeName(),e)}),this.negativeOptions.forEach((e,r)=>{this.positiveOptions.has(r)&&this.dualOptions.add(r)})}valueFromOption(t,e){let r=e.attributeName();if(!this.dualOptions.has(r))return!0;let i=this.negativeOptions.get(r).presetArg,o=i!==void 0?i:!1;return e.negate===(o===t)}};function ue(n){return n.split("-").reduce((t,e)=>t+e[0].toUpperCase()+e.slice(1))}function Ir(n){let t,e,r=/^-[^-]$/,i=/^--[^-]/,o=n.split(/[ |,]+/).concat("guard");if(r.test(o[0])&&(t=o.shift()),i.test(o[0])&&(e=o.shift()),!t&&r.test(o[0])&&(t=o.shift()),!t&&i.test(o[0])&&(t=e,e=o.shift()),o[0].startsWith("-")){let s=o[0],a=`option creation failed due to '${s}' in option flags '${n}'`;throw/^-[^-][^-]/.test(s)?new Error(`${a}
7
+ - a short flag is a single dash and a single character
8
+ - either use a single dash and a single character (for a short flag)
9
+ - or use a double dash for a long option (and can have two, like '--ws, --workspace')`):r.test(s)?new Error(`${a}
10
+ - too many short flags`):i.test(s)?new Error(`${a}
11
+ - too many long flags`):new Error(`${a}
12
+ - unrecognised flag format`)}if(t===void 0&&e===void 0)throw new Error(`option creation failed due to no flags found in '${n}'.`);return{shortFlag:t,longFlag:e}}Lt.Option=Ut;Lt.DualOptions=Nt});var ce=U(le=>{function Pr(n,t){if(Math.abs(n.length-t.length)>3)return Math.max(n.length,t.length);let e=[];for(let r=0;r<=n.length;r++)e[r]=[r];for(let r=0;r<=t.length;r++)e[0][r]=r;for(let r=1;r<=t.length;r++)for(let i=1;i<=n.length;i++){let o=1;n[i-1]===t[r-1]?o=0:o=1,e[i][r]=Math.min(e[i-1][r]+1,e[i][r-1]+1,e[i-1][r-1]+o),i>1&&r>1&&n[i-1]===t[r-2]&&n[i-2]===t[r-1]&&(e[i][r]=Math.min(e[i][r],e[i-2][r-2]+1))}return e[n.length][t.length]}function Rr(n,t){if(!t||t.length===0)return"";t=Array.from(new Set(t));let e=n.startsWith("--");e&&(n=n.slice(2),t=t.map(s=>s.slice(2)));let r=[],i=3,o=.4;return t.forEach(s=>{if(s.length<=1)return;let a=Pr(n,s),l=Math.max(n.length,s.length);(l-a)/l>o&&(a<i?(i=a,r=[s]):a===i&&r.push(s))}),r.sort((s,a)=>s.localeCompare(a)),e&&(r=r.map(s=>`--${s}`)),r.length>1?`
13
+ (Did you mean one of ${r.join(", ")}?)`:r.length===1?`
14
+ (Did you mean ${r[0]}?)`:""}le.suggestSimilar=Rr});var he=U(Wt=>{var jr=require("node:events").EventEmitter,Mt=require("node:child_process"),N=require("node:path"),ut=require("node:fs"),h=require("node:process"),{Argument:Hr,humanReadableArgName:Ur}=at(),{CommanderError:Gt}=et(),{Help:Nr,stripColor:Lr}=Ht(),{Option:de,DualOptions:Vr}=Vt(),{suggestSimilar:me}=ce(),Ft=class n extends jr{constructor(t){super(),this.commands=[],this.options=[],this.parent=null,this._allowUnknownOption=!1,this._allowExcessArguments=!1,this.registeredArguments=[],this._args=this.registeredArguments,this.args=[],this.rawArgs=[],this.processedArgs=[],this._scriptPath=null,this._name=t||"",this._optionValues={},this._optionValueSources={},this._storeOptionsAsProperties=!1,this._actionHandler=null,this._executableHandler=!1,this._executableFile=null,this._executableDir=null,this._defaultCommandName=null,this._exitCallback=null,this._aliases=[],this._combineFlagAndOptionalValue=!0,this._description="",this._summary="",this._argsDescription=void 0,this._enablePositionalOptions=!1,this._passThroughOptions=!1,this._lifeCycleHooks={},this._showHelpAfterError=!1,this._showSuggestionAfterError=!0,this._savedState=null,this._outputConfiguration={writeOut:e=>h.stdout.write(e),writeErr:e=>h.stderr.write(e),outputError:(e,r)=>r(e),getOutHelpWidth:()=>h.stdout.isTTY?h.stdout.columns:void 0,getErrHelpWidth:()=>h.stderr.isTTY?h.stderr.columns:void 0,getOutHasColors:()=>Dt()??(h.stdout.isTTY&&h.stdout.hasColors?.()),getErrHasColors:()=>Dt()??(h.stderr.isTTY&&h.stderr.hasColors?.()),stripColor:e=>Lr(e)},this._hidden=!1,this._helpOption=void 0,this._addImplicitHelpCommand=void 0,this._helpCommand=void 0,this._helpConfiguration={},this._helpGroupHeading=void 0,this._defaultCommandGroup=void 0,this._defaultOptionGroup=void 0}copyInheritedSettings(t){return this._outputConfiguration=t._outputConfiguration,this._helpOption=t._helpOption,this._helpCommand=t._helpCommand,this._helpConfiguration=t._helpConfiguration,this._exitCallback=t._exitCallback,this._storeOptionsAsProperties=t._storeOptionsAsProperties,this._combineFlagAndOptionalValue=t._combineFlagAndOptionalValue,this._allowExcessArguments=t._allowExcessArguments,this._enablePositionalOptions=t._enablePositionalOptions,this._showHelpAfterError=t._showHelpAfterError,this._showSuggestionAfterError=t._showSuggestionAfterError,this}_getCommandAndAncestors(){let t=[];for(let e=this;e;e=e.parent)t.push(e);return t}command(t,e,r){let i=e,o=r;typeof i=="object"&&i!==null&&(o=i,i=null),o=o||{};let[,s,a]=t.match(/([^ ]+) *(.*)/),l=this.createCommand(s);return i&&(l.description(i),l._executableHandler=!0),o.isDefault&&(this._defaultCommandName=l._name),l._hidden=!!(o.noHelp||o.hidden),l._executableFile=o.executableFile||null,a&&l.arguments(a),this._registerCommand(l),l.parent=this,l.copyInheritedSettings(this),i?this:l}createCommand(t){return new n(t)}createHelp(){return Object.assign(new Nr,this.configureHelp())}configureHelp(t){return t===void 0?this._helpConfiguration:(this._helpConfiguration=t,this)}configureOutput(t){return t===void 0?this._outputConfiguration:(this._outputConfiguration={...this._outputConfiguration,...t},this)}showHelpAfterError(t=!0){return typeof t!="string"&&(t=!!t),this._showHelpAfterError=t,this}showSuggestionAfterError(t=!0){return this._showSuggestionAfterError=!!t,this}addCommand(t,e){if(!t._name)throw new Error(`Command passed to .addCommand() must have a name
15
+ - specify the name in Command constructor or using .name()`);return e=e||{},e.isDefault&&(this._defaultCommandName=t._name),(e.noHelp||e.hidden)&&(t._hidden=!0),this._registerCommand(t),t.parent=this,t._checkForBrokenPassThrough(),this}createArgument(t,e){return new Hr(t,e)}argument(t,e,r,i){let o=this.createArgument(t,e);return typeof r=="function"?o.default(i).argParser(r):o.default(r),this.addArgument(o),this}arguments(t){return t.trim().split(/ +/).forEach(e=>{this.argument(e)}),this}addArgument(t){let e=this.registeredArguments.slice(-1)[0];if(e?.variadic)throw new Error(`only the last argument can be variadic '${e.name()}'`);if(t.required&&t.defaultValue!==void 0&&t.parseArg===void 0)throw new Error(`a default value for a required argument is never used: '${t.name()}'`);return this.registeredArguments.push(t),this}helpCommand(t,e){if(typeof t=="boolean")return this._addImplicitHelpCommand=t,t&&this._defaultCommandGroup&&this._initCommandGroup(this._getHelpCommand()),this;let r=t??"help [command]",[,i,o]=r.match(/([^ ]+) *(.*)/),s=e??"display help for command",a=this.createCommand(i);return a.helpOption(!1),o&&a.arguments(o),s&&a.description(s),this._addImplicitHelpCommand=!0,this._helpCommand=a,(t||e)&&this._initCommandGroup(a),this}addHelpCommand(t,e){return typeof t!="object"?(this.helpCommand(t,e),this):(this._addImplicitHelpCommand=!0,this._helpCommand=t,this._initCommandGroup(t),this)}_getHelpCommand(){return this._addImplicitHelpCommand??(this.commands.length&&!this._actionHandler&&!this._findCommand("help"))?(this._helpCommand===void 0&&this.helpCommand(void 0,void 0),this._helpCommand):null}hook(t,e){let r=["preSubcommand","preAction","postAction"];if(!r.includes(t))throw new Error(`Unexpected value for event passed to hook : '${t}'.
16
+ Expecting one of '${r.join("', '")}'`);return this._lifeCycleHooks[t]?this._lifeCycleHooks[t].push(e):this._lifeCycleHooks[t]=[e],this}exitOverride(t){return t?this._exitCallback=t:this._exitCallback=e=>{if(e.code!=="commander.executeSubCommandAsync")throw e},this}_exit(t,e,r){this._exitCallback&&this._exitCallback(new Gt(t,e,r)),h.exit(t)}action(t){let e=r=>{let i=this.registeredArguments.length,o=r.slice(0,i);return this._storeOptionsAsProperties?o[i]=this:o[i]=this.opts(),o.push(this),t.apply(this,o)};return this._actionHandler=e,this}createOption(t,e){return new de(t,e)}_callParseArg(t,e,r,i){try{return t.parseArg(e,r)}catch(o){if(o.code==="commander.invalidArgument"){let s=`${i} ${o.message}`;this.error(s,{exitCode:o.exitCode,code:o.code})}throw o}}_registerOption(t){let e=t.short&&this._findOption(t.short)||t.long&&this._findOption(t.long);if(e){let r=t.long&&this._findOption(t.long)?t.long:t.short;throw new Error(`Cannot add option '${t.flags}'${this._name&&` to command '${this._name}'`} due to conflicting flag '${r}'
17
+ - already used by option '${e.flags}'`)}this._initOptionGroup(t),this.options.push(t)}_registerCommand(t){let e=i=>[i.name()].concat(i.aliases()),r=e(t).find(i=>this._findCommand(i));if(r){let i=e(this._findCommand(r)).join("|"),o=e(t).join("|");throw new Error(`cannot add command '${o}' as already have command '${i}'`)}this._initCommandGroup(t),this.commands.push(t)}addOption(t){this._registerOption(t);let e=t.name(),r=t.attributeName();if(t.negate){let o=t.long.replace(/^--no-/,"--");this._findOption(o)||this.setOptionValueWithSource(r,t.defaultValue===void 0?!0:t.defaultValue,"default")}else t.defaultValue!==void 0&&this.setOptionValueWithSource(r,t.defaultValue,"default");let i=(o,s,a)=>{o==null&&t.presetArg!==void 0&&(o=t.presetArg);let l=this.getOptionValue(r);o!==null&&t.parseArg?o=this._callParseArg(t,o,l,s):o!==null&&t.variadic&&(o=t._collectValue(o,l)),o==null&&(t.negate?o=!1:t.isBoolean()||t.optional?o=!0:o=""),this.setOptionValueWithSource(r,o,a)};return this.on("option:"+e,o=>{let s=`error: option '${t.flags}' argument '${o}' is invalid.`;i(o,s,"cli")}),t.envVar&&this.on("optionEnv:"+e,o=>{let s=`error: option '${t.flags}' value '${o}' from env '${t.envVar}' is invalid.`;i(o,s,"env")}),this}_optionEx(t,e,r,i,o){if(typeof e=="object"&&e instanceof de)throw new Error("To add an Option object use addOption() instead of option() or requiredOption()");let s=this.createOption(e,r);if(s.makeOptionMandatory(!!t.mandatory),typeof i=="function")s.default(o).argParser(i);else if(i instanceof RegExp){let a=i;i=(l,c)=>{let u=a.exec(l);return u?u[0]:c},s.default(o).argParser(i)}else s.default(i);return this.addOption(s)}option(t,e,r,i){return this._optionEx({},t,e,r,i)}requiredOption(t,e,r,i){return this._optionEx({mandatory:!0},t,e,r,i)}combineFlagAndOptionalValue(t=!0){return this._combineFlagAndOptionalValue=!!t,this}allowUnknownOption(t=!0){return this._allowUnknownOption=!!t,this}allowExcessArguments(t=!0){return this._allowExcessArguments=!!t,this}enablePositionalOptions(t=!0){return this._enablePositionalOptions=!!t,this}passThroughOptions(t=!0){return this._passThroughOptions=!!t,this._checkForBrokenPassThrough(),this}_checkForBrokenPassThrough(){if(this.parent&&this._passThroughOptions&&!this.parent._enablePositionalOptions)throw new Error(`passThroughOptions cannot be used for '${this._name}' without turning on enablePositionalOptions for parent command(s)`)}storeOptionsAsProperties(t=!0){if(this.options.length)throw new Error("call .storeOptionsAsProperties() before adding options");if(Object.keys(this._optionValues).length)throw new Error("call .storeOptionsAsProperties() before setting option values");return this._storeOptionsAsProperties=!!t,this}getOptionValue(t){return this._storeOptionsAsProperties?this[t]:this._optionValues[t]}setOptionValue(t,e){return this.setOptionValueWithSource(t,e,void 0)}setOptionValueWithSource(t,e,r){return this._storeOptionsAsProperties?this[t]=e:this._optionValues[t]=e,this._optionValueSources[t]=r,this}getOptionValueSource(t){return this._optionValueSources[t]}getOptionValueSourceWithGlobals(t){let e;return this._getCommandAndAncestors().forEach(r=>{r.getOptionValueSource(t)!==void 0&&(e=r.getOptionValueSource(t))}),e}_prepareUserArgs(t,e){if(t!==void 0&&!Array.isArray(t))throw new Error("first parameter to parse must be array or undefined");if(e=e||{},t===void 0&&e.from===void 0){h.versions?.electron&&(e.from="electron");let i=h.execArgv??[];(i.includes("-e")||i.includes("--eval")||i.includes("-p")||i.includes("--print"))&&(e.from="eval")}t===void 0&&(t=h.argv),this.rawArgs=t.slice();let r;switch(e.from){case void 0:case"node":this._scriptPath=t[1],r=t.slice(2);break;case"electron":h.defaultApp?(this._scriptPath=t[1],r=t.slice(2)):r=t.slice(1);break;case"user":r=t.slice(0);break;case"eval":r=t.slice(1);break;default:throw new Error(`unexpected parse option { from: '${e.from}' }`)}return!this._name&&this._scriptPath&&this.nameFromFilename(this._scriptPath),this._name=this._name||"program",r}parse(t,e){this._prepareForParse();let r=this._prepareUserArgs(t,e);return this._parseCommand([],r),this}async parseAsync(t,e){this._prepareForParse();let r=this._prepareUserArgs(t,e);return await this._parseCommand([],r),this}_prepareForParse(){this._savedState===null?this.saveStateBeforeParse():this.restoreStateBeforeParse()}saveStateBeforeParse(){this._savedState={_name:this._name,_optionValues:{...this._optionValues},_optionValueSources:{...this._optionValueSources}}}restoreStateBeforeParse(){if(this._storeOptionsAsProperties)throw new Error(`Can not call parse again when storeOptionsAsProperties is true.
18
+ - either make a new Command for each call to parse, or stop storing options as properties`);this._name=this._savedState._name,this._scriptPath=null,this.rawArgs=[],this._optionValues={...this._savedState._optionValues},this._optionValueSources={...this._savedState._optionValueSources},this.args=[],this.processedArgs=[]}_checkForMissingExecutable(t,e,r){if(ut.existsSync(t))return;let i=e?`searched for local subcommand relative to directory '${e}'`:"no directory for search for local subcommand, use .executableDir() to supply a custom directory",o=`'${t}' does not exist
19
+ - if '${r}' is not meant to be an executable command, remove description parameter from '.command()' and use '.description()' instead
20
+ - if the default executable name is not suitable, use the executableFile option to supply a custom name or path
21
+ - ${i}`;throw new Error(o)}_executeSubCommand(t,e){e=e.slice();let r=!1,i=[".js",".ts",".tsx",".mjs",".cjs"];function o(u,d){let m=N.resolve(u,d);if(ut.existsSync(m))return m;if(i.includes(N.extname(d)))return;let p=i.find(f=>ut.existsSync(`${m}${f}`));if(p)return`${m}${p}`}this._checkForMissingMandatoryOptions(),this._checkForConflictingOptions();let s=t._executableFile||`${this._name}-${t._name}`,a=this._executableDir||"";if(this._scriptPath){let u;try{u=ut.realpathSync(this._scriptPath)}catch{u=this._scriptPath}a=N.resolve(N.dirname(u),a)}if(a){let u=o(a,s);if(!u&&!t._executableFile&&this._scriptPath){let d=N.basename(this._scriptPath,N.extname(this._scriptPath));d!==this._name&&(u=o(a,`${d}-${t._name}`))}s=u||s}r=i.includes(N.extname(s));let l;h.platform!=="win32"?r?(e.unshift(s),e=pe(h.execArgv).concat(e),l=Mt.spawn(h.argv[0],e,{stdio:"inherit"})):l=Mt.spawn(s,e,{stdio:"inherit"}):(this._checkForMissingExecutable(s,a,t._name),e.unshift(s),e=pe(h.execArgv).concat(e),l=Mt.spawn(h.execPath,e,{stdio:"inherit"})),l.killed||["SIGUSR1","SIGUSR2","SIGTERM","SIGINT","SIGHUP"].forEach(d=>{h.on(d,()=>{l.killed===!1&&l.exitCode===null&&l.kill(d)})});let c=this._exitCallback;l.on("close",u=>{u=u??1,c?c(new Gt(u,"commander.executeSubCommandAsync","(close)")):h.exit(u)}),l.on("error",u=>{if(u.code==="ENOENT")this._checkForMissingExecutable(s,a,t._name);else if(u.code==="EACCES")throw new Error(`'${s}' not executable`);if(!c)h.exit(1);else{let d=new Gt(1,"commander.executeSubCommandAsync","(error)");d.nestedError=u,c(d)}}),this.runningCommand=l}_dispatchSubcommand(t,e,r){let i=this._findCommand(t);i||this.help({error:!0}),i._prepareForParse();let o;return o=this._chainOrCallSubCommandHook(o,i,"preSubcommand"),o=this._chainOrCall(o,()=>{if(i._executableHandler)this._executeSubCommand(i,e.concat(r));else return i._parseCommand(e,r)}),o}_dispatchHelpCommand(t){t||this.help();let e=this._findCommand(t);return e&&!e._executableHandler&&e.help(),this._dispatchSubcommand(t,[],[this._getHelpOption()?.long??this._getHelpOption()?.short??"--help"])}_checkNumberOfArguments(){this.registeredArguments.forEach((t,e)=>{t.required&&this.args[e]==null&&this.missingArgument(t.name())}),!(this.registeredArguments.length>0&&this.registeredArguments[this.registeredArguments.length-1].variadic)&&this.args.length>this.registeredArguments.length&&this._excessArguments(this.args)}_processArguments(){let t=(r,i,o)=>{let s=i;if(i!==null&&r.parseArg){let a=`error: command-argument value '${i}' is invalid for argument '${r.name()}'.`;s=this._callParseArg(r,i,o,a)}return s};this._checkNumberOfArguments();let e=[];this.registeredArguments.forEach((r,i)=>{let o=r.defaultValue;r.variadic?i<this.args.length?(o=this.args.slice(i),r.parseArg&&(o=o.reduce((s,a)=>t(r,a,s),r.defaultValue))):o===void 0&&(o=[]):i<this.args.length&&(o=this.args[i],r.parseArg&&(o=t(r,o,r.defaultValue))),e[i]=o}),this.processedArgs=e}_chainOrCall(t,e){return t?.then&&typeof t.then=="function"?t.then(()=>e()):e()}_chainOrCallHooks(t,e){let r=t,i=[];return this._getCommandAndAncestors().reverse().filter(o=>o._lifeCycleHooks[e]!==void 0).forEach(o=>{o._lifeCycleHooks[e].forEach(s=>{i.push({hookedCommand:o,callback:s})})}),e==="postAction"&&i.reverse(),i.forEach(o=>{r=this._chainOrCall(r,()=>o.callback(o.hookedCommand,this))}),r}_chainOrCallSubCommandHook(t,e,r){let i=t;return this._lifeCycleHooks[r]!==void 0&&this._lifeCycleHooks[r].forEach(o=>{i=this._chainOrCall(i,()=>o(this,e))}),i}_parseCommand(t,e){let r=this.parseOptions(e);if(this._parseOptionsEnv(),this._parseOptionsImplied(),t=t.concat(r.operands),e=r.unknown,this.args=t.concat(e),t&&this._findCommand(t[0]))return this._dispatchSubcommand(t[0],t.slice(1),e);if(this._getHelpCommand()&&t[0]===this._getHelpCommand().name())return this._dispatchHelpCommand(t[1]);if(this._defaultCommandName)return this._outputHelpIfRequested(e),this._dispatchSubcommand(this._defaultCommandName,t,e);this.commands.length&&this.args.length===0&&!this._actionHandler&&!this._defaultCommandName&&this.help({error:!0}),this._outputHelpIfRequested(r.unknown),this._checkForMissingMandatoryOptions(),this._checkForConflictingOptions();let i=()=>{r.unknown.length>0&&this.unknownOption(r.unknown[0])},o=`command:${this.name()}`;if(this._actionHandler){i(),this._processArguments();let s;return s=this._chainOrCallHooks(s,"preAction"),s=this._chainOrCall(s,()=>this._actionHandler(this.processedArgs)),this.parent&&(s=this._chainOrCall(s,()=>{this.parent.emit(o,t,e)})),s=this._chainOrCallHooks(s,"postAction"),s}if(this.parent?.listenerCount(o))i(),this._processArguments(),this.parent.emit(o,t,e);else if(t.length){if(this._findCommand("*"))return this._dispatchSubcommand("*",t,e);this.listenerCount("command:*")?this.emit("command:*",t,e):this.commands.length?this.unknownCommand():(i(),this._processArguments())}else this.commands.length?(i(),this.help({error:!0})):(i(),this._processArguments())}_findCommand(t){if(t)return this.commands.find(e=>e._name===t||e._aliases.includes(t))}_findOption(t){return this.options.find(e=>e.is(t))}_checkForMissingMandatoryOptions(){this._getCommandAndAncestors().forEach(t=>{t.options.forEach(e=>{e.mandatory&&t.getOptionValue(e.attributeName())===void 0&&t.missingMandatoryOptionValue(e)})})}_checkForConflictingLocalOptions(){let t=this.options.filter(r=>{let i=r.attributeName();return this.getOptionValue(i)===void 0?!1:this.getOptionValueSource(i)!=="default"});t.filter(r=>r.conflictsWith.length>0).forEach(r=>{let i=t.find(o=>r.conflictsWith.includes(o.attributeName()));i&&this._conflictingOption(r,i)})}_checkForConflictingOptions(){this._getCommandAndAncestors().forEach(t=>{t._checkForConflictingLocalOptions()})}parseOptions(t){let e=[],r=[],i=e;function o(u){return u.length>1&&u[0]==="-"}let s=u=>/^-(\d+|\d*\.\d+)(e[+-]?\d+)?$/.test(u)?!this._getCommandAndAncestors().some(d=>d.options.map(m=>m.short).some(m=>/^-\d$/.test(m))):!1,a=null,l=null,c=0;for(;c<t.length||l;){let u=l??t[c++];if(l=null,u==="--"){i===r&&i.push(u),i.push(...t.slice(c));break}if(a&&(!o(u)||s(u))){this.emit(`option:${a.name()}`,u);continue}if(a=null,o(u)){let d=this._findOption(u);if(d){if(d.required){let m=t[c++];m===void 0&&this.optionMissingArgument(d),this.emit(`option:${d.name()}`,m)}else if(d.optional){let m=null;c<t.length&&(!o(t[c])||s(t[c]))&&(m=t[c++]),this.emit(`option:${d.name()}`,m)}else this.emit(`option:${d.name()}`);a=d.variadic?d:null;continue}}if(u.length>2&&u[0]==="-"&&u[1]!=="-"){let d=this._findOption(`-${u[1]}`);if(d){d.required||d.optional&&this._combineFlagAndOptionalValue?this.emit(`option:${d.name()}`,u.slice(2)):(this.emit(`option:${d.name()}`),l=`-${u.slice(2)}`);continue}}if(/^--[^=]+=/.test(u)){let d=u.indexOf("="),m=this._findOption(u.slice(0,d));if(m&&(m.required||m.optional)){this.emit(`option:${m.name()}`,u.slice(d+1));continue}}if(i===e&&o(u)&&!(this.commands.length===0&&s(u))&&(i=r),(this._enablePositionalOptions||this._passThroughOptions)&&e.length===0&&r.length===0){if(this._findCommand(u)){e.push(u),r.push(...t.slice(c));break}else if(this._getHelpCommand()&&u===this._getHelpCommand().name()){e.push(u,...t.slice(c));break}else if(this._defaultCommandName){r.push(u,...t.slice(c));break}}if(this._passThroughOptions){i.push(u,...t.slice(c));break}i.push(u)}return{operands:e,unknown:r}}opts(){if(this._storeOptionsAsProperties){let t={},e=this.options.length;for(let r=0;r<e;r++){let i=this.options[r].attributeName();t[i]=i===this._versionOptionName?this._version:this[i]}return t}return this._optionValues}optsWithGlobals(){return this._getCommandAndAncestors().reduce((t,e)=>Object.assign(t,e.opts()),{})}error(t,e){this._outputConfiguration.outputError(`${t}
22
+ `,this._outputConfiguration.writeErr),typeof this._showHelpAfterError=="string"?this._outputConfiguration.writeErr(`${this._showHelpAfterError}
23
+ `):this._showHelpAfterError&&(this._outputConfiguration.writeErr(`
24
+ `),this.outputHelp({error:!0}));let r=e||{},i=r.exitCode||1,o=r.code||"commander.error";this._exit(i,o,t)}_parseOptionsEnv(){this.options.forEach(t=>{if(t.envVar&&t.envVar in h.env){let e=t.attributeName();(this.getOptionValue(e)===void 0||["default","config","env"].includes(this.getOptionValueSource(e)))&&(t.required||t.optional?this.emit(`optionEnv:${t.name()}`,h.env[t.envVar]):this.emit(`optionEnv:${t.name()}`))}})}_parseOptionsImplied(){let t=new Vr(this.options),e=r=>this.getOptionValue(r)!==void 0&&!["default","implied"].includes(this.getOptionValueSource(r));this.options.filter(r=>r.implied!==void 0&&e(r.attributeName())&&t.valueFromOption(this.getOptionValue(r.attributeName()),r)).forEach(r=>{Object.keys(r.implied).filter(i=>!e(i)).forEach(i=>{this.setOptionValueWithSource(i,r.implied[i],"implied")})})}missingArgument(t){let e=`error: missing required argument '${t}'`;this.error(e,{code:"commander.missingArgument"})}optionMissingArgument(t){let e=`error: option '${t.flags}' argument missing`;this.error(e,{code:"commander.optionMissingArgument"})}missingMandatoryOptionValue(t){let e=`error: required option '${t.flags}' not specified`;this.error(e,{code:"commander.missingMandatoryOptionValue"})}_conflictingOption(t,e){let r=s=>{let a=s.attributeName(),l=this.getOptionValue(a),c=this.options.find(d=>d.negate&&a===d.attributeName()),u=this.options.find(d=>!d.negate&&a===d.attributeName());return c&&(c.presetArg===void 0&&l===!1||c.presetArg!==void 0&&l===c.presetArg)?c:u||s},i=s=>{let a=r(s),l=a.attributeName();return this.getOptionValueSource(l)==="env"?`environment variable '${a.envVar}'`:`option '${a.flags}'`},o=`error: ${i(t)} cannot be used with ${i(e)}`;this.error(o,{code:"commander.conflictingOption"})}unknownOption(t){if(this._allowUnknownOption)return;let e="";if(t.startsWith("--")&&this._showSuggestionAfterError){let i=[],o=this;do{let s=o.createHelp().visibleOptions(o).filter(a=>a.long).map(a=>a.long);i=i.concat(s),o=o.parent}while(o&&!o._enablePositionalOptions);e=me(t,i)}let r=`error: unknown option '${t}'${e}`;this.error(r,{code:"commander.unknownOption"})}_excessArguments(t){if(this._allowExcessArguments)return;let e=this.registeredArguments.length,r=e===1?"":"s",o=`error: too many arguments${this.parent?` for '${this.name()}'`:""}. Expected ${e} argument${r} but got ${t.length}.`;this.error(o,{code:"commander.excessArguments"})}unknownCommand(){let t=this.args[0],e="";if(this._showSuggestionAfterError){let i=[];this.createHelp().visibleCommands(this).forEach(o=>{i.push(o.name()),o.alias()&&i.push(o.alias())}),e=me(t,i)}let r=`error: unknown command '${t}'${e}`;this.error(r,{code:"commander.unknownCommand"})}version(t,e,r){if(t===void 0)return this._version;this._version=t,e=e||"-V, --version",r=r||"output the version number";let i=this.createOption(e,r);return this._versionOptionName=i.attributeName(),this._registerOption(i),this.on("option:"+i.name(),()=>{this._outputConfiguration.writeOut(`${t}
25
+ `),this._exit(0,"commander.version",t)}),this}description(t,e){return t===void 0&&e===void 0?this._description:(this._description=t,e&&(this._argsDescription=e),this)}summary(t){return t===void 0?this._summary:(this._summary=t,this)}alias(t){if(t===void 0)return this._aliases[0];let e=this;if(this.commands.length!==0&&this.commands[this.commands.length-1]._executableHandler&&(e=this.commands[this.commands.length-1]),t===e._name)throw new Error("Command alias can't be the same as its name");let r=this.parent?._findCommand(t);if(r){let i=[r.name()].concat(r.aliases()).join("|");throw new Error(`cannot add alias '${t}' to command '${this.name()}' as already have command '${i}'`)}return e._aliases.push(t),this}aliases(t){return t===void 0?this._aliases:(t.forEach(e=>this.alias(e)),this)}usage(t){if(t===void 0){if(this._usage)return this._usage;let e=this.registeredArguments.map(r=>Ur(r));return[].concat(this.options.length||this._helpOption!==null?"[options]":[],this.commands.length?"[command]":[],this.registeredArguments.length?e:[]).join(" ")}return this._usage=t,this}name(t){return t===void 0?this._name:(this._name=t,this)}helpGroup(t){return t===void 0?this._helpGroupHeading??"":(this._helpGroupHeading=t,this)}commandsGroup(t){return t===void 0?this._defaultCommandGroup??"":(this._defaultCommandGroup=t,this)}optionsGroup(t){return t===void 0?this._defaultOptionGroup??"":(this._defaultOptionGroup=t,this)}_initOptionGroup(t){this._defaultOptionGroup&&!t.helpGroupHeading&&t.helpGroup(this._defaultOptionGroup)}_initCommandGroup(t){this._defaultCommandGroup&&!t.helpGroup()&&t.helpGroup(this._defaultCommandGroup)}nameFromFilename(t){return this._name=N.basename(t,N.extname(t)),this}executableDir(t){return t===void 0?this._executableDir:(this._executableDir=t,this)}helpInformation(t){let e=this.createHelp(),r=this._getOutputContext(t);e.prepareContext({error:r.error,helpWidth:r.helpWidth,outputHasColors:r.hasColors});let i=e.formatHelp(this,e);return r.hasColors?i:this._outputConfiguration.stripColor(i)}_getOutputContext(t){t=t||{};let e=!!t.error,r,i,o;return e?(r=a=>this._outputConfiguration.writeErr(a),i=this._outputConfiguration.getErrHasColors(),o=this._outputConfiguration.getErrHelpWidth()):(r=a=>this._outputConfiguration.writeOut(a),i=this._outputConfiguration.getOutHasColors(),o=this._outputConfiguration.getOutHelpWidth()),{error:e,write:a=>(i||(a=this._outputConfiguration.stripColor(a)),r(a)),hasColors:i,helpWidth:o}}outputHelp(t){let e;typeof t=="function"&&(e=t,t=void 0);let r=this._getOutputContext(t),i={error:r.error,write:r.write,command:this};this._getCommandAndAncestors().reverse().forEach(s=>s.emit("beforeAllHelp",i)),this.emit("beforeHelp",i);let o=this.helpInformation({error:r.error});if(e&&(o=e(o),typeof o!="string"&&!Buffer.isBuffer(o)))throw new Error("outputHelp callback must return a string or a Buffer");r.write(o),this._getHelpOption()?.long&&this.emit(this._getHelpOption().long),this.emit("afterHelp",i),this._getCommandAndAncestors().forEach(s=>s.emit("afterAllHelp",i))}helpOption(t,e){return typeof t=="boolean"?(t?(this._helpOption===null&&(this._helpOption=void 0),this._defaultOptionGroup&&this._initOptionGroup(this._getHelpOption())):this._helpOption=null,this):(this._helpOption=this.createOption(t??"-h, --help",e??"display help for command"),(t||e)&&this._initOptionGroup(this._helpOption),this)}_getHelpOption(){return this._helpOption===void 0&&this.helpOption(void 0,void 0),this._helpOption}addHelpOption(t){return this._helpOption=t,this._initOptionGroup(t),this}help(t){this.outputHelp(t);let e=Number(h.exitCode??0);e===0&&t&&typeof t!="function"&&t.error&&(e=1),this._exit(e,"commander.help","(outputHelp)")}addHelpText(t,e){let r=["beforeAll","before","after","afterAll"];if(!r.includes(t))throw new Error(`Unexpected value for position to addHelpText.
26
+ Expecting one of '${r.join("', '")}'`);let i=`${t}Help`;return this.on(i,o=>{let s;typeof e=="function"?s=e({error:o.error,command:o.command}):s=e,s&&o.write(`${s}
27
+ `)}),this}_outputHelpIfRequested(t){let e=this._getHelpOption();e&&t.find(i=>e.is(i))&&(this.outputHelp(),this._exit(0,"commander.helpDisplayed","(outputHelp)"))}};function pe(n){return n.map(t=>{if(!t.startsWith("--inspect"))return t;let e,r="127.0.0.1",i="9229",o;return(o=t.match(/^(--inspect(-brk)?)$/))!==null?e=o[1]:(o=t.match(/^(--inspect(-brk|-port)?)=([^:]+)$/))!==null?(e=o[1],/^\d+$/.test(o[3])?i=o[3]:r=o[3]):(o=t.match(/^(--inspect(-brk|-port)?)=([^:]+):(\d+)$/))!==null&&(e=o[1],r=o[3],i=o[4]),e&&i!=="0"?`${e}=${r}:${parseInt(i)+1}`:t})}function Dt(){if(h.env.NO_COLOR||h.env.FORCE_COLOR==="0"||h.env.FORCE_COLOR==="false")return!1;if(h.env.FORCE_COLOR||h.env.CLICOLOR_FORCE!==void 0)return!0}Wt.Command=Ft;Wt.useColor=Dt});var we=U(P=>{var{Argument:fe}=at(),{Command:qt}=he(),{CommanderError:Mr,InvalidArgumentError:ge}=et(),{Help:Gr}=Ht(),{Option:_e}=Vt();P.program=new qt;P.createCommand=n=>new qt(n);P.createOption=(n,t)=>new _e(n,t);P.createArgument=(n,t)=>new fe(n,t);P.Command=qt;P.Option=_e;P.Argument=fe;P.Help=Gr;P.CommanderError=Mr;P.InvalidArgumentError=ge;P.InvalidOptionArgumentError=ge});var be=U((I,ye)=>{var j=we();I=ye.exports={};I.program=new j.Command;I.Argument=j.Argument;I.Command=j.Command;I.CommanderError=j.CommanderError;I.Help=j.Help;I.InvalidArgumentError=j.InvalidArgumentError;I.InvalidOptionArgumentError=j.InvalidArgumentError;I.Option=j.Option;I.createCommand=n=>new j.Command(n);I.createOption=(n,t)=>new j.Option(n,t);I.createArgument=(n,t)=>new j.Argument(n,t)});var K=U((ii,Bt)=>{var ct=process||{},xe=ct.argv||[],lt=ct.env||{},Fr=!(lt.NO_COLOR||xe.includes("--no-color"))&&(!!lt.FORCE_COLOR||xe.includes("--color")||ct.platform==="win32"||(ct.stdout||{}).isTTY&&lt.TERM!=="dumb"||!!lt.CI),Dr=(n,t,e=n)=>r=>{let i=""+r,o=i.indexOf(t,n.length);return~o?n+Wr(i,t,e,o)+t:n+i+t},Wr=(n,t,e,r)=>{let i="",o=0;do i+=n.substring(o,r)+e,o=r+t.length,r=n.indexOf(t,o);while(~r);return i+n.substring(o)},Oe=(n=Fr)=>{let t=n?Dr:()=>String;return{isColorSupported:n,reset:t("\x1B[0m","\x1B[0m"),bold:t("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:t("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m"),italic:t("\x1B[3m","\x1B[23m"),underline:t("\x1B[4m","\x1B[24m"),inverse:t("\x1B[7m","\x1B[27m"),hidden:t("\x1B[8m","\x1B[28m"),strikethrough:t("\x1B[9m","\x1B[29m"),black:t("\x1B[30m","\x1B[39m"),red:t("\x1B[31m","\x1B[39m"),green:t("\x1B[32m","\x1B[39m"),yellow:t("\x1B[33m","\x1B[39m"),blue:t("\x1B[34m","\x1B[39m"),magenta:t("\x1B[35m","\x1B[39m"),cyan:t("\x1B[36m","\x1B[39m"),white:t("\x1B[37m","\x1B[39m"),gray:t("\x1B[90m","\x1B[39m"),bgBlack:t("\x1B[40m","\x1B[49m"),bgRed:t("\x1B[41m","\x1B[49m"),bgGreen:t("\x1B[42m","\x1B[49m"),bgYellow:t("\x1B[43m","\x1B[49m"),bgBlue:t("\x1B[44m","\x1B[49m"),bgMagenta:t("\x1B[45m","\x1B[49m"),bgCyan:t("\x1B[46m","\x1B[49m"),bgWhite:t("\x1B[47m","\x1B[49m"),blackBright:t("\x1B[90m","\x1B[39m"),redBright:t("\x1B[91m","\x1B[39m"),greenBright:t("\x1B[92m","\x1B[39m"),yellowBright:t("\x1B[93m","\x1B[39m"),blueBright:t("\x1B[94m","\x1B[39m"),magentaBright:t("\x1B[95m","\x1B[39m"),cyanBright:t("\x1B[96m","\x1B[39m"),whiteBright:t("\x1B[97m","\x1B[39m"),bgBlackBright:t("\x1B[100m","\x1B[49m"),bgRedBright:t("\x1B[101m","\x1B[49m"),bgGreenBright:t("\x1B[102m","\x1B[49m"),bgYellowBright:t("\x1B[103m","\x1B[49m"),bgBlueBright:t("\x1B[104m","\x1B[49m"),bgMagentaBright:t("\x1B[105m","\x1B[49m"),bgCyanBright:t("\x1B[106m","\x1B[49m"),bgWhiteBright:t("\x1B[107m","\x1B[49m")}};Bt.exports=Oe();Bt.exports.createColors=Oe});var Ce=B(be(),1),{program:Kn,createCommand:zn,createArgument:Yn,createOption:Jn,CommanderError:Xn,InvalidArgumentError:Qn,InvalidOptionArgumentError:Zn,Command:w,Argument:ti,Option:ei,Help:ri}=Ce.default;var V=B(K(),1);var M=require("node:fs"),ke=require("node:os"),z=require("node:path");var ve=require("node:crypto"),$=require("node:fs"),Ee=require("node:path"),qr=1e4,Br=50,Ae=200;function rt(n,t){(0,$.mkdirSync)((0,Ee.dirname)(n),{recursive:!0,mode:448});let e=Kr(n);try{let r=t();return r instanceof Promise?r.finally(()=>Kt(n,e)):(Kt(n,e),r)}catch(r){throw Kt(n,e),r}}function Kr(n){let t=(0,ve.randomUUID)();for(let e=0;e<Ae;e++)try{let r=(0,$.openSync)(n,"wx");try{(0,$.writeSync)(r,t)}finally{(0,$.closeSync)(r)}return t}catch(r){if(r.code!=="EEXIST")throw r;if(zr(n)){try{(0,$.unlinkSync)(n)}catch{}continue}Yr(Br)}throw new Error(`Could not acquire lock on ${n} after ${Ae} retries. If no other 37audits process is running, delete the lock file manually.`)}function Kt(n,t){try{if((0,$.readFileSync)(n,"utf-8")!==t)return;(0,$.unlinkSync)(n)}catch{}}function zr(n){try{let t=(0,$.statSync)(n);return Date.now()-t.mtimeMs>qr}catch{return!0}}function Yr(n){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0,n)}var Te=require("node:crypto"),k=require("node:fs"),Se=require("node:path");function $e(n,t,e){let r=`${n}.tmp.${process.pid}.${(0,Te.randomUUID)()}`;try{let i=(0,k.openSync)(r,"w",e);try{(0,k.writeSync)(i,t),(0,k.fsyncSync)(i)}finally{(0,k.closeSync)(i)}(0,k.renameSync)(r,n),Jr((0,Se.dirname)(n))}catch(i){try{(0,k.unlinkSync)(r)}catch{}throw i}}function Jr(n){if(process.platform!=="win32")try{let t=(0,k.openSync)(n,"r");try{(0,k.fsyncSync)(t)}finally{(0,k.closeSync)(t)}}catch{}}function zt(){return process.env.XDG_CONFIG_HOME?(0,z.join)(process.env.XDG_CONFIG_HOME,"37audits"):process.platform==="win32"&&process.env.APPDATA?(0,z.join)(process.env.APPDATA,"37audits"):(0,z.join)((0,ke.homedir)(),".config","37audits")}function F(){return(0,z.join)(zt(),"credentials.json")}function dt(){return(0,z.join)(zt(),"credentials.json.lock")}function Xr(n){if(typeof n!="object"||n===null)return!1;let t=n;return t.type==="api_key"?typeof t.api_key=="string"&&t.api_key.length>0:t.type==="oauth"?typeof t.auth_url=="string"&&typeof t.client_id=="string"&&typeof t.access_token=="string"&&typeof t.access_token_expires_at=="number"&&typeof t.refresh_token=="string":!1}function mt(){let n=F(),t;try{t=(0,M.readFileSync)(n,"utf-8")}catch(i){if(i.code==="ENOENT")return null;throw new Error(`Could not read ${n}: ${i.message}`)}let e;try{e=JSON.parse(t)}catch{throw new Error(`${n} is not valid JSON. Run \`37audits logout\` then \`37audits login\`.`)}let r=e;if(r?.version!==1||!Xr(r.credential))throw new Error(`${n} has an unexpected format. Run \`37audits logout\` then \`37audits login\`.`);return{version:1,credential:r.credential}}function Yt(n){let t=F();(0,M.mkdirSync)(zt(),{recursive:!0,mode:448}),$e(t,`${JSON.stringify(n,null,2)}
28
+ `,384);try{(0,M.chmodSync)(t,384)}catch{}return t}function Jt(n){return rt(dt(),()=>Yt({version:1,credential:n}))}function Ie(){let n=F();return rt(dt(),()=>{try{return(0,M.unlinkSync)(n),{path:n,removed:!0}}catch(t){if(t.code==="ENOENT")return{path:n,removed:!1};throw t}})}var Qr="https://api.37audits.com",Zr="https://walqrrhzypvzyrfjmygm.supabase.co/auth/v1",tn="2c7b81f7-5e7e-4927-9321-ba6ec909f11b",Pe=[37371,37372,37373],Xt="/callback",Re="openid email profile offline_access",O={apiKey:"THIRTYSEVENAUDITS_API_KEY",apiBaseUrl:"THIRTYSEVENAUDITS_API_BASE_URL",authUrl:"THIRTYSEVENAUDITS_AUTH_URL",oauthClientId:"THIRTYSEVENAUDITS_OAUTH_CLIENT_ID"},je=n=>n.replace(/\/+$/,"");function He(){return je(process.env[O.apiBaseUrl]||Qr)}function Ue(){return je(process.env[O.authUrl]||Zr)}function Ne(){return process.env[O.oauthClientId]||tn}function Le(n){return`http://127.0.0.1:${n}${Xt}`}var nt=require("node:crypto"),Ge=require("node:http");var y=class extends Error{constructor(e,r){super(e);this.code=r;this.name="OAuthError"}code},Qt="Run `37audits login` to sign in again.",en=3e4,rn=60,nn=300*1e3;function Fe(){let n=(0,nt.randomBytes)(64).toString("base64url"),t=(0,nt.createHash)("sha256").update(n).digest("base64url"),e=(0,nt.randomBytes)(24).toString("base64url");return{codeVerifier:n,codeChallenge:t,state:e}}function on(n){let t=new y(`Received an invalid access token. ${Qt}`,"oauth_error"),e=n.split(".");if(e.length!==3)throw t;let r;try{r=JSON.parse(Buffer.from(e[1],"base64url").toString())}catch{throw t}let i=r?.exp;if(typeof i!="number")throw t;return i}function sn(n){let t=n;if(typeof t!="object"||t===null||typeof t.access_token!="string"||t.access_token.length===0||typeof t.refresh_token!="string"||t.refresh_token.length===0)throw new y(`Unexpected response from the authorization server. ${Qt}`,"oauth_error");return t}function De(n){let t=new URL(`${n.authUrl}/oauth/authorize`);return t.search=new URLSearchParams({client_id:n.clientId,redirect_uri:n.redirectUri,response_type:"code",scope:Re,state:n.state,code_challenge:n.codeChallenge,code_challenge_method:"S256"}).toString(),t.toString()}function Ve(n,t){return`<!doctype html>
29
+ <html lang="en">
30
+ <head>
31
+ <meta charset="utf-8" />
32
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
33
+ <title>37 Audits CLI</title>
34
+ <style>
35
+ :root { color-scheme: light dark; }
36
+ body { margin: 0; min-height: 100vh; display: flex; align-items: center; justify-content: center;
37
+ font-family: ui-sans-serif, system-ui, sans-serif; background: #0b0f19; color: #e5e7eb; padding: 16px; }
38
+ main { max-width: 28rem; text-align: center; }
39
+ h1 { font-size: 1.5rem; margin: 0 0 .5rem; }
40
+ p { margin: 0; color: #9ca3af; }
41
+ </style>
42
+ </head>
43
+ <body><main><h1>${n}</h1><p>${t}</p></main></body>
44
+ </html>`}function an(n){return new Promise((t,e)=>{let r=(0,Ge.createServer)();r.once("error",e),r.listen(n,"127.0.0.1",()=>{r.off("error",e),t(r)})})}async function We(n=Pe){let t,e=0;for(let l of n)try{t=await an(l),e=l;break}catch(c){if(c.code!=="EADDRINUSE")throw new y(`Could not start the login callback server on 127.0.0.1:${l}: ${c.message}`,"oauth_error")}if(!t)throw new y(`Ports ${n.join(", ")} on 127.0.0.1 are all in use. Free one of them and run \`37audits login\` again.`,"oauth_port_unavailable");let r=t,i,o,s=new Promise((l,c)=>{i=l,o=c});s.catch(()=>{});let a=setTimeout(()=>{r.close(),o(new y("Timed out after 5 minutes waiting for the browser. Run `37audits login` again.","oauth_timeout"))},nn);return r.on("request",(l,c)=>{let u=new URL(l.url??"/","http://127.0.0.1");if(u.pathname!==Xt){c.writeHead(404,{Connection:"close"}),c.end();return}let d=u.searchParams.get("code"),m=u.searchParams.get("state"),p=u.searchParams.get("error"),f=u.searchParams.get("error_description"),_=p?`Authorization ${p==="access_denied"?"denied":`failed: ${p}`}${f?` (${f})`:""}`:!d||!m?"The callback is missing the code or state parameter":null;c.writeHead(200,{"Content-Type":"text/html; charset=utf-8",Connection:"close"}),c.end(_?Ve("Sign-in failed",`${_}. Return to your terminal and try again.`):Ve("Signed in to 37 Audits","You can close this tab and return to your terminal."),()=>l.socket.destroy()),clearTimeout(a),r.close(),_?o(new y(_,"oauth_error")):d&&m&&i({code:d,state:m})}),{port:e,redirectUri:Le(e),waitForCallback:s,close(){clearTimeout(a),r.close()}}}async function qe(n,t,e){let r;try{r=await fetch(`${n}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Accept:"application/json"},body:t,signal:AbortSignal.timeout(en)})}catch(i){let o=i instanceof Error&&i.name==="TimeoutError";throw new y(o?`${e} timed out after 30s. Check your connection and try again.`:`Could not reach the authorization server at ${n}. Check your connection and try again.`,"network_error")}if(!r.ok){let i="";try{let o=await r.json();i=String(o.error_description??o.msg??o.error_code??o.error??"")}catch{}throw new y(`${e} failed (HTTP ${r.status})${i?`: ${i}`:""}`,"oauth_error")}return sn(await r.json())}function Be(n){return qe(n.authUrl,new URLSearchParams({grant_type:"authorization_code",client_id:n.clientId,code:n.code,redirect_uri:n.redirectUri,code_verifier:n.codeVerifier}),"Token exchange")}function un(n){return qe(n.authUrl,new URLSearchParams({grant_type:"refresh_token",client_id:n.clientId,refresh_token:n.refreshToken}),"Token refresh")}function Zt(n,t){return{type:"oauth",auth_url:t.authUrl,client_id:t.clientId,access_token:n.access_token,access_token_expires_at:on(n.access_token),refresh_token:n.refresh_token}}function Me(n,t){return n.access_token_expires_at>t+rn}function pt(n,t={}){return rt(dt(),async()=>{let e=n;try{let s=mt()?.credential;s?.type==="oauth"&&(e=s)}catch{}let r=Math.floor(Date.now()/1e3);if(!t.force&&Me(e,r)||t.force&&e.access_token!==n.access_token&&Me(e,r))return e;let i;try{i=await un({authUrl:e.auth_url,clientId:e.client_id,refreshToken:e.refresh_token})}catch(s){throw s instanceof y&&s.code==="network_error"?s:new y(`Your session has expired. ${Qt}`,"auth_error")}let o=Zt(i,{authUrl:e.auth_url,clientId:e.client_id});return Yt({version:1,credential:o}),o})}async function Ke(n,t={}){let e=n?.trim();if(e)return{type:"api_key",key:e,source:"flag"};let r=process.env[O.apiKey]?.trim();if(r)return{type:"api_key",key:r,source:"env"};let i=mt();return i?i.credential.type==="api_key"?{type:"api_key",key:i.credential.api_key,source:"config"}:{type:"oauth",credential:t.refresh===!1?i.credential:await pt(i.credential),source:"config"}:null}function ze(n){return n.type==="api_key"?{"x-api-key":n.key}:{Authorization:`Bearer ${n.credential.access_token}`}}var ht=B(K(),1);function H(n,t){return n instanceof Error?n.message:t}function te(n){return n?!0:!process.stdout.isTTY}function Ye(n,t={}){typeof n=="string"&&!te(t.json)?console.log(n):console.log(JSON.stringify(n,null,2)),t.exitCode!==void 0&&process.exit(t.exitCode)}function A(n,t={}){let e=t.exitCode??1;if(te(t.json)){let r={message:n.message,code:n.code??"unknown"};typeof n.statusCode=="number"&&(r.statusCode=n.statusCode),console.error(JSON.stringify({error:r},null,2))}else console.error(`${ht.default.red("Error:")} ${n.message}`),typeof n.statusCode=="number"&&console.error(ht.default.dim(`[HTTP ${n.statusCode}]`));process.exit(e)}function D(n,t={}){te(t.json)?console.error(JSON.stringify({warning:{message:n}})):console.error(`${ht.default.yellow("Warning:")} ${n}`)}var ee={name:"37audits-cli",version:"1.0.2",description:"37 Audits CLI \u2014 run audits, read results and wrap deploys in maintenance windows from CI/CD",license:"MIT",type:"module",bin:{"37audits":"./dist/cli.cjs"},files:["dist/cli.cjs"],engines:{node:">=22"},repository:{type:"git",url:"git+https://github.com/37audits/37audits-cli.git"},homepage:"https://github.com/37audits/37audits-cli#readme",bugs:{url:"https://github.com/37audits/37audits-cli/issues"},keywords:["37audits","cli","audit","ci","maintenance-window"],scripts:{dev:"tsx src/cli.ts",build:"node scripts/build.mjs",lint:"biome check .","lint:fix":"biome check --write .",typecheck:"tsc --noEmit",test:"vitest run","test:e2e":"vitest run --config vitest.config.e2e.ts",prepack:"npm run build"},dependencies:{"@commander-js/extra-typings":"14.0.0",commander:"14.0.3",picocolors:"1.1.1"},devDependencies:{"@biomejs/biome":"2.4.11","@types/node":"24.12.4",esbuild:"0.28.1",tsx:"4.22.4",typescript:"5.9.3",vitest:"4.1.9"}};var ft=ee.version,Je=ee.name;var C=class extends Error{constructor(e,r,i,o){super(e);this.code=r;this.statusCode=i;this.body=o;this.name="ApiError"}code;statusCode;body},Xe=3e4,gt=[1e3,2e3,4e3],cn=new Set([429,502,503,504]),_t=n=>new Promise(t=>setTimeout(t,n));function dn(n){switch(n){case 401:return"auth_error";case 402:return"insufficient_credits";case 403:return"forbidden";case 404:return"not_found";case 409:return"conflict";default:return n>=500?"server_error":"request_error"}}function mn(n,t,e){if(typeof n=="object"&&n!==null){let o=n;for(let s of["detail","message","title","error"])if(typeof o[s]=="string"&&o[s].trim())return o[s]}let r=t.trim(),i=r.startsWith("{")||r.startsWith("[");return r&&!i&&r.length<=300?r:`HTTP ${e}`}var Y=class{constructor(t,e=He()){this.auth=t;this.baseUrl=e}auth;baseUrl;get authKind(){return this.auth.type}get authSource(){return this.auth.source}get credential(){return this.auth}async request(t,e,r={}){let i=new URL(`${this.baseUrl}${e}`);for(let[l,c]of Object.entries(r.query??{}))for(let u of Array.isArray(c)?c:[c])u!=null&&i.searchParams.append(l,String(u));let o=r.retry??t==="GET",s=0,a=!1;for(;;){let l={...ze(this.auth),Accept:"application/json","User-Agent":`37audits-cli/${ft}`},c={method:t,headers:l,signal:AbortSignal.timeout(r.timeoutMs??Xe)};r.body!==void 0&&(l["Content-Type"]="application/json",c.body=JSON.stringify(r.body));let u;try{u=await fetch(i,c)}catch(p){if(o&&s<gt.length){await _t(gt[s++]);continue}let f=p instanceof Error&&p.name==="TimeoutError";throw new C(f?`Request to ${i.host} timed out after ${(r.timeoutMs??Xe)/1e3}s.`:`Could not reach ${i.host}: ${p.message}`,"network_error")}if(u.status===401&&this.auth.type==="oauth"&&!a){a=!0;try{let p=await pt(this.auth.credential,{force:!0});this.auth={type:"oauth",credential:p,source:"config"};continue}catch(p){throw p instanceof y?new C(p.message,p.code,401):p}}if(o&&cn.has(u.status)&&s<gt.length){await _t(gt[s++]);continue}let d=await u.text(),m;try{m=d?JSON.parse(d):void 0}catch{m=void 0}if(!u.ok){let p=dn(u.status),f=mn(m,d,u.status);throw u.status===401&&(f=this.auth.type==="api_key"?`The API key was rejected (${this.auth.source==="env"?O.apiKey:this.auth.source}). Check it or run \`37audits login\`.`:"Your session was rejected. Run `37audits login` to sign in again."),new C(f,p,u.status,m)}return m}}};async function R(n){let t;try{t=await Ke(n.apiKey)}catch(e){let r=e instanceof y?e.code:"auth_error";return A({message:e.message,code:r},{json:n.json})}return t?new Y(t):A({message:`Not authenticated. Run \`37audits login\`, pass --api-key, or set ${O.apiKey}.`,code:"not_authenticated"},{json:n.json})}var Qe=require("node:util"),pn=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]|\r(?!\n)/g,re=n=>(0,Qe.stripVTControlCharacters)(String(n)).replace(pn,"");function wt(n){return typeof n=="string"?re(n):Array.isArray(n)?n.map(wt):n!==null&&typeof n=="object"?Object.fromEntries(Object.entries(n).map(([t,e])=>[t,wt(e)])):n}var yt=B(K(),1);function J(){return!(!process.stdin.isTTY||!process.stdout.isTTY||process.env.CI==="true"||process.env.CI==="1"||process.env.GITHUB_ACTIONS||process.env.TERM==="dumb")}var W=process.platform!=="win32"||!!process.env.WT_SESSION||process.env.TERM_PROGRAM==="vscode";var hn=W?String.fromCodePoint(10004):"v",fn=W?String.fromCodePoint(9888):"!",gn=W?String.fromCodePoint(10007):"x",_n=[10297,10296,10292,10278,10247,10255,10265,10297].map(n=>String.fromCodePoint(n)),wn=80;function G(n,t){if(t||!J())return{update(){},stop(){},warn(){},fail(){}};let e=W?_n:["-","\\","|","/"],r=0,i=n,o=setInterval(()=>{process.stderr.write(`\r\x1B[2K ${e[r++%e.length]} ${i}`)},wn);return{update(s){i=s},stop(s){clearInterval(o),s?process.stderr.write(`\r\x1B[2K ${yt.default.green(hn)} ${s}
45
+ `):process.stderr.write("\r\x1B[2K")},warn(s){clearInterval(o),process.stderr.write(`\r\x1B[2K ${yt.default.yellow(fn)} ${s}
46
+ `)},fail(s){clearInterval(o),process.stderr.write(`\r\x1B[2K ${yt.default.red(gn)} ${s}
47
+ `)}}}function v(n){return n.optsWithGlobals()}function yn(n){return!n.json&&J()}async function b(n,t,e,r="unexpected_error"){let i=G(t,n.quiet);try{let o=await e();return i.stop(),o}catch(o){return i.stop(),o instanceof C?A({message:o.message,code:o.code,statusCode:o.statusCode},{json:n.json}):o instanceof y?A({message:o.message,code:o.code},{json:n.json}):A({message:H(o,"Unknown error"),code:r},{json:n.json})}}function x(n,t,e){yn(n)?e(wt(t)):Ye(t,{json:n.json})}function g(n,t,e){return A({message:t,code:e},{json:n.json})}function bt(n){if(n==="fail"||n==="warning")return n;throw new Error(`Invalid --fail-on "${n}". Use "fail" or "warning".`)}function bn(n){return n==="warning"?new Set(["fail","error","warning"]):new Set(["fail","error"])}function Ct(n,t){let e=bn(t);return n.filter(r=>e.has(r.status)).length}var L=B(K(),1);var E=W?{h:String.fromCodePoint(9472),v:String.fromCodePoint(9474),tl:String.fromCodePoint(9484),tr:String.fromCodePoint(9488),bl:String.fromCodePoint(9492),br:String.fromCodePoint(9496),lm:String.fromCodePoint(9500),rm:String.fromCodePoint(9508),tm:String.fromCodePoint(9516),bm:String.fromCodePoint(9524),mm:String.fromCodePoint(9532)}:{h:"-",v:"|",tl:"+",tr:"+",bl:"+",br:"+",lm:"+",rm:"+",tm:"+",bm:"+",mm:"+"},Cn=/\x1B\[[0-9;]*m/g,Ze=n=>n.replace(Cn,"").length,xn=(n,t)=>n+" ".repeat(Math.max(0,t-Ze(n)));function On(n,t){let e=Math.max(...n.map(r=>r.length));return t.map((r,i)=>{let o=String(i+1),s=`${E.h}${E.h} ${o} ${E.h.repeat(Math.max(0,56-o.length))}`,a=n.map((l,c)=>` ${l.padEnd(e)} ${r[c]}`);return[s,...a].join(`
48
+ `)}).join(`
49
+
50
+ `)}function ne(n,t,e="(no results)"){if(t.length===0)return e;let r=t.map(l=>l.map(re)),i=n.map((l,c)=>Math.max(l.length,...r.map(u=>Ze(u[c])))),o=process.stdout.columns;if(o!==void 0&&i.reduce((c,u)=>c+u,0)+3*i.length+1>o)return On(n,r);let s=(l,c,u)=>l+i.map(d=>E.h.repeat(d+2)).join(c)+u,a=l=>`${E.v} ${l.map((c,u)=>xn(c,i[u])).join(` ${E.v} `)} ${E.v}`;return[s(E.tl,E.tm,E.tr),a(n),s(E.lm,E.mm,E.rm),...r.map(a),s(E.bl,E.bm,E.br)].join(`
51
+ `)}function tr(n){switch(n){case"success":return L.default.green(n);case"fail":case"error":return L.default.red(n);case"warning":return L.default.yellow(n);case"information":return L.default.blue(n);case"pending":return L.default.dim(n);default:return n}}function An(n){switch(n){case"active":return L.default.yellow(n);case"scheduled":return L.default.blue(n);default:return L.default.dim(n)}}function xt(n){if(!n)return"-";let t=new Date(n);return Number.isNaN(t.getTime())?n:t.toISOString().replace("T"," ").replace(/\.\d+Z$/,"Z")}var er=(n,t)=>n.length>t?`${n.slice(0,t-1)}\u2026`:n;function Ot(n){let t=[...n].sort((e,r)=>(e.auditorName??e.auditorId).localeCompare(r.auditorName??r.auditorId)).map(e=>[e.auditorName??e.auditorId,tr(e.status),er((e.message??"").replace(/\s+/g," "),70)]);return ne(["Auditor","Status","Message"],t)}function X(n){let t=new Map;for(let e of n)t.set(e.status,(t.get(e.status)??0)+1);return["success","warning","fail","error","information","pending"].filter(e=>t.has(e)).map(e=>`${t.get(e)} ${tr(e)}`).join(", ")}function rr(n){let t=n.map(e=>[e.id,An(e.status),xt(e.startsAt),e.endsAt?xt(e.endsAt):"open-ended",er(e.reason??"",50),e.createdBy?.displayName??e.createdBy?.email??"-"]);return ne(["ID","Status","Starts","Ends","Reason","By"],t,"(no maintenance windows)")}var vn=`Global options:
52
+ --api-key <key> API key (or set THIRTYSEVENAUDITS_API_KEY)
53
+ --json Force JSON output (also on when stdout is not a TTY)
54
+ -q, --quiet Suppress spinners and status output (implies --json)`,En=' {"error":{"message":"<message>","code":"<code>"}}';function T(n){let t=[];return n.context!=null&&t.push(n.context),t.push(vn),n.output!=null&&t.push(`Output (--json or piped):
55
+ ${n.output}`),n.errorCodes!=null&&t.push(`Errors (exit code 1, JSON on stderr when using --json or non-TTY):
56
+ ${En}
57
+ Codes: ${n.errorCodes.join(" | ")}`),t.push(`Examples:
58
+ ${n.examples.map(e=>` $ ${e}`).join(`
59
+ `)}`),`
60
+ ${t.join(`
61
+
62
+ `)}`}function At(n,t){n.urlId&&n.url&&g(t,"Pass either --url-id or --url, not both.","ambiguous_target"),!n.urlId&&!n.url&&g(t,"Which monitored URL? Pass --url-id <uuid> or --url <https://...>.","missing_target")}function Q(n,t){let e=[n.project,n.urlId,n.url].filter(Boolean).length;e>1&&g(t,"Pass only one of --project, --url-id or --url.","ambiguous_target"),e===0&&g(t,"Which project? Pass --project <uuid>, or --url-id / --url of one of its monitored URLs.","missing_target")}async function Z(n){try{return await n.request("GET","/v2/me")}catch(t){if(t instanceof C&&t.statusCode===404)return null;throw t}}function nr(n){let t=n.trim();try{let e=new URL(t);e.hash="";let r=e.pathname.replace(/\/+$/,"");return`${e.protocol}//${e.host.toLowerCase()}${r}${e.search}`}catch{return t.replace(/\/+$/,"")}}async function Tn(n){let t=await Z(n);return t?.apiKey?.organizationId?[t.apiKey.organizationId]:(await n.request("GET","/v2/organizations")).map(r=>r.id)}async function Sn(n){let t=[];for(let e of await Tn(n)){let r;try{r=await n.request("GET",`/v2/organizations/${e}/projects`)}catch(i){if(i instanceof C&&i.statusCode===404)continue;throw i}for(let i of r)t.push(...await n.request("GET",`/v2/projects/${i.id}/monitored-urls`))}return t}async function it(n,t){if(t.urlId)return n.request("GET",`/v2/monitored-urls/${t.urlId}`);let e=nr(t.url??""),r=(await Sn(n)).filter(i=>nr(i.url)===e);if(r.length===0)throw new C(`No monitored URL matches ${t.url}. Add it on 37audits.com first, or pass --url-id.`,"url_not_found");if(r.length>1)throw new C(`${r.length} monitored URLs match ${t.url}; pass --url-id with one of: ${r.map(i=>i.id).join(", ")}.`,"ambiguous_target");return r[0]}async function tt(n,t){return t.project?t.project:(await it(n,t)).projectId}var ir=new w("latest").description("Show the latest result of every auditor on a monitored URL").option("--url-id <uuid>","Monitored URL id").option("--url <url>","Monitored URL address (resolved to its id)").option("--fail-on <level>",'Exit 2 when a result is "fail" (fail|error) or "warning" (also warnings)').addHelpText("after",T({context:`One row per enabled auditor with its newest result. Pending results never trip --fail-on.
63
+ Exit codes: 0 ok, 1 error, 2 a --fail-on gate tripped.`,output:' [{"id":"...","auditorId":"...","auditorName":"...","status":"success","message":"...","createdAt":"...","checks":[...]}]',errorCodes:["missing_target","url_not_found","not_found","invalid_options"],examples:["37audits audit latest --url https://www.example.com","37audits audit latest --url-id 4f2c... --fail-on warning"]})).action(async(n,t)=>{let e=v(t);At(n,e);let r;try{r=n.failOn?bt(n.failOn):void 0}catch(a){g(e,a.message,"invalid_options")}let i=await R(e),o=await b(e,"Resolving monitored URL...",()=>it(i,n)),s=await b(e,"Fetching latest results...",()=>i.request("GET",`/v2/monitored-urls/${o.id}/audit-results/latest`),"fetch_error");if(x(e,s,a=>{console.log(Ot(a)),console.log(`
64
+ ${X(a)} on ${o.url}`)}),r){let a=Ct(s,r);a>0&&(D(`${a} result(s) at or above "${r}"; exiting with code ${2}.`,{json:e.json}),process.exit(2))}});var $n={ms:1,s:1e3,m:6e4,h:36e5};function q(n){let t=/^\s*(\d+(?:\.\d+)?)\s*(ms|s|m|h)?\s*$/i.exec(n);if(!t)throw new Error(`Invalid duration "${n}". Use a number with a unit, e.g. 30s, 5m, 2h.`);let e=Number(t[1]),r=(t[2]??"s").toLowerCase();return Math.round(e*$n[r])}var or=require("node:fs");function vt(n,t){let e=process.env.GITHUB_OUTPUT;return e?((0,or.appendFileSync)(e,`${n}=${t}
65
+ `),!0):!1}var kn=100;async function Et(n,t){let e=[],r=0;for(;;){try{if(e=(await n.request("GET",`/v2/monitored-urls/${t.urlId}/audit-results`,{query:{groupId:t.groupId,size:kn},retry:!1})).content??[],r=e.filter(s=>s.status==="pending").length,e.length>0&&r===0)return{results:e,pending:0,timedOut:!1}}catch(o){if(o instanceof C&&(o.code==="network_error"||o.code==="server_error"))t.onError?.(o.message);else throw o}let i=t.deadline-Date.now();if(i<=0)return{results:e,pending:r,timedOut:!0};t.onTick?.(r,e.length,Math.max(0,Math.round(i/1e3))),await _t(Math.min(t.intervalMs,i))}}var sr=new w("run").description("Start an audit run on a monitored URL").option("--url-id <uuid>","Monitored URL id").option("--url <url>","Monitored URL address (resolved to its id)").option("--auditor <id>","Run only this auditor (repeatable); default: every enabled auditor",(n,t)=>[...t,n],[]).option("--note <text>","Note stored with the run (e.g. the deploy)").option("--wait","Poll until every auditor has finished").option("--timeout <duration>","How long --wait polls (e.g. 120s, 5m)","120s").option("--interval <duration>","Polling interval","5s").option("--fail-on <level>",'Exit 2 when a result is "fail" (fail|error) or "warning" (also warnings); implies --wait').addHelpText("after",T({context:`Starts the run (HTTP 202) and prints one entry per auditor. With --wait it polls the
66
+ run group until no result is pending; a timeout is a warning, not an error, unless
67
+ --fail-on is set. In GitHub Actions the group id is written to $GITHUB_OUTPUT as group_id.
68
+ Exit codes: 0 ok, 1 error, 2 a --fail-on gate tripped, 130 cancelled.`,output:` without --wait: [{"id":"...","urlId":"...","auditor":{"id":"...","name":"..."},"status":"pending","groupId":"..."}]
69
+ with --wait: {"groupId":"...","results":[{"auditorId":"...","auditorName":"...","status":"success","message":"..."}],"pending":0,"timedOut":false}`,errorCodes:["missing_target","url_not_found","insufficient_credits","not_found","conflict","wait_timeout","invalid_duration","invalid_options"],examples:["37audits audit run --url https://www.example.com --wait",'37audits audit run --url-id 4f2c... --note "Deploy of app at main@8c811f7" --wait --timeout 3m',"37audits audit run --url https://www.example.com --fail-on fail"]})).action(async(n,t)=>{let e=v(t);At(n,e);let r,i=0,o=0;try{r=n.failOn?bt(n.failOn):void 0}catch(p){g(e,p.message,"invalid_options")}try{i=q(n.timeout),o=q(n.interval)}catch(p){g(e,p.message,"invalid_duration")}let s=!!(n.wait||r),a=await R(e),l=await b(e,"Resolving monitored URL...",()=>it(a,n)),c=await b(e,`Starting audit on ${l.url}...`,()=>a.request("POST",`/v2/monitored-urls/${l.id}/audit-results/run`,{query:{note:n.note,auditorId:n.auditor}}),"run_error"),u=c[0]?.groupId;if(u||g(e,"The API started no auditors (nothing enabled on this URL?).","run_error"),vt("group_id",u),!s){x(e,c,p=>{console.log(`Started ${p.length} auditor${p.length===1?"":"s"} on ${l.url} (group ${u}).`)});return}let d=G("Waiting for auditors...",e.quiet),m=await Et(a,{urlId:l.id,groupId:u,deadline:Date.now()+i,intervalMs:o,onTick:(p,f,_)=>d.update(`${p}/${f||c.length} auditors still pending, ${_}s left...`),onError:p=>d.update(`Poll failed, retrying: ${p}`)});if(d.stop(),m.timedOut&&(r&&g(e,`Timed out after ${n.timeout} with ${m.pending} auditor(s) still pending (group ${u}).`,"wait_timeout"),D(`Timed out after ${n.timeout} with ${m.pending} auditor(s) still pending; results will still land on 37audits.com.`,{json:e.json})),x(e,{groupId:u,results:m.results,pending:m.pending,timedOut:m.timedOut},p=>{console.log(Ot(p.results)),console.log(`
70
+ ${X(p.results)} on ${l.url}`)}),r){let p=Ct(m.results,r);p>0&&(D(`${p} result(s) at or above "${r}"; exiting with code ${2}.`,{json:e.json}),process.exit(2))}});var ar=new w("audit").description("Run audits and read results for a monitored URL").addCommand(sr).addCommand(ir);var ie=B(K(),1);var ur=require("node:child_process");function lr(n){return new Promise(t=>{let e=process.platform==="win32"?"cmd.exe":process.platform==="darwin"?"open":"xdg-open",r=n.replaceAll('"',""),i=process.platform==="win32"?["/c","start",'""',`"${r}"`]:[n];(0,ur.execFile)(e,i,{timeout:5e3,windowsVerbatimArguments:!0},o=>t(!o))})}async function In(n){let t=await Z(n);return t||(await n.request("GET","/v2/organizations"),null)}function Pn(n){if(!n)return"";if(n.apiKey){let t=n.apiKey.organizationName??n.apiKey.organizationId;return`API key "${n.apiKey.name??n.apiKey.id}" for ${t} (${n.apiKey.role})`}return n.email??n.userId}async function Rn(n,t){n.startsWith("tsa_")||g(t,"Invalid API key format: 37 Audits keys start with tsa_live_ or tsa_test_.","invalid_key_format");let e=new Y({type:"api_key",key:n,source:"flag"}),r=await b(t,"Validating API key...",()=>In(e),"validation_failed"),i=Jt({type:"api_key",api_key:n});x(t,{success:!0,authKind:"api_key",...r?.apiKey&&{organization:r.apiKey.organizationName??r.apiKey.organizationId,role:r.apiKey.role},configPath:i},()=>{let o=Pn(r);console.log(`${o?`${o} `:"API key "}saved to ${i}`)})}async function jn(n){let t=Ne();t||g(n,`Browser login is not configured in this build. Set ${O.oauthClientId} to the CLI's OAuth client id, or use --api-key.`,"oauth_not_configured");let e=Ue(),{codeVerifier:r,codeChallenge:i,state:o}=Fe(),s;try{s=await We()}catch(_){return A({message:H(_,"Could not start the login callback server"),code:_ instanceof y?_.code:"oauth_error"},{json:n.json})}let a=De({authUrl:e,clientId:t,redirectUri:s.redirectUri,state:o,codeChallenge:i}),l=await lr(a);n.quiet||console.error(l?`Opening your browser to sign in to 37 Audits. If nothing happens, open:
71
+ ${ie.default.blue(a)}`:`Open this link in your browser to sign in:
72
+ ${ie.default.blue(a)}`);let c=G("Waiting for the browser...",n.quiet),u;try{let _=await s.waitForCallback;if(_.state!==o)throw new y("The callback state does not match this login attempt. Run `37audits login` again.","oauth_state_mismatch");u=_.code,c.stop("Browser sign-in complete")}catch(_){return c.fail("Sign-in failed"),A({message:H(_,"Sign-in failed"),code:_ instanceof y?_.code:"oauth_error"},{json:n.json})}finally{s.close()}let d=await b(n,"Exchanging the authorization code...",()=>Be({authUrl:e,clientId:t,code:u,redirectUri:s.redirectUri,codeVerifier:r}),"oauth_error"),m=Zt(d,{authUrl:e,clientId:t}),p=Jt(m),f=null;try{f=await Z(new Y({type:"oauth",credential:m,source:"config"}))}catch{}x(n,{success:!0,authKind:"user",...f?.email&&{email:f.email},configPath:p},()=>{console.log(`Logged in${f?.email?` as ${f.email}`:""}. Credentials saved to ${p}`)})}var cr=new w("login").description("Sign in through the browser, or save an API key for CI").option("--api-key <key>","API key to store (tsa_live_... or tsa_test_...)").addHelpText("after",T({context:`Interactive: opens the browser for an OAuth sign-in to your 37 Audits account.
73
+ Non-interactive (CI, piped, --json): pass --api-key; or skip login entirely and set ${O.apiKey}.
74
+ Credentials are stored with mode 0600 in ${F()}.`,output:` {"success":true,"authKind":"user","email":"you@example.com","configPath":"<path>"}
75
+ {"success":true,"authKind":"api_key","organization":"Acme","role":"admin","configPath":"<path>"}`,errorCodes:["missing_api_key","invalid_key_format","validation_failed","oauth_not_configured","oauth_port_unavailable","oauth_timeout","oauth_state_mismatch","oauth_error"],examples:["37audits login","37audits login --api-key tsa_live_...",`${O.apiKey}=tsa_live_... 37audits whoami (no login needed)`]})).action(async(n,t)=>{let e=v(t),r=(n.apiKey??t.parent?.opts()?.apiKey)?.trim();if(r){await Rn(r,e);return}(!J()||e.json)&&g(e,`Browser sign-in needs an interactive terminal. In CI pass --api-key <key> or set ${O.apiKey}.`,"missing_api_key"),await jn(e)});var dr=new w("logout").description("Remove the stored credentials").addHelpText("after",T({context:"Deletes the credentials file. Environment variables are not touched.",output:' {"success":true,"removed":true,"configPath":"<path>"}',errorCodes:["remove_failed"],examples:["37audits logout"]})).action((n,t)=>{let e=v(t),r;try{r=Ie()}catch(i){return A({message:H(i,"Could not remove the credentials"),code:"remove_failed"},{json:e.json})}x(e,{success:!0,removed:r.removed,configPath:r.path},({removed:i,configPath:o})=>{console.log(i?`Logged out (removed ${o})`:"Not logged in.")})});var mr=new w("end").description('End the active maintenance window (runs the "after" audit)').argument("[id]","Window id; default: the project's active window").option("--project <uuid>","Project id").option("--url-id <uuid>","A monitored URL id; its project is used").option("--url <url>","A monitored URL address; its project is used").addHelpText("after",T({context:`Needs an organization admin. Ending a window runs every auditor on the project once more
76
+ ("after" run) so the timeline shows a before/after pair. A window that already ended (for
77
+ example by its --ends-in cap) answers window_not_active.`,output:' {"id":"...","status":"ended","endsAt":"...","runs":[{"monitoredUrlId":"...","groupId":"..."}]}',errorCodes:["missing_target","no_active_window","window_not_active","not_found","forbidden"],examples:["37audits mw end --project 2b7e...","37audits mw end 9c1d... --project 2b7e..."]})).action(async(n,t,e)=>{let r=v(e);Q(t,r);let i=await R(r),s=`/v2/projects/${await b(r,"Resolving project...",()=>tt(i,t))}/maintenance-windows`,a=n;if(!a){let u=(await b(r,"Finding the active window...",()=>i.request("GET",s))).find(d=>d.status==="active");u||g(r,"This project has no active maintenance window.","no_active_window"),a=u.id}let l=await b(r,"Ending maintenance window...",async()=>{try{return await i.request("POST",`${s}/${a}/end`)}catch(c){throw c instanceof C&&c.statusCode===409?new C(`Window ${a} is not active: ${c.message}`,"window_not_active",409):c}},"end_error");x(r,l,()=>{let c=l.runs?.length??0;console.log(`Maintenance window ${l.id} ended${c?`; after-run started on ${c} URL${c===1?"":"s"}`:""}.`)})});var Hn=new Set(["scheduled","active","ended"]),pr=new w("list").description("List a project's maintenance windows, newest first").option("--project <uuid>","Project id").option("--url-id <uuid>","A monitored URL id; its project is used").option("--url <url>","A monitored URL address; its project is used").option("--status <status>","Only scheduled, active or ended windows").option("--limit <n>","Maximum number of windows","20").addHelpText("after",T({output:' [{"id":"...","projectId":"...","status":"ended","reason":"...","startsAt":"...","endsAt":"...","preRunAt":"...","postRunAt":"...","createdBy":{"displayName":"..."}}]',errorCodes:["missing_target","invalid_options","not_found","url_not_found"],examples:["37audits mw list --project 2b7e...","37audits mw list --url https://www.example.com --status active"]})).action(async(n,t)=>{let e=v(t);Q(n,e),n.status&&!Hn.has(n.status)&&g(e,`--status must be scheduled, active or ended (got "${n.status}").`,"invalid_options");let r=Number(n.limit);(!Number.isInteger(r)||r<1)&&g(e,`--limit must be a positive integer (got "${n.limit}").`,"invalid_options");let i=await R(e),o=await b(e,"Resolving project...",()=>tt(i,n)),a=(await b(e,"Fetching maintenance windows...",()=>i.request("GET",`/v2/projects/${o}/maintenance-windows`),"list_error")).filter(l=>!n.status||l.status===n.status).slice(0,r);x(e,a,l=>{console.log(rr(l))})});var hr=500,fr=new w("start").description("Start a maintenance window now, optionally auditing first").option("--project <uuid>","Project id").option("--url-id <uuid>","A monitored URL id; its project is used").option("--url <url>","A monitored URL address; its project is used").option("--reason <text>","Why (e.g. the deploy); shown on the timeline").option("--run-auditors",'Run every auditor on the project first ("before" run)').option("--ends-in <duration>","Close the window automatically after e.g. 30m").option("--ends-at <iso>","Close the window automatically at this ISO-8601 time").option("--wait","With --run-auditors: poll until the before-run has finished").option("--timeout <duration>","How long --wait polls","120s").option("--interval <duration>","Polling interval","5s").addHelpText("after",T({context:`Needs an organization admin (an admin-role API key in CI). While the window is active,
78
+ scheduled audits are paused; manual runs are badged. Give it an end (--ends-in / --ends-at) so a
79
+ crashed pipeline cannot leave monitoring paused: the API ends it and runs the "after" audit.
80
+ In GitHub Actions the window id is written to $GITHUB_OUTPUT as window_id for a later
81
+ \`37audits mw end\` step. A --wait timeout is a warning: the window stays open, which is what a
82
+ deploy needs.`,output:' {"id":"...","projectId":"...","status":"active","reason":"...","startsAt":"...","endsAt":"...","runs":[{"monitoredUrlId":"...","groupId":"..."}], "wait":{"results":63,"pending":0,"timedOut":false}}',errorCodes:["missing_target","window_conflict","forbidden","invalid_duration","invalid_options","url_not_found"],examples:['37audits mw start --project 2b7e... --reason "Deploy of api at main@8c811f7" --run-auditors --wait --ends-in 30m','37audits mw start --url https://www.example.com --reason "DB migration"']})).action(async(n,t)=>{let e=v(t);Q(n,e),n.endsIn&&n.endsAt&&g(e,"Pass either --ends-in or --ends-at, not both.","invalid_options"),n.wait&&!n.runAuditors&&g(e,"--wait only makes sense with --run-auditors.","invalid_options"),n.reason&&n.reason.length>hr&&g(e,`--reason is limited to ${hr} characters.`,"invalid_options");let r,i=0,o=0;try{n.endsIn&&(r=new Date(Date.now()+q(n.endsIn)).toISOString()),i=q(n.timeout),o=q(n.interval)}catch(S){g(e,S.message,"invalid_duration")}if(n.endsAt){let S=new Date(n.endsAt);Number.isNaN(S.getTime())&&g(e,`--ends-at "${n.endsAt}" is not an ISO-8601 time.`,"invalid_duration"),r=S.toISOString()}let s=await R(e),a=await b(e,"Resolving project...",()=>tt(s,n)),l=await b(e,n.runAuditors?"Starting maintenance window and the before-run...":"Starting maintenance window...",async()=>{try{return await s.request("POST",`/v2/projects/${a}/maintenance-windows/start`,{body:{runAuditors:!!n.runAuditors,...r&&{endsAt:r},...n.reason&&{reason:n.reason}}})}catch(S){throw S instanceof C&&S.statusCode===409?new C(`Could not start: ${S.message} (another window is active or overlaps).`,"window_conflict",409):S}},"start_error");vt("window_id",l.id);let c=()=>`Maintenance window ${l.id} started (${l.endsAt?`ends ${xt(l.endsAt)}`:"open-ended"})`;if(!n.wait){x(e,l,()=>{let S=l.runs?.length??0;console.log(`${c()}${S?`; before-run started on ${S} URL${S===1?"":"s"}`:""}.`)});return}let u=l.runs??[],d=Date.now()+i,m=G("Waiting for the before-run...",e.quiet),p=[],f=0,_=!1;for(let[S,oe]of u.entries()){let Tt=await Et(s,{urlId:oe.monitoredUrlId,groupId:oe.groupId,deadline:d,intervalMs:o,onTick:(St,yr,br)=>m.update(`URL ${S+1}/${u.length}: ${St}/${yr||"?"} auditors pending, ${br}s left...`),onError:St=>m.update(`Poll failed, retrying: ${St}`)});p.push(...Tt.results),f+=Tt.pending,_=_||Tt.timedOut}m.stop(),_&&D(`Timed out after ${n.timeout} with ${f} auditor result(s) still pending; the window is active and the results will still land.`,{json:e.json}),x(e,{...l,wait:{results:p.length,pending:f,timedOut:_}},()=>{console.log(`${c()}; before-run: ${p.length?X(p):"no auditors ran"}.`)})});var gr=new w("mw").alias("maintenance-window").description("Start, end and list a project's maintenance windows").addCommand(fr).addCommand(mr).addCommand(pr);var _r=new w("whoami").description("Show who the current credentials belong to").addHelpText("after",T({context:"Resolves the credential (--api-key, then THIRTYSEVENAUDITS_API_KEY, then the credentials file) and asks the API who it is.",output:` {"authenticated":true,"authKind":"user","source":"config","userId":"<uuid>","email":"you@example.com","configPath":"<path>"}
83
+ {"authenticated":true,"authKind":"api_key","source":"env","userId":"<uuid>","apiKey":{"id":"<uuid>","name":"CI","role":"admin","organizationId":"<uuid>","organizationName":"Acme"},"configPath":"<path>"}`,errorCodes:["not_authenticated","auth_error"],examples:["37audits whoami","37audits whoami --json"]})).action(async(n,t)=>{let e=v(t),r=await R(e),i=await b(e,"Checking credentials...",()=>Z(r)),o=F();x(e,{authenticated:!0,authKind:i?.authKind??(r.authKind==="oauth"?"user":"api_key"),source:r.authSource,...i?.userId&&{userId:i.userId},...i?.email&&{email:i.email},...i?.apiKey&&{apiKey:i.apiKey},configPath:o},s=>{let a=[` Auth: ${s.authKind==="api_key"?"API key":"user session"} (from ${s.source})`];i?.apiKey?a.push(` Key: ${i.apiKey.name??i.apiKey.id} (${i.apiKey.role})`,` Org: ${i.apiKey.organizationName??i.apiKey.organizationId}`):i?.email&&a.push(` Email: ${i.email}`),s.source==="config"&&a.push(` Config: ${o}`),console.log(["",...a,""].join(`
84
+ `))})});var Un=130;function Nn(){process.stderr.isTTY&&process.stderr.write("\r\x1B[2K"),console.error("Cancelled."),process.exit(Un)}function wr(){process.on("SIGINT",Nn),process.stdout.on("error",n=>{throw n.code==="EPIPE"&&process.exit(0),n}),process.on("uncaughtException",n=>{A({message:H(n,"An unexpected error occurred"),code:"unexpected_error"})})}wr();var Ln=new w().name("37audits").description("37 Audits CLI \u2014 run audits and manage maintenance windows from CI/CD").configureHelp({showGlobalOptions:!0,styleTitle:n=>V.default.gray(n)}).configureOutput({writeErr:n=>{process.stderr.write(n.replace(/^error:/,()=>V.default.red("error:")))}}).helpCommand(!0).version(`${Je} v${ft}`,"-v, --version","Output the current version").option("--api-key <key>",`API key (overrides ${O.apiKey} and the stored credentials)`).option("--json","Force JSON output (also on when stdout is not a TTY)").option("-q, --quiet","Suppress spinners and status output (implies --json)").hook("preAction",(n,t)=>{t.optsWithGlobals().quiet&&n.setOptionValue("json",!0)}).addHelpText("after",`
85
+ ${V.default.gray("Examples:")}
86
+
87
+ ${V.default.blue("$ 37audits login")}
88
+ ${V.default.blue("$ 37audits audit run --url https://www.example.com --wait")}
89
+ ${V.default.blue('$ 37audits mw start --project <uuid> --run-auditors --wait --ends-in 30m --reason "Deploy"')}
90
+ ${V.default.blue("$ 37audits mw end --project <uuid>")}
91
+
92
+ ${V.default.gray("Environment:")}
93
+
94
+ ${O.apiKey} API key for CI (no login needed)
95
+ ${O.apiBaseUrl} API base URL (default https://api.37audits.com)
96
+ `).addCommand(cr).addCommand(dr).addCommand(_r).addCommand(ar).addCommand(gr);Ln.parseAsync().catch(n=>{A({message:H(n,"An unexpected error occurred"),code:"unexpected_error"})});
package/package.json CHANGED
@@ -1,6 +1,54 @@
1
1
  {
2
2
  "name": "37audits-cli",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.2",
4
+ "description": "37 Audits CLI — run audits, read results and wrap deploys in maintenance windows from CI/CD",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "37audits": "./dist/cli.cjs"
9
+ },
10
+ "files": [
11
+ "dist/cli.cjs"
12
+ ],
13
+ "engines": {
14
+ "node": ">=22"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/37audits/37audits-cli.git"
19
+ },
20
+ "homepage": "https://github.com/37audits/37audits-cli#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/37audits/37audits-cli/issues"
23
+ },
24
+ "keywords": [
25
+ "37audits",
26
+ "cli",
27
+ "audit",
28
+ "ci",
29
+ "maintenance-window"
30
+ ],
31
+ "scripts": {
32
+ "dev": "tsx src/cli.ts",
33
+ "build": "node scripts/build.mjs",
34
+ "lint": "biome check .",
35
+ "lint:fix": "biome check --write .",
36
+ "typecheck": "tsc --noEmit",
37
+ "test": "vitest run",
38
+ "test:e2e": "vitest run --config vitest.config.e2e.ts",
39
+ "prepack": "npm run build"
40
+ },
41
+ "dependencies": {
42
+ "@commander-js/extra-typings": "14.0.0",
43
+ "commander": "14.0.3",
44
+ "picocolors": "1.1.1"
45
+ },
46
+ "devDependencies": {
47
+ "@biomejs/biome": "2.4.11",
48
+ "@types/node": "24.12.4",
49
+ "esbuild": "0.28.1",
50
+ "tsx": "4.22.4",
51
+ "typescript": "5.9.3",
52
+ "vitest": "4.1.9"
53
+ }
54
+ }