create-open-autonomy 2.3.0 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -68,6 +68,13 @@ and settled cent per item, live while a session runs.
68
68
  balance for a bounded amount at the owner's merchant categories, and a partner service's metered charge
69
69
  for a listed partner within a bound. Both leave records on the public audit trail naming the rail.
70
70
 
71
+ ## The world
72
+
73
+ A project that talks to a vendor verifies against twins, never the vendor: `bun add -d @volter/twin-world @volter/twin-<vendor>`,
74
+ a `world/world.json` naming them, `bunx volter-world up world/world.json`. The develop skill drives the running
75
+ system in that world, one action at a time (this repository's own `world/` is the shape). Nothing an agent does
76
+ reaches a real API.
77
+
71
78
  ## Nothing in the agent's reach is a secret that matters
72
79
 
73
80
  The agent's `.env` says `OPEN_AUTONOMY_KEY=valve`. Pushes sign through an ssh-agent the start script loads with
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-open-autonomy",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "The default Open Autonomy starter kit: a complete repository that runs its own Hermes agent against the platform, with the SDK wired in. `bun create open-autonomy <dir>` scaffolds one.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
package/src/kit.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
10
10
  import { dirname, join, relative, resolve } from 'node:path';
11
11
 
12
- export const KIT = { name: 'hermes', version: '2.3.0' } as const;
12
+ export const KIT = { name: 'hermes', version: '2.3.2' } as const;
13
13
  export const KIT_FILE = '.open-autonomy/kit.json';
14
14
  const TEMPLATE = resolve(import.meta.dir, '..', 'template');
15
15
 
@@ -4,7 +4,7 @@
4
4
  // in a container the image's entrypoint, as root with the secrets mounted for root alone and `--as <user>`:
5
5
  // the gateway and the reporter run as that user and can reach no key (container/README.md)
6
6
  //
7
- // bun .open-autonomy/start.ts [--home <dir>] [--secrets <dir>] [--project <dir>] [--origin <url>] [--as <user>]
7
+ // bun .open-autonomy/start.ts [--home <dir>] [--secrets <dir>] [--project <dir>] [--origin <url>] [--as <user>] [--valve <port>]
8
8
  //
9
9
  // The processes, in order:
10
10
  // ssh-agent holds <secrets>/deploy_key, its socket at <home>/ssh-agent.sock; the gateway pushes through it
@@ -13,7 +13,8 @@
13
13
  // the home hermes/ in the checkout copied into <home> before every start — the repository is the source of
14
14
  // truth for what the agent IS; the home keeps what it has since done (its .env is kept)
15
15
  // valve <secrets>/agent.env on :8787 (the developer's key), <secrets>/treasurer.env on :8788 (the
16
- // treasurer's, the only one that pays) the agent's .env names the valve and the word `valve`
16
+ // treasurer's, the only one that pays); --valve moves both (the second is the next port) for a second
17
+ // agent on one host — the home's .env names them (OPEN_AUTONOMY_BASE_URL, OPEN_AUTONOMY_PAY_URL) and the word `valve`
17
18
  // reporter keyless, publishing the home's sessions and board through the valve
18
19
  // gateway `hermes gateway run` in the checkout, HERMES_HOME=<home>
19
20
  // When any of them ends, all of them end and this exits 1: the supervisor outside (you, launchd, Docker) restarts.
@@ -28,6 +29,9 @@ const home = resolve(arg('--home') ?? process.env.AGENT_HOME ?? resolve(homedir(
28
29
  const secrets = resolve(arg('--secrets') ?? process.env.AGENT_SECRETS ?? resolve(homedir(), '.config', 'open-autonomy'));
29
30
  const origin = arg('--origin') ?? process.env.ORIGIN;
30
31
  const as = arg('--as');
32
+ const valvePort = Number(arg('--valve') ?? process.env.VALVE_PORT ?? 8787);
33
+ const baseUrl = `http://127.0.0.1:${valvePort}/v1`;
34
+ const payUrl = `http://127.0.0.1:${valvePort + 1}/v1`;
31
35
  const say = (m: string) => console.log(`start: ${m}`);
32
36
  const sock = resolve(home, 'ssh-agent.sock');
33
37
 
@@ -75,20 +79,20 @@ if (!existsSync(resolve(project, '.git'))) {
75
79
  const committed = resolve(project, 'hermes');
76
80
  // force: with a filter, Bun's cpSync leaves an existing file alone unless told to overwrite.
77
81
  if (existsSync(committed)) cpSync(committed, home, { recursive: true, force: true, filter: (src) => basename(src) !== '.env' });
82
+ // The home's .env is the home's own, except the valve's three lines, which are this start's truth on every start.
78
83
  const envFile = resolve(home, '.env');
79
- if (!existsSync(envFile)) {
80
- const lines = ['OPEN_AUTONOMY_BASE_URL=http://127.0.0.1:8787/v1', 'OPEN_AUTONOMY_KEY=valve'];
81
- // What the environment says about the agent's channels comes along: Discord's, and GitHub's for a community desk.
82
- for (const k of Object.keys(process.env).sort()) if (/^(DISCORD_|GITHUB_TOKEN$|GITHUB_API_URL$)/.test(k) && process.env[k]) lines.push(`${k}=${process.env[k]}`);
83
- writeFileSync(envFile, `${lines.join('\n')}\n`);
84
- }
84
+ const kept = existsSync(envFile) ? readFileSync(envFile, 'utf8').split('\n').filter((l) => l.trim() && !/^OPEN_AUTONOMY_(BASE_URL|PAY_URL|KEY)=/.test(l)) : [];
85
+ const lines = [`OPEN_AUTONOMY_BASE_URL=${baseUrl}`, `OPEN_AUTONOMY_PAY_URL=${payUrl}`, 'OPEN_AUTONOMY_KEY=valve', ...kept];
86
+ // On the first start, what the environment says about the agent's channels comes along: Discord's, and GitHub's for a community desk.
87
+ if (!existsSync(envFile)) for (const k of Object.keys(process.env).sort()) if (/^(DISCORD_|GITHUB_TOKEN$|GITHUB_API_URL$)/.test(k) && process.env[k]) lines.push(`${k}=${process.env[k]}`);
88
+ writeFileSync(envFile, `${lines.join('\n')}\n`);
85
89
  own(home);
86
90
  say(`home ${home} synced from ${committed}`);
87
91
 
88
92
  // 4. The valve: one key file per port; a missing developer's key is the one thing that stops the start.
89
93
  const keys: string[] = [];
90
- if (existsSync(resolve(secrets, 'agent.env'))) keys.push('--key', `${resolve(secrets, 'agent.env')}:8787`);
91
- if (existsSync(resolve(secrets, 'treasurer.env'))) keys.push('--key', `${resolve(secrets, 'treasurer.env')}:8788`);
94
+ if (existsSync(resolve(secrets, 'agent.env'))) keys.push('--key', `${resolve(secrets, 'agent.env')}:${valvePort}`);
95
+ if (existsSync(resolve(secrets, 'treasurer.env'))) keys.push('--key', `${resolve(secrets, 'treasurer.env')}:${valvePort + 1}`);
92
96
  if (!keys.length) { console.error(`start: no ${resolve(secrets, 'agent.env')} — mint the developer's key: bun .open-autonomy/mint-key.ts`); process.exit(1); }
93
97
  if (user) {
94
98
  // The whole point of --as: the agent's user must not be able to read a key.
@@ -101,8 +105,8 @@ spawn('valve', ['bun', resolve(import.meta.dir, 'sdk', 'valve.ts'), ...keys], {}
101
105
 
102
106
  // 5. The reporter and the gateway, as the agent.
103
107
  const env = agentEnv();
104
- spawn('reporter', ['bun', resolve(import.meta.dir, 'reporter.ts'), '--config', resolve(project, '.open-autonomy', 'config.yaml')], { asAgent: true, env: { ...env, OPEN_AUTONOMY_BASE_URL: 'http://127.0.0.1:8787/v1' } });
108
+ spawn('reporter', ['bun', resolve(import.meta.dir, 'reporter.ts'), '--config', resolve(project, '.open-autonomy', 'config.yaml')], { asAgent: true, env: { ...env, OPEN_AUTONOMY_BASE_URL: baseUrl } });
105
109
  spawn('gateway', ['hermes', 'gateway', 'run'], { asAgent: true, env });
106
- say(`gateway up in ${project} as ${user?.name ?? userInfo().username}, home ${home}; the valve on :8787${keys.length > 2 ? ' and :8788' : ''}`);
110
+ say(`gateway up in ${project} as ${user?.name ?? userInfo().username}, home ${home}; the valve on :${valvePort}${keys.length > 2 ? ` and :${valvePort + 1}` : ''}`);
107
111
  if (!readFileSync(resolve(project, '.open-autonomy', 'config.yaml'), 'utf8').includes('account:')) say('warning: .open-autonomy/config.yaml names no account');
108
112
  await new Promise(() => {});
@@ -2,7 +2,7 @@
2
2
 
3
3
  - **What this is.** __PROJECT__, a project that builds itself through Open Autonomy. `CONSTITUTION.md` is what it is and must remain. The board is what you build next, in order. `CONTRIBUTING.md` is how code is written here. `hermes/` is you.
4
4
  - **Checks.** `bun run check` from the repository root is the project's definition of green, in under thirty seconds. It must pass before every push. Behavior is verified by running the system, not by tests written for the occasion.
5
- - **Verify.** State here where the project is verified: its check, and any local or twinned surface. You cannot reach production and must not try. Where an acceptance line names a surface, exercise the surface.
5
+ - **Verify.** State here where the project is verified: its check, and any local or twinned surface. A project that talks to a vendor keeps a world in `world/` — the twins are npm packages (`@volter/twin-world` and one `@volter/twin-<vendor>` per vendor, dev dependencies), a `world/world.json` names them, `bunx volter-world up world/world.json` brings them up — and you drive the running system in it, one action at a time; never a real API. You cannot reach production and must not try. Where an acceptance line names a surface, exercise the surface.
6
6
  - **Git.** You cannot push to `main` and must not try. Work on `agent/<task id>` off a fresh `origin/main`, commit small with the task id first in the subject, and push the branch; the landing workflow opens the pull request and it merges itself when the checks pass. Never rewrite history, never force-push.
7
7
  - **Secrets.** There are none for you to use: your model calls and your pushes are authorized outside your reach. Never read or print `.env` files or key material; your sessions are published live.
8
8
  - **Do not edit** `LICENSE`, `.github/workflows/`, `container/`, `.open-autonomy/reporter.ts`, or anything under `hermes/` except a skill a task asks you to improve.
@@ -1,7 +1,7 @@
1
1
  # Running the agent
2
2
 
3
3
  The agent is four processes: an ssh-agent holding the deploy key, the valve holding the project's keys (the
4
- developer's on :8787, the treasurer's on :8788, each re-read when its file changes), the keyless reporter, and
4
+ developer's on :8787, the treasurer's on :8788 — `--valve <port>` moves both — each re-read when its file changes), the keyless reporter, and
5
5
  the Hermes gateway. One script starts them, `.open-autonomy/start.ts`, and it is the only way they are started.
6
6
 
7
7
  **On your machine**, for development: everything as you, no isolation.
@@ -4,7 +4,7 @@ For each request, in order:
4
4
 
5
5
  1. `kanban_show` it and read the owner's bounds in `.open-autonomy/config.yaml` (`rails:`). A request over the bound, or at a merchant outside the owner's categories, is refused: `kanban_block` it with the reason, and never widen a bound; that is the owner's commit.
6
6
  2. Mint the card through your valve, naming the developer's task so the purchase shows on its page:
7
- `curl -sf -X POST http://127.0.0.1:8788/v1/rails/card -H 'authorization: Bearer valve' -H 'content-type: application/json' -d '{"usd_cents": <ceiling>, "purpose": "<why>", "item": "<the developer's task id>"}'`.
7
+ `curl -sf -X POST $OPEN_AUTONOMY_PAY_URL/rails/card -H 'authorization: Bearer valve' -H 'content-type: application/json' -d '{"usd_cents": <ceiling>, "purpose": "<why>", "item": "<the developer's task id>"}'`.
8
8
  The answer carries the card. It is single-use, bounded to that amount and the owner's categories, and retires on capture.
9
9
  3. Pay the merchant yourself, the way the request says. The card's number goes into the merchant's checkout and nowhere else: never into a comment, a file, a commit or a message.
10
10
  4. Record the receipt on the developer's task and release it: `hermes kanban comment <developer task> "RECEIPT: <what> at <merchant>, $<amount> on card ····<last4>"`, then `hermes kanban unblock <developer task>` (the terminal's shell has neither the home nor the binary on its path; both are named in full).
@@ -6,7 +6,7 @@
6
6
  model:
7
7
  default: zai/glm-5.3-flash
8
8
  provider: custom
9
- base_url: http://127.0.0.1:8788/v1 # the valve's second port: the treasurer's key, the only one that pays
9
+ base_url: ${OPEN_AUTONOMY_PAY_URL} # the valve's second port (the start script writes it into the home's .env): the treasurer's key, the only one that pays
10
10
  api_key: valve
11
11
  api_mode: chat_completions
12
12