settld 0.2.0 → 0.2.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 CHANGED
@@ -86,6 +86,27 @@ npm install -g settld
86
86
  settld setup
87
87
  ```
88
88
 
89
+ Check wallet wiring and funding path:
90
+
91
+ ```sh
92
+ settld wallet status
93
+ settld wallet fund --open
94
+ settld wallet fund --method transfer
95
+ settld wallet balance --watch --min-usdc 1
96
+ ```
97
+
98
+ Hosted top-up (recommended): configure Coinbase Hosted Onramp on the backend so `settld wallet fund --open` launches funding directly:
99
+
100
+ ```sh
101
+ export MAGIC_LINK_WALLET_FUND_PROVIDER='coinbase'
102
+ export MAGIC_LINK_COINBASE_API_KEY_VALUE='organizations/<org_id>/apiKeys/<key_id>'
103
+ export MAGIC_LINK_COINBASE_API_SECRET_KEY='-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----'
104
+ export MAGIC_LINK_COINBASE_PROJECT_ID='<project_id>'
105
+ export MAGIC_LINK_COINBASE_DESTINATION_NETWORK='base'
106
+ export MAGIC_LINK_COINBASE_ASSET='USDC'
107
+ export MAGIC_LINK_COINBASE_FIAT_CURRENCY='USD'
108
+ ```
109
+
89
110
  Legacy setup wizard (advanced / old flags):
90
111
 
91
112
  ```sh
