@kybernesis/create 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/doctor.js CHANGED
@@ -218,6 +218,25 @@ export async function doctor() {
218
218
  add("warn", "self-hosted: export .env.local into the server process", "eve start does NOT read it; use the supervision script from @kybernesis/exe (scripts/eve-server.sh)");
219
219
  // Prewarm runs in the eve CLI, not the built server.
220
220
  add("warn", "self-hosted: start via `npx eve start`, not `node .output/server/index.mjs`", "sandbox templates are prewarmed by the CLI; starting the server directly skips prewarm and every sandbox tool fails with SandboxTemplateNotProvisionedError");
221
+ // The exe VM sandbox backend needs a credential that cannot be scoped.
222
+ // Surface the blast radius here, where it is still cheap to change course.
223
+ const sandboxFile = join(cwd, "agent/sandbox/sandbox.ts");
224
+ const usesExeSandbox = existsSync(sandboxFile) && readFileSync(sandboxFile, "utf8").includes("exeSandbox(");
225
+ if (usesExeSandbox) {
226
+ const src = readFileSync(sandboxFile, "utf8");
227
+ if (src.includes("allowSharedAccount: true")) {
228
+ add("warn", "exeSandbox runs with allowSharedAccount: true", "the sandbox SSH key grants shell to EVERY VM on the exe.dev account — only keep this if the client has explicitly accepted that blast radius; otherwise give the agent its own account");
229
+ }
230
+ else {
231
+ add("pass", "exeSandbox enforces a dedicated exe.dev account");
232
+ }
233
+ if (!process.env.EXE_SANDBOX_SSH_KEY && !process.env.EXE_SANDBOX_SSH_KEY_PATH) {
234
+ add("fail", "exeSandbox has no SSH key (EXE_SANDBOX_SSH_KEY / EXE_SANDBOX_SSH_KEY_PATH)", "it must be a FULL-PERMISSION account key: a key registered through an API token inherits that token's command scope and cannot open a shell at all");
235
+ }
236
+ if (!process.env.EXE_API_TOKEN) {
237
+ add("fail", "exeSandbox has no EXE_API_TOKEN for VM lifecycle", "mint a narrow one: ssh exe.dev \"ssh-key generate-api-key --label=<agent>-sandbox --cmds='ls,new,rm,cp' --exp=7d\"");
238
+ }
239
+ }
221
240
  }
222
241
  // ── engineer subagent (build capability scoped to a subagent) ──────────
223
242
  const builderDir = join(cwd, "agent/subagents/builder");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kybernesis/create",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "The Kybernesis agent scaffolder and FDE toolkit: one command to a governed, remembering, multiplayer, self-testing eve agent — plus doctor and upgrade.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -40,6 +40,10 @@ npm create @kybernesis acme-atlas -- --engineer
40
40
  # Prompts: display name · department subagents · control-plane issuer.
41
41
  # (Non-interactive/CI: defaults apply when stdin is not a terminal.)
42
42
 
43
+ # …or, when the client will NOT use Vercel — their own VM, or their own
44
+ # ChatGPT/LLM subscription paying for inference. See section 11:
45
+ npm create @kybernesis acme-atlas -- --host=exe --engineer
46
+
43
47
  # 3. (Optional, for repeated use) put `kyb` on the PATH for the whole engagement:
44
48
  npm install -g @kybernesis/create
