@listeningkit/treg 0.1.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.
@@ -0,0 +1,80 @@
1
+ /* eslint-disable */
2
+ /**
3
+ * Generated utilities for implementing server-side Convex query and mutation functions.
4
+ *
5
+ * THIS CODE IS AUTOMATICALLY GENERATED.
6
+ *
7
+ * To regenerate, run `npx convex dev`.
8
+ * @module
9
+ */
10
+ import { actionGeneric, httpActionGeneric, queryGeneric, mutationGeneric, internalActionGeneric, internalMutationGeneric, internalQueryGeneric, } from "convex/server";
11
+ /**
12
+ * Define a query in this Convex app's public API.
13
+ *
14
+ * This function will be allowed to read your Convex database and will be accessible from the client.
15
+ *
16
+ * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
17
+ * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
18
+ */
19
+ export const query = queryGeneric;
20
+ /**
21
+ * Define a query that is only accessible from other Convex functions (but not from the client).
22
+ *
23
+ * This function will be allowed to read from your Convex database. It will not be accessible from the client.
24
+ *
25
+ * @param func - The query function. It receives a {@link QueryCtx} as its first argument.
26
+ * @returns The wrapped query. Include this as an `export` to name it and make it accessible.
27
+ */
28
+ export const internalQuery = internalQueryGeneric;
29
+ /**
30
+ * Define a mutation in this Convex app's public API.
31
+ *
32
+ * This function will be allowed to modify your Convex database and will be accessible from the client.
33
+ *
34
+ * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
35
+ * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
36
+ */
37
+ export const mutation = mutationGeneric;
38
+ /**
39
+ * Define a mutation that is only accessible from other Convex functions (but not from the client).
40
+ *
41
+ * This function will be allowed to modify your Convex database. It will not be accessible from the client.
42
+ *
43
+ * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.
44
+ * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.
45
+ */
46
+ export const internalMutation = internalMutationGeneric;
47
+ /**
48
+ * Define an action in this Convex app's public API.
49
+ *
50
+ * An action is a function which can execute any JavaScript code, including non-deterministic
51
+ * code and code with side-effects, like calling third-party services.
52
+ * They can be run in Convex's JavaScript environment or in Node.js using the "use node" directive.
53
+ * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.
54
+ *
55
+ * @param func - The action. It receives an {@link ActionCtx} as its first argument.
56
+ * @returns The wrapped action. Include this as an `export` to name it and make it accessible.
57
+ */
58
+ export const action = actionGeneric;
59
+ /**
60
+ * Define an action that is only accessible from other Convex functions (but not from the client).
61
+ *
62
+ * @param func - The function. It receives an {@link ActionCtx} as its first argument.
63
+ * @returns The wrapped function. Include this as an `export` to name it and make it accessible.
64
+ */
65
+ export const internalAction = internalActionGeneric;
66
+ /**
67
+ * Define an HTTP action.
68
+ *
69
+ * The wrapped function will be used to respond to HTTP requests received
70
+ * by a Convex deployment if the requests matches the path and method where
71
+ * this action is routed. Be sure to route your httpAction in `convex/http.js`.
72
+ *
73
+ * @param func - The function. It receives an {@link ActionCtx} as its first argument
74
+ * and a Fetch API `Request` object as its second.
75
+ * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.
76
+ */
77
+ export const httpAction = httpActionGeneric;
78
+ export const env = globalThis
79
+ .process.env;
80
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../_generated/server.ts"],"names":[],"mappings":"AAAA,oBAAoB;AACpB;;;;;;;GAOG;AAaH,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,eAAe,CAAC;AAgBvB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,KAAK,GAAsC,YAAY,CAAC;AAErE;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,oBAAoB,CAAC;AAEvB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAyC,eAAe,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,uBAAuB,CAAC;AAE1B;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,MAAM,GAAuC,aAAa,CAAC;AAExE;;;;;GAKG;AACH,MAAM,CAAC,MAAM,cAAc,GACzB,qBAAqB,CAAC;AAExB;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,UAAU,GAAsB,iBAAiB,CAAC;AAC/D,MAAM,CAAC,MAAM,GAAG,GAAS,UAAmD;KACzE,OAAO,CAAC,GAAG,CAAC"}
@@ -0,0 +1,112 @@
1
+ import type { GenericActionCtx, GenericQueryCtx, FunctionReference } from "convex/server";
2
+ import { buildCallUrl, callFailureReason, TREG_DEFAULT_BASE_URL, type CatalogCall } from "./lib/treg.js";
3
+ /**
4
+ * Interface representing the internal API exposed by the Treg Convex component.
5
+ */
6
+ export type TregComponentApi = {
7
+ treg: {
8
+ call: FunctionReference<"action", "internal", {
9
+ owner: string;
10
+ endpoint: string;
11
+ params?: Record<string, string | number | boolean>;
12
+ maxCostUsd?: number;
13
+ }, any>;
14
+ getCalls: FunctionReference<"query", "internal", {
15
+ ownerHash: string;
16
+ limit?: number;
17
+ }, Array<{
18
+ _id: string;
19
+ _creationTime: number;
20
+ callId: string;
21
+ ownerHash: string;
22
+ endpoint: string;
23
+ costMicro: number;
24
+ servedVia?: string;
25
+ at: number;
26
+ }>>;
27
+ };
28
+ };
29
+ export interface TregCallArgs {
30
+ /**
31
+ * The owner identifier from your host application (e.g. user id, team id).
32
+ * Automatically hashed into a privacy-preserving attribution tag before leaving your backend.
33
+ */
34
+ owner: string;
35
+ /**
36
+ * Catalog endpoint identifier on treg.to (e.g. "spyfu.google.domain.competitors").
37
+ */
38
+ endpoint: string;
39
+ /**
40
+ * Flat key-value parameters passed to the catalog endpoint query string.
41
+ */
42
+ params?: Record<string, string | number | boolean>;
43
+ /**
44
+ * Optional spend ceiling for this call in USD (defaults to $0.05).
45
+ * Treg refuses the call upfront (HTTP 402, charged nothing) if estimated cost exceeds this limit.
46
+ */
47
+ maxCostUsd?: number;
48
+ }
49
+ export interface TregCallRecord {
50
+ _id: string;
51
+ _creationTime: number;
52
+ callId: string;
53
+ ownerHash: string;
54
+ endpoint: string;
55
+ costMicro: number;
56
+ servedVia?: string;
57
+ at: number;
58
+ }
59
+ /**
60
+ * Computes a 16-character SHA-256 hex hash for customer attribution tags.
61
+ */
62
+ export declare function hashOwner(owner: string): Promise<string>;
63
+ /**
64
+ * Client wrapper for the Treg Convex component.
65
+ *
66
+ * Provides a type-safe, ergonomic API for invoking tools in the treg.to catalog
67
+ * and reading spent call receipts.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * import { Treg } from "@superdesign/convex-treg";
72
+ * import { components } from "./_generated/api";
73
+ *
74
+ * export const treg = new Treg(components.treg);
75
+ *
76
+ * // Inside a Convex action:
77
+ * export const findCompetitors = action({
78
+ * args: { domain: v.string() },
79
+ * handler: async (ctx, args) => {
80
+ * const user = await auth.getUser(ctx);
81
+ * return await treg.call(ctx, {
82
+ * owner: user._id,
83
+ * endpoint: "spyfu.google.domain.competitors",
84
+ * params: { domain: args.domain, pageSize: 5 },
85
+ * maxCostUsd: 0.05,
86
+ * });
87
+ * },
88
+ * });
89
+ * ```
90
+ */
91
+ export declare class Treg {
92
+ readonly component: TregComponentApi | any;
93
+ constructor(component: TregComponentApi | any);
94
+ /**
95
+ * Proxies a tool call to https://treg.to with automatic spend guardrails,
96
+ * fresh idempotency key, and hashed customer attribution.
97
+ */
98
+ call<T = unknown>(ctx: {
99
+ runAction: GenericActionCtx<any>["runAction"];
100
+ }, args: TregCallArgs): Promise<T>;
101
+ /**
102
+ * Retrieves logged spend receipts for a specific owner from the component's internal database.
103
+ */
104
+ getCalls(ctx: {
105
+ runQuery: GenericQueryCtx<any>["runQuery"];
106
+ }, args: {
107
+ owner: string;
108
+ limit?: number;
109
+ }): Promise<TregCallRecord[]>;
110
+ }
111
+ export { buildCallUrl, callFailureReason, TREG_DEFAULT_BASE_URL, type CatalogCall };
112
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC1F,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAEzG;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE;QACJ,IAAI,EAAE,iBAAiB,CACrB,QAAQ,EACR,UAAU,EACV;YACE,KAAK,EAAE,MAAM,CAAC;YACd,QAAQ,EAAE,MAAM,CAAC;YACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;YACnD,UAAU,CAAC,EAAE,MAAM,CAAC;SACrB,EACD,GAAG,CACJ,CAAC;QACF,QAAQ,EAAE,iBAAiB,CACzB,OAAO,EACP,UAAU,EACV;YACE,SAAS,EAAE,MAAM,CAAC;YAClB,KAAK,CAAC,EAAE,MAAM,CAAC;SAChB,EACD,KAAK,CAAC;YACJ,GAAG,EAAE,MAAM,CAAC;YACZ,aAAa,EAAE,MAAM,CAAC;YACtB,MAAM,EAAE,MAAM,CAAC;YACf,SAAS,EAAE,MAAM,CAAC;YAClB,QAAQ,EAAE,MAAM,CAAC;YACjB,SAAS,EAAE,MAAM,CAAC;YAClB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,EAAE,EAAE,MAAM,CAAC;SACZ,CAAC,CACH,CAAC;KACH,CAAC;CACH,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACnD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,IAAI;aACa,SAAS,EAAE,gBAAgB,GAAG,GAAG;gBAAjC,SAAS,EAAE,gBAAgB,GAAG,GAAG;IAE7D;;;OAGG;IACG,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,GAAG,EAAE;QAAE,SAAS,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,CAAA;KAAE,EACtD,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,CAAC,CAAC;IAIb;;OAEG;IACG,QAAQ,CACZ,GAAG,EAAE;QAAE,QAAQ,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,CAAA;KAAE,EACnD,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GACtC,OAAO,CAAC,cAAc,EAAE,CAAC;CAO7B;AAED,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,KAAK,WAAW,EAAE,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,64 @@
1
+ import { buildCallUrl, callFailureReason, TREG_DEFAULT_BASE_URL } from "./lib/treg.js";
2
+ /**
3
+ * Computes a 16-character SHA-256 hex hash for customer attribution tags.
4
+ */
5
+ export async function hashOwner(owner) {
6
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(owner));
7
+ return [...new Uint8Array(digest)]
8
+ .map((byte) => byte.toString(16).padStart(2, "0"))
9
+ .join("")
10
+ .slice(0, 16);
11
+ }
12
+ /**
13
+ * Client wrapper for the Treg Convex component.
14
+ *
15
+ * Provides a type-safe, ergonomic API for invoking tools in the treg.to catalog
16
+ * and reading spent call receipts.
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import { Treg } from "@superdesign/convex-treg";
21
+ * import { components } from "./_generated/api";
22
+ *
23
+ * export const treg = new Treg(components.treg);
24
+ *
25
+ * // Inside a Convex action:
26
+ * export const findCompetitors = action({
27
+ * args: { domain: v.string() },
28
+ * handler: async (ctx, args) => {
29
+ * const user = await auth.getUser(ctx);
30
+ * return await treg.call(ctx, {
31
+ * owner: user._id,
32
+ * endpoint: "spyfu.google.domain.competitors",
33
+ * params: { domain: args.domain, pageSize: 5 },
34
+ * maxCostUsd: 0.05,
35
+ * });
36
+ * },
37
+ * });
38
+ * ```
39
+ */
40
+ export class Treg {
41
+ component;
42
+ constructor(component) {
43
+ this.component = component;
44
+ }
45
+ /**
46
+ * Proxies a tool call to https://treg.to with automatic spend guardrails,
47
+ * fresh idempotency key, and hashed customer attribution.
48
+ */
49
+ async call(ctx, args) {
50
+ return (await ctx.runAction(this.component.treg.call, args));
51
+ }
52
+ /**
53
+ * Retrieves logged spend receipts for a specific owner from the component's internal database.
54
+ */
55
+ async getCalls(ctx, args) {
56
+ const ownerHash = await hashOwner(args.owner);
57
+ return (await ctx.runQuery(this.component.treg.getCalls, {
58
+ ownerHash,
59
+ limit: args.limit,
60
+ }));
61
+ }
62
+ }
63
+ export { buildCallUrl, callFailureReason, TREG_DEFAULT_BASE_URL };
64
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAoB,MAAM,eAAe,CAAC;AAuEzG;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,KAAa;IAC3C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACtF,OAAO,CAAC,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;SAC/B,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;SACjD,IAAI,CAAC,EAAE,CAAC;SACR,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,OAAO,IAAI;IACa;IAA5B,YAA4B,SAAiC;QAAjC,cAAS,GAAT,SAAS,CAAwB;IAAG,CAAC;IAEjE;;;OAGG;IACH,KAAK,CAAC,IAAI,CACR,GAAsD,EACtD,IAAkB;QAElB,OAAO,CAAC,MAAM,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAM,CAAC;IACpE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ,CACZ,GAAmD,EACnD,IAAuC;QAEvC,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9C,OAAO,CAAC,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE;YACvD,SAAS;YACT,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAqB,CAAC;IAC1B,CAAC;CACF;AAED,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,qBAAqB,EAAoB,CAAC"}
@@ -0,0 +1,6 @@
1
+ declare const _default: import("convex/server").ComponentDefinition<any, {
2
+ readonly TREG_TOKEN: import("convex/values").VString<string | undefined, "optional">;
3
+ readonly TREG_BASE_URL: import("convex/values").VString<string | undefined, "optional">;
4
+ }>;
5
+ export default _default;
6
+ //# sourceMappingURL=convex.config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex.config.d.ts","sourceRoot":"","sources":["../convex.config.ts"],"names":[],"mappings":";;;;AAGA,wBAOG"}
@@ -0,0 +1,11 @@
1
+ import { defineComponent } from "convex/server";
2
+ import { v } from "convex/values";
3
+ export default defineComponent("treg", {
4
+ env: {
5
+ // Optional so install never blocks codegen: the action throws
6
+ // "not switched on yet" when no token is bound, same as FIRECRAWL_API_KEY.
7
+ TREG_TOKEN: v.optional(v.string()),
8
+ TREG_BASE_URL: v.optional(v.string()),
9
+ },
10
+ });
11
+ //# sourceMappingURL=convex.config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"convex.config.js","sourceRoot":"","sources":["../convex.config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,eAAe,eAAe,CAAC,MAAM,EAAE;IACrC,GAAG,EAAE;QACH,8DAA8D;QAC9D,2EAA2E;QAC3E,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QAClC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACtC;CACF,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Pure helpers for calling treg.to. No network here — the action owns fetch
3
+ * so tests can inject a fake. Protocol: GET https://treg.to/call/<id>?params
4
+ * with an X-Treg-Token header; the upstream answer relays verbatim.
5
+ */
6
+ export declare const TREG_DEFAULT_BASE_URL = "https://treg.to";
7
+ /** A catalog call: endpoint id plus flat string/number/boolean params. */
8
+ export type CatalogCall = {
9
+ endpoint: string;
10
+ params?: Record<string, string | number | boolean>;
11
+ };
12
+ export declare function buildCallUrl(baseUrl: string, call: CatalogCall): string;
13
+ /**
14
+ * Plain-words reason for a failed catalog call. Never includes a response
15
+ * body or a key: 402 means the team balance is out (top up, then retry),
16
+ * 503 means treg's own provider account is out (retry in a minute or pick
17
+ * the next provider row), anything else stays generic.
18
+ */
19
+ export declare function callFailureReason(status: number): string;
20
+ //# sourceMappingURL=treg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"treg.d.ts","sourceRoot":"","sources":["../../lib/treg.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,qBAAqB,oBAAoB,CAAC;AAEvD,0EAA0E;AAC1E,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;CACpD,CAAC;AAEF,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,MAAM,CAKvE;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAMxD"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Pure helpers for calling treg.to. No network here — the action owns fetch
3
+ * so tests can inject a fake. Protocol: GET https://treg.to/call/<id>?params
4
+ * with an X-Treg-Token header; the upstream answer relays verbatim.
5
+ */
6
+ export const TREG_DEFAULT_BASE_URL = "https://treg.to";
7
+ export function buildCallUrl(baseUrl, call) {
8
+ const query = Object.entries(call.params ?? {})
9
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
10
+ .join("&");
11
+ return `${baseUrl.replace(/\/$/, "")}/call/${call.endpoint}${query ? `?${query}` : ""}`;
12
+ }
13
+ /**
14
+ * Plain-words reason for a failed catalog call. Never includes a response
15
+ * body or a key: 402 means the team balance is out (top up, then retry),
16
+ * 503 means treg's own provider account is out (retry in a minute or pick
17
+ * the next provider row), anything else stays generic.
18
+ */
19
+ export function callFailureReason(status) {
20
+ if (status === 401 || status === 403)
21
+ return "Treg refused the team token";
22
+ if (status === 402)
23
+ return "The team balance is out — top up, then try again";
24
+ if (status === 429)
25
+ return "Treg is busy, try again in a minute";
26
+ if (status === 503)
27
+ return "That provider is out right now, try again in a minute";
28
+ return `Treg answered HTTP ${status}`;
29
+ }
30
+ //# sourceMappingURL=treg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"treg.js","sourceRoot":"","sources":["../../lib/treg.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,iBAAiB,CAAC;AAQvD,MAAM,UAAU,YAAY,CAAC,OAAe,EAAE,IAAiB;IAC7D,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,EAAE,CAAC;SAC5C,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,GAAG,kBAAkB,CAAC,GAAG,CAAC,IAAI,kBAAkB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;SACxF,IAAI,CAAC,GAAG,CAAC,CAAC;IACb,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AAC1F,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,6BAA6B,CAAC;IAC3E,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,kDAAkD,CAAC;IAC9E,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,qCAAqC,CAAC;IACjE,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,uDAAuD,CAAC;IACnF,OAAO,sBAAsB,MAAM,EAAE,CAAC;AACxC,CAAC"}
@@ -0,0 +1,22 @@
1
+ declare const _default: import("convex/server").SchemaDefinition<{
2
+ calls: import("convex/server").TableDefinition<import("convex/values").VObject<{
3
+ servedVia?: string | undefined;
4
+ at: number;
5
+ callId: string;
6
+ ownerHash: string;
7
+ endpoint: string;
8
+ costMicro: number;
9
+ }, {
10
+ callId: import("convex/values").VString<string, "required">;
11
+ ownerHash: import("convex/values").VString<string, "required">;
12
+ endpoint: import("convex/values").VString<string, "required">;
13
+ costMicro: import("convex/values").VFloat64<number, "required">;
14
+ servedVia: import("convex/values").VString<string | undefined, "optional">;
15
+ at: import("convex/values").VFloat64<number, "required">;
16
+ }, "required", "at" | "callId" | "ownerHash" | "endpoint" | "costMicro" | "servedVia">, {
17
+ by_owner: ["ownerHash", "_creationTime"];
18
+ by_call: ["callId", "_creationTime"];
19
+ }, {}, {}>;
20
+ }, true>;
21
+ export default _default;
22
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../schema.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAGA,wBAcG"}
package/dist/schema.js ADDED
@@ -0,0 +1,18 @@
1
+ import { defineSchema, defineTable } from "convex/server";
2
+ import { v } from "convex/values";
3
+ export default defineSchema({
4
+ // Spend receipts, one row per catalog call. Cost comes only from the
5
+ // X-Treg-Cost-Micro / X-Treg-Call-Id response headers, never from a
6
+ // provider body. Owner is a SHA-256 hash, never the raw identifier.
7
+ calls: defineTable({
8
+ callId: v.string(),
9
+ ownerHash: v.string(),
10
+ endpoint: v.string(),
11
+ costMicro: v.number(),
12
+ servedVia: v.optional(v.string()),
13
+ at: v.number(),
14
+ })
15
+ .index("by_owner", ["ownerHash"])
16
+ .index("by_call", ["callId"]),
17
+ });
18
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC,eAAe,YAAY,CAAC;IAC1B,qEAAqE;IACrE,oEAAoE;IACpE,oEAAoE;IACpE,KAAK,EAAE,WAAW,CAAC;QACjB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;QACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,SAAS,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;QACjC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;KACf,CAAC;SACC,KAAK,CAAC,UAAU,EAAE,CAAC,WAAW,CAAC,CAAC;SAChC,KAAK,CAAC,SAAS,EAAE,CAAC,QAAQ,CAAC,CAAC;CAChC,CAAC,CAAC"}
package/dist/treg.d.ts ADDED
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Call any catalogued treg endpoint by id. Auth lives in the app: the
3
+ * caller passes its already-verified owner string (components have no
4
+ * ctx.auth), which is hashed before it leaves as the ledger tag. The token
5
+ * and base URL come only from the component's declared env, never from
6
+ * arguments; every call carries a spend ceiling (X-Treg-Route-Max-Cost
7
+ * refuses instead of overspending) and a fresh idempotency key so a retry
8
+ * is never billed twice. The upstream answer relays verbatim.
9
+ */
10
+ export declare const call: import("convex/server").RegisteredAction<"public", {
11
+ params?: Record<string, string | number | boolean> | undefined;
12
+ maxCostUsd?: number | undefined;
13
+ endpoint: string;
14
+ owner: string;
15
+ }, Promise<unknown>>;
16
+ /**
17
+ * Record a spend receipt into the calls table.
18
+ */
19
+ export declare const recordReceipt: import("convex/server").RegisteredMutation<"internal", {
20
+ servedVia?: string | undefined;
21
+ at: number;
22
+ callId: string;
23
+ ownerHash: string;
24
+ endpoint: string;
25
+ costMicro: number;
26
+ }, Promise<void>>;
27
+ /**
28
+ * Read spend receipts for a specific owner hash.
29
+ */
30
+ export declare const getCalls: import("convex/server").RegisteredQuery<"public", {
31
+ limit?: number | undefined;
32
+ ownerHash: string;
33
+ }, Promise<{
34
+ _id: import("convex/values").GenericId<"calls">;
35
+ _creationTime: number;
36
+ servedVia?: string | undefined;
37
+ at: number;
38
+ callId: string;
39
+ ownerHash: string;
40
+ endpoint: string;
41
+ costMicro: number;
42
+ }[]>>;
43
+ //# sourceMappingURL=treg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"treg.d.ts","sourceRoot":"","sources":["../treg.ts"],"names":[],"mappings":"AAMA;;;;;;;;GAQG;AACH,eAAO,MAAM,IAAI;;;;;oBAgDf,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;iBAYxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;KAyBnB,CAAC"}
package/dist/treg.js ADDED
@@ -0,0 +1,109 @@
1
+ import { ConvexError } from "convex/values";
2
+ import { v } from "convex/values";
3
+ import { action, internalMutation, query, env } from "./_generated/server.js";
4
+ import { internal } from "./_generated/api.js";
5
+ import { buildCallUrl, callFailureReason, TREG_DEFAULT_BASE_URL } from "./lib/treg.js";
6
+ /**
7
+ * Call any catalogued treg endpoint by id. Auth lives in the app: the
8
+ * caller passes its already-verified owner string (components have no
9
+ * ctx.auth), which is hashed before it leaves as the ledger tag. The token
10
+ * and base URL come only from the component's declared env, never from
11
+ * arguments; every call carries a spend ceiling (X-Treg-Route-Max-Cost
12
+ * refuses instead of overspending) and a fresh idempotency key so a retry
13
+ * is never billed twice. The upstream answer relays verbatim.
14
+ */
15
+ export const call = action({
16
+ args: {
17
+ owner: v.string(),
18
+ endpoint: v.string(),
19
+ params: v.optional(v.record(v.string(), v.union(v.string(), v.number(), v.boolean()))),
20
+ maxCostUsd: v.optional(v.number()),
21
+ },
22
+ returns: v.any(),
23
+ handler: async (ctx, args) => {
24
+ const token = env.TREG_TOKEN;
25
+ if (!token)
26
+ throw new ConvexError("Treg is not switched on yet.");
27
+ const baseUrl = env.TREG_BASE_URL ?? TREG_DEFAULT_BASE_URL;
28
+ const url = buildCallUrl(baseUrl, { endpoint: args.endpoint, params: args.params });
29
+ const ownerHash = await sha256Hex(args.owner);
30
+ const res = await fetch(url, {
31
+ headers: {
32
+ "X-Treg-Token": token,
33
+ "X-Treg-Route-Max-Cost": String(args.maxCostUsd ?? 0.05),
34
+ "Idempotency-Key": crypto.randomUUID(),
35
+ "X-Treg-Meta": `customer=${ownerHash}`,
36
+ },
37
+ signal: AbortSignal.timeout(55_000),
38
+ });
39
+ if (!res.ok)
40
+ throw new ConvexError(callFailureReason(res.status));
41
+ const json = (await res.json());
42
+ // Record spend receipt in internal calls table if headers are present
43
+ const callId = res.headers.get("x-treg-call-id");
44
+ const costMicroStr = res.headers.get("x-treg-cost-micro");
45
+ const servedVia = res.headers.get("x-treg-served-via") ?? undefined;
46
+ if (callId && costMicroStr) {
47
+ const costMicro = parseInt(costMicroStr, 10);
48
+ if (!Number.isNaN(costMicro)) {
49
+ await ctx.runMutation(internal.treg.recordReceipt, {
50
+ callId,
51
+ ownerHash,
52
+ endpoint: args.endpoint,
53
+ costMicro,
54
+ servedVia,
55
+ at: Date.now(),
56
+ });
57
+ }
58
+ }
59
+ return json;
60
+ },
61
+ });
62
+ /**
63
+ * Record a spend receipt into the calls table.
64
+ */
65
+ export const recordReceipt = internalMutation({
66
+ args: {
67
+ callId: v.string(),
68
+ ownerHash: v.string(),
69
+ endpoint: v.string(),
70
+ costMicro: v.number(),
71
+ servedVia: v.optional(v.string()),
72
+ at: v.number(),
73
+ },
74
+ handler: async (ctx, args) => {
75
+ await ctx.db.insert("calls", args);
76
+ },
77
+ });
78
+ /**
79
+ * Read spend receipts for a specific owner hash.
80
+ */
81
+ export const getCalls = query({
82
+ args: {
83
+ ownerHash: v.string(),
84
+ limit: v.optional(v.number()),
85
+ },
86
+ returns: v.array(v.object({
87
+ _id: v.id("calls"),
88
+ _creationTime: v.number(),
89
+ callId: v.string(),
90
+ ownerHash: v.string(),
91
+ endpoint: v.string(),
92
+ costMicro: v.number(),
93
+ servedVia: v.optional(v.string()),
94
+ at: v.number(),
95
+ })),
96
+ handler: async (ctx, args) => {
97
+ const limit = args.limit ?? 50;
98
+ return await ctx.db
99
+ .query("calls")
100
+ .withIndex("by_owner", (q) => q.eq("ownerHash", args.ownerHash))
101
+ .order("desc")
102
+ .take(limit);
103
+ },
104
+ });
105
+ async function sha256Hex(value) {
106
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(value));
107
+ return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 16);
108
+ }
109
+ //# sourceMappingURL=treg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"treg.js","sourceRoot":"","sources":["../treg.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ {"root":["../convex.config.ts","../schema.ts","../treg.ts","../client.ts","../lib/treg.ts"],"version":"5.6.3"}
package/lib/treg.ts ADDED
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Pure helpers for calling treg.to. No network here — the action owns fetch
3
+ * so tests can inject a fake. Protocol: GET https://treg.to/call/<id>?params
4
+ * with an X-Treg-Token header; the upstream answer relays verbatim.
5
+ */
6
+
7
+ export const TREG_DEFAULT_BASE_URL = "https://treg.to";
8
+
9
+ /** A catalog call: endpoint id plus flat string/number/boolean params. */
10
+ export type CatalogCall = {
11
+ endpoint: string;
12
+ params?: Record<string, string | number | boolean>;
13
+ };
14
+
15
+ export function buildCallUrl(baseUrl: string, call: CatalogCall): string {
16
+ const query = Object.entries(call.params ?? {})
17
+ .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
18
+ .join("&");
19
+ return `${baseUrl.replace(/\/$/, "")}/call/${call.endpoint}${query ? `?${query}` : ""}`;
20
+ }
21
+
22
+ /**
23
+ * Plain-words reason for a failed catalog call. Never includes a response
24
+ * body or a key: 402 means the team balance is out (top up, then retry),
25
+ * 503 means treg's own provider account is out (retry in a minute or pick
26
+ * the next provider row), anything else stays generic.
27
+ */
28
+ export function callFailureReason(status: number): string {
29
+ if (status === 401 || status === 403) return "Treg refused the team token";
30
+ if (status === 402) return "The team balance is out — top up, then try again";
31
+ if (status === 429) return "Treg is busy, try again in a minute";
32
+ if (status === 503) return "That provider is out right now, try again in a minute";
33
+ return `Treg answered HTTP ${status}`;
34
+ }