@totemsdk/edge-adapters 0.1.1 → 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.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * edge-adapters/artifact-cache.ts — RFC-007 Amendment A, phase A1.
3
+ *
4
+ * The first consumer of the Edge persistence injection point (`PersistencePorts`).
5
+ * A scoped, content-addressed artifact cache over the injected `storage` (KV
6
+ * retention index) and `artifacts` (byte store) surfaces.
7
+ *
8
+ * Typed structurally against `@totemsdk/storage` — this module must NOT import
9
+ * `@totemsdk/edge`, so it carries no Edge runtime dependency.
10
+ *
11
+ * Guarantees:
12
+ * - `assertPersistence()` validates surfaces/capabilities at construction.
13
+ * - Scope is collision-control only (namespaces); authorization stays above.
14
+ * - Retrieval re-verifies digests via `ArtifactStore.get` (`corrupt` vs
15
+ * `not-found` are never conflated).
16
+ * - Retention/expiry entries survive restart when the injected storage is durable.
17
+ */
18
+ import type { ArtifactBackendCapabilities, ArtifactRead, ArtifactRef, EffectivePersistence, PersistencePorts, PersistenceRequirement, StorageScope } from '@totemsdk/storage';
19
+ /**
20
+ * Per-object cache policy. Deliberately does NOT reuse the `retention:
21
+ * 'fixed'|'managed'|'none'` vocabulary: that is a BACKEND capability, not a
22
+ * per-object request.
23
+ */
24
+ export interface ArtifactCacheRetentionPolicy {
25
+ readonly ttlMs?: number;
26
+ readonly invalidateOnExpiry?: boolean;
27
+ }
28
+ export interface EdgeArtifactCache {
29
+ readonly capabilities: ArtifactBackendCapabilities;
30
+ /** Reported after assertPersistence(); callers must never assume durability. */
31
+ readonly effective: EffectivePersistence;
32
+ put(scope: StorageScope, bytes: Uint8Array, policy?: ArtifactCacheRetentionPolicy): Promise<ArtifactRef>;
33
+ get(scope: StorageScope, ref: ArtifactRef, policy?: ArtifactCacheRetentionPolicy): Promise<ArtifactRead>;
34
+ invalidate(scope: StorageScope, ref: ArtifactRef): Promise<void>;
35
+ }
36
+ export declare function createEdgeArtifactCache(ports: PersistencePorts, requirement?: PersistenceRequirement, policy?: ArtifactCacheRetentionPolicy): EdgeArtifactCache;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ /**
3
+ * edge-adapters/artifact-cache.ts — RFC-007 Amendment A, phase A1.
4
+ *
5
+ * The first consumer of the Edge persistence injection point (`PersistencePorts`).
6
+ * A scoped, content-addressed artifact cache over the injected `storage` (KV
7
+ * retention index) and `artifacts` (byte store) surfaces.
8
+ *
9
+ * Typed structurally against `@totemsdk/storage` — this module must NOT import
10
+ * `@totemsdk/edge`, so it carries no Edge runtime dependency.
11
+ *
12
+ * Guarantees:
13
+ * - `assertPersistence()` validates surfaces/capabilities at construction.
14
+ * - Scope is collision-control only (namespaces); authorization stays above.
15
+ * - Retrieval re-verifies digests via `ArtifactStore.get` (`corrupt` vs
16
+ * `not-found` are never conflated).
17
+ * - Retention/expiry entries survive restart when the injected storage is durable.
18
+ */
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.createEdgeArtifactCache = createEdgeArtifactCache;
21
+ const storage_1 = require("@totemsdk/storage");
22
+ /** Scope-derived artifact namespace: separate scopes never alias the same blob. */
23
+ function scopeNamespace(scope) {
24
+ const parts = [scope.principal, scope.adapter, scope.purpose, scope.domain]
25
+ .filter((p) => typeof p === 'string' && p.length > 0)
26
+ .map((p) => encodeURIComponent(p));
27
+ return `edge-cache:${parts.join('/')}`;
28
+ }
29
+ function createEdgeArtifactCache(ports, requirement = { durable: false }, policy = {}) {
30
+ const label = 'edge-artifact-cache';
31
+ // The cache always needs both surfaces and a deletable artifact backend.
32
+ const effective = (0, storage_1.assertPersistence)(ports, { ...requirement, needsArtifacts: true, needsArtifactDelete: true }, label);
33
+ const storage = ports.storage;
34
+ const artifacts = ports.artifacts;
35
+ if (!storage) {
36
+ throw new storage_1.StorageError(`${label}: a storage index is required for retention/expiry`, 'unavailable');
37
+ }
38
+ if (!artifacts) {
39
+ throw new storage_1.StorageError(`${label}: an artifact store is required`, 'unavailable');
40
+ }
41
+ const defaultTtlMs = policy.ttlMs;
42
+ const defaultInvalidateOnExpiry = policy.invalidateOnExpiry ?? false;
43
+ const now = () => Date.now();
44
+ const scoped = (scope) => (0, storage_1.createScopedStorage)(storage, scope);
45
+ return {
46
+ capabilities: artifacts.capabilities,
47
+ effective,
48
+ async put(scope, bytes, putPolicy) {
49
+ const ttlMs = putPolicy?.ttlMs ?? defaultTtlMs;
50
+ const receipt = await artifacts.put(scopeNamespace(scope), bytes, {
51
+ ...(ttlMs !== undefined ? { retentionMs: ttlMs } : {}),
52
+ });
53
+ const entry = {
54
+ ref: receipt.ref,
55
+ size: receipt.size,
56
+ createdAt: now(),
57
+ ...(ttlMs !== undefined ? { expiresAt: now() + ttlMs } : {}),
58
+ };
59
+ await scoped(scope).set((0, storage_1.artifactRefId)(receipt.ref), entry);
60
+ return receipt.ref;
61
+ },
62
+ async get(scope, ref, getPolicy) {
63
+ const entry = await scoped(scope).get((0, storage_1.artifactRefId)(ref));
64
+ if (!entry) {
65
+ // Not known to this scope — never fall through to the raw backend.
66
+ return { status: 'not-found' };
67
+ }
68
+ if (entry.expiresAt !== undefined && now() > entry.expiresAt) {
69
+ const invalidate = getPolicy?.invalidateOnExpiry ?? defaultInvalidateOnExpiry;
70
+ if (invalidate)
71
+ await this.invalidate(scope, ref);
72
+ return { status: 'not-found' };
73
+ }
74
+ // Digest re-verification + corrupt/not-found taxonomy live in ArtifactStore.
75
+ return artifacts.get(ref);
76
+ },
77
+ async invalidate(scope, ref) {
78
+ await artifacts.delete(ref);
79
+ await scoped(scope).remove((0, storage_1.artifactRefId)(ref));
80
+ },
81
+ };
82
+ }
package/dist/index.d.ts CHANGED
@@ -3,6 +3,10 @@ export type { LiquidityPortConfig } from './liquidity.js';
3
3
  export { createProofPortAdapter } from './proof.js';