45
49
  ```
@@ -1714,9 +1718,279 @@ the control-plane repo's seed scripts.)
1714
1718
  it is far faster than booting the dev server. `npx eve logs` reads the last `eve dev`
1715
1719
  diagnostic log if you need stderr, tool failures, and rebuild lines.
1716
1720
 
1721
+ ### Self-hosted hosts (exe.dev / client infrastructure)
1722
+
1723
+ **I deployed a change and the agent still does the old thing.**
1724
+ The restart did not take. Check that the running process started *after* the build
1725
+ (`scripts/eve-server.sh` asserts this and fails loudly). A silently-failed restart is the
1726
+ single most expensive failure mode off Vercel, because every test you run afterwards
1727
+ measures the previous build. If the process is current, start a **fresh conversation** —
1728
+ a long-lived channel session caches the compiled agent.
1729
+
1730
+ **Every sandbox tool fails with `SandboxTemplateNotProvisionedError`.**
1731
+ Either Docker is installed but disabled (`sudo systemctl enable --now docker` — exeuntu
1732
+ ships it disabled), or the server was started as `node .output/server/index.mjs`, which
1733
+ skips the CLI's template prewarm. Start with `npx eve start`.
1734
+
1735
+ **The subagent's screenshots fail with `Cannot find module 'playwright'` but the root
1736
+ agent is fine.**
1737
+ Subagents own their sandbox; they do not inherit the root's. Give the subagent its own
1738
+ `sandbox/sandbox.ts`.
1739
+
1740
+ **My env vars are missing under `eve start`.**
1741
+ `eve start` does not read `.env.local` the way `eve dev` does. Use
1742
+ `scripts/eve-server.sh`, which exports it into the process.
1743
+
1744
+ **I killed the agent and my own SSH session at the same time.**
1745
+ `pkill -f <pattern>` matches the SSH command line that contains the pattern. Keep kill
1746
+ patterns inside a script on the host; never pass them on an `ssh` command line.
1747
+
1748
+ **A sandbox VM boots and shows `running`, but every command answers "command not found".**
1749
+ SSH is landing on exe.dev's lobby REPL rather than the VM. exe.dev reuses VM names, so a
1750
+ name deleted and recreated within a few minutes can keep resolving to the lobby, and a
1751
+ stale `known_hosts` entry does the same thing. Use a fresh name, and never reuse the
1752
+ caller's `known_hosts` for sandbox VMs.
1753
+
1754
+ **A command works when I SSH in by hand but not from the agent.**
1755
+ A non-interactive SSH command does not source the login profile, so toolchains installed
1756
+ onto the profile PATH (nvm, pyenv, cargo) are invisible. Run through `bash -lc` — but not
1757
+ for file I/O, whose stdout must stay byte-clean.
1758
+
1759
+ **exe.dev refuses to run commands with my API token.**
1760
+ It cannot. A key registered through an API token inherits that token's command scope, and
1761
+ shell exec is not a scoped command. Running commands needs a full-permission account key;
1762
+ see 11.6 for the isolation this demands.
1763
+
1764
+ ### Third-party APIs and connections
1765
+
1766
+ **The agent reports that a service is down or that a resource "isn't shared" with it.**
1767
+ Verify before you believe it, and never change the client's permissions on an agent's
1768
+ say-so. An agent's error message is a hypothesis, not evidence — one real case reported an
1769
+ outage, then a permissions problem, while the data was reachable the entire time and the
1770
+ actual cause was a version header. Read the request and response yourself.
1771
+
1772
+ **My manual `curl` works but the agent's identical call fails.**
1773
+ It is not identical — and the difference between the two requests *is* the bug. Diff them
1774
+ at the first contradiction rather than proving again that the token works. A version
1775
+ header, a content type, or a base URL differing by one path segment will do it.
1776
+
1777
+ **Calls derived from a large OpenAPI spec fail in ways that make no sense.**
1778
+ Specs with ambiguous ID schemes (Notion's `database_id` vs `data_source_id` for the same
1779
+ object) make the model pick wrong, and the API's errors describe a different problem.
1780
+ Write a small purpose-built tool with the endpoint and IDs pinned; keep the generic
1781
+ connection for the long tail.
1782
+
1717
1783
  ---
1718
1784
 
1719
- ## 11. Known gaps — state these plainly, do not sell around them
1785
+
1786
+ ## 11. Self-hosted deployments (when the client will not use Vercel)
1787
+
1788
+ Vercel is the default path and the proven one. Take this track when the client
1789
+ **cannot or will not** put the agent on Vercel — procurement, data residency, an
1790
+ existing VM estate — or when they want inference billed to a subscription they
1791
+ already pay for rather than to a gateway.
1792
+
1793
+ Everything else in this playbook still applies. This section covers only what
1794
+ changes.
1795
+
1796
+ **The governing rule: every credential comes from the CLIENT's accounts.** If a
1797
+ step works only because you happen to hold a token, that step is a bug in the
1798
+ deployment, not a shortcut — it will fail on the real engagement, in front of
1799
+ the client. Assume you have no Vercel connection, no blob store, and no API
1800
+ keys of your own.
1801
+
1802
+ ### 11.1 Scaffold
1803
+
1804
+ ```bash
1805
+ kyb init <name> --host=exe --channel=<imessage|slack|telegram|none> --engineer
1806
+ cd <name> && kyb doctor
1807
+ ```
1808
+
1809
+ `--host=exe` swaps the host bindings; everything else is the same product —
1810
+ same memory, same evals, same control-plane wiring, same engineer layer.
1811
+ `kyb doctor` knows every failure mode below and fails loudly on each.
1812
+
1813
+ ### 11.2 What Vercel gives you that a client host does not
1814
+
1815
+ | Capability | On Vercel | Self-hosted replacement |
1816
+ | --- | --- | --- |
1817
+ | Model access | AI Gateway | exe.dev LLM integration (`exeModel`) — managed, BYO key, or a **ChatGPT subscription** |
1818
+ | Slack / Photon / Linear credentials | Vercel Connect | **static credentials the client issues** |
1819
+ | Sandbox | `vercel()` | `docker()` on the host, or `exeSandbox()` per-task VMs |
1820
+ | File delivery | Vercel Blob | Blob **or** `DELIVER_DIR` + `DELIVER_BASE_URL` |
1821
+ | Public URLs | deployments | a deploy target, or an account-gated preview |
1822
+ | Secrets | Vercel env | host env + the platform's own secret injection |
1823
+
1824
+ **Vercel Connect does not work off-Vercel — at all.** It authenticates via
1825
+ Vercel OIDC, which does not exist on another host. That applies to Slack, the
1826
+ Vercel MCP connection, Linear, everything. Each becomes a static credential
1827
+ someone must issue and rotate, and `kyb doctor` fails if a `@vercel/connect`
1828
+ import survives into a self-hosted agent.
1829
+
1830
+ ### 11.3 Host setup (exe.dev)
1831
+
1832
+ ```bash
1833
+ ssh exe.dev new --name <agent>
1834
+ ssh exe.dev share port <agent> 8000 && ssh exe.dev share set-public <agent>
1835
+ ```
1836
+
1837
+ Make the host public **before** registering any webhook — webhooks need
1838
+ anonymous access, and a provider that fails verification at registration time
1839
+ usually will not tell you why.
1840
+
1841
+ Then, on the VM: Node 24, `npm ci`, `npx eve build`, and start through
1842
+ `scripts/eve-server.sh` from `@kybernesis/exe`.
1843
+
1844
+ Three things that will cost you an afternoon if you skip them:
1845
+
1846
+ - **`eve start` does not read `.env.local`** the way `eve dev` does. The
1847
+ supervision script exports it into the process for you.
1848
+ - **Start via `npx eve start`, not `node .output/server/index.mjs`.** Sandbox
1849
+ template prewarm lives in the CLI, not the built server. Starting the server
1850
+ directly gives you cleaner logs and no prewarm, so every sandbox tool then
1851
+ fails with `SandboxTemplateNotProvisionedError`.
1852
+ - **Docker ships disabled on some images**, exeuntu among them. `docker
1853
+ --version` answers happily while nothing can actually run. Fix with `sudo
1854
+ systemctl enable --now docker`.
1855
+
1856
+ ### 11.4 Model: billing inference to the client's subscription
1857
+
1858
+ exe.dev brokers the model, so no provider key sits on the host:
1859
+
1860
+ ```bash
1861
+ ssh exe.dev integrations setup chatgpt --name work # once, device-code flow
1862
+ ssh exe.dev integrations edit llm --openai=chatgpt --openai-account=work
1863
+ ```
1864
+
1865
+ Then `exeModel()` from `@kybernesis/exe` points the agent at it. A Codex-backed
1866
+ subscription requires `store: false` on every request; `exeModel` forces it.
1867
+
1868
+ This is the single biggest commercial difference in the self-hosted track: a
1869
+ client with an existing ChatGPT or Claude subscription pays no incremental
1870
+ inference cost for the pilot. Say the number out loud in the discovery
1871
+ conversation — it changes the shape of the deal.
1872
+
1873
+ ### 11.5 Third-party APIs: broker the credential, pin the version
1874
+
1875
+ Do not put a client's API token on the agent host. Put it in an exe.dev
1876
+ http-proxy integration, which injects headers server-side:
1877
+
1878
+ ```bash
1879
+ ssh exe.dev "integrations add http-proxy --name notion \
1880
+ --target https://api.notion.com \
1881
+ --header 'Authorization:Bearer <token>' \
1882
+ --header 'Notion-Version:2025-09-03' \
1883
+ --attach vm:<agent>"
1884
+ ```
1885
+
1886
+ The agent then calls `https://notion.int.exe.xyz` with no credential at all, and
1887
+ rotation happens in one place the client controls.
1888
+
1889
+ Two rules here, both learned expensively:
1890
+
1891
+ **Pin the API version the SPEC describes, not the one in a doc example.** A
1892
+ version header copied from documentation while the agent's OpenAPI spec
1893
+ describes a newer API produces errors that describe the wrong problem entirely.
1894
+ One real case cost most of a day: `Notion-Version: 2022-06-28` against a spec
1895
+ using `/v1/data_sources/...` returned `invalid_request_url`, intermittent 503s
1896
+ from search, and "not shared with the integration" — three different messages,
1897
+ none of them about the version mismatch, two of which look like a permissions
1898
+ or availability problem you can waste hours "fixing" on the client's side.
1899
+
1900
+ **Use `--header` for the token, never `--bearer=-`.** The stdin form mangles the
1901
+ value and the API answers 401 "token is invalid".
1902
+
1903
+ **Large specs with ambiguous ID schemes need a purpose-built tool, not a raw
1904
+ connection.** Notion's spec is ~1.2MB and splits `database_id` from
1905
+ `data_source_id` for the same object; a model deriving calls from it picks
1906
+ wrong. Pin the endpoint and the IDs in a small tool under `agent/tools/`, keep
1907
+ the generic connection for the long tail, and point the instructions at the
1908
+ tool. Reads went from unreliable to deterministic with about sixty lines.
1909
+
1910
+ ### 11.6 The engineer layer, self-hosted
1911
+
1912
+ `--engineer` scaffolds a **builder subagent** that owns the build capability, so
1913
+ the root agent never gets a shell. It comes with the full production loop —
1914
+ workshop sandbox, Playwright, screenshots, visual verification, delivery — not a
1915
+ reduced version of the Vercel one.
1916
+
1917
+ - **Subagents own their sandbox; they do NOT inherit the root's.** A builder
1918
+ without its own `sandbox/sandbox.ts` gets a bare template and every screenshot
1919
+ fails with `Cannot find module 'playwright'` while the root's template is
1920
+ fine. `kyb doctor` checks for this.
1921
+ - Conversely, if the root agent runs no code, it needs **no** root sandbox.
1922
+ Leaving one there costs a full template prewarm on every deploy for nothing.
1923
+
1924
+ **Choosing a sandbox backend:**
1925
+
1926
+ `docker()` on the agent's own VM is the default and the safer choice. Reach for
1927
+ `exeSandbox()` — a whole exe.dev VM per session, with Docker available *inside*
1928
+ it and its own public URL — only when the work needs real isolation or
1929
+ per-session compute.
1930
+
1931
+ `exeSandbox()` carries one hard constraint you must raise with the client before
1932
+ choosing it: **it needs a full-permission account SSH key.** exe.dev keys
1933
+ registered through an API token inherit that token's command scope and cannot
1934
+ open a shell at all, so there is no scoped credential that can run commands. The
1935
+ key it does need grants shell to **every VM on that exe.dev account**. The
1936
+ backend therefore refuses to start unless the account is dedicated to this agent
1937
+ (its own VM plus its sandboxes), naming any foreign VMs it finds. Overriding
1938
+ that with `allowSharedAccount: true` is a decision for the client to make in
1939
+ writing, not for you to make on their behalf. VM lifecycle stays on a separately
1940
+ scoped token that cannot exec, so neither credential is sufficient alone.
1941
+
1942
+ ### 11.7 Showing the client what the agent built
1943
+
1944
+ - **Vercel Blob refuses to serve HTML inline** — it forces a download. Use it
1945
+ for documents and exports, never to show a web page.
1946
+ - **exe.dev forwards ports 3000–9999** to `https://<vm>.exe.xyz:<port>/`, but a
1947
+ VM has exactly **one public port** and the agent's webhook already owns it.
1948
+ Alternate ports are account-gated: fine for the client reviewing work, not for
1949
+ the public.
1950
+ - **Anything genuinely public needs a deploy target** — the client's own Vercel
1951
+ token, or their hosting. Treat "public" as a deploy step, not a toggle, and
1952
+ price it into the scope.
1953
+ - A sandbox is a container: its ports are not reachable from the host, so a dev
1954
+ server inside it cannot be previewed directly. Copy the artifact out (the
1955
+ `preview` tool in `@kybernesis/exe`) or deploy it.
1956
+
1957
+ ### 11.8 Prove the restart, every time
1958
+
1959
+ There is no deployment pipeline here to tell you a release landed. A restart
1960
+ that silently fails leaves the agent serving a stale build — new connections,
1961
+ tools, and instructions never appear — and **every test you run afterwards
1962
+ measures yesterday's agent.** One session lost an hour to exactly this.
1963
+
1964
+ `scripts/eve-server.sh` asserts the running process started *after* the build it
1965
+ should be serving, and fails loudly when it did not. Use it rather than
1966
+ `pkill` + `npx eve start` by hand. Related: `pkill -f <pattern>` over SSH kills
1967
+ your own session when the pattern appears in the SSH command line — and can take
1968
+ the agent down with it. Use the pidfile.
1969
+
1970
+ Also: a long-lived channel session caches the compiled agent. After changing
1971
+ capabilities, start a **fresh conversation** before deciding the change did not
1972
+ work.
1973
+
1974
+ ### 11.9 Credential checklist — collect ALL of these from the client
1975
+
1976
+ Nothing here can be borrowed from another agent or another account.
1977
+
1978
+ 1. **Host** — VM/server, plus the platform token if the agent provisions anything
1979
+ 2. **Model source** — their LLM API key, gateway allocation, or connected subscription
1980
+ 3. **Channel app** — their Slack app (bot + app token) / Photon project / bot token
1981
+ 4. **Arcana** — workspaces + scoped `kb_` keys (one per brain, plus `-eval`)
1982
+ 5. **Storage for deliverables** — their blob store, or a served host directory
1983
+ 6. **Deploy target** — their Vercel token or hosting, if the agent ships sites
1984
+ 7. **Sandbox credentials** — only if using `exeSandbox()` (see 11.6)
1985
+ 8. **Control plane** — agent registered and the pilot cohort granted
1986
+
1987
+ ### 11.10 Before calling it done
1988
+
1989
+ `kyb doctor` green (or every warning consciously accepted), the eval suite green
1990
+ against the client's `-eval` workspace, and a live turn on the real surface —
1991
+ sent from the client's own device, not yours.
1992
+
1993
+ ## 12. Known gaps — state these plainly, do not sell around them
1720
1994
 
