create-open-autonomy 2.1.1 → 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 +1 -1
- package/src/kit.ts +4 -2
- package/template/.open-autonomy/setup.ts +13 -10
- package/template/_gitignore +62 -0
- package/template/container/README.md +4 -0
- package/template/container/compose.yml +10 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-open-autonomy",
|
|
3
|
-
"version": "2.1.
|
|
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": {
|
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.
|
|
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(
|
|
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
|
|
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 [
|
|
67
|
-
if (have(
|
|
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 [
|
|
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',
|
|
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:
|
|
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',
|
|
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:
|
|
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: {}
|