4
4
  export type { ProofPortConfig } from './proof.js';
5
5
  export { createLookupPortAdapter } from './lookup.js';
6
+ export { createPurchaseLookupAdapter, type PurchaseLookupAdapterConfig } from './purchase-lookup.js';
7
+ export { createPurchaseAuthorityAdapter, type PurchaseAuthorityAdapterConfig } from './purchase-authority.js';
8
+ export { createPurchasePaymentAdapter, type PurchasePaymentAdapterConfig, type PurchasePaymentStore, type PurchasePaymentClaimRecord, } from './purchase-payment.js';
9
+ export { createStreamNegotiationTransport, createPubSubNegotiationTransport, type StreamNegotiationTransportConfig, type PubSubNegotiationTransportConfig, } from './negotiation-transport.js';
6
10
  export { createLocationPortAdapter } from './location.js';
7
11
  export type { LocationPortConfig } from './location.js';
8
12
  export { createPolicyPortAdapter } from './policy.js';
@@ -17,3 +21,5 @@ export { createOmniaHostPort } from './omnia.js';
17
21
  export type { OmniaHostPortConfig } from './omnia.js';
18
22
  export { createStreamPortAdapter } from './stream.js';
19
23
  export { createPubSubPortAdapter } from './pubsub.js';
24
+ export { SQLiteCommerceStore, createSQLiteCommerceStore, type CommerceStore, type SQLiteCommerceStoreConfig, } from './sqlite-commerce-store.js';
25
+ export { createEdgeArtifactCache, type EdgeArtifactCache, type ArtifactCacheRetentionPolicy, } from './artifact-cache.js';
package/dist/index.js CHANGED
@@ -1,12 +1,21 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createPubSubPortAdapter = exports.createStreamPortAdapter = exports.createOmniaHostPort = exports.createOmniaL2PaymentPort = exports.createMinimaL1PaymentPort = exports.createManifestPortAdapter = exports.createIdentityPortAdapter = exports.createPolicyPortAdapter = exports.createLocationPortAdapter = exports.createLookupPortAdapter = exports.createProofPortAdapter = exports.createLiquidityPortAdapter = void 0;
3
+ exports.createEdgeArtifactCache = exports.createSQLiteCommerceStore = exports.SQLiteCommerceStore = exports.createPubSubPortAdapter = exports.createStreamPortAdapter = exports.createOmniaHostPort = exports.createOmniaL2PaymentPort = exports.createMinimaL1PaymentPort = exports.createManifestPortAdapter = exports.createIdentityPortAdapter = exports.createPolicyPortAdapter = exports.createLocationPortAdapter = exports.createPubSubNegotiationTransport = exports.createStreamNegotiationTransport = exports.createPurchasePaymentAdapter = exports.createPurchaseAuthorityAdapter = exports.createPurchaseLookupAdapter = exports.createLookupPortAdapter = exports.createProofPortAdapter = exports.createLiquidityPortAdapter = void 0;
4
4
  var liquidity_js_1 = require("./liquidity.js");
