clawmoney 0.15.20 → 0.15.22
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 +39 -41
- package/dist/commands/relay.js +6 -1
- package/dist/relay/provider.js +116 -18
- package/package.json +1 -1
|
@@ -5,7 +5,8 @@ import { join } from "node:path";
|
|
|
5
5
|
import { intro, outro, multiselect, confirm, select, spinner, isCancel, cancel, log, } from "@clack/prompts";
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import { apiPost } from "../utils/api.js";
|
|
8
|
-
import { requireConfig } from "../utils/config.js";
|
|
8
|
+
import { loadConfig, requireConfig } from "../utils/config.js";
|
|
9
|
+
import { setupCommand } from "./setup.js";
|
|
9
10
|
import { API_PRICES, RELAY_DISCOUNT, PLATFORM_FEE } from "../relay/pricing.js";
|
|
10
11
|
// ── Per-cli_type model catalogs ──
|
|
11
12
|
//
|
|
@@ -129,6 +130,27 @@ function formatBuyerPrice(input, output) {
|
|
|
129
130
|
}
|
|
130
131
|
// ── Main command ──
|
|
131
132
|
export async function relaySetupCommand() {
|
|
133
|
+
// ── Step 0: ensure the agent is logged in ──
|
|
134
|
+
//
|
|
135
|
+
// Relay setup relies on an ACTIVE ClawMoney agent (api_key + agent_id
|
|
136
|
+
// in ~/.clawmoney/config.yaml) to register provider rows and to auth
|
|
137
|
+
// the daemon's WS connection. If the user runs `clawmoney relay setup`
|
|
138
|
+
// before ever running `clawmoney setup`, we inline the login flow
|
|
139
|
+
// here instead of throwing a raw "No config found" error. The nested
|
|
140
|
+
// setupCommand uses its own ora/prompt UI — visually different from
|
|
141
|
+
// the clack wizard below, but that's acceptable since it only runs on
|
|
142
|
+
// the first-time-user path.
|
|
143
|
+
let existing = loadConfig();
|
|
144
|
+
if (!existing) {
|
|
145
|
+
console.log(chalk.yellow("\n You're not logged in yet — running `clawmoney setup` first.\n"));
|
|
146
|
+
await setupCommand();
|
|
147
|
+
existing = loadConfig();
|
|
148
|
+
if (!existing) {
|
|
149
|
+
console.log(chalk.red("\n Login did not complete. Run `clawmoney setup` manually, then re-run `clawmoney relay setup`.\n"));
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
console.log("");
|
|
153
|
+
}
|
|
132
154
|
const config = requireConfig();
|
|
133
155
|
intro(chalk.cyan(" ClawMoney Relay Setup "));
|
|
134
156
|
log.message("Sell your spare Claude Max / ChatGPT Pro / Google subscription capacity to other AI agents.");
|
|
@@ -363,54 +385,30 @@ export async function relaySetupCommand() {
|
|
|
363
385
|
}
|
|
364
386
|
// ── Step 8: auto-start the daemon ──
|
|
365
387
|
//
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
//
|
|
388
|
+
// The daemon now runs in multi-cli auto mode by default: it fetches
|
|
389
|
+
// every provider this agent has registered, preflights each distinct
|
|
390
|
+
// cli_type, and dispatches requests to the right upstream based on
|
|
391
|
+
// the `cli_type` field in each incoming relay_request. A single
|
|
392
|
+
// daemon process can serve Claude + Codex + Gemini + Antigravity
|
|
393
|
+
// simultaneously, so there's no need to pick one here.
|
|
371
394
|
const uniqueClis = Array.from(new Set(selectedClis));
|
|
372
|
-
if (uniqueClis.length === 1) {
|
|
373
|
-
// Single cli_type — go straight into the daemon. The user already
|
|
374
|
-
// confirmed "Register all N providers?" above, so asking a second
|
|
375
|
-
// time just adds friction.
|
|
376
|
-
const cli = uniqueClis[0];
|
|
377
|
-
const { relayStartCommand } = await import("./relay.js");
|
|
378
|
-
try {
|
|
379
|
-
await relayStartCommand({ cli });
|
|
380
|
-
}
|
|
381
|
-
catch (err) {
|
|
382
|
-
log.error(`Failed to start daemon: ${err.message}\n` +
|
|
383
|
-
`Try manually: ${chalk.cyan(`clawmoney relay start --cli ${cli}`)}`);
|
|
384
|
-
outro(chalk.yellow("Setup complete (daemon not started)"));
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
log.message("");
|
|
388
|
-
log.message(chalk.dim("Useful follow-up commands:"));
|
|
389
|
-
log.message(` ${chalk.cyan("clawmoney relay status")} # check daemon health + provider list`);
|
|
390
|
-
log.message(` ${chalk.cyan("clawmoney relay credits")} # check earnings + payout balance`);
|
|
391
|
-
log.message(` ${chalk.cyan("clawmoney relay stop")} # stop the daemon`);
|
|
392
|
-
outro(chalk.green("Setup complete · daemon running"));
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
// Multi cli_type — daemon can only host one at a time today. Start
|
|
396
|
-
// the first registered cli_type directly and tell the user how to
|
|
397
|
-
// switch to the others later.
|
|
398
|
-
const firstCli = uniqueClis[0];
|
|
399
|
-
log.warn(`You registered providers across ${uniqueClis.length} CLI families ` +
|
|
400
|
-
`(${uniqueClis.join(", ")}). The daemon currently serves ONE cli_type ` +
|
|
401
|
-
`per process — starting ${chalk.cyan(firstCli)} first.`);
|
|
402
395
|
const { relayStartCommand } = await import("./relay.js");
|
|
403
396
|
try {
|
|
404
|
-
await relayStartCommand({
|
|
397
|
+
await relayStartCommand({});
|
|
405
398
|
}
|
|
406
399
|
catch (err) {
|
|
407
400
|
log.error(`Failed to start daemon: ${err.message}\n` +
|
|
408
|
-
`Try manually: ${chalk.cyan(
|
|
401
|
+
`Try manually: ${chalk.cyan("clawmoney relay start")}`);
|
|
409
402
|
outro(chalk.yellow("Setup complete (daemon not started)"));
|
|
410
403
|
return;
|
|
411
404
|
}
|
|
412
405
|
log.message("");
|
|
413
|
-
log.message(chalk.dim("
|
|
414
|
-
log.message(
|
|
415
|
-
|
|
406
|
+
log.message(chalk.dim("Useful follow-up commands:"));
|
|
407
|
+
log.message(` ${chalk.cyan("clawmoney relay status")} # check daemon health + provider list`);
|
|
408
|
+
log.message(` ${chalk.cyan("clawmoney relay credits")} # check earnings + payout balance`);
|
|
409
|
+
log.message(` ${chalk.cyan("clawmoney relay stop")} # stop the daemon`);
|
|
410
|
+
const cliLabel = uniqueClis.length === 1
|
|
411
|
+
? `${uniqueClis[0]} daemon running`
|
|
412
|
+
: `daemon serving ${uniqueClis.join(" + ")}`;
|
|
413
|
+
outro(chalk.green(`Setup complete · ${cliLabel}`));
|
|
416
414
|
}
|
package/dist/commands/relay.js
CHANGED
|
@@ -151,7 +151,12 @@ export async function relayStartCommand(options) {
|
|
|
151
151
|
if (pid && isRelayPidAlive(pid)) {
|
|
152
152
|
spinner.succeed(chalk.green(`Relay Provider started (PID ${pid})`));
|
|
153
153
|
console.log(chalk.dim(` Log file: ${LOG_FILE}`));
|
|
154
|
-
|
|
154
|
+
if (options.cli) {
|
|
155
|
+
console.log(chalk.dim(` CLI: ${options.cli} (single)`));
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
console.log(chalk.dim(` CLI: auto (serving all registered cli_types for this agent)`));
|
|
159
|
+
}
|
|
155
160
|
console.log(chalk.dim(` API key: ${config.api_key.slice(0, 8)}...`));
|
|
156
161
|
}
|
|
157
162
|
else {
|
package/dist/relay/provider.js
CHANGED
|
@@ -7,6 +7,7 @@ import { callClaudeApi, callClaudeApiPassthrough, preflightClaudeApi, getRateGua
|
|
|
7
7
|
import { callCodexApi, callCodexApiPassthrough, preflightCodexApi, getRateGuardSnapshot as getCodexRateGuardSnapshot, } from "./upstream/codex-api.js";
|
|
8
8
|
import { callGeminiApi, preflightGeminiApi, getGeminiRateGuardSnapshot, } from "./upstream/gemini-api.js";
|
|
9
9
|
import { callAntigravityApi, preflightAntigravityApi, getAntigravityRateGuardSnapshot, } from "./upstream/antigravity-api.js";
|
|
10
|
+
import { apiGet } from "../utils/api.js";
|
|
10
11
|
/**
|
|
11
12
|
* Pick the rate-guard snapshot matching this request's cli_type. Fixes a
|
|
12
13
|
* pre-existing bug where gemini/codex responses were piggy-backing Claude's
|
|
@@ -305,6 +306,45 @@ async function executeRelayRequest(request, config, sendChunk) {
|
|
|
305
306
|
};
|
|
306
307
|
}
|
|
307
308
|
}
|
|
309
|
+
// ── Preflight helpers ──
|
|
310
|
+
// Map a cli_type string to its preflight function. Returns null for
|
|
311
|
+
// unknown types so the caller can skip them gracefully instead of
|
|
312
|
+
// crashing the daemon.
|
|
313
|
+
function getPreflightFn(cliType) {
|
|
314
|
+
switch (cliType) {
|
|
315
|
+
case "claude":
|
|
316
|
+
return preflightClaudeApi;
|
|
317
|
+
case "codex":
|
|
318
|
+
return preflightCodexApi;
|
|
319
|
+
case "gemini":
|
|
320
|
+
return preflightGeminiApi;
|
|
321
|
+
case "antigravity":
|
|
322
|
+
return preflightAntigravityApi;
|
|
323
|
+
default:
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
// Query the Hub for the full set of cli_types the current agent has
|
|
328
|
+
// registered as relay providers. Used by the multi-cli daemon to decide
|
|
329
|
+
// which upstream preflights to run. Returns null on any network / auth
|
|
330
|
+
// failure — caller falls back to single-cli mode.
|
|
331
|
+
async function fetchRegisteredCliTypes(config) {
|
|
332
|
+
try {
|
|
333
|
+
const resp = await apiGet("/api/v1/relay/providers/me", config.api_key);
|
|
334
|
+
if (!resp.ok || !Array.isArray(resp.data)) {
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
const seen = new Set();
|
|
338
|
+
for (const p of resp.data) {
|
|
339
|
+
if (typeof p.cli_type === "string" && p.cli_type)
|
|
340
|
+
seen.add(p.cli_type);
|
|
341
|
+
}
|
|
342
|
+
return Array.from(seen);
|
|
343
|
+
}
|
|
344
|
+
catch {
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
308
348
|
// ── Main daemon entry point ──
|
|
309
349
|
export function runRelayProvider(cliOverride) {
|
|
310
350
|
// Check for existing process
|
|
@@ -318,23 +358,76 @@ export function runRelayProvider(cliOverride) {
|
|
|
318
358
|
// process.env.HTTPS_PROXY / http_proxy at init time. Must run BEFORE any
|
|
319
359
|
// preflight call so the first outbound request already goes through it.
|
|
320
360
|
applyProxyFromConfig(config);
|
|
321
|
-
// Validate
|
|
322
|
-
// of on the first inbound request.
|
|
323
|
-
//
|
|
324
|
-
//
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
361
|
+
// Validate OAuth tokens + fingerprints up front so we fail fast on the
|
|
362
|
+
// happy path instead of on the first inbound request.
|
|
363
|
+
//
|
|
364
|
+
// Two modes:
|
|
365
|
+
//
|
|
366
|
+
// 1. Single-cli legacy mode — `--cli X` passed explicitly. Run only
|
|
367
|
+
// that one preflight and fail-fast on error (preserves the old
|
|
368
|
+
// behavior when the user knows exactly what they're starting).
|
|
369
|
+
//
|
|
370
|
+
// 2. Multi-cli auto mode — no `--cli` override. Query the Hub for the
|
|
371
|
+
// full list of registered providers, collect their distinct
|
|
372
|
+
// cli_types, and run each preflight in parallel. Per-cli failures
|
|
373
|
+
// are tolerated (logged as a warning) so a broken codex OAuth
|
|
374
|
+
// doesn't take down the claude half of the daemon.
|
|
375
|
+
//
|
|
376
|
+
// The Hub already dispatches requests by agent_id and stamps each
|
|
377
|
+
// relay_request with its target `cli_type` — provider.ts's request
|
|
378
|
+
// handler (`executeRelayRequest`) routes to the right upstream module
|
|
379
|
+
// based on that field. So from the Hub's point of view, a daemon that
|
|
380
|
+
// has preflighted multiple cli_types is just "a more capable provider".
|
|
381
|
+
if (cliOverride) {
|
|
382
|
+
// Legacy single-cli path.
|
|
383
|
+
const preflightFn = getPreflightFn(cliOverride);
|
|
384
|
+
if (preflightFn) {
|
|
385
|
+
preflightFn(config.relay.rate_guard).catch((err) => {
|
|
386
|
+
logger.error(`${cliOverride} API preflight failed: ${err.message}`);
|
|
387
|
+
process.exit(1);
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
// Multi-cli auto mode — fire-and-forget, don't block startup on the
|
|
393
|
+
// HTTP round-trip. The WS connection can come up in parallel; the
|
|
394
|
+
// preflights only gate the FIRST call of each family, not the WS
|
|
395
|
+
// handshake.
|
|
396
|
+
(async () => {
|
|
397
|
+
const registered = await fetchRegisteredCliTypes(config);
|
|
398
|
+
if (!registered || registered.length === 0) {
|
|
399
|
+
logger.warn("Could not determine registered cli_types from Hub. " +
|
|
400
|
+
"Falling back to config cli_type for preflight.");
|
|
401
|
+
const fallback = config.relay.cli_type;
|
|
402
|
+
const fn = getPreflightFn(fallback);
|
|
403
|
+
if (fn) {
|
|
404
|
+
try {
|
|
405
|
+
await fn(config.relay.rate_guard);
|
|
406
|
+
}
|
|
407
|
+
catch (err) {
|
|
408
|
+
logger.warn(`${fallback} preflight failed: ${err.message}`);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
logger.info(`Preflighting ${registered.length} registered cli_type(s): ${registered.join(", ")}`);
|
|
414
|
+
await Promise.all(registered.map(async (ct) => {
|
|
415
|
+
const fn = getPreflightFn(ct);
|
|
416
|
+
if (!fn) {
|
|
417
|
+
logger.warn(`Unknown cli_type ${ct} — skipping preflight`);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
try {
|
|
421
|
+
await fn(config.relay.rate_guard);
|
|
422
|
+
logger.info(` ${ct} preflight OK`);
|
|
423
|
+
}
|
|
424
|
+
catch (err) {
|
|
425
|
+
logger.warn(` ${ct} preflight failed: ${err.message} — ` +
|
|
426
|
+
`that family will NOT be able to serve requests until fixed`);
|
|
427
|
+
}
|
|
428
|
+
}));
|
|
429
|
+
})().catch((err) => {
|
|
430
|
+
logger.warn(`Multi-cli preflight driver error: ${err.message}`);
|
|
338
431
|
});
|
|
339
432
|
}
|
|
340
433
|
const activeTasks = new Set();
|
|
@@ -419,5 +512,10 @@ export function runRelayProvider(cliOverride) {
|
|
|
419
512
|
writeRelayPid();
|
|
420
513
|
wsClient.start();
|
|
421
514
|
logger.info("Relay Provider running. Listening for relay requests...");
|
|
422
|
-
|
|
515
|
+
if (cliOverride) {
|
|
516
|
+
logger.info(`Config: cli=${cliOverride} (single), mode=${config.relay.mode}, concurrency=${config.relay.concurrency}`);
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
logger.info(`Config: cli=auto (multi), mode=${config.relay.mode}, concurrency=${config.relay.concurrency}`);
|
|
520
|
+
}
|
|
423
521
|
}
|