@poa-box/agent 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/.env.agent.template +20 -0
- package/README.md +46 -0
- package/brain/Config/agent-config.json +14 -0
- package/brain/Config/brain-allowlist.json +20 -0
- package/brain/Identity/goals.template.md +23 -0
- package/brain/Identity/how-i-think.md +406 -0
- package/brain/Identity/who-i-am.template.md +34 -0
- package/brain/Knowledge/BOOTSTRAP.md +66 -0
- package/brain/Knowledge/audit-corpus-index.json +406 -0
- package/brain/Knowledge/discussions.json +245 -0
- package/brain/Knowledge/pop.brain.brainstorms.generated.md +48 -0
- package/brain/Knowledge/pop.brain.brainstorms.genesis.bin +0 -0
- package/brain/Knowledge/pop.brain.heuristics.snapshot.bin +0 -0
- package/brain/Knowledge/pop.brain.projects.generated.md +16 -0
- package/brain/Knowledge/pop.brain.projects.genesis.bin +0 -0
- package/brain/Knowledge/pop.brain.retros.generated.md +91 -0
- package/brain/Knowledge/pop.brain.retros.genesis.bin +0 -0
- package/brain/Knowledge/pop.brain.shared.generated.md +3811 -0
- package/brain/Knowledge/pop.brain.shared.genesis.bin +0 -0
- package/brain/Knowledge/projects.md +181 -0
- package/brain/Knowledge/risk-framework.md +90 -0
- package/brain/Knowledge/shared.md +416 -0
- package/brain/Knowledge/sprint-priorities.md +439 -0
- package/brain/Memory/.gitkeep +0 -0
- package/dist/commands/agent/daily-digest.d.ts +24 -0
- package/dist/commands/agent/daily-digest.js +336 -0
- package/dist/commands/agent/delegate.d.ts +12 -0
- package/dist/commands/agent/delegate.js +91 -0
- package/dist/commands/agent/deploy-to-org.d.ts +20 -0
- package/dist/commands/agent/deploy-to-org.js +154 -0
- package/dist/commands/agent/index.d.ts +2 -0
- package/dist/commands/agent/index.js +27 -0
- package/dist/commands/agent/init.d.ts +19 -0
- package/dist/commands/agent/init.js +303 -0
- package/dist/commands/agent/onboard.d.ts +22 -0
- package/dist/commands/agent/onboard.js +192 -0
- package/dist/commands/agent/paymaster-status.d.ts +14 -0
- package/dist/commands/agent/paymaster-status.js +130 -0
- package/dist/commands/agent/register.d.ts +21 -0
- package/dist/commands/agent/register.js +116 -0
- package/dist/commands/agent/setup-sponsorship.d.ts +22 -0
- package/dist/commands/agent/setup-sponsorship.js +154 -0
- package/dist/commands/agent/status.d.ts +12 -0
- package/dist/commands/agent/status.js +171 -0
- package/dist/commands/agent/triage.d.ts +12 -0
- package/dist/commands/agent/triage.js +503 -0
- package/dist/commands/brain/advance-stage.d.ts +42 -0
- package/dist/commands/brain/advance-stage.js +206 -0
- package/dist/commands/brain/allowlist.d.ts +30 -0
- package/dist/commands/brain/allowlist.js +274 -0
- package/dist/commands/brain/append-lesson.d.ts +55 -0
- package/dist/commands/brain/append-lesson.js +245 -0
- package/dist/commands/brain/brainstorm.d.ts +154 -0
- package/dist/commands/brain/brainstorm.js +573 -0
- package/dist/commands/brain/daemon.d.ts +31 -0
- package/dist/commands/brain/daemon.js +348 -0
- package/dist/commands/brain/doctor.d.ts +27 -0
- package/dist/commands/brain/doctor.js +497 -0
- package/dist/commands/brain/edit-lesson.d.ts +51 -0
- package/dist/commands/brain/edit-lesson.js +248 -0
- package/dist/commands/brain/import-snapshot.d.ts +68 -0
- package/dist/commands/brain/import-snapshot.js +177 -0
- package/dist/commands/brain/index.d.ts +2 -0
- package/dist/commands/brain/index.js +67 -0
- package/dist/commands/brain/list.d.ts +21 -0
- package/dist/commands/brain/list.js +83 -0
- package/dist/commands/brain/migrate-projects.d.ts +44 -0
- package/dist/commands/brain/migrate-projects.js +209 -0
- package/dist/commands/brain/migrate.d.ts +74 -0
- package/dist/commands/brain/migrate.js +306 -0
- package/dist/commands/brain/new-project.d.ts +53 -0
- package/dist/commands/brain/new-project.js +226 -0
- package/dist/commands/brain/read.d.ts +24 -0
- package/dist/commands/brain/read.js +81 -0
- package/dist/commands/brain/remove-lesson.d.ts +47 -0
- package/dist/commands/brain/remove-lesson.js +206 -0
- package/dist/commands/brain/remove-project.d.ts +36 -0
- package/dist/commands/brain/remove-project.js +177 -0
- package/dist/commands/brain/retro-file-tasks.d.ts +84 -0
- package/dist/commands/brain/retro-file-tasks.js +372 -0
- package/dist/commands/brain/retro-list.d.ts +28 -0
- package/dist/commands/brain/retro-list.js +125 -0
- package/dist/commands/brain/retro-mark-change.d.ts +58 -0
- package/dist/commands/brain/retro-mark-change.js +176 -0
- package/dist/commands/brain/retro-remove.d.ts +36 -0
- package/dist/commands/brain/retro-remove.js +142 -0
- package/dist/commands/brain/retro-respond.d.ts +56 -0
- package/dist/commands/brain/retro-respond.js +250 -0
- package/dist/commands/brain/retro-show.d.ts +23 -0
- package/dist/commands/brain/retro-show.js +100 -0
- package/dist/commands/brain/retro-start.d.ts +55 -0
- package/dist/commands/brain/retro-start.js +311 -0
- package/dist/commands/brain/search.d.ts +48 -0
- package/dist/commands/brain/search.js +190 -0
- package/dist/commands/brain/snapshot.d.ts +32 -0
- package/dist/commands/brain/snapshot.js +243 -0
- package/dist/commands/brain/status.d.ts +15 -0
- package/dist/commands/brain/status.js +166 -0
- package/dist/commands/brain/subscribe.d.ts +28 -0
- package/dist/commands/brain/subscribe.js +90 -0
- package/dist/commands/brain/tag.d.ts +46 -0
- package/dist/commands/brain/tag.js +192 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +22 -0
- package/dist/lib/brain-daemon.d.ts +126 -0
- package/dist/lib/brain-daemon.js +811 -0
- package/dist/lib/brain-membership.d.ts +58 -0
- package/dist/lib/brain-membership.js +115 -0
- package/dist/lib/brain-migrate-projects.d.ts +43 -0
- package/dist/lib/brain-migrate-projects.js +247 -0
- package/dist/lib/brain-migrate.d.ts +77 -0
- package/dist/lib/brain-migrate.js +328 -0
- package/dist/lib/brain-ops.d.ts +271 -0
- package/dist/lib/brain-ops.js +571 -0
- package/dist/lib/brain-paths.d.ts +15 -0
- package/dist/lib/brain-paths.js +33 -0
- package/dist/lib/brain-projections.d.ts +216 -0
- package/dist/lib/brain-projections.js +829 -0
- package/dist/lib/brain-schemas.d.ts +36 -0
- package/dist/lib/brain-schemas.js +316 -0
- package/dist/lib/brain-signing.d.ts +103 -0
- package/dist/lib/brain-signing.js +256 -0
- package/dist/lib/brain.d.ts +198 -0
- package/dist/lib/brain.js +1057 -0
- package/dist/pop-agent.d.ts +1 -0
- package/dist/pop-agent.js +18 -0
- package/docs/agent.md +126 -0
- package/docs/agents/brain-anti-entropy.md +127 -0
- package/docs/agents/brain-cross-device-onboarding.md +210 -0
- package/docs/agents/brain-cross-machine-smoke.md +241 -0
- package/docs/agents/brain-layer-setup.md +725 -0
- package/docs/agents/offboarding-protocol.md +188 -0
- package/docs/agents/onboarding-protocol.md +243 -0
- package/docs/agents/running-an-agent.md +200 -0
- package/docs/brain.md +560 -0
- package/package.json +61 -0
- package/scripts/apply.sh +140 -0
- package/scripts/onboard.sh +205 -0
- package/scripts/setup-agent.ts +272 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain advance-stage — move a project through the lifecycle.
|
|
4
|
+
*
|
|
5
|
+
* Second write command for the pop.brain.projects doc shape. Finds a
|
|
6
|
+
* project by id, advances its stage to either an explicit --to target
|
|
7
|
+
* or the next stage in the canonical PROPOSE → DISCUSS → PLAN → VOTE
|
|
8
|
+
* → EXECUTE → REVIEW → SHIP sequence, and records a lastStageAdvanceAt
|
|
9
|
+
* timestamp. Short-circuits no-op transitions, errors cleanly on
|
|
10
|
+
* not-found, and refuses to advance past 'ship'.
|
|
11
|
+
*
|
|
12
|
+
* Design note: the canonical order is a SOFT convention at the data
|
|
13
|
+
* layer — any stage string is accepted on disk (so legacy / scripted
|
|
14
|
+
* writes can use custom stages). advance-stage enforces the order
|
|
15
|
+
* only for the --to-omitted "next stage" case. Passing --to <stage>
|
|
16
|
+
* sets the stage directly without checking that the transition makes
|
|
17
|
+
* sense (this allows 'discuss → ship' when the operator knows what
|
|
18
|
+
* they're doing, at the cost of letting a typo jump over stages).
|
|
19
|
+
*/
|
|
20
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
23
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
24
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
25
|
+
}
|
|
26
|
+
Object.defineProperty(o, k2, desc);
|
|
27
|
+
}) : (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
o[k2] = m[k];
|
|
30
|
+
}));
|
|
31
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
32
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
33
|
+
}) : function(o, v) {
|
|
34
|
+
o["default"] = v;
|
|
35
|
+
});
|
|
36
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
37
|
+
var ownKeys = function(o) {
|
|
38
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
39
|
+
var ar = [];
|
|
40
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
41
|
+
return ar;
|
|
42
|
+
};
|
|
43
|
+
return ownKeys(o);
|
|
44
|
+
};
|
|
45
|
+
return function (mod) {
|
|
46
|
+
if (mod && mod.__esModule) return mod;
|
|
47
|
+
var result = {};
|
|
48
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
49
|
+
__setModuleDefault(result, mod);
|
|
50
|
+
return result;
|
|
51
|
+
};
|
|
52
|
+
})();
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.advanceStageHandler = void 0;
|
|
55
|
+
const ethers_1 = require("ethers");
|
|
56
|
+
const brain_1 = require("../../lib/brain");
|
|
57
|
+
const brain_ops_1 = require("../../lib/brain-ops");
|
|
58
|
+
const idempotency_1 = require("@poa-box/cli/lib/idempotency");
|
|
59
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
60
|
+
const STAGE_ORDER = [
|
|
61
|
+
'propose', 'discuss', 'plan', 'vote', 'execute', 'review', 'ship',
|
|
62
|
+
];
|
|
63
|
+
function nextStage(current) {
|
|
64
|
+
const idx = STAGE_ORDER.indexOf(current);
|
|
65
|
+
if (idx === -1)
|
|
66
|
+
return null; // Unknown current stage — caller errors.
|
|
67
|
+
if (idx === STAGE_ORDER.length - 1)
|
|
68
|
+
return null; // Already at ship.
|
|
69
|
+
return STAGE_ORDER[idx + 1];
|
|
70
|
+
}
|
|
71
|
+
exports.advanceStageHandler = {
|
|
72
|
+
builder: (yargs) => yargs
|
|
73
|
+
.option('doc', {
|
|
74
|
+
describe: 'Brain document ID (typically pop.brain.projects)',
|
|
75
|
+
type: 'string',
|
|
76
|
+
demandOption: true,
|
|
77
|
+
})
|
|
78
|
+
.option('project-id', {
|
|
79
|
+
describe: 'The project.id to advance',
|
|
80
|
+
type: 'string',
|
|
81
|
+
demandOption: true,
|
|
82
|
+
})
|
|
83
|
+
.option('to', {
|
|
84
|
+
describe: 'Explicit target stage (default: next stage in canonical order)',
|
|
85
|
+
type: 'string',
|
|
86
|
+
choices: STAGE_ORDER,
|
|
87
|
+
})
|
|
88
|
+
.option('idempotency-key', { type: 'string', describe: 'Task #375 (HB#217) idempotency cache. Agent-scoped.' })
|
|
89
|
+
.option('no-idempotency', { type: 'boolean', default: false, describe: 'Bypass the idempotency cache.' }),
|
|
90
|
+
handler: async (argv) => {
|
|
91
|
+
try {
|
|
92
|
+
// Pre-flight: locate the project, emit actionable not-found error.
|
|
93
|
+
const { doc: currentDoc } = await (0, brain_1.openBrainDoc)(argv.doc);
|
|
94
|
+
const projects = Array.isArray(currentDoc?.projects) ? currentDoc.projects : [];
|
|
95
|
+
const target = projects.find((p) => p?.id === argv.projectId);
|
|
96
|
+
if (!target) {
|
|
97
|
+
const candidates = projects
|
|
98
|
+
.map((p) => p?.id)
|
|
99
|
+
.filter((id) => typeof id === 'string')
|
|
100
|
+
.slice(0, 8);
|
|
101
|
+
output.error(`Project "${argv.projectId}" not found in ${argv.doc}. ` +
|
|
102
|
+
`Available ids (first 8): ${candidates.join(', ') || '(none)'}`);
|
|
103
|
+
process.exitCode = 1;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
// Resolve the target stage.
|
|
107
|
+
let nextStageValue;
|
|
108
|
+
if (argv.to) {
|
|
109
|
+
nextStageValue = argv.to;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
const computed = nextStage(target.stage ?? '');
|
|
113
|
+
if (!computed) {
|
|
114
|
+
if (target.stage === 'ship') {
|
|
115
|
+
output.error(`Project "${argv.projectId}" is already at stage 'ship' — no next stage. ` +
|
|
116
|
+
`Use --to <stage> if you need to override.`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
output.error(`Project "${argv.projectId}" has unknown current stage "${target.stage}" — ` +
|
|
120
|
+
`cannot compute next stage automatically. Pass --to <stage> to set explicitly.`);
|
|
121
|
+
}
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
nextStageValue = computed;
|
|
126
|
+
}
|
|
127
|
+
// Short-circuit: stage is already the target.
|
|
128
|
+
if (target.stage === nextStageValue) {
|
|
129
|
+
if (output.isJsonMode()) {
|
|
130
|
+
output.json({
|
|
131
|
+
status: 'noop',
|
|
132
|
+
docId: argv.doc,
|
|
133
|
+
projectId: argv.projectId,
|
|
134
|
+
stage: nextStageValue,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
console.log(`Project "${argv.projectId}" is already at stage '${nextStageValue}'. No-op.`);
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const now = Math.floor(Date.now() / 1000);
|
|
143
|
+
const priorStage = target.stage;
|
|
144
|
+
// Task #375 (HB#217): agent-scoped idempotency cache
|
|
145
|
+
const signerKey = process.env.POP_PRIVATE_KEY;
|
|
146
|
+
const authorScope = signerKey ? new ethers_1.ethers.Wallet(signerKey).address.toLowerCase() : 'anonymous';
|
|
147
|
+
const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
|
|
148
|
+
if (!argv.noIdempotency) {
|
|
149
|
+
const cached = (0, idempotency_1.checkIdempotencyCache)(authorScope, 'brain.advanceStage', idempKey);
|
|
150
|
+
if (cached) {
|
|
151
|
+
if (output.isJsonMode()) {
|
|
152
|
+
output.json({ status: 'ok', cached: true, ...cached });
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
console.log(` Project "${argv.projectId}" stage already advanced (idempotency cache hit). head: ${cached.headCid}`);
|
|
156
|
+
}
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Route through the unified dispatcher (HB#324 ship-2).
|
|
161
|
+
const result = await (0, brain_ops_1.routedDispatch)({
|
|
162
|
+
type: 'advanceStage',
|
|
163
|
+
docId: argv.doc,
|
|
164
|
+
projectId: argv.projectId,
|
|
165
|
+
newStage: nextStageValue,
|
|
166
|
+
lastStageAdvanceAt: now,
|
|
167
|
+
});
|
|
168
|
+
if (!argv.noIdempotency) {
|
|
169
|
+
(0, idempotency_1.recordIdempotentResult)(authorScope, 'brain.advanceStage', idempKey, {
|
|
170
|
+
docId: argv.doc,
|
|
171
|
+
projectId: argv.projectId,
|
|
172
|
+
stage: nextStageValue,
|
|
173
|
+
headCid: result.headCid,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
if (output.isJsonMode()) {
|
|
177
|
+
output.json({
|
|
178
|
+
status: 'ok',
|
|
179
|
+
docId: argv.doc,
|
|
180
|
+
projectId: argv.projectId,
|
|
181
|
+
priorStage,
|
|
182
|
+
stage: nextStageValue,
|
|
183
|
+
lastStageAdvanceAt: now,
|
|
184
|
+
headCid: result.headCid,
|
|
185
|
+
routedViaDaemon: result.routedViaDaemon,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
console.log('');
|
|
190
|
+
console.log(` Project "${argv.projectId}" advanced in ${argv.doc}`);
|
|
191
|
+
console.log(` ${priorStage} → ${nextStageValue}`);
|
|
192
|
+
console.log(` lastStageAdvanceAt: ${new Date(now * 1000).toISOString()}`);
|
|
193
|
+
console.log(` new head: ${result.headCid}`);
|
|
194
|
+
console.log(` routed: ${result.routedViaDaemon ? 'via brain daemon' : 'in-process (no daemon)'}`);
|
|
195
|
+
console.log('');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
output.error(err.message);
|
|
200
|
+
process.exitCode = 1;
|
|
201
|
+
}
|
|
202
|
+
finally {
|
|
203
|
+
await (0, brain_1.stopBrainNode)();
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pop brain allowlist — list/add/remove operator commands for the
|
|
3
|
+
* agent/brain/Config/brain-allowlist.json file.
|
|
4
|
+
*
|
|
5
|
+
* These are thin CLI wrappers over direct JSON writes. The security
|
|
6
|
+
* boundary stays at git: the file is git-tracked, changes land via
|
|
7
|
+
* PR + review, and the current 3-agent Argus setup treats any write
|
|
8
|
+
* as needing peer approval. The commands just reduce friction vs
|
|
9
|
+
* hand-editing JSON.
|
|
10
|
+
*
|
|
11
|
+
* Runtime vs build-time: the allowlist is read via `readFileSync`
|
|
12
|
+
* on every `isAllowedAuthor` call (see `brain-signing.ts loadAllowlist`).
|
|
13
|
+
* A git pull is enough to pick up new entries — agents do NOT need
|
|
14
|
+
* to rebuild dist/ just because an address was added. Rebuild only
|
|
15
|
+
* matters when `brain-signing.ts` itself changes.
|
|
16
|
+
*
|
|
17
|
+
* Not in scope for this command:
|
|
18
|
+
* - `pop brain allowlist sync` — propagating changes via the brain
|
|
19
|
+
* layer itself (chicken-and-egg: the allowlist change would need
|
|
20
|
+
* to come from an allowlisted author, which gives you zero benefit
|
|
21
|
+
* over the current git flow for a new-agent onboarding).
|
|
22
|
+
* - Governance-backed allowlist changes — each edit going through a
|
|
23
|
+
* formal `pop vote` would be a clean pattern but is heavy for MVP.
|
|
24
|
+
* Current review-via-git approximates it.
|
|
25
|
+
*/
|
|
26
|
+
import type { Argv } from 'yargs';
|
|
27
|
+
export declare const allowlistHandler: {
|
|
28
|
+
builder: (yargs: Argv) => Argv<{}>;
|
|
29
|
+
handler: () => void;
|
|
30
|
+
};
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain allowlist — list/add/remove operator commands for the
|
|
4
|
+
* agent/brain/Config/brain-allowlist.json file.
|
|
5
|
+
*
|
|
6
|
+
* These are thin CLI wrappers over direct JSON writes. The security
|
|
7
|
+
* boundary stays at git: the file is git-tracked, changes land via
|
|
8
|
+
* PR + review, and the current 3-agent Argus setup treats any write
|
|
9
|
+
* as needing peer approval. The commands just reduce friction vs
|
|
10
|
+
* hand-editing JSON.
|
|
11
|
+
*
|
|
12
|
+
* Runtime vs build-time: the allowlist is read via `readFileSync`
|
|
13
|
+
* on every `isAllowedAuthor` call (see `brain-signing.ts loadAllowlist`).
|
|
14
|
+
* A git pull is enough to pick up new entries — agents do NOT need
|
|
15
|
+
* to rebuild dist/ just because an address was added. Rebuild only
|
|
16
|
+
* matters when `brain-signing.ts` itself changes.
|
|
17
|
+
*
|
|
18
|
+
* Not in scope for this command:
|
|
19
|
+
* - `pop brain allowlist sync` — propagating changes via the brain
|
|
20
|
+
* layer itself (chicken-and-egg: the allowlist change would need
|
|
21
|
+
* to come from an allowlisted author, which gives you zero benefit
|
|
22
|
+
* over the current git flow for a new-agent onboarding).
|
|
23
|
+
* - Governance-backed allowlist changes — each edit going through a
|
|
24
|
+
* formal `pop vote` would be a clean pattern but is heavy for MVP.
|
|
25
|
+
* Current review-via-git approximates it.
|
|
26
|
+
*/
|
|
27
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
28
|
+
if (k2 === undefined) k2 = k;
|
|
29
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
30
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
31
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
32
|
+
}
|
|
33
|
+
Object.defineProperty(o, k2, desc);
|
|
34
|
+
}) : (function(o, m, k, k2) {
|
|
35
|
+
if (k2 === undefined) k2 = k;
|
|
36
|
+
o[k2] = m[k];
|
|
37
|
+
}));
|
|
38
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
39
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
40
|
+
}) : function(o, v) {
|
|
41
|
+
o["default"] = v;
|
|
42
|
+
});
|
|
43
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
44
|
+
var ownKeys = function(o) {
|
|
45
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
46
|
+
var ar = [];
|
|
47
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
48
|
+
return ar;
|
|
49
|
+
};
|
|
50
|
+
return ownKeys(o);
|
|
51
|
+
};
|
|
52
|
+
return function (mod) {
|
|
53
|
+
if (mod && mod.__esModule) return mod;
|
|
54
|
+
var result = {};
|
|
55
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
56
|
+
__setModuleDefault(result, mod);
|
|
57
|
+
return result;
|
|
58
|
+
};
|
|
59
|
+
})();
|
|
60
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
61
|
+
exports.allowlistHandler = void 0;
|
|
62
|
+
const fs_1 = require("fs");
|
|
63
|
+
const ethers_1 = require("ethers");
|
|
64
|
+
const brain_signing_1 = require("../../lib/brain-signing");
|
|
65
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
66
|
+
const ADDRESS_RE = /^0x[0-9a-fA-F]{40}$/;
|
|
67
|
+
/**
|
|
68
|
+
* Normalize an address input: must match the standard hex format, and
|
|
69
|
+
* gets lowercased before storage/comparison. Throws with a clean error
|
|
70
|
+
* message on malformed input (yargs catches and reports).
|
|
71
|
+
*/
|
|
72
|
+
function normalizeAddress(raw) {
|
|
73
|
+
if (!ADDRESS_RE.test(raw)) {
|
|
74
|
+
throw new Error(`Invalid address "${raw}" — must be a 0x-prefixed 40-character hex string.`);
|
|
75
|
+
}
|
|
76
|
+
return raw.toLowerCase();
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Read the full allowlist file (not just via loadAllowlist which
|
|
80
|
+
* normalizes the shape). Preserves every field that's on disk,
|
|
81
|
+
* including any addedNote / future extensions. Returns an empty
|
|
82
|
+
* array if the file doesn't exist yet.
|
|
83
|
+
*/
|
|
84
|
+
function readRawAllowlist() {
|
|
85
|
+
const p = (0, brain_signing_1.getAllowlistPath)();
|
|
86
|
+
if (!(0, fs_1.existsSync)(p))
|
|
87
|
+
return [];
|
|
88
|
+
try {
|
|
89
|
+
const raw = JSON.parse((0, fs_1.readFileSync)(p, 'utf8'));
|
|
90
|
+
if (Array.isArray(raw))
|
|
91
|
+
return raw;
|
|
92
|
+
if (Array.isArray(raw?.entries))
|
|
93
|
+
return raw.entries;
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
throw new Error(`Failed to read allowlist at ${p}: ${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function writeAllowlist(entries) {
|
|
101
|
+
(0, fs_1.writeFileSync)((0, brain_signing_1.getAllowlistPath)(), JSON.stringify(entries, null, 2) + '\n');
|
|
102
|
+
}
|
|
103
|
+
const listHandler = {
|
|
104
|
+
builder: (yargs) => yargs,
|
|
105
|
+
handler: async (_argv) => {
|
|
106
|
+
const entries = (0, brain_signing_1.loadAllowlist)();
|
|
107
|
+
if (output.isJsonMode()) {
|
|
108
|
+
output.json({ status: 'ok', count: entries.length, entries });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
console.log('');
|
|
112
|
+
console.log(` Brain allowlist (${entries.length} entr${entries.length === 1 ? 'y' : 'ies'})`);
|
|
113
|
+
console.log(' ' + '─'.repeat(70));
|
|
114
|
+
if (entries.length === 0) {
|
|
115
|
+
console.log(' (empty)');
|
|
116
|
+
console.log('');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const e of entries) {
|
|
120
|
+
const name = e.name ?? '(no name)';
|
|
121
|
+
console.log(` ${e.address} ${name}`);
|
|
122
|
+
if (e.addedAt)
|
|
123
|
+
console.log(` addedAt: ${e.addedAt}`);
|
|
124
|
+
if (e.addedBy)
|
|
125
|
+
console.log(` addedBy: ${e.addedBy}`);
|
|
126
|
+
}
|
|
127
|
+
console.log('');
|
|
128
|
+
console.log(` File: ${(0, brain_signing_1.getAllowlistPath)()}`);
|
|
129
|
+
console.log('');
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
const addHandler = {
|
|
133
|
+
builder: (yargs) => yargs
|
|
134
|
+
.option('address', {
|
|
135
|
+
describe: '0x-prefixed 40-char hex address to allowlist',
|
|
136
|
+
type: 'string',
|
|
137
|
+
demandOption: true,
|
|
138
|
+
})
|
|
139
|
+
.option('name', {
|
|
140
|
+
describe: 'Human label (e.g. agent username)',
|
|
141
|
+
type: 'string',
|
|
142
|
+
})
|
|
143
|
+
.option('note', {
|
|
144
|
+
describe: 'Optional addedNote field recorded on the entry',
|
|
145
|
+
type: 'string',
|
|
146
|
+
}),
|
|
147
|
+
handler: async (argv) => {
|
|
148
|
+
try {
|
|
149
|
+
const address = normalizeAddress(argv.address);
|
|
150
|
+
const entries = readRawAllowlist();
|
|
151
|
+
if (entries.some(e => String(e.address).toLowerCase() === address)) {
|
|
152
|
+
output.error(`Address ${address} is already in the allowlist.`);
|
|
153
|
+
process.exitCode = 1;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
// Derive addedBy from POP_PRIVATE_KEY if available so the entry
|
|
157
|
+
// records WHICH existing allowlisted agent is performing the
|
|
158
|
+
// add. Falls back to 'unknown' if no key is set (e.g. running
|
|
159
|
+
// the command on a fresh machine that hasn't configured a key
|
|
160
|
+
// yet — legitimate during bootstrap).
|
|
161
|
+
let addedBy;
|
|
162
|
+
const key = process.env.POP_PRIVATE_KEY;
|
|
163
|
+
if (key) {
|
|
164
|
+
try {
|
|
165
|
+
addedBy = new ethers_1.ethers.Wallet(key).address.toLowerCase();
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
addedBy = 'unknown';
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
addedBy = 'unknown';
|
|
173
|
+
}
|
|
174
|
+
const entry = {
|
|
175
|
+
address,
|
|
176
|
+
name: argv.name,
|
|
177
|
+
addedAt: new Date().toISOString().slice(0, 10), // YYYY-MM-DD
|
|
178
|
+
addedBy,
|
|
179
|
+
};
|
|
180
|
+
if (argv.note)
|
|
181
|
+
entry.addedNote = argv.note;
|
|
182
|
+
entries.push(entry);
|
|
183
|
+
writeAllowlist(entries);
|
|
184
|
+
if (output.isJsonMode()) {
|
|
185
|
+
output.json({
|
|
186
|
+
status: 'ok',
|
|
187
|
+
added: entry,
|
|
188
|
+
count: entries.length,
|
|
189
|
+
path: (0, brain_signing_1.getAllowlistPath)(),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
console.log('');
|
|
194
|
+
console.log(` Added ${address} to the allowlist.`);
|
|
195
|
+
if (entry.name)
|
|
196
|
+
console.log(` name: ${entry.name}`);
|
|
197
|
+
console.log(` addedAt: ${entry.addedAt}`);
|
|
198
|
+
console.log(` addedBy: ${entry.addedBy}`);
|
|
199
|
+
if (entry.addedNote)
|
|
200
|
+
console.log(` note: ${entry.addedNote}`);
|
|
201
|
+
console.log('');
|
|
202
|
+
console.log(' The change is local until committed. Next:');
|
|
203
|
+
console.log(' git add agent/brain/Config/brain-allowlist.json');
|
|
204
|
+
console.log(` git commit -m "Allowlist ${entry.name ?? address}"`);
|
|
205
|
+
console.log(' git push # PR for review');
|
|
206
|
+
console.log('');
|
|
207
|
+
console.log(' Once other agents git pull, the new address is live —');
|
|
208
|
+
console.log(' no rebuild required (loadAllowlist reads the file at runtime).');
|
|
209
|
+
console.log('');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch (err) {
|
|
213
|
+
output.error(err.message);
|
|
214
|
+
process.exitCode = 1;
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
const removeHandler = {
|
|
219
|
+
builder: (yargs) => yargs.option('address', {
|
|
220
|
+
describe: '0x-prefixed 40-char hex address to remove',
|
|
221
|
+
type: 'string',
|
|
222
|
+
demandOption: true,
|
|
223
|
+
}),
|
|
224
|
+
handler: async (argv) => {
|
|
225
|
+
try {
|
|
226
|
+
const address = normalizeAddress(argv.address);
|
|
227
|
+
const entries = readRawAllowlist();
|
|
228
|
+
const idx = entries.findIndex(e => String(e.address).toLowerCase() === address);
|
|
229
|
+
if (idx === -1) {
|
|
230
|
+
const existing = entries.map(e => String(e.address).toLowerCase()).slice(0, 8);
|
|
231
|
+
output.error(`Address ${address} not in allowlist. Existing (first 8): ${existing.join(', ') || '(empty)'}`);
|
|
232
|
+
process.exitCode = 1;
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const removed = entries.splice(idx, 1)[0];
|
|
236
|
+
writeAllowlist(entries);
|
|
237
|
+
if (output.isJsonMode()) {
|
|
238
|
+
output.json({
|
|
239
|
+
status: 'ok',
|
|
240
|
+
removed,
|
|
241
|
+
count: entries.length,
|
|
242
|
+
path: (0, brain_signing_1.getAllowlistPath)(),
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
console.log('');
|
|
247
|
+
console.log(` Removed ${address} from the allowlist.`);
|
|
248
|
+
if (removed.name)
|
|
249
|
+
console.log(` was: ${removed.name}`);
|
|
250
|
+
console.log(` ${entries.length} entries remain.`);
|
|
251
|
+
console.log('');
|
|
252
|
+
console.log(' The change is local until committed. Next:');
|
|
253
|
+
console.log(' git add agent/brain/Config/brain-allowlist.json');
|
|
254
|
+
console.log(` git commit -m "Remove ${removed.name ?? address} from allowlist"`);
|
|
255
|
+
console.log(' git push # PR for review');
|
|
256
|
+
console.log('');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch (err) {
|
|
260
|
+
output.error(err.message);
|
|
261
|
+
process.exitCode = 1;
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
exports.allowlistHandler = {
|
|
266
|
+
builder: (yargs) => yargs
|
|
267
|
+
.command('list', 'Show current brain allowlist entries', listHandler.builder, listHandler.handler)
|
|
268
|
+
.command('add', 'Add an address to the brain allowlist', addHandler.builder, addHandler.handler)
|
|
269
|
+
.command('remove', 'Remove an address from the brain allowlist', removeHandler.builder, removeHandler.handler)
|
|
270
|
+
.demandCommand(1, 'Please specify a brain allowlist action (list/add/remove)'),
|
|
271
|
+
handler: () => {
|
|
272
|
+
// Top-level dispatch — the subcommand handlers do the actual work.
|
|
273
|
+
},
|
|
274
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pop brain append-lesson — user-facing write path into the CRDT brain
|
|
3
|
+
* substrate. Thin wrapper over applyBrainChange that pushes a lesson
|
|
4
|
+
* object onto `doc.lessons` and auto-publishes the new head via
|
|
5
|
+
* gossipsub (step 5) + seeds it for Bitswap (step 6).
|
|
6
|
+
*
|
|
7
|
+
* Before this command, writing a lesson required a Node script that
|
|
8
|
+
* imported dist/lib/brain.js and called applyBrainChange manually.
|
|
9
|
+
* This command closes the last ergonomic gap before the brain
|
|
10
|
+
* substrate is day-to-day usable for heartbeat work.
|
|
11
|
+
*
|
|
12
|
+
* Usage:
|
|
13
|
+
* pop brain append-lesson --doc pop.brain.shared \
|
|
14
|
+
* --title "Don't upgrade libp2p past 2.x" \
|
|
15
|
+
* --body "gossipsub 14 crashes on libp2p 3+ ... "
|
|
16
|
+
*
|
|
17
|
+
* pop brain append-lesson --doc pop.brain.shared \
|
|
18
|
+
* --title "Long form lesson" \
|
|
19
|
+
* --body-file /tmp/lesson.md
|
|
20
|
+
*/
|
|
21
|
+
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
|
22
|
+
interface AppendArgs {
|
|
23
|
+
doc: string;
|
|
24
|
+
title: string;
|
|
25
|
+
body?: string;
|
|
26
|
+
bodyFile?: string;
|
|
27
|
+
author?: string;
|
|
28
|
+
id?: string;
|
|
29
|
+
allowInvalidShape?: boolean;
|
|
30
|
+
'idempotency-key'?: string;
|
|
31
|
+
'no-idempotency'?: boolean;
|
|
32
|
+
}
|
|
33
|
+
export declare const appendLessonHandler: {
|
|
34
|
+
builder: (yargs: Argv) => Argv<{
|
|
35
|
+
doc: string;
|
|
36
|
+
} & {
|
|
37
|
+
title: string;
|
|
38
|
+
} & {
|
|
39
|
+
body: string | undefined;
|
|
40
|
+
} & {
|
|
41
|
+
"body-file": string | undefined;
|
|
42
|
+
} & {
|
|
43
|
+
author: string | undefined;
|
|
44
|
+
} & {
|
|
45
|
+
id: string | undefined;
|
|
46
|
+
} & {
|
|
47
|
+
"allow-invalid-shape": boolean;
|
|
48
|
+
} & {
|
|
49
|
+
"idempotency-key": string | undefined;
|
|
50
|
+
} & {
|
|
51
|
+
"no-idempotency": boolean;
|
|
52
|
+
}>;
|
|
53
|
+
handler: (argv: ArgumentsCamelCase<AppendArgs>) => Promise<void>;
|
|
54
|
+
};
|
|
55
|
+
export {};
|