@open-everywhere/create 0.1.4

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.
Files changed (82) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +21 -0
  3. package/README.md +69 -0
  4. package/bin/create-open-everywhere.js +57 -0
  5. package/package.json +57 -0
  6. package/src/cli.ts +241 -0
  7. package/src/deploy-probe.ts +99 -0
  8. package/src/engine.ts +106 -0
  9. package/src/env-file.ts +53 -0
  10. package/src/scaffold.ts +188 -0
  11. package/src/steps/build-deploy.ts +78 -0
  12. package/src/steps/database.ts +85 -0
  13. package/src/steps/env-checklist.ts +55 -0
  14. package/src/steps/index.ts +49 -0
  15. package/src/steps/prerequisites.ts +50 -0
  16. package/src/steps/repo.ts +69 -0
  17. package/src/steps/roster.ts +111 -0
  18. package/src/steps/slack.ts +74 -0
  19. package/src/steps/vercel.ts +40 -0
  20. package/src/steps/verify.ts +119 -0
  21. package/src/version.ts +20 -0
  22. package/template/.env.example +58 -0
  23. package/template/.mcp.json +8 -0
  24. package/template/AGENTS.md +62 -0
  25. package/template/CLAUDE.md +1 -0
  26. package/template/README.md +87 -0
  27. package/template/agent/access/access-requests.ts +69 -0
  28. package/template/agent/access/browser-hosts.ts +28 -0
  29. package/template/agent/access/bundles.ts +78 -0
  30. package/template/agent/access/connector-credentials.ts +74 -0
  31. package/template/agent/access/credential-bindings.generated.ts +17 -0
  32. package/template/agent/access/credential-bindings.ts +28 -0
  33. package/template/agent/access/grants.generated.ts +18 -0
  34. package/template/agent/access/grants.ts +30 -0
  35. package/template/agent/access/network-access.ts +32 -0
  36. package/template/agent/agent.ts +125 -0
  37. package/template/agent/blob/hosting.ts +42 -0
  38. package/template/agent/identity/authors.ts +79 -0
  39. package/template/agent/identity/groups-overlay.ts +24 -0
  40. package/template/agent/identity/groups.generated.ts +17 -0
  41. package/template/agent/identity/groups.ts +84 -0
  42. package/template/agent/identity/principals.ts +37 -0
  43. package/template/agent/identity/surfaces.ts +48 -0
  44. package/template/agent/instructions/access.ts +11 -0
  45. package/template/agent/instructions.md +295 -0
  46. package/template/agent/instrumentation.ts +6 -0
  47. package/template/agent/lib/memory-grants.ts +52 -0
  48. package/template/agent/lib/register-adapters.ts +286 -0
  49. package/template/agent/lib/register-config.ts +185 -0
  50. package/template/agent/memory/config/agents-overlay.ts +26 -0
  51. package/template/agent/memory/config/agents.generated.ts +21 -0
  52. package/template/agent/memory/config/domains/dm.ts +10 -0
  53. package/template/agent/memory/config/domains/index.ts +125 -0
  54. package/template/agent/memory/config/domains/org.ts +27 -0
  55. package/template/agent/memory/config/domains.ts +165 -0
  56. package/template/agent/model/models.ts +60 -0
  57. package/template/agent/model/policy.ts +19 -0
  58. package/template/agent/model/stacks.ts +75 -0
  59. package/template/agent/plugins/generated/index.ts +13 -0
  60. package/template/agent/plugins/registry.ts +16 -0
  61. package/template/agent/plugins/skill-content.ts +13 -0
  62. package/template/agent/roles/roles.ts +146 -0
  63. package/template/agent/routines/collectors.ts +89 -0
  64. package/template/agent/sandbox/backend-select.ts +16 -0
  65. package/template/agent/sandbox/sandbox.ts +242 -0
  66. package/template/agent/sandbox/workspace/.keep +6 -0
  67. package/template/agent/settings/settings.ts +19 -0
  68. package/template/agent/spend/budgets.ts +25 -0
  69. package/template/agent/spend/checkpoints.ts +51 -0
  70. package/template/agent/spend/model-pricing.ts +25 -0
  71. package/template/agent/webhooks/hooks-overlay.ts +34 -0
  72. package/template/agent/webhooks/hooks.generated.ts +17 -0
  73. package/template/agent/webhooks/hooks.ts +133 -0
  74. package/template/claude/skills/quickstart-open-everywhere/SKILL.md +167 -0
  75. package/template/claude/skills/upgrade-open-everywhere/SKILL.md +160 -0
  76. package/template/gitignore +42 -0
  77. package/template/package.json +62 -0
  78. package/template/roster.config.ts +39 -0
  79. package/template/roster.lock.json +182 -0
  80. package/template/scripts/env-pipe.mjs +74 -0
  81. package/template/scripts/ops-mcp.ts +35 -0
  82. package/template/tsconfig.json +23 -0