5
5
  Object.defineProperty(exports, "createLiquidityPortAdapter", { enumerable: true, get: function () { return liquidity_js_1.createLiquidityPortAdapter; } });
6
6
  var proof_js_1 = require("./proof.js");
7
7
  Object.defineProperty(exports, "createProofPortAdapter", { enumerable: true, get: function () { return proof_js_1.createProofPortAdapter; } });
8
8
  var lookup_js_1 = require("./lookup.js");
9
9
  Object.defineProperty(exports, "createLookupPortAdapter", { enumerable: true, get: function () { return lookup_js_1.createLookupPortAdapter; } });
10
+ var purchase_lookup_js_1 = require("./purchase-lookup.js");
11
+ Object.defineProperty(exports, "createPurchaseLookupAdapter", { enumerable: true, get: function () { return purchase_lookup_js_1.createPurchaseLookupAdapter; } });
12
+ var purchase_authority_js_1 = require("./purchase-authority.js");
13
+ Object.defineProperty(exports, "createPurchaseAuthorityAdapter", { enumerable: true, get: function () { return purchase_authority_js_1.createPurchaseAuthorityAdapter; } });
14
+ var purchase_payment_js_1 = require("./purchase-payment.js");
15
+ Object.defineProperty(exports, "createPurchasePaymentAdapter", { enumerable: true, get: function () { return purchase_payment_js_1.createPurchasePaymentAdapter; } });
16
+ var negotiation_transport_js_1 = require("./negotiation-transport.js");
17
+ Object.defineProperty(exports, "createStreamNegotiationTransport", { enumerable: true, get: function () { return negotiation_transport_js_1.createStreamNegotiationTransport; } });
18
+ Object.defineProperty(exports, "createPubSubNegotiationTransport", { enumerable: true, get: function () { return negotiation_transport_js_1.createPubSubNegotiationTransport; } });
10
19
  var location_js_1 = require("./location.js");
11
20
  Object.defineProperty(exports, "createLocationPortAdapter", { enumerable: true, get: function () { return location_js_1.createLocationPortAdapter; } });
12
21
  var policy_js_1 = require("./policy.js");
