@cantinasecurity/apex-cli 0.1.15 → 0.1.17
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/.claude/skills/apex-cli/SKILL.md +8 -5
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.mcp.claude.json +1 -1
- package/.mcp.codex.json +1 -1
- package/MARKETPLACE.md +1 -1
- package/README.md +16 -11
- package/dist/apex.js +1 -1
- package/dist/commands.js +50 -18
- package/dist/help.js +20 -6
- package/dist/mcp.js +4 -4
- package/dist/session.js +6 -3
- package/dist/shell.js +3 -3
- package/dist/telemetry.js +15 -2
- package/package.json +1 -1
- package/skills/apex-cli/SKILL.md +7 -5
|
@@ -25,26 +25,28 @@ Recommended workflow:
|
|
|
25
25
|
4. Call `apex-doctor` before starting a scan if workspace binding or source planning might be unclear.
|
|
26
26
|
5. If the directory is not bound to the right workspace, call `apex-workspaces` and `apex-workspace-use`.
|
|
27
27
|
6. Use `apex-scan`, `apex-status`, `apex-scans`, `apex-findings`, and `apex-export-findings` for the scan lifecycle.
|
|
28
|
-
7. For
|
|
29
|
-
8.
|
|
30
|
-
9.
|
|
28
|
+
7. For Lite scans, call `apex-scan` with `mode: "lite"` only when the company has Lite Scan beta access and the sources are provider-backed.
|
|
29
|
+
8. For PR scans, call `apex-scan` with `mode: "pr"` and `pullRequests`.
|
|
30
|
+
9. Use `apex-finding-comment` and `apex-finding-feedback` when the user wants to leave review notes or valid/invalid feedback on a finding.
|
|
31
|
+
10. For fix reviews, call `apex-finding-fix-review`. Pass `fixPrUrls` for a PR-based review, or omit them to review the current repository state when Bedrock has original scan repository context.
|
|
31
32
|
|
|
32
33
|
If the Apex MCP server is not configured, fall back to the local CLI:
|
|
33
34
|
|
|
34
35
|
- Prefer scripted commands over the interactive shell.
|
|
35
36
|
- Service-key management has no MCP tools. Never ask a user to paste a service key into a prompt, and never place one in an MCP call, tool result, source file, or log.
|
|
36
|
-
- If a user explicitly needs a new service key, tell them to run `apex login` if needed, then run `apex service-key create --
|
|
37
|
+
- If a user explicitly needs a new service key, tell them to run `apex login` if needed, then run `apex service-key create --name <name> --scope <scope[,scope]>` directly in their own terminal. Use only the required scopes: `scans:create` to trigger, `scans:read` to poll status, and `findings:read` to retrieve findings. A P1-only adapter can use `--scope scans:read,findings:read`; omitting `--scope` grants all three. The CLI automatically selects the company when only one is available and prompts when there are several; use `--company <id-or-handle>` to skip that prompt or resolve a non-interactive command. Service-key commands never initiate login, and the secret is shown once.
|
|
37
38
|
- For a headless HTTP adapter, read the service key from a secret-backed environment variable, send it as a bearer token to `/api/apex/v1`, and redact it from errors and logs. Do not shell out to `apex` or launch MCP from the adapter.
|
|
38
39
|
- Hosted API v1 requires the GitHub repository to be linked to an active Apex workspace. It never creates or mutates workspaces; use optional `workspaceId` to disambiguate matches and treat zero or ambiguous matches as HTTP `409` setup errors.
|
|
39
40
|
- Hosted scan triggers require `Idempotency-Key`. Reuse one stable key only for an identical transport/timeout/5xx retry. After correcting a definitive 4xx response, or for any new intended scan, use a new key.
|
|
40
41
|
- Use `--non-interactive` and `--no-open` for automation-friendly CLI calls.
|
|
41
42
|
- Use `--json` whenever structured output is helpful.
|
|
42
|
-
- `apex credits` reports standard credits plus audit
|
|
43
|
+
- `apex credits` reports standard credits plus audit, fix review, and Lite scan entitlements when Bedrock returns those balances.
|
|
43
44
|
- Work from the target repository directory so Apex can resolve `.apex/workspace.json`.
|
|
44
45
|
- `apex scan` scans the current working directory by default; pass `--repo` only when the user asks to scan explicit alternate roots.
|
|
45
46
|
- `apex-doctor` reports whether Apex will use remote materialization or a local snapshot upload for each selected source.
|
|
46
47
|
- Plain local directories and dirty git worktrees can scan through local snapshot uploads without provider access.
|
|
47
48
|
- Audit scans use `--mode audit` in user-facing CLI calls and request payloads. The legacy `ultra` mode remains accepted as an alias, but audit scans still require provider-backed GitHub or GitLab sources.
|
|
49
|
+
- Lite scans use `--mode lite` in CLI calls or `mode: "lite"` in MCP, require Lite Scan beta access, and require provider-backed GitHub or GitLab sources without local snapshot fallback.
|
|
48
50
|
- PR scans use `--mode pr --pr <number>` in CLI calls, or `mode: "pr"` with `pullRequests` in MCP. They require one provider-backed GitHub repository.
|
|
49
51
|
- `apex-workspace-use` accepts a workspace name, prefix, or ID.
|
|
50
52
|
- Use `sourceMode: "remote"` only when the user explicitly wants to forbid local snapshot fallbacks.
|
|
@@ -59,6 +61,7 @@ If the Apex MCP server is not configured, fall back to the local CLI:
|
|
|
59
61
|
## Examples
|
|
60
62
|
|
|
61
63
|
- Start a scan for the current repository or directory: run `apex-doctor`, bind a workspace if needed, then call `apex-scan`.
|
|
64
|
+
- Start a Lite scan: call `apex-scan` with `mode: "lite"` after confirming beta access and provider-backed sources.
|
|
62
65
|
- Start a PR scan: call `apex-scan` with `mode: "pr"` and `pullRequests`.
|
|
63
66
|
- Check an active scan: call `apex-status`, passing `scanId` when the scan is not the latest binding, then `apex-findings` when the scan completes.
|
|
64
67
|
- Export findings for review: call `apex-export-findings` with `format` set to `markdown`, `json`, or `gitlab-sast`.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Cantina agent plugins for security review workflows.",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.17"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
@@ -14,10 +14,10 @@
|
|
|
14
14
|
"source": {
|
|
15
15
|
"source": "npm",
|
|
16
16
|
"package": "@cantinasecurity/apex-cli",
|
|
17
|
-
"version": "0.1.
|
|
17
|
+
"version": "0.1.17"
|
|
18
18
|
},
|
|
19
19
|
"description": "Run Apex security scans and review findings from Claude Code.",
|
|
20
|
-
"version": "0.1.
|
|
20
|
+
"version": "0.1.17",
|
|
21
21
|
"author": {
|
|
22
22
|
"name": "Cantina",
|
|
23
23
|
"email": "support@cantina.xyz"
|
package/.mcp.claude.json
CHANGED
package/.mcp.codex.json
CHANGED
package/MARKETPLACE.md
CHANGED
|
@@ -13,7 +13,7 @@ This package is prepared as both a Claude Code plugin and a Codex plugin. The pu
|
|
|
13
13
|
The plugin MCP configs launch the pinned npm CLI package with:
|
|
14
14
|
|
|
15
15
|
```bash
|
|
16
|
-
npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
16
|
+
npx -y -p @cantinasecurity/apex-cli@0.1.17 apex-mcp
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
That keeps marketplace installs independent of a user's global `apex` install.
|
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ If Apex asks for a workspace name, that is the Apex workspace name for the curre
|
|
|
103
103
|
Supported shell commands:
|
|
104
104
|
|
|
105
105
|
- `/credits`
|
|
106
|
-
- `/scan [standard|audit]`
|
|
106
|
+
- `/scan [standard|audit|lite]`
|
|
107
107
|
- `/scan pr <pr-number>`
|
|
108
108
|
- `/scans`
|
|
109
109
|
- `/findings [scan-id]`
|
|
@@ -136,6 +136,7 @@ Supported shell commands:
|
|
|
136
136
|
|
|
137
137
|
- `apex credits`
|
|
138
138
|
- `apex scan`
|
|
139
|
+
- `apex scan --mode lite`
|
|
139
140
|
- `apex scan --mode pr --pr <number> [--pr <number>] [--pr-path <path>]`
|
|
140
141
|
- `apex scans`
|
|
141
142
|
- `apex findings [--scan <scan-id>]`
|
|
@@ -151,9 +152,9 @@ Supported shell commands:
|
|
|
151
152
|
- `apex doctor`
|
|
152
153
|
- `apex login`
|
|
153
154
|
- `apex logout`
|
|
154
|
-
- `apex service-key create --company <id-or-handle>
|
|
155
|
-
- `apex service-key list --company <id-or-handle> [--json]`
|
|
156
|
-
- `apex service-key revoke <key-id> --company <id-or-handle> [--json]`
|
|
155
|
+
- `apex service-key create --name <name> [--company <id-or-handle>] [--scope <scope[,scope]>] [--expires-at <ISO-8601>] [--json]`
|
|
156
|
+
- `apex service-key list [--company <id-or-handle>] [--json]`
|
|
157
|
+
- `apex service-key revoke <key-id> [--company <id-or-handle>] [--json]`
|
|
157
158
|
- `apex setup [all|codex|claude|copilot]`
|
|
158
159
|
- `apex telemetry [status|enable|disable]`
|
|
159
160
|
- `apex update`
|
|
@@ -165,7 +166,7 @@ Helpful workspace flags:
|
|
|
165
166
|
- `--company <id-or-handle>` to choose the Apex company when more than one is available
|
|
166
167
|
- `--workspace-name <name>` to set the Apex workspace name for this directory
|
|
167
168
|
|
|
168
|
-
`apex credits` shows standard
|
|
169
|
+
`apex credits` shows standard, audit, fix review, and Lite scan entitlements when the server returns them.
|
|
169
170
|
|
|
170
171
|
## Service Keys And The Hosted API
|
|
171
172
|
|
|
@@ -175,12 +176,13 @@ An organization manager can create a service key with an existing Apex device-lo
|
|
|
175
176
|
|
|
176
177
|
```bash
|
|
177
178
|
apex service-key create \
|
|
178
|
-
--company acme \
|
|
179
179
|
--name internal-audit-hub \
|
|
180
180
|
--scope scans:create,scans:read,findings:read \
|
|
181
181
|
--expires-at 2027-01-01T00:00:00Z
|
|
182
182
|
```
|
|
183
183
|
|
|
184
|
+
The CLI automatically uses your company when only one is available. If you belong to multiple companies, it prompts you to choose one. Pass `--company <id-or-handle>` to skip that prompt, or when a non-interactive or `--json` command cannot prompt.
|
|
185
|
+
|
|
184
186
|
The secret is returned only by `create` and cannot be retrieved later. Store it directly in a secret manager and do not put it in source control, prompts, chat messages, or logs. `--json` also includes the shown-once secret so it can be captured programmatically; handle that output as a credential.
|
|
185
187
|
|
|
186
188
|
Service keys are organization-level credentials. Grant only the scopes the integration needs: `scans:create` triggers scans, `scans:read` polls status, and `findings:read` retrieves findings. Omit `--scope` to grant all three scopes. For a P1-only adapter that reads manually started scans, create a read-only key with `--scope scans:read,findings:read`.
|
|
@@ -190,8 +192,8 @@ A key with `findings:read` can retrieve unpublished draft findings for scans in
|
|
|
190
192
|
List metadata or revoke a key without exposing its secret:
|
|
191
193
|
|
|
192
194
|
```bash
|
|
193
|
-
apex service-key list
|
|
194
|
-
apex service-key revoke <key-id>
|
|
195
|
+
apex service-key list
|
|
196
|
+
apex service-key revoke <key-id>
|
|
195
197
|
```
|
|
196
198
|
|
|
197
199
|
Service-key management is intentionally available only through the scripted CLI. It is not exposed as an MCP tool or interactive-shell command, which keeps raw secrets out of model tool results and transcripts. The CLI requires an existing device-login bearer session, never initiates login from these commands, and never stores a created service key locally.
|
|
@@ -277,7 +279,7 @@ Useful flags:
|
|
|
277
279
|
|
|
278
280
|
- `--repo <path>` to scan one or more explicit local roots instead of the current directory
|
|
279
281
|
- `--source-mode auto|remote|local` to control remote-first fallback behavior
|
|
280
|
-
- `--mode standard|audit|pr` to choose the scan mode
|
|
282
|
+
- `--mode standard|audit|lite|pr` to choose the scan mode
|
|
281
283
|
- `--pr <number>` to select one or more GitHub pull requests for `--mode pr`
|
|
282
284
|
- `--pr <number:path,path>` or `--pr-path <path>` to limit a PR scan to changed paths
|
|
283
285
|
|
|
@@ -285,6 +287,8 @@ Useful flags:
|
|
|
285
287
|
|
|
286
288
|
Audit scans use `audit` as the scan mode and still require provider-backed GitHub or GitLab repositories that Apex can materialize remotely without a local snapshot fallback. `ultra` remains accepted as a backwards-compatible alias.
|
|
287
289
|
|
|
290
|
+
Lite scans use `lite` as the scan mode and require the Lite Scan beta feature plus provider-backed GitHub or GitLab repositories that Apex can materialize remotely. Local snapshot uploads are not supported for Lite scans.
|
|
291
|
+
|
|
288
292
|
PR scans require exactly one provider-backed GitHub repository. If the current directory resolves to multiple sources, pass `--repo <path>` to select the one that contains the pull request.
|
|
289
293
|
|
|
290
294
|
## LLM / MCP Usage
|
|
@@ -381,6 +385,7 @@ The MCP server exposes Apex-specific tools for:
|
|
|
381
385
|
- doctor, credits, and provider connection URLs
|
|
382
386
|
- workspace inspection and workspace binding
|
|
383
387
|
- scan start, status, cancellation, findings, and findings export
|
|
388
|
+
- Lite scan start by calling `apex-scan` with `mode: "lite"`
|
|
384
389
|
- PR scan start by calling `apex-scan` with `mode: "pr"` and `pullRequests`
|
|
385
390
|
- finding comments and valid/invalid feedback with `apex-finding-comment` and `apex-finding-feedback`
|
|
386
391
|
- Fix PR and current-repository fix review scans with `apex-finding-fix-review`, optionally after `apex-finding-feedback`
|
|
@@ -424,7 +429,7 @@ Telemetry event posts do not include bearer tokens. The telemetry endpoint is `P
|
|
|
424
429
|
{
|
|
425
430
|
"schemaVersion": 1,
|
|
426
431
|
"event": "apex.invocation.completed",
|
|
427
|
-
"cliVersion": "0.1.
|
|
432
|
+
"cliVersion": "0.1.17",
|
|
428
433
|
"invocation": {
|
|
429
434
|
"surface": "mcp",
|
|
430
435
|
"command": "apex-scan",
|
|
@@ -455,7 +460,7 @@ The npm package also includes marketplace-ready plugin artifacts:
|
|
|
455
460
|
- `.claude-plugin/plugin.json` and `.mcp.claude.json` for Claude Code plugin installs
|
|
456
461
|
- `.claude-plugin/marketplace.json` for a Claude marketplace entry backed by the public npm package
|
|
457
462
|
|
|
458
|
-
These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.
|
|
463
|
+
These plugin installs launch the pinned npm package with `npx -y -p @cantinasecurity/apex-cli@0.1.17 apex-mcp`, so users do not need to install `apex` globally before enabling the plugin.
|
|
459
464
|
|
|
460
465
|
The repository also includes `.agents/plugins/marketplace.json` for local Codex marketplace testing from a checkout.
|
|
461
466
|
|
package/dist/apex.js
CHANGED
|
@@ -52,7 +52,7 @@ async function dispatch(parsed) {
|
|
|
52
52
|
await commandServiceKeyRevoke(client, parsed.flags, parsed.args[0] ?? "");
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
|
-
throw new Error("Usage: apex service-key <create|list|revoke> [key-id] --company <id-or-handle>");
|
|
55
|
+
throw new Error("Usage: apex service-key <create|list|revoke> [key-id] [--company <id-or-handle>]");
|
|
56
56
|
case "update":
|
|
57
57
|
await commandUpdate(parsed.flags);
|
|
58
58
|
return;
|
package/dist/commands.js
CHANGED
|
@@ -42,9 +42,10 @@ const FIX_REVIEW_TARGETS = [
|
|
|
42
42
|
"pull_request",
|
|
43
43
|
"repository_current",
|
|
44
44
|
];
|
|
45
|
-
const SERVICE_KEY_CREATE_USAGE = "Usage: apex service-key create --company <id-or-handle>
|
|
46
|
-
const SERVICE_KEY_LIST_USAGE = "Usage: apex service-key list --company <id-or-handle> [--json]";
|
|
47
|
-
const SERVICE_KEY_REVOKE_USAGE = "Usage: apex service-key revoke <key-id> --company <id-or-handle> [--json]";
|
|
45
|
+
const SERVICE_KEY_CREATE_USAGE = "Usage: apex service-key create --name <name> [--company <id-or-handle>] [--scope <scope[,scope]>] [--expires-at <ISO-8601>] [--json]";
|
|
46
|
+
const SERVICE_KEY_LIST_USAGE = "Usage: apex service-key list [--company <id-or-handle>] [--json]";
|
|
47
|
+
const SERVICE_KEY_REVOKE_USAGE = "Usage: apex service-key revoke <key-id> [--company <id-or-handle>] [--json]";
|
|
48
|
+
const SERVICE_KEY_COMPANY_PROMPT = "Select the Apex company whose service keys you want to manage:";
|
|
48
49
|
const SERVICE_KEY_EXPIRATION_SCHEMA = z.string().datetime({ offset: true });
|
|
49
50
|
const SERVICE_KEY_NAME_CONTROL_CHARACTER_PATTERN = /[\u0000-\u001f\u007f-\u009f]/;
|
|
50
51
|
const SERVICE_KEY_NAME_SCHEMA = z
|
|
@@ -65,11 +66,6 @@ async function saveCompanyDefault(companyId) {
|
|
|
65
66
|
function formatCommandArgument(value) {
|
|
66
67
|
return /\s/.test(value) ? `"${value}"` : value;
|
|
67
68
|
}
|
|
68
|
-
function requireExplicitServiceKeyCompany(flags, usage) {
|
|
69
|
-
if (!getFlagString(flags, "company")) {
|
|
70
|
-
throw new Error(`${usage}\nService-key commands require an explicit --company.`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
69
|
async function requireExistingServiceKeySession(client) {
|
|
74
70
|
const me = await getMe(client);
|
|
75
71
|
if (!me) {
|
|
@@ -188,6 +184,28 @@ function formatFixReviewScanBalance(scanBalance) {
|
|
|
188
184
|
}
|
|
189
185
|
return `Fix review scans: ${detailParts.join(", ")}`;
|
|
190
186
|
}
|
|
187
|
+
function formatLiteScanBalance(scanBalance) {
|
|
188
|
+
const purchased = getOptionalCount(scanBalance.litePurchased);
|
|
189
|
+
const used = getOptionalCount(scanBalance.liteUsed);
|
|
190
|
+
const remaining = getOptionalCount(scanBalance.liteRemaining);
|
|
191
|
+
const available = remaining ??
|
|
192
|
+
(purchased !== null && used !== null ? Math.max(0, purchased - used) : null);
|
|
193
|
+
if (purchased === null && used === null && available === null) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
const detailParts = [];
|
|
197
|
+
if (purchased !== null) {
|
|
198
|
+
detailParts.push(`${purchased} purchased`);
|
|
199
|
+
}
|
|
200
|
+
if (used !== null) {
|
|
201
|
+
detailParts.push(`${used} used`);
|
|
202
|
+
}
|
|
203
|
+
if (available !== null) {
|
|
204
|
+
const detailSuffix = detailParts.length ? ` (${detailParts.join(", ")})` : "";
|
|
205
|
+
return `Lite scan credits: ${available} available${detailSuffix}`;
|
|
206
|
+
}
|
|
207
|
+
return `Lite scan credits: ${detailParts.join(", ")}`;
|
|
208
|
+
}
|
|
191
209
|
function normalizeRequestedScanMode(value) {
|
|
192
210
|
if (value === "ultra" || value === "audit") {
|
|
193
211
|
return "audit";
|
|
@@ -195,6 +213,9 @@ function normalizeRequestedScanMode(value) {
|
|
|
195
213
|
if (value === "pr") {
|
|
196
214
|
return "pr";
|
|
197
215
|
}
|
|
216
|
+
if (value === "lite") {
|
|
217
|
+
return "lite";
|
|
218
|
+
}
|
|
198
219
|
return "standard";
|
|
199
220
|
}
|
|
200
221
|
function getFlagValues(flags, key) {
|
|
@@ -578,6 +599,10 @@ export async function commandCredits(client, cwd, flags) {
|
|
|
578
599
|
if (redeemableFixReviewScans && redeemableFixReviewScans > 0) {
|
|
579
600
|
logLine(`Redeemable fix review scans from standard credits: ${redeemableFixReviewScans}`, flags);
|
|
580
601
|
}
|
|
602
|
+
const liteBalance = formatLiteScanBalance(payload.scanBalance);
|
|
603
|
+
if (liteBalance) {
|
|
604
|
+
logLine(liteBalance, flags);
|
|
605
|
+
}
|
|
581
606
|
logLine(`Scans enabled: ${payload.scansEnabled ? "yes" : "no"}`, flags);
|
|
582
607
|
return payload;
|
|
583
608
|
}
|
|
@@ -588,12 +613,14 @@ export async function commandLogout(client, flags) {
|
|
|
588
613
|
}
|
|
589
614
|
}
|
|
590
615
|
export async function commandServiceKeyCreate(client, flags) {
|
|
591
|
-
requireExplicitServiceKeyCompany(flags, SERVICE_KEY_CREATE_USAGE);
|
|
592
616
|
const name = normalizeServiceKeyName(getFlagString(flags, "name"));
|
|
593
617
|
const scopes = normalizeServiceKeyScopes(flags);
|
|
594
618
|
const expiresAt = normalizeServiceKeyExpiration(getFlagString(flags, "expires-at"));
|
|
595
619
|
const me = await requireExistingServiceKeySession(client);
|
|
596
|
-
const company = await chooseCompany(me, flags, null
|
|
620
|
+
const company = await chooseCompany(me, flags, null, {
|
|
621
|
+
prompt: SERVICE_KEY_COMPANY_PROMPT,
|
|
622
|
+
useSavedDefault: false,
|
|
623
|
+
});
|
|
597
624
|
const response = await withLoadingIndicator("Creating service key...", flags, () => createServiceApiKey(client, company.id, {
|
|
598
625
|
name,
|
|
599
626
|
expiresAt,
|
|
@@ -623,9 +650,11 @@ export async function commandServiceKeyCreate(client, flags) {
|
|
|
623
650
|
return payload;
|
|
624
651
|
}
|
|
625
652
|
export async function commandServiceKeyList(client, flags) {
|
|
626
|
-
requireExplicitServiceKeyCompany(flags, SERVICE_KEY_LIST_USAGE);
|
|
627
653
|
const me = await requireExistingServiceKeySession(client);
|
|
628
|
-
const company = await chooseCompany(me, flags, null
|
|
654
|
+
const company = await chooseCompany(me, flags, null, {
|
|
655
|
+
prompt: SERVICE_KEY_COMPANY_PROMPT,
|
|
656
|
+
useSavedDefault: false,
|
|
657
|
+
});
|
|
629
658
|
const response = await withLoadingIndicator("Loading service keys...", flags, () => listServiceApiKeys(client, company.id));
|
|
630
659
|
const payload = {
|
|
631
660
|
company,
|
|
@@ -653,13 +682,15 @@ export async function commandServiceKeyList(client, flags) {
|
|
|
653
682
|
return payload;
|
|
654
683
|
}
|
|
655
684
|
export async function commandServiceKeyRevoke(client, flags, requestedKeyId) {
|
|
656
|
-
requireExplicitServiceKeyCompany(flags, SERVICE_KEY_REVOKE_USAGE);
|
|
657
685
|
const keyId = requestedKeyId.trim();
|
|
658
686
|
if (!keyId) {
|
|
659
687
|
throw new Error(SERVICE_KEY_REVOKE_USAGE);
|
|
660
688
|
}
|
|
661
689
|
const me = await requireExistingServiceKeySession(client);
|
|
662
|
-
const company = await chooseCompany(me, flags, null
|
|
690
|
+
const company = await chooseCompany(me, flags, null, {
|
|
691
|
+
prompt: SERVICE_KEY_COMPANY_PROMPT,
|
|
692
|
+
useSavedDefault: false,
|
|
693
|
+
});
|
|
663
694
|
await withLoadingIndicator("Revoking service key...", flags, () => revokeServiceApiKey(client, company.id, keyId));
|
|
664
695
|
const payload = {
|
|
665
696
|
company,
|
|
@@ -853,16 +884,17 @@ export async function commandScan(client, cwd, flags) {
|
|
|
853
884
|
},
|
|
854
885
|
}));
|
|
855
886
|
}
|
|
856
|
-
else if (requestedMode === "audit") {
|
|
887
|
+
else if (requestedMode === "audit" || requestedMode === "lite") {
|
|
888
|
+
const modeLabel = requestedMode === "audit" ? "Audit" : "Lite";
|
|
857
889
|
if (!supportsLegacyRemoteFlow(result.resolve.plannedSources)) {
|
|
858
|
-
throw new Error(
|
|
890
|
+
throw new Error(`${modeLabel} scans currently require provider-backed GitHub or GitLab repositories without local snapshot fallbacks.`);
|
|
859
891
|
}
|
|
860
|
-
const legacyResolve = await withLoadingIndicator(
|
|
892
|
+
const legacyResolve = await withLoadingIndicator(`Preparing ${modeLabel.toLowerCase()} scan...`, flags, () => resolveWorkspaceSelectionLegacy(client, selection, resolvedWorkspaceId));
|
|
861
893
|
if (!legacyResolve.workspaceId) {
|
|
862
894
|
throw new Error("Workspace resolution did not return a workspaceId.");
|
|
863
895
|
}
|
|
864
896
|
workspaceId = legacyResolve.workspaceId;
|
|
865
|
-
scan = await withLoadingIndicator(
|
|
897
|
+
scan = await withLoadingIndicator(`Starting ${modeLabel.toLowerCase()} scan...`, flags, () => client.request(`/api/cli/v1/local-workspaces/${encodeURIComponent(workspaceId)}/scan`, {
|
|
866
898
|
method: "POST",
|
|
867
899
|
json: {
|
|
868
900
|
mode: requestedMode,
|
package/dist/help.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const CLI_HELP_TEXT = `Usage:
|
|
2
2
|
apex Open the interactive Apex shell
|
|
3
|
-
apex credits Show
|
|
3
|
+
apex credits Show standard, audit, fix review, and Lite scan entitlements for the active company
|
|
4
4
|
apex scan Create or resolve a workspace for this directory and start a scan
|
|
5
5
|
apex scans List scans for the current workspace binding
|
|
6
6
|
apex findings List findings for the latest or selected scan
|
|
@@ -33,7 +33,7 @@ export const CLI_HELP_TEXT = `Usage:
|
|
|
33
33
|
apex connect gitlab Open the GitLab connection flow
|
|
34
34
|
|
|
35
35
|
Flags:
|
|
36
|
-
--company <id-or-handle>
|
|
36
|
+
--company <id-or-handle> Override the automatically selected Apex company
|
|
37
37
|
--name <name> Name a new service key
|
|
38
38
|
--scope <scope[,scope]> Limit a service key to scans:create, scans:read, and/or findings:read
|
|
39
39
|
--expires-at <ISO-8601> Set an optional service-key expiration time
|
|
@@ -55,7 +55,7 @@ Flags:
|
|
|
55
55
|
Choose the export format for findings
|
|
56
56
|
--output <path> Write exported findings to this file path
|
|
57
57
|
--limit <count> Limit the number of findings returned
|
|
58
|
-
--mode standard|audit|pr
|
|
58
|
+
--mode standard|audit|lite|pr Choose the scan mode
|
|
59
59
|
--pr <number[:path,path]> Select a GitHub pull request for --mode pr
|
|
60
60
|
--pr-path <path> Limit a single-PR scan to a changed file path
|
|
61
61
|
--source-mode auto|remote|local Control remote-vs-local source materialization
|
|
@@ -71,9 +71,11 @@ Tips:
|
|
|
71
71
|
apex scan uses the current directory as the default source root unless you pass --repo.
|
|
72
72
|
PR scans require --mode pr and at least one --pr selection for a GitHub repository.
|
|
73
73
|
audit is the current scan mode for audit scans; ultra remains accepted as a legacy alias.
|
|
74
|
+
Lite scans use --mode lite and require beta access plus provider-backed GitHub or GitLab sources.
|
|
74
75
|
apex workspace use accepts a workspace name, prefix, or ID.
|
|
75
76
|
Finding comments, feedback, and fix review scans use the same Apex login credentials as other CLI commands.
|
|
76
|
-
Service-key commands require an existing login
|
|
77
|
+
Service-key commands require an existing login; run apex login first when needed.
|
|
78
|
+
Apex automatically selects your company when only one is available. With multiple companies, choose one interactively or pass --company.
|
|
77
79
|
Service-key creation also requires --name and reveals the secret only once; omit --scope to grant all hosted API scopes.
|
|
78
80
|
Service-key management is available only in the scripted CLI, not the interactive shell or MCP server.
|
|
79
81
|
Invalid finding feedback requires --dismissal-reason.
|
|
@@ -86,8 +88,9 @@ Tips:
|
|
|
86
88
|
export const SHELL_HELP_TEXT = `Press Tab to autocomplete commands and common arguments.
|
|
87
89
|
|
|
88
90
|
Commands:
|
|
89
|
-
/credits Show
|
|
90
|
-
/scan [standard|audit]
|
|
91
|
+
/credits Show standard, audit, fix review, and Lite scan entitlements for the active company
|
|
92
|
+
/scan [standard|audit|lite]
|
|
93
|
+
Start a new Apex scan for this workspace
|
|
91
94
|
/scan pr <pr-number> Start a PR scan for this workspace
|
|
92
95
|
/scans List scans for this workspace
|
|
93
96
|
/findings [scan-id] List findings for the latest or selected scan
|
|
@@ -120,11 +123,22 @@ Commands:
|
|
|
120
123
|
/help Show this help
|
|
121
124
|
/exit Exit Apex
|
|
122
125
|
|
|
126
|
+
Scripted-only commands (run outside the interactive shell):
|
|
127
|
+
apex service-key create --name <name> [--company <id-or-handle>] [--scope <scope[,scope]>]
|
|
128
|
+
Create an organization service key (shown once)
|
|
129
|
+
apex service-key list [--company <id-or-handle>]
|
|
130
|
+
List service-key metadata
|
|
131
|
+
apex service-key revoke <key-id> [--company <id-or-handle>]
|
|
132
|
+
Revoke a service key
|
|
133
|
+
|
|
123
134
|
Tips:
|
|
124
135
|
PR scans require a GitHub PR number and provider-backed repository access.
|
|
125
136
|
audit is the current scan mode for audit scans; ultra remains accepted as a legacy alias.
|
|
137
|
+
Lite scans require beta access plus provider-backed GitHub or GitLab sources.
|
|
126
138
|
/workspace use accepts a workspace name, prefix, or ID.
|
|
127
139
|
/findings comment, /findings feedback, and /findings fix-review use the current Apex login.
|
|
140
|
+
Service-key commands automatically use your company when only one is available. With multiple companies, choose one interactively or pass --company.
|
|
141
|
+
Service-key secrets are shown once. Run these commands in your terminal, not in the interactive shell or through MCP.
|
|
128
142
|
Invalid finding feedback requires a dismissal reason.
|
|
129
143
|
Fix review scans use saved Fix PR URLs when present; otherwise they review the current repository when Bedrock has original scan repository context. Use scripted CLI or MCP to supply Fix PR URLs directly.
|
|
130
144
|
Use scripted CLI flags for advanced feedback options such as suggested severity or dismissal reason.
|
package/dist/mcp.js
CHANGED
|
@@ -26,7 +26,7 @@ Notes:
|
|
|
26
26
|
- Finding write tools use the same Apex device-login credentials as read tools.
|
|
27
27
|
- Organization service-key creation, listing, and revocation are intentionally scripted-CLI-only and are not exposed as MCP tools. Never ask a user to paste a service key into an MCP prompt or tool call.
|
|
28
28
|
- HTTP-only serverless and cron integrations should use the hosted API documented at https://ai.cantina.xyz/docs/apex-api instead of launching this stdio server.
|
|
29
|
-
- Use mode "audit" for audit scans; "ultra" remains accepted as a legacy alias. Use mode "pr" for GitHub pull request scans.
|
|
29
|
+
- Use mode "audit" for audit scans; "ultra" remains accepted as a legacy alias. Use mode "lite" only for beta-enabled provider-backed Lite scans, and mode "pr" for GitHub pull request scans.
|
|
30
30
|
- Doctor reports whether Apex will use remote materialization or a local snapshot upload for each source.`;
|
|
31
31
|
const WORKSPACE_BINDING_ERROR = "This directory is not bound to an Apex workspace yet. Run `apex workspaces` to list workspace names, prefixes, and IDs, then bind one with `apex workspace use \"<workspace name>\"`, or run `apex scan` to create or resolve one automatically.";
|
|
32
32
|
const MISSING_PROVIDER_CONNECTIONS_ERROR = "Missing provider connections. Re-run interactively or pre-connect providers.";
|
|
@@ -292,7 +292,7 @@ function registerTools(server) {
|
|
|
292
292
|
}));
|
|
293
293
|
server.registerTool("apex-credits", {
|
|
294
294
|
title: "Get Apex Credits",
|
|
295
|
-
description: "Show scan credits plus audit
|
|
295
|
+
description: "Show scan credits plus audit, fix review, and Lite scan entitlements for the active or selected company.",
|
|
296
296
|
inputSchema: {
|
|
297
297
|
cwd: z.string().optional(),
|
|
298
298
|
company: z.string().optional(),
|
|
@@ -376,13 +376,13 @@ function registerTools(server) {
|
|
|
376
376
|
}));
|
|
377
377
|
server.registerTool("apex-scan", {
|
|
378
378
|
title: "Start Apex Scan",
|
|
379
|
-
description: "Start a new Apex scan for the provided cwd by default. Pass repoPaths only to scan explicit alternate local roots. Use mode audit for audit scans and mode pr for GitHub pull request scans.",
|
|
379
|
+
description: "Start a new Apex scan for the provided cwd by default. Pass repoPaths only to scan explicit alternate local roots. Use mode audit for audit scans, mode lite for beta-gated provider-backed Lite scans, and mode pr for GitHub pull request scans.",
|
|
380
380
|
inputSchema: {
|
|
381
381
|
cwd: z.string().optional(),
|
|
382
382
|
company: z.string().optional(),
|
|
383
383
|
workspaceName: z.string().optional(),
|
|
384
384
|
repoPaths: z.array(z.string()).optional(),
|
|
385
|
-
mode: z.enum(["standard", "audit", "ultra", "pr"]).optional(),
|
|
385
|
+
mode: z.enum(["standard", "audit", "ultra", "lite", "pr"]).optional(),
|
|
386
386
|
pullRequests: z
|
|
387
387
|
.array(z.object({
|
|
388
388
|
number: z.number().int().positive(),
|
package/dist/session.js
CHANGED
|
@@ -23,10 +23,13 @@ export function createWorkspaceBinding(result, currentBinding) {
|
|
|
23
23
|
lastScanUrl: preservedBinding?.lastScanUrl ?? null,
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
-
export async function chooseCompany(me, flags, binding) {
|
|
26
|
+
export async function chooseCompany(me, flags, binding, options = {}) {
|
|
27
27
|
const requested = getFlagString(flags, "company");
|
|
28
28
|
const config = await loadConfig();
|
|
29
|
-
const defaultRef = requested ??
|
|
29
|
+
const defaultRef = requested ??
|
|
30
|
+
(options.useSavedDefault === false
|
|
31
|
+
? null
|
|
32
|
+
: (binding?.companyId ?? config.defaultCompanyId ?? null));
|
|
30
33
|
if (defaultRef) {
|
|
31
34
|
const matched = me.companies.find((company) => company.id === defaultRef ||
|
|
32
35
|
(company.handle ?? "").toLowerCase() === defaultRef.toLowerCase());
|
|
@@ -46,7 +49,7 @@ export async function chooseCompany(me, flags, binding) {
|
|
|
46
49
|
if (!canPromptInteractively(flags)) {
|
|
47
50
|
throw new Error("Multiple companies available. Re-run with --company <id-or-handle>.");
|
|
48
51
|
}
|
|
49
|
-
const selected = await chooseOne("Select the Apex company to use for this directory:", me.companies.map((company) => ({
|
|
52
|
+
const selected = await chooseOne(options.prompt ?? "Select the Apex company to use for this directory:", me.companies.map((company) => ({
|
|
50
53
|
label: company.handle
|
|
51
54
|
? `${company.name ?? company.id} (${company.handle})`
|
|
52
55
|
: (company.name ?? company.id),
|
package/dist/shell.js
CHANGED
|
@@ -7,7 +7,7 @@ import { formatApiError } from "./api-client.js";
|
|
|
7
7
|
import { createShellTelemetryInvocation, emitInvocationCompleted, emitInvocationStarted, withTelemetryContext, } from "./telemetry.js";
|
|
8
8
|
const SHELL_COMPLETIONS = [
|
|
9
9
|
{ command: "credits" },
|
|
10
|
-
{ command: "scan", args: ["standard", "audit", "ultra", "pr"] },
|
|
10
|
+
{ command: "scan", args: ["standard", "audit", "ultra", "lite", "pr"] },
|
|
11
11
|
{ command: "scans" },
|
|
12
12
|
{ command: "findings", args: ["comment", "feedback", "fix-review"] },
|
|
13
13
|
{ command: "export" },
|
|
@@ -195,8 +195,8 @@ async function runShellCommand(client, cwd, parsed, shellFlags, session) {
|
|
|
195
195
|
return {};
|
|
196
196
|
case "scan": {
|
|
197
197
|
const mode = parsed.args[0];
|
|
198
|
-
if (mode && !["standard", "audit", "ultra", "pr"].includes(mode)) {
|
|
199
|
-
process.stderr.write("Usage: /scan [standard|audit|ultra|pr] [pr-number]\n");
|
|
198
|
+
if (mode && !["standard", "audit", "ultra", "lite", "pr"].includes(mode)) {
|
|
199
|
+
process.stderr.write("Usage: /scan [standard|audit|ultra|lite|pr] [pr-number]\n");
|
|
200
200
|
return {};
|
|
201
201
|
}
|
|
202
202
|
const prNumber = mode === "pr" ? parsed.args[1] : null;
|
package/dist/telemetry.js
CHANGED
|
@@ -184,7 +184,13 @@ function sanitizeCliFlags(flags) {
|
|
|
184
184
|
contentProvided: stringFlag(flags, "content") !== null,
|
|
185
185
|
commentProvided: stringFlag(flags, "comment") !== null,
|
|
186
186
|
parentCommentProvided: stringFlag(flags, "parent-comment") !== null,
|
|
187
|
-
mode: safeEnum(stringFlag(flags, "mode"), [
|
|
187
|
+
mode: safeEnum(stringFlag(flags, "mode"), [
|
|
188
|
+
"standard",
|
|
189
|
+
"audit",
|
|
190
|
+
"ultra",
|
|
191
|
+
"lite",
|
|
192
|
+
"pr",
|
|
193
|
+
]),
|
|
188
194
|
sourceMode: safeEnum(stringFlag(flags, "source-mode"), ["auto", "remote", "local"]),
|
|
189
195
|
format: safeEnum(stringFlag(flags, "format"), ["markdown", "json", "gitlab-sast"]),
|
|
190
196
|
feedbackStatus: safeEnum(stringFlag(flags, "status"), ["valid", "invalid"]),
|
|
@@ -351,6 +357,7 @@ export function sanitizeMcpInput(input = {}) {
|
|
|
351
357
|
"standard",
|
|
352
358
|
"audit",
|
|
353
359
|
"ultra",
|
|
360
|
+
"lite",
|
|
354
361
|
"pr",
|
|
355
362
|
]),
|
|
356
363
|
sourceMode: safeEnum(typeof input.sourceMode === "string" ? input.sourceMode : null, [
|
|
@@ -538,7 +545,13 @@ export function createCliTelemetryInvocation(parsed) {
|
|
|
538
545
|
}
|
|
539
546
|
export function createShellTelemetryInvocation(parsed, flags) {
|
|
540
547
|
const shellMode = parsed.command === "scan"
|
|
541
|
-
? safeEnum(parsed.args[0] ?? "default", [
|
|
548
|
+
? safeEnum(parsed.args[0] ?? "default", [
|
|
549
|
+
"standard",
|
|
550
|
+
"audit",
|
|
551
|
+
"ultra",
|
|
552
|
+
"lite",
|
|
553
|
+
"pr",
|
|
554
|
+
]) ??
|
|
542
555
|
(parsed.args[0] ? "other" : "default")
|
|
543
556
|
: null;
|
|
544
557
|
return createInvocation({
|
package/package.json
CHANGED
package/skills/apex-cli/SKILL.md
CHANGED
|
@@ -23,23 +23,25 @@ Workflow:
|
|
|
23
23
|
4. Call `apex-doctor` before starting a scan if workspace binding or source planning might be unclear.
|
|
24
24
|
5. If the directory is not bound to the right workspace, call `apex-workspaces` and `apex-workspace-use`.
|
|
25
25
|
6. Use `apex-scan`, `apex-status`, `apex-scans`, `apex-findings`, and `apex-export-findings` for the scan lifecycle.
|
|
26
|
-
7. For
|
|
27
|
-
8.
|
|
28
|
-
9.
|
|
26
|
+
7. For Lite scans, call `apex-scan` with `mode: "lite"` only when the company has Lite Scan beta access and the sources are provider-backed.
|
|
27
|
+
8. For PR scans, call `apex-scan` with `mode: "pr"` and `pullRequests`.
|
|
28
|
+
9. Use `apex-finding-comment` and `apex-finding-feedback` when the user wants to leave review notes or valid/invalid feedback on a finding.
|
|
29
|
+
10. For fix reviews, call `apex-finding-fix-review`. Pass `fixPrUrls` for a PR-based review, or omit them to review the current repository state when Bedrock has original scan repository context.
|
|
29
30
|
|
|
30
31
|
Guidelines:
|
|
31
32
|
|
|
32
33
|
- Do not rely on interactive CLI prompts. The MCP tools are intentionally non-interactive.
|
|
33
34
|
- Service-key management has no MCP tools. Never ask a user to paste a service key into a prompt, and never place one in an MCP call, tool result, source file, or log.
|
|
34
|
-
- If a user explicitly needs a new service key, tell them to run `apex login` if needed, then run `apex service-key create --
|
|
35
|
+
- If a user explicitly needs a new service key, tell them to run `apex login` if needed, then run `apex service-key create --name <name> --scope <scope[,scope]>` directly in their own terminal. Use only the required scopes: `scans:create` to trigger, `scans:read` to poll status, and `findings:read` to retrieve findings. A P1-only adapter can use `--scope scans:read,findings:read`; omitting `--scope` grants all three. The CLI automatically selects the company when only one is available and prompts when there are several; use `--company <id-or-handle>` to skip that prompt or resolve a non-interactive command. Service-key commands never initiate login, and the secret is shown once.
|
|
35
36
|
- For a headless HTTP adapter, read the service key from a secret-backed environment variable, send it as a bearer token to `/api/apex/v1`, and redact it from errors and logs. Do not shell out to `apex` or launch MCP from the adapter.
|
|
36
37
|
- Hosted API v1 requires the GitHub repository to be linked to an active Apex workspace. It never creates or mutates workspaces; use optional `workspaceId` to disambiguate matches and treat zero or ambiguous matches as HTTP `409` setup errors.
|
|
37
38
|
- Hosted scan triggers require `Idempotency-Key`. Reuse one stable key only for an identical transport/timeout/5xx retry. After correcting a definitive 4xx response, or for any new intended scan, use a new key.
|
|
38
|
-
- `apex-credits` reports standard credits plus audit
|
|
39
|
+
- `apex-credits` reports standard credits plus audit, fix review, and Lite scan entitlements when Bedrock returns those balances.
|
|
39
40
|
- `apex-scan` scans the provided `cwd` by default; pass `repoPaths` only when the user asks to scan explicit alternate roots.
|
|
40
41
|
- `apex-doctor` reports whether Apex will use remote materialization or a local snapshot upload for each selected source.
|
|
41
42
|
- Apex can scan plain local directories and dirty git worktrees without provider connections by using local snapshot uploads.
|
|
42
43
|
- Audit scans use `mode: "audit"` in user-facing instructions and request payloads. The legacy `ultra` mode remains accepted as an alias, but audit scans still require provider-backed GitHub or GitLab sources.
|
|
44
|
+
- Lite scans use `mode: "lite"`, require Lite Scan beta access, and require provider-backed GitHub or GitLab sources without local snapshot fallback.
|
|
43
45
|
- PR scans use `mode: "pr"` and require one provider-backed GitHub repository plus one or more `pullRequests`.
|
|
44
46
|
- `apex-workspace-use` accepts a workspace name, prefix, or ID.
|
|
45
47
|
- Use `sourceMode: "remote"` only when the user explicitly wants to forbid local snapshot fallbacks.
|