Repository navigation
feat(usage): polymorfa usage summary, records and gates #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
13 commits
Select commit
Hold shift + click to select a range
4ea4c1c
feat(usage): polymorfa usage summary, records and gates
purpshell 8023bae
docs(usage): usage gates needs a team API key
purpshell 83dce18
fix(usage): show each row's project in cross-project usage output
purpshell 6e4ddac
Merge remote-tracking branch 'origin/dev' into t3code/usage-metering-…
purpshell 15cdf56
Merge remote-tracking branch 'origin/dev' into t3code/usage-metering-…
purpshell 775e2c1
Use typed SDK usage methods in CLI commands
purpshell d629581
Merge remote-tracking branch 'origin/dev' into t3code/usage-metering-…
purpshell 9c9fe2c
Handle campaign stop without a delivery operation
purpshell cda07b8
Merge remote-tracking branch 'origin/dev' into t3code/usage-metering-…
purpshell 4d3553d
Document immediate campaign stop outcome
purpshell 30d3aa2
Merge remote-tracking branch 'origin/dev' into t3code/usage-metering-…
purpshell e527412
docs(usage): restore Calls usage CLI guidance after dev merge
purpshell 281e3b8
feat(cli): pin typed usage SDK and honor project tokens
purpshell File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import {Flags} from '@oclif/core' | ||
|
|
||
| import {BaseCommand} from '../../base-command.js' | ||
| import { | ||
| createUsageResource, | ||
| listUsageGates, | ||
| renderUsageGates, | ||
| } from '../../services/usage.js' | ||
|
|
||
| export default class UsageGates extends BaseCommand { | ||
| static override description = | ||
| 'List your usage gates with their mode, limit, usage this month and how often an action went over the limit in the last 30 days. In record mode a gate never refuses an action; in enforce mode an action over the limit fails with gate_limit_reached. Pass --session to include limits that apply per number. Gate state covers the whole team, so this needs a team API key; project API keys are refused. A limit that cannot be checked fails the action with 503 in either mode.' | ||
|
|
||
| static override examples = [ | ||
| '<%= config.bin %> usage gates', | ||
| '<%= config.bin %> usage gates --session support --json', | ||
| ] | ||
|
|
||
| static override flags = { | ||
| session: Flags.string({ | ||
| description: 'Include limits that apply to one number (session name).', | ||
| }), | ||
| } | ||
|
|
||
| static override summary = 'List usage gates and their limits.' | ||
|
|
||
| async run(): Promise<void> { | ||
| const {flags} = await this.parse(UsageGates) | ||
| const result = await listUsageGates( | ||
| createUsageResource( | ||
| await this.developerEnvironment(flags, {inferOwner: true}), | ||
| ), | ||
| { | ||
| ...(flags.project === undefined ? {} : {project: flags.project}), | ||
| ...(flags.session === undefined ? {} : {session: flags.session}), | ||
| }, | ||
| ) | ||
| this.writeValue(result, renderUsageGates(result.data), flags) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import {Flags} from '@oclif/core' | ||
|
|
||
| import {BaseCommand} from '../../base-command.js' | ||
| import { | ||
| createUsageResource, | ||
| listUsageRecords, | ||
| renderUsageRecords, | ||
| } from '../../services/usage.js' | ||
|
|
||
| export default class UsageRecords extends BaseCommand { | ||
| static override description = | ||
| 'List usage records, newest first. Filter by call, number, meter or calendar month. A call has one call.duration record, and an outgoing Cloud API call also a call.cloud_pulses record. Pass the printed cursor to read the next page.' | ||
|
|
||
| static override examples = [ | ||
| '<%= config.bin %> usage records --call 3EB0C431C26A1916E07A', | ||
| '<%= config.bin %> usage records --session support --meter call.duration --limit 50 --json', | ||
| ] | ||
|
|
||
| static override flags = { | ||
| call: Flags.string({description: 'One call, by call ID.'}), | ||
| cursor: Flags.string({description: 'Continue from a previous page.'}), | ||
| limit: Flags.integer({ | ||
| description: 'Records per page, 1 to 100.', | ||
| max: 100, | ||
| min: 1, | ||
| }), | ||
| meter: Flags.string({ | ||
| description: 'One meter, for example call.duration.', | ||
| }), | ||
| period: Flags.string({ | ||
| description: 'Calendar month in UTC as YYYY-MM.', | ||
| }), | ||
| session: Flags.string({description: 'Limit to one number (session name).'}), | ||
| } | ||
|
|
||
| static override summary = 'List usage records.' | ||
|
|
||
| async run(): Promise<void> { | ||
| const {flags} = await this.parse(UsageRecords) | ||
| const result = await listUsageRecords( | ||
| createUsageResource( | ||
| await this.developerEnvironment(flags, {inferOwner: true}), | ||
| ), | ||
| { | ||
| ...(flags.call === undefined ? {} : {callId: flags.call}), | ||
| ...(flags.cursor === undefined ? {} : {cursor: flags.cursor}), | ||
| ...(flags.limit === undefined ? {} : {limit: flags.limit}), | ||
| ...(flags.meter === undefined ? {} : {meter: flags.meter}), | ||
| ...(flags.period === undefined ? {} : {period: flags.period}), | ||
| ...(flags.project === undefined ? {} : {project: flags.project}), | ||
| ...(flags.session === undefined ? {} : {session: flags.session}), | ||
| }, | ||
| ) | ||
| this.writeValue(result, renderUsageRecords(result.data), flags) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| import {Flags} from '@oclif/core' | ||
|
|
||
| import {BaseCommand} from '../../base-command.js' | ||
| import { | ||
| createUsageResource, | ||
| getUsageSummary, | ||
| renderUsageSummary, | ||
| } from '../../services/usage.js' | ||
|
|
||
| export default class UsageSummary extends BaseCommand { | ||
| static override description = | ||
| 'Show metered usage for one calendar month (UTC), by meter and by number. Usage is measured, not charged: no credits are deducted for it. Requires sessions:read. A project token reads only its own project.' | ||
|
|
||
| static override examples = [ | ||
| '<%= config.bin %> usage summary', | ||
| '<%= config.bin %> usage summary --period 2026-09 --session support --json', | ||
| ] | ||
|
|
||
| static override flags = { | ||
| period: Flags.string({ | ||
| description: 'Calendar month in UTC as YYYY-MM. Defaults to this month.', | ||
| }), | ||
| session: Flags.string({description: 'Limit to one number (session name).'}), | ||
| } | ||
|
|
||
| static override summary = 'Show metered usage for a month.' | ||
|
|
||
| async run(): Promise<void> { | ||
| const {flags} = await this.parse(UsageSummary) | ||
| const result = await getUsageSummary( | ||
| createUsageResource( | ||
| await this.developerEnvironment(flags, {inferOwner: true}), | ||
| ), | ||
| { | ||
| ...(flags.period === undefined ? {} : {period: flags.period}), | ||
| ...(flags.project === undefined ? {} : {project: flags.project}), | ||
| ...(flags.session === undefined ? {} : {session: flags.session}), | ||
| }, | ||
| ) | ||
| this.writeValue(result, renderUsageSummary(result.data), flags) | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When an organization-key user passes a project slug,
developerEnvironmentresolves it to a canonical UUID, but this reconstructs theprojectIdquery from the originalflags.projectvalue. The API therefore receives the slug in a UUID field and rejects an otherwise supported--project <slug>invocation. The records and gates commands repeat the same pattern; retain the resolved environment and passenvironment.context.projectId, as the call-analytics commands do.Useful? React with 👍 / 👎.