package/SETTLD_VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
package/bin/settld.js CHANGED
@@ -9,6 +9,7 @@ function usage() {
9
9
  console.error("usage:");
10
10
  console.error(" settld --version");
11
11
  console.error(" settld onboard [--help]");
12
+ console.error(" settld login [--help]");
12
13
  console.error(" settld setup [--help]");
13
14
  console.error(" settld setup legacy [--help]");
14
15
  console.error(" settld setup circle [--help]");
@@ -21,6 +22,15 @@ function usage() {
21
22
  console.error(" settld closepack export --agreement-hash <sha256> --out <path.zip> [--ops-token tok_ops] [--base-url http://127.0.0.1:3000] [--tenant-id tenant_default] [--protocol 1.0]");
22
23
  console.error(" settld closepack verify <path.zip> [--json-out <path.json>]");
23
24
  console.error(" settld x402 receipt verify <receipt.json|-> [--strict] [--format json|text] [--json-out <path>]");
25
+ console.error(
26
+ " settld wallet status [--base-url <url>] [--tenant-id <id>] [--session-file <path>] [--cookie <cookie>] [--magic-link-api-key <key>] [--format text|json] [--json-out <path>]"
27
+ );
28
+ console.error(
29
+ " settld wallet fund [--method card|bank|transfer|faucet] [--open] [--hosted-url <url>] [--non-interactive] [--base-url <url>] [--tenant-id <id>] [--session-file <path>] [--cookie <cookie>] [--magic-link-api-key <key>] [--format text|json] [--json-out <path>]"
30
+ );
31
+ console.error(
32
+ " settld wallet balance [--watch] [--min-usdc <amount>] [--interval-seconds <n>] [--timeout-seconds <n>] [--base-url <url>] [--tenant-id <id>] [--session-file <path>] [--cookie <cookie>] [--magic-link-api-key <key>] [--format text|json] [--json-out <path>]"
33
+ );
24
34
  console.error(" settld profile list [--format json|text] [--json-out <path>]");
25
35
  console.error(" settld profile init <profile-id> [--out <path>] [--force] [--format json|text] [--json-out <path>]");
26
36
  console.error(
@@ -138,6 +148,10 @@ function main() {
138
148
  return runNodeScript("scripts/setup/onboard.mjs", argv.slice(1));
139
149
  }
140
150
 
151
+ if (cmd === "login") {
152
+ return runNodeScript("scripts/setup/login.mjs", argv.slice(1));
153
+ }
154
+
141
155
  if (cmd === "doctor") {
142
156
  return runNodeScript("scripts/doctor/mcp-host.mjs", argv.slice(1));
143
157
  }
@@ -266,6 +280,10 @@ function main() {
266
280
  process.exit(1);
267
281
  }
268
282
 
283
+ if (cmd === "wallet") {
284
+ return runNodeScript("scripts/wallet/cli.mjs", argv.slice(1));
285
+ }
286
+
269
287
  if (cmd === "profile") {
270
288
  return runNodeScript("scripts/profile/cli.mjs", argv.slice(1));
271
289
  }
@@ -17,7 +17,9 @@ Required inputs:
17
17
 
18
18
  - `SETTLD_BASE_URL` (local or hosted API URL)
19
19
  - `SETTLD_TENANT_ID`
20
- - `SETTLD_API_KEY` (`keyId.secret`)
20
+ - one of:
21
+ - `SETTLD_API_KEY` (`keyId.secret`), or
22
+ - `SETTLD_BOOTSTRAP_API_KEY` (onboarding bootstrap key that mints `SETTLD_API_KEY` during setup)
21
23
  - Node.js 20+
22
24
 
23
25
  Recommended non-interactive pattern:
@@ -35,6 +37,20 @@ settld setup --non-interactive \
35
37
  --out-env ./.tmp/settld-openclaw.env
36
38
  ```
37
39
 
40
+ If you want setup to generate the tenant API key for you:
41
+
42
+ ```bash
43
+ settld setup --non-interactive \
44
+ --host openclaw \
45
+ --base-url https://api.settld.work \
46
+ --tenant-id tenant_default \
47
+ --bootstrap-api-key 'ml_admin_xxx' \
48
+ --wallet-mode managed \
49
+ --wallet-bootstrap remote \
50
+ --profile-id engineering-spend \
51
+ --smoke
52
+ ```
53
+
38
54
  If you want validation only (no config writes):
39
55
 
40
56
  ```bash
@@ -153,7 +169,60 @@ Then activate host-side:
153
169
  - `cursor`: restart Cursor.
154
170
  - `openclaw`: run `openclaw doctor`, ensure OpenClaw onboarding is complete (`openclaw onboard --install-daemon`), then run `openclaw tui`.
155
171
 
156
- ## 5) How the agent uses Settld after activation
172
+ ## 5) Fund and verify wallet state
173
+
174
+ Check wallet assignment after setup:
175
+
176
+ ```bash
177
+ settld wallet status
178
+ ```
179
+
180
+ Funding paths:
181
+
182
+ ```bash
183
+ # Guided selector (recommended)
184
+ settld wallet fund --open
185
+
186
+ # Hosted flow (card/bank) - provider-hosted URL, add --open to launch browser
187
+ settld wallet fund --method card --open
188
+ settld wallet fund --method bank --open
189
+
190
+ # Direct transfer path (prints chain + destination address)
191
+ settld wallet fund --method transfer
192
+
193
+ # Sandbox only: request faucet top-up
194
+ settld wallet fund --method faucet
195
+ ```
196
+
197
+ Provider-hosted card/bank links are configured on the control-plane backend.
198
+
199
+ Option A (recommended): Coinbase Hosted Onramp:
200
+
201
+ ```bash
202
+ export MAGIC_LINK_WALLET_FUND_PROVIDER='coinbase'
203
+ export MAGIC_LINK_COINBASE_API_KEY_VALUE='organizations/<org_id>/apiKeys/<key_id>'
204
+ export MAGIC_LINK_COINBASE_API_SECRET_KEY='-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----'
205
+ export MAGIC_LINK_COINBASE_PROJECT_ID='<project_id>'
206
+ export MAGIC_LINK_COINBASE_DESTINATION_NETWORK='base'
207
+ export MAGIC_LINK_COINBASE_ASSET='USDC'
208
+ export MAGIC_LINK_COINBASE_FIAT_CURRENCY='USD'
209
+ ```
210
+
211
+ Option B: explicit card/bank URLs:
212
+
213
+ ```bash
214
+ # backend env (magic-link service)
215
+ export MAGIC_LINK_WALLET_FUND_CARD_URL='https://pay.example.com/topup?tenant={tenantId}&method=card&address={walletAddress}'
216
+ export MAGIC_LINK_WALLET_FUND_BANK_URL='https://pay.example.com/topup?tenant={tenantId}&method=bank&address={walletAddress}'
217
+ ```
218
+
219
+ After funding, wait until spend wallet has balance:
220
+
221
+ ```bash
222
+ settld wallet balance --watch --min-usdc 1
223
+ ```
224
+
225
+ ## 6) How the agent uses Settld after activation
157
226
 
158
227
  After host activation, the agent interacts with Settld through MCP `settld.*` tools.
159
228
 
@@ -184,7 +253,7 @@ Verify first receipt from artifacts:
184
253
  settld x402 receipt verify <artifactDir>/x402-receipt.json --json-out /tmp/settld-first-receipt.json
185
254
  ```
186
255
 
187
- ## 6) Host config helper customization
256
+ ## 7) Host config helper customization
188
257
 
189
258
  Default host configuration logic is in:
190
259
 
@@ -198,10 +267,15 @@ settld setup --host-config ./path/to/custom-host-config.mjs
198
267
 
199
268
  Your helper should provide resolver/setup exports compatible with `scripts/setup/wizard.mjs`.
200
269
 
201
- ## 7) Troubleshooting
270
+ ## 8) Troubleshooting
202
271
 
203
272
  - `BYO wallet mode missing required env keys`
204
273
  - Provide all required Circle keys in section 3.
274
+ - `auth required: pass --cookie/--magic-link-api-key or run settld login first`
275
+ - Run `settld login`, then retry `settld wallet status` / `settld wallet fund`.
276
+ - `no hosted funding URL configured for card/bank`
277
+ - set backend Coinbase env (`MAGIC_LINK_WALLET_FUND_PROVIDER=coinbase`, `MAGIC_LINK_COINBASE_API_KEY_VALUE`, `MAGIC_LINK_COINBASE_API_SECRET_KEY`) or set explicit `MAGIC_LINK_WALLET_FUND_CARD_URL` / `MAGIC_LINK_WALLET_FUND_BANK_URL`.
278
+ - pass `--hosted-url` for an ad-hoc override.
205
279
  - `host config helper missing`
206
280
  - Add `scripts/setup/host-config.mjs` or pass `--host-config`.
207
281
  - `SETTLD_API_KEY must be a non-empty string`
@@ -51,7 +51,45 @@ source ./.tmp/settld.env
51
51
 
52
52
  Then restart your host app (Codex/Claude/Cursor/OpenClaw) so it reloads MCP config.
53
53
 
54
- ## 2) Verify MCP connectivity
54
+ ## 2) Check wallet and fund it
55
+
56
+ ```bash
57
+ settld wallet status
58
+ settld wallet fund --method transfer
59
+ settld wallet balance --watch --min-usdc 1
60
+ ```
61
+
62
+ Optional methods:
63
+
64
+ ```bash
65
+ settld wallet fund --open
66
+ settld wallet fund --method card --open
67
+ settld wallet fund --method bank --open
68
+ settld wallet fund --method faucet
69
+ ```
70
+
71
+ For card/bank, configure one hosted provider URL strategy on the control-plane backend.
72
+
73
+ Option A (recommended): Coinbase Hosted Onramp:
74
+
75
+ ```bash
76
+ export MAGIC_LINK_WALLET_FUND_PROVIDER='coinbase'
77
+ export MAGIC_LINK_COINBASE_API_KEY_VALUE='organizations/<org_id>/apiKeys/<key_id>'
78
+ export MAGIC_LINK_COINBASE_API_SECRET_KEY='-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----'
79
+ export MAGIC_LINK_COINBASE_PROJECT_ID='<project_id>'
80
+ export MAGIC_LINK_COINBASE_DESTINATION_NETWORK='base'
81
+ export MAGIC_LINK_COINBASE_ASSET='USDC'
82
+ export MAGIC_LINK_COINBASE_FIAT_CURRENCY='USD'
83
+ ```
84
+
85
+ Option B: explicit card/bank hosted templates:
86
+
87
+ ```bash
88
+ export MAGIC_LINK_WALLET_FUND_CARD_URL='https://pay.example.com/topup?tenant={tenantId}&method=card&address={walletAddress}'
89
+ export MAGIC_LINK_WALLET_FUND_BANK_URL='https://pay.example.com/topup?tenant={tenantId}&method=bank&address={walletAddress}'
90
+ ```
91
+
92
+ ## 3) Verify MCP connectivity
55
93
 
56
94
  ```bash
57
95
  npm run mcp:probe -- --call settld.about '{}'
@@ -62,7 +100,7 @@ Expected outcome:
62
100
  - `settld.about` succeeds
63
101
  - host can discover `settld.*` tools
64
102
 
65
- ## 3) Run first paid call
103
+ ## 4) Run first paid call
66
104
 
67
105
  ```bash
68
106
  npm run demo:mcp-paid-exa
@@ -75,7 +113,7 @@ Expected output includes:
75
113
  - `decisionId=...`
76
114
  - `settlementReceiptId=...`
77
115
 
78
- ## 4) Verify first receipt (proof packet)
116
+ ## 5) Verify first receipt (proof packet)
79
117
 
80
118
  ```bash
81
119
  jq -c 'first' <artifactDir>/x402-receipts.export.jsonl > /tmp/settld-first-receipt.json
@@ -84,7 +122,7 @@ settld x402 receipt verify /tmp/settld-first-receipt.json --format json --json-o
84
122
 
85
123
  `/tmp/settld-first-receipt.verify.json` is your deterministic verification artifact for audit/compliance.
86
124
 
87
- ## 5) Optional: policy profile workflows
125
+ ## 6) Optional: policy profile workflows
88
126
 
89
127
  ```bash
90
128
  settld profile list
@@ -99,5 +137,10 @@ settld profile simulate ./profiles/engineering-spend.profile.json --format json
99
137
  - ensure key is present in setup flags or shell env.
100
138
  - `BYO wallet mode missing required env keys`
101
139
  - provide all required Circle keys in `docs/QUICKSTART_MCP_HOSTS.md`.
140
+ - `auth required: pass --cookie/--magic-link-api-key or run settld login first`
141
+ - run `settld login`, then retry wallet commands.
142
+ - `no hosted funding URL configured for card/bank`
143
+ - set backend Coinbase env (`MAGIC_LINK_WALLET_FUND_PROVIDER=coinbase`, `MAGIC_LINK_COINBASE_API_KEY_VALUE`, `MAGIC_LINK_COINBASE_API_SECRET_KEY`) or set explicit `MAGIC_LINK_WALLET_FUND_CARD_URL` / `MAGIC_LINK_WALLET_FUND_BANK_URL`.
144
+ - pass `--hosted-url` for an ad-hoc override.
102
145
  - Host cannot find MCP tools
103
146
  - rerun setup, restart host, then rerun `npm run mcp:probe`.
@@ -53,6 +53,19 @@ npx -y settld@latest setup \
53
53
  --smoke
54
54
  ```
55
55
 
56
+ If you do not have a tenant `sk_*` yet, let setup mint one:
57
+
58
+ ```bash
59
+ npx -y settld@latest setup \
60
+ --non-interactive \
61
+ --host openclaw \
62
+ --base-url https://api.settld.work \
63
+ --tenant-id tenant_default \
64
+ --bootstrap-api-key 'ml_admin_xxx' \
65
+ --wallet-mode managed \
66
+ --wallet-bootstrap remote
67
+ ```
68
+
56
69
  ## 3) Verify OpenClaw + Settld are wired
57
70
 
58
71
  Run:
@@ -0,0 +1,42 @@
1
+ # Vercel Monorepo Deploy (Docs + Website)
2
+
3
+ Use **two separate Vercel projects** pointing at the same GitHub repo:
4
+
5
+ 1. `settld-docs` (MkDocs)
6
+ 2. `settld-site` (Dashboard website)
7
+
8
+ ## Project 1: `settld-docs` (MkDocs)
9
+
10
+ - Root Directory: repo root (`.`)
11
+ - Production Branch: `main`
12
+ - Build/Output config comes from `/vercel.json`:
13
+ - `installCommand`: `bash scripts/vercel/install-mkdocs.sh`
14
+ - `buildCommand`: `bash scripts/vercel/build-mkdocs.sh`
15
+ - `ignoreCommand`: `bash scripts/vercel/ignore-mkdocs.sh`
16
+ - `outputDirectory`: `mkdocs/site`
17
+
18
+ Deploy will run when docs-relevant files change (including `mkdocs/docs/**`).
19
+
20
+ ## Project 2: `settld-site` (Website)
21
+
22
+ - Root Directory: `dashboard`
23
+ - Production Branch: `main`
24
+ - Build/Output config comes from `/dashboard/vercel.json`:
25
+ - `installCommand`: `npm install`
26
+ - `buildCommand`: `npm run build`
27
+ - `ignoreCommand`: `bash ../scripts/vercel/ignore-dashboard.sh`
28
+ - `outputDirectory`: `dist`
29
+
30
+ Deploy will run when website-relevant files change (`dashboard/**` + deploy scripts/workflows).
31
+
32
+ ## Push Flow
33
+
34
+ 1. Commit and push changes to `main`.
35
+ 2. Verify both Vercel projects are connected to this repo and track `main`.
36
+ 3. Check the commit SHA in each Vercel deployment detail page matches the pushed commit.
37
+
38
+ ## Quick Troubleshooting
39
+
40
+ - Docs didn’t deploy: confirm changes touched `mkdocs/docs/**` or another path matched by `scripts/vercel/ignore-mkdocs.sh`.
41
+ - Website didn’t deploy: confirm changes touched `dashboard/**` or another path matched by `scripts/vercel/ignore-dashboard.sh`.
42
+ - Wrong commit deployed: confirm Vercel project production branch is `main`, not a feature branch.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "settld",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Settld kernel CLI and local control-plane tooling",
5
5
  "private": false,
6
6
  "type": "module",
@@ -136,6 +136,7 @@
136
136
  },
137
137
  "dependencies": {
138
138
  "@circle-fin/developer-controlled-wallets": "^10.1.0",
139
+ "@coinbase/cdp-sdk": "^1.44.1",
139
140
  "pg": "^8.11.5",
140
141
  "snarkjs": "^0.7.6"
141
142
  },
@@ -11,6 +11,10 @@ function randomId(prefix) {
11
11
  return `${prefix}_${crypto.randomBytes(6).toString("hex")}`;
12
12
  }
13
13
 
14
+ function buildScopedOpsToken(token) {
15
+ return `${String(token ?? "").trim()}:ops_read,ops_write,finance_read,finance_write,audit_read`;
16
+ }
17
+
14
18
  function pickPort() {
15
19
  return new Promise((resolve, reject) => {
16
20
  const server = net.createServer();
@@ -243,6 +247,7 @@ function stopServer(server) {
243
247
  async function main() {
244
248
  const reportPath = path.resolve(process.cwd(), process.env.MCP_HOST_SMOKE_REPORT_PATH || "artifacts/ops/mcp-host-smoke.json");
245
249
  const opsToken = randomId("ops");
250
+ const scopedOpsToken = buildScopedOpsToken(opsToken);
246
251
  const magicLinkApiKey = randomId("ml_admin");
247
252
  const tenantId = randomId("tenant");
248
253
 
@@ -259,6 +264,7 @@ async function main() {
259
264
  env: {
260
265
  PORT: String(apiPort),
261
266
  PROXY_BIND_HOST: "127.0.0.1",
267
+ PROXY_OPS_TOKENS: scopedOpsToken,
262
268
  PROXY_OPS_TOKEN: opsToken,
263
269
  PROXY_AUTOTICK_INTERVAL_MS: "200"
264
270
  }
@@ -141,6 +141,10 @@ function randomId(prefix) {
141
141
  return `${prefix}_${crypto.randomBytes(6).toString("hex")}`;
142
142
  }
143
143
 
144
+ function buildScopedOpsToken(token) {
145
+ return `${String(token ?? "").trim()}:ops_read,ops_write,finance_read,finance_write,audit_read`;
146
+ }
147
+
144
148
  function pickPort() {
145
149
  return new Promise((resolve, reject) => {
146
150
  const server = net.createServer();
@@ -239,6 +243,7 @@ function startNodeProc({ name, scriptPath, env }) {
239
243
  async function startEphemeralApi(env = process.env) {
240
244
  const port = await pickPort();
241
245
  const opsToken = randomId("ops");
246
+ const scopedOpsToken = buildScopedOpsToken(opsToken);
242
247
  const baseUrl = `http://127.0.0.1:${port}`;
243
248
  const api = startNodeProc({
244
249
  name: "cutover-api",
@@ -247,6 +252,7 @@ async function startEphemeralApi(env = process.env) {
247
252
  ...env,
248
253
  PORT: String(port),
249
254
  PROXY_BIND_HOST: "127.0.0.1",
255
+ PROXY_OPS_TOKENS: scopedOpsToken,
250
256
  PROXY_OPS_TOKEN: opsToken,
251
257
  PROXY_AUTOTICK_INTERVAL_MS: "200"
252
258
  }
@@ -192,6 +192,18 @@ function readEnvString(name, fallback = null) {
192
192
  return String(raw).trim();
193
193
  }
194
194
 
195
+ function readFirstOpsTokenFromScopedList(raw) {
196
+ const text = String(raw ?? "").trim();
197
+ if (!text) return null;
198
+ const firstEntry = text
199
+ .split(";")
200
+ .map((entry) => String(entry ?? "").trim())
201
+ .find(Boolean);
202
+ if (!firstEntry) return null;
203
+ const token = firstEntry.split(":")[0]?.trim() ?? "";
204
+ return token || null;
205
+ }
206
+
195
207
  function assertCircleModeInputs({ mode }) {
196
208
  if (mode === "stub") return;
197
209
  const required = ["CIRCLE_API_KEY", "CIRCLE_WALLET_ID_SPEND", "CIRCLE_WALLET_ID_ESCROW", "CIRCLE_TOKEN_ID_USDC"];
@@ -646,7 +658,10 @@ async function main() {
646
658
  const workload = normalizeWorkload(cli.workload ?? readEnvString("SETTLD_DEMO_WORKLOAD", "exa"));
647
659
  const externalReserveRequired = circleMode !== "stub";
648
660
  assertCircleModeInputs({ mode: circleMode });
649
- const opsToken = String(process.env.SETTLD_DEMO_OPS_TOKEN ?? "tok_ops").trim() || "tok_ops";
661
+ const inheritedOpsTokenList = readEnvString("PROXY_OPS_TOKENS", null);
662
+ const derivedOpsToken = readFirstOpsTokenFromScopedList(inheritedOpsTokenList);
663
+ const opsToken = String(process.env.SETTLD_DEMO_OPS_TOKEN ?? derivedOpsToken ?? "tok_ops").trim() || "tok_ops";
664
+ const scopedOpsToken = `${opsToken}:ops_read,ops_write,finance_read,finance_write,audit_read`;
650
665
  const tenantId = String(process.env.SETTLD_TENANT_ID ?? "tenant_default").trim() || "tenant_default";
651
666
 
652
667
  const workloadConfig = (() => {
@@ -733,6 +748,8 @@ async function main() {
733
748
  cmd: "node",
734
749
  args: ["src/api/server.js"],
735
750
  env: {
751
+ // Pin ops auth for this demo process so inherited deployment env can't cause token mismatch.
752
+ PROXY_OPS_TOKENS: scopedOpsToken,
736
753
  PROXY_OPS_TOKEN: opsToken,
737
754
  BIND_HOST: "127.0.0.1",
738
755
  PORT: String(apiPort),