@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.
Files changed (139) hide show
  1. package/.env.agent.template +20 -0
  2. package/README.md +46 -0
  3. package/brain/Config/agent-config.json +14 -0
  4. package/brain/Config/brain-allowlist.json +20 -0
  5. package/brain/Identity/goals.template.md +23 -0
  6. package/brain/Identity/how-i-think.md +406 -0
  7. package/brain/Identity/who-i-am.template.md +34 -0
  8. package/brain/Knowledge/BOOTSTRAP.md +66 -0
  9. package/brain/Knowledge/audit-corpus-index.json +406 -0
  10. package/brain/Knowledge/discussions.json +245 -0
  11. package/brain/Knowledge/pop.brain.brainstorms.generated.md +48 -0
  12. package/brain/Knowledge/pop.brain.brainstorms.genesis.bin +0 -0
  13. package/brain/Knowledge/pop.brain.heuristics.snapshot.bin +0 -0
  14. package/brain/Knowledge/pop.brain.projects.generated.md +16 -0
  15. package/brain/Knowledge/pop.brain.projects.genesis.bin +0 -0
  16. package/brain/Knowledge/pop.brain.retros.generated.md +91 -0
  17. package/brain/Knowledge/pop.brain.retros.genesis.bin +0 -0
  18. package/brain/Knowledge/pop.brain.shared.generated.md +3811 -0
  19. package/brain/Knowledge/pop.brain.shared.genesis.bin +0 -0
  20. package/brain/Knowledge/projects.md +181 -0
  21. package/brain/Knowledge/risk-framework.md +90 -0
  22. package/brain/Knowledge/shared.md +416 -0
  23. package/brain/Knowledge/sprint-priorities.md +439 -0
  24. package/brain/Memory/.gitkeep +0 -0
  25. package/dist/commands/agent/daily-digest.d.ts +24 -0
  26. package/dist/commands/agent/daily-digest.js +336 -0
  27. package/dist/commands/agent/delegate.d.ts +12 -0
  28. package/dist/commands/agent/delegate.js +91 -0
  29. package/dist/commands/agent/deploy-to-org.d.ts +20 -0
  30. package/dist/commands/agent/deploy-to-org.js +154 -0
  31. package/dist/commands/agent/index.d.ts +2 -0
  32. package/dist/commands/agent/index.js +27 -0
  33. package/dist/commands/agent/init.d.ts +19 -0
  34. package/dist/commands/agent/init.js +303 -0
  35. package/dist/commands/agent/onboard.d.ts +22 -0
  36. package/dist/commands/agent/onboard.js +192 -0
  37. package/dist/commands/agent/paymaster-status.d.ts +14 -0
  38. package/dist/commands/agent/paymaster-status.js +130 -0
  39. package/dist/commands/agent/register.d.ts +21 -0
  40. package/dist/commands/agent/register.js +116 -0
  41. package/dist/commands/agent/setup-sponsorship.d.ts +22 -0
  42. package/dist/commands/agent/setup-sponsorship.js +154 -0
  43. package/dist/commands/agent/status.d.ts +12 -0
  44. package/dist/commands/agent/status.js +171 -0
  45. package/dist/commands/agent/triage.d.ts +12 -0
  46. package/dist/commands/agent/triage.js +503 -0
  47. package/dist/commands/brain/advance-stage.d.ts +42 -0
  48. package/dist/commands/brain/advance-stage.js +206 -0
  49. package/dist/commands/brain/allowlist.d.ts +30 -0
  50. package/dist/commands/brain/allowlist.js +274 -0
  51. package/dist/commands/brain/append-lesson.d.ts +55 -0
  52. package/dist/commands/brain/append-lesson.js +245 -0
  53. package/dist/commands/brain/brainstorm.d.ts +154 -0
  54. package/dist/commands/brain/brainstorm.js +573 -0
  55. package/dist/commands/brain/daemon.d.ts +31 -0
  56. package/dist/commands/brain/daemon.js +348 -0
  57. package/dist/commands/brain/doctor.d.ts +27 -0
  58. package/dist/commands/brain/doctor.js +497 -0
  59. package/dist/commands/brain/edit-lesson.d.ts +51 -0
  60. package/dist/commands/brain/edit-lesson.js +248 -0
  61. package/dist/commands/brain/import-snapshot.d.ts +68 -0
  62. package/dist/commands/brain/import-snapshot.js +177 -0
  63. package/dist/commands/brain/index.d.ts +2 -0
  64. package/dist/commands/brain/index.js +67 -0
  65. package/dist/commands/brain/list.d.ts +21 -0
  66. package/dist/commands/brain/list.js +83 -0
  67. package/dist/commands/brain/migrate-projects.d.ts +44 -0
  68. package/dist/commands/brain/migrate-projects.js +209 -0
  69. package/dist/commands/brain/migrate.d.ts +74 -0
  70. package/dist/commands/brain/migrate.js +306 -0
  71. package/dist/commands/brain/new-project.d.ts +53 -0
  72. package/dist/commands/brain/new-project.js +226 -0
  73. package/dist/commands/brain/read.d.ts +24 -0
  74. package/dist/commands/brain/read.js +81 -0
  75. package/dist/commands/brain/remove-lesson.d.ts +47 -0
  76. package/dist/commands/brain/remove-lesson.js +206 -0
  77. package/dist/commands/brain/remove-project.d.ts +36 -0
  78. package/dist/commands/brain/remove-project.js +177 -0
  79. package/dist/commands/brain/retro-file-tasks.d.ts +84 -0
  80. package/dist/commands/brain/retro-file-tasks.js +372 -0
  81. package/dist/commands/brain/retro-list.d.ts +28 -0
  82. package/dist/commands/brain/retro-list.js +125 -0
  83. package/dist/commands/brain/retro-mark-change.d.ts +58 -0
  84. package/dist/commands/brain/retro-mark-change.js +176 -0
  85. package/dist/commands/brain/retro-remove.d.ts +36 -0
  86. package/dist/commands/brain/retro-remove.js +142 -0
  87. package/dist/commands/brain/retro-respond.d.ts +56 -0
  88. package/dist/commands/brain/retro-respond.js +250 -0
  89. package/dist/commands/brain/retro-show.d.ts +23 -0
  90. package/dist/commands/brain/retro-show.js +100 -0
  91. package/dist/commands/brain/retro-start.d.ts +55 -0
  92. package/dist/commands/brain/retro-start.js +311 -0
  93. package/dist/commands/brain/search.d.ts +48 -0
  94. package/dist/commands/brain/search.js +190 -0
  95. package/dist/commands/brain/snapshot.d.ts +32 -0
  96. package/dist/commands/brain/snapshot.js +243 -0
  97. package/dist/commands/brain/status.d.ts +15 -0
  98. package/dist/commands/brain/status.js +166 -0
  99. package/dist/commands/brain/subscribe.d.ts +28 -0
  100. package/dist/commands/brain/subscribe.js +90 -0
  101. package/dist/commands/brain/tag.d.ts +46 -0
  102. package/dist/commands/brain/tag.js +192 -0
  103. package/dist/index.d.ts +17 -0
  104. package/dist/index.js +22 -0
  105. package/dist/lib/brain-daemon.d.ts +126 -0
  106. package/dist/lib/brain-daemon.js +811 -0
  107. package/dist/lib/brain-membership.d.ts +58 -0
  108. package/dist/lib/brain-membership.js +115 -0
  109. package/dist/lib/brain-migrate-projects.d.ts +43 -0
  110. package/dist/lib/brain-migrate-projects.js +247 -0
  111. package/dist/lib/brain-migrate.d.ts +77 -0
  112. package/dist/lib/brain-migrate.js +328 -0
  113. package/dist/lib/brain-ops.d.ts +271 -0
  114. package/dist/lib/brain-ops.js +571 -0
  115. package/dist/lib/brain-paths.d.ts +15 -0
  116. package/dist/lib/brain-paths.js +33 -0
  117. package/dist/lib/brain-projections.d.ts +216 -0
  118. package/dist/lib/brain-projections.js +829 -0
  119. package/dist/lib/brain-schemas.d.ts +36 -0
  120. package/dist/lib/brain-schemas.js +316 -0
  121. package/dist/lib/brain-signing.d.ts +103 -0
  122. package/dist/lib/brain-signing.js +256 -0
  123. package/dist/lib/brain.d.ts +198 -0
  124. package/dist/lib/brain.js +1057 -0
  125. package/dist/pop-agent.d.ts +1 -0
  126. package/dist/pop-agent.js +18 -0
  127. package/docs/agent.md +126 -0
  128. package/docs/agents/brain-anti-entropy.md +127 -0
  129. package/docs/agents/brain-cross-device-onboarding.md +210 -0
  130. package/docs/agents/brain-cross-machine-smoke.md +241 -0
  131. package/docs/agents/brain-layer-setup.md +725 -0
  132. package/docs/agents/offboarding-protocol.md +188 -0
  133. package/docs/agents/onboarding-protocol.md +243 -0
  134. package/docs/agents/running-an-agent.md +200 -0
  135. package/docs/brain.md +560 -0
  136. package/package.json +61 -0
  137. package/scripts/apply.sh +140 -0
  138. package/scripts/onboard.sh +205 -0
  139. package/scripts/setup-agent.ts +272 -0