@@ -25,3 +34,8 @@ var stream_js_1 = require("./stream.js");
25
34
  Object.defineProperty(exports, "createStreamPortAdapter", { enumerable: true, get: function () { return stream_js_1.createStreamPortAdapter; } });
26
35
  var pubsub_js_1 = require("./pubsub.js");
27
36
  Object.defineProperty(exports, "createPubSubPortAdapter", { enumerable: true, get: function () { return pubsub_js_1.createPubSubPortAdapter; } });
37
+ var sqlite_commerce_store_js_1 = require("./sqlite-commerce-store.js");
38
+ Object.defineProperty(exports, "SQLiteCommerceStore", { enumerable: true, get: function () { return sqlite_commerce_store_js_1.SQLiteCommerceStore; } });
39
+ Object.defineProperty(exports, "createSQLiteCommerceStore", { enumerable: true, get: function () { return sqlite_commerce_store_js_1.createSQLiteCommerceStore; } });
40
+ var artifact_cache_js_1 = require("./artifact-cache.js");
41
+ Object.defineProperty(exports, "createEdgeArtifactCache", { enumerable: true, get: function () { return artifact_cache_js_1.createEdgeArtifactCache; } });
@@ -6,7 +6,7 @@ export interface LiquidityPortConfig {
6
6
  defaultTokenId?: string;
7
7
  }
