otto-execute 0.1.0 → 0.1.2
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/README.md +110 -124
- package/dist/cli.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/window.d.ts +10 -0
- package/dist/window.js +16 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,151 +1,137 @@
|
|
|
1
1
|
# otto-execute
|
|
2
2
|
|
|
3
|
-
The
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
Otto never holds your keys or funds on either path.
|
|
38
|
-
|
|
39
|
-
## Install
|
|
3
|
+
The user's side of Otto's execution seam. [Otto Intel MCP](https://www.npmjs.com/package/otto-intel-mcp) builds a swap as an unsigned, digest-committed **envelope** for an address you supply; `otto-execute` checks that envelope against **your own intent**, then signs and submits it with **your** key (or under a delegation you minted). CLI and library, one install.
|
|
4
|
+
|
|
5
|
+
- npm: `npm install otto-execute` — 19 packages including itself (`@x402/core`, `@x402/evm`, `viem`, `zod`), 0 `npm audit` findings.
|
|
6
|
+
- Docs: https://docs.useotto.xyz/acp-swarm/execution-seam
|
|
7
|
+
- Requires Node 22 or newer.
|
|
8
|
+
|
|
9
|
+
## Quick start (under a minute)
|
|
10
|
+
|
|
11
|
+
**1. Get an envelope** — ask the hosted MCP (`https://mcp.ottoai.services`, free) for a swap on your address:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"name": "otto_prepare_swap",
|
|
16
|
+
"arguments": {
|
|
17
|
+
"accountProfile": { "kind": "eoa", "address": "0xYourAddress", "chainId": 8453 },
|
|
18
|
+
"chainId": 8453,
|
|
19
|
+
"fromToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
20
|
+
"toToken": "0x4200000000000000000000000000000000000006",
|
|
21
|
+
"fromAmount": "1000000",
|
|
22
|
+
"slippage": { "maxBps": 50, "minAmountOut": "200000000000000" },
|
|
23
|
+
"client_ref": "my-trade-1"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Any MCP client works (connection snippets: the [otto-intel-mcp README](https://www.npmjs.com/package/otto-intel-mcp)); the tool result is one text part containing JSON — save that JSON object (it starts with `schema_version`, `artifact_id`, …) as `envelope.json`. It is valid for **5 minutes** from construction (`valid_until`; the hosted server's window — the tool description states it), and `submit` re-checks freshness before each of its three signatures, so don't pace on the window: verify, then submit, or fetch a fresh envelope. v1 builds Base (chain 8453) swaps.
|
|
29
|
+
|
|
30
|
+
**2. Write your intent** — what *you* meant, in atomic token units (USDC has 6 decimals: `1000000` = 1 USDC; WETH has 18: `200000000000000` = 0.0002 WETH). `minAmountOut` is your own floor from a price source you trust:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{ "fromToken": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", "toToken": "0x4200000000000000000000000000000000000006", "fromAmount": "1000000", "minAmountOut": "200000000000000" }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**3. Verify, sign, submit:**
|
|
40
37
|
|
|
41
38
|
```bash
|
|
42
|
-
|
|
39
|
+
npx otto-execute verify --envelope envelope.json --intent intent.json --account 0xYourAddress # no key needed
|
|
40
|
+
OTTO_EXECUTE_PRIVATE_KEY=0x… npx otto-execute sign --envelope envelope.json --intent intent.json # dry run: signs + decodes back, broadcasts nothing
|
|
41
|
+
OTTO_EXECUTE_PRIVATE_KEY=0x… npx otto-execute submit --envelope envelope.json --intent intent.json --i-understand-mainnet
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`verify` on a real envelope prints (captured 2026-09-02):
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"verified": {
|
|
49
|
+
"account": "0xF7270569486Ae94945360107721273C60dFdD825",
|
|
50
|
+
"checks": ["schema", "payload_digest", "commitment", "freshness", "account_binding", "capabilities", "chain", "plan_shape", "approvals", "swap_decode", "min_output", "fee_attribution"],
|
|
51
|
+
"token": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
52
|
+
"output_token": "0x4200000000000000000000000000000000000006",
|
|
53
|
+
"gross": "1000000",
|
|
54
|
+
"otto_fee": "1000",
|
|
55
|
+
"min_amount_out": "415211826902997",
|
|
56
|
+
"fee_distributions": [
|
|
57
|
+
{ "recipient": "0xC06ebbefD94032B85424D51906e2A335EFAe264B", "amount": "1500" },
|
|
58
|
+
{ "recipient": "0x0E84dDEdAaE6A779c462C22a59F301EC31B6b808", "amount": "1000" }
|
|
59
|
+
],
|
|
60
|
+
"valid_until": "2026-09-02T09:00:49.613Z",
|
|
61
|
+
"seconds_remaining": 287,
|
|
62
|
+
"artifact_id": "01M1GN9T3DH6470V0Y8968QXRX"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
43
65
|
```
|
|
44
66
|
|
|
45
|
-
|
|
46
|
-
`otto-intel-mcp` this package runs at runtime (`otto-intel-mcp/execution`, `/adapter`, `/adapter-cdp` —
|
|
47
|
-
the envelope schema, the verifier, the signing adapters) are **vendored** into `dist/vendor/otto-intel-mcp/`
|
|
48
|
-
at build time as file-for-file copies of the published `otto-intel-mcp` release, so a consumer does not
|
|
49
|
-
install that package (it is also an MCP server and would cost ~240 packages). The copy is byte-verified:
|
|
50
|
-
`dist/vendor/otto-intel-mcp/VENDORED.json` records the version, the registry integrity hash and a sha256 per
|
|
51
|
-
file; `npm run vendor:check` re-derives it (and runs in `prepack`). A change in the seam is therefore a
|
|
52
|
-
visible `otto-intel-mcp` version bump + re-vendor here, never silent drift. `@coinbase/cdp-sdk` stays an
|
|
53
|
-
optional peer, loaded only by the `otto-execute/cdp` entry.
|
|
67
|
+
`seconds_remaining` is the budget you have left to `submit` (the whole plan must clear before `valid_until`).
|
|
54
68
|
|
|
55
|
-
|
|
69
|
+
A plan that does not hold is refused by name, exit code 2 — for example an envelope past its window:
|
|
56
70
|
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
npm run verify # typecheck → build (tsc + vendor-seam) → tests; the prepublishOnly gate
|
|
60
|
-
npm run fixtures:check # monorepo-only: the committed envelope fixtures still match the sibling constructor
|
|
61
|
-
npm run smoke:install # pack + clean install outside the repo + a real verify + the consumer package count
|
|
71
|
+
```json
|
|
72
|
+
{ "refused": { "code": "STALE_ARTIFACT", "message": "[STALE_ARTIFACT] execution artifact has expired" } }
|
|
62
73
|
```
|
|
63
74
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
`scripts/capture-envelopes.ts` (the one file that touches `../otto-intel-mcp`, run only in the monorepo).
|
|
68
|
-
|
|
69
|
-
**Sealed-state rule (Base Flashblocks).** A receipt can arrive from a preconfirmation while `latest` — the
|
|
70
|
-
state gas estimates and reads run against — still predates it, and a preconfirmation is not final (a
|
|
71
|
-
Flashblock reorg = a preconfirmed transaction omitted from the sealed block). Both signers therefore count a
|
|
72
|
-
transaction as mined ONLY once `waitForSealedReceipt` has refetched its receipt and the sealed canonical
|
|
73
|
-
block at that height carries the receipt's block hash — before the next estimate and before the allowance
|
|
74
|
-
read-back after a clear; if that never happens within the bound, the step fails closed. Observed live
|
|
75
|
-
2026-08-28: an approve preconfirmed, the swap call estimated against `latest`, `TRANSFER_FROM_FAILED` on an
|
|
76
|
-
allowance already granted. The wait is what a user's wallet observes.
|
|
77
|
-
|
|
78
|
-
`@coinbase/cdp-sdk` is an OPTIONAL peer: only the CDP-delegated signer needs it, and that signer lives on its
|
|
79
|
-
own entry — `import { CdpDelegatedSigner } from 'otto-execute/cdp'`. The root entry (`otto-execute`: verifier,
|
|
80
|
-
BYO/EOA signer, x402 table) and the CLI never resolve the SDK; `test/root-without-cdp.test.ts` loads the root
|
|
81
|
-
with the peer made unresolvable to prove it. The dev-only `.npmrc` (`legacy-peer-deps`) is **gone**: it existed
|
|
82
|
-
only because the SDK's optional `@x402` peers want `^2.19` while this package pinned `2.16.0`, and the
|
|
83
|
-
reconcile to `2.23.0` satisfies that range natively — `npm install` now succeeds with no flag.
|
|
84
|
-
|
|
85
|
-
> **`@x402/*` reconciled to 2.23.0 (2026-09-01)**, matching what `otto-intel-mcp@0.1.0` ships, so one
|
|
86
|
-
> `@x402/core` instance serves both packages.
|
|
87
|
-
>
|
|
88
|
-
> **`spendControls` is set explicitly, and KEPT.** `@x402/core` ≥ 2.23 seeds `spendControls = {}` in the
|
|
89
|
-
> bare `x402Client` constructor, which the SDK reads as *default assets only, capped at `$1` per payment*.
|
|
90
|
-
> `signX402Payment` states that cap in our own source (`PER_PAYMENT_SPEND_CAP = '$1'`) instead of inheriting
|
|
91
|
-
> it, so the number is visible here and a future SDK default cannot move it silently.
|
|
92
|
-
>
|
|
93
|
-
> It is deliberately **not** disabled. The table checks more *fields*, but it is **not an independent
|
|
94
|
-
> authority**: `table` is a parameter, and even the default table's `amount_atomic` values are hand-edited
|
|
95
|
-
> constants that nothing else cross-checks. A price typo (`5000` → `5000000`) leaves the table agreeing with
|
|
96
|
-
> itself perfectly, and only a cap derived from the requirement amount rather than from the table stands
|
|
97
|
-
> between that typo and a signature for 1000× the intended amount. Field coverage is not a second opinion.
|
|
98
|
-
>
|
|
99
|
-
> The SDK's rejection is translated into this package's own vocabulary as
|
|
100
|
-
> **`X402_SPEND_LIMIT_EXCEEDED`** — a distinct condition from `X402_AMOUNT_EXCEEDED` (which means the
|
|
101
|
-
> *challenge* disagreed with the table; this one means the *table* asked for more than the budget). Only
|
|
102
|
-
> that rejection is translated; every other error rethrows untouched.
|
|
103
|
-
>
|
|
104
|
-
> Three tests pin it, mutation-proved in both directions: a `$5` typo row is never signed (flip the cap to
|
|
105
|
-
> `false` and it signs; delete the translation and a raw SDK error escapes instead of an `X402Refusal`), a
|
|
106
|
-
> live-priced row still signs through the same path, and a challenge that disagrees with the table is still
|
|
107
|
-
> `X402_AMOUNT_EXCEEDED`.
|
|
108
|
-
|
|
109
|
-
## Library
|
|
75
|
+
Exit codes: 0 verified/sent, 2 refused, 1 usage or I/O error. Keys come from the environment only (`OTTO_EXECUTE_PRIVATE_KEY`); a key-looking value in argv aborts; `sign` and `submit` take the account from the key, so `--account` is for `verify` only. `submit` sends up to three transactions and needs a little ETH on Base for gas; `--rpc <https url>` sets your own Base RPC (default: the public Base RPC — fine for `verify`/`sign`, pass your own for `submit`). `--prepare-input p.json` (the exact `arguments` you sent) adds the `intent_digest` check.
|
|
76
|
+
|
|
77
|
+
## Quick start (library)
|
|
110
78
|
|
|
111
79
|
```ts
|
|
80
|
+
import { readFileSync } from 'node:fs';
|
|
112
81
|
import { verifySwapEnvelope, defaultAdapterPolicy, EoaSigner } from 'otto-execute';
|
|
113
82
|
|
|
114
|
-
const
|
|
115
|
-
const
|
|
83
|
+
const USDC = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913';
|
|
84
|
+
const WETH = '0x4200000000000000000000000000000000000006';
|
|
85
|
+
const envelope = JSON.parse(readFileSync('envelope.json', 'utf8')); // the otto_prepare_swap result
|
|
86
|
+
|
|
87
|
+
const signer = EoaSigner.fromPrivateKey(process.env.OTTO_EXECUTE_PRIVATE_KEY as `0x${string}`, 'https://your-base-rpc');
|
|
88
|
+
const verified = verifySwapEnvelope(envelope, {
|
|
116
89
|
profile: { kind: 'eoa', address: signer.address, chainId: 8453 },
|
|
117
|
-
intent: { fromToken: USDC, toToken: WETH, fromAmount:
|
|
90
|
+
intent: { fromToken: USDC, toToken: WETH, fromAmount: 1_000_000n, minAmountOut: 200_000_000_000_000n },
|
|
118
91
|
capabilities: signer.capabilities,
|
|
119
92
|
policy: defaultAdapterPolicy(8453),
|
|
120
|
-
prepareInput:
|
|
93
|
+
// prepareInput: theArgumentsYouSent, // optional: also binds intent_digest to your exact request
|
|
121
94
|
});
|
|
122
95
|
await signer.signPlanDryRun(verified); // proves what would be signed; broadcasts nothing
|
|
123
|
-
await signer.sendPlan(verified);
|
|
96
|
+
await signer.sendPlan(verified); // Base mainnet
|
|
124
97
|
```
|
|
125
98
|
|
|
126
|
-
`minAmountOut` is
|
|
127
|
-
for the swap's internal DEX legs (LI.FI's router reverts unless you receive at least it), so it must
|
|
128
|
-
be positive.
|
|
99
|
+
`minAmountOut` is yours to derive and must be positive: it is also the on-chain floor for the swap's DEX legs (LI.FI's router reverts unless you receive at least it).
|
|
129
100
|
|
|
130
|
-
##
|
|
101
|
+
## What you get
|
|
131
102
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
103
|
+
| Command / API | Does |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `verify` · `verifySwapEnvelope()` | Re-checks the envelope's own assertions (schema, payload digest, commitment, freshness, account binding), then **your intent** (input token + exact amount, every deposit leg pulling exactly that, the output token, your minimum output), then policy (reviewed router, receiver = you, every distribution leg decoded independently and matched to the reviewed, versioned set through a reviewed FeeForwarder deployment, aggregate under the cap). Twelve named checks; a verified plan is frozen and registered by identity. |
|
|
106
|
+
| `sign` · `signPlanDryRun()` | Signs every step with your key, decodes each back and recovers the signer — nothing is broadcast. |
|
|
107
|
+
| `submit` · `sendPlan()` | Sends the steps in order (allowance reset → approval → swap), each waited to a sealed Base block; on any uncertain failure it halts and clears the live allowance, confirmed by an allowance read-back. Freshness and binding are re-checked immediately before every signature. |
|
|
108
|
+
| `x402 --challenge c.json [--sign]` · `validateX402Challenge()` + `signX402Payment()` | For the hosted MCP's **paid intelligence tools** (the constructors are free): when a tool answers `payment_required`, save that object as `c.json`; this validates it against an exact-match table of Otto's routes (resource, recipient, amount, 300 s window, EIP-712 domain) and, with `--sign`, prints the `x_payment` string — retry the same tool with it in the `x_payment` argument. A per-payment cap of `$1` is enforced regardless of the table. |
|
|
109
|
+
| `delegate --email you@example.com [--expires-days 90]` · `otto-execute/delegate` | Mints a time-boxed CDP delegation (default 90 days, max 180) to Otto's public project so Otto's server can submit your own swaps under it; `delegation status\|revoke` read it back / revoke it. Reads the public `otto_delegation_fence_status` first and refuses when the fence is absent. Runs end-user-side: sign-in by email OTP typed on the terminal; no Otto credential in the process. Not a Coinbase-supported path — it drives the pinned `@coinbase/cdp-core` 0.0.120 web build on Node and can break on an SDK update (a canary test names the break). |
|
|
110
|
+
| `otto-execute/cdp` · `CdpDelegatedSigner` | Library only: the same verify → submit over a delegation you minted with `delegate` (needs the optional `@coinbase/cdp-sdk` peer; the CLI's `submit` is the EOA path). |
|
|
111
|
+
|
|
112
|
+
Every refusal is a named code (`AdapterRefusal` / `X402Refusal` / `DelegateRefusal`), never upstream error text.
|
|
113
|
+
|
|
114
|
+
## File shapes
|
|
115
|
+
|
|
116
|
+
- `envelope.json` — the `otto_prepare_swap` result, verbatim.
|
|
117
|
+
- `intent.json` — `{ fromToken, toToken, fromAmount, minAmountOut }`, addresses `0x` + 40 hex, amounts as strings in atomic units.
|
|
118
|
+
- `--prepare-input p.json` — the exact `arguments` object you sent to `otto_prepare_swap` (optional; adds the `intent_digest` check).
|
|
119
|
+
- `x402 --challenge c.json` — the `payment_required` object a hosted tool returned.
|
|
120
|
+
|
|
121
|
+
## Design notes (for reviewers)
|
|
139
122
|
|
|
140
|
-
|
|
141
|
-
`
|
|
123
|
+
- **Vendored seam.** The parts of `otto-intel-mcp` this package runs (`/execution`, `/adapter`, `/adapter-cdp`) are copied file-for-file from the published `otto-intel-mcp` release into `dist/vendor/` at build time; `dist/vendor/otto-intel-mcp/VENDORED.json` records the version, the registry integrity hash and a sha256 per file, re-derived by `npm run vendor:check` in `prepack`. A seam change is a visible version bump + re-vendor, never silent drift.
|
|
124
|
+
- **Sealed-state rule (Base Flashblocks).** A receipt can arrive from a preconfirmation while `latest` still predates it, and a preconfirmation can be reorged. Both signers count a transaction as mined only once the sealed canonical block at that height carries the receipt's block hash — before the next estimate and before the allowance read-back. Observed live 2026-08-28.
|
|
125
|
+
- **`spendControls` kept on.** `@x402/core` ≥ 2.23 caps a payment at `$1` by default; this package states that cap explicitly (`PER_PAYMENT_SPEND_CAP`) and translates the SDK's rejection to `X402_SPEND_LIMIT_EXCEEDED`. The `table` parameter of `validateX402Challenge` is public and defaults to `X402_TABLE`; whatever table a caller passes, the cap bounds what can be signed.
|
|
126
|
+
- **Optional peers.** `@coinbase/cdp-sdk` (the `/cdp` entry) and `@coinbase/cdp-core` (the `/delegate` entry) are optional; the root entry and the CLI's verify/sign/submit/x402 never load them, and a consumer without them typechecks (`test/root-without-cdp.test.ts`, and the isolated-install smoke compiles a consumer with `skipLibCheck: false`).
|
|
127
|
+
- **Hermetic tests.** `npm test` runs on captured genuine envelopes (`test/fixtures/envelopes/`, built by the constructor's own code over its real LI.FI captures) and resolves `otto-intel-mcp/*` from `node_modules`; `scripts/capture-envelopes.ts` (monorepo-only) re-captures and `npm run fixtures:check` fails when they are stale. The 24-row mutation matrix (`MUTATION_CASES`) is the one the Model-B proof kit shipped with, which consumes this package.
|
|
128
|
+
- Not in this package by design: a DEX allowlist (the on-chain minimum-output bind is the fence); bridge / Polymarket / Hyperliquid adapters (their constructors are live on the MCP; adapters follow).
|
|
142
129
|
|
|
143
|
-
|
|
130
|
+
Working in the monorepo: `npm ci && npm run verify` (typecheck → build incl. vendoring → tests), `npm run smoke:install` (pack + clean install outside the repo + real verify + TypeScript consumer compile + package count), `npm run pack:audit` (the privacy gate, also in `prepack`).
|
|
144
131
|
|
|
145
|
-
|
|
146
|
-
(`scripts/probes/model-b-proof/`, PR #1918, codex gate GO r5/5) shipped with; the kit now consumes this
|
|
147
|
-
package. Live sittings run from the kit's runbook (`docs/0826_model_b_proof_runbook.md`).
|
|
132
|
+
## Links
|
|
148
133
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
134
|
+
- Execution seam docs (the envelope field by field, the checks, delegation): https://docs.useotto.xyz/acp-swarm/execution-seam
|
|
135
|
+
- Otto Intel MCP (the constructors + intelligence tools): https://www.npmjs.com/package/otto-intel-mcp · https://docs.useotto.xyz/acp-swarm/otto-intel-mcp
|
|
136
|
+
- Otto AI: https://useotto.xyz · X: [@useOttoAI](https://x.com/useOttoAI)
|
|
137
|
+
- Changelog: 0.1.2 — `verify` prints `seconds_remaining` (the submit budget) and `secondsRemaining()` is exported; envelope window documented as 5 minutes; 0.1.1 — README rewritten for first-time users and agents (quick start, captured real output, file shapes); 0.1.0 — first release.
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { readFileSync } from 'node:fs';
|
|
|
18
18
|
import { createInterface } from 'node:readline';
|
|
19
19
|
import { getAddress } from 'viem';
|
|
20
20
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
21
|
+
import { secondsRemaining } from './window.js';
|
|
21
22
|
import { DEFAULT_OTTO_MCP_URL, DelegateRefusal, UNSUPPORTED_PATH_NOTE, assertFencePresent, initEndUserSdk, isDelegateRefusal, mintDelegation, readDelegationAsUser, readPublicFenceStatus, redactSecrets, resolveDelegateEndpoints, revokeDelegationAsUser, signInWithEmailOtp, } from './delegate.js';
|
|
22
23
|
import { EoaSigner } from './eoa-signer.js';
|
|
23
24
|
import { isAdapterRefusal } from './refusal.js';
|
|
@@ -381,6 +382,8 @@ async function main() {
|
|
|
381
382
|
min_amount_out: verified.minAmountOut,
|
|
382
383
|
fee_distributions: verified.fees.distributions,
|
|
383
384
|
valid_until: verified.artifact.valid_until,
|
|
385
|
+
// The budget left to submit the WHOLE plan (freshness is re-checked before every signature).
|
|
386
|
+
seconds_remaining: secondsRemaining(verified.artifact.valid_until),
|
|
384
387
|
artifact_id: verified.artifact.artifact_id,
|
|
385
388
|
};
|
|
386
389
|
if (command === 'verify') {
|
package/dist/index.d.ts
CHANGED
|
@@ -14,3 +14,4 @@ export { waitForSealedReceipt, type SealedClient, type SealedWaitOptions } from
|
|
|
14
14
|
export { EOA_CAPABILITIES, EoaSigner, PlanHaltedError, type DryRunSignedStep, type SentStep, type SignedX402Payment, } from './eoa-signer.js';
|
|
15
15
|
export { BASE_USDC, OTTO_X402_PAY_TO, X402_REFUSAL_CODES, X402_TABLE, X402_TABLE_VERSION, X402Refusal, canonicalResourceUrl, requirementMatchesRow, validateX402Challenge, type PaymentRequirementLike, type ValidatedX402Challenge, type X402ExpectedRow, type X402RefusalCode, type X402Table, } from './x402-table.js';
|
|
16
16
|
export { FOREIGN_ADDRESS, FOREIGN_TOKEN, MUTATION_CASES, applyMutation, recommit, recommitCommitmentOnly, recommitPayloadOnly, type CatchingLayer, type MutationCase, type MutationContext, } from './mutations.js';
|
|
17
|
+
export { secondsRemaining } from './window.js';
|
package/dist/index.js
CHANGED
|
@@ -16,4 +16,5 @@ export { EOA_CAPABILITIES, EoaSigner, PlanHaltedError, } from './eoa-signer.js';
|
|
|
16
16
|
// optional `@coinbase/cdp-sdk` peer (loaded lazily); this root never references the SDK at all.
|
|
17
17
|
export { BASE_USDC, OTTO_X402_PAY_TO, X402_REFUSAL_CODES, X402_TABLE, X402_TABLE_VERSION, X402Refusal, canonicalResourceUrl, requirementMatchesRow, validateX402Challenge, } from './x402-table.js';
|
|
18
18
|
export { FOREIGN_ADDRESS, FOREIGN_TOKEN, MUTATION_CASES, applyMutation, recommit, recommitCommitmentOnly, recommitPayloadOnly, } from './mutations.js';
|
|
19
|
+
export { secondsRemaining } from './window.js';
|
|
19
20
|
//# sourceMappingURL=index.js.map
|
package/dist/window.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* window.ts — the freshness budget a caller has left on an envelope. The constructor stamps
|
|
3
|
+
* `valid_until` (5 minutes from construction on the hosted server by default; the tool description
|
|
4
|
+
* states the exact window) and the verifier and both signers refuse a stale envelope before every
|
|
5
|
+
* signature — so the WHOLE plan (allowance reset → approval → swap, each waited to a sealed block)
|
|
6
|
+
* must clear before `valid_until`. `verify` prints this so a user sees the budget instead of
|
|
7
|
+
* discovering it as STALE_ARTIFACT mid-plan.
|
|
8
|
+
*/
|
|
9
|
+
/** Whole seconds until `validUntil` (an ISO-8601 instant), never negative; `NaN` in → 0. */
|
|
10
|
+
export declare function secondsRemaining(validUntil: string, nowMs?: number): number;
|
package/dist/window.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* window.ts — the freshness budget a caller has left on an envelope. The constructor stamps
|
|
3
|
+
* `valid_until` (5 minutes from construction on the hosted server by default; the tool description
|
|
4
|
+
* states the exact window) and the verifier and both signers refuse a stale envelope before every
|
|
5
|
+
* signature — so the WHOLE plan (allowance reset → approval → swap, each waited to a sealed block)
|
|
6
|
+
* must clear before `valid_until`. `verify` prints this so a user sees the budget instead of
|
|
7
|
+
* discovering it as STALE_ARTIFACT mid-plan.
|
|
8
|
+
*/
|
|
9
|
+
/** Whole seconds until `validUntil` (an ISO-8601 instant), never negative; `NaN` in → 0. */
|
|
10
|
+
export function secondsRemaining(validUntil, nowMs = Date.now()) {
|
|
11
|
+
const until = Date.parse(validUntil);
|
|
12
|
+
if (!Number.isFinite(until))
|
|
13
|
+
return 0;
|
|
14
|
+
return Math.max(0, Math.floor((until - nowMs) / 1000));
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=window.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "otto-execute",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "The user's side of Otto Model B: verify a prepare-only execution artifact from the Otto Intel MCP against your own intent and the ruled attribution, then sign and submit it with your own key (or under a delegation you minted). Otto never holds your keys or funds.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://docs.useotto.xyz",
|