@cantinasecurity/apex-cli 0.1.0 → 0.1.1
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 +2 -0
- package/README.md +6 -3
- package/dist/commands.js +44 -5
- package/dist/help.js +6 -4
- package/dist/mcp.js +4 -3
- package/dist/scan.js +5 -1
- package/dist/shell.js +3 -3
- package/package.json +1 -1
- package/skills/apex-cli/SKILL.md +2 -0
|
@@ -25,9 +25,11 @@ If the Apex MCP server is not configured, fall back to the local CLI:
|
|
|
25
25
|
- Prefer scripted commands over the interactive shell.
|
|
26
26
|
- Use `--non-interactive` and `--no-open` for automation-friendly CLI calls.
|
|
27
27
|
- Use `--json` whenever structured output is helpful.
|
|
28
|
+
- `apex credits` reports standard credits and audit scan entitlements when Bedrock returns both balances.
|
|
28
29
|
- Work from the target repository directory so Apex can resolve `.apex/workspace.json`.
|
|
29
30
|
- `apex-doctor` reports whether Apex will use remote materialization or a local snapshot upload for each selected source.
|
|
30
31
|
- Plain local directories and dirty git worktrees can scan through local snapshot uploads without provider access.
|
|
32
|
+
- Audit scans use `--mode audit` in user-facing CLI calls. The legacy `ultra` mode remains accepted as an alias, but audit scans still require provider-backed GitHub or GitLab sources.
|
|
31
33
|
- `apex-workspace-use` accepts a workspace name, prefix, or ID.
|
|
32
34
|
- Use `sourceMode: "remote"` only when the user explicitly wants to forbid local snapshot fallbacks.
|
|
33
35
|
|
package/README.md
CHANGED
|
@@ -98,7 +98,7 @@ If Apex asks for a workspace name, that is the Apex workspace name for the curre
|
|
|
98
98
|
Supported shell commands:
|
|
99
99
|
|
|
100
100
|
- `/credits`
|
|
101
|
-
- `/scan [standard|
|
|
101
|
+
- `/scan [standard|audit]`
|
|
102
102
|
- `/scans`
|
|
103
103
|
- `/findings [scan-id]`
|
|
104
104
|
- `/export [scan-id]`
|
|
@@ -147,6 +147,8 @@ Helpful workspace flags:
|
|
|
147
147
|
- `--company <id-or-handle>` to choose the Apex company when more than one is available
|
|
148
148
|
- `--workspace-name <name>` to set the Apex workspace name for this directory
|
|
149
149
|
|
|
150
|
+
`apex credits` shows standard scan credits plus audit scan entitlements when the server returns them.
|
|
151
|
+
|
|
150
152
|
## Local Source Scans
|
|
151
153
|
|
|
152
154
|
`apex scan` now works against any local source root you point it at:
|
|
@@ -159,10 +161,11 @@ Useful flags:
|
|
|
159
161
|
|
|
160
162
|
- `--repo <path>` to scan one or more explicit local roots
|
|
161
163
|
- `--source-mode auto|remote|local` to control remote-first fallback behavior
|
|
164
|
+
- `--mode standard|audit` to choose the scan mode
|
|
162
165
|
|
|
163
166
|
`auto` is the default. `remote` requires Apex to materialize from a remote repository. `local` forces a local snapshot upload even when a clean remote path is available.
|
|
164
167
|
|
|
165
|
-
|
|
168
|
+
Audit scans 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 for the audit scan mode.
|
|
166
169
|
|
|
167
170
|
## LLM / MCP Usage
|
|
168
171
|
|
|
@@ -243,7 +246,7 @@ For Claude Code, the packaged project skill can be installed into the current re
|
|
|
243
246
|
|
|
244
247
|
## Development Notes
|
|
245
248
|
|
|
246
|
-
The CLI uses the Apex `/api/cli/v2/**` local-source routes for scan planning and snapshot uploads, with legacy `/api/cli/v1/**` routes still used for provider-backed flows such as
|
|
249
|
+
The CLI uses the Apex `/api/cli/v2/**` local-source routes for scan planning and snapshot uploads, with legacy `/api/cli/v1/**` routes still used for provider-backed flows such as audit scans. Local state is stored under:
|
|
247
250
|
|
|
248
251
|
- `~/.config/apex/config.json`
|
|
249
252
|
- `~/.config/apex/credentials.json`
|
package/dist/commands.js
CHANGED
|
@@ -36,6 +36,37 @@ function getSuggestedWorkspaceRef(workspace) {
|
|
|
36
36
|
function formatWorkspaceUseCommand(workspace) {
|
|
37
37
|
return `apex workspace use ${formatCommandArgument(getSuggestedWorkspaceRef(workspace))}`;
|
|
38
38
|
}
|
|
39
|
+
function getOptionalCount(value) {
|
|
40
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
41
|
+
? Math.max(0, Math.floor(value))
|
|
42
|
+
: null;
|
|
43
|
+
}
|
|
44
|
+
function formatAuditScanBalance(scanBalance) {
|
|
45
|
+
const purchased = getOptionalCount(scanBalance.auditPurchased);
|
|
46
|
+
const used = getOptionalCount(scanBalance.auditUsed);
|
|
47
|
+
const remaining = getOptionalCount(scanBalance.auditRemaining);
|
|
48
|
+
const available = getOptionalCount(scanBalance.auditAvailable) ??
|
|
49
|
+
remaining ??
|
|
50
|
+
(purchased !== null && used !== null ? Math.max(0, purchased - used) : null);
|
|
51
|
+
if (purchased === null && used === null && available === null) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const detailParts = [];
|
|
55
|
+
if (purchased !== null) {
|
|
56
|
+
detailParts.push(`${purchased} purchased`);
|
|
57
|
+
}
|
|
58
|
+
if (used !== null) {
|
|
59
|
+
detailParts.push(`${used} used`);
|
|
60
|
+
}
|
|
61
|
+
if (available !== null) {
|
|
62
|
+
const detailSuffix = detailParts.length ? ` (${detailParts.join(", ")})` : "";
|
|
63
|
+
return `Audit scans: ${available} available${detailSuffix}`;
|
|
64
|
+
}
|
|
65
|
+
return `Audit scans: ${detailParts.join(", ")}`;
|
|
66
|
+
}
|
|
67
|
+
function normalizeRequestedScanMode(value) {
|
|
68
|
+
return value === "ultra" || value === "audit" ? "ultra" : "standard";
|
|
69
|
+
}
|
|
39
70
|
async function requireWorkspaceBinding(cwd) {
|
|
40
71
|
const binding = await loadWorkspaceBinding(cwd);
|
|
41
72
|
if (binding) {
|
|
@@ -144,7 +175,15 @@ export async function commandCredits(client, cwd, flags) {
|
|
|
144
175
|
return payload;
|
|
145
176
|
}
|
|
146
177
|
logLine(`Company: ${payload.company.handle ?? payload.company.id}`, flags);
|
|
147
|
-
logLine(`
|
|
178
|
+
logLine(`Standard credits: ${payload.scanBalance.remaining} remaining (${payload.scanBalance.purchased} purchased, ${payload.scanBalance.used} used)`, flags);
|
|
179
|
+
const auditBalance = formatAuditScanBalance(payload.scanBalance);
|
|
180
|
+
if (auditBalance) {
|
|
181
|
+
logLine(auditBalance, flags);
|
|
182
|
+
}
|
|
183
|
+
const redeemableAuditScans = getOptionalCount(payload.scanBalance.auditRedeemableFromCredits);
|
|
184
|
+
if (redeemableAuditScans && redeemableAuditScans > 0) {
|
|
185
|
+
logLine(`Redeemable audit scans from standard credits: ${redeemableAuditScans}`, flags);
|
|
186
|
+
}
|
|
148
187
|
logLine(`Scans enabled: ${payload.scansEnabled ? "yes" : "no"}`, flags);
|
|
149
188
|
return payload;
|
|
150
189
|
}
|
|
@@ -244,7 +283,7 @@ export async function commandScan(client, cwd, flags) {
|
|
|
244
283
|
const me = await ensureAuthenticated(client, flags);
|
|
245
284
|
const selection = await selectWorkspaceTarget(cwd, me, flags);
|
|
246
285
|
const result = await withLoadingIndicator("Resolving workspace and scan plan...", flags, () => resolveWorkspaceSelection(client, selection));
|
|
247
|
-
const requestedMode = getFlagString(flags, "mode")
|
|
286
|
+
const requestedMode = normalizeRequestedScanMode(getFlagString(flags, "mode"));
|
|
248
287
|
if (!result.resolve.workspaceId) {
|
|
249
288
|
throw new Error("Workspace resolution did not return a workspaceId.");
|
|
250
289
|
}
|
|
@@ -255,14 +294,14 @@ export async function commandScan(client, cwd, flags) {
|
|
|
255
294
|
let scan;
|
|
256
295
|
if (requestedMode === "ultra") {
|
|
257
296
|
if (!supportsLegacyRemoteFlow(result.resolve.plannedSources)) {
|
|
258
|
-
throw new Error("
|
|
297
|
+
throw new Error("Audit scans currently require provider-backed GitHub or GitLab repositories without local snapshot fallbacks.");
|
|
259
298
|
}
|
|
260
|
-
const legacyResolve = await withLoadingIndicator("Preparing
|
|
299
|
+
const legacyResolve = await withLoadingIndicator("Preparing audit scan...", flags, () => resolveWorkspaceSelectionLegacy(client, selection, resolvedWorkspaceId));
|
|
261
300
|
if (!legacyResolve.workspaceId) {
|
|
262
301
|
throw new Error("Workspace resolution did not return a workspaceId.");
|
|
263
302
|
}
|
|
264
303
|
workspaceId = legacyResolve.workspaceId;
|
|
265
|
-
scan = await withLoadingIndicator("Starting
|
|
304
|
+
scan = await withLoadingIndicator("Starting audit scan...", flags, () => client.request(`/api/cli/v1/local-workspaces/${encodeURIComponent(workspaceId)}/scan`, {
|
|
266
305
|
method: "POST",
|
|
267
306
|
json: {
|
|
268
307
|
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 scan credits for the active company
|
|
3
|
+
apex credits Show scan credits and audit 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
|
|
@@ -28,7 +28,7 @@ Flags:
|
|
|
28
28
|
Choose the export format for findings
|
|
29
29
|
--output <path> Write exported findings to this file path
|
|
30
30
|
--limit <count> Limit the number of findings returned
|
|
31
|
-
--mode standard|
|
|
31
|
+
--mode standard|audit Choose the scan mode
|
|
32
32
|
--source-mode auto|remote|local Control remote-vs-local source materialization
|
|
33
33
|
--force Start a new scan even if another scan is active
|
|
34
34
|
--repo <path> Include one or more explicit local source roots
|
|
@@ -39,6 +39,7 @@ Flags:
|
|
|
39
39
|
|
|
40
40
|
Tips:
|
|
41
41
|
apex scan uses the current directory name as the default workspace name unless you pass --workspace-name.
|
|
42
|
+
audit is the user-facing name for the legacy ultra scan mode; ultra remains accepted as an alias.
|
|
42
43
|
apex workspace use accepts a workspace name, prefix, or ID.
|
|
43
44
|
Quote workspace names that contain spaces:
|
|
44
45
|
apex workspace use "Core Platform"
|
|
@@ -46,8 +47,8 @@ Tips:
|
|
|
46
47
|
export const SHELL_HELP_TEXT = `Press Tab to autocomplete commands and common arguments.
|
|
47
48
|
|
|
48
49
|
Commands:
|
|
49
|
-
/credits Show scan credits for the active company
|
|
50
|
-
/scan [standard|
|
|
50
|
+
/credits Show scan credits and audit scan entitlements for the active company
|
|
51
|
+
/scan [standard|audit] Start a new Apex scan for this workspace
|
|
51
52
|
/scans List scans for this workspace
|
|
52
53
|
/findings [scan-id] List findings for the latest or selected scan
|
|
53
54
|
/export [scan-id] Export findings for the latest or selected scan
|
|
@@ -70,6 +71,7 @@ Commands:
|
|
|
70
71
|
/exit Exit Apex
|
|
71
72
|
|
|
72
73
|
Tips:
|
|
74
|
+
audit is the user-facing name for the legacy ultra scan mode; ultra remains accepted as an alias.
|
|
73
75
|
/workspace use accepts a workspace name, prefix, or ID.
|
|
74
76
|
Quote workspace names that contain spaces: /workspace use "Core Platform"
|
|
75
77
|
`;
|
package/dist/mcp.js
CHANGED
|
@@ -19,6 +19,7 @@ Suggested workflow:
|
|
|
19
19
|
Notes:
|
|
20
20
|
- Pass cwd explicitly for repository-specific operations.
|
|
21
21
|
- Tool calls are always non-interactive and never auto-open a browser.
|
|
22
|
+
- Use mode "audit" for audit scans; "ultra" remains accepted as a legacy alias.
|
|
22
23
|
- Doctor reports whether Apex will use remote materialization or a local snapshot upload for each source.`;
|
|
23
24
|
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.";
|
|
24
25
|
const MISSING_PROVIDER_CONNECTIONS_ERROR = "Missing provider connections. Re-run interactively or pre-connect providers.";
|
|
@@ -248,7 +249,7 @@ function registerTools(server) {
|
|
|
248
249
|
}, (value) => `Apex doctor completed for ${String(value.cwd)}.`));
|
|
249
250
|
server.registerTool("apex-credits", {
|
|
250
251
|
title: "Get Apex Credits",
|
|
251
|
-
description: "Show scan credits for the active or selected company.",
|
|
252
|
+
description: "Show scan credits and audit scan entitlements for the active or selected company.",
|
|
252
253
|
inputSchema: {
|
|
253
254
|
cwd: z.string().optional(),
|
|
254
255
|
company: z.string().optional(),
|
|
@@ -320,13 +321,13 @@ function registerTools(server) {
|
|
|
320
321
|
}, (value) => `Bound ${String(value.cwd)} to an Apex workspace.`));
|
|
321
322
|
server.registerTool("apex-scan", {
|
|
322
323
|
title: "Start Apex Scan",
|
|
323
|
-
description: "Start a new Apex scan for any local repository or directory. Pass cwd explicitly for reliable workspace resolution.",
|
|
324
|
+
description: "Start a new Apex scan for any local repository or directory. Pass cwd explicitly for reliable workspace resolution. Use mode audit for audit scans.",
|
|
324
325
|
inputSchema: {
|
|
325
326
|
cwd: z.string().optional(),
|
|
326
327
|
company: z.string().optional(),
|
|
327
328
|
workspaceName: z.string().optional(),
|
|
328
329
|
repoPaths: z.array(z.string()).optional(),
|
|
329
|
-
mode: z.enum(["standard", "ultra"]).optional(),
|
|
330
|
+
mode: z.enum(["standard", "audit", "ultra"]).optional(),
|
|
330
331
|
sourceMode: z.enum(["auto", "remote", "local"]).optional(),
|
|
331
332
|
force: z.boolean().optional(),
|
|
332
333
|
},
|
package/dist/scan.js
CHANGED
|
@@ -30,6 +30,10 @@ function readNumber(...values) {
|
|
|
30
30
|
}
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
|
+
function readScanMode(...values) {
|
|
34
|
+
const mode = readString(...values);
|
|
35
|
+
return mode === "ultra" ? "audit" : mode;
|
|
36
|
+
}
|
|
33
37
|
function getScanListItems(payload) {
|
|
34
38
|
if (Array.isArray(payload)) {
|
|
35
39
|
return payload;
|
|
@@ -63,7 +67,7 @@ function normalizeScanRecord(payload) {
|
|
|
63
67
|
displayName: readString(record.displayName, record.display_name, record.name),
|
|
64
68
|
sequenceNumber: readNumber(record.sequenceNumber, record.sequence_number),
|
|
65
69
|
status: readString(record.status) ?? "unknown",
|
|
66
|
-
mode:
|
|
70
|
+
mode: readScanMode(record.mode, record.scanType, record.scan_type),
|
|
67
71
|
scanUrl: readString(record.scanUrl, record.url, record.scan_url),
|
|
68
72
|
createdAt: readTimestamp(record, "createdAt", "created_at"),
|
|
69
73
|
startedAt: readTimestamp(record, "startedAt", "started_at"),
|
package/dist/shell.js
CHANGED
|
@@ -6,7 +6,7 @@ import { readLine } from "./prompt.js";
|
|
|
6
6
|
import { formatApiError } from "./api-client.js";
|
|
7
7
|
const SHELL_COMPLETIONS = [
|
|
8
8
|
{ command: "credits" },
|
|
9
|
-
{ command: "scan", args: ["standard", "ultra"] },
|
|
9
|
+
{ command: "scan", args: ["standard", "audit", "ultra"] },
|
|
10
10
|
{ command: "scans" },
|
|
11
11
|
{ command: "findings" },
|
|
12
12
|
{ command: "export" },
|
|
@@ -178,8 +178,8 @@ async function runShellCommand(client, cwd, parsed, shellFlags, session) {
|
|
|
178
178
|
return {};
|
|
179
179
|
case "scan": {
|
|
180
180
|
const mode = parsed.args[0];
|
|
181
|
-
if (mode && !["standard", "ultra"].includes(mode)) {
|
|
182
|
-
process.stderr.write("Usage: /scan [standard|ultra]\n");
|
|
181
|
+
if (mode && !["standard", "audit", "ultra"].includes(mode)) {
|
|
182
|
+
process.stderr.write("Usage: /scan [standard|audit|ultra]\n");
|
|
183
183
|
return {};
|
|
184
184
|
}
|
|
185
185
|
const result = await commandScan(client, cwd, mode ? withFlag(shellFlags, "mode", mode) : shellFlags);
|
package/package.json
CHANGED
package/skills/apex-cli/SKILL.md
CHANGED
|
@@ -21,8 +21,10 @@ Workflow:
|
|
|
21
21
|
Guidelines:
|
|
22
22
|
|
|
23
23
|
- Do not rely on interactive CLI prompts. The MCP tools are intentionally non-interactive.
|
|
24
|
+
- `apex-credits` reports standard credits and audit scan entitlements when Bedrock returns both balances.
|
|
24
25
|
- `apex-doctor` reports whether Apex will use remote materialization or a local snapshot upload for each selected source.
|
|
25
26
|
- Apex can scan plain local directories and dirty git worktrees without provider connections by using local snapshot uploads.
|
|
27
|
+
- Audit scans use `mode: "audit"` in user-facing instructions. The legacy `ultra` mode remains accepted as an alias, but audit scans still require provider-backed GitHub or GitLab sources.
|
|
26
28
|
- `apex-workspace-use` accepts a workspace name, prefix, or ID.
|
|
27
29
|
- Use `sourceMode: "remote"` only when the user explicitly wants to forbid local snapshot fallbacks.
|
|
28
30
|
- Use `force: true` on `apex-scan` only when the user explicitly wants to replace or overlap an active scan.
|