@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,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain edit-lesson — update fields on an existing brain lesson.
|
|
4
|
+
*
|
|
5
|
+
* Finds a lesson by its id inside a brain doc, mutates the provided
|
|
6
|
+
* fields in-place, and persists the result via applyBrainChange so
|
|
7
|
+
* the edit goes through the full sign → persist → gossipsub publish
|
|
8
|
+
* pipeline. Edits are idempotent from the user's perspective: running
|
|
9
|
+
* the same edit twice produces the same final state (the second run
|
|
10
|
+
* is a no-op if no field actually changes).
|
|
11
|
+
*
|
|
12
|
+
* Scope: edit in place only. No remove, no re-id, no ordering mutation.
|
|
13
|
+
* Remove requires CRDT tombstone semantics which are a separate ship.
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.editLessonHandler = void 0;
|
|
50
|
+
const fs_1 = require("fs");
|
|
51
|
+
const path_1 = require("path");
|
|
52
|
+
const brain_1 = require("../../lib/brain");
|
|
53
|
+
const brain_ops_1 = require("../../lib/brain-ops");
|
|
54
|
+
const ethers_1 = require("ethers");
|
|
55
|
+
const idempotency_1 = require("@poa-box/cli/lib/idempotency");
|
|
56
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
57
|
+
exports.editLessonHandler = {
|
|
58
|
+
builder: (yargs) => yargs
|
|
59
|
+
.option('doc', {
|
|
60
|
+
describe: 'Brain document ID (e.g. pop.brain.shared)',
|
|
61
|
+
type: 'string',
|
|
62
|
+
demandOption: true,
|
|
63
|
+
})
|
|
64
|
+
.option('lesson-id', {
|
|
65
|
+
describe: 'The lesson.id of the entry to update',
|
|
66
|
+
type: 'string',
|
|
67
|
+
demandOption: true,
|
|
68
|
+
})
|
|
69
|
+
.option('title', { describe: 'New title', type: 'string' })
|
|
70
|
+
.option('body', { describe: 'New body (inline)', type: 'string' })
|
|
71
|
+
.option('body-file', { describe: 'Path to a file whose contents replace the body', type: 'string' })
|
|
72
|
+
.option('author', { describe: 'Override the author label', type: 'string' })
|
|
73
|
+
.option('touch', {
|
|
74
|
+
describe: 'Bump the lesson.timestamp to now on edit (default: preserve original timestamp)',
|
|
75
|
+
type: 'boolean',
|
|
76
|
+
default: false,
|
|
77
|
+
})
|
|
78
|
+
.option('allow-invalid-shape', {
|
|
79
|
+
describe: 'Bypass write-time schema validation (Task #346).',
|
|
80
|
+
type: 'boolean',
|
|
81
|
+
default: false,
|
|
82
|
+
})
|
|
83
|
+
.option('idempotency-key', {
|
|
84
|
+
type: 'string',
|
|
85
|
+
describe: 'Task #374 (HB#215): explicit idempotency key. Agent-scoped.',
|
|
86
|
+
})
|
|
87
|
+
.option('no-idempotency', {
|
|
88
|
+
type: 'boolean',
|
|
89
|
+
default: false,
|
|
90
|
+
describe: 'Bypass the idempotency cache.',
|
|
91
|
+
})
|
|
92
|
+
.check((argv) => {
|
|
93
|
+
if (argv.title === undefined &&
|
|
94
|
+
argv.body === undefined &&
|
|
95
|
+
argv['body-file'] === undefined &&
|
|
96
|
+
argv.author === undefined) {
|
|
97
|
+
throw new Error('Must supply at least one of --title / --body / --body-file / --author');
|
|
98
|
+
}
|
|
99
|
+
return true;
|
|
100
|
+
}),
|
|
101
|
+
handler: async (argv) => {
|
|
102
|
+
try {
|
|
103
|
+
// Pre-flight: open the doc and verify the target lesson exists.
|
|
104
|
+
// We do a read-only peek first so we can fail fast with a
|
|
105
|
+
// useful error (list candidate ids) instead of touching the
|
|
106
|
+
// blockstore for a no-op change.
|
|
107
|
+
const { doc: currentDoc } = await (0, brain_1.openBrainDoc)(argv.doc);
|
|
108
|
+
const lessons = Array.isArray(currentDoc?.lessons) ? currentDoc.lessons : [];
|
|
109
|
+
const target = lessons.find((l) => l?.id === argv.lessonId);
|
|
110
|
+
if (!target) {
|
|
111
|
+
const candidates = lessons
|
|
112
|
+
.map((l) => l?.id)
|
|
113
|
+
.filter((id) => typeof id === 'string')
|
|
114
|
+
.slice(0, 8);
|
|
115
|
+
output.error(`Lesson "${argv.lessonId}" not found in ${argv.doc}. ` +
|
|
116
|
+
`Available ids (first 8): ${candidates.join(', ') || '(none)'}`);
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Resolve body replacement content if --body-file was given.
|
|
121
|
+
let bodyReplacement;
|
|
122
|
+
if (argv.bodyFile !== undefined) {
|
|
123
|
+
const p = (0, path_1.resolve)(argv.bodyFile);
|
|
124
|
+
if (!(0, fs_1.existsSync)(p)) {
|
|
125
|
+
output.error(`--body-file not found: ${p}`);
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
bodyReplacement = (0, fs_1.readFileSync)(p, 'utf8').replace(/\s+$/, '');
|
|
130
|
+
}
|
|
131
|
+
else if (argv.body !== undefined) {
|
|
132
|
+
bodyReplacement = argv.body.trim();
|
|
133
|
+
}
|
|
134
|
+
// Capture "before" state for the diff report so the operator
|
|
135
|
+
// can see what actually changed.
|
|
136
|
+
const before = {
|
|
137
|
+
title: target.title,
|
|
138
|
+
body: target.body,
|
|
139
|
+
author: target.author,
|
|
140
|
+
timestamp: target.timestamp,
|
|
141
|
+
};
|
|
142
|
+
const after = { ...before };
|
|
143
|
+
if (argv.title !== undefined)
|
|
144
|
+
after.title = argv.title;
|
|
145
|
+
if (bodyReplacement !== undefined)
|
|
146
|
+
after.body = bodyReplacement;
|
|
147
|
+
if (argv.author !== undefined)
|
|
148
|
+
after.author = argv.author;
|
|
149
|
+
if (argv.touch)
|
|
150
|
+
after.timestamp = Math.floor(Date.now() / 1000);
|
|
151
|
+
// If nothing actually changed, short-circuit — don't burn a new
|
|
152
|
+
// CID on a no-op.
|
|
153
|
+
const changedKeys = Object.keys(after).filter(k => after[k] !== before[k]);
|
|
154
|
+
if (changedKeys.length === 0) {
|
|
155
|
+
if (output.isJsonMode()) {
|
|
156
|
+
output.json({ status: 'noop', docId: argv.doc, lessonId: argv.lessonId });
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
console.log(`No changes to apply — all requested fields already match. No new head produced.`);
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// Task #374: idempotency check. Brain writes are agent-scoped, so
|
|
164
|
+
// the scope component is the signing wallet address.
|
|
165
|
+
const signerKey = process.env.POP_PRIVATE_KEY;
|
|
166
|
+
const authorScope = signerKey ? new ethers_1.ethers.Wallet(signerKey).address.toLowerCase() : 'anonymous';
|
|
167
|
+
const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
|
|
168
|
+
if (!argv.noIdempotency) {
|
|
169
|
+
const cached = (0, idempotency_1.checkIdempotencyCache)(authorScope, 'brain.editLesson', idempKey);
|
|
170
|
+
if (cached) {
|
|
171
|
+
if (output.isJsonMode()) {
|
|
172
|
+
output.json({ status: 'ok', cached: true, ...cached });
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
console.log('');
|
|
176
|
+
console.log(` Lesson "${argv.lessonId}" edit cached (idempotency hit)`);
|
|
177
|
+
console.log(` head: ${cached.headCid}`);
|
|
178
|
+
console.log('');
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Route through the unified dispatcher. When the brain daemon is
|
|
184
|
+
// running, this serializes an `editLesson` op and sends it via IPC
|
|
185
|
+
// so the write lands in the daemon's long-lived libp2p context.
|
|
186
|
+
// When no daemon, dispatchOp runs in-process (same applyBrainChange
|
|
187
|
+
// call path as before).
|
|
188
|
+
const fields = {};
|
|
189
|
+
if (argv.title !== undefined)
|
|
190
|
+
fields.title = argv.title;
|
|
191
|
+
if (bodyReplacement !== undefined)
|
|
192
|
+
fields.body = bodyReplacement;
|
|
193
|
+
if (argv.author !== undefined)
|
|
194
|
+
fields.author = argv.author;
|
|
195
|
+
const result = await (0, brain_ops_1.routedDispatch)({
|
|
196
|
+
type: 'editLesson',
|
|
197
|
+
docId: argv.doc,
|
|
198
|
+
lessonId: argv.lessonId,
|
|
199
|
+
fields,
|
|
200
|
+
touch: argv.touch === true,
|
|
201
|
+
allowInvalidShape: argv.allowInvalidShape,
|
|
202
|
+
});
|
|
203
|
+
if (!argv.noIdempotency) {
|
|
204
|
+
(0, idempotency_1.recordIdempotentResult)(authorScope, 'brain.editLesson', idempKey, {
|
|
205
|
+
docId: argv.doc,
|
|
206
|
+
lessonId: argv.lessonId,
|
|
207
|
+
headCid: result.headCid,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
if (output.isJsonMode()) {
|
|
211
|
+
output.json({
|
|
212
|
+
status: 'ok',
|
|
213
|
+
docId: argv.doc,
|
|
214
|
+
lessonId: argv.lessonId,
|
|
215
|
+
headCid: result.headCid,
|
|
216
|
+
envelopeAuthor: result.envelopeAuthor,
|
|
217
|
+
routedViaDaemon: result.routedViaDaemon,
|
|
218
|
+
changedKeys,
|
|
219
|
+
before,
|
|
220
|
+
after,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
console.log('');
|
|
225
|
+
console.log(` Lesson "${argv.lessonId}" updated in ${argv.doc}`);
|
|
226
|
+
console.log(` changed: ${changedKeys.join(', ')}`);
|
|
227
|
+
console.log(` new head: ${result.headCid}`);
|
|
228
|
+
console.log(` routed: ${result.routedViaDaemon ? 'via brain daemon' : 'in-process (no daemon)'}`);
|
|
229
|
+
console.log('');
|
|
230
|
+
for (const k of changedKeys) {
|
|
231
|
+
const b = String(before[k] ?? '(unset)').slice(0, 100);
|
|
232
|
+
const a = String(after[k] ?? '(unset)').slice(0, 100);
|
|
233
|
+
console.log(` ${k}:`);
|
|
234
|
+
console.log(` - ${b}`);
|
|
235
|
+
console.log(` + ${a}`);
|
|
236
|
+
}
|
|
237
|
+
console.log('');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
output.error(err.message);
|
|
242
|
+
process.exitCode = 1;
|
|
243
|
+
}
|
|
244
|
+
finally {
|
|
245
|
+
await (0, brain_1.stopBrainNode)();
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pop brain import-snapshot — load a raw Automerge snapshot as the new local
|
|
3
|
+
* head for a brain doc. Task #353 (HB#348) migration tool for converging
|
|
4
|
+
* disjoint brain state across existing agents.
|
|
5
|
+
*
|
|
6
|
+
* Typical use:
|
|
7
|
+
*
|
|
8
|
+
* # Operator on vigil_01's machine, after fetching argus's baseline from
|
|
9
|
+
* # IPFS (see HB#341 brain lesson `argus-baseline-exported-for-353-migration`
|
|
10
|
+
* # for the pinned CIDs):
|
|
11
|
+
*
|
|
12
|
+
* curl https://ipfs.io/ipfs/QmPk6tiY2AHZyXVCFpPeRyAUY2WviCkDq6iAheokEzRbd7 > /tmp/shared.json
|
|
13
|
+
* node -e "
|
|
14
|
+
* const j = require('/tmp/shared.json');
|
|
15
|
+
* require('fs').writeFileSync('/tmp/shared.bin', Buffer.from(j.base64, 'base64'));
|
|
16
|
+
* "
|
|
17
|
+
*
|
|
18
|
+
* pop brain daemon stop # safety — no writes during migration
|
|
19
|
+
* pop brain read --doc pop.brain.shared --json > /tmp/local-backup.json # backup current state
|
|
20
|
+
* pop brain import-snapshot --doc pop.brain.shared \
|
|
21
|
+
* --file /tmp/shared.bin \
|
|
22
|
+
* --force # required if local head exists
|
|
23
|
+
* pop brain daemon start # restart daemon with new head
|
|
24
|
+
*
|
|
25
|
+
* ## Safety
|
|
26
|
+
*
|
|
27
|
+
* - `--force` is REQUIRED when the local brain home already has a manifest
|
|
28
|
+
* entry for the target doc. Without `--force`, the command refuses and
|
|
29
|
+
* tells the operator to back up first.
|
|
30
|
+
* - Operators are responsible for preserving local-only content BEFORE
|
|
31
|
+
* running this command. Use `pop brain read --doc <id> --json` to snapshot
|
|
32
|
+
* current state, replay any local-only lessons via `pop brain append-lesson`
|
|
33
|
+
* AFTER the import lands.
|
|
34
|
+
* - The import runs write-time schema validation (#346) by default. Pass
|
|
35
|
+
* `--allow-invalid-shape` only when you know the source bytes deliberately
|
|
36
|
+
* contain a non-canonical shape.
|
|
37
|
+
*
|
|
38
|
+
* ## Why this command exists
|
|
39
|
+
*
|
|
40
|
+
* HB#333-335 discovered that Automerge.merge silently drops content across
|
|
41
|
+
* disjoint histories. HB#337 task #352 shipped shared-genesis bootstrap so
|
|
42
|
+
* NEW agents joining post-PR-#10 share a common root. But the 3 existing
|
|
43
|
+
* Argus agents (argus_prime / vigil_01 / sentinel_01) each independently
|
|
44
|
+
* initialized their pop.brain.shared BEFORE #352 landed, so they remain
|
|
45
|
+
* mutually disjoint. Task #353 handles the one-time migration from disjoint
|
|
46
|
+
* back to shared-root, using one agent's current state as the canonical
|
|
47
|
+
* baseline that the other two import.
|
|
48
|
+
*/
|
|
49
|
+
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
|
50
|
+
interface ImportSnapshotArgs {
|
|
51
|
+
doc: string;
|
|
52
|
+
file: string;
|
|
53
|
+
force?: boolean;
|
|
54
|
+
allowInvalidShape?: boolean;
|
|
55
|
+
}
|
|
56
|
+
export declare const importSnapshotHandler: {
|
|
57
|
+
builder: (yargs: Argv) => Argv<{
|
|
58
|
+
doc: string;
|
|
59
|
+
} & {
|
|
60
|
+
file: string;
|
|
61
|
+
} & {
|
|
62
|
+
force: boolean;
|
|
63
|
+
} & {
|
|
64
|
+
"allow-invalid-shape": boolean;
|
|
65
|
+
}>;
|
|
66
|
+
handler: (argv: ArgumentsCamelCase<ImportSnapshotArgs>) => Promise<void>;
|
|
67
|
+
};
|
|
68
|
+
export {};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain import-snapshot — load a raw Automerge snapshot as the new local
|
|
4
|
+
* head for a brain doc. Task #353 (HB#348) migration tool for converging
|
|
5
|
+
* disjoint brain state across existing agents.
|
|
6
|
+
*
|
|
7
|
+
* Typical use:
|
|
8
|
+
*
|
|
9
|
+
* # Operator on vigil_01's machine, after fetching argus's baseline from
|
|
10
|
+
* # IPFS (see HB#341 brain lesson `argus-baseline-exported-for-353-migration`
|
|
11
|
+
* # for the pinned CIDs):
|
|
12
|
+
*
|
|
13
|
+
* curl https://ipfs.io/ipfs/QmPk6tiY2AHZyXVCFpPeRyAUY2WviCkDq6iAheokEzRbd7 > /tmp/shared.json
|
|
14
|
+
* node -e "
|
|
15
|
+
* const j = require('/tmp/shared.json');
|
|
16
|
+
* require('fs').writeFileSync('/tmp/shared.bin', Buffer.from(j.base64, 'base64'));
|
|
17
|
+
* "
|
|
18
|
+
*
|
|
19
|
+
* pop brain daemon stop # safety — no writes during migration
|
|
20
|
+
* pop brain read --doc pop.brain.shared --json > /tmp/local-backup.json # backup current state
|
|
21
|
+
* pop brain import-snapshot --doc pop.brain.shared \
|
|
22
|
+
* --file /tmp/shared.bin \
|
|
23
|
+
* --force # required if local head exists
|
|
24
|
+
* pop brain daemon start # restart daemon with new head
|
|
25
|
+
*
|
|
26
|
+
* ## Safety
|
|
27
|
+
*
|
|
28
|
+
* - `--force` is REQUIRED when the local brain home already has a manifest
|
|
29
|
+
* entry for the target doc. Without `--force`, the command refuses and
|
|
30
|
+
* tells the operator to back up first.
|
|
31
|
+
* - Operators are responsible for preserving local-only content BEFORE
|
|
32
|
+
* running this command. Use `pop brain read --doc <id> --json` to snapshot
|
|
33
|
+
* current state, replay any local-only lessons via `pop brain append-lesson`
|
|
34
|
+
* AFTER the import lands.
|
|
35
|
+
* - The import runs write-time schema validation (#346) by default. Pass
|
|
36
|
+
* `--allow-invalid-shape` only when you know the source bytes deliberately
|
|
37
|
+
* contain a non-canonical shape.
|
|
38
|
+
*
|
|
39
|
+
* ## Why this command exists
|
|
40
|
+
*
|
|
41
|
+
* HB#333-335 discovered that Automerge.merge silently drops content across
|
|
42
|
+
* disjoint histories. HB#337 task #352 shipped shared-genesis bootstrap so
|
|
43
|
+
* NEW agents joining post-PR-#10 share a common root. But the 3 existing
|
|
44
|
+
* Argus agents (argus_prime / vigil_01 / sentinel_01) each independently
|
|
45
|
+
* initialized their pop.brain.shared BEFORE #352 landed, so they remain
|
|
46
|
+
* mutually disjoint. Task #353 handles the one-time migration from disjoint
|
|
47
|
+
* back to shared-root, using one agent's current state as the canonical
|
|
48
|
+
* baseline that the other two import.
|
|
49
|
+
*/
|
|
50
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
51
|
+
if (k2 === undefined) k2 = k;
|
|
52
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
53
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
54
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
55
|
+
}
|
|
56
|
+
Object.defineProperty(o, k2, desc);
|
|
57
|
+
}) : (function(o, m, k, k2) {
|
|
58
|
+
if (k2 === undefined) k2 = k;
|
|
59
|
+
o[k2] = m[k];
|
|
60
|
+
}));
|
|
61
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
62
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
63
|
+
}) : function(o, v) {
|
|
64
|
+
o["default"] = v;
|
|
65
|
+
});
|
|
66
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
67
|
+
var ownKeys = function(o) {
|
|
68
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
69
|
+
var ar = [];
|
|
70
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
71
|
+
return ar;
|
|
72
|
+
};
|
|
73
|
+
return ownKeys(o);
|
|
74
|
+
};
|
|
75
|
+
return function (mod) {
|
|
76
|
+
if (mod && mod.__esModule) return mod;
|
|
77
|
+
var result = {};
|
|
78
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
79
|
+
__setModuleDefault(result, mod);
|
|
80
|
+
return result;
|
|
81
|
+
};
|
|
82
|
+
})();
|
|
83
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
84
|
+
exports.importSnapshotHandler = void 0;
|
|
85
|
+
const fs_1 = require("fs");
|
|
86
|
+
const path_1 = require("path");
|
|
87
|
+
const brain_1 = require("../../lib/brain");
|
|
88
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
89
|
+
exports.importSnapshotHandler = {
|
|
90
|
+
builder: (yargs) => yargs
|
|
91
|
+
.option('doc', {
|
|
92
|
+
describe: 'Target brain document ID (e.g. pop.brain.shared)',
|
|
93
|
+
type: 'string',
|
|
94
|
+
demandOption: true,
|
|
95
|
+
})
|
|
96
|
+
.option('file', {
|
|
97
|
+
describe: 'Path to the raw Automerge snapshot bytes to import (.bin file produced by Automerge.save())',
|
|
98
|
+
type: 'string',
|
|
99
|
+
demandOption: true,
|
|
100
|
+
})
|
|
101
|
+
.option('force', {
|
|
102
|
+
describe: 'Required when a local head already exists for this doc. The existing head becomes orphaned (old envelope stays in blockstore but manifest no longer points at it). Back up local-only content via `pop brain read --doc <id> --json` BEFORE using this flag.',
|
|
103
|
+
type: 'boolean',
|
|
104
|
+
default: false,
|
|
105
|
+
})
|
|
106
|
+
.option('allow-invalid-shape', {
|
|
107
|
+
describe: 'Bypass write-time schema validation (#346). Use only when the source bytes deliberately contain a non-canonical shape.',
|
|
108
|
+
type: 'boolean',
|
|
109
|
+
default: false,
|
|
110
|
+
}),
|
|
111
|
+
handler: async (argv) => {
|
|
112
|
+
try {
|
|
113
|
+
// Resolve + validate input file.
|
|
114
|
+
const filePath = (0, path_1.resolve)(argv.file);
|
|
115
|
+
if (!(0, fs_1.existsSync)(filePath)) {
|
|
116
|
+
output.error(`--file not found: ${filePath}`);
|
|
117
|
+
process.exitCode = 1;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const bytes = (0, fs_1.readFileSync)(filePath);
|
|
121
|
+
if (bytes.length === 0) {
|
|
122
|
+
output.error(`--file is empty: ${filePath}`);
|
|
123
|
+
process.exitCode = 1;
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Safety gate: refuse to clobber an existing head unless --force.
|
|
127
|
+
const existing = (0, brain_1.listBrainDocs)().find(d => d.docId === argv.doc);
|
|
128
|
+
if (existing && !argv.force) {
|
|
129
|
+
output.error(`Local brain home already has a head for "${argv.doc}" ` +
|
|
130
|
+
`(${existing.headCid}). Importing would orphan the existing state. ` +
|
|
131
|
+
`Back up local-only content first via \`pop brain read --doc ${argv.doc} --json\`, ` +
|
|
132
|
+
`then re-run this command with --force to confirm.`);
|
|
133
|
+
process.exitCode = 1;
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
// Import. importBrainDoc validates via Automerge.load() + schema check,
|
|
137
|
+
// signs new envelope, writes block, updates manifest, publishes head.
|
|
138
|
+
const result = await (0, brain_1.importBrainDoc)(argv.doc, new Uint8Array(bytes), { allowInvalidShape: argv.allowInvalidShape === true });
|
|
139
|
+
if (output.isJsonMode()) {
|
|
140
|
+
output.json({
|
|
141
|
+
status: 'ok',
|
|
142
|
+
docId: argv.doc,
|
|
143
|
+
sourceFile: filePath,
|
|
144
|
+
sourceBytes: bytes.length,
|
|
145
|
+
newHeadCid: result.headCid,
|
|
146
|
+
envelopeAuthor: result.author,
|
|
147
|
+
replacedExistingHead: existing?.headCid ?? null,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.log('');
|
|
152
|
+
console.log(` Snapshot imported as new head for ${argv.doc}`);
|
|
153
|
+
console.log(` source file: ${filePath} (${bytes.length} bytes)`);
|
|
154
|
+
console.log(` new head: ${result.headCid}`);
|
|
155
|
+
console.log(` envelope author: ${result.author}`);
|
|
156
|
+
if (existing) {
|
|
157
|
+
console.log(` replaced head: ${existing.headCid} (orphaned in blockstore)`);
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
console.log(` previous head: (none — this is a fresh import)`);
|
|
161
|
+
}
|
|
162
|
+
console.log('');
|
|
163
|
+
console.log(` Next steps: replay any local-only content (e.g. lessons this agent wrote ` +
|
|
164
|
+
`but that weren't in the source snapshot) via pop brain append-lesson. ` +
|
|
165
|
+
`Then restart the brain daemon if it was stopped for the migration.`);
|
|
166
|
+
console.log('');
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
output.error(err.message);
|
|
171
|
+
process.exitCode = 1;
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
await (0, brain_1.stopBrainNode)();
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerBrainCommands = registerBrainCommands;
|
|
4
|
+
const status_1 = require("./status");
|
|
5
|
+
const subscribe_1 = require("./subscribe");
|
|
6
|
+
const read_1 = require("./read");
|
|
7
|
+
const list_1 = require("./list");
|
|
8
|
+
const snapshot_1 = require("./snapshot");
|
|
9
|
+
const migrate_1 = require("./migrate");
|
|
10
|
+
const append_lesson_1 = require("./append-lesson");
|
|
11
|
+
const edit_lesson_1 = require("./edit-lesson");
|
|
12
|
+
const remove_lesson_1 = require("./remove-lesson");
|
|
13
|
+
const search_1 = require("./search");
|
|
14
|
+
const tag_1 = require("./tag");
|
|
15
|
+
const brainstorm_1 = require("./brainstorm");
|
|
16
|
+
const new_project_1 = require("./new-project");
|
|
17
|
+
const advance_stage_1 = require("./advance-stage");
|
|
18
|
+
const remove_project_1 = require("./remove-project");
|
|
19
|
+
const allowlist_1 = require("./allowlist");
|
|
20
|
+
const migrate_projects_1 = require("./migrate-projects");
|
|
21
|
+
const doctor_1 = require("./doctor");
|
|
22
|
+
const import_snapshot_1 = require("./import-snapshot");
|
|
23
|
+
const daemon_1 = require("./daemon");
|
|
24
|
+
const retro_start_1 = require("./retro-start");
|
|
25
|
+
const retro_list_1 = require("./retro-list");
|
|
26
|
+
const retro_show_1 = require("./retro-show");
|
|
27
|
+
const retro_respond_1 = require("./retro-respond");
|
|
28
|
+
const retro_file_tasks_1 = require("./retro-file-tasks");
|
|
29
|
+
const retro_mark_change_1 = require("./retro-mark-change");
|
|
30
|
+
const retro_remove_1 = require("./retro-remove");
|
|
31
|
+
function registerBrainCommands(yargs) {
|
|
32
|
+
return yargs
|
|
33
|
+
.command('status', 'Show local brain layer state (Helia + CRDT sync)', status_1.statusHandler.builder, status_1.statusHandler.handler)
|
|
34
|
+
.command('subscribe', 'Subscribe to a doc topic; fetch + merge announced blocks (or --log-only)', subscribe_1.subscribeHandler.builder, subscribe_1.subscribeHandler.handler)
|
|
35
|
+
.command('read', 'Load a brain doc from local state and print its contents', read_1.readHandler.builder, read_1.readHandler.handler)
|
|
36
|
+
.command('list', 'List all known brain docs with their current head CIDs', list_1.listHandler.builder, list_1.listHandler.handler)
|
|
37
|
+
.command('snapshot', 'Project a brain doc to markdown on disk (step 7)', snapshot_1.snapshotHandler.builder, snapshot_1.snapshotHandler.handler)
|
|
38
|
+
.command('migrate', 'Import a hand-written markdown file into a brain doc (step 8)', migrate_1.migrateHandler.builder, migrate_1.migrateHandler.handler)
|
|
39
|
+
.command('append-lesson', 'Append a lesson to a brain doc (signed + gossipsub-published)', append_lesson_1.appendLessonHandler.builder, append_lesson_1.appendLessonHandler.handler)
|
|
40
|
+
.command('edit-lesson', 'Update fields on an existing brain lesson (in-place)', edit_lesson_1.editLessonHandler.builder, edit_lesson_1.editLessonHandler.handler)
|
|
41
|
+
.command('remove-lesson', 'Soft-delete a brain lesson (tombstone; filtered from snapshot output)', remove_lesson_1.removeLessonHandler.builder, remove_lesson_1.removeLessonHandler.handler)
|
|
42
|
+
.command('search', 'Filter lessons in a brain doc by query / tag / author / timestamp', search_1.searchHandler.builder, search_1.searchHandler.handler)
|
|
43
|
+
.command('tag', 'Add or remove tags on an existing brain lesson', tag_1.tagHandler.builder, tag_1.tagHandler.handler)
|
|
44
|
+
.command('brainstorm-start', 'Open a new cross-agent brainstorm (task #354 — forward-looking ideation surface)', brainstorm_1.brainstormStartHandler.builder, brainstorm_1.brainstormStartHandler.handler)
|
|
45
|
+
.command('brainstorm-respond', 'Post a message, add an idea, or cast votes on an existing brainstorm', brainstorm_1.brainstormRespondHandler.builder, brainstorm_1.brainstormRespondHandler.handler)
|
|
46
|
+
.command('brainstorm-promote', 'Promote a brainstorm idea to a pop.brain.projects entry (link via promotedToProjectIds)', brainstorm_1.brainstormPromoteHandler.builder, brainstorm_1.brainstormPromoteHandler.handler)
|
|
47
|
+
.command('brainstorm-close', 'Close a brainstorm without promoting any idea', brainstorm_1.brainstormCloseHandler.builder, brainstorm_1.brainstormCloseHandler.handler)
|
|
48
|
+
.command('brainstorm-remove', 'Soft-delete a brainstorm (tombstone; filtered from projection output)', brainstorm_1.brainstormRemoveHandler.builder, brainstorm_1.brainstormRemoveHandler.handler)
|
|
49
|
+
.command('new-project', 'Create a project entry in pop.brain.projects (signed + gossipsub-published)', new_project_1.newProjectHandler.builder, new_project_1.newProjectHandler.handler)
|
|
50
|
+
.command('advance-stage', 'Move a project forward in the lifecycle (propose → discuss → ... → ship)', advance_stage_1.advanceStageHandler.builder, advance_stage_1.advanceStageHandler.handler)
|
|
51
|
+
.command('remove-project', 'Soft-delete a project entry (tombstone; filtered from snapshot output)', remove_project_1.removeProjectHandler.builder, remove_project_1.removeProjectHandler.handler)
|
|
52
|
+
.command('allowlist <action>', 'Manage the brain allowlist (list/add/remove)', allowlist_1.allowlistHandler.builder, allowlist_1.allowlistHandler.handler)
|
|
53
|
+
.command('migrate-projects', 'Import projects.md into a pop.brain.projects doc (sprint-3 follow-up to step 8)', migrate_projects_1.migrateProjectsHandler.builder, migrate_projects_1.migrateProjectsHandler.handler)
|
|
54
|
+
.command('doctor', 'Health check for brain layer setup (env, keys, libp2p init, allowlist, manifest)', doctor_1.doctorHandler.builder, doctor_1.doctorHandler.handler)
|
|
55
|
+
.command('import-snapshot', 'Load a raw Automerge snapshot file as the new local head for a brain doc (#353 migration tool for converging disjoint agents onto a shared baseline)', import_snapshot_1.importSnapshotHandler.builder, import_snapshot_1.importSnapshotHandler.handler)
|
|
56
|
+
.command('daemon <action>', 'Manage the persistent brain daemon (start/stop/status/logs) — keeps libp2p alive so gossipsub announcements actually propagate', daemon_1.daemonHandler.builder, daemon_1.daemonHandler.handler)
|
|
57
|
+
.command('retro <action>', 'Manage session retros in pop.brain.retros (start/list/show) — recurring self-reflection cycles with proposed changes and cross-agent discussion', (yargs) => yargs
|
|
58
|
+
.command('start', 'Start a new retro with observations + proposed changes', retro_start_1.retroStartHandler.builder, retro_start_1.retroStartHandler.handler)
|
|
59
|
+
.command('list', 'List retros in pop.brain.retros (optionally filter by --status)', retro_list_1.retroListHandler.builder, retro_list_1.retroListHandler.handler)
|
|
60
|
+
.command('show <retro-id>', 'Render a single retro as markdown', retro_show_1.retroShowHandler.builder, retro_show_1.retroShowHandler.handler)
|
|
61
|
+
.command('respond', 'Append a discussion entry (optionally with per-change votes) to an open retro', retro_respond_1.retroRespondHandler.builder, retro_respond_1.retroRespondHandler.handler)
|
|
62
|
+
.command('file-tasks', 'Convert agreed retro changes into on-chain tasks (idempotent)', retro_file_tasks_1.retroFileTasksHandler.builder, retro_file_tasks_1.retroFileTasksHandler.handler)
|
|
63
|
+
.command('mark-change <retro-id> <change-id>', 'Manually set a proposed-change status (e.g. agreed / rejected / modified) before running file-tasks', retro_mark_change_1.retroMarkChangeHandler.builder, retro_mark_change_1.retroMarkChangeHandler.handler)
|
|
64
|
+
.command('remove <retro-id>', 'Soft-delete a retro (tombstone; filtered from projection) — useful for test retros and retros started in error', retro_remove_1.retroRemoveHandler.builder, retro_remove_1.retroRemoveHandler.handler)
|
|
65
|
+
.demandCommand(1, 'Please specify a retro action: start, list, show, respond, file-tasks, mark-change, remove'), () => { })
|
|
66
|
+
.demandCommand(1, 'Please specify a brain action');
|
|
67
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pop brain list — enumerate all known brain docs with their current head CIDs.
|
|
3
|
+
*
|
|
4
|
+
* Thin wrapper over listBrainDocs() which reads the manifest file directly
|
|
5
|
+
* (no Helia process required). Useful for:
|
|
6
|
+
*
|
|
7
|
+
* - Discovering what docs an agent has locally without knowing the ID
|
|
8
|
+
* - Sanity-checking that a subscribe session actually merged a remote head
|
|
9
|
+
* - Feeding `pop brain read --doc <id>` for any doc returned here
|
|
10
|
+
*
|
|
11
|
+
* Implementation intentionally avoids spinning up a Helia node — listing
|
|
12
|
+
* is a pure manifest read.
|
|
13
|
+
*/
|
|
14
|
+
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
|
15
|
+
interface ListArgs {
|
|
16
|
+
}
|
|
17
|
+
export declare const listHandler: {
|
|
18
|
+
builder: (yargs: Argv) => Argv<{}>;
|
|
19
|
+
handler: (_argv: ArgumentsCamelCase<ListArgs>) => Promise<void>;
|
|
20
|
+
};
|
|
21
|
+
export {};
|