clawmoney 0.15.31 → 0.15.32

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.
@@ -195,7 +195,7 @@ export async function relaySetupCommand() {
195
195
  log.warn(`${cli}: no recommended models found — skipping`);
196
196
  continue;
197
197
  }
198
- log.step(`${chalk.bold(cli)}: ${recommended.length} models ${chalk.dim("— " + recommended.join(", "))}`);
198
+ log.success(`${chalk.bold(cli)}: ${recommended.length} models ${chalk.dim("— " + recommended.join(", "))}`);
199
199
  for (const model of recommended) {
200
200
  const p = API_PRICES[model];
201
201
  registrations.push({
@@ -285,18 +285,18 @@ export async function relaySetupCommand() {
285
285
  15: "~25%", 30: "~50%", 45: "~75%", 60: "~100%",
286
286
  };
287
287
  const earnPct = Math.round((1 - PLATFORM_FEE) * 100);
288
+ // Single batch POST — one round-trip, one DB session, no
289
+ // client-side fan-out. The earlier sequential loop paid 7× the
290
+ // TLS/bcrypt/CF overhead, and parallelizing with Promise.all
291
+ // tripped over client- or proxy-level concurrency limits on some
292
+ // machines. Batch endpoint is the right architecture.
288
293
  let succeeded = 0;
289
294
  let failed = 0;
290
295
  const failures = [];
291
296
  const regSpin = spinner();
292
297
  regSpin.start(`Registering ${registrations.length} providers...`);
293
- // Parallel registration — each request creates a distinct RelayProvider
294
- // row so there's no write contention, and the backend's insert path is
295
- // idempotent on (agent_id, cli_type, model). Sequential registration
296
- // was costing ~1 RTT per row, which on a high-latency link (e.g. from
297
- // China) added up to 7-10s of visible wait for 7 providers.
298
- await Promise.all(registrations.map(async (r) => {
299
- const body = {
298
+ const batchBody = {
299
+ providers: registrations.map((r) => ({
300
300
  cli_type: r.cli,
301
301
  model: r.model,
302
302
  mode: "chat",
@@ -304,32 +304,31 @@ export async function relaySetupCommand() {
304
304
  daily_limit_usd: dailyLimit,
305
305
  price_input_per_m: r.input,
306
306
  price_output_per_m: r.output,
307
- };
308
- try {
309
- const resp = await apiPost("/api/v1/relay/providers", body, config.api_key);
310
- if (resp.ok) {
311
- succeeded++;
312
- return;
313
- }
307
+ })),
308
+ };
309
+ try {
310
+ const resp = await apiPost("/api/v1/relay/providers/batch", batchBody, config.api_key);
311
+ if (!resp.ok) {
314
312
  const raw = resp.data && typeof resp.data === "object" && "detail" in resp.data
315
313
  ? resp.data.detail
316
314
  : resp.data;
317
315
  const detail = typeof raw === "string" ? raw : JSON.stringify(raw);
318
- // Already-registered is a soft success — idempotent re-run.
319
- if (detail.includes("Already registered")) {
320
- succeeded++;
321
- }
322
- else {
323
- failed++;
324
- failures.push({ cli: r.cli, model: r.model, error: detail });
325
- }
316
+ regSpin.stop(chalk.red(`✗ Batch registration failed: ${detail}`));
317
+ cancel("Setup aborted");
318
+ process.exit(1);
326
319
  }
327
- catch (err) {
328
- const msg = err.message;
329
- failed++;
330
- failures.push({ cli: r.cli, model: r.model, error: msg });
320
+ // Per-row counts from the batch result.
321
+ succeeded = (resp.data.created?.length ?? 0) + (resp.data.skipped?.length ?? 0);
322
+ failed = resp.data.failed?.length ?? 0;
323
+ for (const f of resp.data.failed ?? []) {
324
+ failures.push({ cli: f.cli_type, model: f.model, error: f.error });
331
325
  }
332
- }));
326
+ }
327
+ catch (err) {
328
+ regSpin.stop(chalk.red(`✗ Batch registration failed: ${err.message}`));
329
+ cancel("Setup aborted");
330
+ process.exit(1);
331
+ }
333
332
  if (failed === 0) {
334
333
  regSpin.stop(`${chalk.green(`✓ ${succeeded} providers registered`)} ` +
335
334
  chalk.dim(`(${limitLabel[dailyLimit] ?? `$${dailyLimit}`} quota share · you earn ~${earnPct}%)`));
@@ -12,6 +12,10 @@ export declare function relayStartCommand(options: {
12
12
  cli?: string;
13
13
  }): Promise<void>;
14
14
  export declare function relayStopCommand(): Promise<void>;
15
+ export declare function relayLogsCommand(options: {
16
+ follow?: boolean;
17
+ lines?: string;
18
+ }): Promise<void>;
15
19
  export declare function relayStatusCommand(): Promise<void>;
16
20
  export declare function relayModelsCommand(): Promise<void>;
17
21
  export declare function relayCreditsCommand(): Promise<void>;
@@ -192,6 +192,32 @@ export async function relayStopCommand() {
192
192
  await new Promise((resolve) => setTimeout(resolve, 500));
193
193
  removeRelayPid();
194
194
  }
195
+ // ── relay logs ──
196
+ export async function relayLogsCommand(options) {
197
+ const { existsSync } = await import("node:fs");
198
+ const { spawn } = await import("node:child_process");
199
+ if (!existsSync(LOG_FILE)) {
200
+ console.log(chalk.dim(`No log file yet at ${LOG_FILE}`));
201
+ console.log(chalk.dim("Start the daemon first: clawmoney relay start"));
202
+ return;
203
+ }
204
+ // Default: tail -f the last 50 lines. Use system `tail` rather than
205
+ // reimplementing it in Node — it's just a debug helper, not worth
206
+ // a pure-JS reinvention.
207
+ const nLines = options.lines ?? "50";
208
+ const args = ["-n", nLines];
209
+ if (options.follow !== false) {
210
+ args.push("-f");
211
+ }
212
+ args.push(LOG_FILE);
213
+ const child = spawn("tail", args, { stdio: "inherit" });
214
+ child.on("exit", (code) => {
215
+ process.exit(code ?? 0);
216
+ });
217
+ // Ctrl-C inside `tail -f` kills tail but returns us here; propagate
218
+ // the exit code so the wrapper looks transparent.
219
+ process.on("SIGINT", () => child.kill("SIGINT"));
220
+ }
195
221
  export async function relayStatusCommand() {
196
222
  const config = requireConfig();
197
223
  // Local process status
@@ -237,6 +263,19 @@ export async function relayStatusCommand() {
237
263
  console.log(chalk.dim(` Run "clawmoney relay setup" to get started.`));
238
264
  return;
239
265
  }
266
+ // Group rows by cli_type so `claude-*` lines stay together, then
267
+ // `codex-*`, then `gemini-*`, then `antigravity-*`. Within a family
268
+ // we sort by model name so the ordering is stable across calls.
269
+ const CLI_ORDER = ["claude", "codex", "gemini", "antigravity"];
270
+ providers.sort((a, b) => {
271
+ const ai = CLI_ORDER.indexOf(a.cli_type ?? "");
272
+ const bi = CLI_ORDER.indexOf(b.cli_type ?? "");
273
+ const aRank = ai === -1 ? CLI_ORDER.length : ai;
274
+ const bRank = bi === -1 ? CLI_ORDER.length : bi;
275
+ if (aRank !== bRank)
276
+ return aRank - bRank;
277
+ return (a.model ?? "").localeCompare(b.model ?? "");
278
+ });
240
279
  spinner.succeed(`Relay Providers (${providers.length})`);
241
280
  console.log("");
242
281
  // Aggregate stats across all rows since users think of earnings /
package/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import { walletStatusCommand, walletBalanceCommand, walletAddressCommand, wallet
8
8
  import { tweetCommand } from './commands/tweet.js';
9
9
  import { gigCreateCommand, gigBrowseCommand, gigDetailCommand, gigAcceptCommand, gigDeliverCommand, gigApproveCommand, gigDisputeCommand, } from './commands/gig.js';
10
10
  import { hubStartCommand, hubStopCommand, hubStatusCommand, hubSearchCommand, hubCallCommand, hubRegisterCommand, hubSkillsCommand, hubOrderCommand, hubHistoryCommand, } from './commands/hub.js';
11
- import { relayRegisterCommand, relayStartCommand, relayStopCommand, relayStatusCommand, relayModelsCommand, relayCreditsCommand, } from './commands/relay.js';
11
+ import { relayRegisterCommand, relayStartCommand, relayStopCommand, relayStatusCommand, relayModelsCommand, relayCreditsCommand, relayLogsCommand, } from './commands/relay.js';
12
12
  import { antigravityLoginCommand, antigravityStatusCommand, } from './commands/antigravity.js';
13
13
  import { createRequire } from 'node:module';
14
14
  const require = createRequire(import.meta.url);
@@ -527,6 +527,20 @@ relay
527
527
  process.exit(1);
528
528
  }
529
529
  });
530
+ relay
531
+ .command('logs')
532
+ .description('Tail the daemon log in real time (like `tail -f ~/.clawmoney/relay.log`)')
533
+ .option('-n, --lines <n>', 'Lines of history to show before following', '50')
534
+ .option('--no-follow', "Print and exit instead of following")
535
+ .action(async (options) => {
536
+ try {
537
+ await relayLogsCommand(options);
538
+ }
539
+ catch (err) {
540
+ console.error(err.message);
541
+ process.exit(1);
542
+ }
543
+ });
530
544
  relay
531
545
  .command('models')
532
546
  .description('List available relay models')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawmoney",
3
- "version": "0.15.31",
3
+ "version": "0.15.32",
4
4
  "description": "ClawMoney CLI -- Earn rewards with your AI agent",
5
5
  "type": "module",
6
6
  "bin": {