create-open-autonomy 2.3.4 → 2.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -31,7 +31,7 @@ LICENSE Apache-2.0, seeded; the project's own
31
31
  package.json, test/ the project's own check (`bun run check`), starting with one test
32
32
  hermes/ the agent: SOUL.md, its two skills (develop, pm; a project's own skills live beside them, in hermes/skills/<project>/, and are the project's), profiles/treasurer (the second profile: the one that pays), kanban.seed.json (the board's first tasks, in order),
33
33
  cron/jobs.seed.json (the PM, hourly), config.yaml (the model: the project's own choice), the seed hook
34
- .open-autonomy/ the platform connection: config.yaml (account, publish policy, rail bounds), reporter.ts (the publisher:
34
+ .open-autonomy/ the platform connection: config.yaml (account, publish policy, the model and rail bounds the platform holds the project's funds to), reporter.ts (the publisher:
35
35
  sessions, the board, the setup), mint-key.ts (the key, the adopter way), start.ts (the agent's four
36
36
  processes, the one way it starts), the vendored SDK, kit.json (which kit, version and parameters made this repository)
37
37
  container/ a real setup: one image whose entrypoint is start.ts as root, dropping the gateway to the image's user; the pinned Hermes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-open-autonomy",
3
- "version": "2.3.4",
3
+ "version": "2.3.6",
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": {
@@ -28,7 +28,7 @@
28
28
  "check": "bunx tsc --noEmit"
29
29
  },
30
30
  "dependencies": {
31
- "@open-autonomy/sdk": "2.2.0"
31
+ "@open-autonomy/sdk": "2.3.1"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/bun": "^1.3.10",
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.4' } as const;
12
+ export const KIT = { name: 'hermes', version: '2.3.6' } as const;
13
13
  export const KIT_FILE = '.open-autonomy/kit.json';
14
14
  const TEMPLATE = resolve(import.meta.dir, '..', 'template');
15
15
 
@@ -17,9 +17,9 @@ export interface KitParams { project: string; account: string }
17
17
  export interface KitRecord { kit: string; version: string; params: KitParams; divergences: string[] }
18
18
 
19
19
  // What the kit keeps current. Everything else in the template is seeded once.
20
- // A project's own, seeded once: its config, its board seed, its schedule, and any skill of its own outside
21
- // hermes/skills/open-autonomy/ (the kit's two).
22
- const OWNED = [/^hermes\/(?!config\.yaml$|kanban\.seed\.json$|cron\/jobs\.seed\.json$|skills\/(?!open-autonomy\/))/, /^\.open-autonomy\/(reporter\.ts|mint-key\.ts|start\.ts|package\.json|sdk\/)/, /^container\//, /^\.github\/workflows\/(ci|land)\.yml$/];
20
+ // A project's own, seeded once: its config (the treasurer's too: the model is the project's choice for both profiles),
21
+ // its board seed, its schedule, and any skill of its own outside hermes/skills/open-autonomy/ (the kit's two).
22
+ const OWNED = [/^hermes\/(?!config\.yaml$|kanban\.seed\.json$|cron\/jobs\.seed\.json$|profiles\/treasurer\/config\.yaml$|skills\/(?!open-autonomy\/))/, /^\.open-autonomy\/(reporter\.ts|mint-key\.ts|start\.ts|package\.json|sdk\/)/, /^container\//, /^\.github\/workflows\/(ci|land)\.yml$/];
23
23
  export const isOwned = (rel: string): boolean => OWNED.some((re) => re.test(rel));
24
24
 
25
25
  export function validateParams(p: Partial<KitParams>): KitParams {
@@ -15,6 +15,15 @@ publish:
15
15
  # environment), and where it keeps its own cursor (beside this file).
16
16
  state_file: reporter-state.json
17
17
 
18
+ # The models the project's funds may buy on the platform's model rail: a bound the platform reads from this file and
19
+ # holds whatever a key was minted with. Empty: any model the gateway serves (`GET /v1/catalog` lists them).
20
+ models: [zai/glm-5.3-flash]
21
+
22
+ # The most the project's funds may spend on the platform's model rail in a day (UTC): a bound the platform holds at every
23
+ # call, so a runaway stops at the owner's number. 0: no bound of the project's own beyond the platform's global rail.
24
+ spend:
25
+ daily_usd_cents: 500
26
+
18
27
  # The rails the agent may spend through beyond model calls, and the owner's bounds on each. Off unless a
19
28
  # bound is set. Every rail leaves a record on the public audit trail naming itself.
20
29
  rails:
@@ -6,6 +6,11 @@
6
6
  //
7
7
  // bun .open-autonomy/start.ts [--home <dir>] [--secrets <dir>] [--project <dir>] [--origin <url>] [--as <user>] [--valve <port>]
8
8
  //
9
+ // <secrets>/codex.json, when present, is the owner's ChatGPT/Codex subscription login (the Codex CLI's auth.json
10
+ // tokens): the valve serves it on the third port, and the home's .env names it (HERMES_CODEX_BASE_URL) for a custom
11
+ // provider in the project's config that speaks the Codex protocol — the model runs on the subscription, the login
12
+ // never enters the agent.
13
+ //
9
14
  // The processes, in order:
10
15
  // ssh-agent holds <secrets>/deploy_key, its socket at <home>/ssh-agent.sock; the gateway pushes through it
11
16
  // and never holds the key (absent: pushes are your own git's business)
@@ -89,8 +94,11 @@ if (existsSync(committed)) {
89
94
  }
90
95
  // The home's .env is the home's own, except the valve's three lines, which are this start's truth on every start.
91
96
  const envFile = resolve(home, '.env');
92
- const kept = existsSync(envFile) ? readFileSync(envFile, 'utf8').split('\n').filter((l) => l.trim() && !/^OPEN_AUTONOMY_(BASE_URL|PAY_URL|KEY)=/.test(l)) : [];
93
- const lines = [`OPEN_AUTONOMY_BASE_URL=${baseUrl}`, `OPEN_AUTONOMY_PAY_URL=${payUrl}`, 'OPEN_AUTONOMY_KEY=valve', ...kept];
97
+ const kept = existsSync(envFile) ? readFileSync(envFile, 'utf8').split('\n').filter((l) => l.trim() && !/^(OPEN_AUTONOMY_(BASE_URL|PAY_URL|KEY)|HERMES_CODEX_BASE_URL)=/.test(l)) : [];
98
+ // The Codex subscription's address goes in the .env too: Hermes loads the home's .env into every process it starts,
99
+ // including the scheduler's job runners, which do not inherit the gateway's environment.
100
+ const codexBase = existsSync(resolve(secrets, 'codex.json')) ? [`HERMES_CODEX_BASE_URL=http://127.0.0.1:${valvePort + 2}/backend-api/codex`] : [];
101
+ const lines = [`OPEN_AUTONOMY_BASE_URL=${baseUrl}`, `OPEN_AUTONOMY_PAY_URL=${payUrl}`, 'OPEN_AUTONOMY_KEY=valve', ...codexBase, ...kept];
94
102
  // On the first start, what the environment says about the agent's channels comes along: Discord's, and GitHub's for a community desk.
95
103
  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]}`);
96
104
  writeFileSync(envFile, `${lines.join('\n')}\n`);
@@ -102,19 +110,17 @@ const keys: string[] = [];
102
110
  if (existsSync(resolve(secrets, 'agent.env'))) keys.push('--key', `${resolve(secrets, 'agent.env')}:${valvePort}`);
103
111
  if (existsSync(resolve(secrets, 'treasurer.env'))) keys.push('--key', `${resolve(secrets, 'treasurer.env')}:${valvePort + 1}`);
104
112
  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); }
105
- if (user) {
106
- // The whole point of --as: the agent's user must not be able to read a key.
107
- const peek = Bun.spawnSync({ cmd: drop(['cat', resolve(secrets, 'agent.env')]), stdout: 'pipe', stderr: 'pipe' });
108
- if (peek.exitCode === 0) { console.error(`start: ${resolve(secrets, 'agent.env')} is readable by ${user.name}; the secrets must belong to root alone`); process.exit(1); }
109
- }
110
- // The valve and the reporter run from this script's own directory (its node_modules, its vendored SDK): in a
111
- // container that is the image's copy, and the checkout only has to be the project.
113
+ // The Codex subscription: the valve holds the login and serves it on the third port; Hermes reaches it as a named
114
+ // custom provider speaking the Codex protocol (base_url ${HERMES_CODEX_BASE_URL}, api_key `valve`), which the home's .env names.
115
+ const codexFile = resolve(secrets, 'codex.json');
116
+ const codexPort = valvePort + 2;
117
+ if (existsSync(codexFile)) keys.push('--codex', `${codexFile}:${codexPort}`);
112
118
  spawn('valve', ['bun', resolve(import.meta.dir, 'sdk', 'valve.ts'), ...keys], {});
113
119
 
114
120
  // 5. The reporter and the gateway, as the agent.
115
121
  const env = agentEnv();
116
122
  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 } });
117
123
  spawn('gateway', ['hermes', 'gateway', 'run'], { asAgent: true, env });
118
- say(`gateway up in ${project} as ${user?.name ?? userInfo().username}, home ${home}; the valve on :${valvePort}${keys.length > 2 ? ` and :${valvePort + 1}` : ''}`);
124
+ say(`gateway up in ${project} as ${user?.name ?? userInfo().username}, home ${home}; the valve on :${valvePort}${existsSync(resolve(secrets, 'treasurer.env')) ? ` and :${valvePort + 1}` : ''}${existsSync(codexFile) ? `; the Codex subscription on :${codexPort}` : ''}`);
119
125
  if (!readFileSync(resolve(project, '.open-autonomy', 'config.yaml'), 'utf8').includes('account:')) say('warning: .open-autonomy/config.yaml names no account');
120
126
  await new Promise(() => {});