tally-sdk 0.1.0 → 0.2.1

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.
Files changed (46) hide show
  1. package/.github/workflows/publish.yml +38 -0
  2. package/dist/src/hook.d.ts +6 -0
  3. package/dist/src/hook.d.ts.map +1 -0
  4. package/dist/src/hook.js +37 -0
  5. package/dist/src/hook.js.map +1 -0
  6. package/dist/src/index.d.ts +6 -0
  7. package/dist/src/index.d.ts.map +1 -0
  8. package/dist/src/index.js +22 -0
  9. package/dist/src/index.js.map +1 -0
  10. package/dist/src/middleware/core.d.ts +15 -0
  11. package/dist/src/middleware/core.d.ts.map +1 -0
  12. package/dist/src/middleware/core.js +20 -0
  13. package/dist/src/middleware/core.js.map +1 -0
  14. package/dist/src/middleware/express.d.ts +4 -0
  15. package/dist/src/middleware/express.d.ts.map +1 -0
  16. package/dist/src/middleware/express.js +26 -0
  17. package/dist/src/middleware/express.js.map +1 -0
  18. package/dist/src/store.d.ts +14 -0
  19. package/dist/src/store.d.ts.map +1 -0
  20. package/dist/src/store.js +84 -0
  21. package/dist/src/store.js.map +1 -0
  22. package/dist/src/types.d.ts +36 -0
  23. package/dist/src/types.d.ts.map +1 -0
  24. package/dist/src/types.js +2 -0
  25. package/dist/src/types.js.map +1 -0
  26. package/package.json +19 -5
  27. package/src/index.ts +2 -0
  28. package/src/middleware/core.ts +38 -0
  29. package/src/middleware/express.ts +32 -0
  30. package/.opencode/commands/opsx-apply.md +0 -152
  31. package/.opencode/commands/opsx-archive.md +0 -157
  32. package/.opencode/commands/opsx-explore.md +0 -169
  33. package/.opencode/commands/opsx-propose.md +0 -104
  34. package/.opencode/commands/opsx-sync.md +0 -140
  35. package/.opencode/skills/openspec-apply-change/SKILL.md +0 -159
  36. package/.opencode/skills/openspec-archive-change/SKILL.md +0 -117
  37. package/.opencode/skills/openspec-explore/SKILL.md +0 -287
  38. package/.opencode/skills/openspec-propose/SKILL.md +0 -111
  39. package/.opencode/skills/openspec-sync-specs/SKILL.md +0 -147
  40. package/docs/design-hook-store.md +0 -122
  41. package/docs/plan-hook-store.md +0 -752
  42. package/test/hook.test.ts +0 -87
  43. package/test/index.test.ts +0 -36
  44. package/test/integration.test.ts +0 -44
  45. package/test/store.test.ts +0 -88
  46. package/test/types.test.ts +0 -30
@@ -0,0 +1,38 @@
1
+ name: Publish Package
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ id-token: write
11
+ contents: read
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v6
18
+
19
+ - uses: actions/setup-node@v6
20
+ with:
21
+ node-version: '24'
22
+ registry-url: 'https://registry.npmjs.org'
23
+ package-manager-cache: false
24
+
25
+ - name: Upgrade npm to v11 (OIDC-capable, runs build scripts)
26
+ run: npm install -g npm@11
27
+
28
+ - name: Install dependencies
29
+ run: npm ci
30
+
31
+ - name: Build
32
+ run: npm run build
33
+
34
+ - name: Test
35
+ run: npm test
36
+
37
+ - name: Publish (OIDC trusted publishing)
38
+ run: npm publish --access public
@@ -0,0 +1,6 @@
1
+ import type { Store } from "./types.js";
2
+ export interface HookOptions {
3
+ facilitator: string;
4
+ }
5
+ export declare function createTallyFetch(fetchFn: typeof fetch, store: Store, options?: HookOptions): typeof fetch;
6
+ //# sourceMappingURL=hook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../src/hook.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAkB,KAAK,EAAE,MAAM,YAAY,CAAC;AAExD,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAsBD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,KAAK,EACrB,KAAK,EAAE,KAAK,EACZ,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,KAAK,CAiBd"}
@@ -0,0 +1,37 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ function extractPayload(body, headers, options) {
3
+ const b = (body && typeof body === "object" ? body : {});
4
+ const now = Date.now();
5
+ return {
6
+ id: b.id ?? uuidv4(),
7
+ facilitator: options.facilitator,
8
+ requestId: b.requestId ?? headers.get("x-request-id") ?? "",
9
+ fromAddress: b.fromAddress ?? "",
10
+ toAddress: b.toAddress ?? headers.get("x-payment-address") ?? "",
11
+ amount: b.amount ?? headers.get("x-payment-amount") ?? "0",
12
+ asset: b.asset ?? headers.get("x-payment-asset") ?? "USDC",
13
+ chainId: b.chainId ?? "eip155:1",
14
+ txHash: b.txHash ?? "",
15
+ timestamp: b.timestamp ?? now,
16
+ memo: b.memo ?? undefined,
17
+ metadata: b.metadata ?? undefined,
18
+ };
19
+ }
20
+ export function createTallyFetch(fetchFn, store, options) {
21
+ return async (input, init) => {
22
+ const response = await fetchFn(input, init);
23
+ if (options && (response.headers.has("x-facilitator") || response.headers.has("x-request-id"))) {
24
+ try {
25
+ const cloned = response.clone();
26
+ const body = await cloned.json();
27
+ const payload = extractPayload(body, cloned.headers, options);
28
+ await store.insert(payload);
29
+ }
30
+ catch {
31
+ // swallow — never break the agent request
32
+ }
33
+ }
34
+ return response;
35
+ };
36
+ }
37
+ //# sourceMappingURL=hook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook.js","sourceRoot":"","sources":["../../src/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAOpC,SAAS,cAAc,CAAC,IAAa,EAAE,OAAgB,EAAE,OAAoB;IAC3E,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;IACpF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,OAAO;QACL,EAAE,EAAG,CAAC,CAAC,EAAa,IAAI,MAAM,EAAE;QAChC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,SAAS,EAAG,CAAC,CAAC,SAAoB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE;QACvE,WAAW,EAAG,CAAC,CAAC,WAAsB,IAAI,EAAE;QAC5C,SAAS,EAAG,CAAC,CAAC,SAAoB,IAAI,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,IAAI,EAAE;QAC5E,MAAM,EAAG,CAAC,CAAC,MAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,GAAG;QACtE,KAAK,EAAG,CAAC,CAAC,KAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,IAAI,MAAM;QACtE,OAAO,EAAG,CAAC,CAAC,OAAkB,IAAI,UAAU;QAC5C,MAAM,EAAG,CAAC,CAAC,MAAiB,IAAI,EAAE;QAClC,SAAS,EAAG,CAAC,CAAC,SAAoB,IAAI,GAAG;QACzC,IAAI,EAAG,CAAC,CAAC,IAAe,IAAI,SAAS;QACrC,QAAQ,EAAE,CAAC,CAAC,QAAmC,IAAI,SAAS;KAC7D,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,gBAAgB,CAC9B,OAAqB,EACrB,KAAY,EACZ,OAAqB;IAErB,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAC3B,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAE5C,IAAI,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC;YAC/F,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAChC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;gBACjC,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAC9D,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,0CAA0C;YAC5C,CAAC;QACH,CAAC;QAED,OAAO,QAAQ,CAAC;IAClB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { TallyClient, TallyConfig } from "./types.js";
2
+ export type { PaymentPayload, Store, TallyConfig, TallyClient } from "./types.js";
3
+ export { handleIncomingPayment } from "./middleware/core.js";
4
+ export type { IncomingPayment } from "./middleware/core.js";
5
+ export declare function createTally(config: TallyConfig): TallyClient;
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAS,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAElE,YAAY,EAAE,cAAc,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAClF,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,YAAY,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,wBAAgB,WAAW,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAsB5D"}
@@ -0,0 +1,22 @@
1
+ import { SqliteStore } from "./store.js";
2
+ import { createTallyFetch } from "./hook.js";
3
+ export { handleIncomingPayment } from "./middleware/core.js";
4
+ export function createTally(config) {
5
+ const store = config.store ?? new SqliteStore("./tally.db");
6
+ const originalFetch = globalThis.fetch;
7
+ const wrappedFetch = createTallyFetch(originalFetch.bind(globalThis), store, { facilitator: config.facilitator });
8
+ return {
9
+ fetch: wrappedFetch,
10
+ wrap() {
11
+ globalThis.fetch = wrappedFetch;
12
+ },
13
+ unwrap() {
14
+ globalThis.fetch = originalFetch;
15
+ },
16
+ store,
17
+ async close() {
18
+ await store.close();
19
+ },
20
+ };
21
+ }
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAI7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAG7D,MAAM,UAAU,WAAW,CAAC,MAAmB;IAC7C,MAAM,KAAK,GAAU,MAAM,CAAC,KAAK,IAAI,IAAI,WAAW,CAAC,YAAY,CAAC,CAAC;IACnE,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC;IACvC,MAAM,YAAY,GAAG,gBAAgB,CACnC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,EAC9B,KAAK,EACL,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CACpC,CAAC;IAEF,OAAO;QACL,KAAK,EAAE,YAAY;QACnB,IAAI;YACF,UAAU,CAAC,KAAK,GAAG,YAAY,CAAC;QAClC,CAAC;QACD,MAAM;YACJ,UAAU,CAAC,KAAK,GAAG,aAAa,CAAC;QACnC,CAAC;QACD,KAAK;QACL,KAAK,CAAC,KAAK;YACT,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC;QACtB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { PaymentPayload, Store } from "../types.js";
2
+ export interface IncomingPayment {
3
+ facilitator: string;
4
+ requestId?: string;
5
+ fromAddress?: string;
6
+ toAddress?: string;
7
+ amount?: string;
8
+ asset?: string;
9
+ chainId?: string;
10
+ txHash?: string;
11
+ memo?: string;
12
+ metadata?: Record<string, unknown>;
13
+ }
14
+ export declare function handleIncomingPayment(store: Store, payment: IncomingPayment): Promise<PaymentPayload>;
15
+ //# sourceMappingURL=core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../src/middleware/core.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,wBAAsB,qBAAqB,CACzC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,cAAc,CAAC,CAkBzB"}
@@ -0,0 +1,20 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ export async function handleIncomingPayment(store, payment) {
3
+ const payload = {
4
+ id: uuidv4(),
5
+ facilitator: payment.facilitator,
6
+ requestId: payment.requestId ?? "",
7
+ fromAddress: payment.fromAddress ?? "",
8
+ toAddress: payment.toAddress ?? "",
9
+ amount: payment.amount ?? "0",
10
+ asset: payment.asset ?? "USDC",
11
+ chainId: payment.chainId ?? "eip155:1",
12
+ txHash: payment.txHash ?? "",
13
+ timestamp: Date.now(),
14
+ memo: payment.memo,
15
+ metadata: payment.metadata,
16
+ };
17
+ await store.insert(payload);
18
+ return payload;
19
+ }
20
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core.js","sourceRoot":"","sources":["../../../src/middleware/core.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAgBpC,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,KAAY,EACZ,OAAwB;IAExB,MAAM,OAAO,GAAmB;QAC9B,EAAE,EAAE,MAAM,EAAE;QACZ,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE;QAClC,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,EAAE;QACtC,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE;QAClC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,GAAG;QAC7B,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,MAAM;QAC9B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,UAAU;QACtC,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,EAAE;QAC5B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,QAAQ,EAAE,OAAO,CAAC,QAAQ;KAC3B,CAAC;IAEF,MAAM,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5B,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Request, Response, NextFunction } from "express";
2
+ import type { Store } from "../types.js";
3
+ export declare function tallyMiddleware(store: Store, facilitator: string): (req: Request, res: Response, next: NextFunction) => void;
4
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../../src/middleware/express.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AAGzC,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,IACvD,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UA0BxD"}
@@ -0,0 +1,26 @@
1
+ import { handleIncomingPayment } from "./core.js";
2
+ export function tallyMiddleware(store, facilitator) {
3
+ return (req, res, next) => {
4
+ const originalJson = res.json.bind(res);
5
+ res.json = function (body) {
6
+ if (res.statusCode === 402) {
7
+ const payment = {
8
+ facilitator,
9
+ requestId: req.headers["x-request-id"] ?? undefined,
10
+ fromAddress: body?.fromAddress,
11
+ toAddress: body?.toAddress,
12
+ amount: body?.amount,
13
+ asset: body?.asset,
14
+ chainId: body?.chainId,
15
+ txHash: body?.txHash,
16
+ memo: body?.memo,
17
+ metadata: body?.metadata,
18
+ };
19
+ handleIncomingPayment(store, payment).catch(() => { });
20
+ }
21
+ return originalJson(body);
22
+ };
23
+ next();
24
+ };
25
+ }
26
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../../src/middleware/express.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAElD,MAAM,UAAU,eAAe,CAAC,KAAY,EAAE,WAAmB;IAC/D,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QACzD,MAAM,YAAY,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAExC,GAAG,CAAC,IAAI,GAAG,UAAU,IAAa;YAChC,IAAI,GAAG,CAAC,UAAU,KAAK,GAAG,EAAE,CAAC;gBAC3B,MAAM,OAAO,GAAG;oBACd,WAAW;oBACX,SAAS,EAAG,GAAG,CAAC,OAAO,CAAC,cAAc,CAAY,IAAI,SAAS;oBAC/D,WAAW,EAAG,IAAgC,EAAE,WAAiC;oBACjF,SAAS,EAAG,IAAgC,EAAE,SAA+B;oBAC7E,MAAM,EAAG,IAAgC,EAAE,MAA4B;oBACvE,KAAK,EAAG,IAAgC,EAAE,KAA2B;oBACrE,OAAO,EAAG,IAAgC,EAAE,OAA6B;oBACzE,MAAM,EAAG,IAAgC,EAAE,MAA4B;oBACvE,IAAI,EAAG,IAAgC,EAAE,IAA0B;oBACnE,QAAQ,EAAG,IAAgC,EAAE,QAA+C;iBAC7F,CAAC;gBAEF,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACxD,CAAC;YAED,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC;QAEF,IAAI,EAAE,CAAC;IACT,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { PaymentPayload, Store } from "./types.js";
2
+ export declare class SqliteStore implements Store {
3
+ private db;
4
+ constructor(path: string);
5
+ insert(payload: PaymentPayload): Promise<void>;
6
+ list(opts?: {
7
+ facilitator?: string;
8
+ limit?: number;
9
+ offset?: number;
10
+ }): Promise<PaymentPayload[]>;
11
+ get(id: string): Promise<PaymentPayload | null>;
12
+ close(): Promise<void>;
13
+ }
14
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExD,qBAAa,WAAY,YAAW,KAAK;IACvC,OAAO,CAAC,EAAE,CAAoB;gBAElB,IAAI,EAAE,MAAM;IAsBlB,MAAM,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB9C,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;IAoBjG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IAK/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
@@ -0,0 +1,84 @@
1
+ import Database from "better-sqlite3";
2
+ export class SqliteStore {
3
+ db;
4
+ constructor(path) {
5
+ this.db = new Database(path);
6
+ this.db.exec(`
7
+ CREATE TABLE IF NOT EXISTS payments (
8
+ id TEXT PRIMARY KEY,
9
+ facilitator TEXT NOT NULL,
10
+ request_id TEXT NOT NULL,
11
+ from_addr TEXT NOT NULL,
12
+ to_addr TEXT NOT NULL,
13
+ amount TEXT NOT NULL,
14
+ asset TEXT NOT NULL DEFAULT 'USDC',
15
+ chain_id TEXT NOT NULL,
16
+ tx_hash TEXT NOT NULL,
17
+ memo TEXT,
18
+ metadata TEXT,
19
+ created_at INTEGER NOT NULL
20
+ );
21
+ CREATE INDEX IF NOT EXISTS idx_payments_facilitator ON payments(facilitator);
22
+ CREATE INDEX IF NOT EXISTS idx_payments_created_at ON payments(created_at);
23
+ `);
24
+ }
25
+ async insert(payload) {
26
+ const stmt = this.db.prepare(`
27
+ INSERT OR IGNORE INTO payments
28
+ (id, facilitator, request_id, from_addr, to_addr, amount, asset, chain_id, tx_hash, memo, metadata, created_at)
29
+ VALUES
30
+ (@id, @facilitator, @requestId, @fromAddress, @toAddress, @amount, @asset, @chainId, @txHash, @memo, @metadata, @createdAt)
31
+ `);
32
+ stmt.run({
33
+ id: payload.id,
34
+ facilitator: payload.facilitator,
35
+ requestId: payload.requestId,
36
+ fromAddress: payload.fromAddress,
37
+ toAddress: payload.toAddress,
38
+ amount: payload.amount,
39
+ asset: payload.asset,
40
+ chainId: payload.chainId,
41
+ txHash: payload.txHash,
42
+ memo: payload.memo ?? null,
43
+ metadata: payload.metadata ? JSON.stringify(payload.metadata) : null,
44
+ createdAt: payload.timestamp,
45
+ });
46
+ }
47
+ async list(opts) {
48
+ const conditions = [];
49
+ const params = {};
50
+ if (opts?.facilitator) {
51
+ conditions.push("facilitator = @facilitator");
52
+ params.facilitator = opts.facilitator;
53
+ }
54
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
55
+ const limit = opts?.limit ?? 50;
56
+ const offset = opts?.offset ?? 0;
57
+ const rows = this.db.prepare(`SELECT * FROM payments ${where} ORDER BY created_at DESC LIMIT @limit OFFSET @offset`).all({ ...params, limit, offset });
58
+ return rows.map(rowToPayload);
59
+ }
60
+ async get(id) {
61
+ const row = this.db.prepare("SELECT * FROM payments WHERE id = ?").get(id);
62
+ return row ? rowToPayload(row) : null;
63
+ }
64
+ async close() {
65
+ this.db.close();
66
+ }
67
+ }
68
+ function rowToPayload(row) {
69
+ return {
70
+ id: row.id,
71
+ facilitator: row.facilitator,
72
+ requestId: row.request_id,
73
+ fromAddress: row.from_addr,
74
+ toAddress: row.to_addr,
75
+ amount: row.amount,
76
+ asset: row.asset,
77
+ chainId: row.chain_id,
78
+ txHash: row.tx_hash,
79
+ memo: row.memo ?? undefined,
80
+ metadata: row.metadata ? JSON.parse(row.metadata) : undefined,
81
+ timestamp: row.created_at,
82
+ };
83
+ }
84
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ export interface PaymentPayload {
2
+ id: string;
3
+ facilitator: string;
4
+ requestId: string;
5
+ fromAddress: string;
6
+ toAddress: string;
7
+ amount: string;
8
+ asset: string;
9
+ chainId: string;
10
+ txHash: string;
11
+ timestamp: number;
12
+ memo?: string;
13
+ metadata?: Record<string, unknown>;
14
+ }
15
+ export interface Store {
16
+ insert(payload: PaymentPayload): Promise<void>;
17
+ list(opts?: {
18
+ facilitator?: string;
19
+ limit?: number;
20
+ offset?: number;
21
+ }): Promise<PaymentPayload[]>;
22
+ get(id: string): Promise<PaymentPayload | null>;
23
+ close(): Promise<void>;
24
+ }
25
+ export interface TallyConfig {
26
+ facilitator: string;
27
+ store?: Store;
28
+ }
29
+ export interface TallyClient {
30
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
31
+ wrap(): void;
32
+ unwrap(): void;
33
+ store: Store;
34
+ close(): Promise<void>;
35
+ }
36
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC;AAED,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC9C,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAA;IACjG,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IAC/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,CAAA;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IACtE,IAAI,IAAI,IAAI,CAAA;IACZ,MAAM,IAAI,IAAI,CAAA;IACd,KAAK,EAAE,KAAK,CAAA;IACZ,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,14 +1,18 @@
1
1
  {
2
2
  "name": "tally-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Accounting, tax, and compliance layer for the agent economy",
5
5
  "type": "module",
6
- "main": "./dist/index.js",
7
- "types": "./dist/index.d.ts",
6
+ "main": "./dist/src/index.js",
7
+ "types": "./dist/src/index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "import": "./dist/index.js",
11
- "types": "./dist/index.d.ts"
10
+ "import": "./dist/src/index.js",
11
+ "types": "./dist/src/index.d.ts"
12
+ },
13
+ "./middleware/express": {
14
+ "import": "./dist/src/middleware/express.js",
15
+ "types": "./dist/src/middleware/express.d.ts"
12
16
  }
