requestshield 0.1.4 → 0.1.6
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 +421 -85
- package/config/.env.prod +7 -0
- package/package.json +21 -12
- package/skills/requestshield/SKILL.md +299 -307
- package/skills/requestshield/assets/AGENTS.codex.md +62 -62
- package/skills/requestshield/references/backend-java-core.md +128 -128
- package/skills/requestshield/references/backend-spring-boot.md +145 -145
- package/skills/requestshield/references/browser-manual.md +210 -210
- package/skills/requestshield/references/browser-seamless.md +156 -164
- package/skills/requestshield/references/cli.md +107 -182
- package/skills/requestshield/references/integration-planning.md +362 -389
- package/skills/requestshield/references/troubleshooting.md +114 -118
- package/src/agent-detector.mjs +102 -74
- package/src/api-client.mjs +115 -79
- package/src/args.mjs +140 -80
- package/src/browser-opener.mjs +32 -0
- package/src/cli.mjs +277 -51
- package/src/commands/agent-setup.mjs +182 -185
- package/src/commands/application-mutations.mjs +33 -0
- package/src/commands/application-response.mjs +55 -0
- package/src/commands/apps-get.mjs +20 -0
- package/src/commands/apps-list.mjs +94 -0
- 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 +150 -0
- package/src/entrypoint.mjs +24 -0
- package/src/errors.mjs +3 -1
- package/src/main.mjs +5 -24
- 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/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
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
|
-
Current package version: `0.1.
|
|
7
|
+
Current package version: `0.1.6`.
|
|
8
8
|
|
|
9
9
|
## Requirements
|
|
10
10
|
|
|
@@ -12,50 +12,211 @@ Current package version: `0.1.4`.
|
|
|
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
|
|
25
|
-
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]
|
|
61
|
+
requestshield apps get <app-key>
|
|
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]
|
|
26
66
|
requestshield update check
|
|
27
67
|
requestshield --help
|
|
28
68
|
requestshield --version
|
|
29
69
|
```
|
|
30
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
|
+
|
|
31
76
|
### Sign in
|
|
32
77
|
|
|
33
78
|
```console
|
|
34
79
|
requestshield signin
|
|
35
80
|
```
|
|
36
81
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
|
45
144
|
|
|
46
145
|
```console
|
|
47
|
-
requestshield
|
|
146
|
+
requestshield auth status --json
|
|
147
|
+
requestshield signout
|
|
48
148
|
```
|
|
49
149
|
|
|
50
|
-
|
|
51
|
-
the
|
|
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.
|
|
156
|
+
|
|
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.
|
|
52
159
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
160
|
+
### Applications and credentials
|
|
161
|
+
|
|
162
|
+
```console
|
|
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>
|
|
172
|
+
```
|
|
57
173
|
|
|
58
|
-
|
|
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.
|
|
59
220
|
|
|
60
221
|
### Install the Agent Skill
|
|
61
222
|
|
|
@@ -87,7 +248,10 @@ Existing Skill content is preserved. Add `--force` to replace it:
|
|
|
87
248
|
requestshield agent setup --codex --force
|
|
88
249
|
```
|
|
89
250
|
|
|
90
|
-
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.
|
|
91
255
|
|
|
92
256
|
### Check for updates
|
|
93
257
|
|
|
@@ -95,11 +259,12 @@ Restart the selected agent or start a new task after installation.
|
|
|
95
259
|
requestshield update check
|
|
96
260
|
```
|
|
97
261
|
|
|
98
|
-
Checks the npm registry for the latest `requestshield` version.
|
|
99
|
-
version exists, the CLI displays the current and latest versions
|
|
262
|
+
Checks the npm registry for the latest `requestshield` version.
|
|
263
|
+
When a newer version exists, the CLI displays the current and latest versions
|
|
264
|
+
and asks:
|
|
100
265
|
|
|
101
266
|
```text
|
|
102
|
-
|
|
267
|
+
Install RequestShield <version> globally with npm? (y/N):
|
|
103
268
|
```
|
|
104
269
|
|
|
105
270
|
Entering `y` runs the equivalent of:
|
|
@@ -109,7 +274,9 @@ npm install --global requestshield@<latest-version>
|
|
|
109
274
|
```
|
|
110
275
|
|
|
111
276
|
This command requires an interactive terminal, Node.js, and npm. It updates a
|
|
112
|
-
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.
|
|
113
280
|
|
|
114
281
|
## Install with npm
|
|
115
282
|
|
|
@@ -121,17 +288,22 @@ requestshield --help
|
|
|
121
288
|
requestshield --version
|
|
122
289
|
```
|
|
123
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
|
+
|
|
124
296
|
Run a specific version without installing globally:
|
|
125
297
|
|
|
126
298
|
```console
|
|
127
|
-
npx --yes requestshield@0.1.
|
|
128
|
-
npx --yes requestshield@0.1.
|
|
299
|
+
npx --yes --package=requestshield@0.1.6 requestshield --help
|
|
300
|
+
npx --yes --package=requestshield@0.1.6 requestshield signin
|
|
129
301
|
```
|
|
130
302
|
|
|
131
303
|
To invoke the update command through npx, include the package name:
|
|
132
304
|
|
|
133
305
|
```console
|
|
134
|
-
npx --yes requestshield@0.1.
|
|
306
|
+
npx --yes --package=requestshield@0.1.6 requestshield update check
|
|
135
307
|
```
|
|
136
308
|
|
|
137
309
|
Do not run `npx update check`; npm interprets `update` as the name of a different
|
|
@@ -139,66 +311,231 @@ npm package.
|
|
|
139
311
|
|
|
140
312
|
## Local development
|
|
141
313
|
|
|
142
|
-
|
|
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.
|
|
143
317
|
|
|
144
318
|
```console
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
npm run
|
|
148
|
-
npm test
|
|
149
|
-
npm run build
|
|
319
|
+
corepack pnpm install
|
|
320
|
+
cd requestshield-cli
|
|
321
|
+
npm run check
|
|
150
322
|
node src/main.mjs --help
|
|
151
323
|
node src/main.mjs --version
|
|
324
|
+
npm run requestshield-qat -- --help
|
|
325
|
+
npm run requestshield-stg -- --help
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
From `requestshield-cli/`, select the private runner explicitly:
|
|
329
|
+
|
|
330
|
+
```console
|
|
331
|
+
npx --offline --prefix ./dev requestshield-qat --help
|
|
332
|
+
npx --offline --prefix ./dev requestshield-stg --help
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
For plain `npx` commands, change to `requestshield-cli/dev/`:
|
|
336
|
+
|
|
337
|
+
```console
|
|
338
|
+
cd dev
|
|
339
|
+
npx requestshield-qat --help
|
|
340
|
+
npx requestshield-stg --help
|
|
341
|
+
```
|
|
342
|
+
|
|
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.
|
|
368
|
+
|
|
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.
|
|
372
|
+
|
|
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.
|
|
377
|
+
|
|
378
|
+
### Source CLI + Stytch Test + Management QAT
|
|
379
|
+
|
|
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:
|
|
392
|
+
|
|
393
|
+
```console
|
|
394
|
+
cd requestshield-cli
|
|
395
|
+
npx --offline --prefix ./dev requestshield-qat signin
|
|
396
|
+
npx --offline --prefix ./dev requestshield-qat apps list --json
|
|
152
397
|
```
|
|
153
398
|
|
|
154
|
-
The
|
|
155
|
-
|
|
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.
|
|
156
450
|
|
|
157
451
|
### Test the update flow locally
|
|
158
452
|
|
|
159
453
|
The manual update test mocks the registry and install operation, so it does not
|
|
160
454
|
publish a package or change the installed version:
|
|
161
455
|
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
npm run test:update-check:manual
|
|
165
|
-
Remove-Item Env:REQUESTSHIELD_MOCK_LATEST_VERSION
|
|
456
|
+
```console
|
|
457
|
+
npm run test:update-check:manual -- 0.1.6
|
|
166
458
|
```
|
|
167
459
|
|
|
168
460
|
After entering `y`, the test prints `[MOCK] Would install ...`.
|
|
169
461
|
|
|
170
|
-
##
|
|
462
|
+
## Build and inspect the package
|
|
463
|
+
|
|
464
|
+
From `requestshield-cli/`, create and inspect the runtime package:
|
|
465
|
+
|
|
466
|
+
```console
|
|
467
|
+
npm run build
|
|
468
|
+
node build/src/main.mjs --help
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
The generated, gitignored `build/` directory contains:
|
|
472
|
+
|
|
473
|
+
```text
|
|
474
|
+
build/
|
|
475
|
+
src/
|
|
476
|
+
config/.env.prod
|
|
477
|
+
skills/requestshield/
|
|
478
|
+
README.md
|
|
479
|
+
package.json
|
|
480
|
+
```
|
|
171
481
|
|
|
172
|
-
The
|
|
173
|
-
|
|
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.
|
|
174
487
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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.
|
|
178
491
|
|
|
179
|
-
|
|
492
|
+
Optionally inspect the npm file list or create a local distribution archive:
|
|
180
493
|
|
|
181
494
|
```console
|
|
182
|
-
npm pack --dry-run
|
|
495
|
+
npm pack ./build --dry-run
|
|
496
|
+
npm pack ./build --pack-destination ./build
|
|
183
497
|
```
|
|
184
498
|
|
|
185
|
-
|
|
499
|
+
The latter writes `build/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.
|
|
504
|
+
|
|
505
|
+
### Skill source and direct packing
|
|
506
|
+
|
|
507
|
+
The canonical Skill is stored at `../skills/requestshield`. The build copies it
|
|
508
|
+
to `build/skills/requestshield`; maintain only the canonical source.
|
|
509
|
+
|
|
510
|
+
Direct packing from `requestshield-cli/` remains supported:
|
|
186
511
|
|
|
187
512
|
```console
|
|
513
|
+
npm pack --dry-run
|
|
188
514
|
npm pack
|
|
189
515
|
```
|
|
190
516
|
|
|
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.
|
|
520
|
+
|
|
191
521
|
## Publish to npm
|
|
192
522
|
|
|
193
523
|
npm does not allow an existing package version to be overwritten. Check the
|
|
194
524
|
published version, merge the latest team changes, then choose a new version in
|
|
195
525
|
`package.json` before publishing.
|
|
196
526
|
|
|
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.
|
|
530
|
+
|
|
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.
|
|
535
|
+
|
|
197
536
|
```console
|
|
198
537
|
npm view requestshield version
|
|
199
|
-
npm run
|
|
200
|
-
npm run typecheck
|
|
201
|
-
npm test
|
|
538
|
+
npm run check
|
|
202
539
|
npm pack --dry-run
|
|
203
540
|
npm publish --access public
|
|
204
541
|
```
|
|
@@ -208,39 +545,38 @@ Verify the published package:
|
|
|
208
545
|
```console
|
|
209
546
|
npm view requestshield version
|
|
210
547
|
npm dist-tag ls requestshield
|
|
211
|
-
npx --yes requestshield@0.1.
|
|
548
|
+
npx --yes --package=requestshield@0.1.6 requestshield --version
|
|
212
549
|
```
|
|
213
550
|
|
|
214
551
|
## Management API contract
|
|
215
552
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
`POST /v1/cli/keys/rotate` with Bearer authentication and an empty JSON body.
|
|
240
|
-
The response contains `appKey` and the one-time `apiSecret`.
|
|
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.
|
|
241
575
|
|
|
242
576
|
## Security notes
|
|
243
577
|
|
|
244
578
|
- Never place an access token or Secret Key in URLs, command arguments, logs,
|
|
245
579
|
test fixtures, or committed files.
|
|
246
|
-
- 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.
|
package/config/.env.prod
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Public production settings shipped in the npm package. Never add secrets.
|
|
2
|
+
API_URL=https://api.intellifend.ai
|
|
3
|
+
# Fill in the production Connected App values before releasing sign-in.
|
|
4
|
+
OAUTH_ISSUER=
|
|
5
|
+
# Optional callback iss pin; blank uses OAUTH_ISSUER.
|
|
6
|
+
OAUTH_AUTHORIZATION_ISSUER=
|
|
7
|
+
OAUTH_CLIENT_ID=
|