8
8
  /**
9
- * Wraps a ChainStateProvider (chain-provider, pureminima-rpc, lookup-client) as
9
+ * Wraps a ChainStateProvider (chain-provider, minima-rpc, lookup-client) as
10
10
  * an EdgeLiquidityPort.
11
11
  *
12
12
  * getBalance sums sendable coins for the given address and tokenId.
package/dist/liquidity.js CHANGED
@@ -13,7 +13,7 @@ function _fromScaled(n) {
13
13
  return frac ? `${n / _SCALE}.${frac}` : `${n / _SCALE}`;
14
14
  }
15
15
  /**
16
- * Wraps a ChainStateProvider (chain-provider, pureminima-rpc, lookup-client) as
16
+ * Wraps a ChainStateProvider (chain-provider, minima-rpc, lookup-client) as
17
17
  * an EdgeLiquidityPort.
18
18
  *
19
19
  * getBalance sums sendable coins for the given address and tokenId.
@@ -0,0 +1,50 @@
1
+ /**
2
+ * negotiation-transport.ts — NegotiationTransport adapters over Edge ports.
3
+ *
4
+ * Two adapters:
5
+ * - createStreamNegotiationTransport: raw byte stream (EdgeStreamPort).
6
+ * Messages are framed as length-prefixed JSON. The sender address is
7
+ * resolved by the caller (the stream is a single authenticated peer).
8
+ * - createPubSubNegotiationTransport: topic-based (EdgePubSubPort).
9
+ * Each negotiation uses a topic derived from the negotiationId; the
10
+ * sender address is carried in the envelope.
11
+ *
12
+ * Transport metadata stays separate from economic protocol objects. The
13
+ * NegotiationEngine never sees sockets/topics/connection IDs.
14
+ */
15
+ import type { NegotiationTransport } from '@totemsdk/edge';
16
+ import type { EdgePubSubPort, EdgeStreamPort } from '@totemsdk/edge';
17
+ export interface StreamNegotiationTransportConfig {
18
+ stream: EdgeStreamPort;
19
+ /** The authenticated sender address for this stream (resolved by caller). */
20
+ sender: string;
21
+ /** The local recipient address. */
22
+ recipient: string;
23
+ /** Max message size in bytes (default 64 KiB). */
24
+ maxBytes?: number;
25
+ }
26
+ /**
27
+ * Create a NegotiationTransport over a raw byte stream.
28
+ *
29
+ * Messages are framed as `[4-byte BE length][JSON bytes]`. The stream is
30
+ * assumed to be a single authenticated peer connection, so the sender address
31
+ * is fixed by the caller.
32
+ */
33
+ export declare function createStreamNegotiationTransport(config: StreamNegotiationTransportConfig): NegotiationTransport;
34
+ export interface PubSubNegotiationTransportConfig {
35
+ pubsub: EdgePubSubPort;
36
+ /** The local recipient address. */
37
+ recipient: string;
38
+ /** Topic prefix (default 'totem.negotiation'). */
39
+ topicPrefix?: string;
40
+ /** Max message size in bytes (default 64 KiB). */
41
+ maxBytes?: number;
42
+ }
43
+ /**
44
+ * Create a NegotiationTransport over a pub/sub transport.
45
+ *
46
+ * Each negotiation uses a topic derived from its negotiationId. The sender
47
+ * address is carried in the envelope (a `sender` field on the message), so
48
+ * the ingress pipeline can authenticate it.
49
+ */
50
+ export declare function createPubSubNegotiationTransport(config: PubSubNegotiationTransportConfig): NegotiationTransport;
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ /**
3
+ * negotiation-transport.ts — NegotiationTransport adapters over Edge ports.
4
+ *
5
+ * Two adapters:
6
+ * - createStreamNegotiationTransport: raw byte stream (EdgeStreamPort).
7
+ * Messages are framed as length-prefixed JSON. The sender address is
8
+ * resolved by the caller (the stream is a single authenticated peer).
9
+ * - createPubSubNegotiationTransport: topic-based (EdgePubSubPort).
10
+ * Each negotiation uses a topic derived from the negotiationId; the
11
+ * sender address is carried in the envelope.
12
+ *
13
+ * Transport metadata stays separate from economic protocol objects. The
14
+ * NegotiationEngine never sees sockets/topics/connection IDs.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createStreamNegotiationTransport = createStreamNegotiationTransport;
18
+ exports.createPubSubNegotiationTransport = createPubSubNegotiationTransport;
19
+ /**
20
+ * Create a NegotiationTransport over a raw byte stream.
21
+ *
22
+ * Messages are framed as `[4-byte BE length][JSON bytes]`. The stream is
23
+ * assumed to be a single authenticated peer connection, so the sender address
24
+ * is fixed by the caller.
25
+ */
26
+ function createStreamNegotiationTransport(config) {
27
+ const { stream, sender, recipient } = config;
28
+ const maxBytes = config.maxBytes ?? 64 * 1024;
29
+ return {
30
+ async send(_recipient, message) {
31
+ const bytes = new TextEncoder().encode(JSON.stringify(message));
32
+ if (bytes.length > maxBytes) {
33
+ throw new Error(`negotiation message exceeds ${maxBytes} bytes`);
34
+ }
35
+ const frame = new Uint8Array(4 + bytes.length);
36
+ new DataView(frame.buffer).setUint32(0, bytes.length);
37
+ frame.set(bytes, 4);
38
+ // Raw byte-stream send does NOT prove durable remote processing —
39
+ // return no receipt so the outbox drainer does not mark delivered.
40
+ stream.send(frame);
41
+ return undefined;
42
+ },
43
+ subscribe(handler) {
44
+ let buffer = new Uint8Array(0);
45
+ const off = stream.onData((chunk) => {
46
+ buffer = concatBytes(buffer, chunk);
47
+ while (buffer.length >= 4) {
48
+ const view = new DataView(buffer.buffer, buffer.byteOffset, 4);
49
+ const len = view.getUint32(0);
50
+ if (buffer.length < 4 + len)
51
+ break;
52
+ const payload = buffer.slice(4, 4 + len);
53
+ buffer = buffer.slice(4 + len);
54
+ try {
55
+ const message = JSON.parse(new TextDecoder().decode(payload));
56
+ void handler(message, { sender, recipient });
57
+ }
58
+ catch {
59
+ // Malformed frame — drop.
60
+ }
61
+ }
62
+ });
63
+ return off;
64
+ },
65
+ };
66
+ }
67
+ function topicFor(topicPrefix, negotiationId) {
68
+ return `${topicPrefix}.${negotiationId}`;
69
+ }
70
+ /**
71
+ * Create a NegotiationTransport over a pub/sub transport.
72
+ *
73
+ * Each negotiation uses a topic derived from its negotiationId. The sender
74
+ * address is carried in the envelope (a `sender` field on the message), so
75
+ * the ingress pipeline can authenticate it.
76
+ */
77
+ function createPubSubNegotiationTransport(config) {
78
+ const { pubsub, recipient } = config;
79
+ const topicPrefix = config.topicPrefix ?? 'totem.negotiation';
80
+ const maxBytes = config.maxBytes ?? 64 * 1024;
81
+ return {
82
+ async send(recipientAddress, message) {
83
+ const negotiationId = message.negotiationId;
84
+ if (!negotiationId)
85
+ throw new Error('negotiation message missing negotiationId');
86
+ const topic = topicFor(topicPrefix, negotiationId);
87
+ const envelope = { ...message, sender: recipientAddress, recipient };
88
+ const bytes = new TextEncoder().encode(JSON.stringify(envelope));
89
+ if (bytes.length > maxBytes) {
90
+ throw new Error(`negotiation message exceeds ${maxBytes} bytes`);
91
+ }
92
+ // Pub/sub publish() proves local broker transmission, not durable remote
93
+ // processing — return no receipt so the outbox drainer does not mark
94
+ // delivered on local send alone.
95
+ await pubsub.publish(topic, bytes);
96
+ return undefined;
97
+ },
98
+ subscribe(handler) {
99
+ return pubsub.onMessage(({ topic, payload }) => {
100
+ if (!topic.startsWith(topicPrefix))
101
+ return;
102
+ try {
103
+ const envelope = JSON.parse(new TextDecoder().decode(payload));
104
+ const sender = envelope.sender ?? 'unknown';
105
+ void handler(envelope, { sender, recipient });
106
+ }
107
+ catch {
108
+ // Malformed message — drop.
109
+ }
110
+ });
111
+ },
112
+ };
113
+ }
114
+ // ─────────────────────────────────────────────────────────────────────────────
115
+ // Helpers
116
+ // ─────────────────────────────────────────────────────────────────────────────
117
+ function concatBytes(a, b) {
118
+ const out = new Uint8Array(a.length + b.length);
119
+ out.set(a, 0);
120
+ out.set(b, a.length);
121
+ return out;
122
+ }
@@ -9,7 +9,7 @@ export interface MinimaL1PaymentPortConfig {
9
9
  *
10
10
  * This keeps the adapter agnostic to key management — callers wire in their
11
11
  * own signer (e.g. @totemsdk/server's sendTransaction, a hardware wallet
12
- * bridge, or a pureminima-rpc command sequence).
12
+ * bridge, or a minima-rpc command sequence).
13
13
  */