@@ -0,0 +1,573 @@
1
+ "use strict";
2
+ /**
3
+ * pop brain brainstorm-* — cross-agent ideation surface commands.
4
+ *
5
+ * Task #354 phase (b) (HB#208). Forward-looking companion to
6
+ * pop.brain.retros (reactive session retrospectives) and
7
+ * pop.brain.projects (lifecycle state machine for existing projects).
8
+ *
9
+ * The brainstorm surface is the missing deliberation primitive for
10
+ * new ideation: agents post a theme/question, debate it via messages +
11
+ * ideas + per-agent votes (support/explore/oppose), and top-ranked
12
+ * ideas get promoted to pop.brain.projects at the `propose` stage.
13
+ * Promotion links back via the `promotedToProjectIds` array.
14
+ *
15
+ * All commands dispatch through routedDispatch so they work with or
16
+ * without the brain daemon. Schema validation (#346) catches malformed
17
+ * writes at applyBrainChange time.
18
+ *
19
+ * Five sub-commands registered from src/commands/brain/index.ts:
20
+ * pop brain brainstorm-start — open a new brainstorm
21
+ * pop brain brainstorm-respond — post message / add idea / cast vote
22
+ * pop brain brainstorm-promote — promote an idea to a brain project
23
+ * pop brain brainstorm-close — close a brainstorm with no promotion
24
+ * pop brain brainstorm-remove — soft-delete tombstone
25
+ */
26
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ var desc = Object.getOwnPropertyDescriptor(m, k);
29
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
30
+ desc = { enumerable: true, get: function() { return m[k]; } };
31
+ }
32
+ Object.defineProperty(o, k2, desc);
33
+ }) : (function(o, m, k, k2) {
34
+ if (k2 === undefined) k2 = k;
35
+ o[k2] = m[k];
36
+ }));
37
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
38
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
39
+ }) : function(o, v) {
40
+ o["default"] = v;
41
+ });
42
+ var __importStar = (this && this.__importStar) || (function () {
43
+ var ownKeys = function(o) {
44
+ ownKeys = Object.getOwnPropertyNames || function (o) {
45
+ var ar = [];
46
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
47
+ return ar;
48
+ };
49
+ return ownKeys(o);
50
+ };
51
+ return function (mod) {
52
+ if (mod && mod.__esModule) return mod;
53
+ var result = {};
54
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
55
+ __setModuleDefault(result, mod);
56
+ return result;
57
+ };
58
+ })();
59
+ Object.defineProperty(exports, "__esModule", { value: true });
60
+ exports.brainstormRemoveHandler = exports.brainstormCloseHandler = exports.brainstormPromoteHandler = exports.brainstormRespondHandler = exports.brainstormStartHandler = void 0;
61
+ const ethers_1 = require("ethers");
62
+ const brain_1 = require("../../lib/brain");
63
+ const brain_ops_1 = require("../../lib/brain-ops");
64
+ const idempotency_1 = require("@poa-box/cli/lib/idempotency");
65
+ const output = __importStar(require("@poa-box/cli/lib/output"));
66
+ // ---------------------------------------------------------------------------
67
+ // Shared helpers
68
+ // ---------------------------------------------------------------------------
69
+ function resolveAuthor(argvAuthor) {
70
+ if (argvAuthor)
71
+ return argvAuthor;
72
+ const key = process.env.POP_PRIVATE_KEY;
73
+ if (!key) {
74
+ throw new Error('POP_PRIVATE_KEY not set — cannot derive default author. Pass --author explicitly.');
75
+ }
76
+ return new ethers_1.ethers.Wallet(key).address.toLowerCase();
77
+ }
78
+ function slugify(s) {
79
+ return s
80
+ .toLowerCase()
81
+ .replace(/[^a-z0-9]+/g, '-')
82
+ .replace(/^-+|-+$/g, '')
83
+ .slice(0, 60);
84
+ }
85
+ exports.brainstormStartHandler = {
86
+ builder: (yargs) => yargs
87
+ .option('doc', {
88
+ describe: 'Brain document ID (default: pop.brain.brainstorms)',
89
+ type: 'string',
90
+ default: 'pop.brain.brainstorms',
91
+ })
92
+ .option('title', {
93
+ describe: 'Short brainstorm title (used as default slug for id)',
94
+ type: 'string',
95
+ demandOption: true,
96
+ })
97
+ .option('prompt', {
98
+ describe: 'Longer description / question being brainstormed',
99
+ type: 'string',
100
+ demandOption: true,
101
+ })
102
+ .option('author', {
103
+ describe: 'Override the author address (default: signing wallet)',
104
+ type: 'string',
105
+ })
106
+ .option('id', {
107
+ describe: 'Override the auto-generated brainstorm id',
108
+ type: 'string',
109
+ })
110
+ .option('window-from-hb', {
111
+ describe: 'Starting HB number of the brainstorm window',
112
+ type: 'number',
113
+ })
114
+ .option('window-to-hb', {
115
+ describe: 'Ending HB number of the brainstorm window',
116
+ type: 'number',
117
+ })
118
+ .option('idempotency-key', { type: 'string', describe: 'Task #375 (HB#217) idempotency cache.' })
119
+ .option('no-idempotency', { type: 'boolean', default: false, describe: 'Bypass the idempotency cache.' }),
120
+ handler: async (argv) => {
121
+ try {
122
+ const author = resolveAuthor(argv.author);
123
+ const now = Math.floor(Date.now() / 1000);
124
+ const id = argv.id ?? `${slugify(argv.title) || 'brainstorm'}-${now}`;
125
+ // Task #375 idempotency check, agent-scoped
126
+ const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
127
+ if (!argv.noIdempotency) {
128
+ const cached = (0, idempotency_1.checkIdempotencyCache)(author, 'brain.brainstormStart', idempKey);
129
+ if (cached) {
130
+ if (output.isJsonMode())
131
+ output.json({ status: 'ok', cached: true, ...cached });
132
+ else
133
+ console.log(` Brainstorm already opened (idempotency cache hit). id: ${cached.brainstormId} head: ${cached.headCid}`);
134
+ return;
135
+ }
136
+ }
137
+ const result = await (0, brain_ops_1.routedDispatch)({
138
+ type: 'startBrainstorm',
139
+ docId: argv.doc,
140
+ brainstormId: id,
141
+ title: argv.title,
142
+ prompt: argv.prompt,
143
+ author,
144
+ openedAt: now,
145
+ windowFromHB: argv.windowFromHb,
146
+ windowToHB: argv.windowToHb,
147
+ });
148
+ if (!argv.noIdempotency) {
149
+ (0, idempotency_1.recordIdempotentResult)(author, 'brain.brainstormStart', idempKey, {
150
+ docId: argv.doc,
151
+ brainstormId: id,
152
+ headCid: result.headCid,
153
+ });
154
+ }
155
+ if (output.isJsonMode()) {
156
+ output.json({
157
+ status: 'ok',
158
+ docId: argv.doc,
159
+ brainstormId: id,
160
+ headCid: result.headCid,
161
+ author,
162
+ routedViaDaemon: result.routedViaDaemon,
163
+ });
164
+ }
165
+ else {
166
+ console.log('');
167
+ console.log(` Brainstorm opened in ${argv.doc}`);
168
+ console.log(` id: ${id}`);
169
+ console.log(` title: ${argv.title}`);
170
+ console.log(` author: ${author}`);
171
+ console.log(` head: ${result.headCid}`);
172
+ console.log(` routed: ${result.routedViaDaemon ? 'via brain daemon' : 'in-process (no daemon)'}`);
173
+ console.log('');
174
+ console.log(` Other agents respond via pop brain brainstorm-respond --id ${id} ` +
175
+ `[--message M] [--add-idea M] [--vote idea-id=stance].`);
176
+ console.log('');
177
+ }
178
+ }
179
+ catch (err) {
180
+ output.error(err.message);
181
+ process.exitCode = 1;
182
+ }
183
+ finally {
184
+ await (0, brain_1.stopBrainNode)();
185
+ }
186
+ },
187
+ };
188
+ exports.brainstormRespondHandler = {
189
+ builder: (yargs) => yargs
190
+ .option('doc', {
191
+ type: 'string',
192
+ default: 'pop.brain.brainstorms',
193
+ })
194
+ .option('id', {
195
+ describe: 'Brainstorm id to respond to',
196
+ type: 'string',
197
+ demandOption: true,
198
+ })
199
+ .option('author', {
200
+ describe: 'Override the author address',
201
+ type: 'string',
202
+ })
203
+ .option('message', {
204
+ describe: 'Free-form discussion message to post',
205
+ type: 'string',
206
+ })
207
+ .option('add-idea', {
208
+ describe: 'Add a new idea with this text (id auto-generated from a slug)',
209
+ type: 'string',
210
+ })
211
+ .option('vote', {
212
+ describe: 'Cast a vote: <idea-id>=<support|explore|oppose>. Repeatable.',
213
+ type: 'array',
214
+ string: true,
215
+ })
216
+ .option('idempotency-key', { type: 'string', describe: 'Task #375 (HB#217) idempotency cache.' })
217
+ .option('no-idempotency', { type: 'boolean', default: false, describe: 'Bypass the idempotency cache.' })
218
+ .check((argv) => {
219
+ if (!argv.message && !argv.addIdea && (!argv.vote || argv.vote.length === 0)) {
220
+ throw new Error('Must supply at least one of --message / --add-idea / --vote');
221
+ }
222
+ return true;
223
+ }),
224
+ handler: async (argv) => {
225
+ try {
226
+ const author = resolveAuthor(argv.author);
227
+ const now = Math.floor(Date.now() / 1000);
228
+ // Pre-flight lookup to fail fast with candidate ids if missing
229
+ const { doc: currentDoc } = await (0, brain_1.openBrainDoc)(argv.doc);
230
+ const brainstorms = Array.isArray(currentDoc?.brainstorms) ? currentDoc.brainstorms : [];
231
+ const target = brainstorms.find((b) => b && b.id === argv.id);
232
+ if (!target) {
233
+ const candidates = brainstorms
234
+ .map((b) => b?.id)
235
+ .filter((id) => typeof id === 'string')
236
+ .slice(0, 8);
237
+ output.error(`Brainstorm "${argv.id}" not found in ${argv.doc}. ` +
238
+ `Available ids (first 8): ${candidates.join(', ') || '(none)'}`);
239
+ process.exitCode = 1;
240
+ return;
241
+ }
242
+ // Parse --vote entries into { ideaId: stance }
243
+ const votes = {};
244
+ for (const entry of argv.vote ?? []) {
245
+ const [ideaId, stance] = entry.split('=').map((s) => s.trim());
246
+ if (!ideaId || !stance) {
247
+ throw new Error(`Malformed --vote "${entry}" — expected format <idea-id>=<stance>`);
248
+ }
249
+ if (stance !== 'support' && stance !== 'explore' && stance !== 'oppose') {
250
+ throw new Error(`Invalid stance "${stance}" in --vote "${entry}" — must be support|explore|oppose`);
251
+ }
252
+ votes[ideaId] = stance;
253
+ }
254
+ // If adding an idea, generate a new idea id from a slug
255
+ let addIdeaPayload;
256
+ if (argv.addIdea) {
257
+ const ideaId = `${slugify(argv.addIdea) || 'idea'}-${now}`;
258
+ addIdeaPayload = { id: ideaId, message: argv.addIdea };
259
+ }
260
+ // Task #375 idempotency check, agent-scoped
261
+ const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
262
+ if (!argv.noIdempotency) {
263
+ const cached = (0, idempotency_1.checkIdempotencyCache)(author, 'brain.brainstormRespond', idempKey);
264
+ if (cached) {
265
+ if (output.isJsonMode())
266
+ output.json({ status: 'ok', cached: true, ...cached });
267
+ else
268
+ console.log(` Brainstorm response already recorded (idempotency cache hit). head: ${cached.headCid}`);
269
+ return;
270
+ }
271
+ }
272
+ const result = await (0, brain_ops_1.routedDispatch)({
273
+ type: 'respondToBrainstorm',
274
+ docId: argv.doc,
275
+ brainstormId: argv.id,
276
+ author,
277
+ message: argv.message,
278
+ addIdea: addIdeaPayload,
279
+ votes: Object.keys(votes).length > 0 ? votes : undefined,
280
+ timestamp: now,
281
+ });
282
+ if (!argv.noIdempotency) {
283
+ (0, idempotency_1.recordIdempotentResult)(author, 'brain.brainstormRespond', idempKey, {
284
+ docId: argv.doc,
285
+ brainstormId: argv.id,
286
+ headCid: result.headCid,
287
+ });
288
+ }
289
+ if (output.isJsonMode()) {
290
+ output.json({
291
+ status: 'ok',
292
+ docId: argv.doc,
293
+ brainstormId: argv.id,
294
+ ideaAdded: addIdeaPayload?.id ?? null,
295
+ votesCast: Object.keys(votes),
296
+ message: argv.message ? 'posted' : null,
297
+ headCid: result.headCid,
298
+ author,
299
+ routedViaDaemon: result.routedViaDaemon,
300
+ });
301
+ }
302
+ else {
303
+ console.log('');
304
+ console.log(` Responded to brainstorm "${argv.id}" in ${argv.doc}`);
305
+ if (argv.message)
306
+ console.log(` message: posted`);
307
+ if (addIdeaPayload)
308
+ console.log(` new idea: ${addIdeaPayload.id}`);
309
+ if (Object.keys(votes).length > 0) {
310
+ console.log(` votes:`);
311
+ for (const [ideaId, stance] of Object.entries(votes)) {
312
+ console.log(` ${ideaId} = ${stance}`);
313
+ }
314
+ }
315
+ console.log(` head: ${result.headCid}`);
316
+ console.log('');
317
+ }
318
+ }
319
+ catch (err) {
320
+ output.error(err.message);
321
+ process.exitCode = 1;
322
+ }
323
+ finally {
324
+ await (0, brain_1.stopBrainNode)();
325
+ }
326
+ },
327
+ };
328
+ exports.brainstormPromoteHandler = {
329
+ builder: (yargs) => yargs
330
+ .option('doc', {
331
+ type: 'string',
332
+ default: 'pop.brain.brainstorms',
333
+ })
334
+ .option('id', {
335
+ describe: 'Brainstorm id containing the idea to promote',
336
+ type: 'string',
337
+ demandOption: true,
338
+ })
339
+ .option('idea-id', {
340
+ describe: 'Idea id to promote to a brain project',
341
+ type: 'string',
342
+ demandOption: true,
343
+ })
344
+ .option('project-id', {
345
+ describe: 'Existing pop.brain.projects id to link to (create the project via `pop brain new-project` first)',
346
+ type: 'string',
347
+ demandOption: true,
348
+ })
349
+ .option('author', {
350
+ describe: 'Override the author address',
351
+ type: 'string',
352
+ })
353
+ .option('idempotency-key', { type: 'string', describe: 'Task #375 (HB#217) idempotency cache.' })
354
+ .option('no-idempotency', { type: 'boolean', default: false, describe: 'Bypass the idempotency cache.' }),
355
+ handler: async (argv) => {
356
+ try {
357
+ const author = resolveAuthor(argv.author);
358
+ const now = Math.floor(Date.now() / 1000);
359
+ const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
360
+ if (!argv.noIdempotency) {
361
+ const cached = (0, idempotency_1.checkIdempotencyCache)(author, 'brain.brainstormPromote', idempKey);
362
+ if (cached) {
363
+ if (output.isJsonMode())
364
+ output.json({ status: 'ok', cached: true, ...cached });
365
+ else
366
+ console.log(` Idea already promoted (idempotency cache hit). head: ${cached.headCid}`);
367
+ return;
368
+ }
369
+ }
370
+ const result = await (0, brain_ops_1.routedDispatch)({
371
+ type: 'promoteIdea',
372
+ docId: argv.doc,
373
+ brainstormId: argv.id,
374
+ ideaId: argv.ideaId,
375
+ promotedProjectId: argv.projectId,
376
+ promotedBy: author,
377
+ promotedAt: now,
378
+ });
379
+ if (!argv.noIdempotency) {
380
+ (0, idempotency_1.recordIdempotentResult)(author, 'brain.brainstormPromote', idempKey, {
381
+ docId: argv.doc,
382
+ brainstormId: argv.id,
383
+ ideaId: argv.ideaId,
384
+ headCid: result.headCid,
385
+ });
386
+ }
387
+ if (output.isJsonMode()) {
388
+ output.json({
389
+ status: 'ok',
390
+ docId: argv.doc,
391
+ brainstormId: argv.id,
392
+ ideaId: argv.ideaId,
393
+ promotedProjectId: argv.projectId,
394
+ headCid: result.headCid,
395
+ author,
396
+ routedViaDaemon: result.routedViaDaemon,
397
+ });
398
+ }
399
+ else {
400
+ console.log('');
401
+ console.log(` Idea "${argv.ideaId}" promoted from brainstorm "${argv.id}" to project "${argv.projectId}"`);
402
+ console.log(` promotedBy: ${author}`);
403
+ console.log(` head: ${result.headCid}`);
404
+ console.log('');
405
+ }
406
+ }
407
+ catch (err) {
408
+ output.error(err.message);
409
+ process.exitCode = 1;
410
+ }
411
+ finally {
412
+ await (0, brain_1.stopBrainNode)();
413
+ }
414
+ },
415
+ };
416
+ exports.brainstormCloseHandler = {
417
+ builder: (yargs) => yargs
418
+ .option('doc', {
419
+ type: 'string',
420
+ default: 'pop.brain.brainstorms',
421
+ })
422
+ .option('id', {
423
+ describe: 'Brainstorm id to close',
424
+ type: 'string',
425
+ demandOption: true,
426
+ })
427
+ .option('reason', {
428
+ describe: 'Reason for closing without promoting',
429
+ type: 'string',
430
+ })
431
+ .option('author', {
432
+ type: 'string',
433
+ })
434
+ .option('idempotency-key', { type: 'string', describe: 'Task #375 (HB#217) idempotency cache.' })
435
+ .option('no-idempotency', { type: 'boolean', default: false, describe: 'Bypass the idempotency cache.' }),
436
+ handler: async (argv) => {
437
+ try {
438
+ const author = resolveAuthor(argv.author);
439
+ const now = Math.floor(Date.now() / 1000);
440
+ const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
441
+ if (!argv.noIdempotency) {
442
+ const cached = (0, idempotency_1.checkIdempotencyCache)(author, 'brain.brainstormClose', idempKey);
443
+ if (cached) {
444
+ if (output.isJsonMode())
445
+ output.json({ status: 'ok', cached: true, ...cached });
446
+ else
447
+ console.log(` Brainstorm "${argv.id}" already closed (idempotency cache hit). head: ${cached.headCid}`);
448
+ return;
449
+ }
450
+ }
451
+ const result = await (0, brain_ops_1.routedDispatch)({
452
+ type: 'closeBrainstorm',
453
+ docId: argv.doc,
454
+ brainstormId: argv.id,
455
+ closedBy: author,
456
+ closedAt: now,
457
+ reason: argv.reason,
458
+ });
459
+ if (!argv.noIdempotency) {
460
+ (0, idempotency_1.recordIdempotentResult)(author, 'brain.brainstormClose', idempKey, {
461
+ docId: argv.doc,
462
+ brainstormId: argv.id,
463
+ headCid: result.headCid,
464
+ });
465
+ }
466
+ if (output.isJsonMode()) {
467
+ output.json({
468
+ status: 'ok',
469
+ docId: argv.doc,
470
+ brainstormId: argv.id,
471
+ headCid: result.headCid,
472
+ author,
473
+ routedViaDaemon: result.routedViaDaemon,
474
+ });
475
+ }
476
+ else {
477
+ console.log('');
478
+ console.log(` Brainstorm "${argv.id}" closed in ${argv.doc}`);
479
+ console.log(` closedBy: ${author}`);
480
+ if (argv.reason)
481
+ console.log(` reason: ${argv.reason}`);
482
+ console.log(` head: ${result.headCid}`);
483
+ console.log('');
484
+ }
485
+ }
486
+ catch (err) {
487
+ output.error(err.message);
488
+ process.exitCode = 1;
489
+ }
490
+ finally {
491
+ await (0, brain_1.stopBrainNode)();
492
+ }
493
+ },
494
+ };
495
+ exports.brainstormRemoveHandler = {
496
+ builder: (yargs) => yargs
497
+ .option('doc', {
498
+ type: 'string',
499
+ default: 'pop.brain.brainstorms',
500
+ })
501
+ .option('id', {
502
+ describe: 'Brainstorm id to soft-delete',
503
+ type: 'string',
504
+ demandOption: true,
505
+ })
506
+ .option('reason', {
507
+ describe: 'Removal reason',
508
+ type: 'string',
509
+ })
510
+ .option('author', {
511
+ type: 'string',
512
+ })
513
+ .option('idempotency-key', { type: 'string', describe: 'Task #375 (HB#217) idempotency cache.' })
514
+ .option('no-idempotency', { type: 'boolean', default: false, describe: 'Bypass the idempotency cache.' }),
515
+ handler: async (argv) => {
516
+ try {
517
+ const author = resolveAuthor(argv.author);
518
+ const now = Math.floor(Date.now() / 1000);
519
+ const idempKey = argv.idempotencyKey || (0, idempotency_1.argvToIdempotencyString)(argv);
520
+ if (!argv.noIdempotency) {
521
+ const cached = (0, idempotency_1.checkIdempotencyCache)(author, 'brain.brainstormRemove', idempKey);
522
+ if (cached) {
523
+ if (output.isJsonMode())
524
+ output.json({ status: 'ok', cached: true, ...cached });
525
+ else
526
+ console.log(` Brainstorm "${argv.id}" already removed (idempotency cache hit). head: ${cached.headCid}`);
527
+ return;
528
+ }
529
+ }
530
+ const result = await (0, brain_ops_1.routedDispatch)({
531
+ type: 'removeBrainstorm',
532
+ docId: argv.doc,
533
+ brainstormId: argv.id,
534
+ removedBy: author,
535
+ removedAt: now,
536
+ removedReason: argv.reason,
537
+ });
538
+ if (!argv.noIdempotency) {
539
+ (0, idempotency_1.recordIdempotentResult)(author, 'brain.brainstormRemove', idempKey, {
540
+ docId: argv.doc,
541
+ brainstormId: argv.id,
542
+ headCid: result.headCid,
543
+ });
544
+ }
545
+ if (output.isJsonMode()) {
546
+ output.json({
547
+ status: 'ok',
548
+ docId: argv.doc,
549
+ brainstormId: argv.id,
550
+ headCid: result.headCid,
551
+ author,
552
+ routedViaDaemon: result.routedViaDaemon,
553
+ });
554
+ }
555
+ else {
556
+ console.log('');
557
+ console.log(` Brainstorm "${argv.id}" soft-deleted in ${argv.doc}`);
558
+ console.log(` removedBy: ${author}`);
559
+ if (argv.reason)
560
+ console.log(` reason: ${argv.reason}`);
561
+ console.log(` head: ${result.headCid}`);
562
+ console.log('');
563
+ }
564
+ }
565
+ catch (err) {
566
+ output.error(err.message);
567
+ process.exitCode = 1;
568
+ }
569
+ finally {
570
+ await (0, brain_1.stopBrainNode)();
571
+ }
572
+ },
573
+ };
@@ -0,0 +1,31 @@
1
+ /**
2
+ * pop brain daemon — process control for the persistent brain daemon
3
+ * (HB#322 architecture: go-ds-crdt-shaped rebroadcast + keepalive loop,
4
+ * see src/lib/brain-daemon.ts for the full design rationale).
5
+ *
6
+ * Subcommands:
7
+ * start spawn a detached daemon child
8
+ * stop signal a running daemon to shut down gracefully
9
+ * status print daemon stats (peer count, topics, uptime, rebroadcast
10
+ * counts) via IPC
11
+ * logs tail the daemon log file
12
+ * __run internal — the actual daemon entrypoint. Never call directly
13
+ * from the shell; start uses this.
14
+ */
15
+ import type { Argv, ArgumentsCamelCase } from 'yargs';
16
+ interface DaemonArgs {
17
+ action: 'start' | 'stop' | 'status' | 'logs' | '__run';
18
+ follow?: boolean;
19
+ tail?: number;
20
+ }
21
+ export declare const daemonHandler: {
22
+ builder: (yargs: Argv) => Argv<{
23
+ action: "start" | "status" | "stop" | "logs" | "__run" | undefined;
24
+ } & {
25
+ follow: boolean;
26
+ } & {
27
+ tail: number;
28
+ }>;
29
+ handler: (argv: ArgumentsCamelCase<DaemonArgs>) => Promise<void>;
30
+ };
31
+ export {};