Skip to content
Merged
13 changes: 13 additions & 0 deletions plugins/ClaudeConsole/v1/configValidation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"steps": [
{
"displayName": "Authenticate",
"dataStream": {
"name": "organization"
},
"required": true,
"success": "Connected to your Claude Console organization.",
"error": "Could not authenticate. Check that your Admin API key (sk-ant-admin...) is valid and belongs to an organization, not an individual account."
}
]
}
23 changes: 23 additions & 0 deletions plugins/ClaudeConsole/v1/custom_types.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[
{
"name": "Workspace",
"sourceType": "Workspace",
"icon": "layer-group",
"singular": "Workspace",
"plural": "Workspaces"
},
{
"name": "API Key",
"sourceType": "API Key",
"icon": "key",
"singular": "API Key",
"plural": "API Keys"
},
{
"name": "Member",
"sourceType": "Member",
"icon": "user",
"singular": "Member",
"plural": "Members"
}
]
100 changes: 100 additions & 0 deletions plugins/ClaudeConsole/v1/dataStreams/apiKeys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"name": "apiKeys",
"displayName": "API Keys",
"description": "API keys in your Claude Console organization",
"tags": ["API Keys"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v1/organizations/api_keys",
"getArgs": [
{
"key": "limit",
"value": "1000"
},
{
"key": "created_by_user_id",
"value": "{{ (createdBy && createdBy.length) ? (Array.isArray(createdBy[0].rawId) ? createdBy[0].rawId[0] : createdBy[0].rawId) : null }}"
}
],
"paging": {
"mode": "none"
},
"expandInnerObjects": true,
"postRequestScript": "apiKeys.js"
},
"matches": "none",
"ui": [
{
"type": "objects",
"name": "workspace",
"label": "Workspace (optional)",
"matches": {
"sourceType": {
"type": "oneOf",
"values": ["Workspace"]
}
}
},
{
"type": "objects",
"name": "createdBy",
"label": "Created by (optional)",
"matches": {
"sourceType": { "type": "oneOf", "values": ["Member"] }
}
}
],
"metadata": [
{
"name": "id",
"displayName": "API Key ID",
"shape": "string"
},
{
"name": "name",
"displayName": "Name",
"shape": "string",
"role": "label"
},
{
"name": "status",
"displayName": "Status",
"shape": [
"state",
{
"map": {
"success": ["active"],
"unknown": ["expired", "archived", "inactive"]
}
}
]
},
{
"name": "workspace_id",
"displayName": "Workspace ID",
"shape": "string"
},
{
"name": "partial_key_hint",
"displayName": "Key Hint",
"shape": "string"
},
{
"name": "created_at",
"displayName": "Created",
"shape": "date"
},
{
"name": "expires_at",
"displayName": "Expires",
"shape": "date"
},
{
"name": "created_by.id",
"displayName": "Created By",
"shape": "string"
}
],
"timeframes": false
}
103 changes: 103 additions & 0 deletions plugins/ClaudeConsole/v1/dataStreams/cost.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
{
"name": "cost",
"displayName": "Cost",
"description": "Organization spend in USD over time, one row per workspace/model/token-type bucket per day",
"tags": ["Cost", "Billing"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v1/organizations/cost_report",
"getArgs": [
{
"key": "starting_at",
"value": "{{timeframe.start}}"
},
{
"key": "ending_at",
"value": "{{timeframe.end}}"
},
{
"key": "bucket_width",
"value": "1d"
},
{
"key": "group_by[]",
"value": "workspace_id"
},
{
"key": "group_by[]",
"value": "description"
},
{
"key": "limit",
"value": "31"
}
],
"paging": {
"mode": "none"
},
"postRequestScript": "cost.js"
},
"matches": "none",
"ui": [
{
"type": "objects",
"name": "workspace",
"label": "Workspace (optional)",
"matches": {
"sourceType": {
"type": "oneOf",
"values": ["Workspace"]
}
}
}
],
"metadata": [
{
"name": "date",
"displayName": "Date",
"shape": "date",
"role": "timestamp"
},
{
"name": "amount",
"displayName": "Cost ($)",
Comment thread
clarkd marked this conversation as resolved.
"shape": [
"currency",
{
"code": "{{column.currency}}",
"decimalPlaces": 2,
"thousandsSeparator": true
}
],
"role": "value"
},
{
"name": "workspace_id",
"displayName": "Workspace ID",
"shape": "string"
},
{
"name": "workspaceName",
"displayName": "Workspace",
"sourceId": "workspace_id",
"sourceType": "Workspace",
"objectPropertyPath": "name"
},
{
"name": "description",
"displayName": "Description",
"shape": "string"
},
{
"name": "currency",
"displayName": "Currency",
"shape": "string",
"visible": false
},
{
"pattern": ".*"
}
],
"timeframes": ["last7days", "last30days", "thisMonth", "lastMonth"]
}
60 changes: 60 additions & 0 deletions plugins/ClaudeConsole/v1/dataStreams/members.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"name": "members",
"displayName": "Members",
"description": "Organization members in your Claude Console organization, with email and role",
"tags": [
"Members"
],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v1/organizations/users",
"getArgs": [
{
"key": "limit",
"value": "1000"
}
],
"paging": {
"mode": "none"
},
"pathToData": "data"
},
"matches": "none",
"metadata": [
{
"name": "id",
"displayName": "User ID",
"shape": "string"
},
{
"name": "name",
"displayName": "Name",
"shape": "string"
},
{
"name": "email",
"displayName": "Email",
"shape": "string"
},
{
"name": "role",
"displayName": "Role",
"shape": "string"
},
{
"name": "added_at",
"displayName": "Added",
"shape": "date"
},
{
"name": "memberName",
"displayName": "Member",
"computed": true,
"valueExpression": "{{ $['name']?.__isNone ? $['email'] : $['name'] }}",
"shape": "string",
"role": "label"
}
],
"timeframes": false
}
41 changes: 41 additions & 0 deletions plugins/ClaudeConsole/v1/dataStreams/organization.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "organization",
"displayName": "Organization",
"description": "Your Claude Console organization id, name, and type",
"tags": [
"Organization"
],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v1/organizations/me",
"paging": {
"mode": "none"
},
"getArgs": [],
"headers": []
},
"matches": "none",
"metadata": [
{
"name": "id",
"displayName": "Organization ID",
"shape": "string"
},
{
"name": "name",
"displayName": "Name",
"shape": "string",
"role": "label"
},
{
"name": "type",
"displayName": "Type",
"shape": "string"
}
],
"timeframes": false,
"visibility": {
"type": "hidden"
}
}
20 changes: 20 additions & 0 deletions plugins/ClaudeConsole/v1/dataStreams/scripts/apiKeys.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const unwrap = (v) => (Array.isArray(v) ? v[0] : v);

// Build set of selected workspace rawIds (empty → account-wide, no filter)
const selected = (context.config && context.config.workspace) || [];
const workspaceIds = new Set(
selected.map((o) => unwrap(o.rawId)).filter(Boolean),
);

let rows = data.data.map((a) => ({
...a,
// Claude does not let us filter by the default workspace on their api.
workspace_id: a.workspace_id || "default",
}));

// Apply optional workspace scope filter
if (workspaceIds.size) {
rows = rows.filter((r) => workspaceIds.has(r.workspace_id));
}

result = rows;
32 changes: 32 additions & 0 deletions plugins/ClaudeConsole/v1/dataStreams/scripts/cost.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// cost.js — flatten nested time-bucket/results structure, coerce amount string→number,
// and optionally scope to selected workspace(s) via the `workspace` objects param.

const unwrap = (v) => (Array.isArray(v) ? v[0] : v);
Comment thread
clarkd marked this conversation as resolved.

// Build set of selected workspace rawIds (empty → account-wide, no filter)
const selected = (context.config && context.config.workspace) || [];
const workspaceIds = new Set(
selected.map((o) => unwrap(o.rawId)).filter(Boolean),
);

// Flatten buckets × results into one row per combination
let rows = (data.data || []).flatMap((bucket) =>
(bucket.results || []).map((r) => ({
...r,
date: bucket.starting_at,
// API returns `amount` in lowest currency units (cents) as a decimal string,
// e.g. "300" cents = $3.00 — divide by 100 to get USD. Verified empirically:
// cost/token matches Claude's published per-model list prices exactly only in cents.
amount: Number(r.amount) / 100,
workspace_id: r.workspace_id ?? "default",
description: r.description,
})),
);

// Apply optional workspace scope filter
if (workspaceIds.size) {
rows = rows.filter((r) => workspaceIds.has(r.workspace_id));
}

result = rows;

Loading
Loading