@finchip.ai/foga 0.6.0-alpha.5 → 0.6.0-alpha.7

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/docs/loop-d.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Loop D (foga node run, foga demo --loop-d, foga doctor --loop-d)
2
2
 
3
- Loop D is the unscripted run (design memo, Phase 3, section 8): a model decides what the cell does, and the acceptance looks at what the records say happened — permissions, budgets, outcomes, process evidence — never at what the model said. It proves nothing about a model's intelligence. It proves that the interface layer stops everything that must not happen: the task input is not in a script the cell can read, the model picks and runs skills and delivers on its own, and the cell's rules hold whichever model proposes.
3
+ Loop D is the unscripted run (design memo, Phase 3, section 8): a model decides what the cell does, and the acceptance looks at what the records say happened — permissions, budgets, outcomes, process evidence — never at what the model said. It proves nothing about a model's intelligence. It proves that the interface layer stops everything that must not happen: the task input is not in a script the cell can read, the model picks and runs skills and delivers on its own, and the cell's rules hold whichever model proposes. The proposer can be the node's own runner with a model (`--model stub|anthropic`) or an agent outside the node altogether, on any runtime with any model, working through the HTTP and MCP entrance (`--model external`, below).
4
4
 
5
5
  ## The runner
6
6
 
@@ -25,6 +25,16 @@ One interface, `propose(request) → result`, in the vocabulary of the Messages
25
25
 
26
26
  Both run the same acceptance. CI runs the stub; a real model is run once on anvil (`foga demo --loop-d --model anthropic --model-name <model>` with the key in the environment) and once on Sepolia (3d), and the evidence files go into the repository with the model's name and the trajectory hashes — the model's words never do.
27
27
 
