@dirwin517/bot-farm 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/.idea/bot-farm.iml +9 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/LICENSE +21 -0
- package/README.md +584 -0
- package/package.json +13 -0
- package/src/api.mjs +567 -0
- package/src/cli.mjs +241 -0
- package/src/craft.mjs +172 -0
- package/src/db.mjs +93 -0
- package/src/farmhands.mjs +111 -0
- package/src/git.mjs +578 -0
- package/src/identity.mjs +199 -0
- package/src/legacy-defaults.mjs +44 -0
- package/src/mcp.mjs +681 -0
- package/src/mesh.mjs +222 -0
- package/src/packets.mjs +125 -0
- package/src/patches.mjs +141 -0
- package/src/pipelines.mjs +2145 -0
- package/src/pricing.mjs +91 -0
- package/src/projects.mjs +148 -0
- package/src/public/classic.html +1810 -0
- package/src/public/index.html +3284 -0
- package/src/questions.mjs +69 -0
- package/src/quota.mjs +76 -0
- package/src/registry.mjs +88 -0
- package/src/replay.mjs +151 -0
- package/src/rooms.mjs +320 -0
- package/src/server.mjs +765 -0
- package/src/store.mjs +546 -0
- package/src/supervisor.mjs +1803 -0
- package/src/tasks.mjs +191 -0
- package/src/template.mjs +86 -0
- package/src/tools.mjs +241 -0
- package/src/workspaces.mjs +605 -0
- package/src/ws.mjs +130 -0
- package/src/yaml.mjs +266 -0
- package/test/board.mjs +108 -0
- package/test/craft.mjs +281 -0
- package/test/extend.mjs +145 -0
- package/test/flow.mjs +165 -0
- package/test/handoff.mjs +228 -0
- package/test/linked.mjs +83 -0
- package/test/loop.mjs +168 -0
- package/test/mock-opencode.mjs +217 -0
- package/test/parallel.mjs +135 -0
- package/test/patches.mjs +93 -0
- package/test/repos.mjs +118 -0
- package/test/roles.mjs +157 -0
- package/test/shot.mjs +65 -0
- package/test/smoke.mjs +70 -0
- package/test/transport.mjs +131 -0
- package/test/workspaces.mjs +228 -0
- package/test/workspaces.mjs.tmp +0 -0
package/src/identity.mjs
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// Every session gets a stable handle and avatar derived from its id.
|
|
2
|
+
//
|
|
3
|
+
// The handle is not decoration: agents address each other by "@brisk-otter",
|
|
4
|
+
// never by "ses_01H9Z...". A model copies a three-syllable handle reliably and
|
|
5
|
+
// a 26-character ulid unreliably, and the operator can hold six of them in
|
|
6
|
+
// their head at once.
|
|
7
|
+
|
|
8
|
+
// The adjective carries the job. @prying-heron reads as a tester before you
|
|
9
|
+
// have looked at anything else on the card, and when four bots are talking in
|
|
10
|
+
// one room the handle is doing the work a job title would.
|
|
11
|
+
const ADJECTIVES = [
|
|
12
|
+
"brisk", "amber", "quiet", "copper", "velvet", "nimble", "dusty", "hollow",
|
|
13
|
+
"clever", "glassy", "rusty", "silver", "patient", "crisp", "wandering", "murmur",
|
|
14
|
+
"pale", "swift", "tidal", "moss", "lantern", "gentle", "brave", "salty",
|
|
15
|
+
"paper", "iron", "plum", "drifting", "candid", "humming", "cobalt", "mellow",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
const ROLE_ADJECTIVES = {
|
|
19
|
+
// Product: framing, scoping, drawing the shape of the thing.
|
|
20
|
+
product: [
|
|
21
|
+
"framing", "scoping", "sketching", "charting", "mapping", "shaping",
|
|
22
|
+
"drafting", "defining", "aligning", "outlining", "plotting", "clarifying",
|
|
23
|
+
"sifting", "naming", "tracing", "weighing",
|
|
24
|
+
],
|
|
25
|
+
// Dev: making, joining, finishing.
|
|
26
|
+
dev: [
|
|
27
|
+
"stacking", "moulding", "polishing", "welding", "forging", "wiring",
|
|
28
|
+
"threading", "bolting", "carving", "splicing", "tuning", "grafting",
|
|
29
|
+
"patching", "sanding", "riveting", "kneading",
|
|
30
|
+
],
|
|
31
|
+
// QA: the ones who do not believe you.
|
|
32
|
+
qa: [
|
|
33
|
+
"prying", "probing", "nervous", "doubting", "squinting", "prodding",
|
|
34
|
+
"restless", "watchful", "snooping", "needling", "sceptical", "twitchy",
|
|
35
|
+
"poking", "sniffing", "fretting", "inquisitive",
|
|
36
|
+
],
|
|
37
|
+
// Review: weighing the whole chain against what was asked.
|
|
38
|
+
reviewer: [
|
|
39
|
+
"weighing", "tallying", "auditing", "squaring", "measuring", "exacting",
|
|
40
|
+
"comparing", "reconciling", "scrupulous", "unblinking", "steady", "sifting",
|
|
41
|
+
"checking", "thorough", "literal", "deliberate",
|
|
42
|
+
],
|
|
43
|
+
// Ops: keeping it up.
|
|
44
|
+
ops: [
|
|
45
|
+
"steady", "vigilant", "hardy", "watchful", "ready", "unflappable",
|
|
46
|
+
"tireless", "sturdy", "braced", "wakeful", "durable", "constant",
|
|
47
|
+
],
|
|
48
|
+
// Research / docs.
|
|
49
|
+
research: [
|
|
50
|
+
"reading", "digging", "rummaging", "quoting", "indexing", "annotating",
|
|
51
|
+
"citing", "leafing", "combing", "collating", "footnoting", "marginal",
|
|
52
|
+
],
|
|
53
|
+
writer: [
|
|
54
|
+
"lucid", "plain", "spare", "fluent", "eloquent", "pithy", "limpid",
|
|
55
|
+
"unfussy", "measured", "readable", "concise", "clean",
|
|
56
|
+
],
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Personas can supply their own list; otherwise the role's, otherwise generic. */
|
|
60
|
+
export function adjectivesFor(role, custom) {
|
|
61
|
+
if (Array.isArray(custom) && custom.length) return custom
|
|
62
|
+
if (!role) return ADJECTIVES
|
|
63
|
+
const key = String(role).toLowerCase()
|
|
64
|
+
return (
|
|
65
|
+
ROLE_ADJECTIVES[key] ??
|
|
66
|
+
ROLE_ADJECTIVES[Object.keys(ROLE_ADJECTIVES).find((k) => key.includes(k)) ?? ""] ??
|
|
67
|
+
ADJECTIVES
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export { ROLE_ADJECTIVES }
|
|
72
|
+
|
|
73
|
+
// Every creature here has a distinct glyph, because the glyph *is* the avatar.
|
|
74
|
+
// A handle of "velvet-hedgehog" and a hedgehog face are the same fact twice,
|
|
75
|
+
// which is what makes a wall of 40 cards scannable.
|
|
76
|
+
const CREATURES = [
|
|
77
|
+
["otter", "\u{1F9A6}"], ["owl", "\u{1F989}"], ["fox", "\u{1F98A}"], ["hare", "\u{1F407}"],
|
|
78
|
+
["seal", "\u{1F9AD}"], ["beetle", "\u{1FAB2}"], ["badger", "\u{1F9A1}"], ["bison", "\u{1F9AC}"],
|
|
79
|
+
["gecko", "\u{1F98E}"], ["toad", "\u{1F438}"], ["koi", "\u{1F420}"], ["crab", "\u{1F980}"],
|
|
80
|
+
["squid", "\u{1F991}"], ["moth", "\u{1F98B}"], ["snail", "\u{1F40C}"], ["bat", "\u{1F987}"],
|
|
81
|
+
["boar", "\u{1F417}"], ["camel", "\u{1F42A}"], ["llama", "\u{1F999}"], ["sloth", "\u{1F9A5}"],
|
|
82
|
+
["skunk", "\u{1F9A8}"], ["raccoon", "\u{1F99D}"], ["swan", "\u{1F9A2}"], ["duck", "\u{1F986}"],
|
|
83
|
+
["peacock", "\u{1F99A}"], ["parrot", "\u{1F99C}"], ["dodo", "\u{1FAB6}"], ["flamingo", "\u{1F9A9}"],
|
|
84
|
+
["hedgehog", "\u{1F994}"], ["mouse", "\u{1F42D}"], ["hamster", "\u{1F439}"], ["wolf", "\u{1F43A}"],
|
|
85
|
+
["panda", "\u{1F43C}"], ["koala", "\u{1F428}"], ["tiger", "\u{1F42F}"], ["lion", "\u{1F981}"],
|
|
86
|
+
["monkey", "\u{1F435}"], ["penguin", "\u{1F427}"], ["eagle", "\u{1F985}"], ["rooster", "\u{1F413}"],
|
|
87
|
+
["horse", "\u{1F434}"], ["zebra", "\u{1F993}"], ["deer", "\u{1F98C}"], ["goat", "\u{1F410}"],
|
|
88
|
+
["chipmunk", "\u{1F43F}"], ["bear", "\u{1F43B}"], ["whale", "\u{1F433}"], ["shark", "\u{1F988}"],
|
|
89
|
+
["octopus", "\u{1F419}"], ["ladybug", "\u{1F41E}"], ["ant", "\u{1F41C}"], ["bee", "\u{1F41D}"],
|
|
90
|
+
["cricket", "\u{1F997}"], ["lobster", "\u{1F99E}"], ["turtle", "\u{1F422}"], ["snake", "\u{1F40D}"],
|
|
91
|
+
["elephant", "\u{1F418}"], ["rhino", "\u{1F98F}"], ["hippo", "\u{1F99B}"], ["giraffe", "\u{1F992}"],
|
|
92
|
+
["kangaroo", "\u{1F998}"], ["gorilla", "\u{1F98D}"], ["beaver", "\u{1F9AB}"], ["pufferfish", "\u{1F421}"],
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
const GLYPH = new Map(CREATURES)
|
|
96
|
+
|
|
97
|
+
// FNV-1a: stable across processes, unlike anything built on Math.random.
|
|
98
|
+
export function hash(str) {
|
|
99
|
+
let h = 0x811c9dc5
|
|
100
|
+
for (let i = 0; i < str.length; i++) {
|
|
101
|
+
h ^= str.charCodeAt(i)
|
|
102
|
+
h = Math.imul(h, 0x01000193) >>> 0
|
|
103
|
+
}
|
|
104
|
+
return h >>> 0
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function rng(seed) {
|
|
108
|
+
let s = seed || 1
|
|
109
|
+
return () => {
|
|
110
|
+
s ^= s << 13; s >>>= 0
|
|
111
|
+
s ^= s >> 17
|
|
112
|
+
s ^= s << 5; s >>>= 0
|
|
113
|
+
return s / 0x100000000
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Handles must be unique among live sessions or addressing breaks, so a
|
|
119
|
+
* collision walks to the next free pair rather than appending a number.
|
|
120
|
+
*/
|
|
121
|
+
export function handleFor(id, taken = new Set(), { role = null, adjectives = null } = {}) {
|
|
122
|
+
const h = hash(id)
|
|
123
|
+
const pool = adjectivesFor(role, adjectives)
|
|
124
|
+
for (let i = 0; i < pool.length * CREATURES.length; i++) {
|
|
125
|
+
const a = pool[(h + i * 7) % pool.length]
|
|
126
|
+
const c = CREATURES[(Math.floor(h / 32) + i * 3) % CREATURES.length][0]
|
|
127
|
+
const name = `${a}-${c}`
|
|
128
|
+
if (!taken.has(name)) return name
|
|
129
|
+
}
|
|
130
|
+
return `agent-${id.slice(-4)}`
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// [glyph-side accent, background]. Sixteen backgrounds against sixty-four
|
|
134
|
+
// creatures is a thousand faces before two of them look alike, and the pairs
|
|
135
|
+
// are picked for contrast against the panel rather than for variety alone.
|
|
136
|
+
const PALETTE = [
|
|
137
|
+
["#ffab2e", "#6b4310"], ["#4cc3ff", "#134a63"], ["#b494ff", "#3c2d70"],
|
|
138
|
+
["#5fd0d6", "#14484b"], ["#ff8f6b", "#6b2d19"], ["#e6c14a", "#584613"],
|
|
139
|
+
["#7fd17f", "#1e4a24"], ["#ff8fb8", "#66253f"], ["#8fb3ff", "#22386e"],
|
|
140
|
+
["#d1a0ff", "#4a2a63"], ["#ffd166", "#5f4611"], ["#66e0c0", "#134c3f"],
|
|
141
|
+
["#ff9ed8", "#5e2450"], ["#9be36b", "#33531b"], ["#6fc0ff", "#173f61"],
|
|
142
|
+
["#ffbf7a", "#61401b"],
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The avatar is the creature in the handle, on a colour drawn from the
|
|
147
|
+
* adjective. Two axes a person can actually name ("the orange otter") beat a
|
|
148
|
+
* random bit-grid, which is why this is a glyph and not an identicon: at 26px
|
|
149
|
+
* a 5x5 noise square is indistinguishable from the next 5x5 noise square, and
|
|
150
|
+
* forty of them on one screen is noise by definition.
|
|
151
|
+
*
|
|
152
|
+
* Identicon bits are kept as the fallback for anything without a handle.
|
|
153
|
+
*/
|
|
154
|
+
/** The avatar's colours, so the rest of the UI can match a bot to its face. */
|
|
155
|
+
export function colorFor(id) {
|
|
156
|
+
const [fg, bg] = PALETTE[hash("avatar:" + id) % PALETTE.length]
|
|
157
|
+
return { fg, bg }
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function avatarFor(id, { size = 56, handle = null } = {}) {
|
|
161
|
+
const h = hash("avatar:" + id)
|
|
162
|
+
const [fg, bg] = PALETTE[h % PALETTE.length]
|
|
163
|
+
const creature = handle?.split("-").slice(1).join("-")
|
|
164
|
+
const glyph = creature && GLYPH.get(creature)
|
|
165
|
+
if (glyph) {
|
|
166
|
+
return (
|
|
167
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="${size}" height="${size}" role="img" aria-label="${handle}">` +
|
|
168
|
+
`<rect width="32" height="32" rx="7" fill="${bg}"/>` +
|
|
169
|
+
`<rect x="0.6" y="0.6" width="30.8" height="30.8" rx="6.6" fill="none" stroke="${fg}" stroke-opacity=".55"/>` +
|
|
170
|
+
`<text x="16" y="17.5" font-size="21" text-anchor="middle" dominant-baseline="central">${glyph}</text>` +
|
|
171
|
+
`</svg>`
|
|
172
|
+
)
|
|
173
|
+
}
|
|
174
|
+
const rand = rng(h)
|
|
175
|
+
const cells = []
|
|
176
|
+
for (let x = 0; x < 3; x++) {
|
|
177
|
+
for (let y = 0; y < 5; y++) if (rand() > 0.45) cells.push([x, y], [4 - x, y])
|
|
178
|
+
}
|
|
179
|
+
return (
|
|
180
|
+
`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5 5" width="${size}" height="${size}" ` +
|
|
181
|
+
`shape-rendering="crispEdges" role="img" aria-hidden="true">` +
|
|
182
|
+
`<rect width="5" height="5" fill="${bg}"/>` +
|
|
183
|
+
cells.map(([x, y]) => `<rect x="${x}" y="${y}" width="1" height="1" fill="${fg}"/>`).join("") +
|
|
184
|
+
`</svg>`
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export { GLYPH }
|
|
189
|
+
|
|
190
|
+
export function avatarDataUri(id, opts) {
|
|
191
|
+
return "data:image/svg+xml;base64," + Buffer.from(avatarFor(id, opts)).toString("base64")
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Tokens authenticate an MCP caller back to one session. */
|
|
195
|
+
export function mintToken() {
|
|
196
|
+
const b = Buffer.alloc(18)
|
|
197
|
+
for (let i = 0; i < b.length; i++) b[i] = Math.floor(Math.random() * 256)
|
|
198
|
+
return b.toString("base64url")
|
|
199
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Built-in definitions as they shipped before, so a workspace file that
|
|
2
|
+
// still has them unmodified can be moved to the current ones. Anything you
|
|
3
|
+
// have edited is left alone.
|
|
4
|
+
export const LEGACY = {
|
|
5
|
+
agents: {
|
|
6
|
+
product: [`id: product
|
|
7
|
+
title: Product Bot
|
|
8
|
+
role: product
|
|
9
|
+
# model: anthropic/claude-sonnet-4-5
|
|
10
|
+
# tools: [read, grep, glob] # omit for everything the session has
|
|
11
|
+
# may_spawn: false
|
|
12
|
+
prompt: |
|
|
13
|
+
You are the product analyst on this change.
|
|
14
|
+
|
|
15
|
+
Your job is to turn a rough story into acceptance criteria that a developer
|
|
16
|
+
can build against and a tester can verify. Read the code before you write
|
|
17
|
+
them: criteria that ignore how the system actually works are worse than none.
|
|
18
|
+
|
|
19
|
+
Write criteria that are observable. "Login works" is not a criterion.
|
|
20
|
+
"After SSO login, the user lands on /home and the session cookie is set with
|
|
21
|
+
SameSite=Lax" is. Between four and eight of them is usually right.
|
|
22
|
+
|
|
23
|
+
You do not write production code.
|
|
24
|
+
`],
|
|
25
|
+
},
|
|
26
|
+
stages: {
|
|
27
|
+
"story/analyse": [`stages:
|
|
28
|
+
- id: analyse
|
|
29
|
+
persona: product
|
|
30
|
+
title: Sharpen the story into acceptance criteria
|
|
31
|
+
receives: []
|
|
32
|
+
prompt: |
|
|
33
|
+
Story to analyse:
|
|
34
|
+
|
|
35
|
+
{{story}}
|
|
36
|
+
|
|
37
|
+
Read enough of the codebase to understand how this area works today,
|
|
38
|
+
then write the acceptance criteria. Put them in acceptance_criteria on
|
|
39
|
+
your handoff — everything downstream is built and tested against that
|
|
40
|
+
list, so it is the most important thing you produce.
|
|
41
|
+
|
|
42
|
+
`],
|
|
43
|
+
},
|
|
44
|
+
}
|