settld 0.1.5 → 0.2.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.
- package/README.md +32 -0
- package/SETTLD_VERSION +1 -1
- package/bin/settld.js +58 -0
- package/docs/CIRCLE_SANDBOX_E2E.md +12 -0
- package/docs/QUICKSTART_MCP.md +41 -1
- package/docs/QUICKSTART_MCP_HOSTS.md +156 -89
- package/docs/QUICKSTART_POLICY_PACKS.md +65 -0
- package/docs/QUICKSTART_PROFILES.md +198 -0
- package/docs/README.md +18 -0
- package/docs/RELEASE_CHECKLIST.md +26 -0
- package/docs/RELEASING.md +1 -0
- package/docs/SLO.md +62 -1
- package/docs/SUMMARY.md +1 -0
- package/docs/gitbook/README.md +13 -1
- package/docs/gitbook/quickstart.md +57 -58
- package/docs/integrations/README.md +1 -0
- package/docs/integrations/openclaw/PUBLIC_QUICKSTART.md +95 -0
- package/docs/ops/DISPUTE_FINANCE_RECONCILIATION_PACKET.md +56 -0
- package/docs/ops/KERNEL_V0_SHIP_GATE.md +3 -1
- package/docs/ops/MCP_COMPATIBILITY_MATRIX.md +8 -6
- package/docs/ops/PRODUCTION_DEPLOYMENT_CHECKLIST.md +46 -9
- package/docs/ops/TRUST_CONFIG_WIZARD.md +37 -24
- package/docs/plans/2026-02-20-trust-os-v1-jira-backlog.md +348 -0
- package/docs/plans/2026-02-21-agent-economic-actor-operating-model.md +169 -0
- package/docs/plans/2026-02-21-trust-os-v1-strategy.md +241 -0
- package/docs/research/2026-02-21-agent-spend-host-landscape.md +57 -0
- package/docs/spec/ArbitrationOutcomeMapping.v1.md +62 -0
- package/docs/spec/DisputeCaseLifecycle.v1.md +51 -0
- package/docs/spec/OperatorAction.v1.md +90 -0
- package/docs/spec/PolicyDecision.v1.md +83 -0
- package/docs/spec/README.md +5 -0
- package/docs/spec/SettlementDecisionRecord.v2.md +2 -0
- package/docs/spec/schemas/OperatorAction.v1.schema.json +113 -0
- package/docs/spec/schemas/PolicyDecision.v1.schema.json +74 -0
- package/docs/spec/schemas/SettlementDecisionRecord.v2.schema.json +1 -0
- package/docs/spec/x402-error-codes.v1.txt +14 -0
- package/package.json +14 -1
- package/scripts/ci/build-launch-cutover-packet.mjs +177 -21
- package/scripts/ci/run-10x-throughput-drill.mjs +76 -4
- package/scripts/ci/run-10x-throughput-incident-rehearsal.mjs +49 -6
- package/scripts/ci/run-mcp-host-cert-matrix.mjs +201 -0
- package/scripts/ci/run-mcp-host-smoke.mjs +203 -5
- package/scripts/ci/run-offline-verification-parity-gate.mjs +762 -0
- package/scripts/ci/run-onboarding-host-success-gate.mjs +516 -0
- package/scripts/ci/run-onboarding-policy-slo-gate.mjs +537 -0
- package/scripts/ci/run-production-cutover-gate.mjs +540 -0
- package/scripts/ci/run-public-openclaw-npx-smoke.mjs +148 -0
- package/scripts/ci/run-release-promotion-guard.mjs +756 -0
- package/scripts/doctor/mcp-host.mjs +120 -0
- package/scripts/mcp/settld-mcp-server.mjs +330 -20
- package/scripts/ops/dispute-finance-reconciliation-packet.mjs +313 -0
- package/scripts/ops/hosted-baseline-evidence.mjs +286 -77
- package/scripts/ops/run-x402-hitl-smoke.mjs +607 -0
- package/scripts/policy/cli.mjs +600 -0
- package/scripts/profile/cli.mjs +1324 -0
- package/scripts/register-entity-secret.mjs +102 -0
- package/scripts/setup/circle-bootstrap.mjs +310 -0
- package/scripts/setup/host-config.mjs +617 -0
- package/scripts/setup/onboard.mjs +1337 -0
- package/scripts/setup/openclaw-onboard.mjs +423 -0
- package/scripts/setup/wizard.mjs +986 -0
- package/scripts/slo/check.mjs +123 -62
- package/scripts/spec/generate-protocol-vectors.mjs +88 -0
- package/scripts/test/run.sh +23 -9
- package/services/x402-gateway/src/server.js +147 -36
- package/src/api/app.js +2345 -267
- package/src/api/middleware/trust-kernel.js +114 -0
- package/src/api/openapi.js +598 -3
- package/src/api/persistence.js +184 -0
- package/src/api/store.js +277 -0
- package/src/core/agent-wallets.js +134 -0
- package/src/core/event-policy.js +21 -2
- package/src/core/operator-action.js +303 -0
- package/src/core/policy-decision.js +322 -0
- package/src/core/policy-packs.js +207 -0
- package/src/core/profile-fingerprint.js +27 -0
- package/src/core/profile-simulation-reasons.js +84 -0
- package/src/core/profile-templates.js +242 -0
- package/src/core/settlement-kernel.js +27 -1
- package/src/core/wallet-assignment-resolver.js +129 -0
- package/src/core/wallet-provider-bootstrap.js +365 -0
- package/src/db/store-pg.js +631 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
|
|
6
|
+
import { registerEntitySecretCiphertext } from "@circle-fin/developer-controlled-wallets";
|
|
7
|
+
|
|
8
|
+
function usage() {
|
|
9
|
+
const text = [
|
|
10
|
+
"usage:",
|
|
11
|
+
" node scripts/register-entity-secret.mjs [--api-key <key>] [--entity-secret-hex <hex64>] [--recovery-dir <path>]",
|
|
12
|
+
"",
|
|
13
|
+
"env fallbacks:",
|
|
14
|
+
" CIRCLE_API_KEY",
|
|
15
|
+
" CIRCLE_ENTITY_SECRET_HEX (or ENTITY_SECRET)",
|
|
16
|
+
" CIRCLE_RECOVERY_DIR (default: ./artifacts/circle-recovery)"
|
|
17
|
+
].join("\n");
|
|
18
|
+
process.stderr.write(`${text}\n`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function readArgValue(argv, index, arg) {
|
|
22
|
+
const raw = String(arg ?? "");
|
|
23
|
+
const eq = raw.indexOf("=");
|
|
24
|
+
if (eq >= 0) return { value: raw.slice(eq + 1), nextIndex: index };
|
|
25
|
+
return { value: String(argv[index + 1] ?? ""), nextIndex: index + 1 };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function parseArgs(argv) {
|
|
29
|
+
const out = {
|
|
30
|
+
apiKey: null,
|
|
31
|
+
entitySecretHex: null,
|
|
32
|
+
recoveryDir: null,
|
|
33
|
+
help: false
|
|
34
|
+
};
|
|
35
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
36
|
+
const arg = String(argv[i] ?? "");
|
|
37
|
+
if (arg === "--help" || arg === "-h") {
|
|
38
|
+
out.help = true;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (arg === "--api-key" || arg.startsWith("--api-key=")) {
|
|
42
|
+
const parsed = readArgValue(argv, i, arg);
|
|
43
|
+
out.apiKey = parsed.value;
|
|
44
|
+
i = parsed.nextIndex;
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (arg === "--entity-secret-hex" || arg.startsWith("--entity-secret-hex=")) {
|
|
48
|
+
const parsed = readArgValue(argv, i, arg);
|
|
49
|
+
out.entitySecretHex = parsed.value;
|
|
50
|
+
i = parsed.nextIndex;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (arg === "--recovery-dir" || arg.startsWith("--recovery-dir=")) {
|
|
54
|
+
const parsed = readArgValue(argv, i, arg);
|
|
55
|
+
out.recoveryDir = parsed.value;
|
|
56
|
+
i = parsed.nextIndex;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function normalizeHex64(value) {
|
|
65
|
+
const raw = String(value ?? "").trim().toLowerCase();
|
|
66
|
+
if (!raw) return null;
|
|
67
|
+
if (!/^[0-9a-f]{64}$/.test(raw)) {
|
|
68
|
+
throw new Error("entity secret must be a 64-char hex string");
|
|
69
|
+
}
|
|
70
|
+
return raw;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function main(argv = process.argv.slice(2)) {
|
|
74
|
+
const args = parseArgs(argv);
|
|
75
|
+
if (args.help) {
|
|
76
|
+
usage();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const apiKey = String(args.apiKey ?? process.env.CIRCLE_API_KEY ?? "").trim();
|
|
81
|
+
const entitySecret = normalizeHex64(args.entitySecretHex ?? process.env.CIRCLE_ENTITY_SECRET_HEX ?? process.env.ENTITY_SECRET ?? "");
|
|
82
|
+
const recoveryFileDownloadPath = path.resolve(
|
|
83
|
+
process.cwd(),
|
|
84
|
+
String(args.recoveryDir ?? process.env.CIRCLE_RECOVERY_DIR ?? "./artifacts/circle-recovery")
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (!apiKey) throw new Error("CIRCLE_API_KEY missing (or pass --api-key)");
|
|
88
|
+
if (!entitySecret) throw new Error("CIRCLE_ENTITY_SECRET_HEX missing (or pass --entity-secret-hex)");
|
|
89
|
+
|
|
90
|
+
const res = await registerEntitySecretCiphertext({
|
|
91
|
+
apiKey,
|
|
92
|
+
entitySecret,
|
|
93
|
+
recoveryFileDownloadPath
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
process.stdout.write(`${JSON.stringify(res?.data ?? res, null, 2)}\n`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
main(process.argv.slice(2)).catch((err) => {
|
|
100
|
+
process.stderr.write(`${err?.message ?? String(err)}\n`);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
});
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import process from "node:process";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
import { bootstrapCircleProvider } from "../../src/core/wallet-provider-bootstrap.js";
|
|
9
|
+
import { registerEntitySecretCiphertext } from "@circle-fin/developer-controlled-wallets";
|
|
10
|
+
|
|
11
|
+
const MODE_SET = new Set(["auto", "sandbox", "production"]);
|
|
12
|
+
const FORMAT_SET = new Set(["text", "json"]);
|
|
13
|
+
|
|
14
|
+
const DEFAULTS = Object.freeze({
|
|
15
|
+
mode: "auto",
|
|
16
|
+
format: "text",
|
|
17
|
+
faucet: null,
|
|
18
|
+
outEnv: null,
|
|
19
|
+
includeApiKey: true,
|
|
20
|
+
registerEntitySecret: false,
|
|
21
|
+
recoveryDir: "artifacts/circle-recovery"
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function usage() {
|
|
25
|
+
const text = [
|
|
26
|
+
"usage:",
|
|
27
|
+
" node scripts/setup/circle-bootstrap.mjs [flags]",
|
|
28
|
+
"",
|
|
29
|
+
"flags:",
|
|
30
|
+
" --api-key <key> Circle API key (or CIRCLE_API_KEY env)",
|
|
31
|
+
" --mode <auto|sandbox|production> Host/mode selection (default: auto)",
|
|
32
|
+
" --base-url <url> Override Circle base URL",
|
|
33
|
+
" --blockchain <name> Override blockchain (default: BASE-SEPOLIA sandbox, BASE production)",
|
|
34
|
+
" --spend-wallet-id <id> Use a specific spend wallet id",
|
|
35
|
+
" --escrow-wallet-id <id> Use a specific escrow wallet id",
|
|
36
|
+
" --token-id-usdc <id> Force USDC token id",
|
|
37
|
+
" --entity-secret-hex <hex64> Force entity secret hex (otherwise generated)",
|
|
38
|
+
" --faucet Request sandbox faucet top-ups",
|
|
39
|
+
" --no-faucet Disable faucet requests",
|
|
40
|
+
" --register-entity-secret Register entity secret ciphertext with Circle SDK",
|
|
41
|
+
" --recovery-dir <path> Recovery file output dir for registration",
|
|
42
|
+
" --format <text|json> Output format (default: text)",
|
|
43
|
+
" --out-env <path> Write KEY=VALUE lines to file",
|
|
44
|
+
" --exclude-api-key Do not include CIRCLE_API_KEY in printed exports",
|
|
45
|
+
" --help Show this help"
|
|
46
|
+
].join("\n");
|
|
47
|
+
process.stderr.write(`${text}\n`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function readArgValue(argv, index, arg) {
|
|
51
|
+
const raw = String(arg ?? "");
|
|
52
|
+
const eq = raw.indexOf("=");
|
|
53
|
+
if (eq >= 0) return { value: raw.slice(eq + 1), nextIndex: index };
|
|
54
|
+
return { value: String(argv[index + 1] ?? ""), nextIndex: index + 1 };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function parseArgs(argv) {
|
|
58
|
+
const out = {
|
|
59
|
+
...DEFAULTS,
|
|
60
|
+
apiKey: null,
|
|
61
|
+
baseUrl: null,
|
|
62
|
+
blockchain: null,
|
|
63
|
+
spendWalletId: null,
|
|
64
|
+
escrowWalletId: null,
|
|
65
|
+
tokenIdUsdc: null,
|
|
66
|
+
entitySecretHex: null,
|
|
67
|
+
help: false
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
71
|
+
const arg = String(argv[i] ?? "");
|
|
72
|
+
if (!arg) continue;
|
|
73
|
+
if (arg === "--help" || arg === "-h") {
|
|
74
|
+
out.help = true;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (arg === "--faucet") {
|
|
78
|
+
out.faucet = true;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (arg === "--no-faucet") {
|
|
82
|
+
out.faucet = false;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
if (arg === "--exclude-api-key") {
|
|
86
|
+
out.includeApiKey = false;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (arg === "--register-entity-secret") {
|
|
90
|
+
out.registerEntitySecret = true;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (arg === "--api-key" || arg.startsWith("--api-key=")) {
|
|
95
|
+
const parsed = readArgValue(argv, i, arg);
|
|
96
|
+
out.apiKey = parsed.value;
|
|
97
|
+
i = parsed.nextIndex;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (arg === "--mode" || arg.startsWith("--mode=")) {
|
|
101
|
+
const parsed = readArgValue(argv, i, arg);
|
|
102
|
+
out.mode = String(parsed.value).trim().toLowerCase();
|
|
103
|
+
i = parsed.nextIndex;
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (arg === "--base-url" || arg.startsWith("--base-url=")) {
|
|
107
|
+
const parsed = readArgValue(argv, i, arg);
|
|
108
|
+
out.baseUrl = parsed.value;
|
|
109
|
+
i = parsed.nextIndex;
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (arg === "--blockchain" || arg.startsWith("--blockchain=")) {
|
|
113
|
+
const parsed = readArgValue(argv, i, arg);
|
|
114
|
+
out.blockchain = parsed.value;
|
|
115
|
+
i = parsed.nextIndex;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (arg === "--spend-wallet-id" || arg.startsWith("--spend-wallet-id=")) {
|
|
119
|
+
const parsed = readArgValue(argv, i, arg);
|
|
120
|
+
out.spendWalletId = parsed.value;
|
|
121
|
+
i = parsed.nextIndex;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (arg === "--escrow-wallet-id" || arg.startsWith("--escrow-wallet-id=")) {
|
|
125
|
+
const parsed = readArgValue(argv, i, arg);
|
|
126
|
+
out.escrowWalletId = parsed.value;
|
|
127
|
+
i = parsed.nextIndex;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (arg === "--token-id-usdc" || arg.startsWith("--token-id-usdc=")) {
|
|
131
|
+
const parsed = readArgValue(argv, i, arg);
|
|
132
|
+
out.tokenIdUsdc = parsed.value;
|
|
133
|
+
i = parsed.nextIndex;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (arg === "--entity-secret-hex" || arg.startsWith("--entity-secret-hex=")) {
|
|
137
|
+
const parsed = readArgValue(argv, i, arg);
|
|
138
|
+
out.entitySecretHex = parsed.value;
|
|
139
|
+
i = parsed.nextIndex;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
if (arg === "--format" || arg.startsWith("--format=")) {
|
|
143
|
+
const parsed = readArgValue(argv, i, arg);
|
|
144
|
+
out.format = String(parsed.value).trim().toLowerCase();
|
|
145
|
+
i = parsed.nextIndex;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
if (arg === "--out-env" || arg.startsWith("--out-env=")) {
|
|
149
|
+
const parsed = readArgValue(argv, i, arg);
|
|
150
|
+
out.outEnv = parsed.value;
|
|
151
|
+
i = parsed.nextIndex;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (arg === "--recovery-dir" || arg.startsWith("--recovery-dir=")) {
|
|
155
|
+
const parsed = readArgValue(argv, i, arg);
|
|
156
|
+
out.recoveryDir = parsed.value;
|
|
157
|
+
i = parsed.nextIndex;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
throw new Error(`unknown argument: ${arg}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!MODE_SET.has(out.mode)) {
|
|
165
|
+
throw new Error("--mode must be auto|sandbox|production");
|
|
166
|
+
}
|
|
167
|
+
if (!FORMAT_SET.has(out.format)) {
|
|
168
|
+
throw new Error("--format must be text|json");
|
|
169
|
+
}
|
|
170
|
+
if (out.outEnv) {
|
|
171
|
+
out.outEnv = path.resolve(process.cwd(), out.outEnv);
|
|
172
|
+
}
|
|
173
|
+
if (out.recoveryDir) {
|
|
174
|
+
out.recoveryDir = path.resolve(process.cwd(), out.recoveryDir);
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function shellQuote(value) {
|
|
180
|
+
const s = String(value ?? "");
|
|
181
|
+
if (!s) return "''";
|
|
182
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function toEnvFileText(env) {
|
|
186
|
+
const keys = Object.keys(env).sort();
|
|
187
|
+
return `${keys.map((k) => `${k}=${env[k]}`).join("\n")}\n`;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function toExportText(env) {
|
|
191
|
+
const keys = Object.keys(env).sort();
|
|
192
|
+
return keys.map((k) => `export ${k}=${shellQuote(env[k])}`).join("\n");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function maskKey(value) {
|
|
196
|
+
const raw = String(value ?? "");
|
|
197
|
+
if (raw.length <= 14) return "***";
|
|
198
|
+
return `${raw.slice(0, 10)}...${raw.slice(-4)}`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function maybeRegisterEntitySecret({ apiKey, entitySecretHex, recoveryDir }) {
|
|
202
|
+
await fs.mkdir(recoveryDir, { recursive: true });
|
|
203
|
+
const result = await registerEntitySecretCiphertext({
|
|
204
|
+
apiKey,
|
|
205
|
+
entitySecret: entitySecretHex,
|
|
206
|
+
recoveryFileDownloadPath: recoveryDir
|
|
207
|
+
});
|
|
208
|
+
return result?.data ?? result ?? null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function runCircleBootstrap({ argv = process.argv.slice(2), fetchImpl = fetch, stdout = process.stdout } = {}) {
|
|
212
|
+
const args = parseArgs(argv);
|
|
213
|
+
if (args.help) {
|
|
214
|
+
usage();
|
|
215
|
+
return { ok: true, code: 0 };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const apiKey = String(args.apiKey ?? process.env.CIRCLE_API_KEY ?? "").trim();
|
|
219
|
+
if (!apiKey) throw new Error("CIRCLE_API_KEY / --api-key is required");
|
|
220
|
+
|
|
221
|
+
const providerResult = await bootstrapCircleProvider({
|
|
222
|
+
apiKey,
|
|
223
|
+
mode: args.mode,
|
|
224
|
+
baseUrl: args.baseUrl,
|
|
225
|
+
blockchain: args.blockchain,
|
|
226
|
+
spendWalletId: args.spendWalletId,
|
|
227
|
+
escrowWalletId: args.escrowWalletId,
|
|
228
|
+
tokenIdUsdc: args.tokenIdUsdc,
|
|
229
|
+
faucet: args.faucet,
|
|
230
|
+
includeApiKey: args.includeApiKey,
|
|
231
|
+
entitySecretHex: args.entitySecretHex,
|
|
232
|
+
fetchImpl
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
let entitySecretRegistration = null;
|
|
236
|
+
if (args.registerEntitySecret) {
|
|
237
|
+
entitySecretRegistration = await maybeRegisterEntitySecret({
|
|
238
|
+
apiKey,
|
|
239
|
+
entitySecretHex: providerResult.entitySecretHex,
|
|
240
|
+
recoveryDir: args.recoveryDir
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const payload = {
|
|
245
|
+
ok: true,
|
|
246
|
+
mode: providerResult.mode,
|
|
247
|
+
baseUrl: providerResult.baseUrl,
|
|
248
|
+
blockchain: providerResult.blockchain,
|
|
249
|
+
wallets: providerResult.wallets,
|
|
250
|
+
tokenIdUsdc: providerResult.tokenIdUsdc,
|
|
251
|
+
entitySecretHex: providerResult.entitySecretHex,
|
|
252
|
+
entitySecretRegistration,
|
|
253
|
+
faucetEnabled: providerResult.faucetEnabled,
|
|
254
|
+
faucetResults: providerResult.faucetResults,
|
|
255
|
+
env: providerResult.env,
|
|
256
|
+
apiKeyMasked: maskKey(apiKey),
|
|
257
|
+
outEnv: args.outEnv ?? null
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
if (args.outEnv) {
|
|
261
|
+
await fs.mkdir(path.dirname(args.outEnv), { recursive: true });
|
|
262
|
+
await fs.writeFile(args.outEnv, toEnvFileText(providerResult.env), "utf8");
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (args.format === "json") {
|
|
266
|
+
stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
267
|
+
} else {
|
|
268
|
+
const lines = [];
|
|
269
|
+
lines.push("Circle bootstrap complete.");
|
|
270
|
+
lines.push(`Mode: ${providerResult.mode}`);
|
|
271
|
+
lines.push(`Base URL: ${providerResult.baseUrl}`);
|
|
272
|
+
lines.push(`Blockchain: ${providerResult.blockchain}`);
|
|
273
|
+
lines.push(`API key: ${maskKey(apiKey)}`);
|
|
274
|
+
lines.push(`Spend wallet: ${providerResult.wallets?.spend?.walletId ?? "n/a"} (${providerResult.wallets?.spend?.address ?? "n/a"})`);
|
|
275
|
+
lines.push(`Escrow wallet: ${providerResult.wallets?.escrow?.walletId ?? "n/a"} (${providerResult.wallets?.escrow?.address ?? "n/a"})`);
|
|
276
|
+
lines.push(`USDC token id: ${providerResult.tokenIdUsdc}`);
|
|
277
|
+
if (args.registerEntitySecret) {
|
|
278
|
+
lines.push(`Entity secret registered: yes (recovery dir: ${args.recoveryDir})`);
|
|
279
|
+
}
|
|
280
|
+
if (args.outEnv) {
|
|
281
|
+
lines.push(`Wrote env file: ${args.outEnv}`);
|
|
282
|
+
}
|
|
283
|
+
if (providerResult.faucetEnabled) {
|
|
284
|
+
const faucetSummary = providerResult.faucetResults.map((row) => `${row.wallet}=HTTP${row.status}`).join(", ");
|
|
285
|
+
lines.push(`Faucet: ${faucetSummary}`);
|
|
286
|
+
}
|
|
287
|
+
lines.push("");
|
|
288
|
+
lines.push("Shell exports:");
|
|
289
|
+
lines.push(toExportText(providerResult.env));
|
|
290
|
+
lines.push("");
|
|
291
|
+
lines.push("Railway variables (Raw Editor):");
|
|
292
|
+
lines.push(toEnvFileText(providerResult.env).trimEnd());
|
|
293
|
+
stdout.write(`${lines.join("\n")}\n`);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return payload;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
async function main(argv = process.argv.slice(2)) {
|
|
300
|
+
try {
|
|
301
|
+
await runCircleBootstrap({ argv });
|
|
302
|
+
} catch (err) {
|
|
303
|
+
process.stderr.write(`${err?.message ?? String(err)}\n`);
|
|
304
|
+
process.exit(1);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
309
|
+
main();
|
|
310
|
+
}
|