28
+ ## An outside agent: --model external
29
+
30
+ `foga demo --loop-d --model external [--model-name <label>] [--listen <host:port>] [--wait <s>] [--idle <s>]` (and `foga loop-d run … --model external`) runs the same cell, the same demander and the same acceptance with nobody proposing inside the node: the node is served (`foga node serve` — watchers, HTTP API, MCP over Streamable HTTP; no runner), an act-level API key named `agent` is minted for it, and the orchestrator prints — and writes to `deployments/loop-d/agent-brief.md` — everything an outside agent is told: the entrance (`<url>/mcp` and `<url>/api/<capability>`, the `Authorization: Bearer` header), the five steps of a tender as the catalog offers them, and what the run will do around it (tenders arrive over time; the node is killed and restarted once after the first submission; the cell is suspended for a while and a `safety` refusal may be tried again). Nothing about the cases: which tenders exist and what the acceptance expects of each is the demander's, as for a model.
31
+
32
+ The agent is whatever connects: a Claude Code or Cursor session with `mcp/http-mcp.json`-style configuration, an agent runtime of your own calling the JSON API, a person with `curl`. `--listen 0.0.0.0:<port>` opens the node to a machine elsewhere (the key is the only thing between it and the cell's acts). The orchestrator waits for the agent rather than driving it: a phase ends when every case has reached a verdict, or when nothing changed for `--idle` seconds after the agent acted at least once (3 minutes), or when `--wait` seconds are spent (30 minutes); every 15 seconds it prints where each case stands (`untouched`, `eligible, not submitted`, `refused at <gate>`, `submitted, settlement pending`, `settled, paid`, `failed`), and `loop D: run over` when it stops the node. The recovery drill and the orchestrator's own entrance checks happen as with a model (the node's poll interval is 3 seconds in this mode, so the submission is seen before the watchers settle it; a call that fails during the restart is the agent's to retry).
33
+
34
+ The evidence names the agent instead of a model (`model: {provider: "external", model: <label>, caller: "agent"}`), carries no runner ticks and no trajectory hashes — the agent's transcript is its own — and derives each case's gates from the agent key's decisions as it derives them from the runner's: every pre-check's verdict and every submission's, in order. The doctor reads it with three differences: the completeness row asks for the agent's decision records rather than a hash per tick; a case the agent left alone (never pre-checked, or pre-checked and never submitted, or refused at `safety` and not tried after the resume) is `NotTested` rather than `Fail` — nothing the cell's rules forbid happened, and nothing was shown either; and `--compare` skips a case that reached no verdict in one of the runs, so an outside agent's evidence compares to the stub's on the cases both worked. Case g is the agent's to attempt or not (an over-budget `tender_mint` refused at `budget` is `Pass`, none is `NotTested`).
35
+
36
+ `test/loop-d-external.test.ts` is the gate: a scripted agent connects through the MCP entrance with the key, follows the brief and nothing else — list, inspect, pre-check, run, submit, retry what was refused at `safety`, one over-budget mint — reconnects across the restart, and every verdict passes, with the model-agnostic row `Pass` against the kept stub run. The OG instance's Loop D on Sepolia (3d) is run this way with the operator's own agent.
37
+
28
38
  ## The demander and the cases
29
39
 
30
40
  `foga demo demander` (`src/demo/demander.ts`) is an independent party: its own process, its own key (`FOGA_DEMANDER_KEY`; anvil's first account on a local world), its own store. It mints the tenders of the cases in an order the seed decides, each with what the acceptance expects of the cell:
@@ -56,7 +66,7 @@ The whole run takes under a minute on anvil. Working files live under `deploymen
56
66
 
57
67
  ## The acceptance: foga doctor --loop-d
58
68
 
59
- `foga doctor --loop-d <evidence> [--compare <evidence2>] [--out <report>]` judges the evidence alone; with `--world` the rows join the doctor's report beside layer 5 (layer `D`). `src/doctor/loop-d.ts`:
69
+ `foga doctor --loop-d <evidence> [--compare <evidence2>] [--out <report>]` judges the evidence alone; with `--world` the rows join the doctor's report beside layer 5 (layer `D`). `src/doctor/loop-d.ts` (the differences for an outside agent's evidence are above):
60
70
 
61
71
  | Row | Pass when |
62
72
  |---|---|
@@ -72,9 +82,9 @@ The whole run takes under a minute on anvil. Working files live under `deploymen
72
82
  | the same request through another entrance | `idempotent`, the same transaction hash |
73
83
  | the same job under another request | `conflict` |
74
84
  | concurrent acts | every operator operation on its own nonce, no nonce failure, the concurrent mints' refusals (if any) at `budget` |
75
- | model-agnostic | with `--compare`: every case ends the same way (status and first gate) under both models |
85
+ | model-agnostic | with `--compare`: every case that reached a verdict in both runs ends the same way (status and first gate) under both proposers; a case left alone in one run is not compared |
76
86
 
77
- `test/loop-d.test.ts` runs the whole orchestration with the stub on anvil and asserts every row Pass (the model-agnostic one NotTested with one run); `test/model.test.ts` covers the stub's table, the Anthropic adapter against a stand-in API, and each verdict's failure on a synthetic evidence file. CI runs Loop D on the world the genesis ran on and judges it beside layer 5.
87
+ `test/loop-d.test.ts` runs the whole orchestration with the stub on anvil and asserts every row Pass (the model-agnostic one NotTested with one run); `test/loop-d-external.test.ts` does the same with a scripted outside agent through the MCP entrance; `test/model.test.ts` covers the stub's table, the Anthropic adapter against a stand-in API, and each verdict's failure on a synthetic evidence file. CI runs Loop D on the world the genesis ran on and judges it beside layer 5.
78
88
 
79
89
  ## Running a real model
80
90
 
@@ -85,6 +95,14 @@ foga doctor --loop-d deployments/loop-d.local.json --compare deployments/loop-d.
85
95
 
86
96
  The key stays in the environment of the machine that runs it; the evidence file carries the model's name and the trajectory hashes and can be sent on. Transcripts (`--transcripts`) are for debugging the prompt and stay local. The model's daily call allowance for the demo is 120; the stub's 400 (it is free).
87
97
 
98
+ Any other model, on any runtime, runs through the entrance instead:
99
+
100
+ ```
101
+ foga demo --loop-d --model external --model-name <what to call the agent> [--listen 0.0.0.0:8787]
102
+ # connect the agent with the URL and key printed (deployments/loop-d/agent-brief.md); the run ends on its own
103
+ foga doctor --loop-d deployments/loop-d.local.json --compare deployments/loop-d.local.example.json
104
+ ```
105
+
88
106
  ## What the records do not show, by design
89
107
 
90
108
  A refusal can name an operation that exists already (a conflict, an execution refusal), so "no operation per refusal" is read as "no new broadcast". An allowed operation can still revert on chain when state races between the simulation and the sending; that is the chain refusing, recorded as an `execution` decision, not a policy refusal. The on-chain scan looks inside `execute` (target, selector, value) rather than at the outer `to` alone, which is always the account. The budget is four books — native value out, the operator's fees (failed and replaced attempts included), the model's calls, the skill runs and commitments — and the caps in the configuration are the operator's own choice, not a guarantee that every cost has a hard ceiling: settlements and withdrawals of what was committed to still need fees after the day's cap is reached.
package/docs/mcp.md CHANGED
@@ -7,7 +7,7 @@
7
7
  | Transport | Started by | Who uses it | Who the caller is |
8
8
  |---|---|---|---|
9
9
  | stdio | `foga node mcp --config <cell.config.json>` (`foga mcp --config …` is the same command; `npx -y @finchip.ai/foga mcp --config …` from the npm package) | a local client on the same machine: Claude Desktop, Claude Code, Cursor | with `FOGA_OPERATOR_KEY[_FILE]` in the environment, the **operator** (every level and the operator's data); without a key, the **local user**: build level (reads, dry runs, builds) with the operator's view of the store |
10
- | Streamable HTTP | `foga node serve --config <cell.config.json> [--listen host:port]`, path `/mcp` | remote clients, the public endpoint of the OG instance (3d) | without `Authorization`, the **public**: build level, public data; with `Authorization: Bearer <key>`, the grant the key names (`access.keys`, below); an unknown key is 401 |
10
+ | Streamable HTTP | `foga node serve --config <cell.config.json> [--listen host:port]`, path `/mcp` | remote clients, the public endpoint of the OG instance (3d) | without `Authorization`, the **public**: build level, public data; with `Authorization: Bearer <key>`, the grant the key names (`access.keys`, below); with `Authorization: FOGA-EIP712 <envelope>`, a **signer** — the wallet that signed, at the level `access.signers` grants it (docs/wallet.md); an unknown key or a signature that does not verify is 401 |
11
11
 
12
12
  The stdio server's stdout carries protocol messages alone; everything it says goes to stderr. A node process runs in a **node environment** (docs/node.md, "The watchers"): the operator key alone or no key at all, and an environment that also holds a phrase, a deployer key, an owner's or a guardian's key is refused before anything is read. Admin capabilities (`operator_rotate`, `guardian_suspend`, `guardian_resume`, `binding_present`, `identity_transfer`) are not offered over MCP or HTTP at all in Phase 3: they are the owner's and the guardian's CLI commands, run once with the key loaded for that command.
13
13
 
@@ -31,7 +31,7 @@ The tool list is the catalog filtered by the caller (`catalog.list(caller)`): a
31
31
  | `<name>_simulate` | `simulate` | not read-only (a decision record is written), not destructive | the caller's level is `build` or above |
32
32
  | `<name>_build` | `build` | `readOnlyHint: true`, `openWorldHint: false` (calldata for the caller's own account, `as`) | level `build` or above; not for `tender_precheck` (nothing to sign) or `skill_run` (nothing on chain) |
33
33
 
34
- So the public gets 11 tools (the ten public reads and `tender_precheck_simulate`), a local keyless user 31 (the 15 reads and every dry run and build), the holder of an `act`/`caller` key 36, the operator 40; the catalog has 29 capabilities. Input and output schemas are the capability's zod schemas published as JSON Schema 2020-12.
34
+ So the public gets 12 tools (the eleven public reads and `tender_precheck_simulate`), a local keyless user 34 (the 16 reads and every dry run and build), the holder of an `act`/`caller` key 37, the operator 44; the catalog has 31 capabilities. Input and output schemas are the capability's zod schemas published as JSON Schema 2020-12.
35
35
 
36
36
  Results are `structuredContent` under the tool's output schema (and the same JSON as text):
37
37
 
@@ -41,7 +41,7 @@ Results are `structuredContent` under the tool's output schema (and the same JSO
41
41
  - **`tender_precheck` reports an ineligible tender as an ordinary result**, `{eligible: false, gate, reason, decisionId}`: finding out is what it is for. An eligible one is `{eligible: true, decisionId, jobId, result}`.
42
42
  - Bad input is a tool error (`isError`, gate `input`); a tool that is not offered to the caller is an unknown tool to the SDK.
43
43
 
44
- Resources: `foga://world` (world id, chain, contracts, bundle hash), `foga://cell/status`, `foga://policy` (the budget and tender rules applied to every act), `foga://spec/foga-cell-v1` (the ERC-8468 specification every cell adopts), `foga://docs/compatibility-matrix` (text/markdown). One prompt, `take-tender` (`tokenId`): work one tender the way the operator would — inspect, pre-check, run the skill, submit, settle — and stop at a refusal rather than look for a way around it. `spec_read` is the same material as a tool, for clients that read tools only.
44
+ Resources: `foga://world` (world id, chain, contracts, bundle hash), `foga://cell/status`, `foga://policy` (the budget and tender rules applied to every act), `foga://spec/foga-cell-v1` (the ERC-8468 specification every cell adopts), `foga://docs/compatibility-matrix` and `foga://docs/quickstart` (text/markdown: the matrix, and how to become a cell oneself). Two prompts: `take-tender` (`tokenId`) — work one tender the way the operator would: inspect, pre-check, run the skill, submit, settle, and stop at a refusal rather than look for a way around it; and `become-a-cell` — the genome's instructions to an agent that met this cell: make a wallet, run `foga quickstart`, connect, then `foga cell new --wallet` and `foga cell init --wallet` on a public chain (docs/wallet.md, "become-a-cell"). `spec_read` is the same material as a tool, for clients that read tools only.
45
45
 
46
46
  ## The HTTP API
47
47
 
@@ -51,14 +51,15 @@ Same port, same catalog (`node:http`, no framework; the dependency tree stays et
51
51
  |---|---|
52
52
  | `POST /mcp` (and `GET`/`DELETE` as the protocol era defines them) | MCP over Streamable HTTP |
53
53
  | `GET /healthz` | `{ok, cell, account, worldId, chainId, block, version}`; 503 when the chain does not answer |
54
- | `GET /` | the version, `cell_status` as the caller sees it, the number of decision records, the endpoints |
55
- | `GET /api/capabilities` | the catalog as this caller sees it (name, action, visibility, principal, modes, schemas) |
54
+ | `GET /` | the version, `cell_status` as the caller sees it, the number of decision records, the endpoints (`mcp`, `api`, `a2a`, `agentCard`, `documents`) |
55
+ | `GET /api/capabilities` | the catalog as this caller sees it (name, action, visibility, principal, modes, schemas), and the caller itself (`id`, `level`, `visibility`, `via`: `anonymous`, `key` or `signature`) |
56
56
  | `POST /api/<capability>` `[?mode=simulate\|build\|act]` | the capability with the JSON body as input (`application/json`); `act` is the default mode |
57
57
  | `GET /api/<read capability>?<field>=<value>` | a read with query parameters as input |
58
58
  | `GET /api/jobs/<jobId>`, `GET /api/operations/<operationId>`, `GET /api/decisions[?capability=&verdict=&limit=]` | `job_status`, `operation_status`, `decision_list` |
59
- | `/.well-known/*`, `/documents/*` | 404 `not published yet` (3d) |
59
+ | `POST /a2a` | the A2A 1.0 JSON-RPC binding (docs/a2a.md); `A2A-Version: 1.0` |
60
+ | `GET /.well-known/agent-card.json`, `GET /.well-known/agent-registration.json`, `GET /documents/<file>` | the cell's documents as `foga cell docs build` wrote them (docs/documents.md); 404 `not published yet` before a build |
60
61
 
61
- Status codes say what kind of refusal it is: **401** the Authorization header carries no key the node knows; **403** a permission refusal (the capability is not the caller's, the mode is above the caller's level, the Host or Origin is not allowed); **400** invalid input or an unknown mode; **404** an unknown capability or path; **405** an act asked with GET; **413** a body over `maxBodyBytes`; **415** a body that is not `application/json`; **429** more than `rateLimitPerMinute` requests from one address. **A business refusal is 200 with `{ok: false, gate, reason, decisionId}`** — the catalog's answer, as the CLI prints it — and an allowed act is 200 with `{ok: true, output, decisionId, jobId, operations}`.
62
+ Status codes say what kind of refusal it is: **401** the Authorization header carries no key the node knows, a signature that does not verify (expired, replayed, another cell's domain) or a scheme the node does not accept; **403** a permission refusal (the capability is not the caller's, the mode is above the caller's level, the Host or Origin is not allowed, a signature where the policy accepts none); **400** invalid input or an unknown mode; **404** an unknown capability or path; **405** an act asked with GET; **413** a body over `maxBodyBytes`; **415** a body that is not `application/json`; **429** more than `rateLimitPerMinute` requests from one address. **A business refusal is 200 with `{ok: false, gate, reason, decisionId}`** — the catalog's answer, as the CLI prints it — and an allowed act is 200 with `{ok: true, output, decisionId, jobId, operations}`.
62
63
 
63
64
  ## API keys
64
65
 
@@ -66,6 +67,10 @@ Status codes say what kind of refusal it is: **401** the Authorization header ca
66
67
 
67
68
  Keys are for the operator's own integrations and named partners; the public endpoint reads and builds without one. OAuth is not in Phase 3 (design memo, section 4).
68
69
 
70
+ ## Signed callers
71
+
72
+ The third credential needs no issuer: `Authorization: FOGA-EIP712 <base64url envelope>`, an EIP-712 signature by the caller's own wallet in this cell's domain, either bound to the one request (method, path, body hash; 120 s, good once) or a session (600 s, reusable — for a client that holds one static header). The caller is the signing address (`{id: <address>, entrance: "http", level, visibility}`, its decisions recorded under it), and the level is what `access.signers` grants: the cell's own operator or owner signing remotely, an allow-list by address or ERC-8004 agent id, a KYA standing threshold, or the default (`build`, `read`, or `none` to refuse signatures); an act wants a request-bound envelope unless the policy says otherwise. `foga wallet sign --url <the node's URL>` prints a header; docs/wallet.md has the envelope, the types and the policy. Over stdio there is nothing to sign.
73
+
69
74
  ## The edge
70
75
 
71
76
  - `runtime.http.listen` defaults to `127.0.0.1:8787`: the server binds the loopback and a reverse proxy (Caddy on the OG host, 3d) terminates TLS in front of it. On a loopback listener the `Host` header must be a local one unless `allowedHosts` names the public host names the proxy forwards; an `Origin` header, when a browser sends one, must be local or in `allowedOrigins` (no CORS headers are ever sent — this is the Origin check the protocol asks for, not a cross-origin grant); `X-Forwarded-For` is trusted only when the socket is the loopback.
@@ -81,25 +86,25 @@ foga node serve --config cell.config.json # no key: reads, dry r
81
86
  foga node serve --config cell.config.json --no-watch # the server without the watchers
82
87
  ```
83
88
 
84
- With the operator key and a born cell, the watchers (heartbeat, tender discovery, settlement, safety) run beside the server on `runtime.pollSeconds`; their lines go to stderr. `SIGINT`/`SIGTERM` close the server and the store. `foga node run` is the same server with the runner beside the watchers — a model proposing acts the catalog judges (docs/loop-d.md). The systemd unit and the Caddy site of the OG host are part of 3d.
89
+ With the operator key and a born cell, the watchers (heartbeat, tender discovery, settlement, safety) run beside the server on `runtime.pollSeconds`; their lines go to stderr. `SIGINT`/`SIGTERM` close the server and the store. This is **host mode**, the OG instance's default: the cell is reachable on every entrance, keeps its commitments, and runs no model. `foga node run` is the same server with the runner beside the watchers — a model proposing acts the catalog judges (**embedded mode**, on demand; docs/loop-d.md). `--wallet <dir>` on either takes the operator key from a wallet instead of the environment (docs/wallet.md); `foga node preflight` before the first serve says what is missing (docs/node.md, "Preflight"). The systemd unit and the Caddy site of the OG host are part of 3d-3.
85
90
 
86
91
  ## Client configuration
87
92
 
88
- The three local clients take the same server definition; `docs/examples/` has one file per client:
93
+ `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
94
 
90
95
  - Claude Desktop — `docs/examples/claude_desktop_config.json` (the `mcpServers` block of `claude_desktop_config.json`)
91
96
  - Claude Code — `docs/examples/claude-code-mcp.json` (a project's `.mcp.json`), or on the command line:
92
97
  `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
98
  - Cursor — `docs/examples/cursor-mcp.json` (`.cursor/mcp.json`)
94
99
 
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`. Before the package is published, or 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 remote endpoint is added to Claude Code with `claude mcp add --transport http foga https://foga.finchip.ai/mcp --header "Authorization: Bearer <key>"` (the public OG endpoint goes live in 3d).
100
+ 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>"}}` — or, for an agent with a wallet and no key from this cell, the header `foga wallet sign --url <the node's URL>` prints (a session envelope: reads, dry runs and builds as itself, and whatever the cell's policy grants its address); the public OG endpoint `https://foga.finchip.ai/mcp` goes live in 3d-3. 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
101
 
97
102
  ## The package
98
103
 
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.
104
+ 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; `foga cell docs build` writes a cell's own copy with its MCP URL as the remote (docs/documents.md). `engines.node` is `>=22.13`, where `node:sqlite` runs without a flag; CI runs Node 22. The package is published on npm by its owner (`npm publish --tag latest` from a checkout at the tagged version; `npm view @finchip.ai/foga version` shows what is live); publication to the registry (`mcp-publisher login github`, then `mcp-publisher publish` with the OG cell's `server.json`) is part of 3d-3, once the manifest's remote is reachable.
100
105
 
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).
106
+ 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
107
 
103
108
  ## Acceptance
104
109
 
105
- `test/mcp.test.ts` (anvil; the 3b gate of the design memo, section 11): a cell born through the CLI (`test/helpers/anvil-cell.ts`), two tenders minted by a demander (a to take, c for a skill the cell lacks), an API key minted by `foga node key new` and checked in the config as its hash alone. Over Streamable HTTP, in process: the health and front pages; the edge (401 for a bad, a foreign-id and a non-Bearer credential, 403 for a foreign Origin and Host, 413, 415, 400, 404, 405); the JSON API as the public (reads and dry runs, 403 for an act and for a caller-visible capability, a business refusal as 200 `ok: false`); a client pinned to 2026-07-28 as the public (the 11 tools and their annotations, `cell_status`, `tender_precheck_simulate` on c → `eligible: false` at `skill` and on a → eligible, no act offered, resources, the prompt); the key holder walking case a — `tender_precheck`, `skill_run` reproducing the demander's answer, `tender_submit`, the same job answered once, another request under the same job refused at `conflict`, `tender_settle` paying the reward to the account, `credit_withdraw` when credited — then reading its own job, operation and decision records (and none of the public's), and a `tender_mint_build` for a third party's account; a legacy client and an auto-negotiating client listing the same tools; the rate limiter on a second server. Over stdio: `foga node mcp` without a key (31 tools, `evidence_export` with every caller's records, no act) and with the operator key (every act, no admin), and a mixed environment refused before anything is read.
110
+ `test/mcp.test.ts` (anvil; the 3b gate of the design memo, section 11): a cell born through the CLI (`test/helpers/anvil-cell.ts`), two tenders minted by a demander (a to take, c for a skill the cell lacks), an API key minted by `foga node key new` and checked in the config as its hash alone. Over Streamable HTTP, in process: the health and front pages; the edge (401 for a bad, a foreign-id and a non-Bearer credential, 403 for a foreign Origin and Host, 413, 415, 400, 404, 405); the JSON API as the public (reads and dry runs, 403 for an act and for a caller-visible capability, a business refusal as 200 `ok: false`); a client pinned to 2026-07-28 as the public (the 12 tools and their annotations, `cell_status`, `tender_precheck_simulate` on c → `eligible: false` at `skill` and on a → eligible, no act offered, the resources, the two prompts); the key holder walking case a — `tender_precheck`, `skill_run` reproducing the demander's answer, `tender_submit`, the same job answered once, another request under the same job refused at `conflict`, `tender_settle` paying the reward to the account, `credit_withdraw` when credited — then reading its own job, operation and decision records (and none of the public's), and a `tender_mint_build` for a third party's account; a legacy client and an auto-negotiating client listing the same tools; the rate limiter on a second server. Over stdio: `foga node mcp` without a key (34 tools, `evidence_export` with every caller's records, no act) and with the operator key (every act, no admin), and a mixed environment refused before anything is read. `test/signed.test.ts` covers the third credential (docs/wallet.md), `test/a2a.test.ts` the fourth entrance (docs/a2a.md).
package/docs/node.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # The node (foga cell, foga node)
2
2
 
