@routedock/routedock 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,70 @@
1
+ # @routedock/sdk Publish Checklist
2
+
3
+ Complete every step in order. Do not skip.
4
+
5
+ ## Pre-publish
6
+
7
+ - [ ] **1. Build dist/**
8
+ ```bash
9
+ cd packages/sdk
10
+ pnpm build
11
+ ```
12
+ Verify: `dist/index.js`, `dist/index.cjs`, `dist/client.js`, `dist/client.cjs`, `dist/provider.js`, `dist/provider.cjs`, and all `.d.ts` files exist.
13
+
14
+ - [ ] **2. Typecheck**
15
+ ```bash
16
+ pnpm typecheck
17
+ ```
18
+ Must exit with 0 errors.
19
+
20
+ - [ ] **3. Smoke tests**
21
+ ```bash
22
+ pnpm test
23
+ ```
24
+ All 4 smoke tests must pass.
25
+
26
+ - [ ] **4. Confirm no secrets in pack**
27
+ ```bash
28
+ npm pack --dry-run
29
+ ```
30
+ Scan the file list: no `.env`, no `*.pem`, no files containing `SECRET` in the name.
31
+
32
+ - [ ] **5. Update `repository.url`**
33
+ In `packages/sdk/package.json`, replace `YOUR_ORG` with the actual GitHub org/user.
34
+
35
+ ## Publish
36
+
37
+ - [ ] **6. Login to npm**
38
+ ```bash
39
+ npm login
40
+ ```
41
+ Verify you are logged in as the correct user: `npm whoami`
42
+
43
+ - [ ] **7. Publish**
44
+ ```bash
45
+ cd packages/sdk
46
+ pnpm publish --access public
47
+ ```
48
+ The `--access public` flag is required for scoped packages (`@routedock/sdk`) to be publicly accessible.
49
+
50
+ ## Post-publish Verification
51
+
52
+ - [ ] **8. Verify on npm registry**
53
+ ```bash
54
+ npm show @routedock/sdk
55
+ ```
56
+ Confirms: version 0.1.0, correct description, MIT license.
57
+
58
+ - [ ] **9. Test install in a clean directory**
59
+ ```bash
60
+ mkdir /tmp/test-install && cd /tmp/test-install
61
+ npm init -y
62
+ npm install @routedock/sdk
63
+ node -e "const { RouteDockClient } = require('@routedock/sdk'); console.log('ok')"
64
+ ```
65
+
66
+ - [ ] **10. Add npm badge to README.md**
67
+ Once published, update the root README with the live npm badge:
68
+ ```markdown
69
+ ![npm](https://img.shields.io/npm/v/@routedock/sdk)
70
+ ```
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # @routedock/sdk
2
+
3
+ Unified payment execution layer for autonomous agents on Stellar. One SDK for x402, MPP charge, and MPP session — mode selected automatically from the provider's `routedock.json` manifest.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @routedock/sdk
9
+ # or
10
+ pnpm add @routedock/sdk
11
+ ```
12
+
13
+ ## Agent Usage
14
+
15
+ ```ts
16
+ import { RouteDockClient } from '@routedock/sdk/client'
17
+ import { Keypair } from '@stellar/stellar-sdk'
18
+
19
+ const client = new RouteDockClient({
20
+ wallet: Keypair.fromSecret(process.env.AGENT_SECRET),
21
+ network: 'testnet',
22
+ spendCap: { daily: '1.00', asset: 'USDC' }, // optional local guard
23
+ })
24
+
25
+ // Single call — SDK reads manifest, picks mode automatically
26
+ const result = await client.pay('https://provider.railway.app/price')
27
+ // result.data — response body
28
+ // result.txHash — settlement hash (or null for session vouchers)
29
+ // result.mode — 'x402' | 'mpp-charge' | 'mpp-session'
30
+
31
+ // Sustained streaming access
32
+ const session = await client.openSession('https://provider.railway.app/stream/orderbook')
33
+ for await (const update of session.stream()) {
34
+ console.log(update)
35
+ if (done) break
36
+ }
37
+ await session.close() // triggers on-chain settlement
38
+ ```
39
+
40
+ ## Provider Usage
41
+
42
+ ```ts
43
+ import express from 'express'
44
+ import { routedock, SupabaseSessionStore } from '@routedock/sdk/provider'
45
+ import { createClient } from '@supabase/supabase-js'
46
+
47
+ const app = express()
48
+
49
+ // Price endpoint — x402 + MPP charge
50
+ app.use('/price', routedock({
51
+ modes: ['x402', 'mpp-charge'],
52
+ pricing: { x402: '0.001', 'mpp-charge': '0.0008' },
53
+ asset: 'USDC',
54
+ payee: process.env.STELLAR_PAYEE_ADDRESS,
55
+ network: process.env.STELLAR_NETWORK as 'testnet' | 'mainnet',
56
+ facilitatorApiKey: process.env.OPENZEPPELIN_API_KEY,
57
+ }))
58
+
59
+ // Stream endpoint — MPP session only
60
+ const store = new SupabaseSessionStore(createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY))
61
+ app.use('/stream/orderbook', routedock({
62
+ modes: ['mpp-session'],
63
+ pricing: { 'mpp-session': { rate: '0.0001', channelContract: process.env.CHANNEL_CONTRACT_ID } },
64
+ asset: 'USDC',
65
+ payee: process.env.STELLAR_PAYEE_ADDRESS,
66
+ network: process.env.STELLAR_NETWORK as 'testnet' | 'mainnet',
67
+ store,
68
+ }))
69
+ ```
70
+
71
+ ## Mode Selection Logic
72
+
73
+ The `ModeRouter` uses this decision tree (deterministic, no randomness):
74
+
75
+ 1. If `{ sustained: true }` passed AND manifest supports `mpp-session` → session
76
+ 2. Else if manifest supports `mpp-charge` AND network is Stellar → MPP charge (lower fees)
77
+ 3. Else if manifest supports `x402` → x402 with OZ facilitator
78
+ 4. Else throw `RouteDockNoSupportedModeError`
79
+
80
+ Override with `{ forceMode: 'x402' }` to bypass auto-selection.
81
+
82
+ ## Security
83
+
84
+ The `one-way-channel` Soroban contract wrapped by `MppSessionClient` is **unaudited** (`stellar-experimental/one-way-channel`). Safe defaults are enforced: 17280 ledger refund window, durable `SupabaseSessionStore` with monotonic invariant. Production use should await a formal audit.
85
+
86
+ ## License
87
+
88
+ MIT
@@ -0,0 +1,152 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+
3
+ /**
4
+ * TypeScript types for the RouteDock manifest format.
5
+ * Derived from packages/sdk/src/schemas/routedock.schema.json (draft-07).
6
+ *
7
+ * Section 5 of ROUTEDOCK_MASTER.md is the canonical specification.
8
+ */
9
+ type PaymentMode = 'x402' | 'mpp-charge' | 'mpp-session';
10
+ /** Per-request pricing config — used by x402 and mpp-charge modes */
11
+ interface PricingConfig {
12
+ /** Cost per request in the payment asset, e.g. "0.001" */
13
+ amount: string;
14
+ per: 'request';
15
+ /** x402 facilitator URL (OpenZeppelin Channels). Required for x402 mode. */
16
+ facilitator?: string;
17
+ }
18
+ /** Per-voucher pricing config — used by mpp-session (one-way-channel) mode */
19
+ interface SessionPricingConfig {
20
+ /** Cost per voucher, e.g. "0.0001" */
21
+ rate: string;
22
+ per: 'voucher';
23
+ /** Soroban contract address (C...) for the one-way-channel contract */
24
+ channel_contract: string;
25
+ /** Minimum initial deposit to open a channel, e.g. "0.10" */
26
+ min_deposit: string;
27
+ /**
28
+ * Ledgers the server has to settle before the funder can reclaim.
29
+ * Default: 17280 (~24h). Do NOT reduce in production — this is a
30
+ * security parameter the server depends on to settle first.
31
+ */
32
+ refund_waiting_period_ledgers: number;
33
+ }
34
+ /** Full RouteDock discovery manifest — served at /.well-known/routedock.json */
35
+ interface RouteDockManifest {
36
+ /** Schema version — always "1.0" */
37
+ routedock: '1.0';
38
+ /** Human-readable provider name */
39
+ name: string;
40
+ /** Human-readable description of the provider's data or service */
41
+ description: string;
42
+ /** Payment modes supported by this provider */
43
+ modes: PaymentMode[];
44
+ /** Stellar network this provider operates on */
45
+ network: 'testnet' | 'mainnet';
46
+ /** Asset ticker symbol, e.g. "USDC" */
47
+ asset: string;
48
+ /** Stellar Asset Contract (SAC) address for the payment asset */
49
+ asset_contract: string;
50
+ /** Stellar address (G...) that receives payments */
51
+ payee: string;
52
+ /** Pricing configuration per supported payment mode */
53
+ pricing: {
54
+ x402?: PricingConfig;
55
+ 'mpp-charge'?: PricingConfig;
56
+ 'mpp-session'?: SessionPricingConfig;
57
+ };
58
+ /** Map of endpoint name to HTTP method + path, e.g. { price: "GET /price" } */
59
+ endpoints: Record<string, string>;
60
+ /** Capability tags indexed with trigram search in the provider registry */
61
+ tags: string[];
62
+ }
63
+ /** Result returned by client.pay() for any payment mode */
64
+ interface PaymentResult {
65
+ /** Parsed response body from the provider */
66
+ data: unknown;
67
+ /** On-chain Stellar transaction hash, or null for session voucher calls */
68
+ txHash: string | null;
69
+ /** Payment mode that was used for this call */
70
+ mode: PaymentMode;
71
+ /** Amount paid in the payment asset (decimal string) */
72
+ amount: string;
73
+ /** Unix timestamp (ms) when the payment completed */
74
+ timestamp: number;
75
+ }
76
+ /** Result returned by session.close() */
77
+ interface SessionCloseResult {
78
+ /** On-chain transaction hash for the channel close */
79
+ closeTxHash: string;
80
+ /** Total amount settled across all vouchers (decimal string) */
81
+ totalPaid: string;
82
+ /** Number of vouchers issued during the session */
83
+ vouchersIssued: number;
84
+ }
85
+ /** Handle for a live MPP session returned by client.openSession() */
86
+ interface SessionHandle {
87
+ /** Stellar channel contract address (C...) */
88
+ channelId: string;
89
+ /** Transaction hash from the channel-open on-chain call */
90
+ openTxHash: string;
91
+ /**
92
+ * Async generator of server-sent event data.
93
+ * Each iteration sends a voucher and yields the parsed response.
94
+ * UNAUDITED: uses stellar-experimental/one-way-channel contract.
95
+ */
96
+ stream(): AsyncIterable<unknown>;
97
+ /** Close the channel on-chain with the highest signed voucher */
98
+ close(): Promise<SessionCloseResult>;
99
+ }
100
+ /**
101
+ * Durable session state stored in Supabase (sessions table).
102
+ * cumulative_amount is a decimal string to avoid float precision loss.
103
+ * The monotonic invariant is enforced both here and at the DB level via trigger.
104
+ */
105
+ interface SessionState {
106
+ channel_id: string;
107
+ payee: string;
108
+ payer: string;
109
+ /** Monotonically increasing cumulative amount — stored as string to preserve precision */
110
+ cumulative_amount: string;
111
+ last_signature: string;
112
+ status: 'open' | 'closing' | 'closed';
113
+ opened_at: string;
114
+ updated_at: string;
115
+ settlement_tx_hash: string | null;
116
+ }
117
+ declare class RouteDockError extends Error {
118
+ constructor(message: string);
119
+ }
120
+ /** Manifest fetch failed, schema invalid, or required fields missing */
121
+ declare class RouteDockManifestError extends RouteDockError {
122
+ constructor(message: string);
123
+ }
124
+ /** No payment mode in the manifest is supported by this client */
125
+ declare class RouteDockNoSupportedModeError extends RouteDockError {
126
+ constructor(message: string);
127
+ }
128
+ /** Session invariant violated (e.g. non-monotonic voucher amount) */
129
+ declare class RouteDockSessionError extends RouteDockError {
130
+ constructor(message: string);
131
+ }
132
+ /** Local spend cap or on-chain policy rejected the payment */
133
+ declare class RouteDockPolicyRejectedError extends RouteDockError {
134
+ readonly reason: string;
135
+ constructor(reason: string);
136
+ }
137
+
138
+ interface SessionStore {
139
+ get(channelId: string): Promise<SessionState | null>;
140
+ /** Enforce monotonic invariant at application level before writing */
141
+ upsert(channelId: string, state: SessionState): Promise<void>;
142
+ close(channelId: string): Promise<void>;
143
+ }
144
+ declare class SupabaseSessionStore implements SessionStore {
145
+ private readonly supabase;
146
+ constructor(supabase: SupabaseClient);
147
+ get(channelId: string): Promise<SessionState | null>;
148
+ upsert(channelId: string, state: SessionState): Promise<void>;
149
+ close(channelId: string): Promise<void>;
150
+ }
151
+
152
+ export { type PaymentMode as P, RouteDockError as R, type SessionCloseResult as S, type PaymentResult as a, type PricingConfig as b, type RouteDockManifest as c, RouteDockManifestError as d, RouteDockNoSupportedModeError as e, RouteDockPolicyRejectedError as f, RouteDockSessionError as g, type SessionHandle as h, type SessionPricingConfig as i, type SessionState as j, type SessionStore as k, SupabaseSessionStore as l };
@@ -0,0 +1,152 @@
1
+ import { SupabaseClient } from '@supabase/supabase-js';
2
+
3
+ /**
4
+ * TypeScript types for the RouteDock manifest format.
5
+ * Derived from packages/sdk/src/schemas/routedock.schema.json (draft-07).
6
+ *
7
+ * Section 5 of ROUTEDOCK_MASTER.md is the canonical specification.
8
+ */
9
+ type PaymentMode = 'x402' | 'mpp-charge' | 'mpp-session';
10
+ /** Per-request pricing config — used by x402 and mpp-charge modes */
11
+ interface PricingConfig {
12
+ /** Cost per request in the payment asset, e.g. "0.001" */
13
+ amount: string;
14
+ per: 'request';
15
+ /** x402 facilitator URL (OpenZeppelin Channels). Required for x402 mode. */
16
+ facilitator?: string;
17
+ }
18
+ /** Per-voucher pricing config — used by mpp-session (one-way-channel) mode */
19
+ interface SessionPricingConfig {
20
+ /** Cost per voucher, e.g. "0.0001" */
21
+ rate: string;
22
+ per: 'voucher';
23
+ /** Soroban contract address (C...) for the one-way-channel contract */
24
+ channel_contract: string;
25
+ /** Minimum initial deposit to open a channel, e.g. "0.10" */
26
+ min_deposit: string;
27
+ /**
28
+ * Ledgers the server has to settle before the funder can reclaim.
29
+ * Default: 17280 (~24h). Do NOT reduce in production — this is a
30
+ * security parameter the server depends on to settle first.
31
+ */
32
+ refund_waiting_period_ledgers: number;
33
+ }
34
+ /** Full RouteDock discovery manifest — served at /.well-known/routedock.json */
35
+ interface RouteDockManifest {
36
+ /** Schema version — always "1.0" */
37
+ routedock: '1.0';
38
+ /** Human-readable provider name */
39
+ name: string;
40
+ /** Human-readable description of the provider's data or service */
41
+ description: string;
42
+ /** Payment modes supported by this provider */
43
+ modes: PaymentMode[];
44
+ /** Stellar network this provider operates on */
45
+ network: 'testnet' | 'mainnet';
46
+ /** Asset ticker symbol, e.g. "USDC" */
47
+ asset: string;
48
+ /** Stellar Asset Contract (SAC) address for the payment asset */
49
+ asset_contract: string;
50
+ /** Stellar address (G...) that receives payments */
51
+ payee: string;
52
+ /** Pricing configuration per supported payment mode */
53
+ pricing: {
54
+ x402?: PricingConfig;
55
+ 'mpp-charge'?: PricingConfig;
56
+ 'mpp-session'?: SessionPricingConfig;
57
+ };
58
+ /** Map of endpoint name to HTTP method + path, e.g. { price: "GET /price" } */
59
+ endpoints: Record<string, string>;
60
+ /** Capability tags indexed with trigram search in the provider registry */
61
+ tags: string[];
62
+ }
63
+ /** Result returned by client.pay() for any payment mode */
64
+ interface PaymentResult {
65
+ /** Parsed response body from the provider */
66
+ data: unknown;
67
+ /** On-chain Stellar transaction hash, or null for session voucher calls */
68
+ txHash: string | null;
69
+ /** Payment mode that was used for this call */
70
+ mode: PaymentMode;
71
+ /** Amount paid in the payment asset (decimal string) */
72
+ amount: string;
73
+ /** Unix timestamp (ms) when the payment completed */
74
+ timestamp: number;
75
+ }
76
+ /** Result returned by session.close() */
77
+ interface SessionCloseResult {
78
+ /** On-chain transaction hash for the channel close */
79
+ closeTxHash: string;
80
+ /** Total amount settled across all vouchers (decimal string) */
81
+ totalPaid: string;
82
+ /** Number of vouchers issued during the session */
83
+ vouchersIssued: number;
84
+ }
85
+ /** Handle for a live MPP session returned by client.openSession() */
86
+ interface SessionHandle {
87
+ /** Stellar channel contract address (C...) */
88
+ channelId: string;
89
+ /** Transaction hash from the channel-open on-chain call */
90
+ openTxHash: string;
91
+ /**
92
+ * Async generator of server-sent event data.
93
+ * Each iteration sends a voucher and yields the parsed response.
94
+ * UNAUDITED: uses stellar-experimental/one-way-channel contract.
95
+ */
96
+ stream(): AsyncIterable<unknown>;
97
+ /** Close the channel on-chain with the highest signed voucher */
98
+ close(): Promise<SessionCloseResult>;
99
+ }
100
+ /**
101
+ * Durable session state stored in Supabase (sessions table).
102
+ * cumulative_amount is a decimal string to avoid float precision loss.
103
+ * The monotonic invariant is enforced both here and at the DB level via trigger.
104
+ */
105
+ interface SessionState {
106
+ channel_id: string;
107
+ payee: string;
108
+ payer: string;
109
+ /** Monotonically increasing cumulative amount — stored as string to preserve precision */
110
+ cumulative_amount: string;
111
+ last_signature: string;
112
+ status: 'open' | 'closing' | 'closed';
113
+ opened_at: string;
114
+ updated_at: string;
115
+ settlement_tx_hash: string | null;
116
+ }
117
+ declare class RouteDockError extends Error {
118
+ constructor(message: string);
119
+ }
120
+ /** Manifest fetch failed, schema invalid, or required fields missing */
121
+ declare class RouteDockManifestError extends RouteDockError {
122
+ constructor(message: string);
123
+ }
124
+ /** No payment mode in the manifest is supported by this client */
125
+ declare class RouteDockNoSupportedModeError extends RouteDockError {
126
+ constructor(message: string);
127
+ }
128
+ /** Session invariant violated (e.g. non-monotonic voucher amount) */
129
+ declare class RouteDockSessionError extends RouteDockError {
130
+ constructor(message: string);
131
+ }
132
+ /** Local spend cap or on-chain policy rejected the payment */
133
+ declare class RouteDockPolicyRejectedError extends RouteDockError {
134
+ readonly reason: string;
135
+ constructor(reason: string);
136
+ }
137
+
138
+ interface SessionStore {
139
+ get(channelId: string): Promise<SessionState | null>;
140
+ /** Enforce monotonic invariant at application level before writing */
141
+ upsert(channelId: string, state: SessionState): Promise<void>;
142
+ close(channelId: string): Promise<void>;
143
+ }
144
+ declare class SupabaseSessionStore implements SessionStore {
145
+ private readonly supabase;
146
+ constructor(supabase: SupabaseClient);
147
+ get(channelId: string): Promise<SessionState | null>;
148
+ upsert(channelId: string, state: SessionState): Promise<void>;
149
+ close(channelId: string): Promise<void>;
150
+ }
151
+
152
+ export { type PaymentMode as P, RouteDockError as R, type SessionCloseResult as S, type PaymentResult as a, type PricingConfig as b, type RouteDockManifest as c, RouteDockManifestError as d, RouteDockNoSupportedModeError as e, RouteDockPolicyRejectedError as f, RouteDockSessionError as g, type SessionHandle as h, type SessionPricingConfig as i, type SessionState as j, type SessionStore as k, SupabaseSessionStore as l };