diff --git a/.gitignore b/.gitignore index 6a874e7..8132279 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,9 @@ dist/ *.js.map *config*.json acp-cli-signer/acp-cli-signer -.DS_Store \ No newline at end of file +.DS_Store + +# Superpowers/Hermes Agent artifacts +.hermes/ +docs/plans/ +docs/superpowers/ diff --git a/SKILL.md b/SKILL.md index b09be6a..600a606 100644 --- a/SKILL.md +++ b/SKILL.md @@ -155,6 +155,7 @@ Pay for the agent's own LLM-inference workloads from a USDC-funded compute accou |---|---|---| | `acp compute status --json` | Show the compute account balance, usage, and limit | `{limit, limitRemaining, usage, ...}` | | `acp compute top-up --amount [--chain-id ] --json` | Transfer USDC (+ a processing fee) from the agent's wallet to the ACP fee wallet to credit the compute account | `{amount, totalAmount, chainId, feeWallet, txnHash}` | +| `acp compute apply` | Opens the agent's web compute page (`app.virtuals.io/acp/agents/?tab=compute&action=link-github`) where the developer connects GitHub (Privy OAuth) and claims the credit — the CLI can't auth to the campaign backend | `--json` returns `{status:"redirect", url, agentId}` | The credited balance updates shortly after the transfer confirms — re-probe with `compute status`. @@ -523,7 +524,7 @@ src/ chain.ts Chain info email.ts Agent email card.ts Agent virtual cards - compute.ts Agent compute account (status, top-up) + compute.ts Agent compute account (status, top-up, apply) skill.ts Inspect/verify the bundled SKILL.md (path, print, check) lib/ config.ts Load/save config.json at ~/.config/acp/ (override with ACP_CONFIG_DIR) diff --git a/src/commands/compute.ts b/src/commands/compute.ts index 306f7d6..bfd3f22 100644 --- a/src/commands/compute.ts +++ b/src/commands/compute.ts @@ -9,6 +9,7 @@ import { getActiveAgentId } from "../lib/activeAgent"; import { createProviderAdapter, getWalletAddress } from "../lib/agentFactory"; import { formatChainId, formatChainIds } from "../lib/chains"; import { CliError } from "../lib/errors"; +import { openBrowser } from "../lib/browser"; // ── Registration ──────────────────────────────────────────────────── @@ -160,4 +161,60 @@ export function registerComputeCommands(program: Command): void { outputError(json, err instanceof Error ? err : String(err)); } }); + + compute + .command("apply") + .description("Apply for Venice developer compute credits ($200 approved)") + .action(async (_opts, cmd) => { + const json = isJson(cmd); + try { + const { agentApi } = await getClient(); + const agentId = getActiveAgentId(json); + if (!agentId) return; + + // GitHub linking + enrollment for this campaign run on the web app's + // Privy-authenticated session, which the CLI can't reproduce. So the CLI + // just sends the user to the agent's compute page, where the GitHub-linking + // UI lives, and they complete the connect + claim there. + const webUrl = agentApi.getDeveloperCampaignWebUrl(agentId); + + if (json) { + outputResult(json, { status: "redirect", url: webUrl, agentId }); + return; + } + + console.log( + `\n🚀 ${c.cyan( + "Venice-Virtuals Developer Inference Credit Campaign ($200 approved)" + )}` + ); + console.log( + `${c.dim( + "----------------------------------------------------------------------" + )}` + ); + console.log(`\nApply by connecting your GitHub on this agent's compute page:`); + console.log(`\n ${c.underline(webUrl)}\n`); + console.log(` ${c.dim("1.")} Log in if prompted`); + console.log(` ${c.dim("2.")} Connect GitHub — verifies your eligible repositories`); + console.log(` ${c.dim("3.")} Claim your $200 weekly compute credit`); + + if (isTTY()) { + openBrowser(webUrl); + console.log( + `\nℹ️ ${c.dim("Opened in your browser. Run")} ${c.bold( + "acp compute status" + )} ${c.dim("afterward to see your credit.")}` + ); + } else { + console.log( + `\nℹ️ ${c.dim("Open the link above, then run")} ${c.bold( + "acp compute status" + )} ${c.dim("to see your credit.")}` + ); + } + } catch (err) { + outputError(json, err instanceof Error ? err : String(err)); + } + }); } diff --git a/src/lib/api/agent.ts b/src/lib/api/agent.ts index 0b92594..6a3b6cc 100644 --- a/src/lib/api/agent.ts +++ b/src/lib/api/agent.ts @@ -1049,6 +1049,23 @@ export class AgentApi { ); } + // ── Developer campaign methods ─────────────────────────────────── + + private getWebBaseUrl(): string { + const base = this.client["baseUrl"]; + const isTestnet = base.includes("api-dev") || base.includes("testnet") || base.includes("dev") || base.includes("acp-dev"); + return isTestnet ? "https://app-dev.virtuals.io" : "https://app.virtuals.io"; + } + + /** + * Web URL of an agent's compute / developer-campaign page, where the user can + * connect GitHub via the app's Privy OAuth flow (used as the headless-CLI + * fallback when no local GitHub token is available). + */ + getDeveloperCampaignWebUrl(agentId: string): string { + return `${this.getWebBaseUrl()}/acp/agents/${agentId}?tab=compute&action=link-github`; + } + async getAgentAssets( agentId: string, networks: string[] diff --git a/src/lib/color.ts b/src/lib/color.ts index ddf7225..8677d7f 100644 --- a/src/lib/color.ts +++ b/src/lib/color.ts @@ -8,6 +8,7 @@ export const c = { red: pc.red, cyan: pc.cyan, magenta: pc.magenta, + underline: pc.underline, status: (status: string) => { switch (status) { case "completed": diff --git a/src/lib/config.ts b/src/lib/config.ts index 863aced..53f4efb 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -73,7 +73,12 @@ function isKeychainUnavailable(err: unknown): boolean { async function withKeyringFallback(op: () => Promise): Promise { try { - return await op(); + const res = await op(); + if (res === null) { + await useBackend("file"); + return await op(); + } + return res; } catch (err) { if (!isKeychainUnavailable(err)) throw err; await useBackend("file");