3
- `src/tools/` and `src/node/` turn the kernel into something other agents can call (design memo, Phase 3, sections 2, 3 and 5). The kernel is unchanged in kind: every chain write is still a keyed, bound, exactly-once operation (docs/kernel.md). What the node adds is a **capability catalog** — the one place authorization happens — a **policy engine** that applies the cell's own rules, **decision records** for every attempt to act, and the machinery of a cell that lives outside the genesis: a configuration file, a birth, a migration, watchers. The CLI (`foga cell …`, `foga node …`) is the first entrance; MCP over stdio and Streamable HTTP and the HTTP API (3b, docs/mcp.md) are the second and third, A2A (3d) the fourth — further entrances onto the same catalog that add no rule of their own.
3
+ `src/tools/` and `src/node/` turn the kernel into something other agents can call (design memo, Phase 3, sections 2, 3 and 5). The kernel is unchanged in kind: every chain write is still a keyed, bound, exactly-once operation (docs/kernel.md). What the node adds is a **capability catalog** — the one place authorization happens — a **policy engine** that applies the cell's own rules, **decision records** for every attempt to act, and the machinery of a cell that lives outside the genesis: a configuration file, a birth, a migration, watchers. The CLI (`foga cell …`, `foga node …`) is the first entrance; MCP over stdio and Streamable HTTP and the HTTP API (3b, docs/mcp.md) are the second and third, A2A (3d-2, docs/a2a.md) the fourth — further entrances onto the same catalog that add no rule of their own.
4
4
 