@@ -0,0 +1,58 @@
1
+ # Copy to .env.local (it is gitignored) and fill in. eve loads .env and .env.local.
2
+ #
3
+ # Variable NAMES only. Never commit a value. .env.local never ships: the deployment reads its OWN
4
+ # environment. The full setup path, including which variables the deployment needs and how to copy
5
+ # each one without printing it, is the generated reference page:
6
+ # https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/setup-steps.md
7
+ # (README.md § "Set it up" is the one-screen version.) Two routes are supported: Vercel and Docker.
8
+
9
+ # --- Models: the Vercel AI Gateway ---
10
+ # A deployed VERCEL project authenticates to the gateway with OIDC and needs no key. Everything
11
+ # else does: a local run, and the DOCKER route, where there is no OIDC. Running a different model
12
+ # backend instead is an adapter swap in app code, not an env setting (docs/extending/adapter-model.md).
13
+ # Create a key at: Vercel dashboard -> AI Gateway -> API Keys.
14
+ AI_GATEWAY_API_KEY=
15
+
16
+ # --- The operational store, memory and the credential store ---
17
+ # A DEPLOYMENT needs ONE value here: DATABASE_URL. On Vercel a function's disk is ephemeral, and a
18
+ # replaced Docker container starts from an empty volume, so without it core throws at the first
19
+ # store or DM-memory use. On the Vercel route add Neon from the Marketplace
20
+ # (`npx vercel install neon`, or project -> Storage -> Neon -> Connect): it sets DATABASE_URL on the
21
+ # project. Copy the same value here so the operator tools (`npx oe secret`, the ops server's
22
+ # `secret_set`) write the store the deployment reads. A local run needs nothing: it uses an embedded
23
+ # database under .open-everywhere-data/.
24
+ DATABASE_URL=
25
+ # 🔴 NOT REQUIRED (OPE-2230). A present DATABASE_URL resolves BOTH stores to postgres on its own, so
26
+ # there is no selector to set and nothing to push to the deployment. These two are an OVERRIDE only
27
+ # — and a selector pointing AWAY from postgres is the one thing that breaks the resolution.
28
+ # OE_OPERATIONAL_PROVIDER=postgres
29
+ # OE_MEMORY_PROVIDER=postgres
30
+ # A base64-encoded 32-byte AES-256 key that encrypts stored credentials. The deployment and the
31
+ # operator tools need the same value. This command appends a fresh one to .env.local without printing it:
32
+ # node -e "console.log('OE_CREDENTIAL_ENCRYPTION_KEY=' + require('crypto').randomBytes(32).toString('base64'))" >> .env.local
33
+ # OE_CREDENTIAL_ENCRYPTION_KEY= (append it with the command above; do not type it here)
34
+ # Set this only for a custom domain. On Vercel the deployment reads its production host itself.
35
+ # OE_PUBLIC_BASE_URL=https://agent.example.com
36
+ #
37
+ # After DATABASE_URL and the key are set, `npx oe secret set <NAME>` (value on stdin) stores a secret
38
+ # whose credential names `broker: "db"` in the encrypted store. The Slack variables and
39
+ # GITHUB_WEBHOOK_SECRET below are read from the ENVIRONMENT only, so they stay environment variables.
40
+
41
+ # --- Slack (https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/surfaces/slack.md) ---
42
+ # Slack app -> OAuth & Permissions -> Bot User OAuth Token (xoxb-…).
43
+ SLACK_BOT_TOKEN=
44
+ # Slack app -> Basic Information -> Signing Secret. Verifies every inbound event.
45
+ SLACK_SIGNING_SECRET=
46
+
47
+ # --- GitHub (https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/surfaces/github.md) ---
48
+ # The GitHub App that receives webhooks and replies as the app.
49
+ # GITHUB_APP_ID=
50
+ # GITHUB_APP_PRIVATE_KEY=
51
+ # GITHUB_APP_SLUG=
52
+ # Verifies inbound deliveries. If it is unset, the route refuses every delivery.
53
+ # GITHUB_WEBHOOK_SECRET=
54
+
55
+ # --- Notion (https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/surfaces/notion.md) ---
56
+ # Notion shows the webhook verification token once, at subscription time. Store it here or with
57
+ # `npx oe secret set NOTION_WEBHOOK_SECRET` (the stored value wins over this variable).
58
+ # NOTION_WEBHOOK_SECRET=
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "open-everywhere-ops": {
4
+ "command": "node",
5
+ "args": ["--import", "tsx", "scripts/ops-mcp.ts"]
6
+ }
7
+ }
8
+ }
@@ -0,0 +1,62 @@
1
+ # AGENTS.md
2
+
3
+ This is an **Open Everywhere app**: an autonomous teammate for Slack, Notion and GitHub. The runtime
4
+ is the `@open-everywhere/*` npm packages on the `eve` agent framework. This repository holds only
5
+ the app's configuration.
6
+
7
+ ## Setting it up
8
+
9
+ Setup is yours to drive, with the user's consent at every command. Use the
10
+ `quickstart-open-everywhere` skill (`.claude/skills/`) and the `open-everywhere-ops` MCP server
11
+ (`.mcp.json`).
12
+
13
+ - **Step 1 is to ask which route**: **Vercel** (the `vercel` CLI plus Marketplace Neon) or
14
+ **Docker** (self-host against the deployment contract,
15
+ `https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/contract.md`). These
16
+ are the two supported routes. Do not invent a third, and do not pick one for the user.
17
+ - **Never propose a local run as a setup step.** `npm run dev` is the development loop after the
18
+ deploy — changing tools and skills, evals, debugging a turn. A local run has no public HTTPS URL
19
+ for Slack to reach and a different store, so it proves little about the deployment.
20
+ - **Never ask the user to paste a secret into the chat.** They paste it where a tool reads it; you
21
+ write it with `scaffold_env`, or mint it with `scaffold_credential_key`, which never prints it.
22
+ - `npx @open-everywhere/create --check --json` is the status report: every step, with `satisfied`,
23
+ `detail` and a `next` command. Add `--route docker` for the Docker route and
24
+ `--host https://<host>` to probe a deployment. The human-readable version of the same step list is
25
+ `https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/setup-steps.md`.
26
+
27
+ ## Where things live
28
+
29
+ - `roster.config.ts` — what the agent can do. `roster.lock.json` is the reviewed roll-call of every
30
+ contribution. Both are committed.
31
+ - `agent/agent.ts` — the eve agent definition.
32
+ - `agent/` — the config: access bundles, identity and trust, memory domains, models, settings, spend
33
+ caps, webhooks. Each file's header says what it controls.
34
+ - `agent/tools/`, `agent/channels/`, `agent/hooks/`, `agent/schedules/`, `agent/skills/`,
35
+ `agent/connections/` and `agent/lib/providers.generated.ts` — `oe build` output. Never edit or
36
+ commit them.
37
+ - `*.generated.ts` — machine-owned overlays that the agent's own approval flows write by pull
38
+ request. Never edit them by hand.
39
+ - `scripts/ops-mcp.ts` — the ops MCP server. `scripts/env-pipe.mjs` — `npm run -s env:pipe`, which
40
+ pipes one `.env.local` value into `vercel env add`. Always pipe it; never run it bare.
41
+
42
+ ## The pipeline
43
+
44
+ - `npm install` runs the `postinstall` script, `oe-eve-patch`. It applies the Open Everywhere patch
45
+ to the installed `eve` and verifies it. The app refuses to boot on an unpatched `eve`.
46
+ - `npm run build` runs `oe build` (writes the slot tree from the roster), then `eve build`.
47
+ `npm run dev` and `npm start` run `oe build` first too.
48
+ - `oe build` refuses a contribution the lock has not reviewed. Decide about it in
49
+ `roster.config.ts`, then run `npx oe build --accept`, and review the lock diff.
50
+ - `npm run typecheck` type-checks the app.
51
+ - Deploy is a separate, explicit step. On the Vercel route that is `npx vercel deploy --prod`; a
52
+ `git push` does NOT deploy.
53
+
54
+ ## Rules
55
+
56
+ - Never commit a secret, a token, an `.env` value or a credential. Secrets go in `.env.local`
57
+ (gitignored), and the deployment reads them from its own environment. A credential whose
58
+ `broker` is `"db"` goes in the encrypted credential store instead (`npx oe secret set <NAME>`).
59
+ - Upgrade every `@open-everywhere/*` package together, to one version. Keep `eve` at the exact
60
+ version `@open-everywhere/core` declares as its peer. A `devDependencies` key that still reads
61
+ `create-open-everywhere` is replaced by `@open-everywhere/create`, not bumped: the unscoped name is
62
+ retired. `npx oe upgrade` does that rename.
@@ -0,0 +1 @@
1
+ Read [AGENTS.md](./AGENTS.md): it describes this Open Everywhere app, where its config lives, and the rules.
@@ -0,0 +1,87 @@
1
+ # Open Everywhere app
2
+
3
+ This is an [Open Everywhere](https://github.com/open-everywhere/open-everywhere) app: an
4
+ autonomous teammate you tag in Slack, Notion and GitHub. The runtime comes from the
5
+ `@open-everywhere/*` npm packages; this repository holds only your configuration.
6
+
7
+ ## Set it up
8
+
9
+ **Open this folder in your coding agent and ask it to set up Open Everywhere.**
10
+
11
+ It reads `AGENTS.md`, `.mcp.json` gives it the `open-everywhere-ops` MCP server, and
12
+ `.claude/skills/quickstart-open-everywhere/` is the runbook it follows. It will ask you to pick a
13
+ route first:
14
+
15
+ - **Vercel** — the `vercel` CLI plus Marketplace Neon. The deployment authenticates to the AI
16
+ Gateway with OIDC, so it needs no gateway key.
17
+ - **Docker** — self-host a container that satisfies the
18
+ [deployment contract](https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/contract.md).
19
+
20
+ These are the two supported routes. Some steps stay with you: creating the Slack app, approving
21
+ each deploy, and pasting each secret where the agent's tools can read it but the chat cannot.
22
+
23
+ Want the whole path on one page, or doing a step by hand? Read the
24
+ [setup steps reference](https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/setup-steps.md).
25
+ It is generated from the same step list the status check reports, so it cannot drift from it.
26
+
27
+ ## Check where you are
28
+
29
+ `npx @open-everywhere/create --check` reports every setup step and the next command for each
30
+ unsatisfied one. It changes nothing. Add `--json` for the report as data, `--route docker` to judge
31
+ against the Docker route, and `--host https://<host>` to probe a deployment.
32
+
33
+ ## Develop locally
34
+
35
+ After the deploy, `npm run dev` runs the agent on this machine and talks to it in a terminal UI.
36
+ That is the loop for changing tools and skills, running evals, and debugging a turn — not a way to
37
+ set the app up. It needs `AI_GATEWAY_API_KEY` in `.env.local` (Vercel → AI Gateway → API Keys), and
38
+ it keeps its state in an embedded database under `.open-everywhere-data/`. Slack cannot reach it
39
+ without a public tunnel.
40
+
41
+ ## What is in this repository
42
+
43
+ | Path | What it is |
44
+ | --- | --- |
45
+ | `roster.config.ts` | What the agent can do: which packages contribute, and which eve built-ins it takes away. |
46
+ | `roster.lock.json` | The reviewed roll-call of every contribution. `npx oe build --accept` updates it. Review its diff. |
47
+ | `agent/agent.ts` | The eve agent definition. |
48
+ | `agent/access/` | Bundles and bindings (what the agent may reach, and where), connector credentials, browser hosts. |
49
+ | `agent/identity/` | Trusted surfaces, principals and groups. |
50
+ | `agent/memory/config/` | Memory domains and silos. |
51
+ | `agent/model/` | Model stacks and the org model policy. |
52
+ | `agent/settings/settings.ts` | Behaviour settings. Empty means the shipped defaults. |
53
+ | `agent/spend/` | Spend caps and model pricing. |
54
+ | `agent/webhooks/hooks.ts` | Inbound webhook triggers. |
55
+ | `AGENTS.md`, `CLAUDE.md` | The guide a coding agent reads in this repository. |
56
+ | `.mcp.json`, `scripts/ops-mcp.ts` | The `open-everywhere-ops` MCP server: setup status, `.env.local` values and stored secrets, for a coding agent. |
57
+ | `scripts/env-pipe.mjs` | `npm run -s env:pipe`: writes one `.env.local` value to a pipe for `vercel env add`. It refuses a terminal or a file. Never run it bare. |
58
+ | `.claude/skills/quickstart-open-everywhere/` | The setup skill that drives that server. |
59
+
60
+ `agent/tools/`, `agent/channels/`, `agent/hooks/`, `agent/schedules/`, `agent/skills/`,
61
+ `agent/connections/` and `agent/lib/providers.generated.ts` are `oe build` output. They are not
62
+ committed. The `*.generated.ts` files are machine-owned overlays that the agent's own approval flows
63
+ write by pull request. Do not edit them by hand.
64
+
65
+ ## Add a surface
66
+
67
+ Notion and GitHub are in the roster. Each one needs its own setup before it answers:
68
+
69
+ - **Notion** — create an internal integration, subscribe its webhook to
70
+ `https://<host>/eve/v1/notion`, and put the pages it may act on in a memory domain's `reach`. See the
71
+ [Notion guide](https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/surfaces/notion.md).
72
+ - **GitHub** — create a GitHub App, point its webhook at `https://<host>/eve/v1/github`, and set
73
+ `GITHUB_WEBHOOK_SECRET`. See the
74
+ [GitHub guide](https://github.com/open-everywhere/open-everywhere/blob/main/docs/deploy/surfaces/github.md).
75
+
76
+ Discord, Microsoft Teams and Linear are installed too. To add one, add it under `providers` in
77
+ `roster.config.ts`, then run `npx oe build --accept`.
78
+
79
+ ## Upgrade
80
+
81
+ Bump every `@open-everywhere/*` package to the same version, run `npm install`, then
82
+ `npx oe build --accept`, and review the `roster.lock.json` diff before you deploy. Keep `eve` at the
83
+ version `@open-everywhere/core` declares as its peer.
84
+
85
+ If `devDependencies` still names `create-open-everywhere`, replace that key with
86
+ `@open-everywhere/create` at the same version. The unscoped name is retired and publishes no further
87
+ version. `npx oe upgrade` does this rename for you.
@@ -0,0 +1,69 @@
1
+ // Access-request flow config (docs/how-it-works/access-requests.md in the Open Everywhere repository) — the deployer's
2
+ // knobs for the widen-slow path: request card → deterministic codemod → bot-PR →
3
+ // ADMIN MERGES IN GITHUB (that merge is the approval; there is no admin card).
4
+ //
5
+ // FAIL-CLOSED: with either env var below unset, the `request_access` tool refuses
6
+ // politely and the flow is simply OFF — nothing else changes. Both values are
7
+ // operator-specific (a private channel id, YOUR fork's repo), so they live in env,
8
+ // never in this committed file (AGENTS.md security rules).
9
+ //
10
+ // OE_ACCESS_ADMIN_CHANNEL Slack channel id that hears "PR opened — merge to grant".
11
+ // OE_ACCESS_CONFIG_REPO "owner/repo" of THIS deployment's config repo (the one
12
+ // the grants overlay lives in). The GitHub App needs write on it.
13
+ // OE_ACCESS_CONFIG_BASE Base branch (default "main").
14
+ // OE_ACCESS_GRANTS_PATH Overlay path (default "agent/access/grants.generated.ts").
15
+ // OE_WEBHOOK_HOOKS_PATH Webhook-declaration overlay path (default
16
+ // "agent/webhooks/hooks.generated.ts") — the `declare_webhook` flow (OPE-208)
17
+ // rides this same repo/branch/credential, only the file differs.
18
+ // OE_ACCESS_DRY_RUN=1 e2e/test mode: a tap validates + reports, no branch/PR.
19
+ //
20
+ // baseBundles/baseBindings hand core the CODE-ONLY baseline (overlay excluded) so the
21
+ // execute path can re-validate a CANDIDATE grants file merged against it before any PR
22
+ // opens — the runtime resolver keeps using the merged loaders.
23
+
24
+ import type { AccessRequestsConfig } from "@open-everywhere/core/agent/access/config.js";
25
+ import { emptyAgentsFile } from "@open-everywhere/core/agent/memory/agents-codemod.js";
26
+ import { loadBaseBundles, loadBaseBindings } from "./bundles.js";
27
+ import { loadBaseConnectorCredentials } from "./connector-credentials.js";
28
+ import { loadBaseWebhooks } from "../webhooks/hooks.js";
29
+ import { loadBaseDomains, loadBaseSilos, loadDomains } from "../memory/config/domains.js";
30
+ import { loadGroups } from "../identity/groups.js";
31
+
32
+ export function loadAccessRequests(): AccessRequestsConfig | null {
33
+ return {
34
+ adminChannel: process.env.OE_ACCESS_ADMIN_CHANNEL,
35
+ configRepo: process.env.OE_ACCESS_CONFIG_REPO,
36
+ configBase: process.env.OE_ACCESS_CONFIG_BASE || undefined,
37
+ grantsPath: process.env.OE_ACCESS_GRANTS_PATH || undefined,
38
+ dryRun: process.env.OE_ACCESS_DRY_RUN === "1",
39
+ baseBundles: loadBaseBundles,
40
+ baseBindings: loadBaseBindings,
41
+ // The MEMORY-DOMAIN registry — the MERGED one (code config ∪ generated agents), because a
42
+ // domain-scoped request freezes the domain name at proposal and must prove that exact name
43
+ // still resolves when a member taps. Never retarget a stale request to a different domain.
44
+ domainRegistry: loadDomains,
45
+ // The PERSON-GROUP registry (C.1) — the MERGED one (hand-written ∪ the IdP-group overlay),
46
+ // deliberately not a code-only baseline: this flow never writes agent/identity/groups*.ts, so
47
+ // the honest thing to validate a `group`-scoped candidate binding against is what the
48
+ // deployment actually resolves. It is what makes a self-serve DM/group grant naming an unknown
49
+ // group refuse BEFORE a PR opens instead of at the next config load.
50
+ groupRegistry: loadGroups,
51
+ // The stage-5 credential-binding PR path re-validates the merged catalogue set (base ⊕ candidate
52
+ // override) before opening a PR against this SAME config repo.
53
+ baseConnectorCredentials: loadBaseConnectorCredentials,
54
+ // The `declare_webhook` PR path (OPE-208) rides this SAME config repo, branch and bot
55
+ // credential — only the machine-written file differs. `baseWebhooks` is the code-declared
56
+ // baseline a candidate overlay is validated against before any PR opens.
57
+ hooksPath: process.env.OE_WEBHOOK_HOOKS_PATH || undefined,
58
+ baseWebhooks: loadBaseWebhooks,
59
+ // The `create_agent` PR path (create_agent flow, increment 2) rides this SAME config repo, branch
60
+ // and bot credential — only the machine-written file differs (the agents overlay). `baseAgents` is
61
+ // the code-declared agents baseline (empty — agents live only in the overlay); `baseDomains` /
62
+ // `baseSilos` are the code-only memory baseline a candidate overlay is validated against (ring ②)
63
+ // before any PR opens. `agentsPath` defaults to "agent/memory/config/agents.generated.ts".
64
+ agentsPath: process.env.OE_AGENTS_PATH || undefined,
65
+ baseAgents: emptyAgentsFile,
66
+ baseDomains: loadBaseDomains,
67
+ baseSilos: loadBaseSilos,
68
+ };
69
+ }
@@ -0,0 +1,28 @@
1
+ // The in-sandbox browser's whole reach. A host reaches this list by pull request, and the diff is
2
+ // the review. Each row is a security decision:
3
+ // host — where the browser may go.
4
+ // methods — which HTTP verbs the sandbox firewall admits there. `["GET", "HEAD"]` cannot
5
+ // submit a form. Adding `POST` permits one, with no per-call approval card.
6
+ // credential — optional: the egress connection (agent/access/bundles.ts) whose secret the
7
+ // firewall stamps on requests to this host. The browser never holds it.
8
+ // evaluate — optional: arm `browser_evaluate` (model-written JavaScript) for this host.
9
+ //
10
+ // The browser is OFF until `browser.enabled` is true in agent/settings/settings.ts. Nothing in this
11
+ // file is a secret, and nothing in it may be.
12
+
13
+ import { registerBrowserHosts, type BrowserHostDeclaration } from "@open-everywhere/core/agent/access/browser-hosts.js";
14
+
15
+ const browserHosts: BrowserHostDeclaration[] = [
16
+ {
17
+ host: "localhost",
18
+ methods: ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"],
19
+ why: "The in-sandbox dev server a worker boots to see its own UI change.",
20
+ },
21
+ {
22
+ host: "127.0.0.1",
23
+ methods: ["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE"],
24
+ why: "The same in-sandbox dev server, reached by loopback address instead of by name.",
25
+ },
26
+ ];
27
+
28
+ registerBrowserHosts(browserHosts);
@@ -0,0 +1,78 @@
1
+ // Access bundles and bindings — WHAT the agent may reach, and WHERE.
2
+ //
3
+ // A bundle is a named set of capabilities: egress hosts (`domains`), repositories (`repos`),
4
+ // connectors, egress connections and plugins, plus instructions the model reads when the bundle
5
+ // applies. A binding attaches a bundle to a scope: the whole org, one domain, one surface, or one
6
+ // principal. Nothing is reachable until a binding grants it.
7
+ //
8
+ // Start small. Add a bundle when a room needs a capability, and bind it as narrowly as you can.
9
+ // The configuration reference is docs/deploy/configuration-reference.md in the Open Everywhere repository.
10
+
11
+ import {
12
+ assertBindingsValid,
13
+ assertBundlesValid,
14
+ defineBinding,
15
+ defineBundle,
16
+ type Binding,
17
+ type Bundle,
18
+ } from "@open-everywhere/core/agent/access/bundle.js";
19
+ import { loadGrantsOverlay } from "./grants.js";
20
+ import { loadAgentsOverlay } from "../memory/config/agents-overlay.js";
21
+ import { loadGroups } from "../identity/groups.js";
22
+
23
+ const bundles: Bundle[] = [
24
+ defineBundle({
25
+ name: "org-default",
26
+ // Credential-less egress that is safe org-wide.
27
+ domains: ["raw.githubusercontent.com"],
28
+ instructions: "Never act outside the access granted to this room.",
29
+ }),
30
+ // Example — let one domain open pull requests on one repository:
31
+ //
32
+ // defineBundle({
33
+ // name: "coding",
34
+ // repos: ["your-org/your-repo"],
35
+ // domains: [...PACKAGE_REGISTRY_HOSTS], // import it from "@open-everywhere/core/agent/access/bundle.js"
36
+ // instructions: "Changes go via branch + open_pull_request; never push to main.",
37
+ // }),
38
+ //
39
+ // Example — the Notion MCP connector on the shared workspace credential:
40
+ //
41
+ // defineBundle({
42
+ // name: "notion",
43
+ // connectors: [{ name: "notion", credential: "workspace-oauth", contentOrigin: "internal" }],
44
+ // }),
45
+ ];
46
+
47
+ const bindings: Binding[] = [
48
+ defineBinding({ bundle: "org-default", scope: { level: "org" } }),
49
+ // defineBinding({ bundle: "coding", scope: { level: "domain", domain: "org" } }),
50
+ ];
51
+
52
+ // The machine-owned overlays: access requests the agent raised and an admin approved by merging a
53
+ // pull request (grants.generated.ts), and agent-authored domains (agents.generated.ts).
54
+ const overlay = loadGrantsOverlay();
55
+ const agentsOverlay = loadAgentsOverlay();
56
+ const mergedBundles: readonly Bundle[] = [...bundles, ...overlay.bundles, ...agentsOverlay.bundles];
57
+ const mergedBindings: readonly Binding[] = [...bindings, ...overlay.bindings, ...agentsOverlay.bindings];
58
+
59
+ assertBundlesValid(mergedBundles);
60
+ assertBindingsValid(mergedBindings, mergedBundles, loadGroups());
61
+
62
+ /** Code config plus the overlays. */
63
+ export function loadBundles(): readonly Bundle[] {
64
+ return mergedBundles;
65
+ }
66
+
67
+ export function loadBindings(): readonly Binding[] {
68
+ return mergedBindings;
69
+ }
70
+
71
+ /** Code config only — the base the access-request codemod writes its overlay against. */
72
+ export function loadBaseBundles(): readonly Bundle[] {
73
+ return bundles;
74
+ }
75
+
76
+ export function loadBaseBindings(): readonly Binding[] {
77
+ return bindings;
78
+ }
@@ -0,0 +1,74 @@
1
+ // Connector credential catalogues — HOW each connector authenticates.
2
+ //
3
+ // One catalogue per connector. Each credential names its type, where its secret lives
4
+ // (`secretRef` + `broker`: `env` reads an environment variable by NAME, `db` reads the encrypted
5
+ // credential store), and the endpoints it may be presented to. A bundle grant
6
+ // (agent/access/bundles.ts) picks a credential by `id`. A credential nobody grants is inert.
7
+ //
8
+ // The secret VALUES never live in this file — only the names they are stored under.
9
+
10
+ import {
11
+ assertConnectorCredentialsValid,
12
+ defineConnectorCredentials,
13
+ vercelConnectorHome,
14
+ type ConnectorCredentials,
15
+ } from "@open-everywhere/core/agent/access/connector-credentials.js";
16
+ import { applyOverridesToConnectorCredentials } from "@open-everywhere/core/agent/access/credential-bindings-codemod.js";
17
+ import { loadCredentialBindingsOverlay } from "./credential-bindings.js";
18
+
19
+ const catalogues: ConnectorCredentials[] = [
20
+ defineConnectorCredentials({
21
+ description: "Notion workspace (host-side MCP) — read and write the team knowledge base.",
22
+ connector: "notion",
23
+ allows: ["api-key", "service-oauth", "user-oauth"],
24
+ credentials: [
25
+ { id: "team-key", type: "api-key", secretRef: "db:notion:team-key", broker: "db", endpoints: { hosts: ["api.notion.com"], pathPrefixes: ["/v1/"], methods: ["GET", "POST"] } },
26
+ // The Notion CHAT surface posts its replies with this credential.
27
+ {
28
+ id: "channel-key",
29
+ type: "api-key",
30
+ secretRef: "db:notion:channel-key",
31
+ broker: "db",
32
+ intake: false,
33
+ endpoints: { hosts: ["api.notion.com"], pathPrefixes: ["/v1/"], methods: ["GET", "POST"] },
34
+ },
35
+ {
36
+ id: "memory",
37
+ type: "api-key",
38
+ secretRef: "db:notion:memory",
39
+ broker: "db",
40
+ intake: false,
41
+ endpoints: { hosts: ["api.notion.com"], pathPrefixes: ["/v1/blocks/"], methods: ["GET"] },
42
+ },
43
+ { id: "user-oauth", type: "user-oauth", home: vercelConnectorHome("mcp.notion.com/notion-mcp"), endpoints: { hosts: ["mcp.notion.com"] } },
44
+ { id: "workspace-oauth", type: "service-oauth", home: vercelConnectorHome("mcp.notion.com/notion-mcp"), endpoints: { hosts: ["mcp.notion.com"] } },
45
+ ],
46
+ }),
47
+ defineConnectorCredentials({
48
+ description: "Slack workspace — the WORK surface a granted domain's delegated workers read conversations on.",
49
+ connector: "slack",
50
+ allows: ["api-key"],
51
+ credentials: [
52
+ { id: "work-token", type: "api-key", secretRef: "SLACK_BOT_TOKEN", broker: "env", intake: false },
53
+ { id: "chat-token", type: "api-key", secretRef: "SLACK_BOT_TOKEN", broker: "env", intake: false },
54
+ ],
55
+ }),
56
+ ];
57
+
58
+ assertConnectorCredentialsValid(catalogues);
59
+
60
+ // Custody moves (env → db) land as a machine-owned overlay (credential-bindings.generated.ts).
61
+ const merged: readonly ConnectorCredentials[] = applyOverridesToConnectorCredentials(
62
+ catalogues,
63
+ loadCredentialBindingsOverlay().overrides,
64
+ );
65
+ assertConnectorCredentialsValid(merged);
66
+
67
+ export function loadConnectorCredentials(): readonly ConnectorCredentials[] {
68
+ return merged;
69
+ }
70
+
71
+ /** The code catalogues only — the base the credential-bindings codemod writes against. */
72
+ export function loadBaseConnectorCredentials(): readonly ConnectorCredentials[] {
73
+ return catalogues;
74
+ }
@@ -0,0 +1,17 @@
1
+ // GENERATED + MACHINE-OWNED — DO NOT HAND-EDIT.
2
+ //
3
+ // The credential-bindings overlay: DATA ONLY. Written exclusively by the deterministic codemod
4
+ // (packages/core/agent/access/credential-bindings-codemod.ts) on a bot-PR branch; the PR merge is the approval gate.
5
+ //
6
+ // The codemod re-emits this file from its own parsed content and refuses to rewrite it when the
7
+ // bytes differ, so a hand-edit stops the next machine PR. Before a merge, nothing checks that the
8
+ // file is still data only unless the repository runs its own check: the person who merges the PR
9
+ // is the gate. Read a PR to this file as code, because it is code.
10
+ //
11
+ // To change it by hand, edit only the data below and keep the layout exactly as emitted.
12
+
13
+ import type { CredentialBindingsFile } from "@open-everywhere/core/agent/access/credential-bindings-codemod.js";
14
+
15
+ export default {
16
+ "overrides": []
17
+ } satisfies CredentialBindingsFile;
@@ -0,0 +1,28 @@
1
+ // The credential-bindings OVERLAY loader — the runtime half of the stage-5 credential-binding flow
2
+ // (task #113; docs/security/credentials-and-planes.md in the Open Everywhere repository). `credential-bindings.generated.ts` (next to
3
+ // this file) is a MACHINE-OWNED, DATA-ONLY overlay of custody re-stamps (connector credential → new
4
+ // broker + secretRef) that merges over the config-as-code catalogue set in connector-credentials.ts
5
+ // at load. Each override names its `connector` and `credential` (the codemod refuses the old
6
+ // `provider` spelling at load — OPE-598). Nothing edits it by hand in the normal flow: the deterministic codemod (core
7
+ // access/credential-bindings-codemod.ts) writes it on a `credential-binding/<connector>-<credential>`
8
+ // branch, a bot-PR carries the diff, and GitHub's merge button — with CODEOWNERS on this path +
9
+ // branch protection — is the approval gate.
10
+ //
11
+ // WHY a static import (not an fs read): Eve's bundler inlines the module into the deploy artifact,
12
+ // so the overlay can never be silently absent at runtime. Parsing is STRICT + fail-loud
13
+ // (parseCredentialBindingsFile): a malformed overlay refuses to boot rather than loading half-right,
14
+ // and Vercel keeps the previous deployment live on a failed build/boot (the build-then-promote
15
+ // backstop) — worst case a red pipeline, never a downed agent. Mirrors grants.ts exactly, including
16
+ // the reason the DATA lives in a `.ts` module (OPE-256): one authoring language. Nothing in this
17
+ // app checks that the file stays data only: the person who merges the codemod's PR is the gate.
18
+
19
+ import { parseCredentialBindingsFile, type CredentialBindingsFile } from "@open-everywhere/core/agent/access/credential-bindings-codemod.js";
20
+ import bindingsOverlay from "./credential-bindings.generated.js";
21
+
22
+ const overlay: CredentialBindingsFile = parseCredentialBindingsFile(bindingsOverlay);
23
+
24
+ /** The parsed credential-bindings overlay — merged over loadConnectorCredentials() by
25
+ * connector-credentials.ts. */
26
+ export function loadCredentialBindingsOverlay(): CredentialBindingsFile {
27
+ return overlay;
28
+ }
@@ -0,0 +1,18 @@
1
+ // GENERATED + MACHINE-OWNED — DO NOT HAND-EDIT.
2
+ //
3
+ // The grants overlay: DATA ONLY. Written exclusively by the deterministic codemod
4
+ // (packages/core/agent/access/grants-codemod.ts) on a bot-PR branch; the PR merge is the approval gate.
5
+ //
6
+ // The codemod re-emits this file from its own parsed content and refuses to rewrite it when the
7
+ // bytes differ, so a hand-edit stops the next machine PR. Before a merge, nothing checks that the
8
+ // file is still data only unless the repository runs its own check: the person who merges the PR
9
+ // is the gate. Read a PR to this file as code, because it is code.
10
+ //
11
+ // To change it by hand, edit only the data below and keep the layout exactly as emitted.
12
+
13
+ import type { GrantsFile } from "@open-everywhere/core/agent/access/grants-codemod.js";
14
+
15
+ export default {
16
+ "bundles": [],
17
+ "bindings": []
18
+ } satisfies GrantsFile;
@@ -0,0 +1,30 @@
1
+ // The grants OVERLAY loader — the runtime half of the access-request flow's widen path
2
+ // (docs/how-it-works/access-requests.md in the Open Everywhere repository). `grants.generated.ts` (next to this file) is a
3
+ // MACHINE-OWNED, DATA-ONLY overlay of extra bundles + channel-scoped bindings that merges
4
+ // into the config-as-code baseline in bundles.ts at load. Nothing edits it by hand in the
5
+ // normal flow: the deterministic codemod (core access/grants-codemod.ts) writes it on an
6
+ // `access-request/<id>` branch, a bot-PR carries the diff, and GitHub's merge button is
7
+ // the approval gate.
8
+ //
9
+ // WHY a static import (not an fs read): Eve's bundler inlines the module into the deploy
10
+ // artifact, so the overlay can never be silently absent at runtime the way a stripped file
11
+ // path could be — a grant that merged is a grant that ships. Parsing is STRICT + fail-loud
12
+ // (parseGrantsFile): a malformed overlay (a bad merge) refuses to boot rather than loading
13
+ // as "roughly right" — and Vercel keeps the previous deployment live on a failed build/boot,
14
+ // so the worst case is a red pipeline, never a downed agent (the build-then-promote backstop).
15
+ //
16
+ // WHY THE DATA LIVES IN A `.ts` MODULE AND NOT JSON (OPE-256): one authoring language for the
17
+ // whole deployer config. The split that is load-bearing is not JSON-vs-TS but MACHINE-OWNED vs
18
+ // human-authored. A module IS executable at load, and this parse only sees the value, so this
19
+ // loader does not prove the file is data. In this app no check before the merge does: whoever merges the
20
+ // codemod's PR is the only gate on the file staying data only. Review that PR as code.
21
+
22
+ import { parseGrantsFile, type GrantsFile } from "@open-everywhere/core/agent/access/grants-codemod.js";
23
+ import grantsOverlay from "./grants.generated.js";
24
+
25
+ const overlay: GrantsFile = parseGrantsFile(grantsOverlay);
26
+
27
+ /** The parsed grants overlay — merged into loadBundles()/loadBindings() by bundles.ts. */
28
+ export function loadGrantsOverlay(): GrantsFile {
29
+ return overlay;
30
+ }
@@ -0,0 +1,32 @@
1
+ // Network-access LEVEL configuration — config-as-code (OPE-509), the "environment" axis
2
+ // alongside bundles.ts's capability axis. Declare a named level ("trusted" | "none" | "full")
3
+ // per scope on the container ladder (org < tenant < domain < surface); an unpinned scope
4
+ // inherits the nearest wider pin, and a scope no pin reaches at all resolves "none" — the same
5
+ // egress posture every scope already has today. See docs/how-it-works/access-and-scoping.md in the Open Everywhere repository
6
+ // (§ Network-access levels) and packages/core/agent/access/network-access.ts for the resolution
7
+ // doctrine and exactly what "trusted" opens.
8
+ //
9
+ // LEFT EMPTY, DELIBERATELY. This ticket adds the CONFIG SHAPE — it does not widen this
10
+ // deployment's live egress by default. The commented example below shows the intended usage:
11
+ // it would let the "coding" bundle in agent/access/bundles.ts drop its hand-copied
12
+ // `domains: [...PACKAGE_REGISTRY_HOSTS]` line in favour of a reviewable, named level, but
13
+ // switching that bundle over is a separate, deliberate config change — not a side effect of
14
+ // shipping the mechanism.
15
+
16
+ import { assertNetworkAccessValid, defineNetworkAccess, type NetworkAccessBinding } from "@open-everywhere/core/agent/access/network-access.js";
17
+
18
+ const networkAccess: NetworkAccessBinding[] = [
19
+ // Example: give the coding channel the curated "trusted" set (package registries +
20
+ // developer hosts) instead of the hand-copied PACKAGE_REGISTRY_HOSTS domains grant on the
21
+ // "coding" bundle in ../access/bundles.ts — same effective egress, named and reviewable at
22
+ // the scope instead of buried in a bundle's host list.
23
+ // defineNetworkAccess({ scope: { level: "surface", surface: "slack/<team-id>/<channel-id>" }, level: "trusted" }),
24
+ ];
25
+
26
+ // Validate at module load (fail loud), same doctrine as bundles.ts's assertBundlesValid.
27
+ assertNetworkAccessValid(networkAccess);
28
+
29
+ /** The seam a DB/dashboard would swap behind, same shape as bundles.ts's loadBundles(). */
30
+ export function loadNetworkAccess(): readonly NetworkAccessBinding[] {
31
+ return networkAccess;
32
+ }