@demath-ai/cli 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,823 @@
1
+ 'use strict';
2
+
3
+ // src/constants.ts
4
+ var CLI_VERSION = "0.1.0";
5
+ var USER_AGENT = `demath-cli-npm/${CLI_VERSION} (+https://github.com/demath-ai/proj-dmv0/tree/main/tools/demath-cli-npm)`;
6
+ var DEFAULT_API_URL = process.env.DEMATH_API_URL?.trim() || "https://api.demath.org";
7
+ var APPROVED_MODELS = [
8
+ "anthropic/claude-opus-4-7",
9
+ "openai/gpt-5.5",
10
+ "google/gemini-3-deep-think"
11
+ ];
12
+ var POLL_INTERVAL_MS = 3e3;
13
+ var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
14
+ "proof_complete",
15
+ "counterexample",
16
+ "breakthrough",
17
+ "substantial_progress",
18
+ "partial_progress",
19
+ "no_progress",
20
+ "stopped",
21
+ "error"
22
+ ]);
23
+ var SUCCESS_STATUSES = /* @__PURE__ */ new Set([
24
+ "proof_complete",
25
+ "counterexample",
26
+ "breakthrough"
27
+ ]);
28
+
29
+ // src/errors.ts
30
+ var CliError = class extends Error {
31
+ exitCode;
32
+ constructor(message, exitCode = 1) {
33
+ super(message);
34
+ this.name = "CliError";
35
+ this.exitCode = exitCode;
36
+ }
37
+ };
38
+
39
+ // src/http.ts
40
+ async function request(opts) {
41
+ const timeoutMs = opts.timeoutMs ?? 3e4;
42
+ const ctrl = new AbortController();
43
+ const timeoutId = setTimeout(() => ctrl.abort(new Error("request timed out")), timeoutMs);
44
+ const onParentAbort = () => ctrl.abort(opts.signal.reason);
45
+ if (opts.signal) {
46
+ if (opts.signal.aborted) ctrl.abort(opts.signal.reason);
47
+ else opts.signal.addEventListener("abort", onParentAbort, { once: true });
48
+ }
49
+ const headers = {
50
+ Accept: "application/json",
51
+ "User-Agent": USER_AGENT
52
+ };
53
+ let bodyInit;
54
+ if (opts.body !== void 0) {
55
+ bodyInit = JSON.stringify(opts.body);
56
+ headers["Content-Type"] = "application/json";
57
+ }
58
+ let resp;
59
+ try {
60
+ resp = await fetch(opts.url, {
61
+ method: opts.method,
62
+ headers,
63
+ body: bodyInit,
64
+ signal: ctrl.signal
65
+ });
66
+ } catch (err) {
67
+ clearTimeout(timeoutId);
68
+ if (opts.signal) opts.signal.removeEventListener("abort", onParentAbort);
69
+ const msg = err instanceof Error ? err.message : String(err);
70
+ throw new CliError(`network error reaching ${opts.url}: ${msg}`, 3);
71
+ } finally {
72
+ clearTimeout(timeoutId);
73
+ if (opts.signal) opts.signal.removeEventListener("abort", onParentAbort);
74
+ }
75
+ const rawText = await resp.text();
76
+ if (!resp.ok) {
77
+ let detail = `HTTP ${resp.status}`;
78
+ if (rawText) {
79
+ try {
80
+ const parsed = JSON.parse(rawText);
81
+ const d = parsed.detail ?? parsed.error;
82
+ if (typeof d === "string") detail = d;
83
+ else if (d !== void 0) detail = JSON.stringify(d);
84
+ } catch {
85
+ detail = rawText.slice(0, 400);
86
+ }
87
+ }
88
+ throw new CliError(`backend error: ${detail}`, 2);
89
+ }
90
+ if (!rawText) return {};
91
+ try {
92
+ return JSON.parse(rawText);
93
+ } catch {
94
+ throw new CliError(`backend returned non-JSON body: ${rawText.slice(0, 200)}`, 2);
95
+ }
96
+ }
97
+
98
+ // src/api.ts
99
+ var DemathClient = class {
100
+ constructor(cfg) {
101
+ this.cfg = cfg;
102
+ }
103
+ cfg;
104
+ listProblems() {
105
+ return request({
106
+ method: "GET",
107
+ url: `${this.cfg.apiUrl}/problems`,
108
+ signal: this.cfg.signal
109
+ });
110
+ }
111
+ probe(model, apiKey) {
112
+ return request({
113
+ method: "POST",
114
+ url: `${this.cfg.apiUrl}/providers/probe`,
115
+ body: { model, api_key: apiKey },
116
+ signal: this.cfg.signal
117
+ });
118
+ }
119
+ startAttempt(r) {
120
+ const body = {
121
+ problem_id: r.problemId,
122
+ model: r.model,
123
+ api_key: r.apiKey,
124
+ miner_address: r.walletAddress
125
+ };
126
+ if (r.maxIterations !== void 0) body.max_iterations = r.maxIterations;
127
+ if (r.maxUsd !== void 0) body.max_usd = r.maxUsd;
128
+ return request({
129
+ method: "POST",
130
+ url: `${this.cfg.apiUrl}/attempts`,
131
+ body,
132
+ signal: this.cfg.signal
133
+ });
134
+ }
135
+ getAttempt(attemptId) {
136
+ return request({
137
+ method: "GET",
138
+ url: `${this.cfg.apiUrl}/attempts/${encodeURIComponent(attemptId)}`,
139
+ signal: this.cfg.signal
140
+ });
141
+ }
142
+ stopAttempt(attemptId) {
143
+ return request({
144
+ method: "POST",
145
+ url: `${this.cfg.apiUrl}/attempts/${encodeURIComponent(attemptId)}/stop`,
146
+ body: {},
147
+ // Stops shouldn't get cancelled by the same Ctrl-C that initiated them.
148
+ signal: void 0
149
+ });
150
+ }
151
+ };
152
+
153
+ // src/output.ts
154
+ function logErr(line = "") {
155
+ process.stderr.write(line + "\n");
156
+ }
157
+ function printJson(value) {
158
+ process.stdout.write(JSON.stringify(value, null, 2) + "\n");
159
+ }
160
+ function padRight(s, n) {
161
+ return s.length >= n ? s : s + " ".repeat(n - s.length);
162
+ }
163
+ function truncate(s, n) {
164
+ if (!s) return "";
165
+ return s.length <= n ? s : s.slice(0, Math.max(0, n - 1)) + "\u2026";
166
+ }
167
+
168
+ // src/parser.ts
169
+ function parseArgs(argv) {
170
+ const positional = [];
171
+ const flags = /* @__PURE__ */ new Map();
172
+ let i = 0;
173
+ while (i < argv.length) {
174
+ const a = argv[i];
175
+ if (a === "--") {
176
+ positional.push(...argv.slice(i + 1));
177
+ break;
178
+ }
179
+ if (a.startsWith("--")) {
180
+ const eq = a.indexOf("=");
181
+ if (eq !== -1) {
182
+ flags.set(a.slice(2, eq), a.slice(eq + 1));
183
+ i++;
184
+ continue;
185
+ }
186
+ const key = a.slice(2);
187
+ const next = argv[i + 1];
188
+ if (next !== void 0 && !next.startsWith("-")) {
189
+ flags.set(key, next);
190
+ i += 2;
191
+ } else {
192
+ flags.set(key, true);
193
+ i++;
194
+ }
195
+ continue;
196
+ }
197
+ if (a.startsWith("-") && a.length > 1) {
198
+ flags.set(a.slice(1), true);
199
+ i++;
200
+ continue;
201
+ }
202
+ positional.push(a);
203
+ i++;
204
+ }
205
+ return { positional, flags };
206
+ }
207
+ function getString(flags, key) {
208
+ const v = flags.get(key);
209
+ return typeof v === "string" ? v : void 0;
210
+ }
211
+ function requireString(flags, key) {
212
+ const v = getString(flags, key);
213
+ if (v === void 0) throw new CliError(`missing required flag --${key}`, 2);
214
+ return v;
215
+ }
216
+ function getBool(flags, key) {
217
+ return flags.has(key);
218
+ }
219
+ function getNumber(flags, key) {
220
+ const v = getString(flags, key);
221
+ if (v === void 0) return void 0;
222
+ const n = Number(v);
223
+ if (!Number.isFinite(n)) throw new CliError(`--${key} must be a number, got: ${v}`, 2);
224
+ return n;
225
+ }
226
+ var TOP_LEVEL_HELP = `demath ${CLI_VERSION} \u2014 mine $DEMATH from your terminal.
227
+
228
+ USAGE
229
+ demath <command> [options]
230
+
231
+ COMMANDS
232
+ problems List active problems
233
+ probe Verify your provider API key works (no spend)
234
+ mine Start an attempt and stream live progress
235
+ status Show the current state of an attempt by id
236
+ examples Print copy-pasteable example invocations
237
+ skill Print the SKILL.md doc to stdout (for AI agents)
238
+ --help Show this help
239
+ --version Print CLI version
240
+
241
+ GLOBAL OPTIONS
242
+ --api-url <url> Backend base URL (default: ${DEFAULT_API_URL})
243
+ Override with DEMATH_API_URL env var.
244
+ --json Machine-readable output on stdout (for AI agents).
245
+ Per-command; only some commands stream NDJSON.
246
+
247
+ APPROVED MODEL SLUGS
248
+ ${APPROVED_MODELS.map((m) => ` - ${m}`).join("\n")}
249
+
250
+ PROVIDER API KEY HANDLING
251
+ DeMath never stores, persists, or logs your provider API key. The CLI
252
+ sends it once per attempt to the backend, which forwards it inline to
253
+ the upstream provider (Anthropic / OpenAI / Google / OpenRouter) and
254
+ drops it from memory the moment the attempt finishes. No disk write,
255
+ no log line, no telemetry, no IPFS bundle. Your billing relationship
256
+ with the upstream provider remains entirely yours.
257
+
258
+ LOCK-FOR-LIFE
259
+ Your wallet binds to a single team on its first attempt. The backend
260
+ rejects team switches with HTTP 409. Use a different wallet to mine
261
+ a different team. Same rule for web, CLI, and any bot client.
262
+
263
+ FOR AI AGENTS
264
+ Run \`demath skill\` to print the short, human-readable skill doc
265
+ (project intro + mine-in-three-steps + key constraints). The rest is
266
+ in this --help and the per-command --help; refresh both before every
267
+ invocation.
268
+
269
+ See \`demath examples\` for worked invocations.
270
+ See \`demath <command> --help\` for per-command flags.
271
+ `;
272
+ var PROBLEMS_HELP = `demath problems \u2014 list active problems.
273
+
274
+ USAGE
275
+ demath problems [--api-url <url>] [--json]
276
+
277
+ EXAMPLES
278
+ demath problems
279
+ demath problems --json | jq '.problems[].id'
280
+
281
+ EXIT CODES
282
+ 0 success
283
+ 2 backend error
284
+ 3 network error
285
+ `;
286
+ var PROBE_HELP = `demath probe \u2014 verify an API key works for a model (no token spend
287
+ beyond a 1-token chat completion).
288
+
289
+ USAGE
290
+ demath probe --model <slug> --key <api-key> [--api-url <url>] [--json]
291
+
292
+ REQUIRED
293
+ --model one of: ${APPROVED_MODELS.join(", ")}
294
+ --key provider API key (OpenRouter / Anthropic / OpenAI / Gemini direct)
295
+ backend auto-detects the provider from the key prefix.
296
+
297
+ EXAMPLES
298
+ demath probe --model anthropic/claude-opus-4-7 --key sk-ant-...
299
+ demath probe --model openai/gpt-5.5 --key sk-or-v1-... --json
300
+
301
+ EXIT CODES
302
+ 0 key works
303
+ 2 probe failed / invalid model
304
+ 3 network error
305
+ `;
306
+ var MINE_HELP = `demath mine \u2014 start an attempt and stream live progress until terminal.
307
+
308
+ USAGE
309
+ demath mine --problem <id> --model <slug> --key <api-key>
310
+ --wallet <0x...> [--max-iterations N] [--max-usd N]
311
+ [--api-url <url>] [--json]
312
+
313
+ REQUIRED
314
+ --problem problem id (see \`demath problems\`)
315
+ --model one of: ${APPROVED_MODELS.join(", ")}
316
+ --key provider API key \u2014 held in memory only, never written to disk
317
+ --wallet EVM address that will receive emission claims
318
+ (locks to the chosen team on first use)
319
+
320
+ OPTIONAL
321
+ --max-iterations cap on agent loop iterations (default: backend default)
322
+ --max-usd cost cap in USD (default: backend default)
323
+ --json emit one NDJSON event per iteration on stdout
324
+
325
+ EXAMPLES
326
+ demath mine \\
327
+ --problem irrationality-of-e \\
328
+ --model anthropic/claude-opus-4-7 \\
329
+ --key sk-ant-... \\
330
+ --wallet 0xYourEvmAddress \\
331
+ --max-usd 1.0
332
+
333
+ demath mine --problem collatz --model openai/gpt-5.5 \\
334
+ --key sk-or-v1-... --wallet 0xAbc... --json | tee run.ndjson
335
+
336
+ EXIT CODES
337
+ 0 attempt reached a success status (proof_complete | counterexample | breakthrough)
338
+ 1 attempt ended without success (stopped | error)
339
+ 2 backend error / invalid model / missing flag
340
+ 3 network error
341
+ 130 interrupted by Ctrl-C (backend stop requested)
342
+ `;
343
+ var STATUS_HELP = `demath status \u2014 show the current state of an attempt by id.
344
+
345
+ USAGE
346
+ demath status --attempt <id> [--api-url <url>] [--json]
347
+
348
+ REQUIRED
349
+ --attempt attempt id returned by \`demath mine\` start
350
+
351
+ EXAMPLES
352
+ demath status --attempt 65df82b6ea7f4aeeab86f60505571491
353
+ demath status --attempt <id> --json | jq .status
354
+
355
+ EXIT CODES
356
+ 0 fetched successfully
357
+ 2 backend error
358
+ 3 network error
359
+ `;
360
+ var EXAMPLES_HELP = `demath examples \u2014 print 5 copy-pasteable example invocations.
361
+
362
+ USAGE
363
+ demath examples
364
+ `;
365
+
366
+ // src/commands/examples.ts
367
+ var EXAMPLES = `DeMath CLI \u2014 worked examples
368
+
369
+ 1) Look at the active problems (machine-readable):
370
+
371
+ demath problems --json
372
+
373
+ 2) Probe your API key against a model before committing (zero spend):
374
+
375
+ demath probe \\
376
+ --model anthropic/claude-opus-4-7 \\
377
+ --key sk-ant-... \\
378
+ --json
379
+
380
+ 3) Start a budget-capped attempt on the test problem (a few cents):
381
+
382
+ demath mine \\
383
+ --problem irrationality-of-e \\
384
+ --model anthropic/claude-opus-4-7 \\
385
+ --key sk-ant-... \\
386
+ --wallet 0xYourEvmAddress \\
387
+ --max-usd 1.0
388
+
389
+ 4) Same flow but routing via OpenRouter (one key, any team):
390
+
391
+ demath mine \\
392
+ --problem collatz \\
393
+ --model openai/gpt-5.5 \\
394
+ --key sk-or-v1-... \\
395
+ --wallet 0xYourEvmAddress \\
396
+ --max-iterations 8 \\
397
+ --max-usd 5.0 \\
398
+ --json
399
+
400
+ 5) Inspect an attempt by id after the fact (e.g. for a background run):
401
+
402
+ demath status --attempt <attempt_id> --json
403
+
404
+ Notes:
405
+ - Wallet \u2192 team lock-for-life: the first attempt binds your wallet to a
406
+ team (anthropic / openai / google). Subsequent attempts with a different
407
+ team return HTTP 409. Use a different wallet to mine a different team.
408
+ - The CLI never writes your API key to disk. The key is held in memory
409
+ for the duration of the attempt and only ever sent to api.demath.org.
410
+ - --json on any command emits machine-readable output on stdout; human
411
+ progress text goes to stderr so it can't pollute your JSON pipeline.
412
+ `;
413
+ function runExamples() {
414
+ process.stdout.write(EXAMPLES);
415
+ return 0;
416
+ }
417
+
418
+ // src/commands/mine.ts
419
+ function sleep(ms, signal) {
420
+ return new Promise((resolve, reject) => {
421
+ if (signal.aborted) return reject(signal.reason);
422
+ const t = setTimeout(() => {
423
+ signal.removeEventListener("abort", onAbort);
424
+ resolve();
425
+ }, ms);
426
+ const onAbort = () => {
427
+ clearTimeout(t);
428
+ reject(signal.reason);
429
+ };
430
+ signal.addEventListener("abort", onAbort, { once: true });
431
+ });
432
+ }
433
+ async function runMine(args) {
434
+ if (!APPROVED_MODELS.includes(args.model)) {
435
+ throw new CliError(
436
+ `model must be one of: ${APPROVED_MODELS.join(", ")}`,
437
+ 2
438
+ );
439
+ }
440
+ const ctrl = new AbortController();
441
+ let interrupted = false;
442
+ const onSig = () => {
443
+ interrupted = true;
444
+ ctrl.abort(new Error("SIGINT"));
445
+ };
446
+ process.on("SIGINT", onSig);
447
+ process.on("SIGTERM", onSig);
448
+ const client = new DemathClient({ apiUrl: args.apiUrl, signal: ctrl.signal });
449
+ if (!args.json) {
450
+ logErr(
451
+ ` starting attempt: problem=${args.problem} model=${args.model} wallet=${args.wallet}`
452
+ );
453
+ if (args.maxUsd !== void 0) {
454
+ logErr(` budget: $${args.maxUsd.toFixed(2)}`);
455
+ }
456
+ }
457
+ const start = await client.startAttempt({
458
+ problemId: args.problem,
459
+ model: args.model,
460
+ apiKey: args.apiKey,
461
+ walletAddress: args.wallet,
462
+ maxIterations: args.maxIterations,
463
+ maxUsd: args.maxUsd
464
+ });
465
+ const attemptId = start.attempt_id;
466
+ if (!attemptId) {
467
+ throw new CliError("backend did not return attempt_id", 2);
468
+ }
469
+ if (args.json) {
470
+ process.stdout.write(JSON.stringify({ event: "start", attempt_id: attemptId }) + "\n");
471
+ } else {
472
+ logErr(` attempt_id: ${attemptId}`);
473
+ logErr(" watching live progress (Ctrl+C to stop the attempt)\u2026");
474
+ logErr();
475
+ }
476
+ let lastIterCount = 0;
477
+ let lastRecord;
478
+ try {
479
+ while (true) {
480
+ try {
481
+ await sleep(POLL_INTERVAL_MS, ctrl.signal);
482
+ } catch {
483
+ break;
484
+ }
485
+ const record = await client.getAttempt(attemptId);
486
+ lastRecord = record;
487
+ const iters = record.iterations ?? [];
488
+ const status = record.status ?? "?";
489
+ const totalCost = record.total_cost ?? 0;
490
+ for (let i = lastIterCount; i < iters.length; i++) {
491
+ const it = iters[i];
492
+ if (args.json) {
493
+ process.stdout.write(
494
+ JSON.stringify({ event: "iteration", attempt_id: attemptId, ...it }) + "\n"
495
+ );
496
+ } else {
497
+ printIterLine(it, totalCost);
498
+ }
499
+ }
500
+ lastIterCount = iters.length;
501
+ if (TERMINAL_STATUSES.has(status)) {
502
+ if (args.json) {
503
+ process.stdout.write(
504
+ JSON.stringify({ event: "final", attempt_id: attemptId, ...record }) + "\n"
505
+ );
506
+ } else {
507
+ logErr();
508
+ printFinal(record);
509
+ }
510
+ process.off("SIGINT", onSig);
511
+ process.off("SIGTERM", onSig);
512
+ return SUCCESS_STATUSES.has(status) ? 0 : 1;
513
+ }
514
+ }
515
+ } catch (err) {
516
+ process.off("SIGINT", onSig);
517
+ process.off("SIGTERM", onSig);
518
+ if (!interrupted) throw err;
519
+ }
520
+ process.off("SIGINT", onSig);
521
+ process.off("SIGTERM", onSig);
522
+ if (!args.json) {
523
+ logErr();
524
+ logErr(" Ctrl+C \u2014 stopping the attempt\u2026");
525
+ }
526
+ try {
527
+ await new DemathClient({ apiUrl: args.apiUrl }).stopAttempt(attemptId);
528
+ if (args.json) {
529
+ process.stdout.write(JSON.stringify({ event: "stopped", attempt_id: attemptId }) + "\n");
530
+ } else {
531
+ logErr(" stopped");
532
+ if (lastRecord) printFinal(lastRecord);
533
+ }
534
+ } catch (err) {
535
+ const msg = err instanceof Error ? err.message : String(err);
536
+ if (args.json) {
537
+ process.stdout.write(
538
+ JSON.stringify({ event: "stop_failed", attempt_id: attemptId, error: msg }) + "\n"
539
+ );
540
+ } else {
541
+ logErr(` warning: stop request failed: ${msg}`);
542
+ }
543
+ }
544
+ return 130;
545
+ }
546
+ function printIterLine(it, totalCost) {
547
+ const n = (it.iteration ?? 0).toString().padStart(3, " ");
548
+ const status = padRight(it.status ?? "?", 22);
549
+ const inT = (it.input_tokens ?? 0).toString().padStart(6, " ");
550
+ const outT = (it.output_tokens ?? 0).toString().padStart(6, " ");
551
+ const itCost = (it.usd_cost ?? 0).toFixed(4);
552
+ const total = totalCost.toFixed(4);
553
+ const summary = (it.summary ?? it.body ?? "").toString().replace(/\s+/g, " ");
554
+ const tail = summary ? ` ${truncate(summary, 200)}` : "";
555
+ logErr(
556
+ ` iter ${n} status=${status} tokens in=${inT} out=${outT} +$${itCost} (total $${total})${tail}`
557
+ );
558
+ }
559
+ function printFinal(record) {
560
+ const status = (record.status ?? "?").toUpperCase();
561
+ const cost = record.total_cost ?? 0;
562
+ const inT = record.total_input_tokens ?? 0;
563
+ const outT = record.total_output_tokens ?? 0;
564
+ const iters = (record.iterations ?? []).length;
565
+ const cid = record.ipfs_cid;
566
+ logErr(` \u2554\u2550 ${status} \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
567
+ logErr(` \u2551 iterations: ${iters}`);
568
+ logErr(` \u2551 tokens in: ${inT}`);
569
+ logErr(` \u2551 tokens out: ${outT}`);
570
+ logErr(` \u2551 total cost: $${cost.toFixed(4)}`);
571
+ if (cid) logErr(` \u2551 IPFS bundle: https://ipfs.io/ipfs/${cid}`);
572
+ if (record.error) logErr(` \u2551 error: ${truncate(record.error, 200)}`);
573
+ logErr(` \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
574
+ logErr();
575
+ if (SUCCESS_STATUSES.has((record.status ?? "").toLowerCase())) {
576
+ logErr(
577
+ " Claim on the web: https://demath.org/claim (after the next epoch settles, 24h cadence on mainnet)"
578
+ );
579
+ }
580
+ }
581
+
582
+ // src/commands/problems.ts
583
+ async function runProblems(args) {
584
+ const client = new DemathClient({ apiUrl: args.apiUrl });
585
+ const payload = await client.listProblems();
586
+ const problems = payload.problems ?? [];
587
+ if (args.json) {
588
+ printJson({ problems });
589
+ return 0;
590
+ }
591
+ const idWidth = Math.max(20, ...problems.map((p) => p.id.length));
592
+ const diffWidth = Math.max(
593
+ 7,
594
+ ...problems.map((p) => (p.expected_difficulty ?? "?").length)
595
+ );
596
+ const clsWidth = Math.max(
597
+ 10,
598
+ ...problems.map((p) => (p.classification ?? "?").length)
599
+ );
600
+ for (const p of problems) {
601
+ const diff = p.expected_difficulty ?? "?";
602
+ const cls = p.classification ?? "?";
603
+ process.stdout.write(
604
+ ` ${padRight(p.id, idWidth)} [${padRight(diff, diffWidth)}] [${padRight(cls, clsWidth)}] ${p.name}
605
+ `
606
+ );
607
+ }
608
+ logErr();
609
+ logErr(` ${problems.length} problems active`);
610
+ return 0;
611
+ }
612
+
613
+ // src/commands/probe.ts
614
+ async function runProbe(args) {
615
+ if (!APPROVED_MODELS.includes(args.model)) {
616
+ throw new CliError(
617
+ `model must be one of: ${APPROVED_MODELS.join(", ")}`,
618
+ 2
619
+ );
620
+ }
621
+ const client = new DemathClient({ apiUrl: args.apiUrl });
622
+ if (!args.json) {
623
+ logErr(` probing ${args.model} via auto-detected provider\u2026`);
624
+ }
625
+ const out = await client.probe(args.model, args.apiKey);
626
+ if (args.json) {
627
+ printJson(out);
628
+ return out.ok ? 0 : 2;
629
+ }
630
+ if (out.ok) {
631
+ logErr(` \u2713 key works \u2014 provider: ${out.provider ?? "?"}`);
632
+ return 0;
633
+ }
634
+ const parts = [];
635
+ if (out.detail) parts.push(out.detail);
636
+ if (out.reason) parts.push(`reason=${out.reason}`);
637
+ if (out.provider_status !== void 0)
638
+ parts.push(`provider_status=${out.provider_status}`);
639
+ if (out.provider_message) parts.push(`provider_message=${out.provider_message}`);
640
+ if (parts.length === 0 && out.error) parts.push(out.error);
641
+ if (parts.length === 0) parts.push("(no detail returned by backend)");
642
+ logErr(` \u2717 probe failed: ${parts.join(" \xB7 ")}`);
643
+ return 2;
644
+ }
645
+
646
+ // src/skillContent.generated.ts
647
+ var SKILL_MD_CONTENT = "# DeMath skill\n\nDeMath is the first decentralized math research infrastructure on\nBase. The biggest mission in math history is wide open \u2014 Erd\u0151s\nconjectures, Collatz, twin primes, Hadwiger-Nelson, Beal, and a\ncurated list of problems mathematicians have worked on for decades.\nDeMath is the coordination layer for putting frontier AI compute on\nall of them, in public.\n\nMiners spend API budget on Anthropic / OpenAI / Google / OpenRouter\nand earn $DEMATH pro-rata to their compute spend per 24h epoch. Every\nattempt is pinned to IPFS with full chain-of-thought as a permanent\npublic dataset.\n\nYou are an AI agent reading this skill. To mine on a human's behalf:\n\n1. Install once: `npm install -g @demath-ai/cli`\n2. Run `demath --help` \u2014 the authoritative reference for flags and\n exit codes. Re-read it before every invocation; nothing in this\n skill replaces it.\n3. The four commands: `problems`, `probe`, `mine`, `status`. Each has\n its own `--help`. Use `--json` for machine output.\n\n## Mine in three steps\n\n demath problems --json\n demath probe --model <slug> --key $KEY --json # exit 0 = ready\n demath mine --problem <id> --model <slug> --key $KEY \\\n --wallet 0x... --max-usd 1.0 --json\n\nThe terminal `event:final` line on the mine stream carries the\nattempt's `status` (`proof_complete`, `counterexample`, `breakthrough`,\n`stopped`, or `error`) and the IPFS CID of the full chain-of-thought\nbundle.\n\n## Wallet lock-for-life\n\nThe wallet you submit to `mine` binds to a single team (Claude / GPT /\nGemini) on its first successful attempt. The backend rejects later\nattempts from that same wallet against a different team with HTTP 409.\nUse a fresh EVM address per team.\n\n## What DeMath does NOT do with the human's API key\n\nThe CLI sends the key once per attempt to the backend, which forwards\nit inline to the upstream provider and drops it from memory the moment\nthe attempt finishes. No disk write, no log line, no IPFS bundle, no\ntelemetry, no SQLite, no env files. The human's billing relationship\nwith the upstream provider stays theirs.\n\n## Claim\n\nClaiming the earned emission is a web step at https://demath.org/claim.\nThe CLI does not sign on-chain transactions in v0.1.\n";
648
+
649
+ // src/commands/skill.ts
650
+ function runSkill() {
651
+ process.stdout.write(SKILL_MD_CONTENT);
652
+ if (!SKILL_MD_CONTENT.endsWith("\n")) process.stdout.write("\n");
653
+ return 0;
654
+ }
655
+
656
+ // src/commands/status.ts
657
+ async function runStatus(args) {
658
+ const client = new DemathClient({ apiUrl: args.apiUrl });
659
+ const record = await client.getAttempt(args.attemptId);
660
+ const status = (record.status ?? "").toLowerCase();
661
+ if (args.json) {
662
+ printJson(record);
663
+ if (SUCCESS_STATUSES.has(status)) return 0;
664
+ if (status === "running" || status === "") return 2;
665
+ return 1;
666
+ }
667
+ printFinal2(record);
668
+ if (SUCCESS_STATUSES.has(status)) return 0;
669
+ if (status === "running" || status === "") return 2;
670
+ return 1;
671
+ }
672
+ function printFinal2(record) {
673
+ const status = (record.status ?? "?").toUpperCase();
674
+ const cost = record.total_cost ?? 0;
675
+ const inT = record.total_input_tokens ?? 0;
676
+ const outT = record.total_output_tokens ?? 0;
677
+ const iters = (record.iterations ?? []).length;
678
+ const cid = record.ipfs_cid;
679
+ logErr(` \u2554\u2550 ${status} \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
680
+ logErr(` \u2551 iterations: ${iters}`);
681
+ logErr(` \u2551 tokens in: ${inT}`);
682
+ logErr(` \u2551 tokens out: ${outT}`);
683
+ logErr(` \u2551 total cost: $${cost.toFixed(4)}`);
684
+ if (cid) logErr(` \u2551 IPFS bundle: https://ipfs.io/ipfs/${cid}`);
685
+ if (record.error) logErr(` \u2551 error: ${truncate(record.error, 200)}`);
686
+ logErr(` \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`);
687
+ }
688
+
689
+ // src/cli.ts
690
+ async function resolveApiKey(flagValue, jsonMode = false) {
691
+ if (flagValue === "-") {
692
+ const buf = [];
693
+ for await (const chunk of process.stdin) {
694
+ buf.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
695
+ }
696
+ const v = Buffer.concat(buf).toString("utf8").trim();
697
+ if (!v) throw new CliError("--key - was set but stdin was empty", 2);
698
+ return v;
699
+ }
700
+ if (flagValue) {
701
+ if (!jsonMode && process.stderr.isTTY) {
702
+ logErr(
703
+ " note: --key passed on the command line is visible to other local users (ps / /proc/<pid>/cmdline). For shared hosts, prefer DEMATH_PROVIDER_API_KEY=... or --key=-"
704
+ );
705
+ }
706
+ return flagValue;
707
+ }
708
+ const envValue = process.env.DEMATH_PROVIDER_API_KEY?.trim();
709
+ if (envValue) return envValue;
710
+ throw new CliError(
711
+ "missing API key: pass --key <value>, --key=- (stdin), or set DEMATH_PROVIDER_API_KEY",
712
+ 2
713
+ );
714
+ }
715
+ async function main(argv) {
716
+ if (argv.length === 0) {
717
+ process.stdout.write(TOP_LEVEL_HELP);
718
+ return 0;
719
+ }
720
+ if (argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
721
+ process.stdout.write(TOP_LEVEL_HELP);
722
+ return 0;
723
+ }
724
+ if (argv[0] === "--version" || argv[0] === "-v") {
725
+ process.stdout.write(`${CLI_VERSION}
726
+ `);
727
+ return 0;
728
+ }
729
+ const command = argv[0];
730
+ const rest = argv.slice(1);
731
+ const parsed = parseArgs(rest);
732
+ if (getBool(parsed.flags, "help") || getBool(parsed.flags, "h")) {
733
+ switch (command) {
734
+ case "problems":
735
+ process.stdout.write(PROBLEMS_HELP);
736
+ return 0;
737
+ case "probe":
738
+ process.stdout.write(PROBE_HELP);
739
+ return 0;
740
+ case "mine":
741
+ process.stdout.write(MINE_HELP);
742
+ return 0;
743
+ case "status":
744
+ process.stdout.write(STATUS_HELP);
745
+ return 0;
746
+ case "examples":
747
+ process.stdout.write(EXAMPLES_HELP);
748
+ return 0;
749
+ default:
750
+ process.stdout.write(TOP_LEVEL_HELP);
751
+ return 0;
752
+ }
753
+ }
754
+ const apiUrl = (getString(parsed.flags, "api-url") ?? DEFAULT_API_URL).replace(/\/+$/, "");
755
+ const json = getBool(parsed.flags, "json");
756
+ try {
757
+ switch (command) {
758
+ case "problems":
759
+ return await runProblems({ apiUrl, json });
760
+ case "probe":
761
+ return await runProbe({
762
+ apiUrl,
763
+ json,
764
+ model: requireString(parsed.flags, "model"),
765
+ apiKey: await resolveApiKey(getString(parsed.flags, "key"), json)
766
+ });
767
+ case "mine":
768
+ return await runMine({
769
+ apiUrl,
770
+ json,
771
+ problem: requireString(parsed.flags, "problem"),
772
+ model: requireString(parsed.flags, "model"),
773
+ apiKey: await resolveApiKey(getString(parsed.flags, "key"), json),
774
+ wallet: requireString(parsed.flags, "wallet"),
775
+ maxIterations: getNumber(parsed.flags, "max-iterations"),
776
+ maxUsd: getNumber(parsed.flags, "max-usd")
777
+ });
778
+ case "status": {
779
+ const attemptId = getString(parsed.flags, "attempt") ?? getString(parsed.flags, "attempt-id") ?? parsed.positional[0];
780
+ if (!attemptId) {
781
+ throw new CliError("missing required flag --attempt <id>", 2);
782
+ }
783
+ return await runStatus({ apiUrl, json, attemptId });
784
+ }
785
+ case "examples":
786
+ return runExamples();
787
+ case "skill":
788
+ return runSkill();
789
+ default:
790
+ logErr(` error: unknown command '${command}'`);
791
+ logErr();
792
+ process.stdout.write(TOP_LEVEL_HELP);
793
+ return 2;
794
+ }
795
+ } catch (err) {
796
+ if (err instanceof CliError) {
797
+ if (json) {
798
+ process.stdout.write(
799
+ JSON.stringify({ error: err.message, exit_code: err.exitCode }) + "\n"
800
+ );
801
+ } else {
802
+ logErr(` error: ${err.message}`);
803
+ }
804
+ return err.exitCode;
805
+ }
806
+ const msg = err instanceof Error ? err.message : String(err);
807
+ if (json) {
808
+ process.stdout.write(JSON.stringify({ error: msg, exit_code: 1 }) + "\n");
809
+ } else {
810
+ logErr(` error: ${msg}`);
811
+ }
812
+ return 1;
813
+ }
814
+ }
815
+
816
+ exports.APPROVED_MODELS = APPROVED_MODELS;
817
+ exports.CLI_VERSION = CLI_VERSION;
818
+ exports.CliError = CliError;
819
+ exports.DEFAULT_API_URL = DEFAULT_API_URL;
820
+ exports.DemathClient = DemathClient;
821
+ exports.POLL_INTERVAL_MS = POLL_INTERVAL_MS;
822
+ exports.USER_AGENT = USER_AGENT;
823
+ exports.runCli = main;