@defdone/annon 0.0.0-stage → 0.1.0-alpha.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.
Files changed (214) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/CLI.md +378 -0
  3. package/LICENSE +201 -0
  4. package/NOTICE +7 -0
  5. package/README.md +581 -2
  6. package/dist/api/annon-types.d.ts +108 -0
  7. package/dist/api/annon-types.js +2 -0
  8. package/dist/api/annon.d.ts +117 -0
  9. package/dist/api/annon.js +1152 -0
  10. package/dist/api/hosted-record.d.ts +13 -0
  11. package/dist/api/hosted-record.js +149 -0
  12. package/dist/api/sharing-types.d.ts +109 -0
  13. package/dist/api/sharing-types.js +1 -0
  14. package/dist/api/sharing.d.ts +36 -0
  15. package/dist/api/sharing.js +879 -0
  16. package/dist/browser/download.d.ts +19 -0
  17. package/dist/browser/download.js +52 -0
  18. package/dist/browser/index.d.ts +23 -0
  19. package/dist/browser/index.js +22 -0
  20. package/dist/browser/login-bundle.js +116 -0
  21. package/dist/browser/login.d.ts +1 -0
  22. package/dist/browser/login.js +177 -0
  23. package/dist/browser/purchase-bundle.js +84 -0
  24. package/dist/browser/purchases.d.ts +98 -0
  25. package/dist/browser/purchases.js +735 -0
  26. package/dist/browser/record.d.ts +55 -0
  27. package/dist/browser/record.js +16 -0
  28. package/dist/browser/selling.d.ts +146 -0
  29. package/dist/browser/selling.js +418 -0
  30. package/dist/browser/wallets.d.ts +53 -0
  31. package/dist/browser/wallets.js +286 -0
  32. package/dist/bytes.d.ts +4 -0
  33. package/dist/bytes.js +16 -0
  34. package/dist/download/download.d.ts +59 -0
  35. package/dist/download/download.js +384 -0
  36. package/dist/download/host.d.ts +56 -0
  37. package/dist/download/host.js +161 -0
  38. package/dist/download/http.d.ts +23 -0
  39. package/dist/download/http.js +87 -0
  40. package/dist/download/proof.d.ts +20 -0
  41. package/dist/download/proof.js +79 -0
  42. package/dist/download/upload.d.ts +130 -0
  43. package/dist/download/upload.js +408 -0
  44. package/dist/envelope/evm.d.ts +4 -0
  45. package/dist/envelope/evm.js +32 -0
  46. package/dist/envelope/pay2.d.ts +176 -0
  47. package/dist/envelope/pay2.js +541 -0
  48. package/dist/envelope/replay.d.ts +45 -0
  49. package/dist/envelope/replay.js +186 -0
  50. package/dist/envelope/stream.d.ts +78 -0
  51. package/dist/envelope/stream.js +372 -0
  52. package/dist/errors.d.ts +21 -0
  53. package/dist/errors.js +300 -0
  54. package/dist/index.d.ts +25 -0
  55. package/dist/index.js +23 -0
  56. package/dist/keys/near-ckd.d.ts +47 -0
  57. package/dist/keys/near-ckd.js +96 -0
  58. package/dist/limits.d.ts +4 -0
  59. package/dist/limits.js +4 -0
  60. package/dist/near/nep413.d.ts +22 -0
  61. package/dist/near/nep413.js +39 -0
  62. package/dist/near/rpc.d.ts +170 -0
  63. package/dist/near/rpc.js +423 -0
  64. package/dist/near/texts.d.ts +39 -0
  65. package/dist/near/texts.js +60 -0
  66. package/dist/net/ip.d.ts +7 -0
  67. package/dist/net/ip.js +62 -0
  68. package/dist/net/retry.d.ts +3 -0
  69. package/dist/net/retry.js +31 -0
  70. package/dist/node/adapter-checks.d.ts +6 -0
  71. package/dist/node/adapter-checks.js +13 -0
  72. package/dist/node/adapter.d.ts +8 -0
  73. package/dist/node/adapter.js +99 -0
  74. package/dist/node/agent.d.ts +49 -0
  75. package/dist/node/agent.js +266 -0
  76. package/dist/node/amounts.d.ts +2 -0
  77. package/dist/node/amounts.js +20 -0
  78. package/dist/node/browser-login.d.ts +18 -0
  79. package/dist/node/browser-login.js +212 -0
  80. package/dist/node/budget.d.ts +24 -0
  81. package/dist/node/budget.js +59 -0
  82. package/dist/node/buy-commands.d.ts +24 -0
  83. package/dist/node/buy-commands.js +301 -0
  84. package/dist/node/cli.d.ts +60 -0
  85. package/dist/node/cli.js +201 -0
  86. package/dist/node/command-context.d.ts +82 -0
  87. package/dist/node/command-context.js +77 -0
  88. package/dist/node/config.d.ts +37 -0
  89. package/dist/node/config.js +189 -0
  90. package/dist/node/core-adapter.d.ts +43 -0
  91. package/dist/node/core-adapter.js +122 -0
  92. package/dist/node/errors.d.ts +8 -0
  93. package/dist/node/errors.js +12 -0
  94. package/dist/node/index.d.ts +7 -0
  95. package/dist/node/index.js +5 -0
  96. package/dist/node/io.d.ts +13 -0
  97. package/dist/node/io.js +112 -0
  98. package/dist/node/keychain.d.ts +10 -0
  99. package/dist/node/keychain.js +67 -0
  100. package/dist/node/main.d.ts +2 -0
  101. package/dist/node/main.js +21 -0
  102. package/dist/node/offers.d.ts +16 -0
  103. package/dist/node/offers.js +91 -0
  104. package/dist/node/origin.d.ts +11 -0
  105. package/dist/node/origin.js +80 -0
  106. package/dist/node/paths.d.ts +3 -0
  107. package/dist/node/paths.js +71 -0
  108. package/dist/node/payment-terms.d.ts +26 -0
  109. package/dist/node/payment-terms.js +127 -0
  110. package/dist/node/purchase-commands.d.ts +16 -0
  111. package/dist/node/purchase-commands.js +237 -0
  112. package/dist/node/purchase-guard.d.ts +19 -0
  113. package/dist/node/purchase-guard.js +53 -0
  114. package/dist/node/relay-transport.d.ts +6 -0
  115. package/dist/node/relay-transport.js +173 -0
  116. package/dist/node/resources.d.ts +2 -0
  117. package/dist/node/resources.js +26 -0
  118. package/dist/node/retry-gate.d.ts +15 -0
  119. package/dist/node/retry-gate.js +53 -0
  120. package/dist/node/seller-commands.d.ts +12 -0
  121. package/dist/node/seller-commands.js +207 -0
  122. package/dist/node/sharing.d.ts +3 -0
  123. package/dist/node/sharing.js +28 -0
  124. package/dist/node/shops.d.ts +17 -0
  125. package/dist/node/shops.js +74 -0
  126. package/dist/node/signer-mock.d.ts +11 -0
  127. package/dist/node/signer-mock.js +79 -0
  128. package/dist/node/signer-wallet.d.ts +8 -0
  129. package/dist/node/signer-wallet.js +147 -0
  130. package/dist/node/signing.d.ts +29 -0
  131. package/dist/node/signing.js +200 -0
  132. package/dist/node/storage-commands.d.ts +11 -0
  133. package/dist/node/storage-commands.js +50 -0
  134. package/dist/node/sync-dir.d.ts +1 -0
  135. package/dist/node/sync-dir.js +11 -0
  136. package/dist/node/wallets.d.ts +13 -0
  137. package/dist/node/wallets.js +146 -0
  138. package/dist/oneclick/oneclick.d.ts +132 -0
  139. package/dist/oneclick/oneclick.js +342 -0
  140. package/dist/pay/backup.d.ts +70 -0
  141. package/dist/pay/backup.js +402 -0
  142. package/dist/pay/base.d.ts +135 -0
  143. package/dist/pay/base.js +393 -0
  144. package/dist/pay/eip3009.d.ts +59 -0
  145. package/dist/pay/eip3009.js +72 -0
  146. package/dist/pay/pay.d.ts +86 -0
  147. package/dist/pay/pay.js +297 -0
  148. package/dist/pay/purchase.d.ts +103 -0
  149. package/dist/pay/purchase.js +20 -0
  150. package/dist/pay/recover.d.ts +31 -0
  151. package/dist/pay/recover.js +34 -0
  152. package/dist/pay/seller.d.ts +130 -0
  153. package/dist/pay/seller.js +444 -0
  154. package/dist/pay/settle.d.ts +96 -0
  155. package/dist/pay/settle.js +308 -0
  156. package/dist/pay/split.d.ts +115 -0
  157. package/dist/pay/split.js +256 -0
  158. package/dist/pay/store-node.d.ts +8 -0
  159. package/dist/pay/store-node.js +224 -0
  160. package/dist/pay/store.d.ts +54 -0
  161. package/dist/pay/store.js +135 -0
  162. package/dist/pay/x402.d.ts +95 -0
  163. package/dist/pay/x402.js +136 -0
  164. package/dist/relay/admission.d.ts +8 -0
  165. package/dist/relay/admission.js +58 -0
  166. package/dist/relay/capabilities.d.ts +36 -0
  167. package/dist/relay/capabilities.js +232 -0
  168. package/dist/relay/fees.d.ts +17 -0
  169. package/dist/relay/fees.js +105 -0
  170. package/dist/relay/index.d.ts +10 -0
  171. package/dist/relay/index.js +8 -0
  172. package/dist/relay/keeper-balance.d.ts +17 -0
  173. package/dist/relay/keeper-balance.js +46 -0
  174. package/dist/relay/keeper-runner.d.ts +114 -0
  175. package/dist/relay/keeper-runner.js +703 -0
  176. package/dist/relay/keeper.d.ts +38 -0
  177. package/dist/relay/keeper.js +73 -0
  178. package/dist/relay/ledger.d.ts +155 -0
  179. package/dist/relay/ledger.js +392 -0
  180. package/dist/relay/relay-checks.d.ts +3 -0
  181. package/dist/relay/relay-checks.js +244 -0
  182. package/dist/relay/relay-reconciliation.d.ts +15 -0
  183. package/dist/relay/relay-reconciliation.js +293 -0
  184. package/dist/relay/relay-request.d.ts +6 -0
  185. package/dist/relay/relay-request.js +139 -0
  186. package/dist/relay/relay-transactions.d.ts +23 -0
  187. package/dist/relay/relay-transactions.js +182 -0
  188. package/dist/relay/relay-types.d.ts +128 -0
  189. package/dist/relay/relay-types.js +17 -0
  190. package/dist/relay/relay.d.ts +102 -0
  191. package/dist/relay/relay.js +426 -0
  192. package/dist/relay/reservation.d.ts +29 -0
  193. package/dist/relay/reservation.js +132 -0
  194. package/dist/relay/sponsorship.d.ts +42 -0
  195. package/dist/relay/sponsorship.js +184 -0
  196. package/dist/routes.d.ts +2 -0
  197. package/dist/routes.js +1 -0
  198. package/dist/settle-near/codec.d.ts +17 -0
  199. package/dist/settle-near/codec.js +86 -0
  200. package/dist/settle-near/identity.d.ts +27 -0
  201. package/dist/settle-near/identity.js +80 -0
  202. package/dist/settle-near/purchase.d.ts +40 -0
  203. package/dist/settle-near/purchase.js +153 -0
  204. package/dist/settle-near/terms.d.ts +37 -0
  205. package/dist/settle-near/terms.js +89 -0
  206. package/dist/shops/discovery.d.ts +95 -0
  207. package/dist/shops/discovery.js +384 -0
  208. package/dist/wallet/account-only.d.ts +11 -0
  209. package/dist/wallet/account-only.js +6 -0
  210. package/dist/wallet/uncertain.d.ts +8 -0
  211. package/dist/wallet/uncertain.js +13 -0
  212. package/dist/wallet/wallet.d.ts +100 -0
  213. package/dist/wallet/wallet.js +204 -0
  214. package/package.json +135 -3
