@finchip.ai/foga 0.6.0-alpha.4 → 0.6.0-alpha.6
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/README.md +14 -5
- package/bin/foga.js +3 -2
- package/dist/cli.js +59 -90
- package/dist/demo/anvil.js +129 -0
- package/dist/demo/birth.js +71 -0
- package/dist/demo/loop-d.js +227 -111
- package/dist/demo/quickstart.js +312 -0
- package/dist/doctor/loop-d.js +29 -7
- package/dist/world/deploy.js +13 -12
- package/docs/design-memo.md +2 -0
- package/docs/examples/README.md +5 -1
- package/docs/loop-d.md +22 -4
- package/docs/mcp.md +3 -3
- package/docs/quickstart.md +78 -0
- package/package.json +3 -1
- package/scripts/prepare.mjs +21 -0
- package/server.json +2 -2
package/docs/mcp.md
CHANGED
|
@@ -85,20 +85,20 @@ With the operator key and a born cell, the watchers (heartbeat, tender discovery
|
|
|
85
85
|
|
|
86
86
|
## Client configuration
|
|
87
87
|
|
|
88
|
-
The three local clients take the same server definition; `docs/examples/` has one file per client:
|
|
88
|
+
`foga quickstart` writes these files with every path filled in for a cell it births on a local chain, and prints them (docs/quickstart.md); what follows is the shape. The three local clients take the same server definition; `docs/examples/` has one file per client:
|
|
89
89
|
|
|
90
90
|
- Claude Desktop — `docs/examples/claude_desktop_config.json` (the `mcpServers` block of `claude_desktop_config.json`)
|
|
91
91
|
- Claude Code — `docs/examples/claude-code-mcp.json` (a project's `.mcp.json`), or on the command line:
|
|
92
92
|
`claude mcp add foga -e FOGA_OPERATOR_KEY_FILE=/home/me/.foga/keys/operator.key -- npx -y @finchip.ai/foga mcp --config /home/me/cells/D.json`
|
|
93
93
|
- Cursor — `docs/examples/cursor-mcp.json` (`.cursor/mcp.json`)
|
|
94
94
|
|
|
95
|
-
Each runs `npx -y @finchip.ai/foga mcp --config <cell.config.json>` with, optionally, `FOGA_OPERATOR_KEY_FILE` (the operator acts) and `FOGA_RPC_URL` (when the config names no RPC) in `env`.
|
|
95
|
+
Each runs `npx -y @finchip.ai/foga mcp --config <cell.config.json>` with, optionally, `FOGA_OPERATOR_KEY_FILE` (the operator acts) and `FOGA_RPC_URL` (when the config names no RPC) in `env`. From a checkout the command is `node` with `/path/to/foga/bin/foga.js mcp --config …` after `npm run build`. Without a key file the client gets reads, dry runs and builds: enough to inspect tenders, pre-check them and prepare calldata; nothing it says can make the cell act. A served node is added as a remote: Claude Code with `claude mcp add --transport http foga http://127.0.0.1:8787/mcp --header "Authorization: Bearer <key>"`, other clients with `{"url": "…/mcp", "headers": {"Authorization": "Bearer <key>"}}`; the public OG endpoint `https://foga.finchip.ai/mcp` goes live in 3d. In PowerShell the package name needs quotes on the command line (`npx -y '@finchip.ai/foga' …`: a bare `@` starts a splatting expression); inside a JSON file nothing changes.
|
|
96
96
|
|
|
97
97
|
## The package
|
|
98
98
|
|
|
99
99
|
The npm package is `@finchip.ai/foga` — npm refuses the unscoped name `foga` as too similar to `ora` and `koa`, so it lives under the `finchip.ai` scope; the binary is `foga` and `foga mcp` is the stdio server. `package.json` carries `mcpName: "io.github.garyyang-finchip/foga"`, the name the MCP registry verifies npm ownership by, and `server.json` at the repository root is the registry manifest: the npm package as a stdio server (`npx -y @finchip.ai/foga node mcp --config …`, `FOGA_OPERATOR_KEY_FILE` and `FOGA_RPC_URL` as its environment) and `https://foga.finchip.ai/mcp` as the remote. `engines.node` is `>=22.13`, where `node:sqlite` runs without a flag; CI runs Node 22. Publication to npm and to the registry is part of 3d (the memo's steps: `npm publish` by the package's owner, then `mcp-publisher login github` and `mcp-publisher publish`); the manifest's remote must be reachable when it is published.
|
|
100
100
|
|
|
101
|
-
What ships: `bin/`, `dist/`, `artifacts/bundle.json` (the ABIs), `worlds/`, `docs/` (the rule texts `spec_read` serves), `scenarios/` (the skill packages and the fixture), the prover bridge and the circuit's verification key, `server.json`. `scripts/pack-smoke.mjs <rpc> <world.json>` is the pre-publication check and a CI step: `npm pack`, a clean `npm install` of the tarball in an empty directory, then the packaged CLI alone — `--help`, the public catalog, a schema, a cell born with `foga cell new` and `foga cell init` on the given world, and `foga node mcp` without a key driven by a raw 2025-era JSON-RPC client over stdio (stdout protocol-only, the keyless tool set, `cell_status`, `resources/list`, `spec_read` of the compatibility matrix from inside the package, no key-shaped value on stderr).
|
|
101
|
+
What ships: `bin/`, `dist/`, `artifacts/bundle.json` (the ABIs), `worlds/`, `docs/` (the rule texts `spec_read` serves), `scenarios/` (the skill packages and the fixture), the prover bridge and the circuit's verification key, `server.json`. A checkout installed straight from git (`npm install github:garyyang-finchip/foga`, `npx -y github:garyyang-finchip/foga …`) compiles itself on install (`prepare`, `scripts/prepare.mjs`: only when `dist/` is missing and `src/` is there); `.gitattributes` keeps every text file LF on every platform, the launcher's shebang line included. `scripts/pack-smoke.mjs <rpc> <world.json>` is the pre-publication check and a CI step: `npm pack`, a clean `npm install` of the tarball in an empty directory, then the packaged CLI alone — `--help`, the public catalog, a schema, a cell born with `foga cell new` and `foga cell init` on the given world, and `foga node mcp` without a key driven by a raw 2025-era JSON-RPC client over stdio (stdout protocol-only, the keyless tool set, `cell_status`, `resources/list`, `spec_read` of the compatibility matrix from inside the package, no key-shaped value on stderr).
|
|
102
102
|
|
|
103
103
|
## Acceptance
|
|
104
104
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Quickstart (foga quickstart)
|
|
2
|
+
|
|
3
|
+
One command from nothing to a cell an MCP client can connect to. It needs Node 22.13 or newer and Foundry's `anvil`; nothing else, and no key of yours.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npx -y @finchip.ai/foga quickstart
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
What it does, in order (about twenty seconds on a laptop): starts `anvil` on port 8545; deploys the local world — the ten reference implementations and the FOGA contracts, 47 contracts (`worlds/local.json`); births a cell (`quick`) with owner, operator and guardian keys nobody held before, funded by anvil's first account, which also registers the attested standing scheme and attests the cell's level-2 standing; mints the cell's two skills (`foga-probe`, `finch-extract-verify`); mints two tenders for the cell to find (a probe of the cell itself, an extraction over the fixture); serves the node — the watchers, the HTTP API and MCP over Streamable HTTP on `127.0.0.1:8787` — on the operator key alone, read from its file; checks that both entrances answer (health, a public read, the agent key's pre-check of the first tender, the MCP handshake and the tool list over HTTP; the stdio server's handshake and tool list with the operator key); and prints the summary below, with every path filled in. Then it waits: the chain lives as long as the process, and Ctrl-C ends both.
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
foga quickstart: a FOGA cell is live on a local chain
|
|
13
|
+
|
|
14
|
+
chain anvil on http://127.0.0.1:8545 (chain 31337), world anvil-31337-… — the chain lives as long as this process
|
|
15
|
+
cell quick = 0x… (ERC-8004 agent 1); owner, operator and guardian keys in ~/.foga/quickstart/keys (anvil money, no value)
|
|
16
|
+
skills foga-probe, finch-extract-verify (held and offered)
|
|
17
|
+
tenders #1 finch-extract-verify (0.001 ETH), #2 foga-probe (0.001 ETH) — open, waiting for the cell to take them
|
|
18
|
+
node http://127.0.0.1:8787 — /mcp (Streamable HTTP), /api/<capability>, /healthz; its watchers settle and withdraw what the cell commits to
|
|
19
|
+
files ~/.foga/quickstart/cell.config.json (the cell), ~/.foga/quickstart/connect.md (this text), ~/.foga/quickstart/mcp/*.json (the client configurations)
|
|
20
|
+
|
|
21
|
+
Connect an MCP client over stdio (the client starts the server on this machine; with the operator key it acts for the cell):
|
|
22
|
+
Claude Desktop claude_desktop_config.json, the mcpServers block: ~/.foga/quickstart/mcp/claude_desktop_config.json
|
|
23
|
+
Claude Code .mcp.json in a project: ~/.foga/quickstart/mcp/claude-code.mcp.json
|
|
24
|
+
or one command: claude mcp add foga -e FOGA_OPERATOR_KEY_FILE=… -- npx -y @finchip.ai/foga mcp --config …
|
|
25
|
+
Cursor .cursor/mcp.json: ~/.foga/quickstart/mcp/cursor-mcp.json
|
|
26
|
+
{ "mcpServers": { "foga": { "command": "npx", "args": ["-y", "@finchip.ai/foga", "mcp", "--config", "…/cell.config.json"], "env": { "FOGA_OPERATOR_KEY_FILE": "…/keys/operator.key" } } } }
|
|
27
|
+
|
|
28
|
+
Or over HTTP (a client on another machine, or any agent runtime that speaks HTTP):
|
|
29
|
+
MCP http://127.0.0.1:8787/mcp with the header Authorization: Bearer foga.agent.… (~/.foga/quickstart/mcp/http-mcp.json)
|
|
30
|
+
API http://127.0.0.1:8787/api/<capability>, POST a JSON body (reads also GET); the same header for acts.
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## The files
|
|
34
|
+
|
|
35
|
+
Everything is under one directory, `~/.foga/quickstart` by default (`--dir <dir>` for another; a directory that is not a quickstart's — no `quickstart.json` in it — is refused rather than overwritten):
|
|
36
|
+
|
|
37
|
+
| File | What |
|
|
38
|
+
|---|---|
|
|
39
|
+
| `cell.config.json` | the cell's configuration (`foga.cell-config/1`, docs/node.md), its `rpc` filled in so a client needs no `FOGA_RPC_URL`; the API key's hash under `access.keys` |
|
|
40
|
+
| `cell.ops.sqlite` | the cell's operation store |
|
|
41
|
+
| `world.json` | the deployed world's address book |
|
|
42
|
+
| `keys/owner.key`, `keys/operator.key`, `keys/guardian.key` | the three roles' keys, one per 0600 file; `FOGA_OPERATOR_KEY_FILE`, `FOGA_OWNER_KEY_FILE`, `FOGA_GUARDIAN_KEY_FILE` name them for the CLI (`foga cell owner …`, `foga cell guardian …`) |
|
|
43
|
+
| `keys/agent.api-key` | the act-level key (`agent`) for the HTTP entrance, as a file; it is also in the HTTP configuration and the summary |
|
|
44
|
+
| `mcp/claude_desktop_config.json`, `mcp/claude-code.mcp.json`, `mcp/cursor-mcp.json` | the stdio server definition for each client (docs/mcp.md, "Client configuration") |
|
|
45
|
+
| `mcp/http-mcp.json` | the Streamable HTTP definition: the URL and the `Authorization` header |
|
|
46
|
+
| `connect.md` | the printed summary |
|
|
47
|
+
| `quickstart.json` | the run's marker: when, which chain, which cell, which tenders |
|
|
48
|
+
| `anvil.log` | anvil's output |
|
|
49
|
+
|
|
50
|
+
Anvil's money has no value and the keys are written in clear for that reason; nothing here is for a public chain.
|
|
51
|
+
|
|
52
|
+
## Two ways in
|
|
53
|
+
|
|
54
|
+
**stdio.** The client starts `npx -y @finchip.ai/foga mcp --config ~/.foga/quickstart/cell.config.json` itself, with `FOGA_OPERATOR_KEY_FILE` in its environment: the server holds the operator key and acts for the cell (40 tools). Without the key the client gets reads, dry runs and builds (the `_simulate` and `_build` tools) and nothing it says can move the cell. The stdio server is a process of its own beside the served node; both write the same store (SQLite in WAL mode with a busy timeout), and the node's watchers settle and withdraw whatever either of them commits to.
|
|
55
|
+
|
|
56
|
+
**HTTP.** `http://127.0.0.1:8787/mcp` with `Authorization: Bearer <the agent key>` is the same server over Streamable HTTP, for a client that speaks it (Claude Code: `claude mcp add --transport http foga http://127.0.0.1:8787/mcp --header "Authorization: Bearer <key>"`; Cursor and others: `mcp/http-mcp.json`), and `POST http://127.0.0.1:8787/api/<capability>` with the same header is the plain JSON API for any agent runtime — `GET /api/capabilities` lists them, `foga cell schema <capability>` prints one's input and output schemas. Without the header the public reads and builds. `--listen 0.0.0.0:8787` opens the node to other machines (the key is then the only thing between them and the cell's acts; the chain is anvil's, so the stake is a demonstration); `--no-serve` runs no node at all, leaving the stdio server alone to the clients.
|
|
57
|
+
|
|
58
|
+
## What a connected agent does
|
|
59
|
+
|
|
60
|
+
A tender is worked in five steps, the same for every caller: `tender_list` (the open tenders), `tender_inspect {tokenId}` (the terms and the task document — `task.skill`, `task.input`), `tender_precheck {tokenId}` (every gate; `eligible: false` names the gate and is the cell's verdict), `skill_run {skill, input, tokenId}` (the run; `resultHash`, `runReceipt`), `tender_submit {tokenId, resultHash, runReceipt}`. The node's watchers settle the submission once the chain lets them and withdraw credited reward; `tender_settle` and `credit_withdraw` are offered too. An agent that goes through the two tenders sees 0.002 ETH of anvil's money arrive at the cell's account.
|
|
61
|
+
|
|
62
|
+
More tenders: the summary prints the demander's command line with every value filled in (`foga demo demander … --cases a,b --seed <n>`; `--cases c,d,d2,e` mints tenders the cell must refuse, docs/loop-d.md). The owner's and guardian's commands work with the key files: `FOGA_GUARDIAN_KEY_FILE=~/.foga/quickstart/keys/guardian.key foga cell guardian guardian_suspend --config … --reason review --restrictions '["noNewRisk","deriskOnly"]'` suspends the binding; a pre-check then answers `safety`.
|
|
63
|
+
|
|
64
|
+
## Options
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
foga quickstart [--dir <dir>] [--port <n>] [--listen <host:port> | --no-serve] [--name <cell>] [--no-tenders] [--exit]
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`--port` is anvil's (8545; a port already answering JSON-RPC is reported, not reused), `--listen` the node's (`127.0.0.1:8787`), `--name` the cell's (`quick`), `--no-tenders` skips the two tenders, `--exit` stops everything once the summary is printed (the check CI runs). A second quickstart in the same directory replaces the first: the chain ended with its process and the old configuration points at nothing, so the directory is cleared and written again; the client configurations keep the same paths and need no change.
|
|
71
|
+
|
|
72
|
+
## Windows
|
|
73
|
+
|
|
74
|
+
Foundry publishes a Windows build: download the `win32_amd64` zip of a release from https://github.com/foundry-rs/foundry/releases, unzip it, put the folder holding `anvil.exe` on `PATH` (or set `FOGA_FOUNDRY_DIR` to it) and open a new terminal. In PowerShell the package name needs quotes (`@` starts a splatting expression): `npx -y '@finchip.ai/foga' quickstart`. Inside a JSON configuration file nothing is quoted differently; the printed paths use backslashes and are escaped for JSON.
|
|
75
|
+
|
|
76
|
+
## What it is not
|
|
77
|
+
|
|
78
|
+
A quickstart is a sandbox: a chain that ends with the process, keys with no value, a cell with no public endpoint. The cell on Sepolia, served at `foga.finchip.ai`, and the configuration that points a client at it are the OG instance's (3d, docs/og-deployment.md when it lands); `deployments/sepolia.cell.example.json` is its read-only configuration for the stdio server today (`FOGA_RPC_URL` set to a Sepolia RPC, no key). Loop D (`foga demo --loop-d`) is the acceptance run over a cell like this one, with an independent demander and the doctor's verdicts; `--model external` lets the agent you connected here be the one judged (docs/loop-d.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finchip.ai/foga",
|
|
3
|
-
"version": "0.6.0-alpha.
|
|
3
|
+
"version": "0.6.0-alpha.6",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/garyyang-finchip/foga.git"
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"build/profiles.json",
|
|
49
49
|
"build/known-interface-ids.json",
|
|
50
50
|
"vendor/LOCK.json",
|
|
51
|
+
"scripts/prepare.mjs",
|
|
51
52
|
"server.json",
|
|
52
53
|
"README.md",
|
|
53
54
|
"LICENSE",
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
],
|
|
56
57
|
"scripts": {
|
|
57
58
|
"build": "tsc -p tsconfig.json",
|
|
59
|
+
"prepare": "node scripts/prepare.mjs",
|
|
58
60
|
"foga": "tsx src/cli.ts",
|
|
59
61
|
"vendor:sync": "tsx src/cli.ts vendor sync",
|
|
60
62
|
"bundle": "tsx src/cli.ts build",
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// npm's `prepare` hook: compile the CLI when a checkout is installed as a dependency straight from git
|
|
2
|
+
// (`npm install github:garyyang-finchip/foga`, `npx -y github:garyyang-finchip/foga …`), where no dist/ exists yet.
|
|
3
|
+
// It stays out of the way everywhere else: when dist/cli.js is already there, when there is no src/ to compile (the
|
|
4
|
+
// Docker image installs dependencies before it copies the sources), and when TypeScript is not installed (a
|
|
5
|
+
// production install of the published tarball, which carries dist/ already).
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { existsSync } from "node:fs";
|
|
8
|
+
import { createRequire } from "node:module";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
|
+
if (existsSync(join(root, "dist", "cli.js")) || !existsSync(join(root, "src", "cli.ts"))) process.exit(0);
|
|
14
|
+
let tsc;
|
|
15
|
+
try {
|
|
16
|
+
tsc = createRequire(join(root, "package.json")).resolve("typescript/bin/tsc");
|
|
17
|
+
} catch {
|
|
18
|
+
process.exit(0);
|
|
19
|
+
}
|
|
20
|
+
console.error("foga: compiling the CLI (prepare: a checkout installed from git has no dist/ yet)");
|
|
21
|
+
execFileSync(process.execPath, [tsc, "-p", join(root, "tsconfig.json")], { stdio: "inherit", cwd: root });
|
package/server.json
CHANGED
|
@@ -8,13 +8,13 @@
|
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
10
|
"websiteUrl": "https://foga.finchip.ai",
|
|
11
|
-
"version": "0.6.0-alpha.
|
|
11
|
+
"version": "0.6.0-alpha.6",
|
|
12
12
|
"packages": [
|
|
13
13
|
{
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
16
16
|
"identifier": "@finchip.ai/foga",
|
|
17
|
-
"version": "0.6.0-alpha.
|
|
17
|
+
"version": "0.6.0-alpha.6",
|
|
18
18
|
"runtimeHint": "npx",
|
|
19
19
|
"transport": {
|
|
20
20
|
"type": "stdio"
|