@clocknext/sdk 0.14.0 → 0.16.0
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/README.md +274 -128
- package/dist/index.cjs +345 -281
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +689 -773
- package/dist/index.d.ts +689 -773
- package/dist/index.js +345 -281
- package/dist/index.js.map +1 -1
- package/examples/node-basic.ts +24 -32
- package/package.json +1 -1
- package/src/client.ts +15 -23
- package/src/customer-ref.ts +14 -0
- package/src/errors.ts +10 -2
- package/src/filters.ts +1 -1
- package/src/index.ts +32 -58
- package/src/resources/composites.ts +55 -20
- package/src/resources/credits.ts +40 -35
- package/src/resources/customers.ts +201 -82
- package/src/resources/invoices.ts +1 -1
- package/src/resources/outcomes.ts +40 -41
- package/src/resources/plans.ts +38 -32
- package/src/resources/signals.ts +8 -46
- package/src/resources/units.ts +37 -37
- package/src/types.ts +549 -619
- package/src/resources/webhooks.ts +0 -37
- package/src/resources/workspace.ts +0 -33
package/examples/node-basic.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Basic usage: record a few signals from a long-lived Node process.
|
|
3
|
-
* Run with: CLOCKNEXT_API_KEY=cnk_… npx tsx examples/node-basic.ts
|
|
4
3
|
*
|
|
5
4
|
* Note the discovery step first. `customerId`, `model` and `agentKey` are not
|
|
6
5
|
* strings you can invent — they name rows that must already exist in your
|
|
7
6
|
* workspace, and a signal naming one that does not is rejected. So this example
|
|
8
|
-
* reads
|
|
9
|
-
* guaranteed to 422 on someone else's workspace.
|
|
7
|
+
* reads the customer and its agent keys out of the API rather than hard-coding
|
|
8
|
+
* placeholders that are guaranteed to 422 on someone else's workspace. The
|
|
9
|
+
* model id comes from Settings → Models in the dashboard:
|
|
10
|
+
*
|
|
11
|
+
* CLOCKNEXT_API_KEY=cnk_… CLOCKNEXT_MODEL=gpt-4o npx tsx examples/node-basic.ts
|
|
10
12
|
*/
|
|
11
13
|
import { ClockNext } from "@clocknext/sdk";
|
|
12
14
|
|
|
@@ -18,34 +20,28 @@ const cnk = new ClockNext({
|
|
|
18
20
|
});
|
|
19
21
|
|
|
20
22
|
async function main() {
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
// 1. Discover real ids instead of guessing them.
|
|
27
|
-
const [models, credits, customers] = await Promise.all([
|
|
28
|
-
cnk.workspace.models({ active: true }),
|
|
29
|
-
cnk.credits.list({ active: true }),
|
|
30
|
-
cnk.customers.list({ limit: 1 }),
|
|
31
|
-
]);
|
|
32
|
-
|
|
33
|
-
const model = models[0];
|
|
34
|
-
const credit = credits[0];
|
|
35
|
-
const customer = customers.customers[0];
|
|
36
|
-
if (!model || !credit || !customer) {
|
|
23
|
+
// 1. Discover real ids instead of guessing them. A customer's balances list
|
|
24
|
+
// every credit and outcome step its plan sells, each with its agentKey.
|
|
25
|
+
const model = process.env.CLOCKNEXT_MODEL;
|
|
26
|
+
const customer = (await cnk.customers.list({ limit: 1 })).customers[0];
|
|
27
|
+
if (!model || !customer) {
|
|
37
28
|
throw new Error(
|
|
38
|
-
"
|
|
29
|
+
"Set CLOCKNEXT_MODEL to a model enabled under Settings → Models, and create at least one customer, before metering anything.",
|
|
39
30
|
);
|
|
40
31
|
}
|
|
41
|
-
|
|
32
|
+
const balances = await cnk.customers.balances(customer.id);
|
|
33
|
+
const credit = balances.credits[0];
|
|
34
|
+
if (!credit) {
|
|
35
|
+
throw new Error("This customer's plan needs at least one credit before it can meter credit usage.");
|
|
36
|
+
}
|
|
37
|
+
console.log(`metering ${credit.agentKey} on ${model}`);
|
|
42
38
|
|
|
43
39
|
// 2. Credit usage — metered against a credit by its stable `agentKey`, priced
|
|
44
40
|
// by tokens. Optionally group it into a composite occurrence, billed once
|
|
45
41
|
// on close.
|
|
46
42
|
await cnk.signals.credit({
|
|
47
43
|
customerId: customer.id,
|
|
48
|
-
model
|
|
44
|
+
model,
|
|
49
45
|
agentKey: credit.agentKey,
|
|
50
46
|
tokens: { input: 1200, output: 340, cache: 0 },
|
|
51
47
|
custom: { feature: "summarize", tier: "pro" },
|
|
@@ -54,23 +50,19 @@ async function main() {
|
|
|
54
50
|
// 3. Wallet usage (debits the customer's USD wallet at model cost).
|
|
55
51
|
await cnk.signals.wallet({
|
|
56
52
|
customerId: customer.id,
|
|
57
|
-
model
|
|
53
|
+
model,
|
|
58
54
|
tokens: { input: 800, output: 5000 },
|
|
59
55
|
});
|
|
60
56
|
|
|
61
57
|
// 4. Outcome run — every step shares a `runId`; a separate, dedicated call
|
|
62
58
|
// (`completeOutcome`) closes and bills the run — never a flag on a step.
|
|
63
|
-
// `agentKey` is the OutcomeStep's key,
|
|
64
|
-
|
|
65
|
-
// `outcomes.list()` returns catalogue rows WITHOUT their steps; the step
|
|
66
|
-
// agent keys — which is what a signal reports against — are on the detail.
|
|
67
|
-
const outcomeRow = (await cnk.outcomes.list({ active: true }))[0];
|
|
68
|
-
const outcome = outcomeRow ? await cnk.outcomes.get(outcomeRow.id) : undefined;
|
|
59
|
+
// `agentKey` is the OutcomeStep's key, read off the same balances.
|
|
60
|
+
const outcome = balances.outcomes[0];
|
|
69
61
|
if (outcome?.steps[0]) {
|
|
70
62
|
const runId = `demo_run_${Date.now()}`;
|
|
71
63
|
await cnk.signals.outcome({
|
|
72
64
|
customerId: customer.id,
|
|
73
|
-
model
|
|
65
|
+
model,
|
|
74
66
|
agentKey: outcome.steps[0].agentKey,
|
|
75
67
|
runId,
|
|
76
68
|
tokens: { input: 600, output: 150 },
|
|
@@ -83,8 +75,8 @@ async function main() {
|
|
|
83
75
|
// asynchronous, so this — not the track() return value — is what proves a
|
|
84
76
|
// signal actually landed.
|
|
85
77
|
await cnk.flush();
|
|
86
|
-
const
|
|
87
|
-
console.log("recent logs:",
|
|
78
|
+
const page = await cnk.signals.usageLog({ customerId: customer.id, limit: 10 });
|
|
79
|
+
console.log("recent logs:", page.usageLogs.length);
|
|
88
80
|
|
|
89
81
|
await cnk.close();
|
|
90
82
|
}
|
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -12,8 +12,6 @@ import { Portal } from "./resources/portal.js";
|
|
|
12
12
|
import { Purchases } from "./resources/purchases.js";
|
|
13
13
|
import { Signals } from "./resources/signals.js";
|
|
14
14
|
import { Units } from "./resources/units.js";
|
|
15
|
-
import { Webhooks } from "./resources/webhooks.js";
|
|
16
|
-
import { Workspace } from "./resources/workspace.js";
|
|
17
15
|
import type { ClockNextConfig, ResolvedConfig } from "./types.js";
|
|
18
16
|
|
|
19
17
|
/**
|
|
@@ -34,32 +32,28 @@ import type { ClockNextConfig, ResolvedConfig } from "./types.js";
|
|
|
34
32
|
* ```
|
|
35
33
|
*/
|
|
36
34
|
export class ClockNext {
|
|
37
|
-
/** Record
|
|
35
|
+
/** Record usage signals (credit / wallet / outcome / unit) and read the usage log. */
|
|
38
36
|
readonly signals: Signals;
|
|
39
|
-
/** Manage customers, their members,
|
|
37
|
+
/** Manage customers, their members, and balances. */
|
|
40
38
|
readonly customers: Customers;
|
|
41
|
-
/**
|
|
39
|
+
/** Billing plans: list, get, create, update, archive, unarchive. */
|
|
42
40
|
readonly plans: Plans;
|
|
41
|
+
/** Credits: list, get, create, update, archive, unarchive (no delete). */
|
|
42
|
+
readonly credits: Credits;
|
|
43
|
+
/** Outcomes: list, get, create, update, archive, unarchive (no delete). */
|
|
44
|
+
readonly outcomes: Outcomes;
|
|
45
|
+
/** Units: list, get, create, update, archive, unarchive (no delete). */
|
|
46
|
+
readonly units: Units;
|
|
47
|
+
/** Composites: list, get, create, update, archive, unarchive (no delete). */
|
|
48
|
+
readonly composites: Composites;
|
|
43
49
|
/** Subscribe customers to plans (purchases). */
|
|
44
50
|
readonly purchases: Purchases;
|
|
45
51
|
/** Read invoices and mint hosted pay links. */
|
|
46
52
|
readonly invoices: Invoices;
|
|
47
53
|
/** Read payments (paid invoices). */
|
|
48
54
|
readonly payments: Payments;
|
|
49
|
-
/** Manage the credit-type catalog. */
|
|
50
|
-
readonly credits: Credits;
|
|
51
|
-
/** Manage the composite catalog — bundles billed as one thing. */
|
|
52
|
-
readonly composites: Composites;
|
|
53
|
-
/** Manage the outcome-type catalog. */
|
|
54
|
-
readonly outcomes: Outcomes;
|
|
55
|
-
/** Manage the unit-type catalog. */
|
|
56
|
-
readonly units: Units;
|
|
57
|
-
/** Configure threshold webhooks. */
|
|
58
|
-
readonly webhooks: Webhooks;
|
|
59
55
|
/** Mint customer-portal embed tokens. */
|
|
60
56
|
readonly portal: Portal;
|
|
61
|
-
/** Identify the workspace (sandbox/live) + list its models. `/me`, `/models`. */
|
|
62
|
-
readonly workspace: Workspace;
|
|
63
57
|
|
|
64
58
|
private readonly cfg: ResolvedConfig;
|
|
65
59
|
private readonly flusher: Flusher;
|
|
@@ -78,16 +72,14 @@ export class ClockNext {
|
|
|
78
72
|
this.signals = new Signals(transport, this.flusher, this.cfg);
|
|
79
73
|
this.customers = new Customers(transport);
|
|
80
74
|
this.plans = new Plans(transport);
|
|
81
|
-
this.purchases = new Purchases(transport);
|
|
82
|
-
this.invoices = new Invoices(transport);
|
|
83
|
-
this.payments = new Payments(transport);
|
|
84
75
|
this.credits = new Credits(transport);
|
|
85
|
-
this.composites = new Composites(transport);
|
|
86
76
|
this.outcomes = new Outcomes(transport);
|
|
87
77
|
this.units = new Units(transport);
|
|
88
|
-
this.
|
|
78
|
+
this.composites = new Composites(transport);
|
|
79
|
+
this.purchases = new Purchases(transport);
|
|
80
|
+
this.invoices = new Invoices(transport);
|
|
81
|
+
this.payments = new Payments(transport);
|
|
89
82
|
this.portal = new Portal(transport);
|
|
90
|
-
this.workspace = new Workspace(transport);
|
|
91
83
|
}
|
|
92
84
|
|
|
93
85
|
/** Number of signals currently buffered (async mode). */
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The customer half of a request body: `customerId` and/or `externalId`,
|
|
3
|
+
* each only when set. Both are forwarded as given — the server applies the
|
|
4
|
+
* precedence (`customerId` wins), so the SDK never second-guesses it.
|
|
5
|
+
*/
|
|
6
|
+
export function customerRefToWire(ref: {
|
|
7
|
+
customerId?: string;
|
|
8
|
+
externalId?: string;
|
|
9
|
+
}): { customerId?: string; externalId?: string } {
|
|
10
|
+
return {
|
|
11
|
+
...(ref.customerId != null ? { customerId: ref.customerId } : {}),
|
|
12
|
+
...(ref.externalId != null ? { externalId: ref.externalId } : {}),
|
|
13
|
+
};
|
|
14
|
+
}
|
package/src/errors.ts
CHANGED
|
@@ -107,7 +107,7 @@ export class NetworkError extends ClockNextError {
|
|
|
107
107
|
*
|
|
108
108
|
* The server validates the WIRE body and names the offending path in its 400
|
|
109
109
|
* (`usage.inputTokens: Expected number…`). But the SDK groups those fields
|
|
110
|
-
* differently — `tokens: { input, output, cache }` — so the path it reports
|
|
110
|
+
* differently — `tokens: { input, output, cache, cacheWrite }` — so the path it reports
|
|
111
111
|
* names something that does not exist anywhere in this SDK's API. A caller
|
|
112
112
|
* (human or LLM) then searches their own code for `usage.inputTokens`, finds
|
|
113
113
|
* nothing, and has no next move.
|
|
@@ -120,6 +120,7 @@ const FIELD_PATHS: ReadonlyArray<readonly [RegExp, string]> = [
|
|
|
120
120
|
[/\busage\.inputTokens\b/g, "tokens.input"],
|
|
121
121
|
[/\busage\.outputTokens\b/g, "tokens.output"],
|
|
122
122
|
[/\busage\.cacheTokens\b/g, "tokens.cache"],
|
|
123
|
+
[/\busage\.cacheWriteTokens\b/g, "tokens.cacheWrite"],
|
|
123
124
|
[/\busage\.model\b/g, "model"],
|
|
124
125
|
// A missing/invalid `usage` block as a whole: the caller's equivalent is
|
|
125
126
|
// `tokens` (plus `model`, which the SDK hoists to the top level).
|
|
@@ -154,7 +155,11 @@ export function translateFieldPaths(message: string): string {
|
|
|
154
155
|
* toward it still classifies, never as the sole test.
|
|
155
156
|
*/
|
|
156
157
|
const ALLOWANCE_MESSAGE =
|
|
157
|
-
/\binsufficient\b|\bno allowance left\b|\bno slots left\b|\boutcomes remaining\b|\btop up the (?:allowance|balance)\b/i;
|
|
158
|
+
/\binsufficient\b|\bno allowance left\b|\bno slots left\b|\boutcomes remaining\b|\btop up the (?:allowance|balance)\b|\bbelow zero\b/i;
|
|
159
|
+
|
|
160
|
+
/** The adjust API's 422 for an idempotency key reused with a DIFFERENT
|
|
161
|
+
* request. A conflict, not a plan problem — and never worth retrying. */
|
|
162
|
+
const IDEMPOTENCY_KEY_REUSED = /\bidempotency key was already used\b/i;
|
|
158
163
|
|
|
159
164
|
export function isAllowanceFailure(message: string, code?: string): boolean {
|
|
160
165
|
if (code) return code === "ALLOWANCE_EXHAUSTED";
|
|
@@ -205,6 +210,9 @@ export function errorFromResponse(
|
|
|
205
210
|
});
|
|
206
211
|
}
|
|
207
212
|
if (status === 422) {
|
|
213
|
+
if (IDEMPOTENCY_KEY_REUSED.test(raw)) {
|
|
214
|
+
return new ConflictError(raw, { ...opts, retryable: false });
|
|
215
|
+
}
|
|
208
216
|
return isAllowanceFailure(raw, code)
|
|
209
217
|
? new AllowanceError(raw, opts)
|
|
210
218
|
: new PlanError(raw, opts);
|
package/src/filters.ts
CHANGED
package/src/index.ts
CHANGED
|
@@ -47,7 +47,6 @@ export type {
|
|
|
47
47
|
OutcomeCompletionInput,
|
|
48
48
|
OutcomeCompletionResult,
|
|
49
49
|
TrackResult,
|
|
50
|
-
UsageList,
|
|
51
50
|
SerializedUsageLog,
|
|
52
51
|
UsageBalance,
|
|
53
52
|
SerializedRuleApplication,
|
|
@@ -56,8 +55,6 @@ export type {
|
|
|
56
55
|
UnitTrackResult,
|
|
57
56
|
CompositeCompletionInput,
|
|
58
57
|
CompositeCompletionResult,
|
|
59
|
-
UnitUsageRow,
|
|
60
|
-
UnitUsageList,
|
|
61
58
|
SerializedUnitUsage,
|
|
62
59
|
UsageLogPeriod,
|
|
63
60
|
UsageLogListParams,
|
|
@@ -65,10 +62,8 @@ export type {
|
|
|
65
62
|
UsageLogOutcomeAttribution,
|
|
66
63
|
UsageLogEntry,
|
|
67
64
|
UsageLogPage,
|
|
68
|
-
// workspace
|
|
69
|
-
WorkspaceIdentity,
|
|
70
|
-
Model,
|
|
71
65
|
// customers
|
|
66
|
+
CustomerRef,
|
|
72
67
|
ApiCustomer,
|
|
73
68
|
CustomerProfileInput,
|
|
74
69
|
CreateCustomerInput,
|
|
@@ -77,6 +72,7 @@ export type {
|
|
|
77
72
|
CreditBalance,
|
|
78
73
|
OutcomeBalance,
|
|
79
74
|
UnitBalance,
|
|
75
|
+
CompositeBalance,
|
|
80
76
|
CustomerPlan,
|
|
81
77
|
CustomerPlanDetails,
|
|
82
78
|
// members
|
|
@@ -85,30 +81,44 @@ export type {
|
|
|
85
81
|
RemovedMember,
|
|
86
82
|
AddMemberInput,
|
|
87
83
|
UpdateMemberInput,
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
WalletEntitlementKind,
|
|
93
|
-
WalletList,
|
|
94
|
-
WalletEntryInput,
|
|
84
|
+
// balance adjustments
|
|
85
|
+
WalletAdjustInput,
|
|
86
|
+
AdjustResult,
|
|
87
|
+
WalletPool,
|
|
95
88
|
CreditAdjustInput,
|
|
96
89
|
OutcomeAdjustInput,
|
|
97
90
|
UnitAdjustInput,
|
|
98
91
|
CompositeAdjustInput,
|
|
92
|
+
RecurringGrantInput,
|
|
93
|
+
RecurringGrantResult,
|
|
99
94
|
// plans
|
|
100
95
|
Plan,
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
PlanComponentInput,
|
|
104
|
-
WalletComponentInput,
|
|
105
|
-
FlatComponentInput,
|
|
106
|
-
CreditComponentInput,
|
|
107
|
-
OutcomeComponentInput,
|
|
108
|
-
UnitComponentInput,
|
|
109
|
-
CompositeComponentInput,
|
|
96
|
+
PlanEntitlement,
|
|
97
|
+
PlanEntitlementInput,
|
|
110
98
|
CreatePlanInput,
|
|
111
99
|
UpdatePlanInput,
|
|
100
|
+
// catalogue: credits, outcomes, units, composites
|
|
101
|
+
ModelBundleLine,
|
|
102
|
+
ModelBundleLineInput,
|
|
103
|
+
Credit,
|
|
104
|
+
CreateCreditInput,
|
|
105
|
+
UpdateCreditInput,
|
|
106
|
+
Outcome,
|
|
107
|
+
OutcomeStep,
|
|
108
|
+
OutcomeWithSteps,
|
|
109
|
+
OutcomeStepInput,
|
|
110
|
+
CreateOutcomeInput,
|
|
111
|
+
UpdateOutcomeInput,
|
|
112
|
+
UnitPricingType,
|
|
113
|
+
UnitTier,
|
|
114
|
+
Unit,
|
|
115
|
+
CreateUnitInput,
|
|
116
|
+
UpdateUnitInput,
|
|
117
|
+
CompositeItem,
|
|
118
|
+
Composite,
|
|
119
|
+
CreateCompositeInput,
|
|
120
|
+
UpdateCompositeInput,
|
|
121
|
+
PlanComponent,
|
|
112
122
|
BillingCycle,
|
|
113
123
|
PlanType,
|
|
114
124
|
PlanKind,
|
|
@@ -132,42 +142,6 @@ export type {
|
|
|
132
142
|
// payments
|
|
133
143
|
PaymentRow,
|
|
134
144
|
PaymentDetail,
|
|
135
|
-
// cost analytics (credits / outcomes)
|
|
136
|
-
CostPoint,
|
|
137
|
-
CostStats,
|
|
138
|
-
CreditAnalytics,
|
|
139
|
-
OutcomeAnalytics,
|
|
140
|
-
// credits catalog
|
|
141
|
-
Credit,
|
|
142
|
-
CreditDetail,
|
|
143
|
-
CreateCreditInput,
|
|
144
|
-
UpdateCreditInput,
|
|
145
|
-
ModelBundleEntry,
|
|
146
|
-
// outcomes catalog
|
|
147
|
-
Outcome,
|
|
148
|
-
OutcomeDetail,
|
|
149
|
-
OutcomeStepInput,
|
|
150
|
-
CreateOutcomeInput,
|
|
151
|
-
UpdateOutcomeInput,
|
|
152
|
-
// composites catalog
|
|
153
|
-
Composite,
|
|
154
|
-
CompositeCatalogueRef,
|
|
155
|
-
CreateCompositeInput,
|
|
156
|
-
// units catalog
|
|
157
|
-
Unit,
|
|
158
|
-
UnitDetail,
|
|
159
|
-
UnitCustomerRow,
|
|
160
|
-
UnitTier,
|
|
161
|
-
UnitTierInput,
|
|
162
|
-
UnitPricingType,
|
|
163
|
-
CreateUnitInput,
|
|
164
|
-
FlatUnitInput,
|
|
165
|
-
TieredUnitInput,
|
|
166
|
-
UpdateUnitInput,
|
|
167
|
-
// webhooks
|
|
168
|
-
Webhook,
|
|
169
|
-
UpsertWebhookInput,
|
|
170
|
-
ThresholdKind,
|
|
171
145
|
// portal
|
|
172
146
|
PortalTokenInput,
|
|
173
147
|
PortalToken,
|
|
@@ -1,31 +1,24 @@
|
|
|
1
1
|
import { activeQuery, filterByActive } from "../filters.js";
|
|
2
2
|
import type { Transport } from "../http.js";
|
|
3
|
-
import type { Composite, CreateCompositeInput } from "../types.js";
|
|
3
|
+
import type { Composite, CreateCompositeInput, UpdateCompositeInput } from "../types.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* The `composites` resource —
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* The `composites` resource — several credits / outcomes / units grouped under
|
|
7
|
+
* one tag (`refId`) and billed as ONE thing per completed occurrence.
|
|
8
|
+
* `/api/v1/composites`.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* on the signal itself (see `signals.credit()` / `.outcome()` / `.unit()`).
|
|
13
|
-
* • Closing an occurrence — the moment it is billed — is
|
|
14
|
-
* `signals.completeComposite()`.
|
|
10
|
+
* Creating a composite charges nobody: it only bills once a plan sells it as
|
|
11
|
+
* a `PRICING_METRIC` entitlement (`plans.create()` with its `compositeId`).
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* it (`quantity × price`, frozen into the purchase at buy time).
|
|
13
|
+
* There is no `delete()` — the API never deletes catalogue items. Retire a
|
|
14
|
+
* composite with `archive()` (new signals stop resolving to it; its history is
|
|
15
|
+
* kept); `unarchive()` brings it back.
|
|
20
16
|
*/
|
|
21
17
|
export class Composites {
|
|
22
18
|
constructor(private readonly transport: Transport) {}
|
|
23
19
|
|
|
24
|
-
/** List
|
|
25
|
-
*
|
|
26
|
-
* keep every row they already own.
|
|
27
|
-
* `{ active: false }` returns only archived ones; omitting it returns
|
|
28
|
-
* both. See `activeQuery` — the API itself only supports `true`. */
|
|
20
|
+
/** List them. `{ active: true }` returns only active ones, `{ active: false }`
|
|
21
|
+
* only archived ones; omit it for both. */
|
|
29
22
|
async list(params: { active?: boolean } = {}): Promise<Composite[]> {
|
|
30
23
|
const res = await this.transport.request<{ composites: Composite[] }>({
|
|
31
24
|
method: "GET",
|
|
@@ -35,13 +28,55 @@ export class Composites {
|
|
|
35
28
|
return filterByActive(res.composites, params.active);
|
|
36
29
|
}
|
|
37
30
|
|
|
38
|
-
/**
|
|
39
|
-
async
|
|
31
|
+
/** One composite's configuration. */
|
|
32
|
+
async get(id: string): Promise<Composite> {
|
|
40
33
|
const res = await this.transport.request<{ composite: Composite }>({
|
|
34
|
+
method: "GET",
|
|
35
|
+
path: `/api/v1/composites/${encodeURIComponent(id)}`,
|
|
36
|
+
});
|
|
37
|
+
return res.composite;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Create one. It starts active. Not retried on failure — a retry could
|
|
41
|
+
* create a second row. */
|
|
42
|
+
async create(input: CreateCompositeInput): Promise<Composite> {
|
|
43
|
+
const res = await this.transport.request<{ created: true; composite: Composite }>({
|
|
41
44
|
method: "POST",
|
|
42
45
|
path: "/api/v1/composites",
|
|
43
46
|
body: input,
|
|
44
47
|
});
|
|
45
48
|
return res.composite;
|
|
46
49
|
}
|
|
50
|
+
|
|
51
|
+
/** PARTIAL update: only the fields you send change. `entitlements`, when
|
|
52
|
+
* sent, is the complete new set. Renaming `refId` cuts live traffic over at
|
|
53
|
+
* once — ship the new tag to your callers first. */
|
|
54
|
+
async update(id: string, input: UpdateCompositeInput): Promise<Composite> {
|
|
55
|
+
const res = await this.transport.request<{ updated: true; composite: Composite }>({
|
|
56
|
+
method: "PATCH",
|
|
57
|
+
path: `/api/v1/composites/${encodeURIComponent(id)}`,
|
|
58
|
+
body: input,
|
|
59
|
+
});
|
|
60
|
+
return res.composite;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** Retire it (`isActive` → false). Nothing is deleted. Safe to retry. */
|
|
64
|
+
async archive(id: string): Promise<Composite> {
|
|
65
|
+
const res = await this.transport.request<{ archived: true; composite: Composite }>({
|
|
66
|
+
method: "POST",
|
|
67
|
+
path: `/api/v1/composites/${encodeURIComponent(id)}/archive`,
|
|
68
|
+
retry: true,
|
|
69
|
+
});
|
|
70
|
+
return res.composite;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Bring an archived one back (`isActive` → true). Safe to retry. */
|
|
74
|
+
async unarchive(id: string): Promise<Composite> {
|
|
75
|
+
const res = await this.transport.request<{ unarchived: true; composite: Composite }>({
|
|
76
|
+
method: "POST",
|
|
77
|
+
path: `/api/v1/composites/${encodeURIComponent(id)}/unarchive`,
|
|
78
|
+
retry: true,
|
|
79
|
+
});
|
|
80
|
+
return res.composite;
|
|
81
|
+
}
|
|
47
82
|
}
|
package/src/resources/credits.ts
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { activeQuery, filterByActive } from "../filters.js";
|
|
2
2
|
import type { Transport } from "../http.js";
|
|
3
|
-
import type {
|
|
4
|
-
CreateCreditInput,
|
|
5
|
-
Credit,
|
|
6
|
-
CreditDetail,
|
|
7
|
-
UpdateCreditInput,
|
|
8
|
-
} from "../types.js";
|
|
3
|
+
import type { CreateCreditInput, Credit, UpdateCreditInput } from "../types.js";
|
|
9
4
|
|
|
10
5
|
/**
|
|
11
|
-
* The `credits` resource —
|
|
12
|
-
*
|
|
13
|
-
*
|
|
6
|
+
* The `credits` resource — the token-metered credits your product draws down
|
|
7
|
+
* by `agentKey`. `/api/v1/credits`.
|
|
8
|
+
*
|
|
9
|
+
* Pricing is never typed in: `create()` takes a `modelBundle` (one line per
|
|
10
|
+
* model, each with `averageTokensPerLLMCall` and its input / output / cache /
|
|
11
|
+
* cacheWrite split) and the server works out `basePrice` and
|
|
12
|
+
* `pricePerCredit` from the models' live prices.
|
|
13
|
+
*
|
|
14
|
+
* There is no `delete()` — the API never deletes catalogue items. Retire a
|
|
15
|
+
* credit with `archive()`; `unarchive()` brings it back unchanged.
|
|
14
16
|
*/
|
|
15
17
|
export class Credits {
|
|
16
18
|
constructor(private readonly transport: Transport) {}
|
|
17
19
|
|
|
18
|
-
/** List
|
|
19
|
-
*
|
|
20
|
-
* both. See `activeQuery` — the API itself only supports `true`. */
|
|
20
|
+
/** List them. `{ active: true }` returns only active ones, `{ active: false }`
|
|
21
|
+
* only archived ones; omit it for both. */
|
|
21
22
|
async list(params: { active?: boolean } = {}): Promise<Credit[]> {
|
|
22
23
|
const res = await this.transport.request<{ credits: Credit[] }>({
|
|
23
24
|
method: "GET",
|
|
@@ -27,28 +28,30 @@ export class Credits {
|
|
|
27
28
|
return filterByActive(res.credits, params.active);
|
|
28
29
|
}
|
|
29
30
|
|
|
30
|
-
/**
|
|
31
|
-
async
|
|
31
|
+
/** One credit's configuration. */
|
|
32
|
+
async get(id: string): Promise<Credit> {
|
|
32
33
|
const res = await this.transport.request<{ credit: Credit }>({
|
|
33
|
-
method: "
|
|
34
|
-
path:
|
|
35
|
-
body: input,
|
|
34
|
+
method: "GET",
|
|
35
|
+
path: `/api/v1/credits/${encodeURIComponent(id)}`,
|
|
36
36
|
});
|
|
37
37
|
return res.credit;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
/**
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
/** Create one. It starts active. Not retried on failure — a retry could
|
|
41
|
+
* create a second row. */
|
|
42
|
+
async create(input: CreateCreditInput): Promise<Credit> {
|
|
43
|
+
const res = await this.transport.request<{ created: true; credit: Credit }>({
|
|
44
|
+
method: "POST",
|
|
45
|
+
path: "/api/v1/credits",
|
|
46
|
+
body: input,
|
|
45
47
|
});
|
|
46
48
|
return res.credit;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
/**
|
|
51
|
+
/** PARTIAL update: only the fields you send change. Send `modelBundle` to
|
|
52
|
+
* re-price; leave it out to keep the stored base price. */
|
|
50
53
|
async update(id: string, input: UpdateCreditInput): Promise<Credit> {
|
|
51
|
-
const res = await this.transport.request<{ credit: Credit }>({
|
|
54
|
+
const res = await this.transport.request<{ updated: true; credit: Credit }>({
|
|
52
55
|
method: "PATCH",
|
|
53
56
|
path: `/api/v1/credits/${encodeURIComponent(id)}`,
|
|
54
57
|
body: input,
|
|
@@ -56,21 +59,23 @@ export class Credits {
|
|
|
56
59
|
return res.credit;
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
/**
|
|
60
|
-
async
|
|
61
|
-
const res = await this.transport.request<{ credit: Credit }>({
|
|
62
|
-
method: "
|
|
63
|
-
path: `/api/v1/credits/${encodeURIComponent(id)}`,
|
|
64
|
-
|
|
62
|
+
/** Retire it (`isActive` → false). Nothing is deleted. Safe to retry. */
|
|
63
|
+
async archive(id: string): Promise<Credit> {
|
|
64
|
+
const res = await this.transport.request<{ archived: true; credit: Credit }>({
|
|
65
|
+
method: "POST",
|
|
66
|
+
path: `/api/v1/credits/${encodeURIComponent(id)}/archive`,
|
|
67
|
+
retry: true,
|
|
65
68
|
});
|
|
66
69
|
return res.credit;
|
|
67
70
|
}
|
|
68
71
|
|
|
69
|
-
/**
|
|
70
|
-
async
|
|
71
|
-
await this.transport.request({
|
|
72
|
-
method: "
|
|
73
|
-
path: `/api/v1/credits/${encodeURIComponent(id)}`,
|
|
72
|
+
/** Bring an archived one back (`isActive` → true). Safe to retry. */
|
|
73
|
+
async unarchive(id: string): Promise<Credit> {
|
|
74
|
+
const res = await this.transport.request<{ unarchived: true; credit: Credit }>({
|
|
75
|
+
method: "POST",
|
|
76
|
+
path: `/api/v1/credits/${encodeURIComponent(id)}/unarchive`,
|
|
77
|
+
retry: true,
|
|
74
78
|
});
|
|
79
|
+
return res.credit;
|
|
75
80
|
}
|
|
76
81
|
}
|