create-open-autonomy 2.1.0 → 2.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-open-autonomy",
3
- "version": "2.1.0",
3
+ "version": "2.1.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": {
@@ -29,7 +29,7 @@
29
29
  "check": "bun test test/*.test.ts && bunx tsc --noEmit"
30
30
  },
31
31
  "dependencies": {
32
- "@open-autonomy/sdk": "2.0.0"
32
+ "@open-autonomy/sdk": "2.1.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@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.1.0' } as const;
12
+ export const KIT = { name: 'hermes', version: '2.1.2' } as const;
13
13
  export const KIT_FILE = '.open-autonomy/kit.json';
14
14
  const TEMPLATE = resolve(import.meta.dir, '..', 'template');
15
15
 
@@ -47,8 +47,10 @@ export function render(params: KitParams): Map<string, Buffer> {
47
47
  for (const rel of walk(TEMPLATE)) {
48
48
  const raw = readFileSync(join(TEMPLATE, rel));
49
49
  const text = raw.toString('utf8');
50
+ // The template ships its gitignore as `_gitignore`: a `.gitignore` never survives npm's pack rules.
51
+ const out_rel = rel === '_gitignore' ? '.gitignore' : rel;
50
52
  const rendered = /[\x00]/.test(text) ? raw : Buffer.from(text.replaceAll('__PROJECT__', params.project).replaceAll('__ACCOUNT_ENC__', encodeURIComponent(params.account)).replaceAll('__ACCOUNT__', params.account));
51
- out.set(rel, rendered);
53
+ out.set(out_rel, rendered);
52
54
  }
53
55
  for (const f of SDK_FILES) out.set(`.open-autonomy/sdk/${f}`, readFileSync(join(SDK_SRC, f)));
54
56
  out.set(KIT_FILE, Buffer.from(`${JSON.stringify({ kit: KIT.name, version: KIT.version, params, divergences: [] } satisfies KitRecord, null, 2)}\n`));
@@ -2,14 +2,14 @@
2
2
  // The host, set up by one command, idempotently: what container/README.md asks of the owner before
3
3
  // `docker compose up`, done or found done, and what it cannot do said plainly. Safe to run again.
4
4
  //
5
- // bun .open-autonomy/setup.ts [--context <docker context>] [--secrets <dir>] [--origin <url>]
5
+ // bun .open-autonomy/setup.ts [--context <docker context>] [--secrets <dir>] [--origin <url>] [--stack <name>]
6
6
  // [--origin-in-container <url>] [--env KEY=VALUE ...] [--uid N --gid N] [--fresh]
7
7
  //
8
8
  // 1. the key files <secrets>/agent.env (the developer's: spend + narrate) and <secrets>/treasurer.env (the
9
9
  // treasurer's: spend + narrate + pay), default ~/.config/open-autonomy, from mint-key.ts — found or named
10
10
  // 2. the image hermes-agent:<tag> from container/hermes.pin — present, copied from another Docker host
11
11
  // that has it, or built (container/build-hermes.sh, ~10 minutes)
12
- // 3. the volumes oa-home from hermes/ (its .env: the valve's address, the dummy key, every --env), oa-repo
12
+ // 3. the volumes <stack>-home from hermes/ (its .env: the valve's address, the dummy key, every --env), oa-repo
13
13
  // a clone of --origin (default: this repository's origin, cloned with your own git and
14
14
  // keys); --fresh recreates both
15
15
  // 4. what is yours the deploy key and the ssh-agent that forwards it, the Discord token; then compose up
@@ -28,6 +28,9 @@ const secrets = resolve(arg('--secrets') ?? join(homedir(), '.config', 'open-aut
28
28
  const uid = arg('--uid') ?? String(process.getuid?.() ?? 501);
29
29
  const gid = arg('--gid') ?? String(process.getgid?.() ?? 20);
30
30
  const fresh = argv.includes('--fresh');
31
+ // The stack's name on a Docker host shared with others (the containers and volumes carry it); `oa` alone otherwise.
32
+ const stack = arg('--stack') ?? 'oa';
33
+ const HOME_VOL = `${stack}-home`; const REPO_VOL = `${stack}-repo`;
31
34
  const docker = ['docker', ...(context ? ['--context', context] : [])];
32
35
  const say = (m: string) => console.log(`setup: ${m}`);
33
36
  const run = (cmd: string[], opts: { quiet?: boolean; check?: boolean; env?: Record<string, string>; cwd?: string } = {}) => {
@@ -63,16 +66,16 @@ else {
63
66
 
64
67
  // 3. The volumes.
65
68
  const have = (v: string) => run([...docker, 'volume', 'inspect', v], { quiet: true, check: false }).code === 0;
66
- if (fresh) for (const v of ['oa-home', 'oa-repo']) run([...docker, 'volume', 'rm', '-f', v], { quiet: true, check: false });
67
- if (have('oa-home') && have('oa-repo')) say('volumes: oa-home and oa-repo present (compose re-syncs the home from hermes/ on every start; --fresh recreates both)');
69
+ if (fresh) for (const v of [HOME_VOL, REPO_VOL]) run([...docker, 'volume', 'rm', '-f', v], { quiet: true, check: false });
70
+ if (have(HOME_VOL) && have(REPO_VOL)) say(`volumes: ${HOME_VOL} and ${REPO_VOL} present (compose re-syncs the home from hermes/ on every start; --fresh recreates both)`);
68
71
  else {
69
72
  const origin = arg('--origin') ?? run(['git', 'remote', 'get-url', 'origin'], { quiet: true }).out.trim();
70
73
  const originInside = arg('--origin-in-container') ?? origin;
71
- for (const v of ['oa-home', 'oa-repo']) if (!have(v)) run([...docker, 'volume', 'create', v], { quiet: true });
74
+ for (const v of [HOME_VOL, REPO_VOL]) if (!have(v)) run([...docker, 'volume', 'create', v], { quiet: true });
72
75
  const env = [`OPEN_AUTONOMY_BASE_URL=http://valve:8787/v1`, `OPEN_AUTONOMY_KEY=valve`, ...args('--env')];
73
- run([...docker, 'run', '--rm', '-v', 'oa-home:/opt/data', '-v', `${join(here, 'hermes')}:/src:ro`, 'alpine:3', 'sh', '-c',
76
+ run([...docker, 'run', '--rm', '-v', `${HOME_VOL}:/opt/data`, '-v', `${join(here, 'hermes')}:/src:ro`, 'alpine:3', 'sh', '-c',
74
77
  `cp -a /src/. /opt/data/ && printf '%s\\n' ${env.map((e) => `'${e.replace(/'/g, "'\\''")}'`).join(' ')} > /opt/data/.env && chown -R ${uid}:${gid} /opt/data`], { quiet: true });
75
- say(`home: oa-home seeded from hermes/ (.env: the valve's address, the dummy key${args('--env').length ? `, ${args('--env').map((e) => e.split('=')[0]).join(', ')}` : ''})`);
78
+ say(`home: ${HOME_VOL} seeded from hermes/ (.env: the valve's address, the dummy key${args('--env').length ? `, ${args('--env').map((e) => e.split('=')[0]).join(', ')}` : ''})`);
76
79
  // The clone is made on the host with your own git (and so your own keys), then carried into the volume through a
77
80
  // directory under your home: a Docker host mounts the home directory, not the system's temporary one.
78
81
  mkdirSync(join(homedir(), '.config', 'open-autonomy'), { recursive: true });
@@ -80,12 +83,12 @@ else {
80
83
  try {
81
84
  run(['git', 'clone', '-q', origin, join(tmp, 'repo')], { quiet: true });
82
85
  if (originInside !== origin) run(['git', '-C', join(tmp, 'repo'), 'remote', 'set-url', 'origin', originInside], { quiet: true });
83
- run([...docker, 'run', '--rm', '-v', 'oa-repo:/work', '-v', `${join(tmp, 'repo')}:/src:ro`, 'alpine:3', 'sh', '-c', `cp -a /src/. /work/ && chown -R ${uid}:${gid} /work`], { quiet: true });
86
+ run([...docker, 'run', '--rm', '-v', `${REPO_VOL}:/work`, '-v', `${join(tmp, 'repo')}:/src:ro`, 'alpine:3', 'sh', '-c', `cp -a /src/. /work/ && chown -R ${uid}:${gid} /work`], { quiet: true });
84
87
  } finally { rmSync(tmp, { recursive: true, force: true }); }
85
- say(`repo: oa-repo cloned from ${origin}${originInside !== origin ? ` (origin inside the container: ${originInside})` : ''}`);
88
+ say(`repo: ${REPO_VOL} cloned from ${origin}${originInside !== origin ? ` (origin inside the container: ${originInside})` : ''}`);
86
89
  }
87
90
 
88
91
  // 4. What is the owner's, and what is next.
89
92
  say('yours: the deploy key and the ssh-agent that forwards it into the Docker host (container/README.md), and the Discord bot token if you deliver there');
90
93
  for (const t of todo) say(`next: ${t}`);
91
- say(`next: AGENT_SECRETS=${secrets} ${docker.join(' ')} compose -f container/compose.yml up -d --build`);
94
+ say(`next: ${stack === 'oa' ? '' : `STACK=${stack} `}AGENT_SECRETS=${secrets} ${docker.join(' ')} compose${stack === 'oa' ? '' : ` -p ${stack}`} -f container/compose.yml up -d --build`);
@@ -0,0 +1,62 @@
1
+ node_modules/
2
+ *.log
3
+ .env
4
+
5
+ # The agent's home (hermes/): its identity, skills and schedule are committed; its runtime state is not.
6
+ hermes/.env
7
+ hermes/auth.json
8
+ hermes/*.lock
9
+ hermes/state.db*
10
+ hermes/sessions/
11
+ hermes/logs/
12
+ hermes/cache/
13
+ hermes/*_cache/
14
+ hermes/memories/
15
+ hermes/pairing/
16
+ hermes/hooks/*
17
+ !hermes/hooks/seed/
18
+ !hermes/hooks/seed/HOOK.yaml
19
+ !hermes/hooks/seed/handler.py
20
+ # The treasurer profile's runtime state, like the default's.
21
+ hermes/profiles/*/.env
22
+ hermes/profiles/*/auth.json
23
+ hermes/profiles/*/*.lock
24
+ hermes/profiles/*/state.db*
25
+ hermes/profiles/*/sessions/
26
+ hermes/profiles/*/logs/
27
+ hermes/profiles/*/cache/
28
+ hermes/profiles/*/*_cache/
29
+ hermes/profiles/*/memories/
30
+ hermes/profiles/*/cron/
31
+ hermes/profiles/*/kanban*
32
+ hermes/cron/output/
33
+ hermes/cron/*.lock
34
+ hermes/cron/jobs.json
35
+ hermes/cron/ticker_*
36
+ hermes/cron/executions.db
37
+ hermes/cron/notepad.db
38
+ hermes/cron/usage_audit.jsonl
39
+ hermes/__pycache__/
40
+ __pycache__/
41
+ hermes/bin/
42
+ hermes/.update_check
43
+ hermes/channel_directory.json
44
+ hermes/gateway.pid
45
+ hermes/gateway_state.json
46
+ hermes/gateway/
47
+ hermes/state/
48
+ hermes/lsp/
49
+ hermes/install_id
50
+ hermes/kanban.db
51
+ hermes/kanban/
52
+ hermes/skills/.bundled_manifest
53
+ hermes/skills/.usage.json*
54
+ hermes/skills/.curator_state
55
+ hermes/skills/autonomous-ai-agents/
56
+ hermes/verification_evidence.db
57
+ hermes/.restart_pending.json
58
+ hermes/.clean_shutdown
59
+ hermes/.skills_prompt_snapshot.json
60
+
61
+ # The reporter's own cursor (which sessions it has published how far).
62
+ .open-autonomy/reporter-state.json
@@ -42,4 +42,8 @@ docker exec -u $UID oa-agent hermes kanban list # the board: th
42
42
  docker logs -f oa-reporter # what is being published
43
43
  ```
44
44
 
45
+ Several stacks on one Docker host, two projects or a project beside a world's copy of it: give each a name,
46
+ `bun .open-autonomy/setup.ts --stack <name>` and `STACK=<name> docker compose -p <name> …`; the containers and
47
+ volumes carry it (`<name>-agent`, `<name>-home`). The default is `oa`.
48
+
45
49
  The kit owns this directory; `create-open-autonomy upgrade .` brings it forward.
@@ -9,13 +9,16 @@
9
9
  # to happen, and nothing on the host needs them. Inspect with `docker exec`.
10
10
  #
11
11
  # AGENT_SECRETS=~/.config/open-autonomy docker compose -f container/compose.yml up -d --build
12
+ # Several stacks on one Docker host (two projects, or a project beside a world's copy of it): each has its own
13
+ # STACK name, which the containers and volumes carry, and its own compose project: `STACK=<name> docker compose
14
+ # -p <name> …`. The default is `oa`.
12
15
  services:
13
16
  valve:
14
17
  build:
15
18
  context: ..
16
19
  dockerfile: container/Dockerfile.valve
17
20
  image: __PROJECT__-valve:local
18
- container_name: oa-valve
21
+ container_name: ${STACK:-oa}-valve
19
22
  restart: unless-stopped
20
23
  environment:
21
24
  - AGENT_ENV_FILE=/secrets/agent.env
@@ -27,7 +30,7 @@ services:
27
30
  context: ..
28
31
  dockerfile: container/Dockerfile.valve
29
32
  image: __PROJECT__-valve:local
30
- container_name: oa-valve-pay
33
+ container_name: ${STACK:-oa}-valve-pay
31
34
  restart: unless-stopped
32
35
  environment:
33
36
  - AGENT_ENV_FILE=/secrets/treasurer.env
@@ -39,7 +42,7 @@ services:
39
42
  # agent has since done.
40
43
  home-sync:
41
44
  image: alpine:3
42
- container_name: oa-home-sync
45
+ container_name: ${STACK:-oa}-home-sync
43
46
  restart: "no"
44
47
  command: ["sh", "-c", "cd /repo/hermes && find . -type f ! -name '.env' -exec cp -a --parents {} /opt/data/ \\; && chown -R ${AGENT_UID:-501}:${AGENT_GID:-20} /opt/data && echo 'home synced from the repository'"]
45
48
  volumes:
@@ -52,7 +55,7 @@ services:
52
55
  args:
53
56
  HERMES_IMAGE: ${HERMES_IMAGE:-hermes-agent:v2026.8.31} # container/build-hermes.sh builds it
54
57
  image: __PROJECT__-agent:local
55
- container_name: oa-agent
58
+ container_name: ${STACK:-oa}-agent
56
59
  restart: unless-stopped
57
60
  depends_on:
58
61
  valve: { condition: service_started }
@@ -74,7 +77,7 @@ services:
74
77
  context: ..
75
78
  dockerfile: container/Dockerfile.reporter
76
79
  image: __PROJECT__-reporter:local
77
- container_name: oa-reporter
80
+ container_name: ${STACK:-oa}-reporter
78
81
  restart: unless-stopped
79
82
  # The agent's own ids: the home it reads and the checkout it keeps its cursor in are the agent's.
80
83
  user: "${AGENT_UID:-501}:${AGENT_GID:-20}"
@@ -90,10 +93,10 @@ services:
90
93
  networks: [agent]
91
94
  volumes:
92
95
  oa-home:
93
- name: oa-home
96
+ name: ${STACK:-oa}-home
94
97
  external: true # seeded once from hermes/ (container/README.md)
95
98
  oa-repo:
96
- name: oa-repo
99
+ name: ${STACK:-oa}-repo
97
100
  external: true # the agent's clone
98
101
  networks:
99
102
  agent: {}