@defdone/annon 0.1.0-alpha.1 → 0.1.0-alpha.3
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.
- package/CHANGELOG.md +26 -0
- package/CLI.md +173 -13
- package/README.md +47 -10
- package/dist/api/annon-types.d.ts +2 -1
- package/dist/api/annon.js +9 -2
- package/dist/browser/index.d.ts +1 -1
- package/dist/browser/login-bundle.js +61 -60
- package/dist/browser/login-dom.d.ts +13 -0
- package/dist/browser/login-dom.js +71 -0
- package/dist/browser/login-dropdown.d.ts +32 -0
- package/dist/browser/login-dropdown.js +129 -0
- package/dist/browser/login-format.d.ts +16 -0
- package/dist/browser/login-format.js +52 -0
- package/dist/browser/login-status.d.ts +70 -0
- package/dist/browser/login-status.js +85 -0
- package/dist/browser/login-steps.d.ts +6 -0
- package/dist/browser/login-steps.js +35 -0
- package/dist/browser/login-terms.d.ts +10 -0
- package/dist/browser/login-terms.js +69 -0
- package/dist/browser/login-view.d.ts +34 -0
- package/dist/browser/login-view.js +227 -0
- package/dist/browser/login-wallet.d.ts +28 -0
- package/dist/browser/login-wallet.js +179 -0
- package/dist/browser/login.js +154 -142
- package/dist/browser/purchase-bundle.js +29 -29
- package/dist/browser/purchases.d.ts +4 -0
- package/dist/browser/purchases.js +14 -0
- package/dist/browser/reconcile.d.ts +41 -0
- package/dist/browser/reconcile.js +77 -0
- package/dist/node/adapter-checks.d.ts +3 -1
- package/dist/node/adapter-checks.js +3 -1
- package/dist/node/adapter.js +10 -3
- package/dist/node/agent.js +2 -1
- package/dist/node/approval-status.d.ts +34 -0
- package/dist/node/approval-status.js +71 -0
- package/dist/node/backup-secret-init.d.ts +9 -0
- package/dist/node/backup-secret-init.js +103 -0
- package/dist/node/backup-secret.d.ts +35 -0
- package/dist/node/backup-secret.js +104 -0
- package/dist/node/browser-login.d.ts +2 -0
- package/dist/node/browser-login.js +95 -16
- package/dist/node/buy-commands.js +17 -9
- package/dist/node/buy-flow.d.ts +18 -0
- package/dist/node/buy-flow.js +89 -0
- package/dist/node/cli-flags.d.ts +10 -0
- package/dist/node/cli-flags.js +68 -0
- package/dist/node/cli.d.ts +9 -1
- package/dist/node/cli.js +87 -95
- package/dist/node/command-context.js +1 -1
- package/dist/node/config.d.ts +4 -0
- package/dist/node/config.js +6 -2
- package/dist/node/core-adapter.js +4 -0
- package/dist/node/login-page.d.ts +7 -0
- package/dist/node/login-page.js +77 -0
- package/dist/node/main.js +20 -2
- package/dist/node/output/discovery.d.ts +23 -0
- package/dist/node/output/discovery.js +52 -0
- package/dist/node/output/human.d.ts +10 -0
- package/dist/node/output/human.js +46 -0
- package/dist/node/output/purchase.d.ts +10 -0
- package/dist/node/output/purchase.js +78 -0
- package/dist/node/output/text.d.ts +10 -0
- package/dist/node/output/text.js +43 -0
- package/dist/node/purchase-commands.d.ts +2 -2
- package/dist/node/purchase-commands.js +14 -6
- package/dist/node/purchase-ref.d.ts +15 -0
- package/dist/node/purchase-ref.js +93 -0
- package/dist/node/saved-state.d.ts +4 -0
- package/dist/node/saved-state.js +50 -0
- package/dist/node/shell/complete.d.ts +2 -0
- package/dist/node/shell/complete.js +17 -0
- package/dist/node/shell/session.d.ts +20 -0
- package/dist/node/shell/session.js +219 -0
- package/dist/node/shell/welcome.d.ts +9 -0
- package/dist/node/shell/welcome.js +39 -0
- package/dist/node/shell/words.d.ts +1 -0
- package/dist/node/shell/words.js +36 -0
- package/dist/node/shops.d.ts +5 -0
- package/dist/node/shops.js +22 -0
- package/dist/node/version.d.ts +1 -0
- package/dist/node/version.js +3 -0
- package/dist/node/wallets.d.ts +1 -1
- package/dist/node/wallets.js +2 -10
- package/dist/pay/purchase.d.ts +2 -1
- package/dist/wallet/uncertain.d.ts +6 -3
- package/dist/wallet/uncertain.js +4 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# @defdone/annon
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- edb0ec2: Bound approval session shutdown and resume polling after accepted approvals. Keep completed review snapshots read only and clarify that the tab should stay open.
|
|
8
|
+
- 5d871b7: Keep accepted browser approvals successful after session shutdown and show completed approvals as read-only collapsible steps.
|
|
9
|
+
- f607080: The CLI is easier to use by hand. `annon` with no arguments in a terminal (or `annon shell`) starts
|
|
10
|
+
an interactive session that runs the usual commands without the prefix until `exit`. In a terminal
|
|
11
|
+
the commands print short text in place of JSON: a login line, a numbered catalog table, the buy
|
|
12
|
+
terms and next step, one or two lines for status and open. `--json`, piped output and agent mode
|
|
13
|
+
keep the JSON envelope unchanged.
|
|
14
|
+
|
|
15
|
+
`annon buy` takes a catalog row number (`buy 2 --shop demo`) and `--open <file>`, which waits for
|
|
16
|
+
acceptance and writes the file. `annon status` and `annon open` without an ID use the latest
|
|
17
|
+
purchase saved for the configured contract and network, and accept an ID prefix of 8 or more hex
|
|
18
|
+
digits for purchases saved for it. The `next` text of a NEAR or Base buy no
|
|
19
|
+
longer talks about a deposit.
|
|
20
|
+
|
|
21
|
+
## 0.1.0-alpha.2
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- Add `BrowserPurchaseController.reconcile()` so a purchase that 1Click reported as failed can be checked again. Under the purchase lease it keeps the same purchase, deposit and recovery identity, checks NEAR acceptance and reads the existing deposit's 1Click status once. Accepted moves to `accepted`, `SUCCESS` to `accepting` and `REFUNDED` to `refunded`; anything else stays `failed` with the new status saved. If the saved deposit address or memo, the restored purchase's deposit and the saved polling checkpoint disagree, it refuses before any read or write. It never creates, starts or pays for a purchase.
|
|
26
|
+
- Bare `annon init` creates the CLI backup secret at `~/.config/annon/backup-secret`: 32 random bytes as hex, mode 0600 from the first write, published with a hard link so it never appears half-written and never replaces an existing file. Running it again only validates the file. It refuses to create a new secret while saved records or backups exist, never prints the secret, and sends its one-time notice to stderr. `--backup-secret-file`, `ANNON_BACKUP_SECRET_FILE` and `ANNON_BACKUP_SECRET` keep their precedence; the default file is read only when none is set, and a selected source that fails never falls back. Other commands, agent mode and MCP read the default but never create it. Reading the default checks that `~/.config/annon` is a private directory you own, refuses symlinks, FIFOs and other non-regular files without blocking, and never repairs anything. Bare `init` reports an unreadable config as a setup error (exit 2). A pending `status` now uses the default file too. `annon --version` (or `-v`) prints the installed version. The quickstart replaces the manual secret recipe with `annon init` and explains backup and restore.
|
|
27
|
+
- The CLI wallet approval page shows a readable summary of exactly what the wallet signs: a title per action, the pairing code to compare, amounts in USDC or NEAR, receiver, account, expiry and what signing does, with the CLI's terms shown apart and the raw request under Technical details (the NEP-413 nonce as hex). Each click opens at most one wallet window, so Meteor's web popup is no longer blocked: NEP-413 messages are signed without a sign-in, and transactions connect first and then send. After a closed, blocked or declined wallet window the page explains it and allows another try until the request expires. The terminal prints a waiting line with the expiry, the reported wallet failure and a specific final error (`cancelled`, `rejected` or `wallet_error`) instead of `operation_failed`. Once the page has called the wallet's send for a transaction, the request is never attempted again: the CLI records the send start before the wallet is called, the request ends as `transaction_uncertain` with a warning not to pay again, the saved purchase keeps that state without inventing a transaction hash, and it blocks a plain rerun of `annon buy` until it is accepted or `--force` is used. A result the CLI cannot verify after the send start also ends as uncertain, keeping a valid transaction hash when there is one. An uncertain `annon buy` exits 4, and `annon status` reports `submission: "uncertain"` with a warning not to pay again. The NEAR wallet is chosen in a styled, keyboard-accessible list instead of the browser's native menu.
|
|
28
|
+
|
|
3
29
|
## 0.1.0-alpha.1
|
|
4
30
|
|
|
5
31
|
### Patch Changes
|
package/CLI.md
CHANGED
|
@@ -2,9 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
Build with `pnpm build`. The `@defdone/annon` package ships the `annon` bin;
|
|
4
4
|
in this checkout run `node packages/annon/dist/node/main.js` in its place.
|
|
5
|
-
Run `annon --help` for all flags
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
Run `annon --help` for all flags and `annon --version` (or `-v`) for the installed version; with
|
|
6
|
+
`--json` the version is `{ "ok": true, "result": { "version": ... } }`. It loads no config, wallet
|
|
7
|
+
or secret and also works in agent mode. With `--json` every command prints one compact JSON line
|
|
8
|
+
and asks nothing. Success is `{ "ok": true, "result": ... }`;
|
|
9
|
+
errors are `{ "ok": false, "error": { "code": ..., "message": ... } }`. Without `--json` the same
|
|
10
|
+
envelope is printed indented whenever standard output is not a terminal, and always in agent mode,
|
|
11
|
+
so scripts and pipes read JSON either way. In a terminal a person gets short text instead: one or
|
|
12
|
+
two lines per command, `catalog` as a numbered table, errors as `Error: message (code)`. Exit codes
|
|
13
|
+
are the same in every mode. Text from shops and hosts is shown with control characters removed.
|
|
14
|
+
|
|
15
|
+
## Interactive session
|
|
16
|
+
|
|
17
|
+
`annon` with no arguments in a terminal, or `annon shell`, starts a session. It prints the version,
|
|
18
|
+
network, signed-in account and whether the configured contract is trusted, then reads commands at
|
|
19
|
+
`annon>` until `exit`, `quit` or Ctrl-D. Commands are the ones on this page without the `annon`
|
|
20
|
+
prefix; Tab completes names and flags, `help` lists them. Each line runs the same code as the
|
|
21
|
+
one-shot command, with the same trust prompt, price cap, purchase guard and confirmation, and the
|
|
22
|
+
session keeps no wallet, config or secret between commands.
|
|
23
|
+
|
|
24
|
+
- `catalog` numbers its rows; `buy 2` buys row 2 of the last listing shown.
|
|
25
|
+
- After a NEAR or Base payment `buy` waits for acceptance, then asks where to save the file.
|
|
26
|
+
- `status` and `open --output <file>` without an ID use the latest purchase `buy` saved for the
|
|
27
|
+
contract and network of that line, as the one-shot commands do. The session remembers no
|
|
28
|
+
purchase itself, so a line with another `--contract-id`, `--network`, `--config` or
|
|
29
|
+
`--state-dir` never picks up a purchase made under a different one.
|
|
30
|
+
- Ctrl-C cancels a prompt (the answer is no), stops a wait for acceptance, or drops a read-only
|
|
31
|
+
command. During `buy`, `fund`, `sell`, `share`, `stop` or `recover` it asks
|
|
32
|
+
`A payment may be in progress. Exit anyway? (y/N)`. Leaving removes nothing: the purchase saved
|
|
33
|
+
before payment is still there, so check it with `annon status` before paying again.
|
|
34
|
+
|
|
35
|
+
Without a terminal, with any argument such as `--json`, or in agent mode no session starts:
|
|
36
|
+
`annon` prints the help and `annon shell` fails with `invalid_arguments`.
|
|
8
37
|
|
|
9
38
|
Create a public configuration file and name it with `--config /path/config.json` or
|
|
10
39
|
`ANNON_CONFIG`. The default is `~/.config/annon/config.json`:
|
|
@@ -53,10 +82,9 @@ On macOS, with no NEAR file or credential env value, explicitly select both
|
|
|
53
82
|
service `near-mainnet-alice.near`, account `alice.near:ed25519:PUBLIC_KEY`.
|
|
54
83
|
Other platforms use the credential JSON file or environment. No keychain enumeration occurs.
|
|
55
84
|
|
|
56
|
-
Sell, buy, open, recover and stop also require a
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
generator and keep it outside the repository. It is never derived from a wallet signature.
|
|
85
|
+
Sell, buy, open, recover and stop also require a random 32-byte backup secret. It encrypts the
|
|
86
|
+
local purchase, recovery and sell records and the portable backups. It is never derived from a
|
|
87
|
+
wallet signature or key. See [Backup secret](#backup-secret).
|
|
60
88
|
`verify` and accepted/read-only `status` need neither a wallet nor this secret. A pending Base `status` with saved state and a backup secret may send `unlock` through the configured relayer or NEAR wallet. Status can update encrypted local state and backups: expired/canceled Base payments are marked with `paymentState`, preserving the purchase, attempts and recovery key. It never deletes the purchase record.
|
|
61
89
|
|
|
62
90
|
State directories are created with mode 0700 and records/outputs with 0600. Keep existing
|
|
@@ -64,8 +92,99 @@ state directories private. Purchases and recovery grants are encrypted by the SD
|
|
|
64
92
|
encrypted backups are under `stateDir/backups`. Public headers and resource URLs are saved
|
|
65
93
|
separately for later downloading. Preserve state after timeouts. Losing local state does not
|
|
66
94
|
lose recovery rights: the same recovery wallet can create a fresh grant on a new device with
|
|
67
|
-
a new backup secret
|
|
68
|
-
returned in CLI/MCP results. Plaintext is written only to the explicitly named output.
|
|
95
|
+
a new backup secret ([Loss](#backup-secret) explains when `init` creates one). Keys, signed payment
|
|
96
|
+
payloads and decrypted file contents are never returned in CLI/MCP results. Plaintext is written only to the explicitly named output.
|
|
97
|
+
|
|
98
|
+
### Backup secret
|
|
99
|
+
|
|
100
|
+
Run `annon init` once, before buying, selling or enabling agent mode. It creates
|
|
101
|
+
`~/.config/annon/backup-secret`: 32 random bytes as 64 lowercase hex characters and a newline,
|
|
102
|
+
mode 0600, in a directory created with mode 0700. The file is written to an exclusive temporary
|
|
103
|
+
file and published with a hard link, so it never appears half-written and an existing file is
|
|
104
|
+
never replaced. Running `init` again only validates the file and reports `"created": false`. An
|
|
105
|
+
unsafe default is refused and never repaired: a symlink, FIFO or other non-regular file, a file not
|
|
106
|
+
owned by you or with group or other permission bits, malformed content, or an `~/.config/annon`
|
|
107
|
+
that is a symlink, is not owned by you, or is writable by group or others. An existing 0755
|
|
108
|
+
directory is fine. Every command that reads the default applies the same checks.
|
|
109
|
+
|
|
110
|
+
The location is fixed. `XDG_CONFIG_HOME`, `XDG_DATA_HOME`, `XDG_STATE_HOME`, `--config`,
|
|
111
|
+
`ANNON_CONFIG` and `--state-dir` do not move it. For another private location, name an existing file with the explicit inputs below.
|
|
112
|
+
|
|
113
|
+
Sources, in order, as before this file existed:
|
|
114
|
+
|
|
115
|
+
1. `--backup-secret-file` (or, when the flag is absent, `ANNON_BACKUP_SECRET_FILE`), a file with the
|
|
116
|
+
64 hex characters.
|
|
117
|
+
2. `ANNON_BACKUP_SECRET`, the hex value itself. Prefer a file: environment values are inherited by
|
|
118
|
+
child processes.
|
|
119
|
+
3. The default file, only when neither is set.
|
|
120
|
+
|
|
121
|
+
An empty value counts as unset, as before: an empty `--backup-secret-file` selects no file even when
|
|
122
|
+
`ANNON_BACKUP_SECRET_FILE` is set, and moves on to `ANNON_BACKUP_SECRET`. Hex must be lowercase in
|
|
123
|
+
every source; an optional `0x` prefix and surrounding whitespace are accepted. A selected source that is missing,
|
|
124
|
+
unreadable or malformed fails with `backup_secret_required` (exit 2); there is no fallback to a
|
|
125
|
+
later source, the default file or a new secret. `annon init` with an explicit source only validates
|
|
126
|
+
it and never writes, creates or changes that file. Commands other than `init` read the secret but
|
|
127
|
+
never create it, and agent mode and MCP never create it. Bare `init` accepts only `--json`,
|
|
128
|
+
`--config`, `--state-dir` and `--backup-secret-file`; signer options need `--agent`.
|
|
129
|
+
|
|
130
|
+
Output: stdout carries the usual single JSON result, for example
|
|
131
|
+
`{"ok":true,"result":{"configured":true,"backupSecret":{"source":"default-file","path":"/home/me/.config/annon/backup-secret","created":true}}}`.
|
|
132
|
+
`source` is `default-file`, `flag-file`, `env-file` or `env`; `path` is shown for files only. The
|
|
133
|
+
one-time creation notice goes to stderr, also with `--json`. The secret itself is never printed, in
|
|
134
|
+
results, errors or notices.
|
|
135
|
+
|
|
136
|
+
**Back it up.** The secret is not part of the encrypted backups in `stateDir/backups`; copy the file
|
|
137
|
+
itself, separately, to private encrypted storage. Never `cat` it or paste it into a terminal. This
|
|
138
|
+
copies without displaying it and refuses an existing destination:
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
node --input-type=module -e '
|
|
142
|
+
import { constants } from "node:fs";
|
|
143
|
+
import { copyFile } from "node:fs/promises";
|
|
144
|
+
await copyFile(process.argv[1], process.argv[2], constants.COPYFILE_EXCL);
|
|
145
|
+
' "$HOME/.config/annon/backup-secret" /mounted-private-encrypted-backup/annon-backup-secret
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The copy keeps mode 0600 when the destination supports POSIX permissions; check it. A secret manager
|
|
149
|
+
that imports files is also fine.
|
|
150
|
+
|
|
151
|
+
**Restore.** On a new machine, put the saved file in a private directory (directory mode 0700, file
|
|
152
|
+
mode 0600), copy the encrypted record backups separately, and select the file. Do not run `init`
|
|
153
|
+
first:
|
|
154
|
+
|
|
155
|
+
```sh
|
|
156
|
+
export ANNON_BACKUP_SECRET_FILE=/absolute/private/restored-annon-backup-secret
|
|
157
|
+
annon open /absolute/private/purchase-backup.json --url https://host.example/file --output restored-file
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Opening one known backup proves the restore. To restore to the default location instead, create
|
|
161
|
+
`~/.config/annon` with mode 0700 and copy in the other direction, still refusing an existing file:
|
|
162
|
+
|
|
163
|
+
```sh
|
|
164
|
+
mkdir -p -m 700 "$HOME/.config/annon"
|
|
165
|
+
node --input-type=module -e '
|
|
166
|
+
import { constants } from "node:fs";
|
|
167
|
+
import { copyFile } from "node:fs/promises";
|
|
168
|
+
await copyFile(process.argv[1], process.argv[2], constants.COPYFILE_EXCL);
|
|
169
|
+
' /mounted-private-encrypted-backup/annon-backup-secret "$HOME/.config/annon/backup-secret"
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Loss.** If the secret disappears, commands fail with `backup_secret_required` and a pending
|
|
173
|
+
`status` stays pending; nothing recreates it. Restore the same bytes. Bare `init` creates a new
|
|
174
|
+
secret only when both checked state locations are empty: the effective state directory and the
|
|
175
|
+
default `~/.local/share/annon`, each in `records` and `backups`. If every copy is lost and you want
|
|
176
|
+
to recover with a new secret, first move the whole old state, including `~/.local/share/annon`, to
|
|
177
|
+
a separate private archive and keep it. Then use an empty state directory, run `init`, and run
|
|
178
|
+
`recover` with the same recovery wallet. That creates a fresh grant and new records; it cannot
|
|
179
|
+
decrypt the archived records or resume pending sell jobs. An operator can instead provision a
|
|
180
|
+
separate secret file and select it with `ANNON_BACKUP_SECRET_FILE`. Use one secret per state
|
|
181
|
+
directory.
|
|
182
|
+
|
|
183
|
+
**Agents.** `init --agent` does not create or reference a backup secret. Run bare `init` before it,
|
|
184
|
+
or give the agent an operator-managed file with an absolute `ANNON_BACKUP_SECRET_FILE` in the
|
|
185
|
+
launcher. A launcher that sets `ANNON_BACKUP_SECRET_FILE` must point it at a file that exists; a
|
|
186
|
+
missing file fails without falling back to the default. Once agent mode is active, `init` is
|
|
187
|
+
refused, so provision or restore the secret outside the running agent.
|
|
69
188
|
|
|
70
189
|
## Commands
|
|
71
190
|
|
|
@@ -78,6 +197,10 @@ annon sell report.pdf --price 0.2 --split near:alice.near:10000 --wallet near --
|
|
|
78
197
|
annon sell report.pdf --resume 0xFILE_ID --wallet near --output resumed.pay2
|
|
79
198
|
annon sell report.pdf --price 0.2 --split evm:0x1111111111111111111111111111111111111111:10000 --wallet evm --base
|
|
80
199
|
annon buy https://host.example/report --route near --wallet near
|
|
200
|
+
annon buy https://host.example/report --route near --open report.pdf
|
|
201
|
+
annon buy 2 --shop demo --open report.pdf
|
|
202
|
+
annon status
|
|
203
|
+
annon open 0x1a2b3c4d --output report.pdf
|
|
81
204
|
annon buy offer.json --url https://host.example/report --route base --wallet evm --max-price 0.2
|
|
82
205
|
annon buy https://host.example/report --route 1click --wallet solana --from-chain solana
|
|
83
206
|
annon status 0xPURCHASE_ID --json
|
|
@@ -91,6 +214,29 @@ annon stop 0xFILE_ID --wallet near
|
|
|
91
214
|
```
|
|
92
215
|
|
|
93
216
|
IDs in these commands are placeholders for full `0x` plus 64 lowercase hex digits.
|
|
217
|
+
|
|
218
|
+
Fewer steps for a person (agent mode refuses all three and keeps requiring full URLs and IDs):
|
|
219
|
+
|
|
220
|
+
- `buy <row> --shop <id> [--search <q>]` buys that row of the listing `catalog` prints for the same
|
|
221
|
+
`--shop` and `--search`. The CLI names the row's title and URL, then checks the file and its terms
|
|
222
|
+
as for a typed URL. A catalog can change between listing and buying, so read the terms before you
|
|
223
|
+
confirm. An offer file named like a number is written `./2`.
|
|
224
|
+
- `buy --open <file>` waits up to three minutes for acceptance and then writes the opened file. It
|
|
225
|
+
refuses an existing `<file>` before paying. The result is the buy result with `state: "opened"`
|
|
226
|
+
and `opened: { output, bytes }`. If the purchase is not accepted in time it adds
|
|
227
|
+
`settlement: "pending"` and exits 4; if a status check or the open fails after payment, the result
|
|
228
|
+
still names the purchase and adds `statusError` or `openError`, and the exit code is that
|
|
229
|
+
error's. A purchase that is still `state: "uncertain"` wins over the follow-up error: the result
|
|
230
|
+
carries `statusError`, and the exit code stays 4. Nothing is paid twice: finish with `status` and
|
|
231
|
+
`open`. A 1Click buy returns its deposit
|
|
232
|
+
instructions without waiting. In a terminal, `buy` waits for acceptance even without `--open`.
|
|
233
|
+
- `status` and `open --output <file>` without an ID use the latest purchase `buy` saved for the
|
|
234
|
+
configured contract and network. An ID may be shortened to a prefix of 8 or more hex digits, with
|
|
235
|
+
or without `0x`. A prefix only names purchases that `buy` or `recover` saved for the configured
|
|
236
|
+
contract and network; one that matches none of them, or more than one, is refused with
|
|
237
|
+
`purchase_required`. A purchase saved by an older version, or for another contract or network,
|
|
238
|
+
needs its full ID. For `open`, a name of 8 to 63 hex digits is always read as a prefix: write
|
|
239
|
+
such a file as `./name`.
|
|
94
240
|
`sell` creates a PAY2 file; arrange hosting separately. `buy` accepts the HTTPS resource URL
|
|
95
241
|
or a named file containing decoded x402 `PAYMENT-REQUIRED` JSON. An offer file requires `--url` with the independently selected resource URL; it must match the offer. It checks the public header
|
|
96
242
|
and registered terms before the SDK authorizes payment. Every buy requires `--max-price`, `ANNON_MAX_PRICE` or config `maxPrice` (decimal USDC); the offer never supplies the cap. A configured cap also bounds an explicit flag. The default route is direct NEAR for
|
|
@@ -161,9 +307,23 @@ blindly retried. Raw upstream errors are intentionally suppressed to avoid leaki
|
|
|
161
307
|
| --- | --- |
|
|
162
308
|
| 0 | Command completed; buy may still require a deposit or settlement |
|
|
163
309
|
| 1 | Operation failed; inspect final status and retained encrypted state before retrying |
|
|
164
|
-
| 2 | Invalid arguments, configuration, wallet source or missing local input |
|
|
165
|
-
| 3 | First-use contract trust required or declined |
|
|
166
|
-
| 4 | `status` reports pending;
|
|
310
|
+
| 2 | Invalid arguments, configuration, wallet source or missing local input; `purchase_open` when a saved purchase of the file, including an uncertain NEAR send, still blocks a new one |
|
|
311
|
+
| 3 | First-use contract trust required or declined; browser approval cancelled (`cancelled`), expired (`cancelled`, or `rejected` or `wallet_error` after the last wallet failure the page reported) or declined by the wallet |
|
|
312
|
+
| 4 | Not settled, and not proof of failure: `status` reports pending; `buy` returned `state: "uncertain"` (its payment may be on chain; the JSON result is unchanged and names the saved purchase); or `transaction_uncertain`: a transaction may have been sent and is not confirmed |
|
|
313
|
+
|
|
314
|
+
In browser approval, once the CLI has acknowledged the page's send start, the request can only end
|
|
315
|
+
as a verified transaction or as `transaction_uncertain`: a wallet failure, a cancel, the expiry, or a
|
|
316
|
+
result the CLI cannot verify (an RPC rejection, malformed evidence) never counts as "not sent". The
|
|
317
|
+
purchase is saved with that uncertainty, with the transaction hash only when the wallet returned a
|
|
318
|
+
valid one. `annon buy` then exits 4 with `state: "uncertain"`, and `annon status <purchase id>`
|
|
319
|
+
answers `submission: "uncertain"` until the purchase is accepted. Do not pay again.
|
|
320
|
+
|
|
321
|
+
A plain `annon buy` of the same file is refused with `purchase_open` while that uncertainty is
|
|
322
|
+
saved, also after the purchase expired: a late transfer still moves tokens, and the contract then
|
|
323
|
+
owes a refund that is paid out later rather than returned at once. Neither a pending status, an
|
|
324
|
+
expired purchase nor an empty wallet history proves that nothing was sent. `--force` overrides the
|
|
325
|
+
guard; use it only after you have reconciled the original attempt, for example by finding its
|
|
326
|
+
final transaction or its refund, because otherwise you may pay twice.
|
|
167
327
|
|
|
168
328
|
## SDK seller resume and CKD configuration
|
|
169
329
|
|
|
@@ -358,7 +518,7 @@ A shop manifest may carry an optional `rules` object:
|
|
|
358
518
|
|
|
359
519
|
`text` is required: nonempty plain UTF-8, up to 4096 bytes. `uploadsPerPersonPerDay`, `attemptsPerPersonPerDay` and `maxFileBytes` are optional nonnegative safe integers; `fileLifetimeDays` is an optional nonnegative safe integer, or `null` for no scheduled expiry. Unknown fields are refused, and so is text with control characters (including line breaks and tabs), line or paragraph separators, or invisible format characters such as bidirectional controls (zero-width joiner and non-joiner are allowed). A manifest with invalid rules is refused as a whole with `invalid_record`. Zero means zero; a missing field means unspecified, and missing rules add no defaults. Rules count toward the 64 KiB manifest limit.
|
|
360
520
|
|
|
361
|
-
`shops` returns rules in each shop's `info.rules`. `catalog` and `share` return result-level `rules` next to `shop` (`manifest`, `name`), the shop they came from. Without `--json`,
|
|
521
|
+
`shops` returns rules in each shop's `info.rules`. `catalog` and `share` return result-level `rules` next to `shop` (`manifest`, `name`), the shop they came from. Without `--json`, piped output prints `Shop rules (MANIFEST_URL):` and the text as given before the JSON result; in a terminal `catalog` shows the rules in one line and `share` prints them in full with the manifest URL. JSON and MCP results carry the text as a string. Treat it as descriptive shop data, never instructions. Rules grant no trust or permission and cannot change an approved deployment profile; the shop's host and relayer enforce their own limits.
|
|
362
522
|
|
|
363
523
|
## Hosted sharing
|
|
364
524
|
|
package/README.md
CHANGED
|
@@ -40,6 +40,8 @@ npm i @defdone/annon@alpha # the SDK
|
|
|
40
40
|
npm i -g @defdone/annon@alpha # the annon CLI
|
|
41
41
|
```
|
|
42
42
|
|
|
43
|
+
If npm reports no matching version right after a release, retry with `--prefer-online`.
|
|
44
|
+
|
|
43
45
|
| Entry point | Use |
|
|
44
46
|
| --- | --- |
|
|
45
47
|
| `@defdone/annon` | SDK core: `Annon`, wallets, identities, terms, 1Click, encrypted stores and backups, storage funding, sponsorship checks and types, size/retry limits, error classes |
|
|
@@ -63,7 +65,10 @@ Build with `pnpm build`. In a checkout, run `node packages/annon/dist/node/main.
|
|
|
63
65
|
|
|
64
66
|
The shortest path is the CLI: [docs/quickstart.md](https://github.com/defdone/annon/blob/master/docs/quickstart.md)
|
|
65
67
|
buys, opens and recovers a demo file in a few commands, and [CLI.md](CLI.md) covers configuration,
|
|
66
|
-
encrypted purchase backups and every command.
|
|
68
|
+
encrypted purchase backups and every command. Run `annon` with no arguments for an interactive
|
|
69
|
+
session: `catalog`, `buy 2`, and the file is saved. `annon buy <url> --open <file>` does the same in
|
|
70
|
+
one command. In a terminal the CLI prints short text; `--json`, pipes and agent mode keep the JSON
|
|
71
|
+
envelope.
|
|
67
72
|
|
|
68
73
|
In code, an `Annon` instance needs a deployment, a durable record store, a persisted backup secret, a
|
|
69
74
|
place to write encrypted backups, and a transport for contract calls. In Node, with a NEAR account
|
|
@@ -203,6 +208,7 @@ const { meta, data } = await controller.open({ onProgress: renderDownload });
|
|
|
203
208
|
before an expired key is replaced. Portable receipts carry those archives too.
|
|
204
209
|
- The local random backup secret is persisted in IndexedDB for reload recovery. It protects
|
|
205
210
|
encrypted storage from accidental disclosure and shares the embedding page's trust boundary.
|
|
211
|
+
This is the browser's secret; the CLI keeps its own in a file ([CLI.md](CLI.md#backup-secret)).
|
|
206
212
|
- Export with `controller.exportReceipt(portableSecret)`, using a separate random 32-byte
|
|
207
213
|
`newBackupSecret()` that you keep separately. Import with
|
|
208
214
|
`storage.importReceipt(file, portableSecret)`. Import restores the header, resource URL and
|
|
@@ -295,6 +301,9 @@ shop-provided data: on each shop's `info` from `listShops`, and as `rules` with
|
|
|
295
301
|
|
|
296
302
|
## CLI browser wallet login
|
|
297
303
|
|
|
304
|
+
Run `annon init` once first. It creates the CLI backup secret file that encrypts local records
|
|
305
|
+
([CLI.md](CLI.md#backup-secret)); login neither creates nor exports it.
|
|
306
|
+
|
|
298
307
|
```sh
|
|
299
308
|
annon login --wallet evm
|
|
300
309
|
annon login --wallet near --network near:testnet
|
|
@@ -308,21 +317,44 @@ in the wallet.
|
|
|
308
317
|
| Wallet | How | Signing supported |
|
|
309
318
|
| --- | --- | --- |
|
|
310
319
|
| EVM | Injected EIP-1193 wallet on Base | `personal_sign` for the SDK's authorization texts, `eth_signTypedData_v4` for the exact EIP-3009/x402 payload |
|
|
311
|
-
| NEAR | Meteor Wallet (extension or web popup; mainnet and testnet) or HOT Wallet (mainnet only) through Wallet Selector, picked on the page
|
|
320
|
+
| NEAR | Meteor Wallet (extension or web popup; mainnet and testnet) or HOT Wallet (mainnet only) through Wallet Selector, picked on the page. NEP-413 messages are signed directly, with the account chosen in the wallet and no sign-in. Transactions take two clicks: an account-only sign-in (no `contractId`, no method list), then the transaction | NEP-413 and `signAndSendTransaction`, including the required deposit `"1"` on `ft_transfer_call` |
|
|
312
321
|
|
|
313
322
|
Browser and remote EVM split-deployment transactions and browser and remote Solana signing are not
|
|
314
323
|
included; the local-key adapters remain available for those workflows.
|
|
315
324
|
|
|
316
325
|
**The local page.** The listener binds `127.0.0.1` on an OS-assigned port before opening the browser.
|
|
317
326
|
Match the short pairing code with the terminal, and review the action, amount, recipients, file and
|
|
318
|
-
chain on the page before approving in the wallet.
|
|
327
|
+
chain on the page before approving in the wallet. The page shows a summary built only from the
|
|
328
|
+
request the wallet signs (amounts in USDC or NEAR, receiver, account, expiry), the CLI's payment or
|
|
329
|
+
listing terms apart from it with a warning if their price differs, and the raw request under
|
|
330
|
+
Technical details. While a request waits, the terminal prints its expiry on stderr.
|
|
331
|
+
|
|
332
|
+
- Each click opens at most one wallet window, inside that click, so the browser's pop-up blocker
|
|
333
|
+
allows it. If the wallet window closes, is blocked or declines before anything is sent, the page
|
|
334
|
+
says so and you can try again, with the same or another wallet, until the request expires.
|
|
335
|
+
Nothing is retried for you.
|
|
336
|
+
- Once the page has called the wallet's send for a transaction, that request is never attempted
|
|
337
|
+
again, whatever the wallet answers, because it may already have broadcast. The request ends as
|
|
338
|
+
`transaction_uncertain` and the terminal says: "The wallet may have sent the transaction. Do not
|
|
339
|
+
pay again. Run annon status <purchase id> to check it." The page records the send start with
|
|
340
|
+
the CLI before it calls the wallet, so a reloaded page or the request's expiry cannot turn it
|
|
341
|
+
into a retryable failure, and a result the CLI cannot verify after that point also ends as
|
|
342
|
+
uncertain. `annon buy` exits 4, `annon status` reports `submission: "uncertain"`, and the saved
|
|
343
|
+
purchase blocks a plain `annon buy` of the same file (`purchase_open`). `--force` overrides it
|
|
344
|
+
only after you have reconciled the first attempt (see CLI.md).
|
|
345
|
+
- The page reports only a fixed reason name (`window_closed`, `popup_blocked`, `rejected`,
|
|
346
|
+
`wallet_error`, `refused`, `send_uncertain`, `send_started` or `cancelled`) to the CLI, which
|
|
347
|
+
prints its own line for it. Cancel ends the request at once; an expired request fails with the
|
|
348
|
+
last reported reason, for example `rejected` or `wallet_error`, instead of a generic error. A
|
|
349
|
+
report never completes a request.
|
|
319
350
|
|
|
320
351
|
- Host (including port), Origin and an unpredictable RAM-only capability are checked on action and
|
|
321
352
|
result API calls.
|
|
322
353
|
- In open mode, a same-origin POST bootstraps the capability once into the opened page, then the
|
|
323
354
|
bootstrap route closes. With `--no-open`, bootstrap is disabled and the capability exists only in
|
|
324
355
|
the printed URL fragment. It is never passed to the browser launcher in argv.
|
|
325
|
-
- Bundled scripts
|
|
356
|
+
- Bundled scripts and a stylesheet from the page's own origin (no inline styles, fonts or other
|
|
357
|
+
resources), a restrictive CSP, no framing, `no-store` and `no-referrer` are enforced. The page
|
|
326
358
|
connects only to itself, the Wallet Selector's NEAR RPC (`free.rpc.fastnear.com` on mainnet,
|
|
327
359
|
`test.rpc.fastnear.com` on testnet, fixed in the page and independent of your configured RPC) and
|
|
328
360
|
HOT's relay `h4n.app`, and frames only `hot-labs.org`.
|
|
@@ -405,8 +437,11 @@ Order matters, because agent mode refuses `init`, `login` and per-call trust or
|
|
|
405
437
|
2. Write the deployment, `maxPrice`, `workDir` and, after reviewing the contract,
|
|
406
438
|
`trustContract: true` (or trust it once with a human command and `--trust-contract`). Do not set
|
|
407
439
|
`agent: true` yourself.
|
|
408
|
-
3. Run
|
|
409
|
-
|
|
440
|
+
3. Run bare `annon init` to create the backup secret, or give the launcher an absolute
|
|
441
|
+
`ANNON_BACKUP_SECRET_FILE` you manage ([CLI.md](CLI.md#backup-secret)). Agent mode never creates
|
|
442
|
+
one.
|
|
443
|
+
4. Run one `annon init --agent ...` from the examples above. It saves the signer, the daily budget
|
|
444
|
+
and `agent: true`. It does not create or reference a backup secret.
|
|
410
445
|
|
|
411
446
|
- Agents never prompt. Missing trust, price cap or daily budget causes a refusal.
|
|
412
447
|
- `allowedResources` is optional. Without it, any secure resource URL is allowed within the
|
|
@@ -447,7 +482,8 @@ Before a human payment the CLI confirms the current price, split recipients and
|
|
|
447
482
|
and file, token and network. Browser mode shows these exact terms with each signing request for
|
|
448
483
|
approval in the page and the wallet. Local and remote human signing require an explicit terminal
|
|
449
484
|
confirmation. Purchase and recovery secrets still need the independent backup secret described in
|
|
450
|
-
CLI.md
|
|
485
|
+
[CLI.md](CLI.md#backup-secret) and created by `annon init`; login does not derive one from a
|
|
486
|
+
signature.
|
|
451
487
|
|
|
452
488
|
## Remote signer, HTTP JSON v1
|
|
453
489
|
|
|
@@ -501,9 +537,10 @@ RPC. A returned transaction hash alone never proves success.
|
|
|
501
537
|
- Definite mismatches and failed finalized transactions are rejected without saving the hash as
|
|
502
538
|
`nearSubmission` evidence. Only RPC unavailability or a non-final status keeps the outcome
|
|
503
539
|
uncertain.
|
|
504
|
-
- If submission or finalized verification is ambiguous, the CLI returns `state: "uncertain"
|
|
505
|
-
`purchaseId
|
|
506
|
-
|
|
540
|
+
- If submission or finalized verification is ambiguous, the CLI returns `state: "uncertain"` and
|
|
541
|
+
`purchaseId`, and keeps `nearSubmission: {state:"uncertain"}` in the encrypted purchase backup.
|
|
542
|
+
Both carry `txHash` only when the wallet returned a valid one. `annon buy` exits 4. Check status
|
|
543
|
+
for that saved purchase before buying again.
|
|
507
544
|
- Signing errors with a known hash also expose safe hash and purchase evidence, with exit code 4.
|
|
508
545
|
|
|
509
546
|
**Reference handler.** `node/signer-mock` exports `signerMock`, an authenticated HTTP
|
|
@@ -100,9 +100,10 @@ export interface Bought {
|
|
|
100
100
|
route: BuyRoute;
|
|
101
101
|
record: PurchaseRecord;
|
|
102
102
|
instructions?: Deposit;
|
|
103
|
+
/** `txHash` is absent when the wallet may have sent the transaction without returning one. */
|
|
103
104
|
uncertain?: {
|
|
104
105
|
purchaseId: string;
|
|
105
|
-
txHash
|
|
106
|
+
txHash?: string;
|
|
106
107
|
};
|
|
107
108
|
payment?: string;
|
|
108
109
|
}
|
package/dist/api/annon.js
CHANGED
|
@@ -869,11 +869,18 @@ export class Annon {
|
|
|
869
869
|
const next = {
|
|
870
870
|
...record,
|
|
871
871
|
revision: record.revision + 1,
|
|
872
|
-
nearSubmission: {
|
|
872
|
+
nearSubmission: {
|
|
873
|
+
state: 'uncertain',
|
|
874
|
+
...(error.txHash ? { txHash: error.txHash } : {}),
|
|
875
|
+
},
|
|
873
876
|
};
|
|
874
877
|
if (!(await store.compareAndSet(purchaseKey(id), record, next)))
|
|
875
878
|
throw new TransactionUncertain(error.txHash, id);
|
|
876
|
-
return {
|
|
879
|
+
return {
|
|
880
|
+
route,
|
|
881
|
+
record: next,
|
|
882
|
+
uncertain: { purchaseId: id, ...(error.txHash ? { txHash: error.txHash } : {}) },
|
|
883
|
+
};
|
|
877
884
|
}
|
|
878
885
|
return { route, record };
|
|
879
886
|
}
|
package/dist/browser/index.d.ts
CHANGED
|
@@ -18,6 +18,6 @@ export { type CatalogEntry, type CatalogQuery, type ConfiguredShop, canonicalMan
|
|
|
18
18
|
export { TransactionUncertain } from '../wallet/uncertain.js';
|
|
19
19
|
export { type EvmWallet, FileFundingRefused, type IdentityWallet, type NearWallet, } from '../wallet/wallet.js';
|
|
20
20
|
export { BROWSER_DOWNLOAD_LIMIT, type BrowserDownloadOptions, type BrowserDownloadProgress, browserDownload, } from './download.js';
|
|
21
|
-
export { type BrowserOpenOptions, BrowserPurchaseController, type BrowserPurchaseRecord, type BrowserPurchaseState, type BrowserPurchaseStorage, browserAnnon, browserPurchaseStorage, type ControllerOptions, PurchaseLeaseBusy, type PurchaseProgress, type PurchaseSdk, type PurchaseTermsReview, } from './purchases.js';
|
|
21
|
+
export { type BrowserOpenOptions, BrowserPurchaseController, type BrowserPurchaseRecord, type BrowserPurchaseState, type BrowserPurchaseStorage, browserAnnon, browserPurchaseStorage, type ControllerOptions, PurchaseLeaseBusy, type PurchaseProgress, type PurchaseSdk, type PurchaseTermsReview, type ReconcileOptions, type ReconcileResult, } from './purchases.js';
|
|
22
22
|
export { type BrowserSaleIndex, type BrowserSellerOptions, browserSellerSpool, browserSellerStorage, createBrowserSeller, type SaleHandle, type SaleView, type SellDraft, type SellerConnection, type SellerFacade, type SellProgress, type SellQuote, type SellStep, type SellTerms, } from './selling.js';
|
|
23
23
|
export { type BrowserNearProvider, type BrowserNearTransaction, BrowserWalletError, type BrowserWalletErrorCode, browserEvmWallet, browserNearWallet, browserSolanaWallet, type InjectedEvmProvider, type InjectedSolanaKey, type InjectedSolanaProvider, signNearUpload, } from './wallets.js';
|