@@ -0,0 +1,60 @@
1
+ import type { BuyRoute } from '../routes.js';
2
+ export type Command = 'shops' | 'shop' | 'catalog' | 'fund' | 'budget' | 'share' | 'sell' | 'buy' | 'open' | 'recover' | 'stop' | 'verify' | 'status' | 'login' | 'init';
3
+ export interface Input {
4
+ command: Command;
5
+ shop?: string;
6
+ id?: string;
7
+ search?: string;
8
+ limit?: number;
9
+ offset?: number;
10
+ storageBudget?: string;
11
+ listingStorage?: string;
12
+ title?: string;
13
+ description?: string;
14
+ fundAmount?: string;
15
+ agent?: boolean;
16
+ noOpen?: boolean;
17
+ signer?: string;
18
+ credentialEnv?: string;
19
+ credentialKeychain?: string;
20
+ keyEnv?: string;
21
+ keychainEntry?: string;
22
+ newKey?: boolean;
23
+ dailyBudget?: string;
24
+ target?: string;
25
+ json?: boolean;
26
+ config?: string;
27
+ contractId?: string;
28
+ nearRpc?: string;
29
+ baseRpc?: string;
30
+ network?: string;
31
+ stateDir?: string;
32
+ relayer?: string;
33
+ trustContract?: boolean;
34
+ wallet?: 'evm' | 'solana' | 'near';
35
+ evmKeyFile?: string;
36
+ solanaKeyFile?: string;
37
+ nearKeyFile?: string;
38
+ nearAccount?: string;
39
+ nearPublicKey?: string;
40
+ backupSecretFile?: string;
41
+ resume?: string;
42
+ origin?: string;
43
+ price?: string;
44
+ split?: string;
45
+ base?: boolean;
46
+ force?: boolean;
47
+ moderator?: string;
48
+ route?: BuyRoute;
49
+ fromChain?: string;
50
+ fromAsset?: string;
51
+ refundTo?: string;
52
+ recovery?: string;
53
+ maxPrice?: string;
54
+ purchase?: string;
55
+ file?: string;
56
+ url?: string;
57
+ output?: string;
58
+ }
59
+ export declare function parseCli(argv: string[]): Input;
60
+ export declare function runCli(argv: string[], execute: (input: Input) => Promise<unknown>, write: (text: string) => void): Promise<number>;
@@ -0,0 +1,201 @@
1
+ import { parseArgs } from 'node:util';
2
+ import { publicError, usage } from './errors.js';
3
+ const common = [
4
+ 'no-open',
5
+ 'json',
6
+ 'config',
7
+ 'contract-id',
8
+ 'near-rpc',
9
+ 'base-rpc',
10
+ 'network',
11
+ 'state-dir',
12
+ 'relayer',
13
+ 'trust-contract',
14
+ 'wallet',
15
+ 'evm-key-file',
16
+ 'solana-key-file',
17
+ 'near-key-file',
18
+ 'near-account',
19
+ 'near-public-key',
20
+ 'backup-secret-file',
21
+ ];
22
+ const perCommand = {
23
+ shops: ['search', 'limit', 'offset'],
24
+ shop: ['id'],
25
+ catalog: ['shop', 'search', 'limit', 'offset'],
26
+ fund: [],
27
+ budget: [],
28
+ login: [],
29
+ init: [
30
+ 'signer',
31
+ 'credential-env',
32
+ 'credential-keychain',
33
+ 'agent',
34
+ 'recovery',
35
+ 'key-env',
36
+ 'keychain-entry',
37
+ 'new-key',
38
+ 'daily-budget',
39
+ ],
40
+ share: ['resume', 'price', 'shop', 'title', 'description', 'storage-budget', 'listing-storage'],
41
+ sell: ['resume', 'price', 'split', 'base', 'moderator', 'output', 'storage-budget'],
42
+ buy: [
43
+ 'force',
44
+ 'origin',
45
+ 'route',
46
+ 'from-chain',
47
+ 'from-asset',
48
+ 'refund-to',
49
+ 'recovery',
50
+ 'max-price',
51
+ 'moderator',
52
+ 'url',
53
+ ],
54
+ open: ['purchase', 'file', 'url', 'output'],
55
+ recover: ['recovery', 'purchase'],
56
+ stop: [],
57
+ verify: [],
58
+ status: [],
59
+ };
60
+ export function parseCli(argv) {
61
+ try {
62
+ const keys = [...new Set([...common, ...Object.values(perCommand).flat()])];
63
+ const booleans = ['json', 'trust-contract', 'base', 'agent', 'no-open', 'new-key', 'force'];
64
+ const parsed = parseArgs({
65
+ args: argv,
66
+ allowPositionals: true,
67
+ strict: true,
68
+ options: Object.fromEntries(keys.map((k) => [k, { type: booleans.includes(k) ? 'boolean' : 'string' }])),
69
+ });
70
+ const [command, target, ...extra] = parsed.positionals;
71
+ if (!command ||
72
+ !(command in perCommand) ||
73
+ (command === 'fund' || command === 'shop' ? extra.length !== 1 : extra.length !== 0))
74
+ throw usage();
75
+ const cmd = command;
76
+ if ((['recover', 'login', 'init', 'shops', 'catalog'].includes(cmd) && target !== undefined) ||
77
+ (!['recover', 'login', 'init', 'shops', 'catalog'].includes(cmd) && !target))
78
+ throw usage();
79
+ if (cmd === 'shop' && target !== 'add')
80
+ throw usage();
81
+ const allowed = [...common, ...perCommand[cmd]];
82
+ if (Object.keys(parsed.values).some((k) => !allowed.includes(k)))
83
+ throw usage();
84
+ const values = Object.fromEntries(Object.entries(parsed.values).map(([k, v]) => [
85
+ k.replace(/-([a-z])/g, (_, c) => c.toUpperCase()),
86
+ v,
87
+ ]));
88
+ for (const key of ['limit', 'offset']) {
89
+ if (values[key] !== undefined) {
90
+ if (typeof values[key] !== 'string' || !/^(0|[1-9][0-9]*)$/.test(values[key]))
91
+ throw usage();
92
+ const n = Number(values[key]);
93
+ if (!Number.isSafeInteger(n) ||
94
+ n < (key === 'limit' ? 1 : 0) ||
95
+ (key === 'limit' && n > 100))
96
+ throw usage();
97
+ values[key] = n;
98
+ }
99
+ }
100
+ if (cmd === 'share') {
101
+ if (values.resume
102
+ ? !/^0x[0-9a-f]{64}$/.test(String(values.resume)) ||
103
+ ['price', 'title', 'description', 'storageBudget', 'listingStorage'].some((k) => values[k] !== undefined)
104
+ : !values.price)
105
+ throw usage();
106
+ }
107
+ if (cmd === 'sell') {
108
+ if (values.resume) {
109
+ if (!/^0x[0-9a-f]{64}$/.test(String(values.resume)) ||
110
+ values.price ||
111
+ values.split ||
112
+ values.base ||
113
+ values.moderator ||
114
+ values.storageBudget)
115
+ throw usage();
116
+ }
117
+ else if (!values.price || !values.split)
118
+ throw usage();
119
+ }
120
+ if (cmd === 'init' && values.agent !== true)
121
+ throw usage();
122
+ if (cmd === 'recover' && !values.recovery)
123
+ throw usage();
124
+ if (values.route && !['near', 'base', '1click'].includes(String(values.route)))
125
+ throw usage();
126
+ if (values.wallet && !['evm', 'solana', 'near'].includes(String(values.wallet)))
127
+ throw usage();
128
+ return {
129
+ command: cmd,
130
+ ...(target === undefined ? {} : { target: cmd === 'shop' ? extra[0] : target }),
131
+ ...(cmd === 'fund' ? { fundAmount: extra[0] } : {}),
132
+ ...values,
133
+ };
134
+ }
135
+ catch {
136
+ throw usage();
137
+ }
138
+ }
139
+ const HELP = `annon <command> [options]
140
+ shops [--search <q>] [--limit <n>] [--offset <n>]
141
+ shop add <url> [--id <alias>]
142
+ catalog [--shop <id>] [--search <q>] [--limit <n>] [--offset <n>]
143
+ login [--wallet evm|near] [--no-open]
144
+ init --agent --daily-budget <USDC> [--recovery <kind:id>] [--key-env <name> | --keychain-entry <name> | --new-key | --signer <https origin> (--credential-env <name> | --credential-keychain <name>)]
145
+ share <file> --price <USDC> [--shop <id>] [--title <title>] [--description <text>] [--listing-storage <NEAR>] [--storage-budget <NEAR>]
146
+ share <file> --resume <file-id> [--shop <id>]
147
+ sell <file> --resume <file-id> [--output <PAY2>]
148
+ fund <file-id> <NEAR>
149
+ budget <file-id>
150
+ sell <file> [--storage-budget <NEAR>] --price <USDC> --split <kind:id:bps,...> [--base] [--moderator <account>] [--output <PAY2>]
151
+ buy <url|offer.json> [--route near|base|1click] [--origin <configured name>] [--from-chain <chain>] [--from-asset <1Click asset>] [--refund-to <address>] [--recovery <kind:id>] [--max-price <USDC> or configured cap] [--url <expected resource for offer.json>] [--moderator <trusted account>]
152
+ open <purchase-id|backup|PAY2> [--purchase <id|backup>] [--file <PAY2> | --url <url>] --output <file>
153
+ recover --recovery <kind:id> [--purchase <id>]
154
+ stop <file-id>
155
+ verify <PAY2|url>
156
+ status <purchase-id>
157
+ Common: --json --config <file> --contract-id <account> --near-rpc <url> --base-rpc <url>
158
+ --network <near:mainnet> --state-dir <dir> --relayer <url> --trust-contract
159
+ --wallet evm|solana|near --evm-key-file <file> --solana-key-file <file> --near-key-file <file>
160
+ --near-account <account> --near-public-key <ed25519:key> --backup-secret-file <file>
161
+ Secrets: env ANNON_EVM_PRIVATE_KEY, ANNON_SOLANA_KEYPAIR, ANNON_NEAR_CREDENTIALS,
162
+ ANNON_BACKUP_SECRET (64 hex); or explicitly named files. See packages/annon/CLI.md.
163
+ Exit: 0 success (buy may need deposit), 1 operation failed, 2 arguments/config, 3 trust required, 4 purchase pending.
164
+ `;
165
+ export async function runCli(argv, execute, write) {
166
+ const json = argv.includes('--json') || argv.includes('--json=true');
167
+ if (argv.length === 0 || argv.includes('--help')) {
168
+ write(HELP);
169
+ return 0;
170
+ }
171
+ try {
172
+ const input = parseCli(argv);
173
+ const result = await execute(input);
174
+ if (!json && ['shops', 'catalog', 'share'].includes(input.command)) {
175
+ const show = (value) => {
176
+ const r = value;
177
+ const rules = r.info?.rules ?? r.rules;
178
+ if (rules)
179
+ write(`Shop rules (${r.shop?.manifest ?? r.manifest}):\n${rules.text}`);
180
+ };
181
+ show(result);
182
+ const items = result.items;
183
+ if (Array.isArray(items))
184
+ for (const item of items)
185
+ show(item);
186
+ }
187
+ write(JSON.stringify({ ok: true, result }, null, json ? undefined : 2));
188
+ return input.command === 'status' &&
189
+ result &&
190
+ typeof result === 'object' &&
191
+ 'state' in result &&
192
+ result.state === 'pending'
193
+ ? 4
194
+ : 0;
195
+ }
196
+ catch (error) {
197
+ const safe = publicError(error);
198
+ write(JSON.stringify({ ok: false, error: safe }, null, json ? undefined : 2));
199
+ return safe.exitCode;
200
+ }
201
+ }
@@ -0,0 +1,82 @@
1
+ /** What one CLI command run shares across the command modules: input, config, wallet and stores. */
2
+ import type { Annon, AnnonOptions } from '../api/annon.js';
3
+ import { getFile, type getPurchase } from '../near/rpc.js';
4
+ import type { EncryptedRecord } from '../pay/backup.js';
5
+ import type { PaymentSummary, PurchaseRecord } from '../pay/purchase.js';
6
+ import type { Store } from '../pay/store.js';
7
+ import type { startBrowserSession } from './browser-login.js';
8
+ import type { Input } from './cli.js';
9
+ import type { Config, TrustPrompt } from './config.js';
10
+ import type { Keychain } from './keychain.js';
11
+ import { type Environment, type LocalWallet, loadWallet } from './wallets.js';
12
+ export interface Dependencies {
13
+ /** Server-owned policy; never taken from tool arguments. */
14
+ agent?: boolean;
15
+ keychain?: Keychain;
16
+ browser?: typeof startBrowserSession;
17
+ paymentTerms?: (terms: unknown) => void;
18
+ browserApprovals?: boolean;
19
+ sdk?: (options: AnnonOptions) => Pick<Annon, 'fund' | 'sell' | 'buy' | 'open' | 'openTo' | 'recover' | 'stop' | 'store' | 'accept' | 'resumeSell'> & Partial<Pick<Annon, 'share' | 'resumeShare'>>;
20
+ wallet?: typeof loadWallet;
21
+ file?: typeof getFile;
22
+ purchase?: typeof getPurchase;
23
+ fetch?: typeof fetch;
24
+ prompt?: TrustPrompt;
25
+ notify?: (message: string) => void;
26
+ wait?: (ms: number) => Promise<void>;
27
+ }
28
+ interface Resource {
29
+ id: string;
30
+ revision: number;
31
+ contractId: string;
32
+ header: string;
33
+ url?: string;
34
+ }
35
+ type AdapterSdk = ReturnType<NonNullable<Dependencies['sdk']>>;
36
+ /** One command run: its input, the resolved config and the injected dependencies. */
37
+ interface RequestContext {
38
+ env: Environment;
39
+ d: Dependencies;
40
+ fetcher: typeof fetch;
41
+ wait: (ms: number) => Promise<void>;
42
+ o: Input;
43
+ c: Config;
44
+ /** Set by `fund` and `wallet()`; the relay transport signs with it when it is a NEAR wallet. */
45
+ selected: LocalWallet | undefined;
46
+ readListing: (fileId: string) => ReturnType<typeof readListing>;
47
+ paymentDetails: (load: () => Promise<PurchaseRecord | null | undefined>) => ReturnType<typeof paymentDetails>;
48
+ pendingSetup: (purchaseId: string) => ReturnType<typeof pendingSetup>;
49
+ wallet: () => Promise<LocalWallet>;
50
+ }
51
+ /** A command run once the backup secret, the local stores and the SDK are ready. */
52
+ interface CommandSession extends RequestContext {
53
+ secret: Uint8Array;
54
+ storeDir: string;
55
+ rawStore: Store<EncryptedRecord>;
56
+ resources: Store<Resource>;
57
+ sdk: AdapterSdk;
58
+ }
59
+ /** The helpers close over the returned object, so they all see the same selected wallet. */
60
+ declare function requestContext(env: Environment, d: Dependencies, fetcher: typeof fetch, wait: (ms: number) => Promise<void>, o: Input, c: Config): RequestContext;
61
+ /** The registered file and the listing its header must match, for the configured deployment. */
62
+ declare function readListing(ctx: RequestContext, fileId: string): Promise<{
63
+ f: import("../near/rpc.js").FileInfo;
64
+ listing: {
65
+ publishedHeaderHash?: string;
66
+ fileId: string;
67
+ cipherHash: string;
68
+ seller: import("../index.js").Identity;
69
+ };
70
+ }>;
71
+ /** Payment summary of a saved purchase, or nothing when the record is missing or unreadable. */
72
+ declare function paymentDetails(ctx: RequestContext, load: () => Promise<PurchaseRecord | null | undefined>): Promise<{
73
+ route?: PaymentSummary['route'];
74
+ payment?: PaymentSummary;
75
+ }>;
76
+ /** A purchase whose acceptance cannot be checked without saved state and the backup secret. */
77
+ declare function pendingSetup(ctx: RequestContext, purchaseId: string): Promise<{
78
+ purchaseId: string;
79
+ state: string;
80
+ next: string;
81
+ }>;
82
+ export { type AdapterSdk, type CommandSession, type RequestContext, type Resource, requestContext };
@@ -0,0 +1,77 @@
1
+ import { getFile } from '../near/rpc.js';
2
+ import { fail } from './adapter-checks.js';
3
+ import { savedPayment } from './payment-terms.js';
4
+ import { retryGate } from './retry-gate.js';
5
+ import { loadWallet } from './wallets.js';
6
+ /** The helpers close over the returned object, so they all see the same selected wallet. */
7
+ function requestContext(env, d, fetcher, wait, o, c) {
8
+ const ctx = {
9
+ env,
10
+ d,
11
+ fetcher,
12
+ wait,
13
+ o,
14
+ c,
15
+ selected: undefined,
16
+ readListing: (fileId) => readListing(ctx, fileId),
17
+ paymentDetails: (load) => paymentDetails(ctx, load),
18
+ pendingSetup: (purchaseId) => pendingSetup(ctx, purchaseId),
19
+ wallet: () => selectWallet(ctx),
20
+ };
21
+ return ctx;
22
+ }
23
+ /** The registered file and the listing its header must match, for the configured deployment. */
24
+ async function readListing(ctx, fileId) {
25
+ const { c, d } = ctx;
26
+ const f = await (d.file ?? getFile)(c.nearRpc, c.contractId, fileId);
27
+ if (!f)
28
+ return fail('unknown_file', 'No registered file exists at this contract.');
29
+ if (f.takenDown)
30
+ return fail('taken_down', 'The file has been taken down.');
31
+ if (f.terms.network !== c.network || f.terms.contract !== c.contractId)
32
+ throw new Error('deployment mismatch');
33
+ return {
34
+ f,
35
+ listing: {
36
+ fileId,
37
+ cipherHash: f.cipherHash,
38
+ seller: f.seller,
39
+ ...(f.headerHash ? { publishedHeaderHash: f.headerHash } : {}),
40
+ },
41
+ };
42
+ }
43
+ /** Payment summary of a saved purchase, or nothing when the record is missing or unreadable. */
44
+ async function paymentDetails(ctx, load) {
45
+ const { c } = ctx;
46
+ try {
47
+ const record = await load();
48
+ if (!record ||
49
+ record.purchase.contract !== c.contractId ||
50
+ record.terms.contractId !== c.contractId)
51
+ return {};
52
+ const payment = savedPayment(record);
53
+ return { route: payment.route, payment };
54
+ }
55
+ catch {
56
+ // Optional reporting must not block an accepted purchase or a successful open.
57
+ return {};
58
+ }
59
+ }
60
+ /** A purchase whose acceptance cannot be checked without saved state and the backup secret. */
61
+ async function pendingSetup(ctx, purchaseId) {
62
+ const { c, d } = ctx;
63
+ if (d.agent)
64
+ await retryGate({ ...c, purchaseId }).defer('settlement_pending', 30);
65
+ return {
66
+ purchaseId,
67
+ state: 'pending',
68
+ next: 'Check the payment; configure readable saved state and the matching backup secret to resume Base unlock. 1Click SUCCESS alone is not acceptance.',
69
+ };
70
+ }
71
+ /** Loads the command's wallet once; later calls reuse it. */
72
+ async function selectWallet(ctx) {
73
+ const { c, d, env, o } = ctx;
74
+ ctx.selected ??= await (d.wallet ?? loadWallet)({ ...o, network: c.network }, env, c.nearRpc);
75
+ return ctx.selected;
76
+ }
77
+ export { requestContext };
@@ -0,0 +1,37 @@
1
+ import { type DiscoveryConfig } from '../shops/discovery.js';
2
+ import { type SignerConfig } from './agent.js';
3
+ import type { Input } from './cli.js';
4
+ import type { Environment } from './wallets.js';
5
+ export interface Config extends DiscoveryConfig {
6
+ signer?: SignerConfig;
7
+ dailyBudget?: string;
8
+ agent?: boolean;
9
+ contractId: string;
10
+ nearRpc: string;
11
+ baseRpc: string;
12
+ network: string;
13
+ stateDir: string;
14
+ workDir?: string;
15
+ relayer?: string;
16
+ maxPrice?: string;
17
+ trustContract?: boolean;
18
+ mpcKey?: string;
19
+ moderators?: string[];
20
+ origins?: Record<string, {
21
+ fromChain: string;
22
+ fromAsset: string;
23
+ refundTo?: string;
24
+ maxSourceAmount?: string;
25
+ usdcDecimals?: number;
26
+ }>;
27
+ allowedResources?: string[];
28
+ storageBudget?: string;
29
+ listingStorage?: string;
30
+ maxProtocolFee?: {
31
+ fixed: string;
32
+ bps: number;
33
+ };
34
+ }
35
+ export declare function loadConfig(o: Input, env: Environment, login?: boolean, forcedAgent?: boolean): Promise<Config>;
36
+ export type TrustPrompt = (description: string) => Promise<boolean>;
37
+ export declare function trustContract(c: Config, explicit: boolean, prompt?: TrustPrompt): Promise<void>;
@@ -0,0 +1,189 @@
1
+ import { mkdir, readFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { join, resolve } from 'node:path';
4
+ import { secureUrl } from '../download/download.js';
5
+ import { fileStore } from '../pay/store-node.js';
6
+ import { account, network } from '../settle-near/codec.js';
7
+ import { validateDiscoveryConfig } from '../shops/discovery.js';
8
+ import { agentPolicy, configPath, validateSigner } from './agent.js';
9
+ import { AdapterError } from './errors.js';
10
+ import { resourceAllowed } from './resources.js';
11
+ export async function loadConfig(o, env, login = false, forcedAgent = false) {
12
+ const pinnedAgent = await agentPolicy(o, env, forcedAgent);
13
+ try {
14
+ let file = {};
15
+ const path = configPath(o, env);
16
+ try {
17
+ file = JSON.parse(await readFile(path, 'utf8'));
18
+ }
19
+ catch (error) {
20
+ if (error.code !== 'ENOENT')
21
+ throw error;
22
+ }
23
+ const agent = pinnedAgent || file.agent === true;
24
+ if (agent &&
25
+ (typeof file.maxPrice !== 'string' || !/^(0|[1-9][0-9]*)(\.[0-9]{1,6})?$/.test(file.maxPrice)))
26
+ throw new AdapterError('max_price_required', 'Agent mode requires maxPrice in the pinned config.', 2);
27
+ const policyEnv = agent && env.ANNON_AGENT !== 'true' ? {} : env;
28
+ const c = {
29
+ contractId: o.contractId ?? policyEnv.ANNON_CONTRACT_ID ?? file.contractId ?? '',
30
+ nearRpc: o.nearRpc ??
31
+ policyEnv.ANNON_NEAR_RPC ??
32
+ file.nearRpc ??
33
+ (login
34
+ ? (o.network ?? policyEnv.ANNON_NETWORK ?? file.network ?? 'near:mainnet') ===
35
+ 'near:testnet'
36
+ ? 'https://rpc.testnet.fastnear.com'
37
+ : 'https://rpc.mainnet.fastnear.com'
38
+ : ''),
39
+ baseRpc: o.baseRpc ?? policyEnv.ANNON_BASE_RPC ?? file.baseRpc ?? '',
40
+ network: o.network ?? policyEnv.ANNON_NETWORK ?? file.network ?? 'near:mainnet',
41
+ stateDir: o.stateDir ??
42
+ (agent ? undefined : env.ANNON_STATE_DIR) ??
43
+ file.stateDir ??
44
+ join(homedir(), '.local', 'share', 'annon'),
45
+ };
46
+ Object.assign(c, validateDiscoveryConfig(file));
47
+ const workDir = (env.ANNON_AGENT === 'true' ? env.ANNON_WORK_DIR : undefined) ?? file.workDir;
48
+ if (agent && (typeof workDir !== 'string' || !workDir.trim()))
49
+ throw new AdapterError('work_dir_required', 'Agent mode requires an operator-configured workDir.', 2);
50
+ if (workDir !== undefined) {
51
+ if (typeof workDir !== 'string' || !workDir.trim())
52
+ throw new Error('invalid working directory');
53
+ c.workDir = workDir;
54
+ }
55
+ if (file.maxProtocolFee !== undefined) {
56
+ const fee = file.maxProtocolFee;
57
+ if (!fee ||
58
+ typeof fee.fixed !== 'string' ||
59
+ !/^(0|[1-9][0-9]*)$/.test(fee.fixed) ||
60
+ !Number.isSafeInteger(fee.bps) ||
61
+ fee.bps < 0 ||
62
+ fee.bps >= 10000)
63
+ throw new Error('invalid protocol fee ceiling');
64
+ c.maxProtocolFee = fee;
65
+ }
66
+ if (file.listingStorage !== undefined)
67
+ c.listingStorage = file.listingStorage;
68
+ if (file.storageBudget !== undefined)
69
+ c.storageBudget = file.storageBudget;
70
+ if (file.signer)
71
+ c.signer = validateSigner(file.signer);
72
+ if (file.dailyBudget !== undefined) {
73
+ if (typeof file.dailyBudget !== 'string' ||
74
+ !/^(0|[1-9][0-9]*)(\.[0-9]{1,6})?$/.test(file.dailyBudget))
75
+ throw new Error('invalid daily budget');
76
+ c.dailyBudget = file.dailyBudget;
77
+ }
78
+ c.agent = pinnedAgent || file.agent === true;
79
+ if (file.agent === true && resolve(path) !== resolve(configPath({}, env)))
80
+ throw new AdapterError('agent_policy', 'Agent configs must be pinned by the launcher environment.', 2);
81
+ const origins = policyEnv.ANNON_ORIGINS ? JSON.parse(policyEnv.ANNON_ORIGINS) : file.origins;
82
+ if (origins) {
83
+ if (typeof origins !== 'object' || Array.isArray(origins))
84
+ throw new Error('invalid origins');
85
+ for (const value of Object.values(origins)) {
86
+ if (!value ||
87
+ typeof value.fromChain !== 'string' ||
88
+ !value.fromChain ||
89
+ typeof value.fromAsset !== 'string' ||
90
+ !value.fromAsset.startsWith('nep141:') ||
91
+ (value.maxSourceAmount !== undefined &&
92
+ (typeof value.maxSourceAmount !== 'string' ||
93
+ !/^[1-9][0-9]{0,38}$/.test(value.maxSourceAmount) ||
94
+ BigInt(value.maxSourceAmount) >= 2n ** 128n)) ||
95
+ (value.usdcDecimals !== undefined &&
96
+ (!Number.isSafeInteger(value.usdcDecimals) ||
97
+ value.usdcDecimals < 0 ||
98
+ value.usdcDecimals > 255)) ||
99
+ (value.refundTo !== undefined && (typeof value.refundTo !== 'string' || !value.refundTo)))
100
+ throw new Error('invalid origin');
101
+ if (agent && value.usdcDecimals === undefined && value.maxSourceAmount === undefined)
102
+ throw new AdapterError('origin_not_allowed', 'Configure usdcDecimals or maxSourceAmount for every origin.', 2);
103
+ }
104
+ c.origins = origins;
105
+ }
106
+ const envResources = env.ANNON_ALLOWED_RESOURCES?.split(',');
107
+ for (const list of [file.allowedResources, envResources]) {
108
+ if (list !== undefined) {
109
+ if (!Array.isArray(list))
110
+ throw new Error('invalid allowed resources');
111
+ for (const url of list)
112
+ secureUrl(url);
113
+ }
114
+ }
115
+ const allowedResources = agent
116
+ ? file.allowedResources === undefined
117
+ ? envResources
118
+ : envResources === undefined
119
+ ? file.allowedResources
120
+ : Array.from(new Set(file.allowedResources.flatMap((rule) => envResources
121
+ .filter((other) => resourceAllowed([rule], other))
122
+ .concat(envResources.some((other) => resourceAllowed([other], rule)) ? [rule] : []))))
123
+ : (envResources ?? file.allowedResources);
124
+ if (allowedResources !== undefined)
125
+ c.allowedResources = allowedResources;
126
+ const envMax = env.ANNON_MAX_PRICE;
127
+ const units = (value) => {
128
+ if (!/^(0|[1-9][0-9]*)(\.[0-9]{1,6})?$/.test(value))
129
+ throw new Error('invalid price cap');
130
+ const [whole = '', fraction = ''] = value.split('.');
131
+ return BigInt(whole) * 1000000n + BigInt(fraction.padEnd(6, '0'));
132
+ };
133
+ const maxPrice = agent && envMax !== undefined && file.maxPrice !== undefined
134
+ ? units(envMax) < units(file.maxPrice)
135
+ ? envMax
136
+ : file.maxPrice
137
+ : (envMax ?? file.maxPrice);
138
+ if (maxPrice !== undefined)
139
+ c.maxPrice = maxPrice;
140
+ c.trustContract =
141
+ policyEnv.ANNON_TRUST_CONTRACT === undefined
142
+ ? file.trustContract === true
143
+ : policyEnv.ANNON_TRUST_CONTRACT === 'true';
144
+ const moderators = policyEnv.ANNON_MODERATORS?.split(',') ?? file.moderators;
145
+ if (moderators) {
146
+ for (const moderator of moderators)
147
+ account(moderator);
148
+ c.moderators = moderators;
149
+ }
150
+ const mpcKey = policyEnv.ANNON_MPC_KEY ?? file.mpcKey;
151
+ if (mpcKey)
152
+ c.mpcKey = mpcKey;
153
+ const relayer = o.relayer ?? policyEnv.ANNON_RELAYER ?? file.relayer;
154
+ if (relayer)
155
+ c.relayer = relayer;
156
+ if (!login || c.contractId)
157
+ account(c.contractId);
158
+ network(c.network);
159
+ for (const endpoint of [
160
+ c.nearRpc,
161
+ ...(c.baseRpc ? [c.baseRpc] : []),
162
+ ...(c.relayer ? [c.relayer] : []),
163
+ ]) {
164
+ const url = secureUrl(endpoint);
165
+ if (url.username || url.password || url.search || url.hash)
166
+ throw new Error('endpoint credentials must not appear in prompts');
167
+ }
168
+ if (!c.stateDir || typeof c.stateDir !== 'string')
169
+ throw new Error('missing state directory');
170
+ return c;
171
+ }
172
+ catch (error) {
173
+ if (error instanceof AdapterError)
174
+ throw error;
175
+ throw new AdapterError('invalid_config', 'Set contractId, network and nearRpc (plus baseRpc for Base operations) in the config file, environment or flags. Endpoints must use HTTPS (HTTP only on localhost).', 2);
176
+ }
177
+ }
178
+ export async function trustContract(c, explicit, prompt) {
179
+ const dir = join(c.stateDir, 'trust');
180
+ await mkdir(dir, { recursive: true, mode: 0o700 });
181
+ const store = fileStore(dir);
182
+ const id = JSON.stringify([c.network, c.nearRpc, c.contractId]);
183
+ if (await store.load(id))
184
+ return;
185
+ if (!explicit &&
186
+ !(await prompt?.(`Trust contract ${c.contractId} on ${c.network} via ${c.nearRpc}? This contract controls file key release. Type yes: `)))
187
+ throw new AdapterError('trust_required', 'Review the configured deployment, then use --trust-contract or trustContract: true. Interactive CLI users can answer the first-use prompt.', 3);
188
+ await store.compareAndSet(id, null, { id, revision: 0 });
189
+ }