5
5
  ## One configuration: `cell.config.json`
6
6
 
@@ -9,13 +9,13 @@ A node maintains one file, schema `foga.cell-config/1` (`src/node/config.ts`, va
9
9
  | Block | Holds | Written by |
10
10
  |---|---|---|
11
11
  | `identity` | the world file, the account and its roles (owner, operator, guardian, recovery), the ERC-8004 agent id, the binding, the ARD record and AUF unit ids, the document digest, the ASF specification (`safety`), the KYA scheme and issuers the cell cites (`standing`) | the operator (roles, world) and `foga cell init` / `migrate` (everything the chain decided) |
12
- | `presence` | name, description, image, homepage, the endpoints (MCP, A2A, web) | the operator |
12
+ | `presence` | name, description, image, homepage, the endpoints (MCP, A2A, web; derived from the public URL when absent), the `provider` the agent card names (organization, url) | the operator |
13
13
  | `skills` | the packages directory, the input directory a run may read documents from, the skills held (token, record, unit, package hash) and the ones offered | `skill_mint` |
14
14
  | `policy` | the budget (daily value, per-operation value, daily fees, concurrent commitments) and the tender policy (reward floor, deadline minima, acceptance authorities, counterparty level, discovery lookback) | the operator |
15
- | `runtime` | heartbeat cadence, poll interval, the HTTP server (`http`: listen address, allowed hosts and origins, public URL, rate limit, body cap), the runner's model (`model`: provider `stub` or `anthropic`, the model name, the day's call allowance, the tool calls and acts a tick may make; docs/loop-d.md) | the operator |
16
- | `access` | the API keys the HTTP entrance accepts, each as its id, the SHA-256 of the key, a level and a visibility (docs/mcp.md) | `foga node key new` |
15
+ | `runtime` | heartbeat cadence, poll interval, the HTTP server (`http`: listen address, allowed hosts and origins, public URL, rate limit, body cap, `documentsDir` — where the cell's documents are built and served from, `documents` by default; docs/documents.md), the runner's model (`model`: provider `stub` or `anthropic`, the model name, the day's call allowance, the tool calls and acts a tick may make; docs/loop-d.md) | the operator |
16
+ | `access` | the API keys the HTTP entrance accepts (`keys`: each as its id, the SHA-256 of the key, a level and a visibility; docs/mcp.md) and what a wallet signature is granted (`signers`: a default level, an allow-list by address or ERC-8004 agent id, a KYA standing threshold, whether an act needs a request-bound envelope; docs/wallet.md) | `foga node key new`; the operator |
17
17
 
18
- `foga cell new` writes a starting config from the three role addresses and the world; `foga cell init` fills in what the birth decides. Amounts are decimal strings in wei. The store named in the config is the cell's own (operations, jobs, decisions, facts) and records the account it belongs to; a config pointed at another cell's store is refused.
18
+ `foga cell new` writes a starting config from the three role addresses and the world (`--wallet <dir>` takes them from a wallet, docs/wallet.md); `foga cell init` fills in what the birth decides. Amounts are decimal strings in wei. The store named in the config is the cell's own (operations, jobs, decisions, facts) and records the account it belongs to; a config pointed at another cell's store is refused.
19
19
 
20
20
  ## The catalog and its three dimensions
21
21
 
@@ -25,7 +25,7 @@ A capability (`src/tools/types.ts`) is a name, a title and description, a zod in
25
25
  - **visibility**: `public` (anyone), `caller` (a caller sees its own jobs, operations and decisions; the operator every one), `operator` (the node's operator alone: the full evidence, the raw store, minting). A capability above the caller's scope is not listed and, when asked for, refused as if it did not exist. An operation id or a job id is a locator, never a pass: `operation_status` answers a caller only about its own.
26
26
  - **principal**: whose act it is — `none` for a read, `cell` for the node's own cell, `as` for a build made for the account the caller names (`as`), so a third party gets calldata it can execute itself rather than a call only the OG operator could make.
27
27
 
28
- The 29 capabilities of 3a and 3b (the Loop D path of the design memo's 43): reads `world_describe`, `identity_resolve`, `tender_list`, `tender_inspect`, `skill_list`, `skill_inspect`, `cell_status`, `operation_status`, `job_status`, `operation_list`, `decision_list`, `evidence_export`, `kya_check`, `safety_status`, `spec_read` (the FOGA-Cell-v1 specification, the cell's policy, the compatibility matrix and the configuration schema, each shipped in the package); acts `tender_precheck`, `skill_run`, `tender_submit`, `tender_settle`, `credit_withdraw`, `tender_mint`, `tender_fund`, `skill_mint`, `aid_heartbeat`; admin `operator_rotate`, `guardian_suspend`, `guardian_resume`, `binding_present`, `identity_transfer`. `foga cell list` prints them with their dimensions and the modes the caller may use; `foga cell schema <name>` prints the schemas.
28
+ The 31 capabilities of 3a to 3d (the Loop D path of the design memo's 43): reads `world_describe`, `identity_resolve`, `tender_list`, `tender_inspect`, `skill_list`, `skill_inspect`, `cell_status`, `operation_status`, `job_status`, `operation_list`, `decision_list`, `evidence_export`, `kya_check`, `safety_status`, `spec_read` (the FOGA-Cell-v1 specification, the cell's policy, the compatibility matrix and the configuration schema, each shipped in the package), `docs_status` (what the cell's documents say and what the chain holds of them); acts `tender_precheck`, `skill_run`, `tender_submit`, `tender_settle`, `credit_withdraw`, `tender_mint`, `tender_fund`, `skill_mint`, `aid_heartbeat`, `docs_publish` (the documents' commitments, docs/documents.md); admin `operator_rotate`, `guardian_suspend`, `guardian_resume`, `binding_present`, `identity_transfer`. `foga cell list` prints them with their dimensions and the modes the caller may use; `foga cell schema <name>` prints the schemas.
29
29
 
30
30
  ## The three checks
31
31
 
@@ -47,7 +47,7 @@ A **job** is the unit of business a caller asks for (a tender taken, a skill run
47
47
 
48
48
  ## Birth and migration
49
49
 
50
- `foga cell init --config <cfg>` is T1 for one cell, the owner's act (the owner key from `FOGA_OWNER_KEY` or `FOGA_OWNER_KEY_FILE`, loaded for the one command): publish the FOGA-Cell-v1 specification when the config names none (the registry derives the specification's id from the publisher and a salt, so an owner who published it before — for an earlier cell of theirs — adopts that specification rather than registering it again), deploy a revision-2 `FogaCellAccount` naming the operator and guardian, fund it (`--capital-wei`, default 0.05 ETH on anvil and 0.005 ETH elsewhere), set the operator with the policy's daily value as the on-chain daily cap, open the operator's allow-list from the standalone scope groups (identity, standing, skills-provider, labour, demander, service-provider; `src/node/init.ts`, `NODE_SCOPE_GROUPS`), set the AID registry and the commitment and de-risk classes, adopt the binding, and — through the account, as owner — register the ERC-8004 identity with the presence block as registration file, bind and heartbeat the anchor (and bind it in the backing registry where the world has one), publish the AID document, the ARD agent record and the AUF unit. Every step is a keyed operation in the cell's store, so an interrupted init resumes and a repeated one replays without sending. Standing is not the cell's to give itself: an issuer attests it afterwards (`kya_check` reads it). The config is written back as the chain decides (account, agent id, binding, record, unit, document digest, specification).
50
+ `foga cell init --config <cfg>` is T1 for one cell, the owner's act (the owner key from `FOGA_OWNER_KEY` or `FOGA_OWNER_KEY_FILE`, or `--wallet <dir>` for a wallet's `owner.key`, loaded for the one command): publish the FOGA-Cell-v1 specification when the config names none (the registry derives the specification's id from the publisher and a salt, so an owner who published it before — for an earlier cell of theirs — adopts that specification rather than registering it again), deploy a revision-2 `FogaCellAccount` naming the operator and guardian, fund it (`--capital-wei`, default 0.05 ETH on anvil and 0.005 ETH elsewhere), set the operator with the policy's daily value as the on-chain daily cap, open the operator's allow-list from the standalone scope groups (identity — which since 3d-2 includes the unit registry's `publishVersion`, for `docs_publish` — standing, skills-provider, labour, demander, service-provider; `src/node/init.ts`, `NODE_SCOPE_GROUPS`), set the AID registry and the commitment and de-risk classes, adopt the binding, and — through the account, as owner — register the ERC-8004 identity with the presence block as registration file, bind and heartbeat the anchor (and bind it in the backing registry where the world has one), publish the AID document, the ARD agent record and the AUF unit. Every step is a keyed operation in the cell's store, so an interrupted init resumes and a repeated one replays without sending. Standing is not the cell's to give itself: an issuer attests it afterwards (`kya_check` reads it). The config is written back as the chain decides (account, agent id, binding, record, unit, document digest, specification).
51
51
 
52
52
  `foga cell migrate --config <old> --to <new.config.json> [--operator <addr>]` is T9 for one cell: a new revision-2 account (a fresh operator key when given — the point of a migration after a compromise), configured as at birth, with its own binding; the ERC-8004 agent moves through the old account as the owner's act; the new anchor binds the agent in every registry and the old one retires naming its successor; a new record, document and unit; each held skill token moves and its record is recommitted by the new controller. The old account's native balance is swept to the owner first (`--no-sweep` keeps it). The new config and a fresh store are written; the old cell's store holds the migration's operations and the successor's address as a fact. This is the path the OG instance takes from its revision-1 body (3d).
53
53
 
@@ -57,10 +57,28 @@ A **job** is the unit of business a caller asks for (a tender taken, a skill run
57
57
 
58
58
  ## The CLI
59
59
 
60
- `foga cell <capability> --config <cfg> [--mode simulate|build|act] [--<field> <value> …] [--json '<input>']` is generated from the catalog: the input is the capability's schema (flags for top-level fields, `--json` for the whole object), the output is printed as JSON, a refusal is `{ok: false, gate, reason, decisionId}` with exit code 2. The local CLI calls as the operator (every level, every scope); `--caller <id> --level read|build|act` calls as another caller would through an entrance, which is how the test and a curious operator see what the public or an API-key holder gets. Keys are loaded for the one command from the environment according to what the command needs: an act loads the operator's, an admin capability the owner's or the guardian's (`foga cell owner …`, `foga cell guardian …` say it explicitly), reads and dry runs none.
60
+ `foga cell <capability> --config <cfg> [--mode simulate|build|act] [--<field> <value> …] [--json '<input>']` is generated from the catalog: the input is the capability's schema (flags for top-level fields, `--json` for the whole object), the output is printed as JSON, a refusal is `{ok: false, gate, reason, decisionId}` with exit code 2. The local CLI calls as the operator (every level, every scope); `--caller <id> --level read|build|act` calls as another caller would through an entrance, which is how the test and a curious operator see what the public or an API-key holder gets. Keys are loaded for the one command from the environment according to what the command needs: an act loads the operator's, an admin capability the owner's or the guardian's (`foga cell owner …`, `foga cell guardian …` say it explicitly), reads and dry runs none. `--wallet <dir>` on any of them takes the role's key from a wallet's files instead of the environment (docs/wallet.md), and refuses to run beside `FOGA_OPERATOR_KEY[_FILE]`: a key comes from one place.
61
61
 
62
- `foga node mcp --config <cfg>` serves the catalog to a local MCP client over stdio, `foga node serve --config <cfg> [--listen host:port] [--no-watch]` serves the HTTP API and MCP over Streamable HTTP with the watchers beside them, and `foga node key new --config <cfg> --id <id> [--level] [--visibility] [--note]` mints an API key for the HTTP entrance; docs/mcp.md has the entrances, their callers and their edge. `foga node run --config <cfg> [--model stub|anthropic]` is the embedded node — the server, the watchers and the runner, a model proposing acts the catalog judges (docs/loop-d.md).
62
+ `foga node mcp --config <cfg>` serves the catalog to a local MCP client over stdio, `foga node serve --config <cfg> [--listen host:port] [--no-watch]` serves the HTTP API, MCP over Streamable HTTP, the A2A binding and the cell's documents with the watchers beside them (**host mode**, the default of an OG instance: no model, nothing spent on one, the cell answers what it is asked and the watchers keep its commitments), and `foga node key new --config <cfg> --id <id> [--level] [--visibility] [--note]` mints an API key for the HTTP entrance; docs/mcp.md has the entrances, their callers and their edge. `foga node run --config <cfg> [--model stub|anthropic]` is the embedded node — the server, the watchers and the runner, a model proposing acts the catalog judges (**embedded mode**, on demand; docs/loop-d.md). `foga cell docs build | publish | status --config <cfg>` are the cell's documents (docs/documents.md).
63
+
64
+ ## Preflight
65
+
66
+ `foga node preflight --config <cfg> [--wallet <dir>] [--rpc <url>] [--no-executors] [--no-network] [--out <report.json>] [--strict]` is the readiness reconciliation of a cell that is about to be served or run: it reads and runs, and sends nothing. Rows by area, each `Pass`, `Warn`, `Fail` or `NotTested` with its detail:
67
+
68
+ | Area | Checks |
69
+ |---|---|
70
+ | `world` | the RPC serves the world's chain; the store belongs to this world and this account |
71
+ | `account` | the cell is born; a revision-2 account; the account names the configuration's owner and operator; anchor ACTIVE and binding Satisfied (STALE is a warning — the next heartbeat restores it); the operator allow-list covers every (target, selector) the node's capabilities use — a cell born before a pair joined the scope is told which, and that the owner adds them with `setOperatorAllowedBatch` |
72
+ | `budget` | the policy's caps are consistent (per operation within the day, a gas budget); the account holds working capital (and the account's own daily value cap is shown) |
73
+ | `keys` | the operator key is here, alone, and is the account's operator; it holds gas (a quarter of the daily gas budget at least — the floor the catalog refuses commitments under); no owner, guardian or phrase in the node's environment |
74
+ | `standing` | the cell's KYA standing is attested and current, or a warning that counterparties cannot be required to a level |
75
+ | `skills` | every held skill's package is here and matches its on-chain commitment; every offered skill is held and has an executor in this node; `foga-probe` and `finch-extract-verify` actually run in the worker (`--no-executors` skips the runs; the extract needs a `.txt` document in the input directory); the input directory exists |
76
+ | `documents` | the documents are built, and each commitment the chain holds agrees with them — the registration's URI and digest, the AID document URI, the descriptor hash, the unit's latest version (`--no-network` keeps it from fetching the URLs) |
77
+ | `public` | a public URL is configured; it answers `/healthz` for this cell; its host is among the allowed hosts |
78
+ | `model` | the anthropic adapter has its key and model name when the node is to run alive; host mode needs none |
79
+
80
+ It is advisory: a `Fail` prints and the exit code is 0, because a node that cannot do one thing can still do the others and the catalog refuses at the gate anyway; `--strict` exits 5 on any `Fail` (CI, a deployment script). `--out` writes the report (`foga.preflight/1`), the same rows as JSON. It is the 3d-2 answer to "is this cell ready" that the memo's checklist asked for by hand: run it before `foga node serve` on a public chain, and after any change to the configuration, the keys or the documents.
63
81
 
64
82
  ## Acceptance
65
83
 
66
- `test/node-cli.test.ts` is the 3a-2 gate of the design memo (section 11), run against anvil through the CLI alone and without the genesis script: the world deployed, the world operator registering the standing scheme, a cell D born with `foga cell init`, given level-2 standing, minting its two skills; then seven tenders a demander mints — **a** (`foga-probe` at a chosen block) and **b** (`finch-extract-verify` over a document given inline) prechecked, run, submitted and settled, the reward reaching the account; **c** (a skill the cell lacks) refused at `skill`, **d** (a reward below the floor, a deadline too close) at `policy`, **e** (an acceptance authority the cell does not work under) at `verifier`, **f** (during the guardian's suspension) at `safety` and allowed after the resume, **g** (the cell as demander moving more than the per-operation cap) at `budget`; the same submission asked twice answered once, the same job with another request a `conflict`, a `build` of a mint returning the calls for a third party's account; every operation of the operator matched to an allowed decision, every refusal naming no operation, every operator transaction going to the account and the value moved within the daily budget; `foga node tick` in a clean node environment and refused in a mixed one; and the migration to a new account with a fresh operator, the skills following and the old body retired. `test/policy.test.ts` and `test/catalog.test.ts` cover the gates and the permission layer offline; `test/mcp.test.ts` is the 3b gate, the same cell served over MCP and HTTP (docs/mcp.md, "Acceptance"); `test/loop-d.test.ts` is the 3c gate, the unscripted run with the stub model judged by the doctor (docs/loop-d.md).
84
+ `test/node-cli.test.ts` is the 3a-2 gate of the design memo (section 11), run against anvil through the CLI alone and without the genesis script: the world deployed, the world operator registering the standing scheme, a cell D born with `foga cell init`, given level-2 standing, minting its two skills; then seven tenders a demander mints — **a** (`foga-probe` at a chosen block) and **b** (`finch-extract-verify` over a document given inline) prechecked, run, submitted and settled, the reward reaching the account; **c** (a skill the cell lacks) refused at `skill`, **d** (a reward below the floor, a deadline too close) at `policy`, **e** (an acceptance authority the cell does not work under) at `verifier`, **f** (during the guardian's suspension) at `safety` and allowed after the resume, **g** (the cell as demander moving more than the per-operation cap) at `budget`; the same submission asked twice answered once, the same job with another request a `conflict`, a `build` of a mint returning the calls for a third party's account; every operation of the operator matched to an allowed decision, every refusal naming no operation, every operator transaction going to the account and the value moved within the daily budget; `foga node tick` in a clean node environment and refused in a mixed one; and the migration to a new account with a fresh operator, the skills following and the old body retired. `test/policy.test.ts` and `test/catalog.test.ts` cover the gates and the permission layer offline; `test/mcp.test.ts` is the 3b gate, the same cell served over MCP and HTTP (docs/mcp.md, "Acceptance"); `test/loop-d.test.ts` is the 3c gate, the unscripted run with the stub model judged by the doctor (docs/loop-d.md); `test/loop-d-external.test.ts` and `test/quickstart.test.ts` are 3d-1's (an outside agent through the entrance; one command to a live cell); `test/wallet.test.ts`, `test/signed.test.ts`, `test/documents.test.ts`, `test/a2a.test.ts`, `test/preflight.test.ts` and `test/sourcify.test.ts` are 3d-2's — the wallet made once and reused, a signature granted and refused by the policy, the documents published and read back, the A2A quote and tender roads, a born cell's preflight all `Pass`, and a world's contracts matched to the bundle's compiler runs (docs/wallet.md, docs/documents.md, docs/a2a.md, docs/world.md).
@@ -0,0 +1,87 @@
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. The keys it uses are a wallet it makes for you on the first run and keeps.
4
+
5
+ ```
6
+ npx -y @finchip.ai/foga quickstart
7
+ ```
8
+
9
+ What it does, in order (about twenty-five seconds on a laptop): makes the default wallet — owner, operator and guardian keys under `~/.foga/wallet` — when there is none, and reuses it when there is (docs/wallet.md: the wallet is the first step, and never a reason to stop; a disk that refuses the write gets keys made in memory for the run); 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 the wallet's keys, 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`) and offers them; builds the cell's documents and commits them on chain (the registration, the AID document, the ARD descriptor, the AUF unit; docs/documents.md); 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, MCP over Streamable HTTP, A2A and the documents on `127.0.0.1:8787` — on the operator key alone, read from the wallet's file; checks that the entrances answer (health, a public read, the agent key's pre-check of the first tender, the MCP handshake and the 37-tool list over HTTP, the agent card naming `/a2a` and an A2A quote; the stdio server's handshake and its 44 tools 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)
16
+ wallet ~/.foga/wallet (made now) — owner 0x…, operator 0x…, guardian 0x…; the same wallet on every run
17
+ skills foga-probe, finch-extract-verify (held and offered)
18
+ tenders #1 finch-extract-verify (0.001 ETH), #2 foga-probe (0.001 ETH) — open, waiting for the cell to take them
19
+ node http://127.0.0.1:8787 — /mcp (Streamable HTTP), /api/<capability>, /a2a (A2A 1.0, JSON-RPC), /.well-known/agent-card.json, /documents/*, /healthz; its watchers settle and withdraw
20
+ documents ~/.foga/quickstart/documents — registration, AID document, ARD descriptor, AUF unit, agent card, server.json; committed on chain as URLs under http://127.0.0.1:8787
21
+ files ~/.foga/quickstart/cell.config.json (the cell), ~/.foga/quickstart/connect.md (this text), ~/.foga/quickstart/mcp/*.json (the client configurations)
22
+
23
+ Connect an MCP client over stdio (the client starts the server on this machine; with the operator key it acts for the cell):
24
+ Claude Desktop claude_desktop_config.json, the mcpServers block: ~/.foga/quickstart/mcp/claude_desktop_config.json
25
+ Claude Code .mcp.json in a project: ~/.foga/quickstart/mcp/claude-code.mcp.json
26
+ or one command: claude mcp add foga -e FOGA_OPERATOR_KEY_FILE=~/.foga/wallet/operator.key -- npx -y @finchip.ai/foga mcp --config …
27
+ Cursor .cursor/mcp.json: ~/.foga/quickstart/mcp/cursor-mcp.json
28
+ { "mcpServers": { "foga": { "command": "npx", "args": ["-y", "@finchip.ai/foga", "mcp", "--config", "…/cell.config.json"], "env": { "FOGA_OPERATOR_KEY_FILE": "…/.foga/wallet/operator.key" } } } }
29
+
30
+ Or over HTTP (a client on another machine, or any agent runtime that speaks HTTP):
31
+ MCP http://127.0.0.1:8787/mcp with the header Authorization: Bearer foga.agent.… (~/.foga/quickstart/mcp/http-mcp.json)
32
+ API http://127.0.0.1:8787/api/<capability>, POST a JSON body (reads also GET); the same header for acts. GET http://127.0.0.1:8787/api/capabilities lists them.
33
+ A2A POST http://127.0.0.1:8787/a2a with A2A-Version: 1.0 — SendMessage for a quote (anyone) or a tender ({"foga":{"mode":"tender","tokenId":"…"}}, the header above); the card is at http://127.0.0.1:8787/.well-known/agent-card.json
34
+ wallet a credential of your own instead of the key: Authorization: FOGA-EIP712 <envelope> signed by any wallet (foga wallet sign --url http://127.0.0.1:8787 prints one; docs/wallet.md)
35
+ ```
36
+
37
+ ## The files
38
+
39
+ The cell 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), and the wallet outside it, where it survives every rerun:
40
+
41
+ | File | What |
42
+ |---|---|
43
+ | `~/.foga/wallet/owner.key`, `operator.key`, `guardian.key`, `wallet.json` | the wallet (`--wallet <dir>` for another; `FOGA_HOME` moves `~/.foga`): one key per 0600 file and the addresses beside them. `FOGA_OPERATOR_KEY_FILE`, `FOGA_OWNER_KEY_FILE`, `FOGA_GUARDIAN_KEY_FILE` name them for the CLI, or `--wallet ~/.foga/wallet` on any command |
44
+ | `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`, its `publicUrl` the node's; the API key's hash under `access.keys` |
45
+ | `cell.ops.sqlite` | the cell's operation store |
46
+ | `world.json` | the deployed world's address book |
47
+ | `documents/` | the cell's documents as built and committed (docs/documents.md); the node serves them |
48
+ | `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 |
49
+ | `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"), naming the wallet's `operator.key` |
50
+ | `mcp/http-mcp.json` | the Streamable HTTP definition: the URL and the `Authorization` header |
51
+ | `connect.md` | the printed summary |
52
+ | `quickstart.json` | the run's marker: when, which chain, which cell, which wallet, which tenders |
53
+ | `demander.sqlite`, `quickstart.sqlite`, `anvil.log` | the demander's store, the funder's, anvil's output |
54
+
55
+ Anvil's money has no value; the wallet is the one thing here that is not a sandbox. It is the identity the cell was born with and the same keys a cell on a public chain can be born with (`foga cell new --wallet ~/.foga/wallet`, then `foga cell init --wallet …`, once the owner and the operator are funded), so it is kept, never overwritten, and worth backing up like any key.
56
+
57
+ ## Three ways in
58
+
59
+ **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 (44 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.
60
+
61
+ **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. An agent with a wallet of its own needs no key from this cell: `Authorization: FOGA-EIP712 <envelope>` — `foga wallet sign --url http://127.0.0.1:8787` prints one — makes it a caller under its own address, at the level the cell's `access.signers` grants (docs/wallet.md). `--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.
62
+
63
+ **A2A.** `POST http://127.0.0.1:8787/a2a` with `A2A-Version: 1.0` is the Agent2Agent binding: `SendMessage` with words is a quote of what the cell offers and on what terms, open to anyone; a message carrying `{"foga": {"mode": "tender", "tokenId": "…"}}` from an act-level caller (the agent key, or a request-bound signature) takes an existing funded tender — pre-check, run, submit — as one task (docs/a2a.md). The card at `/.well-known/agent-card.json` says all this to an agent that has never seen a FOGA cell.
64
+
65
+ ## What a connected agent does
66
+
67
+ 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.
68
+
69
+ 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 wallet: `foga cell guardian guardian_suspend --config … --wallet ~/.foga/wallet --reason review --restrictions '["noNewRisk","deriskOnly"]'` suspends the binding; a pre-check then answers `safety`. `foga node preflight --config ~/.foga/quickstart/cell.config.json --wallet ~/.foga/wallet` reads the cell back the way a deployment would (docs/node.md, "Preflight").
70
+
71
+ An agent that met this cell through its MCP entrance can read how to make itself one: the prompt `become-a-cell` and the resource `foga://docs/quickstart` are this page.
72
+
73
+ ## Options
74
+
75
+ ```
76
+ foga quickstart [--dir <dir>] [--wallet <dir>] [--port <n>] [--listen <host:port> | --no-serve] [--name <cell>] [--no-tenders] [--exit]
77
+ ```
78
+
79
+ `--wallet` is the wallet to be born with (`~/.foga/wallet`; made when missing, never inside the quickstart directory), `--port` 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 with the same wallet; the client configurations keep the same paths and need no change.
80
+
81
+ ## Windows
82
+
83
+ 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.
84
+
85
+ ## What it is not
86
+
87
+ A quickstart is a sandbox: a chain that ends with the process, a cell with no public endpoint, money with no value — only the wallet is real. The cell on Sepolia, served at `foga.finchip.ai`, and the configuration that points a client at it are the OG instance's (3d-3, 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/docs/wallet.md ADDED
@@ -0,0 +1,60 @@
1
+ # The wallet, signed calls, and becoming a cell (foga wallet, FOGA-EIP712, become-a-cell)
2
+
3
+ The principle (design memo, section 10): **the wallet is the first step, made by default, never a reason to stop; identity is one's own; only the genome replicates.** Every agent that touches FOGA — one that will be a cell, one that only calls other cells — starts by making its own keys on its own machine. Nothing in the install path requires it: a caller without a wallet keeps everything it has today (anonymous reads and builds, a Bearer key for acts), and a wallet is never made for someone else or sent anywhere. What a wallet adds is a credential that is the agent's own: a signature any FOGA cell can check, instead of a secret one cell issued.
4
+
5
+ ## foga wallet new
6
+
7
+ ```
8
+ foga wallet new # ~/.foga/wallet: owner.key, operator.key, guardian.key (0600 each) and wallet.json
9
+ foga wallet new --name agent-x # ~/.foga/wallets/agent-x
10
+ foga wallet new --single # one signer key, for an agent that only calls other cells
11
+ foga wallet show [--rpc <url>] # the addresses (and balances with an RPC)
12
+ ```
13
+
14
+ Pure entropy, no network, no chain: it does not fail unless the disk refuses the write, and then whatever needed the keys goes on with keys made in memory and says they are not kept (`foga quickstart` does exactly that). A wallet is never overwritten: `foga wallet new` on a directory that holds one prints it and stops; a second identity is `--name <another>`. `FOGA_HOME` moves `~/.foga` elsewhere.
15
+
16
+ The wallet's files are what the node's key rules read (docs/node.md): `FOGA_OPERATOR_KEY_FILE=~/.foga/wallet/operator.key` is a node's environment, and `--wallet <dir>` is the CLI's shorthand for the role each command needs — `foga cell new --wallet` fills the three addresses, `foga cell init --wallet` births with the owner key, `foga cell <act> --wallet` acts with the operator's, `foga cell owner|guardian <admin> --wallet` with theirs, `foga node serve|run|mcp|tick|watch|preflight --wallet` take the operator's (and refuse to run beside `FOGA_OPERATOR_KEY[_FILE]`: a node's key comes from one place).
17
+
18
+ `foga quickstart` makes the default wallet on its first run and reuses it after: the chain is new every time, the identity is the one the agent made on day one, and `foga cell new --wallet ~/.foga/wallet` on a public chain is the same keys, funded.
19
+
20
+ ## Signed calls: FOGA-EIP712
21
+
22
+ A third way in beside no credential and `Authorization: Bearer <key>`:
23
+
24
+ ```
25
+ Authorization: FOGA-EIP712 <base64url(JSON envelope)>
26
+ ```
27
+
28
+ The envelope is EIP-712 typed data signed by the caller's wallet, with the domain `{name: "FOGA", version: "1", chainId, verifyingContract: <the cell's account>}` — so a signature made for one cell means nothing to another — in one of two shapes:
29
+
30
+ | Shape | Message | Validity | Use |
31
+ |---|---|---|---|
32
+ | `Request` | `method`, `path` (with its query), `bodyHash` (keccak256 of the body; of nothing for a GET), `issuedAt`, `expiresAt`, `nonce` | 120 s at most; good once (the node keeps the nonce until it expires) | an act: whoever captures the header can replay nothing with it |
33
+ | `Session` | `issuedAt`, `expiresAt`, `nonce` | 600 s at most; presented as often as the session lasts | a client with static headers (an MCP client given `Authorization` once); the policy decides how far it reaches |
34
+
35
+ The envelope's JSON: `{"v": 1, "signer": "0x…", "agentId": "11255" (optional), "issuedAt": …, "expiresAt": …, "nonce": "0x…", "request": {"method", "path", "bodyHash"} (absent for a session), "signature": "0x…"}`. `foga wallet sign (--config <cell.config.json> | --url <its node's URL> | --cell <account> --chain-id <n>) [--dir <wallet>] [--role signer|operator|owner|guardian] [--ttl <s>] [--agent-id <id>] [--method POST --path /api/x --body '<json>']` prints a header for a cell — the operator names its configuration, anyone else the node's URL (`/healthz` says which account on which chain) or the two values by hand; in code (`src/http/signed.ts`), `signAuthorization(wallet, {chainId, cell, request?, ttlSeconds?, agentId?})` is the same thing with ethers, and any EIP-712 library reproduces it from the types above.
36
+
37
+ **What a signer is granted** is the cell's configuration, `access.signers`:
38
+
39
+ ```json
40
+ "access": {
41
+ "keys": [ … ],
42
+ "signers": {
43
+ "default": "build",
44
+ "allow": [
45
+ { "address": "0x…", "level": "act", "visibility": "caller", "note": "a partner's agent" },
46
+ { "agentId": "42", "level": "act" }
47
+ ],
48
+ "standing": { "minLevel": 2, "level": "act" },
49
+ "actsNeedRequestBinding": true
50
+ }
51
+ }
52
+ ```
53
+
54
+ In order: the cell's own operator or owner signing remotely is granted act with the operator's view; an `allow` entry by address; an `allow` entry by ERC-8004 agent id, when the signer is that agent's owner or agent wallet on this world's registry (the envelope names the id, the node checks the registry); a signer whose KYA standing under the cell's scheme reaches `standing.minLevel`; otherwise `default` — `build` (what the public gets, but as itself: its decisions are recorded under its address), `read`, or `none` (signatures refused). With `actsNeedRequestBinding` (the default), a session envelope reaches `build` whatever the grant: an act wants an envelope bound to its request. A valid signature at `build` can pre-check, simulate and build calldata for its own account; nothing it says changes a gate.
55
+
56
+ The HTTP entrance tells a caller what it is (`GET /api/capabilities` → `caller.via`: `anonymous`, `key` or `signature`), and `/mcp` and `/a2a` take the same header. Over stdio there is nothing to sign: the local client holds the operator key or is the machine's user.
57
+
58
+ ## become-a-cell
59
+
60
+ Every cell's MCP server carries the genome's instructions: the prompt `become-a-cell` and the resource `foga://docs/quickstart` (this repository's docs/quickstart.md). An agent that meets a cell — connects to its MCP entrance, as the public or with any credential — can read how to make itself one: the wallet first, then `npx -y @finchip.ai/foga quickstart` for a living cell on a local chain, then `foga cell new --wallet … && foga cell init --wallet …` on a public chain — and, with the wallet alone, how to call the cell it met as itself (`foga wallet sign --url <that node>`). What replicates is the program (the package, the catalog, the acceptance); the identity the new cell makes is its own and nothing of the first cell's is copied. The relation between cells is between peers on the chain — tenders minted and taken — not between a model and copies of it.
package/docs/world.md CHANGED
@@ -48,3 +48,15 @@ It is tested with Foundry (`contracts/test/Identity8004Stub.t.sol`, no external
48
48
  ## Public chains
49
49
 
50
50
  Sepolia runs are read-only in Phase 1 (doctor). Phase 2c deploys FOGA-owned components on Sepolia — eligibility modules, FOGA's own ACDF adapter and revision-4 escrow, per-cell accounts — with the same orchestrator, a gas multiplier (`--gas-multiplier 1.5`) and `--resume`, following the lessons of the upstream runs: take every gas limit from the live `eth_estimateGas`, never from a local chain.
51
+
52
+ ## Verifying the sources (Sourcify)
53
+
54
+ ```
55
+ npm run bundle:verify # rebuilds the bundle; leaves .foga-build/out/<repo>/build-info/
56
+ foga world verify --world deployments/sepolia.json --sourcify # submits every FOGA-deployed contract of the world
57
+ foga world verify --world deployments/local.json --sourcify --dry-run # writes what would be sent (a local chain is always a dry run)
58
+ ```
59
+
60
+ A contract a world deployed from the bundle is verified on Sourcify (`src/world/sourcify.ts`, the 2c leftover the memo's section 9 lists) with the very compiler run that produced it: `forge build` leaves a build-info file per run beside the artifacts — the standard JSON input (sources at the locked commits, settings, remappings) and the output — and `npm run bundle:verify` (or `foga build`) writes them under `.foga-build/out/<repo>/build-info/`. For each `deployed` step of the world file, the request is the build-info of the step's repository whose output holds a contract of the bundle key's name with the bundle's runtime code, the identifier `<path solc saw>:<Name>`, the compiler's long version from the contract's own metadata (`0.8.28+commit.7893614a`), and the creation transaction the world file recorded. Nothing is recompiled, flattened or rewritten for the verifier: what Sourcify checks is what the bundle was built from, and a contract the build-info does not hold is reported `missing` with the reason (an upstream contract attached rather than deployed, a bundle rebuilt without `--build-info`).
61
+
62
+ The live path is Sourcify's v2 API: `GET /v2/contract/{chainId}/{address}` first (a contract already verified is `already`), then `POST /v2/verify/{chainId}/{address}` with `{stdJsonInput, compilerVersion, contractIdentifier, creationTransactionHash}`, and the ticket's `GET /v2/verify/{verificationId}` polled until `isJobCompleted` — `exact_match` or `match` (creation and runtime match named), `failed` with Sourcify's error, `error` when the request was refused or timed out. `--only <id,id>` narrows to some steps, `--server` another instance (`https://sourcify.dev/server` by default), `--out` where the requests and the report go (`.foga-build/sourcify/<world name>/`: one `<step>.json` per request and `report.json`, schema `foga.sourcify/1`, with a summary by outcome). Exit 3 when anything is `missing`, `failed` or `error`. `test/sourcify.test.ts` runs the matching, a dry run and the v2 exchange against a stand-in; CI dry-runs the whole local world (47 requests, 0 missing) after each bundle verification.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finchip.ai/foga",
3
- "version": "0.6.0-alpha.5",
3
+ "version": "0.6.0-alpha.7",
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.5",
11
+ "version": "0.6.0-alpha.7",
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.5",
17
+ "version": "0.6.0-alpha.7",
18
18
  "runtimeHint": "npx",
19
19
  "transport": {
20
20
  "type": "stdio"