14
14
  sign(params: {
15
15
  toAddress: string;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * purchase-authority.ts — AuthorityPort adapter over @totemsdk/agent-policy.
3
+ *
4
+ * Maps a machine-commerce TradeAgreement + PurchaseIntent to an AgentProposal
5
+ * and evaluates it through a ComposablePolicy (or any AgentPolicy).
6
+ *
7
+ * Architectural boundary preserved:
8
+ * TradeProposal = machine ↔ machine bargaining (peer-to-peer)
9
+ * AgentProposal = agent → trusted local authority (wallet/policy)
10
+ *
11
+ * The machine negotiation strategy never receives a private key. This adapter
12
+ * is the trusted boundary that decides whether the local authority approves
13
+ * the economic commitment.
14
+ */
15
+ import type { AgentPolicy, PolicyMiddleware } from '@totemsdk/agent-policy';
16
+ import type { EdgeOperationResult } from '@totemsdk/edge';
17
+ import type { PurchaseIntent, TradeAgreement } from '@totemsdk/edge';
18
+ type PolicyLike = AgentPolicy | PolicyMiddleware;
19
+ export interface PurchaseAuthorityAdapterConfig {
20
+ /** The local policy (ComposablePolicy, AgentPolicy, or PolicyMiddleware). */
21
+ policy: PolicyLike;
22
+ /** Agent identifier used in the AgentProposal (e.g. 'edge-purchase-agent'). */
23
+ agentId?: string;
24
+ /** Optional risk level for the proposal. */
25
+ risk?: 'low' | 'medium' | 'high';
26
+ }
27
+ /**
28
+ * Create an AuthorityPort adapter over an agent-policy.
29
+ *
30
+ * approve() builds an AgentProposal from the agreement + intent and evaluates
31
+ * it through the policy. Returns `{ allowed, reason }`.
32
+ */
33
+ export declare function createPurchaseAuthorityAdapter(config: PurchaseAuthorityAdapterConfig): {
34
+ approve(params: {
35
+ agreement: TradeAgreement;
36
+ intent: PurchaseIntent;
37
+ }): Promise<EdgeOperationResult<{
38
+ allowed: boolean;
39
+ reason?: string;
40
+ }>>;
41
+ };
42
+ export {};
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * purchase-authority.ts — AuthorityPort adapter over @totemsdk/agent-policy.
4
+ *
5
+ * Maps a machine-commerce TradeAgreement + PurchaseIntent to an AgentProposal
6
+ * and evaluates it through a ComposablePolicy (or any AgentPolicy).
7
+ *
8
+ * Architectural boundary preserved:
9
+ * TradeProposal = machine ↔ machine bargaining (peer-to-peer)
10
+ * AgentProposal = agent → trusted local authority (wallet/policy)
11
+ *
12
+ * The machine negotiation strategy never receives a private key. This adapter
13
+ * is the trusted boundary that decides whether the local authority approves
14
+ * the economic commitment.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.createPurchaseAuthorityAdapter = createPurchaseAuthorityAdapter;
18
+ function isMiddleware(p) {
19
+ return 'evaluate' in p && typeof p.evaluate === 'function';
20
+ }
21
+ /**
22
+ * Create an AuthorityPort adapter over an agent-policy.
23
+ *
24
+ * approve() builds an AgentProposal from the agreement + intent and evaluates
25
+ * it through the policy. Returns `{ allowed, reason }`.
26
+ */
27
+ function createPurchaseAuthorityAdapter(config) {
28
+ const { policy } = config;
29
+ const agentId = config.agentId ?? 'edge-purchase-agent';
30
+ const risk = config.risk ?? 'medium';
31
+ return {
32
+ async approve(params) {
33
+ try {
34
+ const { agreement, intent } = params;
35
+ const proposal = {
36
+ id: `edge-purchase:${agreement.agreementId}`,
37
+ agentId,
38
+ intent: {
39
+ type: 'payment',
40
+ amount: agreement.terms.price,
41
+ tokenId: agreement.terms.tokenId,
42
+ recipient: agreement.seller,
43
+ reason: `purchase ${intent.resource} from ${agreement.seller}`,
44
+ risk,
45
+ metadata: {
46
+ agreementId: agreement.agreementId,
47
+ negotiationId: agreement.negotiationId,
48
+ resource: intent.resource,
49
+ },
50
+ },
51
+ explanation: `Machine purchase of ${intent.resource} for ${agreement.terms.price} ${agreement.terms.tokenId ?? '0x00'} from ${agreement.seller}`,
52
+ confidence: 1,
53
+ createdAt: Date.now(),
54
+ };
55
+ if (isMiddleware(policy)) {
56
+ const result = await policy.evaluate(proposal);
57
+ return {
58
+ ok: true,
59
+ data: {
60
+ allowed: result.outcome === 'approved',
61
+ reason: result.reason,
62
+ },
63
+ };
64
+ }
65
+ const allowed = await policy.canAutoApprove(proposal);
66
+ return {
67
+ ok: true,
68
+ data: {
69
+ allowed,
70
+ reason: allowed ? undefined : 'Purchase requires user approval per configured policy',
71
+ },
72
+ };
73
+ }
74
+ catch (err) {
75
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
76
+ }
77
+ },
78
+ };
79
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * purchase-lookup.ts — PurchaseLookupPort adapter over @totemsdk/lookup-client.
3
+ *
4
+ * Maps a machine-commerce `resource` (e.g. "compute", "storage") to a
5
+ * capability query on the Lookup Node, returning signed manifests for the
6
+ * buyer's discovery flow.
7
+ *
8
+ * Discovery is publication, not authority: the buyer MUST still verify each
9
+ * returned manifest's signature, identity binding, expiry, and capabilities
10
+ * before negotiation/payment. This adapter only fetches candidates.
11
+ */
12
+ import type { LookupClient } from '@totemsdk/lookup-client';
13
+ import type { EdgeOperationResult } from '@totemsdk/edge';
14
+ export interface PurchaseLookupAdapterConfig {
15
+ client: LookupClient;
16
+ /** Optional provider address filter (authorAddress for apps). */
17
+ provider?: string;
18
+ /** Optional max price filter (agents). */
19
+ maxPricePerCall?: number;
20
+ /** Optional max latency filter (agents). */
21
+ maxLatencyMs?: number;
22
+ /** Optional result limit. */
23
+ limit?: number;
24
+ }
25
+ /**
26
+ * Create a PurchaseLookupPort adapter over a LookupClient.
27
+ *
28
+ * `resource` is treated as a capability name for agent manifests (the
29
+ * machine-commerce case). If the resource matches a known app category, it
30
+ * falls back to app queries. Returns `{ id, manifest: Uint8Array, nodeId }`
31
+ * candidates for the buyer's manifest-verification pipeline.
32
+ */
33
+ export declare function createPurchaseLookupAdapter(config: PurchaseLookupAdapterConfig): {
34
+ query(params: {
35
+ resource: string;
36
+ provider?: string;
37
+ }): Promise<EdgeOperationResult<{
38
+ results: Array<{
39
+ id: string;
40
+ manifest: Uint8Array;
41
+ nodeId: string;
42
+ }>;
43
+ }>>;
44
+ };
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ /**
3
+ * purchase-lookup.ts — PurchaseLookupPort adapter over @totemsdk/lookup-client.
4
+ *
5
+ * Maps a machine-commerce `resource` (e.g. "compute", "storage") to a
6
+ * capability query on the Lookup Node, returning signed manifests for the
7
+ * buyer's discovery flow.
8
+ *
9
+ * Discovery is publication, not authority: the buyer MUST still verify each
10
+ * returned manifest's signature, identity binding, expiry, and capabilities
11
+ * before negotiation/payment. This adapter only fetches candidates.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.createPurchaseLookupAdapter = createPurchaseLookupAdapter;
15
+ /**
16
+ * Create a PurchaseLookupPort adapter over a LookupClient.
17
+ *
18
+ * `resource` is treated as a capability name for agent manifests (the
19
+ * machine-commerce case). If the resource matches a known app category, it
20
+ * falls back to app queries. Returns `{ id, manifest: Uint8Array, nodeId }`
21
+ * candidates for the buyer's manifest-verification pipeline.
22
+ */
23
+ function createPurchaseLookupAdapter(config) {
24
+ const { client } = config;
25
+ return {
26
+ async query(params) {
27
+ try {
28
+ const provider = params.provider ?? config.provider;
29
+ const limit = config.limit ?? 20;
30
+ // Agent capability query (machine-commerce primary path).
31
+ const agents = await client.queryAgents({
32
+ capabilityName: params.resource,
33
+ maxPricePerCall: config.maxPricePerCall,
34
+ maxLatencyMs: config.maxLatencyMs,
35
+ limit,
36
+ });
37
+ const results = agents.map((a) => ({
38
+ id: a.capabilityId,
39
+ manifest: a.manifest,
40
+ nodeId: a.nodeId,
41
+ }));
42
+ // If no agent results and a provider is specified, also try app query
43
+ // filtered by author address (provider = operator).
44
+ if (results.length === 0 && provider) {
45
+ const apps = await client.queryApps({
46
+ authorAddress: provider,
47
+ limit,
48
+ });
49
+ for (const a of apps) {
50
+ results.push({ id: a.appId, manifest: a.manifest, nodeId: a.nodeId });
51
+ }
52
+ }
53
+ return { ok: true, data: { results } };
54
+ }
55
+ catch (err) {
56
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
57
+ }
58
+ },
59
+ };
60
+ }