@fluxpointstudios/orynq-sdk-quickstart 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.
@@ -0,0 +1,423 @@
1
+ import { TraceBundle } from '@fluxpointstudios/orynq-sdk-process-trace';
2
+
3
+ /**
4
+ * @summary Local sr25519 identity bootstrap for solo-dev quickstart.
5
+ *
6
+ * Generates a fresh BIP39 mnemonic on first run, derives an sr25519 keypair,
7
+ * and persists the mnemonic to `~/.orynq/config.json` (or any caller-supplied
8
+ * path) with 0600 permissions on POSIX systems. Subsequent calls reload the
9
+ * same identity so the address stays stable across processes.
10
+ *
11
+ * This is intentionally pure-local: no network, no chain RPC, no faucet.
12
+ * Anchoring + faucet drip belong in `bootstrap.ts` so callers who already
13
+ * have an identity can skip identity generation entirely.
14
+ *
15
+ * Trust model: the mnemonic on disk is treated like any other developer
16
+ * secret. The config file is created with 0600 perms; an explicit warning is
17
+ * emitted via the returned `OrynqIdentity.warnings` array when the env
18
+ * suggests a shared filesystem (which is reserved for a follow-up — kept
19
+ * as `warnings: []` today so the public shape stays stable).
20
+ */
21
+ /**
22
+ * Solo-dev identity loaded from disk or freshly generated.
23
+ *
24
+ * Fields:
25
+ * - `mnemonic` BIP39 12-word seed phrase. Required to sign on-chain
26
+ * txs and blob-gateway uploads. Treat as a secret.
27
+ * - `address` sr25519 SS58 address derived from `mnemonic`. Safe to
28
+ * log; this is the public chain identity.
29
+ * - `generatedAt` ISO timestamp of original generation.
30
+ * - `configPath` Where the identity is persisted.
31
+ * - `freshlyGenerated` True iff this call generated a new mnemonic (vs
32
+ * reloading an existing one). Lets the CLI print "saved
33
+ * new identity to..." only on the first run.
34
+ * - `warnings` Non-fatal advisories from the loader. Empty today;
35
+ * reserved for shared-FS / world-readable-perm checks.
36
+ */
37
+ interface OrynqIdentity {
38
+ mnemonic: string;
39
+ address: string;
40
+ generatedAt: string;
41
+ configPath: string;
42
+ freshlyGenerated: boolean;
43
+ warnings: string[];
44
+ }
45
+ interface LoadOrCreateIdentityOptions {
46
+ /**
47
+ * Path to the persistent identity file. Defaults to
48
+ * `${HOME}/.orynq/config.json`. The parent directory is created
49
+ * recursively if it does not exist.
50
+ */
51
+ configPath?: string | undefined;
52
+ /**
53
+ * SS58 prefix for the encoded address. Defaults to 42 (generic Substrate).
54
+ * Materios uses 42 in v6 preprod; pass a different value here if you're
55
+ * targeting a chain with a custom prefix.
56
+ */
57
+ ss58Format?: number | undefined;
58
+ }
59
+ /**
60
+ * Default config-file location: `~/.orynq/config.json`.
61
+ *
62
+ * Exposed so other code (`bootstrap.ts`, the CLI) can reference the same
63
+ * default without duplicating the homedir join.
64
+ */
65
+ declare function defaultConfigPath(): string;
66
+ /**
67
+ * Load an existing identity from `configPath`, or generate + persist a new
68
+ * one if the file does not exist.
69
+ *
70
+ * Throws if the config file exists but cannot be parsed — better to fail
71
+ * loudly than silently regenerate and orphan whatever identity used to be
72
+ * there (and any MATRA balance on it).
73
+ */
74
+ declare function loadOrCreateIdentity(opts?: LoadOrCreateIdentityOptions): Promise<OrynqIdentity>;
75
+
76
+ /**
77
+ * @summary Minimal trace-bundle factory used by `orynq init` / `orynq trace`.
78
+ *
79
+ * Wraps `@fluxpointstudios/orynq-sdk-process-trace` with a one-call helper
80
+ * that produces a finalised bundle from a single observation event. The
81
+ * heavyweight builder (multi-span, multi-event, custom kinds) lives in
82
+ * the underlying package — quickstart deliberately ships only the
83
+ * "hello world" path so a fresh dev sees a trace land before they're
84
+ * forced to learn span semantics.
85
+ */
86
+
87
+ /**
88
+ * Slimmed-down public view of a `TraceBundle` — exposes only the fields
89
+ * `orynq init` / `orynq trace` needs to print + the raw `content` JSON the
90
+ * caller will upload as a blob. The full `bundle` is preserved on the
91
+ * returned object so power-users can still walk events/spans.
92
+ */
93
+ interface TraceBundleLite {
94
+ runId: string;
95
+ agentId: string;
96
+ rootHash: string;
97
+ merkleRoot: string;
98
+ /**
99
+ * SHA-256 of the canonical JSON content payload as a hex string. Set by
100
+ * `firstTraceBundle()` so the same hash that ends up in the on-chain
101
+ * receipt is available without re-canonicalising downstream.
102
+ */
103
+ manifestHash: string;
104
+ /**
105
+ * Canonical JSON serialisation of `bundle.publicView`. This is what we
106
+ * upload to the blob gateway under `contentHash = sha256(content)`.
107
+ */
108
+ content: string;
109
+ /** Original full bundle, in case callers want spans/events. */
110
+ bundle: TraceBundle;
111
+ }
112
+ /**
113
+ * Optional deterministic-clock + identifier hooks. Used by the
114
+ * documentation tests + recipes that need stable hashes across runs.
115
+ *
116
+ * In normal use (production), callers pass nothing here and let the
117
+ * trace-builder pick wall-clock timestamps + random UUIDs.
118
+ */
119
+ interface DeterministicHooks {
120
+ /** Pin `new Date()`/`Date.now()` for the duration of this call. */
121
+ now?: () => Date;
122
+ /** Pin the run UUID returned by `createTrace`. */
123
+ runId?: string;
124
+ /** Pin the span UUID returned by `addSpan`. */
125
+ spanId?: string;
126
+ /** Pin the event UUID returned by `addEvent`. */
127
+ eventId?: string;
128
+ }
129
+ interface FirstTraceBundleOptions extends DeterministicHooks {
130
+ agentId: string;
131
+ /** Free-form one-liner appended as the public observation event. */
132
+ summary: string;
133
+ }
134
+ /**
135
+ * Build, finalise, and serialise a one-event, one-span trace bundle.
136
+ *
137
+ * The optional `now`/`runId`/`spanId`/`eventId` hooks are useful for tests
138
+ * that need byte-stable hashes; they patch the globals only for the
139
+ * duration of this single call and restore them in a `finally` block so
140
+ * we never leak the patch into surrounding code.
141
+ */
142
+ declare function firstTraceBundle(opts: FirstTraceBundleOptions): Promise<TraceBundleLite>;
143
+
144
+ /**
145
+ * @summary Free-tier MATRA faucet client.
146
+ *
147
+ * The Materios preprod blob-gateway exposes `POST /blobs/faucet/drip` (and
148
+ * `POST /faucet/drip` mounted at the same handler). One-shot per SS58
149
+ * address, IP-cooldown 5 min on the un-prefixed path. The dripped MATRA
150
+ * generates MOTRA (fee currency) over the next few blocks — that's how a
151
+ * fresh dev pays for their first `submit_receipt` extrinsic without an
152
+ * out-of-band funding step.
153
+ *
154
+ * Returns a discriminated union so callers can branch on `kind` without
155
+ * sniffing error messages.
156
+ */
157
+ interface FaucetDripSuccess {
158
+ kind: "success";
159
+ txHash: string;
160
+ amount: string;
161
+ message: string;
162
+ }
163
+ interface FaucetDripAlreadyFunded {
164
+ kind: "already-funded";
165
+ drippedAtMs: number;
166
+ }
167
+ interface FaucetDripCooldown {
168
+ kind: "cooldown";
169
+ retryAfterMs: number;
170
+ message: string;
171
+ }
172
+ interface FaucetDripError {
173
+ kind: "error";
174
+ status: number;
175
+ message: string;
176
+ }
177
+ type FaucetDripResult = FaucetDripSuccess | FaucetDripAlreadyFunded | FaucetDripCooldown | FaucetDripError;
178
+ interface RequestFaucetOptions {
179
+ /** SS58 address to drip MATRA into. */
180
+ address: string;
181
+ /**
182
+ * Gateway base URL. Accepts either `https://host` or `https://host/blobs`.
183
+ * The /blobs/-prefixed faucet path is preferred (per-address ledger);
184
+ * the bare /faucet path adds an IP-level 5-min cooldown so we leave it
185
+ * alone here.
186
+ */
187
+ gatewayBaseUrl: string;
188
+ /**
189
+ * Optional fetch impl injection (for tests + Cloudflare Workers).
190
+ * Defaults to the global `fetch`.
191
+ */
192
+ fetchImpl?: typeof fetch | undefined;
193
+ /**
194
+ * Optional AbortSignal — propagated to the underlying fetch so callers
195
+ * can wire up a Ctrl-C handler.
196
+ */
197
+ signal?: AbortSignal | undefined;
198
+ }
199
+ /**
200
+ * Drip MATRA to a fresh SS58 address.
201
+ *
202
+ * Idempotent at the caller's level: if the address has already been
203
+ * dripped (per-address ledger), returns `kind: "already-funded"` instead
204
+ * of throwing — the caller can treat both `success` and `already-funded`
205
+ * as "we have MATRA, proceed".
206
+ */
207
+ declare function requestFaucet(opts: RequestFaucetOptions): Promise<FaucetDripResult>;
208
+
209
+ /**
210
+ * @summary Compose the user-facing URLs that close the loop on "first trace".
211
+ *
212
+ * The DX requirement (#175) is: after submission, the SDK MUST print a URL
213
+ * the developer can click and see something. Materios doesn't yet ship a
214
+ * native trace-detail explorer page, so we compose three known-good URLs:
215
+ *
216
+ * 1. `blobStatus` — `${gateway}/blobs/<contentHash>/status` — gateway-
217
+ * side status of the receipt (HTTP 200 + JSON,
218
+ * browser-renderable).
219
+ * 2. `explorer` — Polkadot.js apps explorer pre-pointed at the
220
+ * submission block. Shows the on-chain extrinsic
221
+ * with full SCALE-decoded args.
222
+ * 3. `chainInfo` — `${gateway}/chain-info` — JSON with the live
223
+ * genesis hash + best block, useful as a sanity
224
+ * check that the gateway is the chain you think
225
+ * it is.
226
+ * 4. `gatewayHealth` — `${gateway}/health` — cluster-health summary
227
+ * (cert-daemon, anchor-worker, storage usage).
228
+ *
229
+ * A follow-up will replace `explorer` with a first-party
230
+ * `https://materios.fluxpointstudios.com/trace/<contentHash>` page (filed
231
+ * separately) — at which point the field swaps and the rest of the SDK
232
+ * surface keeps working.
233
+ */
234
+ interface BuildExplorerUrlsInput {
235
+ /**
236
+ * Hex content hash, with or without `0x` prefix. Used to build the
237
+ * gateway status URL.
238
+ */
239
+ contentHash: string;
240
+ /**
241
+ * Hex block hash from the on-chain submission, with or without `0x`.
242
+ * Used to build the Polkadot.js apps query URL.
243
+ */
244
+ blockHash: string;
245
+ /**
246
+ * Gateway base URL. Accepts either `https://host` or `https://host/blobs`
247
+ * — the function normalises so callers don't have to remember which
248
+ * variant the env exports.
249
+ */
250
+ gatewayBaseUrl: string;
251
+ /**
252
+ * Substrate websocket RPC URL. Used to build the Polkadot.js apps
253
+ * pre-pointed-at-this-chain URL.
254
+ */
255
+ rpcUrl: string;
256
+ }
257
+ interface ExplorerUrls {
258
+ /** Gateway blob status JSON. */
259
+ blobStatus: string;
260
+ /** Polkadot.js apps pre-pointed at this chain's submission block. */
261
+ explorer: string;
262
+ /** Gateway chain-info endpoint (genesis + best block). */
263
+ chainInfo: string;
264
+ /** Gateway top-level health roll-up. */
265
+ gatewayHealth: string;
266
+ }
267
+ declare function buildExplorerUrls(input: BuildExplorerUrlsInput): ExplorerUrls;
268
+
269
+ /**
270
+ * @summary One-call solo-dev bootstrap.
271
+ *
272
+ * `bootstrapAndTrace()` does ALL of:
273
+ *
274
+ * 1. `loadOrCreateIdentity()` — fresh sr25519 keypair on first run.
275
+ * 2. `requestFaucet()` — free-tier MATRA drip on the gateway.
276
+ * 3. `firstTraceBundle()` — build + finalise a "hello" trace.
277
+ * 4. `submitCertifiedReceipt()` — upload blob + submit receipt on chain.
278
+ * 5. `buildExplorerUrls()` — compose the URLs the dev needs to click.
279
+ *
280
+ * Compared to the manual flow (e2e-flow.ts), this collapses ~10 lines of
281
+ * MateriosProvider config + 30 lines of error handling into a single
282
+ * `await bootstrapAndTrace({})`. Defaults target Materios preprod; pass
283
+ * env-driven overrides to point at a different chain.
284
+ *
285
+ * Designed to surface, not paper over, real failures. Faucet cooldown is
286
+ * NOT retried; certification timeout is NOT swallowed. The expectation is
287
+ * that a fresh dev sees a green path on first run and a clear error
288
+ * message otherwise — never a hung "loading...".
289
+ */
290
+
291
+ declare const DEFAULT_RPC_URL = "wss://materios.fluxpointstudios.com/rpc";
292
+ declare const DEFAULT_GATEWAY_URL = "https://materios.fluxpointstudios.com/blobs";
293
+ declare const DEFAULT_AGENT_ID = "orynq-quickstart";
294
+ interface BootstrapAndTraceOptions {
295
+ /** Path to the on-disk identity (defaults to `~/.orynq/config.json`). */
296
+ configPath?: string | undefined;
297
+ /** Substrate WS RPC URL. Defaults to preprod. */
298
+ rpcUrl?: string | undefined;
299
+ /** Blob-gateway base URL (with or without /blobs suffix). */
300
+ gatewayBaseUrl?: string | undefined;
301
+ /** AgentId stamped on the trace bundle. */
302
+ agentId?: string | undefined;
303
+ /**
304
+ * One-liner observation stamped as the public event of the first trace.
305
+ * Defaults to a self-describing message that includes the env's wall
306
+ * clock, so the trace is identifiable on the explorer.
307
+ */
308
+ summary?: string | undefined;
309
+ /**
310
+ * Wait this long for the cert-daemon committee to certify the receipt.
311
+ * Defaults to 120 s. Set to 0 to skip the cert wait entirely (returns
312
+ * as soon as the receipt is on chain). On preprod the cert window is
313
+ * ~30-90 s depending on attestor load + finality gap; 120 s gives the
314
+ * happy path a comfortable margin without keeping the dev hanging.
315
+ */
316
+ certTimeoutMs?: number | undefined;
317
+ /**
318
+ * If true, a cert timeout is treated as success — the receipt is on
319
+ * chain, the explorer URL is printed, and the cert is just "still
320
+ * pending". Defaults to true so a fresh dev sees a working trace URL
321
+ * even when the committee is mid-vote.
322
+ */
323
+ treatCertTimeoutAsSuccess?: boolean | undefined;
324
+ /**
325
+ * Hook fired after each major step. Lets the CLI render a live status
326
+ * line without coupling business logic to console.log.
327
+ */
328
+ onProgress?: ((step: BootstrapStep) => void) | undefined;
329
+ /**
330
+ * If true, the faucet step is skipped — useful when the dev has
331
+ * pre-funded their address via Discord faucet, an existing wallet, etc.
332
+ * Defaults to false.
333
+ */
334
+ skipFaucet?: boolean | undefined;
335
+ }
336
+ type BootstrapStep = {
337
+ kind: "identity-loaded";
338
+ identity: OrynqIdentity;
339
+ } | {
340
+ kind: "faucet-result";
341
+ result: FaucetDripResult;
342
+ } | {
343
+ kind: "waiting-for-motra";
344
+ } | {
345
+ kind: "motra-ready";
346
+ balance: bigint;
347
+ } | {
348
+ kind: "trace-built";
349
+ bundle: TraceBundleLite;
350
+ } | {
351
+ kind: "blob-uploaded";
352
+ contentHash: string;
353
+ } | {
354
+ kind: "receipt-submitted";
355
+ receiptId: string;
356
+ blockHash: string;
357
+ } | {
358
+ kind: "certified";
359
+ certHash: string;
360
+ } | {
361
+ kind: "explorer-ready";
362
+ urls: ExplorerUrls;
363
+ };
364
+ interface BootstrapAndTraceResult {
365
+ identity: OrynqIdentity;
366
+ bundle: TraceBundleLite;
367
+ receiptId: string;
368
+ blockHash: string;
369
+ certHash?: string;
370
+ urls: ExplorerUrls;
371
+ /** Wall-clock duration from start of bootstrap to URLs available. */
372
+ elapsedMs: number;
373
+ }
374
+ /**
375
+ * Bootstrap a fresh dev to a chain-anchored first trace.
376
+ *
377
+ * Steps (each emits a progress event via `onProgress`):
378
+ * 1. load-or-create identity ~50 ms
379
+ * 2. faucet drip (skipped if funded) ~2 s
380
+ * 3. wait for MOTRA to generate ~10-30 s (chain block production)
381
+ * 4. build local trace bundle ~10 ms
382
+ * 5. upload blob + submit receipt ~6 s (1 block)
383
+ * 6. await certification (optional) ~10-30 s (committee voting)
384
+ * 7. compose explorer URLs instant
385
+ *
386
+ * Total budget on a fresh address: 30-60 s wall-clock. With faucet skipped
387
+ * and MOTRA already in hand: <10 s.
388
+ */
389
+ declare function bootstrapAndTrace(opts?: BootstrapAndTraceOptions): Promise<BootstrapAndTraceResult>;
390
+ /**
391
+ * Standalone helper: spin up a `Keyring` from a mnemonic. Exposed so the
392
+ * CLI can re-derive an address from the saved config without pulling in
393
+ * the full bootstrap path.
394
+ */
395
+ declare function deriveAddress(mnemonic: string, ss58Format?: number): Promise<string>;
396
+
397
+ /**
398
+ * @fluxpointstudios/orynq-sdk-quickstart
399
+ *
400
+ * Solo-developer DX surface. Get from `npm install` to a chain-anchored
401
+ * first trace in under 5 minutes — no signer URI to manage, no wallet to
402
+ * seed, no Cardano addresses to look up.
403
+ *
404
+ * Three layers:
405
+ *
406
+ * - **CLI** (`bin/orynq.mjs`): `orynq init`, `orynq trace`,
407
+ * `orynq whoami`, `orynq status`.
408
+ * - **One-call API**: `bootstrapAndTrace()` — identity,
409
+ * faucet, submit, certify, URL.
410
+ * - **Primitives**: `loadOrCreateIdentity`,
411
+ * `firstTraceBundle`, `requestFaucet`,
412
+ * `buildExplorerUrls`. Mix and match
413
+ * when you're past the hello-world tier.
414
+ *
415
+ * All primitives are pure ESM, zero side-effects on import. The first
416
+ * filesystem write happens only when you call into `loadOrCreateIdentity`
417
+ * (or any helper that wraps it), so this package is safe to require()
418
+ * from a Cloudflare Worker or a Vite client bundle.
419
+ */
420
+
421
+ declare const VERSION = "0.1.0";
422
+
423
+ export { type BootstrapAndTraceOptions, type BootstrapAndTraceResult, type BootstrapStep, type BuildExplorerUrlsInput, DEFAULT_AGENT_ID, DEFAULT_GATEWAY_URL, DEFAULT_RPC_URL, type DeterministicHooks, type ExplorerUrls, type FaucetDripAlreadyFunded, type FaucetDripCooldown, type FaucetDripError, type FaucetDripResult, type FaucetDripSuccess, type FirstTraceBundleOptions, type LoadOrCreateIdentityOptions, type OrynqIdentity, type RequestFaucetOptions, type TraceBundleLite, VERSION, bootstrapAndTrace, buildExplorerUrls, defaultConfigPath, deriveAddress, firstTraceBundle, loadOrCreateIdentity, requestFaucet };