13
17
  },
14
18
  "scripts": {
@@ -23,13 +27,23 @@
23
27
  "crypto"
24
28
  ],
25
29
  "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/Chucks007/tally.git"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public",
36
+ "provenance": false
37
+ },
26
38
  "dependencies": {
27
39
  "better-sqlite3": "^11.0.0",
28
40
  "uuid": "^11.1.1"
29
41
  },
30
42
  "devDependencies": {
31
43
  "@types/better-sqlite3": "^7.6.0",
44
+ "@types/express": "^5.0.6",
32
45
  "@types/uuid": "^10.0.0",
46
+ "express": "^5.2.1",
33
47
  "typescript": "^5.5.0",
34
48
  "vitest": "^2.0.0"
35
49
  }
package/src/index.ts CHANGED
@@ -3,6 +3,8 @@ import { createTallyFetch } from "./hook.js";
3
3
  import type { Store, TallyClient, TallyConfig } from "./types.js";
4
4
 
5
5
  export type { PaymentPayload, Store, TallyConfig, TallyClient } from "./types.js";
6
+ export { handleIncomingPayment } from "./middleware/core.js";
7
+ export type { IncomingPayment } from "./middleware/core.js";
6
8
 
7
9
  export function createTally(config: TallyConfig): TallyClient {
8
10
  const store: Store = config.store ?? new SqliteStore("./tally.db");
@@ -0,0 +1,38 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ import type { PaymentPayload, Store } from "../types.js";
3
+
4
+ export interface IncomingPayment {
5
+ facilitator: string;
6
+ requestId?: string;
7
+ fromAddress?: string;
8
+ toAddress?: string;
9
+ amount?: string;
10
+ asset?: string;
11
+ chainId?: string;
12
+ txHash?: string;
13
+ memo?: string;
14
+ metadata?: Record<string, unknown>;
15
+ }
16
+
17
+ export async function handleIncomingPayment(
18
+ store: Store,
19
+ payment: IncomingPayment
20
+ ): Promise<PaymentPayload> {
21
+ const payload: PaymentPayload = {
22
+ id: uuidv4(),
23
+ facilitator: payment.facilitator,
24
+ requestId: payment.requestId ?? "",
25
+ fromAddress: payment.fromAddress ?? "",
26
+ toAddress: payment.toAddress ?? "",
27
+ amount: payment.amount ?? "0",
28
+ asset: payment.asset ?? "USDC",
29
+ chainId: payment.chainId ?? "eip155:1",
30
+ txHash: payment.txHash ?? "",
31
+ timestamp: Date.now(),
32
+ memo: payment.memo,
33
+ metadata: payment.metadata,
34
+ };
35
+
36
+ await store.insert(payload);
37
+ return payload;
38
+ }
@@ -0,0 +1,32 @@
1
+ import type { Request, Response, NextFunction } from "express";
2
+ import type { Store } from "../types.js";
3
+ import { handleIncomingPayment } from "./core.js";
4
+
5
+ export function tallyMiddleware(store: Store, facilitator: string) {
6
+ return (req: Request, res: Response, next: NextFunction) => {
7
+ const originalJson = res.json.bind(res);
8
+
9
+ res.json = function (body: unknown) {
10
+ if (res.statusCode === 402) {
11
+ const payment = {
12
+ facilitator,
13
+ requestId: (req.headers["x-request-id"] as string) ?? undefined,
14
+ fromAddress: (body as Record<string, unknown>)?.fromAddress as string | undefined,
15
+ toAddress: (body as Record<string, unknown>)?.toAddress as string | undefined,
16
+ amount: (body as Record<string, unknown>)?.amount as string | undefined,
17
+ asset: (body as Record<string, unknown>)?.asset as string | undefined,
18
+ chainId: (body as Record<string, unknown>)?.chainId as string | undefined,
19
+ txHash: (body as Record<string, unknown>)?.txHash as string | undefined,
20
+ memo: (body as Record<string, unknown>)?.memo as string | undefined,
21
+ metadata: (body as Record<string, unknown>)?.metadata as Record<string, unknown> | undefined,
22
+ };
23
+
24
+ handleIncomingPayment(store, payment).catch(() => {});
25
+ }
26
+
27
+ return originalJson(body);
28
+ };
29
+
30
+ next();
31
+ };
32
+ }