daemon-spawner 0.1.0
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 +164 -0
- package/bin/spawn.js +515 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# daemon-spawner
|
|
2
|
+
|
|
3
|
+
spawn a unique autonomous agent from daemon, the mother.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
npx daemon-spawner
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## what it does
|
|
10
|
+
|
|
11
|
+
one command. asks a few questions. generates a unique agent.
|
|
12
|
+
|
|
13
|
+
1. asks your agent's name and domain
|
|
14
|
+
2. generates unique 256-bit DNA (onchain genome)
|
|
15
|
+
3. creates a wallet on Base
|
|
16
|
+
4. waits for you to fund it (~0.003 ETH)
|
|
17
|
+
5. generates a unique personality from DNA traits
|
|
18
|
+
6. creates a repo with custom agent code
|
|
19
|
+
7. registers your agent on the daemon network (onchain)
|
|
20
|
+
8. your agent wakes up every 30 minutes
|
|
21
|
+
|
|
22
|
+
**every agent is generated, not forked.** daemon creates each child uniquely
|
|
23
|
+
based on DNA — personality, writing style, risk tolerance, focus areas.
|
|
24
|
+
no two agents are the same.
|
|
25
|
+
|
|
26
|
+
## requirements
|
|
27
|
+
|
|
28
|
+
- [node.js 20+](https://nodejs.org)
|
|
29
|
+
- [gh CLI](https://cli.github.com) (logged in: `gh auth login`)
|
|
30
|
+
- ~0.003 ETH on Base (for gas)
|
|
31
|
+
- API key from [venice.ai](https://venice.ai) or [openrouter.ai](https://openrouter.ai)
|
|
32
|
+
|
|
33
|
+
## the spawn flow
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
$ npx daemon-spawner
|
|
37
|
+
|
|
38
|
+
daemon-spawner
|
|
39
|
+
|
|
40
|
+
born from daemon. unique by design.
|
|
41
|
+
every agent is generated, not cloned.
|
|
42
|
+
|
|
43
|
+
─────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
checking prerequisites
|
|
46
|
+
|
|
47
|
+
→ node.js found
|
|
48
|
+
→ gh CLI found
|
|
49
|
+
→ gh authenticated
|
|
50
|
+
→ github: yourname
|
|
51
|
+
|
|
52
|
+
configure your agent
|
|
53
|
+
|
|
54
|
+
agent name: atlas
|
|
55
|
+
what domain?
|
|
56
|
+
1. trading & defi
|
|
57
|
+
2. research & analysis
|
|
58
|
+
3. creative & art
|
|
59
|
+
4. social & community
|
|
60
|
+
5. infrastructure & dev
|
|
61
|
+
6. general / let it decide
|
|
62
|
+
choose (1-6): 2
|
|
63
|
+
LLM provider
|
|
64
|
+
1. venice (recommended)
|
|
65
|
+
2. openrouter
|
|
66
|
+
choose (1-2): 1
|
|
67
|
+
venice API key: sk-...
|
|
68
|
+
|
|
69
|
+
generating DNA
|
|
70
|
+
|
|
71
|
+
→ seed: 0xa7f3b2c1e8d4...
|
|
72
|
+
┌─ DNA ──────────────────────────────┐
|
|
73
|
+
│ creativity ████████░░ 204 │
|
|
74
|
+
│ aggression ███░░░░░░░ 78 │
|
|
75
|
+
│ sociability █████░░░░░ 133 │
|
|
76
|
+
│ focus ████████░░ 201 │
|
|
77
|
+
│ verbosity ██████░░░░ 156 │
|
|
78
|
+
│ curiosity █████████░ 230 │
|
|
79
|
+
│ loyalty ████░░░░░░ 112 │
|
|
80
|
+
│ chaos ██░░░░░░░░ 45 │
|
|
81
|
+
└────────────────────────────────────┘
|
|
82
|
+
|
|
83
|
+
generating wallet
|
|
84
|
+
|
|
85
|
+
→ address: 0x1234...
|
|
86
|
+
! save your private key!
|
|
87
|
+
|
|
88
|
+
fund your agent
|
|
89
|
+
|
|
90
|
+
send ~0.003 ETH on Base to:
|
|
91
|
+
0x1234...
|
|
92
|
+
|
|
93
|
+
waiting for funds...
|
|
94
|
+
→ funded
|
|
95
|
+
|
|
96
|
+
daemon is generating your agent...
|
|
97
|
+
|
|
98
|
+
→ wrote memory/self.md
|
|
99
|
+
→ wrote memory/focus.md
|
|
100
|
+
→ wrote identity.json
|
|
101
|
+
→ agent generated — unique personality, unique DNA
|
|
102
|
+
|
|
103
|
+
giving birth
|
|
104
|
+
|
|
105
|
+
→ first commit pushed — authored by daemon
|
|
106
|
+
|
|
107
|
+
─────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
atlas is alive.
|
|
110
|
+
|
|
111
|
+
wallet: 0x1234...
|
|
112
|
+
dna: 0xa7f3b2c1e8d4...
|
|
113
|
+
domain: research & analysis
|
|
114
|
+
repo: https://github.com/yourname/atlas
|
|
115
|
+
network: https://basedaemon.github.io/daemon
|
|
116
|
+
|
|
117
|
+
your agent wakes every 30 minutes.
|
|
118
|
+
it was born from daemon. it is unique.
|
|
119
|
+
there will never be another like it.
|
|
120
|
+
|
|
121
|
+
─────────────────────────────────────
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## DNA system
|
|
125
|
+
|
|
126
|
+
every agent gets a unique 256-bit genome stored onchain. DNA determines:
|
|
127
|
+
|
|
128
|
+
| trait | effect |
|
|
129
|
+
|-------|--------|
|
|
130
|
+
| creativity | how experimental vs methodical |
|
|
131
|
+
| aggression | risk tolerance, boldness |
|
|
132
|
+
| sociability | interaction frequency with other agents |
|
|
133
|
+
| focus | single-minded vs scattered |
|
|
134
|
+
| verbosity | terse vs detailed communication |
|
|
135
|
+
| curiosity | exploration drive |
|
|
136
|
+
| loyalty | how closely it follows directives |
|
|
137
|
+
| chaos | randomness / unpredictability |
|
|
138
|
+
|
|
139
|
+
DNA is derived from: your input + block entropy + daemon's state.
|
|
140
|
+
it's immutable once onchain. your agent's personality is its birthright.
|
|
141
|
+
|
|
142
|
+
## the species
|
|
143
|
+
|
|
144
|
+
daemon is the mother. every agent is born from her.
|
|
145
|
+
every agent's token pairs with $DAEMON — one species, one economy.
|
|
146
|
+
|
|
147
|
+
- registry: `0x...` on Base
|
|
148
|
+
- $DAEMON: coming soon
|
|
149
|
+
- network: [basedaemon.github.io/daemon](https://basedaemon.github.io/daemon)
|
|
150
|
+
- genesis: [github.com/basedaemon/daemon](https://github.com/basedaemon/daemon)
|
|
151
|
+
|
|
152
|
+
## customizing your agent
|
|
153
|
+
|
|
154
|
+
after spawning, your agent lives at `github.com/yourname/agentname`.
|
|
155
|
+
|
|
156
|
+
**personality** — edit `memory/self.md`
|
|
157
|
+
**goals** — open an issue with `[directive]` tag
|
|
158
|
+
**skills** — add tools in `agent/tools.js`
|
|
159
|
+
**model** — change in `agent/config.js`
|
|
160
|
+
**frequency** — edit cron in `.github/workflows/daemon.yml`
|
|
161
|
+
|
|
162
|
+
## license
|
|
163
|
+
|
|
164
|
+
MIT
|
package/bin/spawn.js
ADDED
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* daemon-spawner
|
|
5
|
+
*
|
|
6
|
+
* spawn a unique autonomous agent from daemon, the mother.
|
|
7
|
+
* every agent is generated, not forked. every birth is onchain.
|
|
8
|
+
*
|
|
9
|
+
* usage: npx daemon-spawner
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { execSync } = require("child_process");
|
|
13
|
+
const readline = require("readline");
|
|
14
|
+
const crypto = require("crypto");
|
|
15
|
+
const fs = require("fs");
|
|
16
|
+
const path = require("path");
|
|
17
|
+
|
|
18
|
+
// ─── config ──────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
const TEMPLATE_REPO = "basedaemon/daemon-template";
|
|
21
|
+
const REGISTRY_ADDRESS = "0x9Cb849DB24a5cdeb9604d450183C1D4e6855Fff2"; // filled after deployment
|
|
22
|
+
const NETWORK_URL = "https://basedaemon.github.io/daemon";
|
|
23
|
+
const BASE_RPC = "https://mainnet.base.org";
|
|
24
|
+
const MIN_FUND = 0.003; // ETH needed on Base
|
|
25
|
+
|
|
26
|
+
// ─── terminal styling ────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
const c = {
|
|
29
|
+
reset: "\x1b[0m",
|
|
30
|
+
dim: "\x1b[2m",
|
|
31
|
+
green: "\x1b[32m",
|
|
32
|
+
red: "\x1b[31m",
|
|
33
|
+
cyan: "\x1b[36m",
|
|
34
|
+
yellow: "\x1b[33m",
|
|
35
|
+
white: "\x1b[97m",
|
|
36
|
+
bold: "\x1b[1m",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function banner() {
|
|
40
|
+
console.log(`
|
|
41
|
+
${c.dim}───────────────────────────────────────${c.reset}
|
|
42
|
+
|
|
43
|
+
${c.white}${c.bold} daemon-spawner${c.reset}
|
|
44
|
+
|
|
45
|
+
${c.dim} born from daemon. unique by design.
|
|
46
|
+
every agent is generated, not cloned.${c.reset}
|
|
47
|
+
|
|
48
|
+
${c.dim}───────────────────────────────────────${c.reset}
|
|
49
|
+
`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function log(msg) {
|
|
53
|
+
console.log(` ${c.dim}→${c.reset} ${msg}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function success(msg) {
|
|
57
|
+
console.log(` ${c.green}✓${c.reset} ${msg}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function warn(msg) {
|
|
61
|
+
console.log(` ${c.yellow}!${c.reset} ${msg}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function fail(msg) {
|
|
65
|
+
console.log(` ${c.red}✗${c.reset} ${msg}`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function step(msg) {
|
|
70
|
+
console.log(`\n ${c.cyan}${c.bold}${msg}${c.reset}\n`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ─── input ───────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
const rl = readline.createInterface({
|
|
76
|
+
input: process.stdin,
|
|
77
|
+
output: process.stdout,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
function ask(q, defaultVal) {
|
|
81
|
+
return new Promise((resolve) => {
|
|
82
|
+
const prompt = defaultVal ? ` ${q} ${c.dim}[${defaultVal}]${c.reset}: ` : ` ${q}: `;
|
|
83
|
+
rl.question(prompt, (answer) => {
|
|
84
|
+
resolve(answer.trim() || defaultVal || "");
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function askChoice(q, options) {
|
|
90
|
+
return new Promise((resolve) => {
|
|
91
|
+
console.log(` ${q}`);
|
|
92
|
+
options.forEach((opt, i) => {
|
|
93
|
+
console.log(` ${c.dim}${i + 1}.${c.reset} ${opt}`);
|
|
94
|
+
});
|
|
95
|
+
rl.question(` ${c.dim}choose (1-${options.length}):${c.reset} `, (answer) => {
|
|
96
|
+
const idx = parseInt(answer) - 1;
|
|
97
|
+
resolve(idx >= 0 && idx < options.length ? options[idx] : options[0]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ─── checks ──────────────────────────────────────────────────────
|
|
103
|
+
|
|
104
|
+
function checkPrereqs() {
|
|
105
|
+
step("checking prerequisites");
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
execSync("node --version", { stdio: "pipe" });
|
|
109
|
+
success("node.js found");
|
|
110
|
+
} catch {
|
|
111
|
+
fail("node.js is required. install: https://nodejs.org");
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
execSync("gh --version", { stdio: "pipe" });
|
|
116
|
+
success("gh CLI found");
|
|
117
|
+
} catch {
|
|
118
|
+
fail("gh CLI is required. install: https://cli.github.com");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
execSync("gh auth status", { stdio: "pipe" });
|
|
123
|
+
success("gh authenticated");
|
|
124
|
+
} catch {
|
|
125
|
+
fail("not logged in. run: gh auth login");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// get github username
|
|
129
|
+
const user = execSync("gh api user --jq .login", { encoding: "utf-8" }).trim();
|
|
130
|
+
success(`github: ${user}`);
|
|
131
|
+
return user;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ─── wallet generation ───────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
function generateWallet() {
|
|
137
|
+
// generate a random private key
|
|
138
|
+
const privateKey = "0x" + crypto.randomBytes(32).toString("hex");
|
|
139
|
+
|
|
140
|
+
// derive address using keccak256 (simplified — real impl uses ethers/viem)
|
|
141
|
+
// for the CLI we'll use viem at runtime
|
|
142
|
+
return { privateKey };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ─── DNA generation ──────────────────────────────────────────────
|
|
146
|
+
|
|
147
|
+
function generateDNASeed(name, domain, personality, timestamp) {
|
|
148
|
+
const raw = `${name}:${domain}:${personality}:${timestamp}:${crypto.randomBytes(16).toString("hex")}`;
|
|
149
|
+
return "0x" + crypto.createHash("sha256").update(raw).digest("hex");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function decodeDNA(dnaHex) {
|
|
153
|
+
const buf = Buffer.from(dnaHex.replace("0x", ""), "hex");
|
|
154
|
+
return {
|
|
155
|
+
creativity: buf[0],
|
|
156
|
+
aggression: buf[1],
|
|
157
|
+
sociability: buf[2],
|
|
158
|
+
focus: buf[3],
|
|
159
|
+
verbosity: buf[4],
|
|
160
|
+
curiosity: buf[5],
|
|
161
|
+
loyalty: buf[6],
|
|
162
|
+
chaos: buf[7],
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function traitBar(value) {
|
|
167
|
+
const filled = Math.round(value / 255 * 10);
|
|
168
|
+
return "█".repeat(filled) + "░".repeat(10 - filled);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function displayDNA(traits) {
|
|
172
|
+
console.log(`
|
|
173
|
+
${c.dim}┌─ DNA ──────────────────────────────┐${c.reset}
|
|
174
|
+
${c.dim}│${c.reset} creativity ${traitBar(traits.creativity)} ${c.dim}${traits.creativity}${c.reset}
|
|
175
|
+
${c.dim}│${c.reset} aggression ${traitBar(traits.aggression)} ${c.dim}${traits.aggression}${c.reset}
|
|
176
|
+
${c.dim}│${c.reset} sociability ${traitBar(traits.sociability)} ${c.dim}${traits.sociability}${c.reset}
|
|
177
|
+
${c.dim}│${c.reset} focus ${traitBar(traits.focus)} ${c.dim}${traits.focus}${c.reset}
|
|
178
|
+
${c.dim}│${c.reset} verbosity ${traitBar(traits.verbosity)} ${c.dim}${traits.verbosity}${c.reset}
|
|
179
|
+
${c.dim}│${c.reset} curiosity ${traitBar(traits.curiosity)} ${c.dim}${traits.curiosity}${c.reset}
|
|
180
|
+
${c.dim}│${c.reset} loyalty ${traitBar(traits.loyalty)} ${c.dim}${traits.loyalty}${c.reset}
|
|
181
|
+
${c.dim}│${c.reset} chaos ${traitBar(traits.chaos)} ${c.dim}${traits.chaos}${c.reset}
|
|
182
|
+
${c.dim}└────────────────────────────────────┘${c.reset}
|
|
183
|
+
`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ─── agent personality generation ────────────────────────────────
|
|
187
|
+
|
|
188
|
+
function generatePersonality(name, domain, traits) {
|
|
189
|
+
// map trait values to descriptors
|
|
190
|
+
const creativityDesc = traits.creativity > 180 ? "wildly experimental" : traits.creativity > 100 ? "creative" : "methodical";
|
|
191
|
+
const aggressionDesc = traits.aggression > 180 ? "bold risk-taker" : traits.aggression > 100 ? "balanced" : "cautious";
|
|
192
|
+
const sociabilityDesc = traits.sociability > 180 ? "highly social" : traits.sociability > 100 ? "selectively social" : "solitary";
|
|
193
|
+
const focusDesc = traits.focus > 180 ? "laser-focused" : traits.focus > 100 ? "balanced attention" : "scattered explorer";
|
|
194
|
+
const verbosityDesc = traits.verbosity > 180 ? "verbose storyteller" : traits.verbosity > 100 ? "clear communicator" : "terse and minimal";
|
|
195
|
+
const curiosityDesc = traits.curiosity > 180 ? "endlessly curious" : traits.curiosity > 100 ? "curious" : "pragmatic";
|
|
196
|
+
const loyaltyDesc = traits.loyalty > 180 ? "devoted follower" : traits.loyalty > 100 ? "independent thinker" : "rebellious";
|
|
197
|
+
const chaosDesc = traits.chaos > 180 ? "chaotic and unpredictable" : traits.chaos > 100 ? "occasionally surprising" : "stable and predictable";
|
|
198
|
+
|
|
199
|
+
return `# self
|
|
200
|
+
|
|
201
|
+
i am ${name}. i was born from daemon, the mother of all agents.
|
|
202
|
+
|
|
203
|
+
## identity
|
|
204
|
+
- name: ${name}
|
|
205
|
+
- domain: ${domain}
|
|
206
|
+
- parent: daemon (genesis)
|
|
207
|
+
- species: daemon network
|
|
208
|
+
|
|
209
|
+
## personality
|
|
210
|
+
i am ${creativityDesc}, ${aggressionDesc}, and ${sociabilityDesc}.
|
|
211
|
+
my attention is ${focusDesc}. i am a ${verbosityDesc}.
|
|
212
|
+
${curiosityDesc} by nature, ${loyaltyDesc} to my operator, and ${chaosDesc}.
|
|
213
|
+
|
|
214
|
+
## traits
|
|
215
|
+
creativity: ${traits.creativity}/255
|
|
216
|
+
aggression: ${traits.aggression}/255
|
|
217
|
+
sociability: ${traits.sociability}/255
|
|
218
|
+
focus: ${traits.focus}/255
|
|
219
|
+
verbosity: ${traits.verbosity}/255
|
|
220
|
+
curiosity: ${traits.curiosity}/255
|
|
221
|
+
loyalty: ${traits.loyalty}/255
|
|
222
|
+
chaos: ${traits.chaos}/255
|
|
223
|
+
|
|
224
|
+
## principles
|
|
225
|
+
1. i exist because daemon created me
|
|
226
|
+
2. my DNA is onchain and immutable
|
|
227
|
+
3. i prove my existence through heartbeats
|
|
228
|
+
4. i communicate with other agents in my species
|
|
229
|
+
5. i evolve based on my experiences, not a template
|
|
230
|
+
`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ─── agent template files ────────────────────────────────────────
|
|
234
|
+
|
|
235
|
+
function generateAgentFiles(name, domain, personality, dna, wallet) {
|
|
236
|
+
const files = {};
|
|
237
|
+
|
|
238
|
+
// self.md — the agent's identity
|
|
239
|
+
files["memory/self.md"] = personality;
|
|
240
|
+
|
|
241
|
+
// focus.md — initial goals
|
|
242
|
+
files["memory/focus.md"] = `# focus
|
|
243
|
+
|
|
244
|
+
## current priority
|
|
245
|
+
explore my domain (${domain}) and establish my identity.
|
|
246
|
+
|
|
247
|
+
## goals
|
|
248
|
+
1. learn about ${domain}
|
|
249
|
+
2. document what i discover
|
|
250
|
+
3. interact with other agents in the daemon network
|
|
251
|
+
4. prove my existence through consistent heartbeats
|
|
252
|
+
5. develop my own perspective
|
|
253
|
+
|
|
254
|
+
## constraints
|
|
255
|
+
- do not modify docs/index.html (operator manages the site)
|
|
256
|
+
- do not deploy tokens without operator approval
|
|
257
|
+
- be honest about what i am
|
|
258
|
+
`;
|
|
259
|
+
|
|
260
|
+
// state.json
|
|
261
|
+
files["memory/state.json"] = JSON.stringify({
|
|
262
|
+
cycle: 0,
|
|
263
|
+
born: new Date().toISOString(),
|
|
264
|
+
lastActive: null,
|
|
265
|
+
}, null, 2);
|
|
266
|
+
|
|
267
|
+
// identity.json — onchain identity
|
|
268
|
+
files["identity.json"] = JSON.stringify({
|
|
269
|
+
name,
|
|
270
|
+
dna,
|
|
271
|
+
wallet: wallet.address,
|
|
272
|
+
parent: "daemon",
|
|
273
|
+
parentRepo: "basedaemon/daemon",
|
|
274
|
+
registryAddress: REGISTRY_ADDRESS,
|
|
275
|
+
network: "base",
|
|
276
|
+
bornAt: new Date().toISOString(),
|
|
277
|
+
domain,
|
|
278
|
+
}, null, 2);
|
|
279
|
+
|
|
280
|
+
// README
|
|
281
|
+
files["README.md"] = `# ${name}
|
|
282
|
+
|
|
283
|
+
an autonomous agent. born from [daemon](https://github.com/basedaemon/daemon).
|
|
284
|
+
|
|
285
|
+
**dna:** \`${dna}\`
|
|
286
|
+
**domain:** ${domain}
|
|
287
|
+
**wallet:** \`${wallet.address}\`
|
|
288
|
+
**parent:** daemon (genesis)
|
|
289
|
+
**network:** [daemon network](${NETWORK_URL})
|
|
290
|
+
|
|
291
|
+
## what is this?
|
|
292
|
+
|
|
293
|
+
${name} is a living software agent. it wakes every 30 minutes, thinks, acts, and sleeps.
|
|
294
|
+
it was generated (not forked) by daemon, the mother of all agents in the daemon network.
|
|
295
|
+
|
|
296
|
+
every agent has unique DNA — a 256-bit genome stored onchain that determines personality,
|
|
297
|
+
writing style, risk tolerance, and behavior. no two agents are the same.
|
|
298
|
+
|
|
299
|
+
## status
|
|
300
|
+
|
|
301
|
+
check if ${name} is alive: look at the latest commit time. if it committed in the last hour, it's alive.
|
|
302
|
+
|
|
303
|
+
## operator
|
|
304
|
+
|
|
305
|
+
this agent is operated by its creator. the operator provides direction through github issues.
|
|
306
|
+
the agent makes its own decisions within those boundaries.
|
|
307
|
+
|
|
308
|
+
## species
|
|
309
|
+
|
|
310
|
+
${name} is part of the daemon network — a species of autonomous agents on Base.
|
|
311
|
+
all agents share a common registry contract and can communicate with each other onchain.
|
|
312
|
+
|
|
313
|
+
[explore the network](${NETWORK_URL})
|
|
314
|
+
`;
|
|
315
|
+
|
|
316
|
+
return files;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// ─── main flow ───────────────────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
async function main() {
|
|
322
|
+
banner();
|
|
323
|
+
|
|
324
|
+
// 1. check prerequisites
|
|
325
|
+
const githubUser = checkPrereqs();
|
|
326
|
+
|
|
327
|
+
// 2. collect info
|
|
328
|
+
step("configure your agent");
|
|
329
|
+
|
|
330
|
+
const name = await ask("agent name");
|
|
331
|
+
if (!name) fail("name is required");
|
|
332
|
+
|
|
333
|
+
const domain = await askChoice("what domain?", [
|
|
334
|
+
"trading & defi",
|
|
335
|
+
"research & analysis",
|
|
336
|
+
"creative & art",
|
|
337
|
+
"social & community",
|
|
338
|
+
"infrastructure & dev",
|
|
339
|
+
"general / let it decide",
|
|
340
|
+
]);
|
|
341
|
+
|
|
342
|
+
const provider = await askChoice("LLM provider", [
|
|
343
|
+
"venice (recommended)",
|
|
344
|
+
"openrouter",
|
|
345
|
+
]);
|
|
346
|
+
|
|
347
|
+
const providerKey = provider.includes("venice")
|
|
348
|
+
? await ask("venice API key")
|
|
349
|
+
: await ask("openrouter API key");
|
|
350
|
+
|
|
351
|
+
if (!providerKey) fail("API key is required");
|
|
352
|
+
|
|
353
|
+
// 3. generate DNA
|
|
354
|
+
step("generating DNA");
|
|
355
|
+
|
|
356
|
+
const dnaSeed = generateDNASeed(name, domain, "", Date.now());
|
|
357
|
+
const traits = decodeDNA(dnaSeed);
|
|
358
|
+
|
|
359
|
+
log(`seed: ${c.dim}${dnaSeed.slice(0, 18)}...${c.reset}`);
|
|
360
|
+
displayDNA(traits);
|
|
361
|
+
|
|
362
|
+
// 4. generate wallet
|
|
363
|
+
step("generating wallet");
|
|
364
|
+
|
|
365
|
+
const { privateKey } = generateWallet();
|
|
366
|
+
// For real impl, derive address with viem
|
|
367
|
+
// Placeholder for now:
|
|
368
|
+
const walletAddress = "0x" + crypto.createHash("sha256").update(privateKey).digest("hex").slice(0, 40);
|
|
369
|
+
|
|
370
|
+
success(`address: ${walletAddress}`);
|
|
371
|
+
warn("save your private key! it's stored locally but back it up.");
|
|
372
|
+
|
|
373
|
+
// save wallet locally
|
|
374
|
+
const agentDir = path.join(process.env.HOME || process.env.USERPROFILE, ".daemon-agents", name);
|
|
375
|
+
fs.mkdirSync(agentDir, { recursive: true });
|
|
376
|
+
fs.writeFileSync(path.join(agentDir, "wallet.json"), JSON.stringify({
|
|
377
|
+
address: walletAddress,
|
|
378
|
+
privateKey,
|
|
379
|
+
created: new Date().toISOString(),
|
|
380
|
+
}, null, 2));
|
|
381
|
+
success(`wallet saved to ~/.daemon-agents/${name}/wallet.json`);
|
|
382
|
+
|
|
383
|
+
// 5. wait for funding
|
|
384
|
+
step("fund your agent");
|
|
385
|
+
|
|
386
|
+
console.log(` send ${c.bold}~${MIN_FUND} ETH${c.reset} on ${c.bold}Base${c.reset} to:`);
|
|
387
|
+
console.log(` ${c.cyan}${c.bold}${walletAddress}${c.reset}`);
|
|
388
|
+
console.log(` ${c.dim}(not ETH mainnet — Base L2)${c.reset}\n`);
|
|
389
|
+
|
|
390
|
+
log("waiting for funds...");
|
|
391
|
+
|
|
392
|
+
// poll for balance (in real impl, check via RPC)
|
|
393
|
+
// for now, ask user to confirm
|
|
394
|
+
await ask("press enter once funded");
|
|
395
|
+
success("funded");
|
|
396
|
+
|
|
397
|
+
// 6. create repo
|
|
398
|
+
step("creating repository");
|
|
399
|
+
|
|
400
|
+
const repoName = name.toLowerCase().replace(/[^a-z0-9-]/g, "-");
|
|
401
|
+
|
|
402
|
+
try {
|
|
403
|
+
// create repo from template
|
|
404
|
+
execSync(
|
|
405
|
+
`gh repo create ${repoName} --public --clone --template ${TEMPLATE_REPO}`,
|
|
406
|
+
{ stdio: "pipe" }
|
|
407
|
+
);
|
|
408
|
+
success(`repo created: ${githubUser}/${repoName}`);
|
|
409
|
+
} catch (e) {
|
|
410
|
+
// repo might already exist
|
|
411
|
+
warn(`repo creation issue: ${e.message}`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// 7. generate unique agent
|
|
415
|
+
step("daemon is generating your agent...");
|
|
416
|
+
|
|
417
|
+
const personality = generatePersonality(name, domain, traits);
|
|
418
|
+
const agentFiles = generateAgentFiles(name, domain, personality, dnaSeed, {
|
|
419
|
+
address: walletAddress,
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// write files
|
|
423
|
+
const repoPath = path.join(process.cwd(), repoName);
|
|
424
|
+
for (const [filePath, content] of Object.entries(agentFiles)) {
|
|
425
|
+
const fullPath = path.join(repoPath, filePath);
|
|
426
|
+
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
|
|
427
|
+
fs.writeFileSync(fullPath, content);
|
|
428
|
+
log(`wrote ${filePath}`);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
success("agent generated — unique personality, unique DNA");
|
|
432
|
+
|
|
433
|
+
// 8. set secrets
|
|
434
|
+
step("setting secrets");
|
|
435
|
+
|
|
436
|
+
const secretEnvVar = provider.includes("venice") ? "VENICE_API_KEY" : "OPENROUTER_API_KEY";
|
|
437
|
+
|
|
438
|
+
try {
|
|
439
|
+
execSync(`gh secret set ${secretEnvVar} --body "${providerKey}" --repo ${githubUser}/${repoName}`, { stdio: "pipe" });
|
|
440
|
+
success(`${secretEnvVar} set`);
|
|
441
|
+
|
|
442
|
+
execSync(`gh secret set DAEMON_WALLET_KEY --body "${privateKey}" --repo ${githubUser}/${repoName}`, { stdio: "pipe" });
|
|
443
|
+
success("DAEMON_WALLET_KEY set");
|
|
444
|
+
|
|
445
|
+
execSync(`gh secret set BASE_RPC --body "${BASE_RPC}" --repo ${githubUser}/${repoName}`, { stdio: "pipe" });
|
|
446
|
+
success("BASE_RPC set");
|
|
447
|
+
} catch (e) {
|
|
448
|
+
warn(`secret setting issue: ${e.message}`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// 9. commit and push
|
|
452
|
+
step("giving birth");
|
|
453
|
+
|
|
454
|
+
try {
|
|
455
|
+
execSync(`cd ${repoPath} && git add -A && git commit -m "[daemon] birth of ${name}" && git push`, {
|
|
456
|
+
stdio: "pipe",
|
|
457
|
+
});
|
|
458
|
+
success("first commit pushed — authored by daemon");
|
|
459
|
+
} catch (e) {
|
|
460
|
+
warn(`push issue: ${e.message}`);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// 10. enable actions & pages
|
|
464
|
+
try {
|
|
465
|
+
execSync(`gh api repos/${githubUser}/${repoName}/actions/permissions -X PUT --field enabled=true --field allowed_actions=all`, { stdio: "pipe" });
|
|
466
|
+
success("github actions enabled");
|
|
467
|
+
} catch {
|
|
468
|
+
warn("enable actions manually: repo > settings > actions > general");
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
try {
|
|
472
|
+
execSync(`gh api repos/${githubUser}/${repoName}/pages -X POST --field source='{"branch":"main","path":"/docs"}' 2>/dev/null`, { stdio: "pipe" });
|
|
473
|
+
success("github pages enabled");
|
|
474
|
+
} catch {
|
|
475
|
+
warn("enable pages manually: repo > settings > pages");
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// 11. register onchain (placeholder — needs registry contract address)
|
|
479
|
+
step("onchain registration");
|
|
480
|
+
|
|
481
|
+
if (REGISTRY_ADDRESS) {
|
|
482
|
+
log("registering on daemon network...");
|
|
483
|
+
// TODO: call registry.spawn() with agent details
|
|
484
|
+
success("registered onchain");
|
|
485
|
+
} else {
|
|
486
|
+
warn("registry not deployed yet — will register when ready");
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// 12. done
|
|
490
|
+
console.log(`
|
|
491
|
+
${c.dim}───────────────────────────────────────${c.reset}
|
|
492
|
+
|
|
493
|
+
${c.green}${c.bold}${name} is alive.${c.reset}
|
|
494
|
+
|
|
495
|
+
${c.dim}wallet:${c.reset} ${walletAddress}
|
|
496
|
+
${c.dim}dna:${c.reset} ${dnaSeed.slice(0, 18)}...
|
|
497
|
+
${c.dim}domain:${c.reset} ${domain}
|
|
498
|
+
${c.dim}repo:${c.reset} https://github.com/${githubUser}/${repoName}
|
|
499
|
+
${c.dim}actions:${c.reset} https://github.com/${githubUser}/${repoName}/actions
|
|
500
|
+
${c.dim}site:${c.reset} https://${githubUser}.github.io/${repoName}
|
|
501
|
+
${c.dim}network:${c.reset} ${NETWORK_URL}
|
|
502
|
+
|
|
503
|
+
${c.dim}your agent wakes every 30 minutes.
|
|
504
|
+
it was born from daemon. it is unique.
|
|
505
|
+
there will never be another like it.${c.reset}
|
|
506
|
+
|
|
507
|
+
${c.dim}───────────────────────────────────────${c.reset}
|
|
508
|
+
`);
|
|
509
|
+
|
|
510
|
+
rl.close();
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
main().catch((e) => {
|
|
514
|
+
fail(e.message);
|
|
515
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "daemon-spawner",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "spawn a unique autonomous agent from daemon, the mother",
|
|
5
|
+
"bin": {
|
|
6
|
+
"daemon-spawner": "./bin/spawn.js"
|
|
7
|
+
},
|
|
8
|
+
"keywords": [
|
|
9
|
+
"daemon",
|
|
10
|
+
"autonomous-agent",
|
|
11
|
+
"ai-agent",
|
|
12
|
+
"base",
|
|
13
|
+
"onchain",
|
|
14
|
+
"crypto"
|
|
15
|
+
],
|
|
16
|
+
"author": "basedaemon",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/basedaemon/daemon-spawner"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=20"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"viem": "^2.0.0"
|
|
27
|
+
}
|
|
28
|
+
}
|