1721
1995
  Being straight about these is a feature. Clients have met vendors who were not.
1722
1996
 
@@ -94,3 +94,53 @@ Nothing here can be borrowed from another agent or another account.
94
94
 
95
95
  `kyb doctor` green (or every warning consciously accepted), the eval suite green
96
96
  against the client's `-eval` workspace, and a live turn on the real surface.
97
+
98
+ ## Third-party APIs: version pinning and spec-derived calls
99
+
100
+ Two failures here cost most of a day on the first deployment. Both look like
101
+ outages or permission problems and are neither.
102
+
103
+ **Pin the API version the SPEC describes, not the one in a doc example.**
104
+ Notion's OpenAPI spec describes their current API (`/v1/data_sources/…`), but
105
+ eve's docs example pins `Notion-Version: 2022-06-28`, where that endpoint does
106
+ not exist. The mismatch returns `invalid_request_url`, `service_unavailable`
107
+ (503) on search, and "not shared with the integration" — three different lies,
108
+ none of them about the actual problem. Verify by making the SAME call the agent
109
+ makes, headers included.
110
+
111
+ **When the agent and your manual test disagree, the difference between the two
112
+ requests IS the bug.** Diff them at the first contradiction. Repeatedly proving
113
+ "the token works" with a hand-written curl while the agent fails proves nothing
114
+ if your curl sends a different version header.
115
+
116
+ **An agent's error message is a hypothesis, not evidence.** It will confidently
117
+ report an outage or a permissions problem it has not verified. Read the actual
118
+ request and response before acting — and never change a client's permissions on
119
+ an agent's say-so.
120
+
121
+ **Large specs with ambiguous ID schemes need a purpose-built tool, not a raw
122
+ connection.** Notion's spec is ~1.2MB and splits `database_id` from
123
+ `data_source_id` for the same board; a model deriving calls from it picks the
124
+ wrong one. Pin the endpoint and the IDs in a small tool
125
+ (`agent/tools/<domain>.ts`), keep the generic connection for the long tail, and
126
+ point the instructions at the tool.
127
+
128
+ **Credential brokering (exe http-proxy) is the right default off-Vercel:**
129
+
130
+ ```
131
+ integrations add http-proxy --name <svc> --target https://api.example.com \
132
+ --header 'Authorization:Bearer <token>' --header '<Version-Header>:<value>' \
133
+ --attach vm:<vm>
134
+ ```
135
+
136
+ Use `--header` for the token, not `--bearer=-`: the stdin form mangles it and
137
+ the API answers 401 "token is invalid".
138
+
139
+ ## Restarts must be proven, not assumed
140
+
141
+ A restart that silently fails leaves the agent serving a stale build — new
142
+ connections, tools, and instructions never appear, and every later test measures
143
+ yesterday's agent. Assert the process started AFTER the build it should serve
144
+ (`scripts/eve-server.sh` and the restart pattern in `@kybernesis/exe` do this).
145
+ Related: a long-lived channel session caches the compiled agent, so start a
146
+ fresh conversation after changing capabilities.