Repository navigation
feat(cli): introduce recordings commands - #98
Conversation
…getFile Fix handleMcpError bug where extractText threw before custom error formatting could run. Extract withAuth higher-order wrapper and resolveTargetFile helper to eliminate repeated boilerplate across 13 handlers. Remove redundant ?? fallbacks (yargs provides defaults), switch to barrel imports from @network/index, and simplify addRecordingRule param forwarding. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
filipmyllari
left a comment
There was a problem hiding this comment.
Review notes (AI-assisted). Inline comments below. Tests were skimmed and the events refactor was only checked for the withAuth/safely mapping.
| message(''); | ||
| message('Rules:'); | ||
| print({ | ||
| data: data.rules.map((r) => ({ |
There was a problem hiding this comment.
With --json / --output json, this prints one JSON document, then the plain-text line Rules: (or No rules configured.), then a second JSON document. The combined output isn't valid JSON, so policy get X --json | jq fails. In JSON mode, print a single object ({...policy, rules}) and skip the message calls.
| if (data.nextPageToken) { | ||
| message(`\nNext page: --page-token ${data.nextPageToken}`); |
There was a problem hiding this comment.
In JSON mode, the Next page: --page-token … line is added after the JSON array, so the output isn't valid JSON. Include nextPageToken in the JSON output, or write the hint to stderr.
| } | ||
|
|
||
| print<PolicyRow>({ | ||
| data: data.recordingPolicies.map((p) => ({ |
There was a problem hiding this comment.
Proto3 JSON drops empty repeated fields, so recordingPolicies and clients may be missing. If they are, an account with no policies, or a policy with no clients, hits Cannot read properties of undefined instead of the empty-state message. Use ?? [] here, and the same in get-policy.ts:33 (clients) and show-targeting-keys.ts:31 (fields).
| }; | ||
|
|
||
| export const addRule = withAuth(async function addRule(argv, token) { | ||
| const params = resolveTargetFile<RuleParams>(argv, () => ({ |
There was a problem hiding this comment.
With --from-file, the parsed file is used as-is, with no checks. Missing keys are sent as undefined and the CLI defaults are lost (audience-percentage 100, sample-rate 1, enabled true). The file also has to use kebab-case keys such as "targeting-key", which nothing documents. Merge the file over the argv defaults and check the required keys after the merge. The same applies to add-targeting-key.ts and create-policy.ts.
| .option('audience-percentage', { | ||
| type: 'number', | ||
| describe: 'Stable audience percentage (0-100)', | ||
| default: 100, | ||
| }) | ||
| .option('sample-rate', { | ||
| type: 'number', | ||
| describe: 'Session sample rate (0-1)', | ||
| default: 1, | ||
| }) | ||
| .option('enabled', { |
There was a problem hiding this comment.
Nothing checks the range of --audience-percentage (0–100) or --sample-rate (0–1). A value that isn't a number becomes NaN and is sent anyway, and --sample-rate 50 goes through as is. Add these checks to the existing .check(). Also, enabled defaults to true with a 100% audience, so leaving the flags off starts recording every visitor straight away. Consider defaulting to enabled: false.
| 'is-entity': argv['is-entity'] as boolean, | ||
| })); | ||
|
|
||
| const result = await addContextField(token, { |
There was a problem hiding this comment.
addContextField takes no client argument, but targeting-key show is per-client. I couldn't check the MCP tool's schema. If the field belongs to one client's schema, a --client option is missing.
| import { extractText, type CallToolResult } from '@spotify-confidence/core'; | ||
| import { error, fail } from '@output/print.js'; | ||
|
|
||
| export function handleMcpError(result: CallToolResult): boolean { |
There was a problem hiding this comment.
handleMcpError, which prints the "Available clients are:" list nicely, is only used by list, get and show. policy create --client-name is the command most likely to return that error, but it goes through printMcpResult → safely and prints the raw message. Use handleMcpError there as well.
| return mcpCallTool(serverOpts(token), 'addContextField', { | ||
| fieldName: params.fieldName, | ||
| fieldType: params.fieldType, | ||
| isEntity: String(params.isEntity), |
There was a problem hiding this comment.
String(params.isEntity) sends the string "true" instead of a boolean. That's fine if the tool's schema expects a string, but it's worth confirming.
In JSON mode, policy details and rules were printed as two separate JSON documents with plain-text lines between them, breaking piped jq usage. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The next-page hint was appended as plain text after the JSON array, breaking piped jq usage. In JSON mode, nextPageToken is now included in the envelope's meta field instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Proto3 JSON omits empty repeated fields, so recordingPolicies, clients, and fields may be undefined. Default to empty arrays to avoid crashing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…keys With --from-file, the parsed file replaced argv entirely, losing CLI defaults (audience-percentage 100, sample-rate 1, enabled true) and requiring undocumented kebab-case keys. Now the file is merged over defaults with camelCase-to-kebab normalization, and required keys are validated post-merge. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reject out-of-range, NaN, and Infinity values for --audience-percentage (0-100) and --sample-rate (0-1) in both CLI args and --from-file input. Default enabled to false to avoid accidentally recording all visitors. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rgeting-key add The MCP tool accepts displayName, entityReference, and a clients list, but the CLI only sent fieldName, fieldType, and isEntity. Wire through the missing parameters. Update tests for earlier default changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add formatted MCP error handling to create-policy, add-rule, add-targeting-key, and set-rule-enabled. Rename handleMcpError to tryHandleMcpError for clarity. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The MCP tool expects a boolean for isEntity. The String() conversion sent "true"/"false" strings instead. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
policy,rule,targeting-key) backed by MCP tools on theconfidence-flagsserverrequireAuth,readJsonFile,serverOpts, test helpers) to reduce duplication across events and recordingsCommands Added
confidence recordings policy listconfidence recordings policy createconfidence recordings policy get <policy>confidence recordings rule addconfidence recordings rule enable <rule>confidence recordings rule disable <rule>confidence recordings targeting-key show <client>confidence recordings targeting-key addAll write commands (
policy create,rule add,targeting-key add) support--from-filefor JSON input.