@clocknext/sdk 0.8.0 → 0.10.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 +77 -15
- package/dist/index.cjs +166 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +387 -61
- package/dist/index.d.ts +387 -61
- package/dist/index.js +166 -28
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -119,7 +119,8 @@ var Flusher = class {
|
|
|
119
119
|
try {
|
|
120
120
|
await this.transport.request({
|
|
121
121
|
method: "POST",
|
|
122
|
-
|
|
122
|
+
// Per-item, not per-Flusher: one route per meter. See `QueuedSignal`.
|
|
123
|
+
path: item.path,
|
|
123
124
|
body: item.body,
|
|
124
125
|
// Best-effort metering opts into retries on transient failures.
|
|
125
126
|
retry: true
|
|
@@ -227,7 +228,7 @@ function sleep(ms) {
|
|
|
227
228
|
}
|
|
228
229
|
|
|
229
230
|
// src/http.ts
|
|
230
|
-
var SDK_VERSION = "0.
|
|
231
|
+
var SDK_VERSION = "0.10.0" ;
|
|
231
232
|
var Transport = class {
|
|
232
233
|
constructor(cfg) {
|
|
233
234
|
this.cfg = cfg;
|
|
@@ -322,6 +323,36 @@ var Transport = class {
|
|
|
322
323
|
}
|
|
323
324
|
};
|
|
324
325
|
|
|
326
|
+
// src/resources/composites.ts
|
|
327
|
+
var Composites = class {
|
|
328
|
+
constructor(transport) {
|
|
329
|
+
this.transport = transport;
|
|
330
|
+
}
|
|
331
|
+
transport;
|
|
332
|
+
/** List composites. Pass `{ active: true }` for only the ones a signal can
|
|
333
|
+
* still be tagged with — the default list INCLUDES archived ones, which
|
|
334
|
+
* keep every row they already own. */
|
|
335
|
+
async list(params = {}) {
|
|
336
|
+
const res = await this.transport.request({
|
|
337
|
+
method: "GET",
|
|
338
|
+
path: "/api/v1/composites",
|
|
339
|
+
query: {
|
|
340
|
+
active: params.active === void 0 ? void 0 : String(params.active)
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
return res.composites;
|
|
344
|
+
}
|
|
345
|
+
/** Create a composite. `POST /api/v1/composites`. */
|
|
346
|
+
async create(input) {
|
|
347
|
+
const res = await this.transport.request({
|
|
348
|
+
method: "POST",
|
|
349
|
+
path: "/api/v1/composites",
|
|
350
|
+
body: input
|
|
351
|
+
});
|
|
352
|
+
return res.composite;
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
|
|
325
356
|
// src/resources/credits.ts
|
|
326
357
|
var Credits = class {
|
|
327
358
|
constructor(transport) {
|
|
@@ -504,7 +535,7 @@ var Customers = class {
|
|
|
504
535
|
});
|
|
505
536
|
return res.members;
|
|
506
537
|
}
|
|
507
|
-
/** Add a member to a customer. Duplicate `
|
|
538
|
+
/** Add a member to a customer. Duplicate `email` → ConflictError. */
|
|
508
539
|
async addMember(id, input) {
|
|
509
540
|
const res = await this.transport.request({
|
|
510
541
|
method: "POST",
|
|
@@ -513,7 +544,7 @@ var Customers = class {
|
|
|
513
544
|
});
|
|
514
545
|
return res.member;
|
|
515
546
|
}
|
|
516
|
-
/** Update a member's profile
|
|
547
|
+
/** Update a member's profile. Duplicate `email` → ConflictError. `PATCH …/:id/members/:memberId`. */
|
|
517
548
|
async updateMember(id, memberId, input) {
|
|
518
549
|
const res = await this.transport.request({
|
|
519
550
|
method: "PATCH",
|
|
@@ -522,12 +553,13 @@ var Customers = class {
|
|
|
522
553
|
});
|
|
523
554
|
return res.member;
|
|
524
555
|
}
|
|
525
|
-
/** Remove a member from a customer. `DELETE …/:id/members/:memberId`. */
|
|
556
|
+
/** Remove a member from a customer, returning who was removed. `DELETE …/:id/members/:memberId`. */
|
|
526
557
|
async removeMember(id, memberId) {
|
|
527
|
-
await this.transport.request({
|
|
558
|
+
const res = await this.transport.request({
|
|
528
559
|
method: "DELETE",
|
|
529
560
|
path: `/api/v1/customers/${encodeURIComponent(id)}/members/${encodeURIComponent(memberId)}`
|
|
530
561
|
});
|
|
562
|
+
return res.member;
|
|
531
563
|
}
|
|
532
564
|
// --- Wallet & billing config --------------------------------------------
|
|
533
565
|
/** The customer's wallet balance + transactions. `GET …/:id/wallet`. */
|
|
@@ -853,18 +885,28 @@ function newKey() {
|
|
|
853
885
|
}
|
|
854
886
|
|
|
855
887
|
// src/resources/signals.ts
|
|
888
|
+
var SIGNAL_PATHS = {
|
|
889
|
+
credit: "/api/v1/signal/credit",
|
|
890
|
+
outcome: "/api/v1/signal/outcome",
|
|
891
|
+
wallet: "/api/v1/signal/wallet"
|
|
892
|
+
};
|
|
893
|
+
var UNIT_PATH = "/api/v1/signal/unit";
|
|
894
|
+
var COMPOSITE_COMPLETE_PATH = "/api/v1/signal/composite/complete";
|
|
856
895
|
function signalToWire(signal) {
|
|
896
|
+
const t = signal.tokens;
|
|
857
897
|
const body = {
|
|
858
898
|
customerId: signal.customerId,
|
|
859
|
-
|
|
860
|
-
|
|
899
|
+
// `usage` is required on all three model-priced kinds. input/output are
|
|
900
|
+
// always sent; `cacheTokens` only when provided (the server defaults 0).
|
|
901
|
+
usage: {
|
|
902
|
+
model: signal.model,
|
|
903
|
+
inputTokens: t.input,
|
|
904
|
+
outputTokens: t.output,
|
|
905
|
+
...t.cache != null ? { cacheTokens: t.cache } : {}
|
|
906
|
+
}
|
|
861
907
|
};
|
|
862
908
|
if (signal.member) body.member = signal.member;
|
|
863
909
|
if (signal.custom) body.custom = signal.custom;
|
|
864
|
-
const t = signal.tokens;
|
|
865
|
-
body.inputTokens = t.input;
|
|
866
|
-
body.outputTokens = t.output;
|
|
867
|
-
if (t.cache != null) body.cacheTokens = t.cache;
|
|
868
910
|
if (signal.type === "credit" || signal.type === "outcome") {
|
|
869
911
|
body.agentKey = signal.agentKey;
|
|
870
912
|
}
|
|
@@ -872,12 +914,18 @@ function signalToWire(signal) {
|
|
|
872
914
|
body.runId = signal.runId;
|
|
873
915
|
if (signal.complete != null) body.complete = signal.complete;
|
|
874
916
|
}
|
|
875
|
-
if (signal.type !== "wallet" && signal.
|
|
876
|
-
body[signal.
|
|
917
|
+
if (signal.type !== "wallet" && signal.composite) {
|
|
918
|
+
body.composite = { [signal.composite.ref]: signal.composite.value };
|
|
877
919
|
}
|
|
878
920
|
if (signal.idempotencyKey) body.idempotencyKey = signal.idempotencyKey;
|
|
879
921
|
return body;
|
|
880
922
|
}
|
|
923
|
+
function handles(res) {
|
|
924
|
+
return {
|
|
925
|
+
...res.messageId != null ? { messageId: res.messageId } : {},
|
|
926
|
+
...res.rawId != null ? { rawId: res.rawId } : {}
|
|
927
|
+
};
|
|
928
|
+
}
|
|
881
929
|
var Signals = class {
|
|
882
930
|
constructor(transport, flusher, cfg) {
|
|
883
931
|
this.transport = transport;
|
|
@@ -888,7 +936,8 @@ var Signals = class {
|
|
|
888
936
|
flusher;
|
|
889
937
|
cfg;
|
|
890
938
|
/**
|
|
891
|
-
* Record one usage signal
|
|
939
|
+
* Record one usage signal, to the route its `type` selects:
|
|
940
|
+
* `POST /api/v1/signal/credit` | `/signal/outcome` | `/signal/wallet`.
|
|
892
941
|
*
|
|
893
942
|
* Returns as soon as the signal is buffered (async mode) or the request has
|
|
894
943
|
* been answered (`{ wait: true }` / sync mode). See {@link TrackResult}: a
|
|
@@ -897,14 +946,15 @@ var Signals = class {
|
|
|
897
946
|
async track(signal, opts = {}) {
|
|
898
947
|
const body = signalToWire(signal);
|
|
899
948
|
if (body.idempotencyKey == null) body.idempotencyKey = newKey();
|
|
949
|
+
const path = SIGNAL_PATHS[signal.type];
|
|
900
950
|
const sendNow = opts.wait || this.cfg.mode === "sync";
|
|
901
951
|
if (!sendNow) {
|
|
902
|
-
this.flusher.enqueue({ body, signal });
|
|
952
|
+
this.flusher.enqueue({ path, body, signal });
|
|
903
953
|
return { queued: true };
|
|
904
954
|
}
|
|
905
955
|
const res = await this.transport.request({
|
|
906
956
|
method: "POST",
|
|
907
|
-
path
|
|
957
|
+
path,
|
|
908
958
|
body,
|
|
909
959
|
// Best-effort metering opts into retries on transient failures.
|
|
910
960
|
retry: true
|
|
@@ -912,39 +962,52 @@ var Signals = class {
|
|
|
912
962
|
return {
|
|
913
963
|
queued: res.queued === true,
|
|
914
964
|
usageLog: res.usageLog ?? null,
|
|
915
|
-
...res
|
|
965
|
+
...handles(res)
|
|
916
966
|
};
|
|
917
967
|
}
|
|
918
968
|
/**
|
|
919
969
|
* Validate + price a signal WITHOUT recording it (server `dryRun`). Returns
|
|
920
970
|
* the projected usage log so an integration can preflight before sending real
|
|
921
971
|
* traffic — no inbox row, wallet / outcome writes, or webhooks fire.
|
|
972
|
+
*
|
|
973
|
+
* Only the model-priced kinds support this. A dry run is not offered for unit
|
|
974
|
+
* reports or composite completions, and the server REJECTS the flag on those
|
|
975
|
+
* routes rather than accepting and ignoring it — which is why there is no
|
|
976
|
+
* dry-run path through `unit()` or `completeComposite()`.
|
|
922
977
|
*/
|
|
923
978
|
async verify(signal) {
|
|
924
979
|
const body = signalToWire(signal);
|
|
925
980
|
body.dryRun = true;
|
|
926
981
|
const res = await this.transport.request({
|
|
927
982
|
method: "POST",
|
|
928
|
-
path:
|
|
983
|
+
path: SIGNAL_PATHS[signal.type],
|
|
929
984
|
body,
|
|
930
985
|
// A dry run writes nothing, so it is always safe to retry.
|
|
931
986
|
retry: true
|
|
932
987
|
});
|
|
933
988
|
return res.usageLog ?? null;
|
|
934
989
|
}
|
|
935
|
-
/** Meter a named credit. */
|
|
990
|
+
/** Meter a named credit. `POST /api/v1/signal/credit`. */
|
|
936
991
|
credit(input, opts) {
|
|
937
992
|
return this.track({ ...input, type: "credit" }, opts);
|
|
938
993
|
}
|
|
939
|
-
/** Debit the customer's wallet at model cost. */
|
|
994
|
+
/** Debit the customer's wallet at model cost. `POST /api/v1/signal/wallet`. */
|
|
940
995
|
wallet(input, opts) {
|
|
941
996
|
return this.track({ ...input, type: "wallet" }, opts);
|
|
942
997
|
}
|
|
943
|
-
/** Advance one step of an outcome workflow. */
|
|
998
|
+
/** Advance one step of an outcome workflow. `POST /api/v1/signal/outcome`. */
|
|
944
999
|
outcome(input, opts) {
|
|
945
1000
|
return this.track({ ...input, type: "outcome" }, opts);
|
|
946
1001
|
}
|
|
947
|
-
/**
|
|
1002
|
+
/**
|
|
1003
|
+
* Recent usage logs + totals for a customer. `GET /api/v1/usage`.
|
|
1004
|
+
*
|
|
1005
|
+
* Still the legacy path, deliberately. The `/api/v1/signal/*` family is
|
|
1006
|
+
* POST-only — it replaced how usage is INGESTED, not how it is read — so
|
|
1007
|
+
* `GET /api/v1/usage` has no successor and is not deprecated. It is also the
|
|
1008
|
+
* only way to prove a signal landed, which matters more now that ingestion
|
|
1009
|
+
* answers `202` for everything.
|
|
1010
|
+
*/
|
|
948
1011
|
async list(params) {
|
|
949
1012
|
const res = await this.transport.request({
|
|
950
1013
|
method: "GET",
|
|
@@ -956,22 +1019,94 @@ var Signals = class {
|
|
|
956
1019
|
/**
|
|
957
1020
|
* Record one unit-metered event (a fixed-price meter with no tokens),
|
|
958
1021
|
* addressed to a catalog unit by its `agentKey`. Sent immediately — unit
|
|
959
|
-
* events are not buffered. `POST /api/v1/
|
|
1022
|
+
* events are not buffered. `POST /api/v1/signal/unit`.
|
|
1023
|
+
*
|
|
1024
|
+
* Two things changed with the move off `POST /api/v1/units`, and both show up
|
|
1025
|
+
* in the return type. The route is now durable (the report lands in an inbox
|
|
1026
|
+
* before it is priced, so a failure parks as Pending and can be retried
|
|
1027
|
+
* instead of losing a billable event) and it answers `202` when it queues —
|
|
1028
|
+
* so this resolves {@link UnitTrackResult}, not a bare `{ unitUsage }`.
|
|
1029
|
+
* Branch on `queued`.
|
|
960
1030
|
*/
|
|
961
1031
|
async unit(input) {
|
|
962
|
-
|
|
1032
|
+
const res = await this.transport.request({
|
|
963
1033
|
method: "POST",
|
|
964
|
-
path:
|
|
1034
|
+
path: UNIT_PATH,
|
|
965
1035
|
body: {
|
|
966
1036
|
customerId: input.customerId,
|
|
967
1037
|
agentKey: input.agentKey,
|
|
968
|
-
...input.
|
|
1038
|
+
...input.quantity != null ? { quantity: input.quantity } : {},
|
|
1039
|
+
...input.member ? { member: input.member } : {},
|
|
1040
|
+
...input.composite ? { composite: { [input.composite.ref]: input.composite.value } } : {},
|
|
1041
|
+
// Assigned HERE, before the send, so the SAME key rides every
|
|
1042
|
+
// transport-level retry below. Without it a single 500 followed by a
|
|
1043
|
+
// success is TWO un-keyed reports, and the server dedups on the key —
|
|
1044
|
+
// so it bills the allowance down twice, up to `retry.maxAttempts`
|
|
1045
|
+
// times. The route gained idempotency precisely to stop that; not
|
|
1046
|
+
// using it by default would leave the double-charge reachable from the
|
|
1047
|
+
// client instead of the server.
|
|
1048
|
+
idempotencyKey: input.idempotencyKey ?? newKey()
|
|
969
1049
|
},
|
|
970
1050
|
// Best-effort metering opts into retries on transient failures.
|
|
971
1051
|
retry: true
|
|
972
1052
|
});
|
|
1053
|
+
return {
|
|
1054
|
+
queued: res.queued === true,
|
|
1055
|
+
unitUsage: res.unitUsage ?? null,
|
|
1056
|
+
...handles(res)
|
|
1057
|
+
};
|
|
973
1058
|
}
|
|
974
|
-
/**
|
|
1059
|
+
/**
|
|
1060
|
+
* Close ONE occurrence of a composite — a distinct tag value.
|
|
1061
|
+
* `POST /api/v1/signal/composite/complete`.
|
|
1062
|
+
*
|
|
1063
|
+
* A composite bundles several credits / outcomes / units and is billed as one
|
|
1064
|
+
* thing. The occurrence opens on the first tagged signal and costs nothing;
|
|
1065
|
+
* this call is the moment it is billed. Only your code knows when the
|
|
1066
|
+
* workflow ended, so this is a DECLARATION, not a hint: tagged signals may
|
|
1067
|
+
* arrive in any order and the occurrence stays open and unbilled until you
|
|
1068
|
+
* say otherwise. Put it on the line before your own `return`.
|
|
1069
|
+
*
|
|
1070
|
+
* An OUTCOME never needs this — its own `complete: true` closes its run and
|
|
1071
|
+
* the matching occurrence together. Credits and units have no "done" signal
|
|
1072
|
+
* of their own, so this is theirs.
|
|
1073
|
+
*
|
|
1074
|
+
* Closing twice is safe: the second call answers `ALREADY_COMPLETED` and
|
|
1075
|
+
* cannot bill again. Calling it EARLY is also safe — before every entitlement
|
|
1076
|
+
* the composite is restricted to has reported, or while an ADVANCE pool is
|
|
1077
|
+
* full — the server parks the ping and applies it when the blocker clears,
|
|
1078
|
+
* rather than failing it.
|
|
1079
|
+
*/
|
|
1080
|
+
async completeComposite(input) {
|
|
1081
|
+
const res = await this.transport.request({
|
|
1082
|
+
method: "POST",
|
|
1083
|
+
path: COMPOSITE_COMPLETE_PATH,
|
|
1084
|
+
body: {
|
|
1085
|
+
customerId: input.customerId,
|
|
1086
|
+
composite: { [input.composite.ref]: input.composite.value },
|
|
1087
|
+
// Auto-assigned like the others. A retry here could not double-BILL
|
|
1088
|
+
// even without it — closing a closed occurrence is a server-side
|
|
1089
|
+
// no-op — but that safety comes from occurrence-level dedup, not from
|
|
1090
|
+
// idempotency, and an un-keyed replay still writes a redundant inbox
|
|
1091
|
+
// row. Keying it collapses the retry into the one attempt it is.
|
|
1092
|
+
idempotencyKey: input.idempotencyKey ?? newKey()
|
|
1093
|
+
},
|
|
1094
|
+
retry: true
|
|
1095
|
+
});
|
|
1096
|
+
return {
|
|
1097
|
+
queued: res.queued === true,
|
|
1098
|
+
...res.status != null ? { status: res.status } : {},
|
|
1099
|
+
...res.composite !== void 0 ? { composite: res.composite } : {},
|
|
1100
|
+
...res.value !== void 0 ? { value: res.value } : {},
|
|
1101
|
+
...handles(res)
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
/**
|
|
1105
|
+
* A customer's unit balances + recent unit events. `GET /api/v1/units`.
|
|
1106
|
+
*
|
|
1107
|
+
* Legacy path for the same reason as {@link list}: the signal family is
|
|
1108
|
+
* POST-only, so the read side has no successor and is not deprecated.
|
|
1109
|
+
*/
|
|
975
1110
|
unitUsage(params) {
|
|
976
1111
|
return this.transport.request({
|
|
977
1112
|
method: "GET",
|
|
@@ -1115,6 +1250,8 @@ var ClockNext = class {
|
|
|
1115
1250
|
payments;
|
|
1116
1251
|
/** Manage the credit-type catalog. */
|
|
1117
1252
|
credits;
|
|
1253
|
+
/** Manage the composite catalog — bundles billed as one thing. */
|
|
1254
|
+
composites;
|
|
1118
1255
|
/** Manage the outcome-type catalog. */
|
|
1119
1256
|
outcomes;
|
|
1120
1257
|
/** Manage the unit-type catalog. */
|
|
@@ -1143,6 +1280,7 @@ var ClockNext = class {
|
|
|
1143
1280
|
this.invoices = new Invoices(transport);
|
|
1144
1281
|
this.payments = new Payments(transport);
|
|
1145
1282
|
this.credits = new Credits(transport);
|
|
1283
|
+
this.composites = new Composites(transport);
|
|
1146
1284
|
this.outcomes = new Outcomes(transport);
|
|
1147
1285
|
this.units = new Units(transport);
|
|
1148
1286
|
this.webhooks = new Webhooks(transport);
|