@clocknext/sdk 0.7.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 +93 -18
- package/dist/index.cjs +174 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +464 -66
- package/dist/index.d.ts +464 -66
- package/dist/index.js +174 -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;
|
|
@@ -887,56 +935,79 @@ var Signals = class {
|
|
|
887
935
|
transport;
|
|
888
936
|
flusher;
|
|
889
937
|
cfg;
|
|
938
|
+
/**
|
|
939
|
+
* Record one usage signal, to the route its `type` selects:
|
|
940
|
+
* `POST /api/v1/signal/credit` | `/signal/outcome` | `/signal/wallet`.
|
|
941
|
+
*
|
|
942
|
+
* Returns as soon as the signal is buffered (async mode) or the request has
|
|
943
|
+
* been answered (`{ wait: true }` / sync mode). See {@link TrackResult}: a
|
|
944
|
+
* resolved promise means "accepted", which is not the same as "billed".
|
|
945
|
+
*/
|
|
890
946
|
async track(signal, opts = {}) {
|
|
891
947
|
const body = signalToWire(signal);
|
|
892
948
|
if (body.idempotencyKey == null) body.idempotencyKey = newKey();
|
|
949
|
+
const path = SIGNAL_PATHS[signal.type];
|
|
893
950
|
const sendNow = opts.wait || this.cfg.mode === "sync";
|
|
894
951
|
if (!sendNow) {
|
|
895
|
-
this.flusher.enqueue({ body, signal });
|
|
952
|
+
this.flusher.enqueue({ path, body, signal });
|
|
896
953
|
return { queued: true };
|
|
897
954
|
}
|
|
898
955
|
const res = await this.transport.request({
|
|
899
956
|
method: "POST",
|
|
900
|
-
path
|
|
957
|
+
path,
|
|
901
958
|
body,
|
|
902
959
|
// Best-effort metering opts into retries on transient failures.
|
|
903
960
|
retry: true
|
|
904
961
|
});
|
|
905
962
|
return {
|
|
906
|
-
queued:
|
|
907
|
-
usageLog: res.usageLog ?? null
|
|
963
|
+
queued: res.queued === true,
|
|
964
|
+
usageLog: res.usageLog ?? null,
|
|
965
|
+
...handles(res)
|
|
908
966
|
};
|
|
909
967
|
}
|
|
910
968
|
/**
|
|
911
969
|
* Validate + price a signal WITHOUT recording it (server `dryRun`). Returns
|
|
912
970
|
* the projected usage log so an integration can preflight before sending real
|
|
913
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()`.
|
|
914
977
|
*/
|
|
915
978
|
async verify(signal) {
|
|
916
979
|
const body = signalToWire(signal);
|
|
917
980
|
body.dryRun = true;
|
|
918
981
|
const res = await this.transport.request({
|
|
919
982
|
method: "POST",
|
|
920
|
-
path:
|
|
983
|
+
path: SIGNAL_PATHS[signal.type],
|
|
921
984
|
body,
|
|
922
985
|
// A dry run writes nothing, so it is always safe to retry.
|
|
923
986
|
retry: true
|
|
924
987
|
});
|
|
925
988
|
return res.usageLog ?? null;
|
|
926
989
|
}
|
|
927
|
-
/** Meter a named credit. */
|
|
990
|
+
/** Meter a named credit. `POST /api/v1/signal/credit`. */
|
|
928
991
|
credit(input, opts) {
|
|
929
992
|
return this.track({ ...input, type: "credit" }, opts);
|
|
930
993
|
}
|
|
931
|
-
/** Debit the customer's wallet at model cost. */
|
|
994
|
+
/** Debit the customer's wallet at model cost. `POST /api/v1/signal/wallet`. */
|
|
932
995
|
wallet(input, opts) {
|
|
933
996
|
return this.track({ ...input, type: "wallet" }, opts);
|
|
934
997
|
}
|
|
935
|
-
/** Advance one step of an outcome workflow. */
|
|
998
|
+
/** Advance one step of an outcome workflow. `POST /api/v1/signal/outcome`. */
|
|
936
999
|
outcome(input, opts) {
|
|
937
1000
|
return this.track({ ...input, type: "outcome" }, opts);
|
|
938
1001
|
}
|
|
939
|
-
/**
|
|
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
|
+
*/
|
|
940
1011
|
async list(params) {
|
|
941
1012
|
const res = await this.transport.request({
|
|
942
1013
|
method: "GET",
|
|
@@ -948,22 +1019,94 @@ var Signals = class {
|
|
|
948
1019
|
/**
|
|
949
1020
|
* Record one unit-metered event (a fixed-price meter with no tokens),
|
|
950
1021
|
* addressed to a catalog unit by its `agentKey`. Sent immediately — unit
|
|
951
|
-
* 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`.
|
|
952
1030
|
*/
|
|
953
1031
|
async unit(input) {
|
|
954
|
-
|
|
1032
|
+
const res = await this.transport.request({
|
|
955
1033
|
method: "POST",
|
|
956
|
-
path:
|
|
1034
|
+
path: UNIT_PATH,
|
|
957
1035
|
body: {
|
|
958
1036
|
customerId: input.customerId,
|
|
959
1037
|
agentKey: input.agentKey,
|
|
960
|
-
...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()
|
|
961
1049
|
},
|
|
962
1050
|
// Best-effort metering opts into retries on transient failures.
|
|
963
1051
|
retry: true
|
|
964
1052
|
});
|
|
1053
|
+
return {
|
|
1054
|
+
queued: res.queued === true,
|
|
1055
|
+
unitUsage: res.unitUsage ?? null,
|
|
1056
|
+
...handles(res)
|
|
1057
|
+
};
|
|
965
1058
|
}
|
|
966
|
-
/**
|
|
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
|
+
*/
|
|
967
1110
|
unitUsage(params) {
|
|
968
1111
|
return this.transport.request({
|
|
969
1112
|
method: "GET",
|
|
@@ -1107,6 +1250,8 @@ var ClockNext = class {
|
|
|
1107
1250
|
payments;
|
|
1108
1251
|
/** Manage the credit-type catalog. */
|
|
1109
1252
|
credits;
|
|
1253
|
+
/** Manage the composite catalog — bundles billed as one thing. */
|
|
1254
|
+
composites;
|
|
1110
1255
|
/** Manage the outcome-type catalog. */
|
|
1111
1256
|
outcomes;
|
|
1112
1257
|
/** Manage the unit-type catalog. */
|
|
@@ -1135,6 +1280,7 @@ var ClockNext = class {
|
|
|
1135
1280
|
this.invoices = new Invoices(transport);
|
|
1136
1281
|
this.payments = new Payments(transport);
|
|
1137
1282
|
this.credits = new Credits(transport);
|
|
1283
|
+
this.composites = new Composites(transport);
|
|
1138
1284
|
this.outcomes = new Outcomes(transport);
|
|
1139
1285
|
this.units = new Units(transport);
|
|
1140
1286
|
this.webhooks = new Webhooks(transport);
|