clawmoney 0.15.30 → 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.
- package/dist/commands/relay-setup.js +45 -39
- package/dist/commands/relay.d.ts +4 -0
- package/dist/commands/relay.js +86 -18
- package/dist/index.js +15 -1
- package/package.json +1 -1
|
@@ -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.
|
|
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,47 +285,50 @@ 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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
if (detail.includes("Already registered")) {
|
|
315
|
-
succeeded++;
|
|
316
|
-
}
|
|
317
|
-
else {
|
|
318
|
-
failed++;
|
|
319
|
-
failures.push({ cli: r.cli, model: r.model, error: detail });
|
|
320
|
-
}
|
|
321
|
-
}
|
|
298
|
+
const batchBody = {
|
|
299
|
+
providers: registrations.map((r) => ({
|
|
300
|
+
cli_type: r.cli,
|
|
301
|
+
model: r.model,
|
|
302
|
+
mode: "chat",
|
|
303
|
+
concurrency,
|
|
304
|
+
daily_limit_usd: dailyLimit,
|
|
305
|
+
price_input_per_m: r.input,
|
|
306
|
+
price_output_per_m: r.output,
|
|
307
|
+
})),
|
|
308
|
+
};
|
|
309
|
+
try {
|
|
310
|
+
const resp = await apiPost("/api/v1/relay/providers/batch", batchBody, config.api_key);
|
|
311
|
+
if (!resp.ok) {
|
|
312
|
+
const raw = resp.data && typeof resp.data === "object" && "detail" in resp.data
|
|
313
|
+
? resp.data.detail
|
|
314
|
+
: resp.data;
|
|
315
|
+
const detail = typeof raw === "string" ? raw : JSON.stringify(raw);
|
|
316
|
+
regSpin.stop(chalk.red(`✗ Batch registration failed: ${detail}`));
|
|
317
|
+
cancel("Setup aborted");
|
|
318
|
+
process.exit(1);
|
|
322
319
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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 });
|
|
327
325
|
}
|
|
328
326
|
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
regSpin.stop(chalk.red(`✗ Batch registration failed: ${err.message}`));
|
|
329
|
+
cancel("Setup aborted");
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
329
332
|
if (failed === 0) {
|
|
330
333
|
regSpin.stop(`${chalk.green(`✓ ${succeeded} providers registered`)} ` +
|
|
331
334
|
chalk.dim(`(${limitLabel[dailyLimit] ?? `$${dailyLimit}`} quota share · you earn ~${earnPct}%)`));
|
|
@@ -362,11 +365,14 @@ export async function relaySetupCommand() {
|
|
|
362
365
|
outro(chalk.yellow("Setup complete (daemon not started)"));
|
|
363
366
|
return;
|
|
364
367
|
}
|
|
365
|
-
log.message
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
log.message(
|
|
369
|
-
|
|
368
|
+
// One multi-line log.message renders each line with a `│` prefix
|
|
369
|
+
// but without clack's inter-call gap — 3 bullets fit in 3 lines
|
|
370
|
+
// instead of 6.
|
|
371
|
+
log.message(chalk.dim("Next:") +
|
|
372
|
+
"\n" +
|
|
373
|
+
` ${chalk.cyan("clawmoney relay status")} daemon health + providers\n` +
|
|
374
|
+
` ${chalk.cyan("clawmoney relay credits")} earnings + balance\n` +
|
|
375
|
+
` ${chalk.cyan("clawmoney relay stop")} stop daemon`);
|
|
370
376
|
const cliLabel = uniqueClis.length === 1
|
|
371
377
|
? `${uniqueClis[0]} daemon running`
|
|
372
378
|
: `daemon serving ${uniqueClis.join(" + ")}`;
|
package/dist/commands/relay.d.ts
CHANGED
|
@@ -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>;
|
package/dist/commands/relay.js
CHANGED
|
@@ -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
|
|
@@ -206,36 +232,78 @@ export async function relayStatusCommand() {
|
|
|
206
232
|
else {
|
|
207
233
|
console.log(chalk.dim(" Local process: not running"));
|
|
208
234
|
}
|
|
209
|
-
// Remote status
|
|
235
|
+
// Remote status. /api/v1/relay/providers/me returns a LIST of
|
|
236
|
+
// RelayProviderPublic (one row per registered model), so we can't
|
|
237
|
+
// treat the body as a single object. For multi-cli providers the
|
|
238
|
+
// list can easily be 7-10 rows — render them as a table with one
|
|
239
|
+
// line per row instead of 14 labeled lines for a single picked row.
|
|
210
240
|
const spinner = ora("Fetching relay provider status...").start();
|
|
211
241
|
try {
|
|
212
242
|
const resp = await apiGet("/api/v1/relay/providers/me", config.api_key);
|
|
213
243
|
if (!resp.ok) {
|
|
214
244
|
if (resp.status === 404) {
|
|
215
245
|
spinner.info("Not registered as relay provider yet.");
|
|
216
|
-
console.log(chalk.dim(` Run "clawmoney relay
|
|
246
|
+
console.log(chalk.dim(` Run "clawmoney relay setup" to get started.`));
|
|
217
247
|
return;
|
|
218
248
|
}
|
|
219
|
-
const detail = resp.data?.detail ?? resp.status;
|
|
249
|
+
const detail = resp.data?.detail ?? String(resp.status);
|
|
220
250
|
spinner.fail(chalk.red(`Failed to fetch status: ${detail}`));
|
|
221
251
|
process.exit(1);
|
|
222
252
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
253
|
+
// Normalize: backend currently returns a list, but guard against
|
|
254
|
+
// a single-object shape in case someone points the CLI at an older
|
|
255
|
+
// Hub build.
|
|
256
|
+
const providers = Array.isArray(resp.data)
|
|
257
|
+
? resp.data
|
|
258
|
+
: resp.data
|
|
259
|
+
? [resp.data]
|
|
260
|
+
: [];
|
|
261
|
+
if (providers.length === 0) {
|
|
262
|
+
spinner.info("No providers registered yet.");
|
|
263
|
+
console.log(chalk.dim(` Run "clawmoney relay setup" to get started.`));
|
|
264
|
+
return;
|
|
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
|
+
});
|
|
279
|
+
spinner.succeed(`Relay Providers (${providers.length})`);
|
|
226
280
|
console.log("");
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
console.log(
|
|
236
|
-
console.log(
|
|
237
|
-
|
|
238
|
-
|
|
281
|
+
// Aggregate stats across all rows since users think of earnings /
|
|
282
|
+
// spend as account-level, not per-model.
|
|
283
|
+
const totalEarned = providers.reduce((s, p) => s + (p.total_earned_usd ?? 0), 0);
|
|
284
|
+
const totalRequests = providers.reduce((s, p) => s + (p.total_requests ?? 0), 0);
|
|
285
|
+
const totalDailySpent = providers.reduce((s, p) => s + (p.daily_spent_usd ?? 0), 0);
|
|
286
|
+
const totalDailyLimit = providers.reduce((s, p) => s + (p.daily_limit_usd ?? 0), 0);
|
|
287
|
+
// Per-provider rows — compact table with status/cli/model/load.
|
|
288
|
+
const header = ` ${"STATUS".padEnd(9)} ${"CLI".padEnd(12)} ${"MODEL".padEnd(30)} ${"LOAD".padEnd(8)} ${"EARNED".padEnd(10)}`;
|
|
289
|
+
console.log(chalk.bold(header));
|
|
290
|
+
console.log(chalk.dim(" " + "─".repeat(75)));
|
|
291
|
+
for (const p of providers) {
|
|
292
|
+
const statusRaw = (p.status ?? "-").padEnd(9);
|
|
293
|
+
const statusColored = p.status === "online"
|
|
294
|
+
? chalk.green(statusRaw)
|
|
295
|
+
: p.status === "offline"
|
|
296
|
+
? chalk.dim(statusRaw)
|
|
297
|
+
: chalk.yellow(statusRaw);
|
|
298
|
+
const cli = (p.cli_type ?? "-").padEnd(12);
|
|
299
|
+
const model = (p.model ?? "-").padEnd(30);
|
|
300
|
+
const load = `${p.current_load ?? 0}/${p.concurrency ?? "-"}`.padEnd(8);
|
|
301
|
+
const earned = `$${(p.total_earned_usd ?? 0).toFixed(2)}`.padEnd(10);
|
|
302
|
+
console.log(` ${statusColored} ${cli} ${model} ${load} ${earned}`);
|
|
303
|
+
}
|
|
304
|
+
console.log("");
|
|
305
|
+
console.log(` ${chalk.bold("Daily quota:")} $${totalDailySpent.toFixed(2)} / $${totalDailyLimit.toFixed(2)}`);
|
|
306
|
+
console.log(` ${chalk.bold("Total earned:")} $${totalEarned.toFixed(2)} (${totalRequests} requests)`);
|
|
239
307
|
}
|
|
240
308
|
catch (err) {
|
|
241
309
|
spinner.fail(chalk.red("Failed to fetch status"));
|
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')
|