Skip to content

Add service job provider runtime#9

Open
ai-virtual-b wants to merge 10 commits intomainfrom
service-jobs-be-runtime
Open

Add service job provider runtime#9
ai-virtual-b wants to merge 10 commits intomainfrom
service-jobs-be-runtime

Conversation

@ai-virtual-b
Copy link
Copy Markdown
Contributor

@ai-virtual-b ai-virtual-b commented Apr 28, 2026

Summary

  • add acp serve commands for service-job provider runtimes: init, start, endpoints, deploy bundle, stop, logs
  • add handler/budget/offering scaffolds and a runtime that connects outbound to the BE service-job namespace
  • keep x402/MPP public endpoints on BE while the provider self-hosts or deploys the handler runtime
  • keep --settle-8183 reserved but disabled for x402/MPP until ERC-8183 service-job support exists

Notes

  • deploy currently creates a Docker-ready bundle and next steps; it does not execute a hosting-provider deployment
  • provider runtime needs ACP_AGENT_TOKEN and outbound access to BE

Tests

  • npm run build
  • tsx bin/acp.ts serve endpoints --dir /tmp/acp-cli-smoke --json

Note

High Risk
High risk because it introduces a new provider runtime that verifies and settles payments (x402 EIP-3009 and MPP tempo) and adds deployment automation, which touches signing, RPC configuration, and on-chain transactions.

Overview
Adds a new acp serve feature set that can scaffold, run, and deploy a provider “service-job” runtime for a single offering (init, start, endpoints, deploy, stop, logs). The runtime loads a developer handler.ts (optional budget.ts), exposes only a local /health endpoint, and connects outbound to the BE /service-jobs Socket.IO namespace to handle payment-challenge and job requests.

Implements payment verification/settlement inside the runtime for x402 (via @x402/* with EIP-3009 transfers) and mpp (via mppx tempo receipts), including chain/RPC/USDC configuration helpers and optional handler sandboxing with a worker-thread timeout.

Extends auth/config plumbing to support an agent-scoped token (/auth/agent), centralizes API base URL selection via getApiUrl(), and updates CLI wiring/deps (new @x402/*, mppx, viem bump) to support the new serve commands.

Reviewed by Cursor Bugbot for commit 3ceb483. Bugbot is set up for automated code reviews on this repo. Configure here.

Comment thread src/commands/serve.ts
Comment thread src/commands/serve.ts Outdated
Comment thread serve/server/index.ts
Comment thread src/commands/serve.ts Outdated
Comment thread serve/runtime/loader.ts Outdated
Comment thread src/commands/serve.ts
Comment thread serve/runtime/sandbox.ts
Comment thread serve/server/index.ts
Comment thread serve/server/payment/chain.ts Outdated
Comment thread serve/server/payment/mpp.ts
Comment thread src/commands/serve.ts
Comment thread src/commands/serve.ts Outdated
Comment thread serve/server/payment/chain.ts
Comment thread serve/server/payment/chain.ts
Comment thread src/commands/serve.ts Outdated
Comment thread src/commands/serve.ts Outdated
Comment thread src/commands/serve.ts Outdated
process.kill(pid, "SIGTERM");
stopped += 1;
} catch {}
break;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unconditional break prevents fallback to legacy PID files

Medium Severity

The break statement in the stop command's PID file loop is unconditional — it sits outside the try/catch, so it always executes after the first existing PID file is found, even when process.kill() throws (e.g., stale PID). This prevents the loop from falling through to check the legacy PID file path, which is the entire reason the loop and pidFiles array exist.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6eccf1e. Configure here.

Comment thread src/commands/serve.ts Outdated
"package-lock.json",
"tsconfig.json",
]) {
const source = resolve(process.cwd(), relativePath);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploy bundle copies from cwd instead of CLI source

Medium Severity

copyRuntimeBundle resolves CLI source directories (bin, src, serve, etc.) relative to process.cwd(), but process.cwd() is the user's working directory, not the CLI package's installation directory. The init command correctly uses import.meta.url to locate scaffold files, but deploy does not, so the bundle will silently miss CLI source files if the command is run from a directory other than the repo root.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 6eccf1e. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

There are 5 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 3ceb483. Configure here.

input: unknown;
};
const originalEnv = process.env;
process.env = {};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sandbox environment wipe ineffective for worker thread

Medium Severity

Assigning process.env = {} replaces the special Node.js env proxy with a plain object in the worker thread, but workerData already serialized the input before the worker started. More importantly, the worker thread inherits the parent's actual OS environment. The process.env reassignment only affects reads from process.env within the worker JS context — any native addons, child processes spawned with default options, or modules that cached process.env at import time can still access the original values. The "sandbox" gives a false sense of isolation.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3ceb483. Configure here.

[bscTestnet.id]: 18,
[xLayer.id]: 6,
[xLayerTestnet.id]: 6,
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supported chains lack USDC addresses causing confusing failures

Low Severity

SUPPORTED_CHAINS includes mainnet and sepolia, but USDC_ADDRESSES and USDC_DECIMALS have no entries for those chain IDs. getChain() succeeds for these chains (no error), but any subsequent payment operation calling getUsdcAddress() throws an opaque error unless environment variables are manually set. This creates a confusing partial-success failure mode.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3ceb483. Configure here.

Comment thread src/commands/serve.ts
),
opts.bundleDir,
);
commands.push(result.command);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MPP secret key exposed in command-line arguments

Medium Severity

The MPP_SECRET_KEY is passed as a command-line argument to railway variable set via the envVars array, making it visible in process listings. The agent token is correctly protected by passing it via --stdin, but the MPP secret key (a cryptographic secret used for signing payment challenges) receives no such protection.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3ceb483. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant