requestshield 0.1.5 → 0.1.7
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.
- package/README.md +407 -269
- package/config/.env.prod +5 -0
- package/package.json +8 -5
- package/skills/requestshield/SKILL.md +55 -63
- package/skills/requestshield/assets/AGENTS.codex.md +17 -17
- package/skills/requestshield/references/backend-java-core.md +3 -3
- package/skills/requestshield/references/backend-spring-boot.md +3 -3
- package/skills/requestshield/references/browser-manual.md +4 -4
- package/skills/requestshield/references/browser-seamless.md +7 -15
- package/skills/requestshield/references/cli.md +93 -169
- package/skills/requestshield/references/integration-planning.md +19 -46
- package/skills/requestshield/references/troubleshooting.md +26 -30
- package/src/api-client.mjs +106 -165
- package/src/args.mjs +108 -151
- package/src/browser-opener.mjs +32 -0
- package/src/cli.mjs +50 -28
- package/src/commands/agent-setup.mjs +34 -37
- package/src/commands/application-mutations.mjs +33 -0
- package/src/commands/application-response.mjs +55 -0
- package/src/commands/apps-get.mjs +3 -47
- package/src/commands/apps-list.mjs +40 -36
- package/src/commands/auth-status.mjs +37 -0
- package/src/commands/keys-create.mjs +7 -38
- package/src/commands/mutation-support.mjs +110 -0
- package/src/commands/secret-commands.mjs +45 -0
- package/src/commands/signin.mjs +70 -57
- package/src/commands/signout.mjs +9 -0
- package/src/commands/update-check.mjs +12 -4
- package/src/config.mjs +145 -3
- package/src/entrypoint.mjs +24 -0
- package/src/errors.mjs +3 -1
- package/src/main.mjs +2 -21
- package/src/oauth-client.mjs +153 -0
- package/src/oauth-loopback.mjs +120 -0
- package/src/session-files.mjs +213 -0
- package/src/session-store.mjs +177 -64
- package/src/commands/billing-get.mjs +0 -110
- package/src/commands/challenge-volume.mjs +0 -81
- package/src/commands/contract.mjs +0 -106
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# RequestShield CLI
|
|
2
2
|
|
|
3
3
|
Customer-facing CLI for IntelliFend RequestShield. It allows customers to sign
|
|
4
|
-
in,
|
|
5
|
-
or Claude, and update an npm-installed CLI.
|
|
4
|
+
in, manage applications and their credentials, install the RequestShield Skill
|
|
5
|
+
for Codex or Claude, and update an npm-installed CLI.
|
|
6
6
|
|
|
7
7
|
Current package version: `0.1.5`.
|
|
8
8
|
|
|
@@ -12,124 +12,211 @@ Current package version: `0.1.5`.
|
|
|
12
12
|
- A RequestShield customer management API.
|
|
13
13
|
- npm for local development and publishing.
|
|
14
14
|
|
|
15
|
-
The
|
|
16
|
-
|
|
15
|
+
The public npm package installs only `requestshield`, using production
|
|
16
|
+
configuration. QAT and STG runners are available only in this repository:
|
|
17
|
+
|
|
18
|
+
| Command | Environment | Configuration file |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| `requestshield` | PROD | `config/.env.prod`; API URL is `https://api.intellifend.ai`, while production OAuth values remain unconfigured. |
|
|
21
|
+
| `npx requestshield-qat <args>` from `requestshield-cli/dev/` | QAT | `config/.env.qat`; includes the public Stytch Test issuer/client ID and Management API QAT URL. |
|
|
22
|
+
| `npx requestshield-stg <args>` from `requestshield-cli/dev/` | STG | `config/.env.stg`; requires the actual STG API and OAuth values before use. |
|
|
23
|
+
|
|
24
|
+
The private `dev/package.json` resolves these `npx` commands to this checkout's
|
|
25
|
+
wrappers without a separate install, link or publish step. They are local source
|
|
26
|
+
runners, not packages fetched from the registry. From the parent
|
|
27
|
+
`requestshield-cli/` directory, use `npx --offline --prefix ./dev requestshield-qat <args>`
|
|
28
|
+
(or `requestshield-stg`). The matching `npm run requestshield-qat -- <args>` and
|
|
29
|
+
`npm run requestshield-stg -- <args>` scripts are alternatives. A bare QAT/STG
|
|
30
|
+
`npx` command from the parent directory does not select the private child package
|
|
31
|
+
and may search dependencies or the registry instead.
|
|
32
|
+
|
|
33
|
+
Each runner reads its package-relative file directly with Node.js `parseEnv`.
|
|
34
|
+
It does not load a `.env` from the current working directory or read OAuth/API
|
|
35
|
+
settings from `process.env`. Shell variables cannot switch the selected profile
|
|
36
|
+
or override its configuration. Missing STG/PROD values fail explicitly and never
|
|
37
|
+
fall back to QAT.
|
|
38
|
+
|
|
39
|
+
These files contain only public product settings: `OAUTH_ISSUER`,
|
|
40
|
+
`OAUTH_CLIENT_ID`, `API_URL` and optional `OAUTH_AUTHORIZATION_ISSUER`. They must never contain
|
|
41
|
+
access tokens, refresh tokens, client secrets or application API secrets. Release
|
|
42
|
+
maintainers must populate the production public settings before publishing a
|
|
43
|
+
working production sign-in flow. The npm package includes only `.env.prod`;
|
|
44
|
+
QAT/STG files and the private development package are excluded.
|
|
17
45
|
|
|
18
46
|
## Implemented commands
|
|
19
47
|
|
|
48
|
+
Examples use the production command. From `requestshield-cli/`, substitute
|
|
49
|
+
`npx --offline --prefix ./dev requestshield-qat` or `requestshield-stg` for local
|
|
50
|
+
QAT/STG runs. Plain `npx requestshield-qat` requires `requestshield-cli/dev/`.
|
|
51
|
+
|
|
20
52
|
```console
|
|
21
|
-
requestshield signin
|
|
22
|
-
requestshield
|
|
23
|
-
requestshield
|
|
24
|
-
requestshield
|
|
53
|
+
requestshield signin [--no-open]
|
|
54
|
+
requestshield auth status [--json]
|
|
55
|
+
requestshield signout
|
|
56
|
+
requestshield keys create --app-name <name> [--idempotency-key <key>]
|
|
57
|
+
requestshield keys rotate <app-key> [--idempotency-key <key>] [--yes]
|
|
58
|
+
requestshield keys reveal <app-key> [--yes]
|
|
59
|
+
requestshield keys revoke <app-key> [--idempotency-key <key>] [--yes]
|
|
60
|
+
requestshield apps list [--json] [--limit <1-100>] [--cursor <cursor> | --all]
|
|
25
61
|
requestshield apps get <app-key>
|
|
26
|
-
requestshield
|
|
27
|
-
requestshield
|
|
28
|
-
requestshield
|
|
29
|
-
requestshield agent setup --codex [--force]
|
|
30
|
-
requestshield agent setup --claude [--force]
|
|
62
|
+
requestshield apps rename <app-key> --name <name> [--idempotency-key <key>]
|
|
63
|
+
requestshield apps enable <app-key> [--idempotency-key <key>]
|
|
64
|
+
requestshield apps disable <app-key> [--idempotency-key <key>] [--yes]
|
|
65
|
+
requestshield agent setup [--codex | --claude] [--force]
|
|
31
66
|
requestshield update check
|
|
32
67
|
requestshield --help
|
|
33
68
|
requestshield --version
|
|
34
69
|
```
|
|
35
70
|
|
|
71
|
+
`contract`, `challenge volume` and `get billing` return `COMMAND_UNAVAILABLE`
|
|
72
|
+
before configuration, authentication or network access. Their Management API
|
|
73
|
+
endpoints are not implemented. `server` and `credentials status` are also absent
|
|
74
|
+
from the supported command surface.
|
|
75
|
+
|
|
36
76
|
### Sign in
|
|
37
77
|
|
|
38
78
|
```console
|
|
39
79
|
requestshield signin
|
|
40
80
|
```
|
|
41
81
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
82
|
+
Opens the configured login portal in your browser using Authorization Code with
|
|
83
|
+
S256 PKCE. Sign in, verify your email if needed, then approve the displayed
|
|
84
|
+
permissions. The browser must run on the same machine as the CLI. Use
|
|
85
|
+
`requestshield signin --no-open` to open the printed authorization URL yourself.
|
|
86
|
+
If automatic browser opening fails, the CLI also prints that URL.
|
|
87
|
+
|
|
88
|
+
The CLI listens only on `127.0.0.1` at a dynamically allocated port and waits up
|
|
89
|
+
to ten minutes. The browser callback carries an authorization code, state and
|
|
90
|
+
optional issuer metadata, not access or refresh tokens. The CLI exchanges that
|
|
91
|
+
code and its PKCE verifier directly with the configured identity provider,
|
|
92
|
+
without a client secret. With `offline_access` granted, the exchange returns
|
|
93
|
+
access and refresh tokens; the refresh token stays in the protected local session
|
|
94
|
+
file. Sign-in succeeds only after saving that session. Closing the browser does
|
|
95
|
+
not cancel the command; press Ctrl+C to stop.
|
|
96
|
+
|
|
97
|
+
The selected runner reads only its environment's configuration file. QAT's public
|
|
98
|
+
settings are included in the repository. Production and STG sign-in still require
|
|
99
|
+
their actual public settings in the corresponding file.
|
|
100
|
+
Discovery must match `OAUTH_ISSUER` and use
|
|
101
|
+
an HTTPS token endpoint on that issuer's origin; an HTTP loopback authorization page is allowed for local
|
|
102
|
+
development. The requested permissions are `offline_access`,
|
|
103
|
+
`theair:applications:read`, `theair:applications:write`, `theair:secrets:read`, and
|
|
104
|
+
`theair:secrets:write`. The CLI does not request `full_access`.
|
|
105
|
+
|
|
106
|
+
If the provider includes `iss` in its authorization response, the CLI compares
|
|
107
|
+
it exactly with `OAUTH_AUTHORIZATION_ISSUER`, falling back to `OAUTH_ISSUER` when
|
|
108
|
+
the optional setting is absent or blank. This is an opaque identifier, not a URL
|
|
109
|
+
to fetch or normalize. Stytch's QAT response uses its project identifier, which
|
|
110
|
+
differs from the HTTPS discovery issuer; the QAT file pins that value explicitly.
|
|
111
|
+
This setting does not change discovery, token transport or the version-1 session
|
|
112
|
+
format and its API URL/discovery issuer/client ID binding.
|
|
113
|
+
|
|
114
|
+
Access and rotating refresh tokens are stored in an owner-only session file,
|
|
115
|
+
bound to the API URL, issuer and client ID. Commands refresh an access token
|
|
116
|
+
when it has less than 60 seconds remaining. Refresh is serialized between CLI
|
|
117
|
+
processes and credentials are replaced atomically. Older device-flow records
|
|
118
|
+
and records for another environment require a new sign-in. An uncertain refresh
|
|
119
|
+
result requires a new sign-in rather than retrying a potentially consumed token;
|
|
120
|
+
the previous credential file is retained if writing or replacing the new file
|
|
121
|
+
fails. If a filesystem durability check fails after the atomic replacement, the
|
|
122
|
+
CLI reports uncertain persistence: the new record may already be stored. It does
|
|
123
|
+
not roll back to a potentially consumed refresh token or repeat the exchange.
|
|
124
|
+
|
|
125
|
+
Each profile keeps a separate session. On Windows, the base directory is
|
|
126
|
+
`%LOCALAPPDATA%/IntelliFend/RequestShield`; on macOS/Linux it is
|
|
127
|
+
`$XDG_STATE_HOME/intellifend/requestshield` (default
|
|
128
|
+
`~/.local/state/intellifend/requestshield`). Production retains `session.json`,
|
|
129
|
+
while QAT uses `qat/session.json` and STG uses `stg/session.json` beneath that base.
|
|
130
|
+
Existing sessions are not moved or deleted. A session remains usable only when
|
|
131
|
+
its saved issuer, client ID and API URL exactly match the selected file; changing
|
|
132
|
+
those values requires a new sign-in. A command
|
|
133
|
+
that crashes while holding the refresh lock can leave `session.json.lock` beside
|
|
134
|
+
it. The CLI deliberately does not steal that lock from a possibly suspended
|
|
135
|
+
process. If it remains locked, confirm every RequestShield command has exited
|
|
136
|
+
before removing only the lock file, then sign in again if requested.
|
|
137
|
+
|
|
138
|
+
Authenticated commands use the private saved session. The CLI does not accept
|
|
139
|
+
`REQUESTSHIELD_ACCESS_TOKEN` or other credential overrides from the shell or
|
|
140
|
+
configuration files. A separate non-interactive authentication mechanism is not
|
|
141
|
+
implemented.
|
|
142
|
+
|
|
143
|
+
### Local session status and sign-out
|
|
50
144
|
|
|
51
145
|
```console
|
|
52
|
-
requestshield
|
|
146
|
+
requestshield auth status --json
|
|
147
|
+
requestshield signout
|
|
53
148
|
```
|
|
54
149
|
|
|
55
|
-
|
|
56
|
-
the
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- `--yes` skips the confirmation prompt for non-interactive automation.
|
|
150
|
+
Status reads only the selected profile's saved session. It never refreshes,
|
|
151
|
+
contacts the provider or prints credentials. JSON includes `profile`, `apiUrl`,
|
|
152
|
+
`issuer`, `clientId`, `state` and `localOnly: true`; applicable records also include
|
|
153
|
+
`expiresAt` and `scopes`. States are `signed_out`, `valid`, `expired`,
|
|
154
|
+
`refresh_uncertain`, `config_mismatch`, `invalid` and `configuration_error`.
|
|
155
|
+
`valid` describes local expiry and binding checks, not current provider acceptance.
|
|
62
156
|
|
|
63
|
-
|
|
157
|
+
Sign-out removes only the selected local session under its lock. It does not
|
|
158
|
+
revoke the provider grant, sign out the browser or remove another profile's session.
|
|
64
159
|
|
|
65
|
-
###
|
|
160
|
+
### Applications and credentials
|
|
66
161
|
|
|
67
162
|
```console
|
|
68
|
-
requestshield
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```console
|
|
79
|
-
requestshield apps list
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
Lists the applications the signed-in user is allowed to access:
|
|
83
|
-
|
|
84
|
-
```text
|
|
85
|
-
APP KEY NAME STATUS
|
|
86
|
-
app_123 Production Website pending
|
|
87
|
-
app_456 Staging Website active
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
Use `--json` to print the stable `{ "data": [...], "nextCursor": null }`
|
|
91
|
-
response shape. The CLI allowlists `appKey`, `name`, `status`, `createdAt`, and
|
|
92
|
-
`updatedAt`; it never prints an API Secret returned accidentally by the service.
|
|
93
|
-
|
|
94
|
-
### Get an application
|
|
95
|
-
|
|
96
|
-
```console
|
|
97
|
-
requestshield apps get app_123
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Returns `app_key`, `name`, and the application's current status as JSON.
|
|
101
|
-
Valid statuses are:
|
|
102
|
-
|
|
103
|
-
- `ready` — the application key was just created and no traffic has been received.
|
|
104
|
-
- `active` — the application is receiving traffic.
|
|
105
|
-
- `deactivated` — the application has been deactivated.
|
|
106
|
-
|
|
107
|
-
The requested App Key must exactly match the key in the API response.
|
|
108
|
-
|
|
109
|
-
### Get challenge volume
|
|
110
|
-
|
|
111
|
-
```console
|
|
112
|
-
requestshield challenge volume app_123 \
|
|
113
|
-
--from 2026-08-01T00:00:00Z \
|
|
114
|
-
--to 2026-08-27T23:59:59Z \
|
|
115
|
-
--granularity day
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
Returns challenge/request volume as JSON. Times must be ISO-8601 timestamps, and
|
|
119
|
-
`--from` cannot be later than `--to`. Granularity values such as `hour` and `day`
|
|
120
|
-
are passed to the API, which owns the supported set.
|
|
121
|
-
|
|
122
|
-
Challenge volume proves that traffic reached the platform; it does not prove
|
|
123
|
-
that the protected backend rejects requests without a valid token.
|
|
124
|
-
|
|
125
|
-
### Get billing information
|
|
126
|
-
|
|
127
|
-
```console
|
|
128
|
-
requestshield get billing app_123
|
|
163
|
+
requestshield keys create --app-name "Checkout"
|
|
164
|
+
requestshield apps list --json --all
|
|
165
|
+
requestshield apps get <app-key>
|
|
166
|
+
requestshield apps rename <app-key> --name "Checkout API"
|
|
167
|
+
requestshield apps disable <app-key>
|
|
168
|
+
requestshield apps enable <app-key>
|
|
169
|
+
requestshield keys rotate <app-key>
|
|
170
|
+
requestshield keys reveal <app-key>
|
|
171
|
+
requestshield keys revoke <app-key>
|
|
129
172
|
```
|
|
130
173
|
|
|
131
|
-
|
|
132
|
-
|
|
174
|
+
Create makes a new application and initial secret. Rotate replaces the current
|
|
175
|
+
secret while preserving the App Key. Reveal retrieves the current active secret;
|
|
176
|
+
revoke invalidates that secret. Disable changes the application's administrative
|
|
177
|
+
state while retaining its secret; enable requires an active secret.
|
|
178
|
+
|
|
179
|
+
Create, rename and enable run without a confirmation prompt. Disable, rotate,
|
|
180
|
+
revoke and reveal require confirmation; `--yes` explicitly skips that prompt.
|
|
181
|
+
App names are not unique; commands targeting an existing app require its exact
|
|
182
|
+
App Key. The App Key is public. Create, rotate and reveal print a backend-only
|
|
183
|
+
API secret when supplied by the service; store it directly in a backend secret
|
|
184
|
+
manager. The CLI never saves that secret.
|
|
185
|
+
|
|
186
|
+
Mutations except reveal accept `--idempotency-key`. The CLI generates a fresh
|
|
187
|
+
key for each invocation when omitted and prints it to stderr before dispatch,
|
|
188
|
+
leaving JSON stdout intact. Retain it for interruption or crash recovery. The CLI
|
|
189
|
+
never automatically retries a mutation.
|
|
190
|
+
For an uncertain result, repeat the **identical command and arguments with the
|
|
191
|
+
same key printed in the error**, within seven days of the original attempt.
|
|
192
|
+
A new key starts a new operation. After seven days, inspect the application before
|
|
193
|
+
acting. Create/rotate replay returns `apiSecret: null`; the CLI suggests explicit
|
|
194
|
+
`keys reveal` and never reveals or rotates automatically.
|
|
195
|
+
|
|
196
|
+
Enable, disable and revoke return HTTP `202` acceptance. Publication is
|
|
197
|
+
asynchronous; acceptance and an `enabled` status do not prove global propagation.
|
|
198
|
+
|
|
199
|
+
### List and inspect applications
|
|
200
|
+
|
|
201
|
+
List defaults to one page. `--limit` accepts 1–100; `--cursor` continues a previous
|
|
202
|
+
page. `--all` traverses at most 100 pages and rejects repeated cursors or an
|
|
203
|
+
unfinished traversal at that bound. It cannot be combined with `--cursor`.
|
|
204
|
+
|
|
205
|
+
`apps list --json` returns `{ "data": [...], "nextCursor": null }` (or a cursor
|
|
206
|
+
for an incomplete single-page listing). `apps get` and rename return
|
|
207
|
+
`{ "data": { ... } }`. Application objects have exactly `appKey`, `name`,
|
|
208
|
+
`status`, `createdAt` and `updatedAt`; unexpected fields and secrets are not printed.
|
|
209
|
+
|
|
210
|
+
| Status | Meaning |
|
|
211
|
+
| --- | --- |
|
|
212
|
+
| `attention_required` | Publication is blocked or uncertain. |
|
|
213
|
+
| `pending` | The latest configuration is awaiting publication. |
|
|
214
|
+
| `disabled` | The saved administrative state disables the application. |
|
|
215
|
+
| `revoked` | The enabled application has a revoked secret. |
|
|
216
|
+
| `enabled` | The saved application and active secret are enabled. |
|
|
217
|
+
|
|
218
|
+
These aggregate states describe configuration, not traffic, backend enforcement
|
|
219
|
+
or exact secret state while pending, blocked or disabled.
|
|
133
220
|
|
|
134
221
|
### Install the Agent Skill
|
|
135
222
|
|
|
@@ -161,7 +248,10 @@ Existing Skill content is preserved. Add `--force` to replace it:
|
|
|
161
248
|
requestshield agent setup --codex --force
|
|
162
249
|
```
|
|
163
250
|
|
|
164
|
-
Restart the selected agent or start a new task after installation.
|
|
251
|
+
Restart the selected agent or start a new task after installation. Published
|
|
252
|
+
packages use their bundled skill. Repository runners resolve the canonical
|
|
253
|
+
`../skills/requestshield` using source-checkout markers; they do not depend on
|
|
254
|
+
the working directory or require a staged packaging copy.
|
|
165
255
|
|
|
166
256
|
### Check for updates
|
|
167
257
|
|
|
@@ -174,7 +264,7 @@ When a newer version exists, the CLI displays the current and latest versions
|
|
|
174
264
|
and asks:
|
|
175
265
|
|
|
176
266
|
```text
|
|
177
|
-
|
|
267
|
+
Install RequestShield <version> globally with npm? (y/N):
|
|
178
268
|
```
|
|
179
269
|
|
|
180
270
|
Entering `y` runs the equivalent of:
|
|
@@ -184,7 +274,9 @@ npm install --global @intellifend/requestshield@<latest-version>
|
|
|
184
274
|
```
|
|
185
275
|
|
|
186
276
|
This command requires an interactive terminal, Node.js, and npm. It updates a
|
|
187
|
-
global npm installation
|
|
277
|
+
global npm installation. Repository checkouts and pinned `npx` invocations are
|
|
278
|
+
managed separately. QAT/STG `update check` prints local Git/source guidance and
|
|
279
|
+
returns without querying the registry, prompting or installing anything.
|
|
188
280
|
|
|
189
281
|
## Install with npm
|
|
190
282
|
|
|
@@ -196,6 +288,11 @@ requestshield --help
|
|
|
196
288
|
requestshield --version
|
|
197
289
|
```
|
|
198
290
|
|
|
291
|
+
The public package exposes only the `requestshield` executable and production
|
|
292
|
+
configuration. Internal QAT/STG runners require this repository's private `dev/`
|
|
293
|
+
package and are not installed by the public package. Source changes do not alter
|
|
294
|
+
versions already published.
|
|
295
|
+
|
|
199
296
|
Run a specific version without installing globally:
|
|
200
297
|
|
|
201
298
|
```console
|
|
@@ -214,231 +311,272 @@ npm package.
|
|
|
214
311
|
|
|
215
312
|
## Local development
|
|
216
313
|
|
|
217
|
-
|
|
314
|
+
Use Node.js >=22.21 for repository development. Install workspace dependencies
|
|
315
|
+
from the repository root; the CLI check includes contract tests that require the
|
|
316
|
+
sibling Management API package's dependencies.
|
|
218
317
|
|
|
219
318
|
```console
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
npm run
|
|
223
|
-
npm test
|
|
224
|
-
npm run build
|
|
319
|
+
corepack pnpm install
|
|
320
|
+
cd requestshield-cli
|
|
321
|
+
npm run check
|
|
225
322
|
node src/main.mjs --help
|
|
226
323
|
node src/main.mjs --version
|
|
324
|
+
npm run requestshield-qat -- --help
|
|
325
|
+
npm run requestshield-stg -- --help
|
|
227
326
|
```
|
|
228
327
|
|
|
229
|
-
|
|
230
|
-
does not generate a standalone executable.
|
|
328
|
+
From `requestshield-cli/`, select the private runner explicitly:
|
|
231
329
|
|
|
232
|
-
|
|
330
|
+
```console
|
|
331
|
+
npx --offline --prefix ./dev requestshield-qat --help
|
|
332
|
+
npx --offline --prefix ./dev requestshield-stg --help
|
|
333
|
+
```
|
|
233
334
|
|
|
234
|
-
|
|
235
|
-
publish a package or change the installed version:
|
|
335
|
+
For plain `npx` commands, change to `requestshield-cli/dev/`:
|
|
236
336
|
|
|
237
|
-
```
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
337
|
+
```console
|
|
338
|
+
cd dev
|
|
339
|
+
npx requestshield-qat --help
|
|
340
|
+
npx requestshield-stg --help
|
|
241
341
|
```
|
|
242
342
|
|
|
243
|
-
|
|
343
|
+
`dev/package.json` is private and exposes only local QAT/STG wrappers. No
|
|
344
|
+
additional installation or linking is needed. Keep this working directory for
|
|
345
|
+
the plain `npx` commands; they resolve the current checkout rather than a
|
|
346
|
+
published QAT/STG artifact. To keep package resolution offline, use:
|
|
347
|
+
|
|
348
|
+
```console
|
|
349
|
+
npx --offline requestshield-qat --help
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Both invocation forms run `dev/main-qat.mjs` or `dev/main-stg.mjs` and read the
|
|
353
|
+
parent package's fixed `config/.env.qat` or `config/.env.stg` file
|
|
354
|
+
(`../config/.env.qat` or `../config/.env.stg` from `dev/`). To change an internal
|
|
355
|
+
endpoint or public client, edit that file; do not set shell overrides. All three
|
|
356
|
+
profile files use the same public keys:
|
|
357
|
+
|
|
358
|
+
```dotenv
|
|
359
|
+
OAUTH_ISSUER=https://identity.example.com
|
|
360
|
+
# Optional exact callback iss; blank uses OAUTH_ISSUER.
|
|
361
|
+
OAUTH_AUTHORIZATION_ISSUER=
|
|
362
|
+
OAUTH_CLIENT_ID=public-client-id
|
|
363
|
+
API_URL=https://management.example.com
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
Do not put credentials in these files. Session storage remains private and
|
|
367
|
+
separate from package configuration.
|
|
244
368
|
|
|
245
|
-
|
|
369
|
+
`npm run test:contract` exercises the real Management router in
|
|
370
|
+
`management-api/test/core/cli-contract.spec.mjs`; `npm run check` includes it.
|
|
371
|
+
These isolated tests use synthetic credentials and do not prove live acceptance.
|
|
246
372
|
|
|
247
|
-
|
|
248
|
-
|
|
373
|
+
`npm run build` validates source syntax and creates the standalone runtime
|
|
374
|
+
package in `build/`. The package gate runs this build after its other checks.
|
|
375
|
+
See [Build and inspect the package](#build-and-inspect-the-package) for the output
|
|
376
|
+
layout and tarball commands.
|
|
249
377
|
|
|
250
|
-
|
|
251
|
-
canonical Skill to `skills/requestshield`. The `postpack` hook removes that
|
|
252
|
-
generated copy after packaging.
|
|
378
|
+
### Source CLI + Stytch Test + Management QAT
|
|
253
379
|
|
|
254
|
-
|
|
380
|
+
Use the current source build for this workflow; the source package version does
|
|
381
|
+
not establish that these changes have been published to npm. `config/.env.qat`
|
|
382
|
+
contains the public configuration:
|
|
383
|
+
|
|
384
|
+
| Setting | QAT file value |
|
|
385
|
+
| --- | --- |
|
|
386
|
+
| Discovery issuer (`OAUTH_ISSUER`) | `https://incongruous-cherry-0283.customers.stytch.dev` |
|
|
387
|
+
| Authorization response issuer (`OAUTH_AUTHORIZATION_ISSUER`) | `stytch.com/project-test-968146e2-6f34-4936-9d1c-a7e966813c70` |
|
|
388
|
+
| Public client ID | `connected-app-test-5ff62ca4-c0eb-48c3-84e5-06ae8f669998` |
|
|
389
|
+
| API URL | `https://management-api-qat.intellifend.ai` |
|
|
390
|
+
|
|
391
|
+
From the repository root:
|
|
255
392
|
|
|
256
393
|
```console
|
|
257
|
-
|
|
394
|
+
cd requestshield-cli
|
|
395
|
+
npx --offline --prefix ./dev requestshield-qat signin
|
|
396
|
+
npx --offline --prefix ./dev requestshield-qat apps list --json
|
|
258
397
|
```
|
|
259
398
|
|
|
260
|
-
|
|
399
|
+
The prefix selects the local private package; do not omit it from this directory.
|
|
400
|
+
Alternatively, from `requestshield-cli/`, run
|
|
401
|
+
`npm run requestshield-qat -- signin` and
|
|
402
|
+
`npm run requestshield-qat -- apps list --json`.
|
|
403
|
+
|
|
404
|
+
The private developer package and its runners are excluded from the public npm
|
|
405
|
+
package. Old `REQUESTSHIELD_OAUTH_*`, `REQUESTSHIELD_API_URL` and
|
|
406
|
+
`REQUESTSHIELD_ACCESS_TOKEN` shell variables have no effect on profile selection,
|
|
407
|
+
configuration or credentials.
|
|
408
|
+
|
|
409
|
+
The current QAT provider opens the deployed portal at
|
|
410
|
+
`https://requestshield-qat.intellifend.ai/oauth/authorize`; this CLI smoke test
|
|
411
|
+
does not require a local portal server.
|
|
412
|
+
If verification or password reset opens another tab, complete it and return to
|
|
413
|
+
the original authorization tab to continue. Email tokens are not shared between
|
|
414
|
+
tabs by the portal.
|
|
415
|
+
|
|
416
|
+
Provider settings to verify for QAT acceptance:
|
|
417
|
+
|
|
418
|
+
- Authorization URL `https://requestshield-qat.intellifend.ai/oauth/authorize`.
|
|
419
|
+
- A First Party Public Connected App with redirect `http://127.0.0.1/callback`
|
|
420
|
+
registered without a port, enabling the CLI's dynamic loopback port.
|
|
421
|
+
- The four custom scopes above plus `offline_access`, with user role permissions
|
|
422
|
+
and explicit consent; keep full-access and offline-consent bypass disabled.
|
|
423
|
+
- Access-token lifetime at most 900 seconds, audience
|
|
424
|
+
`urn:theair:management-api:dev`, and access-only template markers
|
|
425
|
+
`theair_token_use=management_access` and `theair_client_id` matching the client ID.
|
|
426
|
+
- SDK allowed origin and email callbacks matching
|
|
427
|
+
`https://requestshield-qat.intellifend.ai`.
|
|
428
|
+
|
|
429
|
+
Public discovery rechecked on 2026-09-16 advertises
|
|
430
|
+
`https://requestshield-qat.intellifend.ai/oauth/authorize`. A read-only QAT run on
|
|
431
|
+
the same date successfully listed all applications (an empty list). Local auth
|
|
432
|
+
status was expired before the command and valid with a later expiry afterward,
|
|
433
|
+
confirming automatic refresh and API acceptance for that session. This does not
|
|
434
|
+
audit every provider setting or verify live mutations, publication or production.
|
|
435
|
+
Source changes do not configure the provider or deploy the portal.
|
|
436
|
+
See the [Stytch CLI guide](https://stytch.com/docs/connected-apps/guides/cli-agents)
|
|
437
|
+
and [refresh contract](https://stytch.com/docs/api-reference/consumer/api/connected-apps/exchange-refresh-token).
|
|
438
|
+
|
|
439
|
+
Acceptance requires a real browser sign-in, then `apps list --json` accepted by
|
|
440
|
+
QAT. Wait until the access token expires and repeat the command to verify refresh.
|
|
441
|
+
An account with no apps should return an empty list; do not create an app just
|
|
442
|
+
for this test. Local mock checks do not establish provider or deployed API acceptance.
|
|
443
|
+
|
|
444
|
+
To test local portal changes separately, start the
|
|
445
|
+
[portal](../theair-customer-portal-frontend/README.md) on `http://localhost:5173`
|
|
446
|
+
and coordinate with the provider owner to point the authorization URL to
|
|
447
|
+
`http://localhost:5173/oauth/authorize`, with matching SDK allowed origin and email
|
|
448
|
+
callbacks. That optional provider configuration change is not a prerequisite for
|
|
449
|
+
the default QAT CLI smoke test above.
|
|
450
|
+
|
|
451
|
+
### Test the update flow locally
|
|
452
|
+
|
|
453
|
+
The manual update test mocks the registry and install operation, so it does not
|
|
454
|
+
publish a package or change the installed version:
|
|
261
455
|
|
|
262
456
|
```console
|
|
263
|
-
npm
|
|
457
|
+
npm run test:update-check:manual -- 0.1.6
|
|
264
458
|
```
|
|
265
459
|
|
|
266
|
-
|
|
460
|
+
After entering `y`, the test prints `[MOCK] Would install ...`.
|
|
267
461
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
`
|
|
462
|
+
## Build and inspect the package
|
|
463
|
+
|
|
464
|
+
From `requestshield-cli/`, create and inspect the runtime package:
|
|
271
465
|
|
|
272
466
|
```console
|
|
273
|
-
npm
|
|
274
|
-
|
|
275
|
-
npm run typecheck
|
|
276
|
-
npm test
|
|
277
|
-
npm pack --dry-run
|
|
278
|
-
npm publish --access public
|
|
467
|
+
npm run build
|
|
468
|
+
node build/src/main.mjs --help
|
|
279
469
|
```
|
|
280
470
|
|
|
281
|
-
|
|
471
|
+
The generated, gitignored `build/` directory contains:
|
|
282
472
|
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
473
|
+
```text
|
|
474
|
+
build/
|
|
475
|
+
src/
|
|
476
|
+
config/.env.prod
|
|
477
|
+
skills/requestshield/
|
|
478
|
+
README.md
|
|
479
|
+
package.json
|
|
287
480
|
```
|
|
288
481
|
|
|
289
|
-
|
|
482
|
+
The CLI remains Node.js ESM; the build copies the runtime files without
|
|
483
|
+
compilation or bundling. The generated `package.json` preserves package metadata,
|
|
484
|
+
the production executable and the file allowlist, while omitting development
|
|
485
|
+
dependencies and source lifecycle scripts. The directory can therefore run and
|
|
486
|
+
be packed independently of the repository's source tooling.
|
|
290
487
|
|
|
291
|
-
|
|
488
|
+
The build creates a directory only, not a `.tgz` archive. Rebuild after changing
|
|
489
|
+
source, configuration, package metadata or Skill content; `build/` is generated
|
|
490
|
+
output and should not be edited directly.
|
|
292
491
|
|
|
293
|
-
|
|
492
|
+
Optionally inspect the npm file list or create a local distribution archive:
|
|
294
493
|
|
|
295
|
-
```
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
494
|
+
```console
|
|
495
|
+
npm pack ./build --dry-run
|
|
496
|
+
npm pack ./build --pack-destination ./build
|
|
299
497
|
```
|
|
300
498
|
|
|
301
|
-
The
|
|
302
|
-
and
|
|
499
|
+
The latter writes `build/intellifend-requestshield-<version>.tgz`. Archives are
|
|
500
|
+
generated artifacts, ignored by Git, and are not required in source control or
|
|
501
|
+
before publishing. The package listing should contain `src/main.mjs`,
|
|
502
|
+
`config/.env.prod` and the complete Skill, with no QAT/STG configuration or
|
|
503
|
+
private `dev/` package.
|
|
303
504
|
|
|
304
|
-
###
|
|
505
|
+
### Skill source and direct packing
|
|
305
506
|
|
|
306
|
-
|
|
507
|
+
The canonical Skill is stored at `../skills/requestshield`. The build copies it
|
|
508
|
+
to `build/skills/requestshield`; maintain only the canonical source.
|
|
307
509
|
|
|
308
|
-
|
|
309
|
-
`slow_down`. A successful response contains `accessToken` and optional account
|
|
310
|
-
metadata.
|
|
510
|
+
Direct packing from `requestshield-cli/` remains supported:
|
|
311
511
|
|
|
312
|
-
|
|
512
|
+
```console
|
|
513
|
+
npm pack --dry-run
|
|
514
|
+
npm pack
|
|
515
|
+
```
|
|
313
516
|
|
|
314
|
-
|
|
315
|
-
|
|
517
|
+
For this source-package path, `prepack` temporarily copies the canonical Skill to
|
|
518
|
+
`skills/requestshield`, and `postpack` removes that generated copy. The build
|
|
519
|
+
directory already includes its Skill and needs no staging hook.
|
|
316
520
|
|
|
317
|
-
|
|
521
|
+
## Publish to npm
|
|
318
522
|
|
|
319
|
-
|
|
320
|
-
|
|
523
|
+
npm does not allow an existing package version to be overwritten. Check the
|
|
524
|
+
published version, merge the latest team changes, then choose a new version in
|
|
525
|
+
`package.json` before publishing.
|
|
321
526
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
{
|
|
326
|
-
"appKey": "pk_AAAAAAAAAAAAAAAAAAAAAAAA",
|
|
327
|
-
"name": "Checkout",
|
|
328
|
-
"status": "pending",
|
|
329
|
-
"createdAt": "2026-09-11T00:00:00.000Z",
|
|
330
|
-
"updatedAt": "2026-09-11T00:00:00.000Z"
|
|
331
|
-
}
|
|
332
|
-
],
|
|
333
|
-
"nextCursor": null
|
|
334
|
-
}
|
|
335
|
-
```
|
|
527
|
+
Run the release commands from the source `requestshield-cli/` directory. Its
|
|
528
|
+
`prepublishOnly` hook runs the complete package check before publication;
|
|
529
|
+
`build/` is the inspection and packaging output.
|
|
336
530
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
### Get an application
|
|
343
|
-
|
|
344
|
-
`GET /v1/applications/{appKey}` with Bearer authentication and no request body.
|
|
345
|
-
The path segment is URL-encoded by the CLI.
|
|
346
|
-
|
|
347
|
-
```json
|
|
348
|
-
{
|
|
349
|
-
"ok": true,
|
|
350
|
-
"data": {
|
|
351
|
-
"app_key": "app_123",
|
|
352
|
-
"name": "Website",
|
|
353
|
-
"status": "active"
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
```
|
|
531
|
+
`npm publish` packages and uploads the module itself; a separate `npm pack`
|
|
532
|
+
command is optional. CI can install workspace dependencies from the repository
|
|
533
|
+
root and publish from this source package. Store a `.tgz` as a CI artifact only
|
|
534
|
+
when the release workflow needs a separately retained archive.
|
|
357
535
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
`GET /v1/applications/{appKey}/challenge-volume` with Bearer authentication and
|
|
365
|
-
optional `from`, `to`, and `granularity` query parameters.
|
|
366
|
-
|
|
367
|
-
```json
|
|
368
|
-
{
|
|
369
|
-
"ok": true,
|
|
370
|
-
"data": {
|
|
371
|
-
"app_key": "app_123",
|
|
372
|
-
"from": "2026-08-01T00:00:00Z",
|
|
373
|
-
"to": "2026-08-27T23:59:59Z",
|
|
374
|
-
"granularity": "day",
|
|
375
|
-
"challenge_count": 125430
|
|
376
|
-
}
|
|
377
|
-
}
|
|
536
|
+
```console
|
|
537
|
+
npm view @intellifend/requestshield version
|
|
538
|
+
npm run check
|
|
539
|
+
npm pack --dry-run
|
|
540
|
+
npm publish --access public
|
|
378
541
|
```
|
|
379
542
|
|
|
380
|
-
|
|
381
|
-
granularity, and a non-negative safe-integer `challenge_count`. It rebuilds the
|
|
382
|
-
output from these fields so unexpected API fields cannot be printed.
|
|
383
|
-
|
|
384
|
-
### Get billing information
|
|
385
|
-
|
|
386
|
-
`GET /v1/applications/{appKey}/billing` with Bearer authentication and no
|
|
387
|
-
request body.
|
|
388
|
-
|
|
389
|
-
```json
|
|
390
|
-
{
|
|
391
|
-
"ok": true,
|
|
392
|
-
"data": {
|
|
393
|
-
"plan": {
|
|
394
|
-
"tier": "pro",
|
|
395
|
-
"current_cycle": [
|
|
396
|
-
"2026-07-07T07:07:07",
|
|
397
|
-
"2027-07-07T07:07:07"
|
|
398
|
-
],
|
|
399
|
-
"billing": 1080,
|
|
400
|
-
"next_charge": "2027-07-07T07:07:07"
|
|
401
|
-
},
|
|
402
|
-
"usage": {
|
|
403
|
-
"monthly_quota": 100000,
|
|
404
|
-
"current_usage": 110000,
|
|
405
|
-
"overage_charge": 10
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
```
|
|
543
|
+
Verify the published package:
|
|
410
544
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
`GET /v1/integration-contract` with Bearer authentication and no request body.
|
|
417
|
-
|
|
418
|
-
```json
|
|
419
|
-
{
|
|
420
|
-
"ok": true,
|
|
421
|
-
"data": {
|
|
422
|
-
"contract_version": "2026-08-27",
|
|
423
|
-
"browser": {
|
|
424
|
-
"script_url": "https://intellifend.ai/intellifend.js",
|
|
425
|
-
"token_header": "X-IntelliFend-Token",
|
|
426
|
-
"available_modes": ["manual"]
|
|
427
|
-
},
|
|
428
|
-
"backend": {
|
|
429
|
-
"supported_languages": ["java"],
|
|
430
|
-
"min_jdk": 17
|
|
431
|
-
},
|
|
432
|
-
"release_state": "released"
|
|
433
|
-
}
|
|
434
|
-
}
|
|
545
|
+
```console
|
|
546
|
+
npm view @intellifend/requestshield version
|
|
547
|
+
npm dist-tag ls @intellifend/requestshield
|
|
548
|
+
npx --yes --package=@intellifend/requestshield@0.1.5 requestshield --version
|
|
435
549
|
```
|
|
436
550
|
|
|
437
|
-
|
|
438
|
-
|
|
551
|
+
## Management API contract
|
|
552
|
+
|
|
553
|
+
The authoritative HTTP definitions are [OpenAPI](../management-api/openapi.json)
|
|
554
|
+
and the [CLI mapping](../management-api/CLI-CONTRACT.md). All nine app/secret
|
|
555
|
+
operations are covered by the CLI-to-router contract gate.
|
|
556
|
+
|
|
557
|
+
| CLI command | Method and path |
|
|
558
|
+
| --- | --- |
|
|
559
|
+
| `keys create --app-name <name>` | `POST /v1/applications` with `{name}` |
|
|
560
|
+
| `apps list` | `GET /v1/applications` with optional `limit` and `cursor` |
|
|
561
|
+
| `apps get <app-key>` | `GET /v1/applications/{appKey}` |
|
|
562
|
+
| `apps rename <app-key> --name <name>` | `PATCH /v1/applications/{appKey}` with `{name}` |
|
|
563
|
+
| `apps enable` / `apps disable` | Bodyless `POST /v1/applications/{appKey}/enable` or `/disable` |
|
|
564
|
+
| `keys rotate` / `keys revoke` / `keys reveal` | Bodyless `POST /v1/applications/{appKey}/secret/rotate`, `/revoke` or `/reveal` |
|
|
565
|
+
|
|
566
|
+
Create and rotate return `{appKey,status,apiSecret}`; a matching replay has a
|
|
567
|
+
null secret. Reveal returns `{apiSecret}` and is not replayed. Application detail
|
|
568
|
+
and rename return `{data:Application}`; list returns `{data:Application[],nextCursor}`.
|
|
569
|
+
Lifecycle acknowledgements are `{status:"accepted"}`. Mutations other than reveal
|
|
570
|
+
send a caller-scoped `Idempotency-Key`.
|
|
571
|
+
|
|
572
|
+
Only OAuth access tokens authenticate these requests. Sign-in and refresh call
|
|
573
|
+
the identity provider directly; browser sessions, refresh tokens and application
|
|
574
|
+
API secrets cannot authenticate Management. No `/v1/cli/*` route is used.
|
|
439
575
|
|
|
440
576
|
## Security notes
|
|
441
577
|
|
|
442
578
|
- Never place an access token or Secret Key in URLs, command arguments, logs,
|
|
443
579
|
test fixtures, or committed files.
|
|
444
|
-
- The
|
|
580
|
+
- The three environment files contain public configuration only. OAuth
|
|
581
|
+
credentials belong exclusively in the protected saved session.
|
|
582
|
+
- The CLI does not persist API secrets returned by create, rotate or reveal.
|