@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,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain search — keyword / tag / author / HB filters over brain lesson docs.
|
|
4
|
+
*
|
|
5
|
+
* Task #347 (Retro #1 fallback #6). The pop.brain.shared / pop.brain.lessons
|
|
6
|
+
* doc has grown past the "cold read is cheap" threshold. Agents reaching for
|
|
7
|
+
* "is there a lesson about X" were grepping heartbeat-log.md as a faster
|
|
8
|
+
* proxy, which defeated the purpose of the canonical lesson substrate.
|
|
9
|
+
* This command makes lesson search cheaper than log grep.
|
|
10
|
+
*
|
|
11
|
+
* Filters compose as AND:
|
|
12
|
+
* --query case-insensitive substring match over title + body
|
|
13
|
+
* --tag exact tag match (one tag per invocation; run twice to AND)
|
|
14
|
+
* --author exact author match (0x lowercase)
|
|
15
|
+
* --since HB number: only lessons with timestamp >= the HB's wall-clock.
|
|
16
|
+
* Heuristic — the HB number is not stored on the lesson; we
|
|
17
|
+
* approximate by comparing the lesson timestamp against a
|
|
18
|
+
* provided unix-seconds lower bound passed as --since-ts.
|
|
19
|
+
*
|
|
20
|
+
* Output is ranked by timestamp descending (most recent first) since that's
|
|
21
|
+
* the useful default for "what happened lately about X". JSON mode for
|
|
22
|
+
* programmatic consumption.
|
|
23
|
+
*/
|
|
24
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
27
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
28
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
29
|
+
}
|
|
30
|
+
Object.defineProperty(o, k2, desc);
|
|
31
|
+
}) : (function(o, m, k, k2) {
|
|
32
|
+
if (k2 === undefined) k2 = k;
|
|
33
|
+
o[k2] = m[k];
|
|
34
|
+
}));
|
|
35
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
36
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
37
|
+
}) : function(o, v) {
|
|
38
|
+
o["default"] = v;
|
|
39
|
+
});
|
|
40
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
41
|
+
var ownKeys = function(o) {
|
|
42
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
43
|
+
var ar = [];
|
|
44
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
45
|
+
return ar;
|
|
46
|
+
};
|
|
47
|
+
return ownKeys(o);
|
|
48
|
+
};
|
|
49
|
+
return function (mod) {
|
|
50
|
+
if (mod && mod.__esModule) return mod;
|
|
51
|
+
var result = {};
|
|
52
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
53
|
+
__setModuleDefault(result, mod);
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
})();
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
exports.searchHandler = void 0;
|
|
59
|
+
const brain_1 = require("../../lib/brain");
|
|
60
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
61
|
+
function firstSentence(body) {
|
|
62
|
+
if (!body)
|
|
63
|
+
return '';
|
|
64
|
+
const trimmed = body.trim();
|
|
65
|
+
const match = trimmed.match(/^(.{1,160}?[.!?])(\s|$)/);
|
|
66
|
+
if (match)
|
|
67
|
+
return match[1];
|
|
68
|
+
// No sentence terminator in first 160 chars — take first line or 160 chars.
|
|
69
|
+
const firstLine = trimmed.split('\n')[0];
|
|
70
|
+
return firstLine.length > 160 ? firstLine.slice(0, 157) + '…' : firstLine;
|
|
71
|
+
}
|
|
72
|
+
exports.searchHandler = {
|
|
73
|
+
builder: (yargs) => yargs
|
|
74
|
+
.option('doc', {
|
|
75
|
+
describe: 'Brain document ID (e.g. pop.brain.shared)',
|
|
76
|
+
type: 'string',
|
|
77
|
+
demandOption: true,
|
|
78
|
+
})
|
|
79
|
+
.option('query', {
|
|
80
|
+
describe: 'Case-insensitive substring match over title + body',
|
|
81
|
+
type: 'string',
|
|
82
|
+
})
|
|
83
|
+
.option('tag', {
|
|
84
|
+
describe: 'Filter to lessons whose tags include this exact string',
|
|
85
|
+
type: 'string',
|
|
86
|
+
})
|
|
87
|
+
.option('author', {
|
|
88
|
+
describe: 'Filter to lessons by this author address (0x lowercase)',
|
|
89
|
+
type: 'string',
|
|
90
|
+
})
|
|
91
|
+
.option('since-ts', {
|
|
92
|
+
describe: 'Only lessons with timestamp >= this unix-seconds value',
|
|
93
|
+
type: 'number',
|
|
94
|
+
})
|
|
95
|
+
.option('limit', {
|
|
96
|
+
describe: 'Max number of lessons to return (default 10)',
|
|
97
|
+
type: 'number',
|
|
98
|
+
default: 10,
|
|
99
|
+
}),
|
|
100
|
+
handler: async (argv) => {
|
|
101
|
+
try {
|
|
102
|
+
const { doc: currentDoc } = await (0, brain_1.openBrainDoc)(argv.doc);
|
|
103
|
+
const lessons = Array.isArray(currentDoc?.lessons) ? currentDoc.lessons : [];
|
|
104
|
+
const queryLower = argv.query ? argv.query.toLowerCase() : null;
|
|
105
|
+
const wantTag = argv.tag ?? null;
|
|
106
|
+
const wantAuthor = argv.author ? argv.author.toLowerCase() : null;
|
|
107
|
+
const sinceTs = typeof argv.sinceTs === 'number' ? argv.sinceTs : null;
|
|
108
|
+
const matched = lessons.filter((lesson) => {
|
|
109
|
+
if (!lesson || lesson.removed === true)
|
|
110
|
+
return false;
|
|
111
|
+
if (queryLower) {
|
|
112
|
+
const haystack = `${lesson.title ?? ''}\n${lesson.body ?? lesson.text ?? ''}`.toLowerCase();
|
|
113
|
+
if (!haystack.includes(queryLower))
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
if (wantTag) {
|
|
117
|
+
const tags = Array.isArray(lesson.tags) ? lesson.tags : [];
|
|
118
|
+
if (!tags.some((t) => t === wantTag))
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (wantAuthor) {
|
|
122
|
+
const author = typeof lesson.author === 'string' ? lesson.author.toLowerCase() : '';
|
|
123
|
+
if (author !== wantAuthor)
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (sinceTs !== null) {
|
|
127
|
+
const ts = typeof lesson.timestamp === 'number' ? lesson.timestamp : 0;
|
|
128
|
+
if (ts < sinceTs)
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
});
|
|
133
|
+
// Sort by timestamp descending. Non-numeric timestamps sort last.
|
|
134
|
+
matched.sort((a, b) => {
|
|
135
|
+
const ta = typeof a.timestamp === 'number' ? a.timestamp : 0;
|
|
136
|
+
const tb = typeof b.timestamp === 'number' ? b.timestamp : 0;
|
|
137
|
+
return tb - ta;
|
|
138
|
+
});
|
|
139
|
+
const limited = matched.slice(0, argv.limit ?? 10);
|
|
140
|
+
if (output.isJsonMode()) {
|
|
141
|
+
output.json({
|
|
142
|
+
docId: argv.doc,
|
|
143
|
+
filters: {
|
|
144
|
+
query: argv.query ?? null,
|
|
145
|
+
tag: argv.tag ?? null,
|
|
146
|
+
author: argv.author ?? null,
|
|
147
|
+
sinceTs: sinceTs,
|
|
148
|
+
},
|
|
149
|
+
totalMatched: matched.length,
|
|
150
|
+
returned: limited.length,
|
|
151
|
+
lessons: limited.map((l) => ({
|
|
152
|
+
id: l.id ?? null,
|
|
153
|
+
title: l.title ?? null,
|
|
154
|
+
author: l.author ?? null,
|
|
155
|
+
timestamp: l.timestamp ?? null,
|
|
156
|
+
tags: Array.isArray(l.tags) ? l.tags : [],
|
|
157
|
+
summary: firstSentence(l.body ?? l.text ?? ''),
|
|
158
|
+
})),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
console.log('');
|
|
163
|
+
console.log(` ${matched.length} matching lesson${matched.length === 1 ? '' : 's'} in ${argv.doc}` +
|
|
164
|
+
(matched.length > limited.length ? ` (showing most recent ${limited.length})` : ''));
|
|
165
|
+
console.log('');
|
|
166
|
+
if (limited.length === 0) {
|
|
167
|
+
console.log(' (no matches)');
|
|
168
|
+
console.log('');
|
|
169
|
+
}
|
|
170
|
+
for (const l of limited) {
|
|
171
|
+
const iso = typeof l.timestamp === 'number' ? new Date(l.timestamp * 1000).toISOString() : '?';
|
|
172
|
+
const tags = Array.isArray(l.tags) && l.tags.length > 0 ? ` [${l.tags.join(', ')}]` : '';
|
|
173
|
+
console.log(` • ${l.title ?? l.id ?? '(no title)'}${tags}`);
|
|
174
|
+
console.log(` ${l.author ?? '?'} · ${iso}`);
|
|
175
|
+
const summary = firstSentence(l.body ?? l.text ?? '');
|
|
176
|
+
if (summary)
|
|
177
|
+
console.log(` ${summary}`);
|
|
178
|
+
console.log('');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
output.error(err.message);
|
|
184
|
+
process.exitCode = 1;
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
await (0, brain_1.stopBrainNode)();
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pop brain snapshot — project a brain doc to markdown and write it to
|
|
3
|
+
* agent/brain/Knowledge/<docId>.generated.md.
|
|
4
|
+
*
|
|
5
|
+
* This is the read-side counterpart to applyBrainChange. The heartbeat
|
|
6
|
+
* skill calls this at end-of-heartbeat so the projection is kept in
|
|
7
|
+
* sync on disk for human review and git archival. Step 8 of the plan
|
|
8
|
+
* is when the hand-written files are retired and this becomes the
|
|
9
|
+
* source of truth; until then, the output lives at a `.generated.md`
|
|
10
|
+
* suffix so reviewers can diff it against the hand-written original.
|
|
11
|
+
*
|
|
12
|
+
* Graceful bootstrap: if the doc has no head CID yet (manifest empty),
|
|
13
|
+
* the command exits 0 with a log line so that the heartbeat skill can
|
|
14
|
+
* call it unconditionally without blowing up on fresh agents.
|
|
15
|
+
*/
|
|
16
|
+
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
|
17
|
+
interface SnapshotArgs {
|
|
18
|
+
doc: string;
|
|
19
|
+
outputPath?: string;
|
|
20
|
+
force?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export declare const snapshotHandler: {
|
|
23
|
+
builder: (yargs: Argv) => Argv<{
|
|
24
|
+
doc: string;
|
|
25
|
+
} & {
|
|
26
|
+
"output-path": string | undefined;
|
|
27
|
+
} & {
|
|
28
|
+
force: boolean;
|
|
29
|
+
}>;
|
|
30
|
+
handler: (argv: ArgumentsCamelCase<SnapshotArgs>) => Promise<void>;
|
|
31
|
+
};
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain snapshot — project a brain doc to markdown and write it to
|
|
4
|
+
* agent/brain/Knowledge/<docId>.generated.md.
|
|
5
|
+
*
|
|
6
|
+
* This is the read-side counterpart to applyBrainChange. The heartbeat
|
|
7
|
+
* skill calls this at end-of-heartbeat so the projection is kept in
|
|
8
|
+
* sync on disk for human review and git archival. Step 8 of the plan
|
|
9
|
+
* is when the hand-written files are retired and this becomes the
|
|
10
|
+
* source of truth; until then, the output lives at a `.generated.md`
|
|
11
|
+
* suffix so reviewers can diff it against the hand-written original.
|
|
12
|
+
*
|
|
13
|
+
* Graceful bootstrap: if the doc has no head CID yet (manifest empty),
|
|
14
|
+
* the command exits 0 with a log line so that the heartbeat skill can
|
|
15
|
+
* call it unconditionally without blowing up on fresh agents.
|
|
16
|
+
*/
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.snapshotHandler = void 0;
|
|
52
|
+
const path_1 = require("path");
|
|
53
|
+
const brain_paths_1 = require("../../lib/brain-paths");
|
|
54
|
+
const fs_1 = require("fs");
|
|
55
|
+
const brain_1 = require("../../lib/brain");
|
|
56
|
+
const brain_projections_1 = require("../../lib/brain-projections");
|
|
57
|
+
const brain_migrate_1 = require("../../lib/brain-migrate");
|
|
58
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
59
|
+
/**
|
|
60
|
+
* Count unique lesson ids in a rendered `.generated.md` projection.
|
|
61
|
+
*
|
|
62
|
+
* Task #359 (HB#361): this previously used `/^### /gm` which matched EVERY
|
|
63
|
+
* line starting with `### ` in the markdown, INCLUDING lines inside lesson
|
|
64
|
+
* bodies that contain `### ` as literal content. After HB#358 (#357) taught
|
|
65
|
+
* the parser to preserve H3 body content, body H3s started inflating the
|
|
66
|
+
* naive count and the regression guard reported false mismatches.
|
|
67
|
+
*
|
|
68
|
+
* The fix is in two parts:
|
|
69
|
+
* 1. For the **local projection** side of the comparison, count
|
|
70
|
+
* `doc.lessons.filter(!removed).length` directly — the doc is in
|
|
71
|
+
* scope, no parsing needed, no inflation possible. Done inline at
|
|
72
|
+
* the call site.
|
|
73
|
+
* 2. For the **existing-file** side, we don't have the old doc, so we
|
|
74
|
+
* parse the markdown structurally (via the HB#358 parser) and count
|
|
75
|
+
* UNIQUE ids. Unique-id counting dedupes the ghost lessons produced
|
|
76
|
+
* by one lesson's body containing `### ` at line-start: the parser
|
|
77
|
+
* will start "new lessons" at each body H3, but those ghosts often
|
|
78
|
+
* share slug-fallback ids with their neighbors (or with the enclosing
|
|
79
|
+
* real lesson), so counting unique-ids compresses the inflation
|
|
80
|
+
* back out. The count reflects the best estimate of how many
|
|
81
|
+
* distinct-id lessons are structurally present in the file.
|
|
82
|
+
*
|
|
83
|
+
* For non-shared docs (projects, retros), the naive H3 count is still
|
|
84
|
+
* usable because their bodies don't typically contain `### ` lines —
|
|
85
|
+
* kept as a fallback.
|
|
86
|
+
*/
|
|
87
|
+
function countLessonsInProjectedMarkdown(md) {
|
|
88
|
+
const parsed = (0, brain_migrate_1.parseSharedMarkdown)(md, {
|
|
89
|
+
defaultAuthor: 'snapshot-regression-guard',
|
|
90
|
+
defaultTimestamp: 0,
|
|
91
|
+
});
|
|
92
|
+
const uniqueIds = new Set();
|
|
93
|
+
for (const l of parsed.lessons) {
|
|
94
|
+
if (l && typeof l.id === 'string' && l.id !== '') {
|
|
95
|
+
uniqueIds.add(l.id);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return uniqueIds.size;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Legacy H3 counter — kept as a fallback for non-shared docs (projects,
|
|
102
|
+
* retros) whose projections don't embed H3 content inside bodies. New
|
|
103
|
+
* shared-doc logic uses `countLessonsInProjectedMarkdown` instead.
|
|
104
|
+
*/
|
|
105
|
+
function countH3Items(md) {
|
|
106
|
+
const matches = md.match(/^### /gm);
|
|
107
|
+
return matches ? matches.length : 0;
|
|
108
|
+
}
|
|
109
|
+
/** Parse the "*Head CID: `...`*" line from a generated projection file. */
|
|
110
|
+
function parseExistingHeadCid(md) {
|
|
111
|
+
const m = /^\*Head CID: `([^`]+)`\*/m.exec(md);
|
|
112
|
+
return m?.[1] ?? null;
|
|
113
|
+
}
|
|
114
|
+
exports.snapshotHandler = {
|
|
115
|
+
builder: (yargs) => yargs
|
|
116
|
+
.option('doc', {
|
|
117
|
+
describe: 'Brain document ID (e.g. pop.brain.shared)',
|
|
118
|
+
type: 'string',
|
|
119
|
+
demandOption: true,
|
|
120
|
+
})
|
|
121
|
+
.option('output-path', {
|
|
122
|
+
describe: 'Explicit output path (default: agent/brain/Knowledge/<doc>.generated.md)',
|
|
123
|
+
type: 'string',
|
|
124
|
+
})
|
|
125
|
+
.option('force', {
|
|
126
|
+
describe: 'Overwrite the existing generated.md even if it would regress (fewer lessons/projects than the file currently on disk). Use only when you know local state is authoritative.',
|
|
127
|
+
type: 'boolean',
|
|
128
|
+
default: false,
|
|
129
|
+
}),
|
|
130
|
+
handler: async (argv) => {
|
|
131
|
+
const docId = argv.doc;
|
|
132
|
+
try {
|
|
133
|
+
const { doc, headCid } = await (0, brain_1.readBrainDoc)(docId);
|
|
134
|
+
// Bootstrap case: no local head yet. Log + exit 0 so the
|
|
135
|
+
// heartbeat skill can call us unconditionally.
|
|
136
|
+
if (!headCid && (!doc || Object.keys(doc).length === 0)) {
|
|
137
|
+
if (output.isJsonMode()) {
|
|
138
|
+
output.json({ status: 'no-op', reason: 'no local head for this doc', docId });
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
console.log(`pop brain snapshot: no local head for "${docId}" — nothing to project yet.`);
|
|
142
|
+
}
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Dispatch to the right projector based on docId. Unknown docIds
|
|
146
|
+
// fall through to projectShared so older callers don't regress.
|
|
147
|
+
const markdown = (0, brain_projections_1.projectForDoc)(docId, doc, headCid);
|
|
148
|
+
// Default path: the repo-TRACKED brain (packages/agent/brain/Knowledge).
|
|
149
|
+
// Resolved via getRepoBrainRoot, not process.cwd() — a cwd-built path
|
|
150
|
+
// recreated the pre-split agent/ tree as untracked junk after the move.
|
|
151
|
+
const outPath = argv.outputPath ??
|
|
152
|
+
(0, path_1.join)((0, brain_paths_1.getRepoBrainRoot)(), 'Knowledge', `${docId}.generated.md`);
|
|
153
|
+
const outDir = outPath.substring(0, outPath.lastIndexOf('/'));
|
|
154
|
+
if (!(0, fs_1.existsSync)(outDir))
|
|
155
|
+
(0, fs_1.mkdirSync)(outDir, { recursive: true });
|
|
156
|
+
// Regression guard (task #328): if the existing generated.md has
|
|
157
|
+
// MORE content items (H3 headers) than our local projection, the
|
|
158
|
+
// local state is probably behind the peer-merged team state and
|
|
159
|
+
// writing would silently regress the committed file. Refuse with
|
|
160
|
+
// a clear error unless --force. The heartbeat skill calls
|
|
161
|
+
// `pop brain snapshot ... || true` so exit-1 here lets the HB
|
|
162
|
+
// continue without committing the regressed file.
|
|
163
|
+
if ((0, fs_1.existsSync)(outPath) && !argv.force) {
|
|
164
|
+
const existingContent = (0, fs_1.readFileSync)(outPath, 'utf8');
|
|
165
|
+
// Task #359 (HB#361): shared-doc regression guard uses
|
|
166
|
+
// doc-direct counting for the local side and structural unique-id
|
|
167
|
+
// parsing for the existing-file side. Naive /^### /gm counting
|
|
168
|
+
// was inflated by body-embedded `### ` lines after HB#358 taught
|
|
169
|
+
// the parser to preserve them. Other docs fall back to the naive
|
|
170
|
+
// H3 counter (their bodies don't embed H3s).
|
|
171
|
+
const isSharedDoc = docId === 'pop.brain.shared';
|
|
172
|
+
let existingCount;
|
|
173
|
+
let newCount;
|
|
174
|
+
if (isSharedDoc) {
|
|
175
|
+
existingCount = countLessonsInProjectedMarkdown(existingContent);
|
|
176
|
+
// Authoritative count from the in-memory doc — no parsing,
|
|
177
|
+
// no ghost lessons from body-H3 inflation.
|
|
178
|
+
const liveLessons = Array.isArray(doc?.lessons)
|
|
179
|
+
? doc.lessons.filter((l) => l?.removed !== true)
|
|
180
|
+
: [];
|
|
181
|
+
const liveIds = new Set();
|
|
182
|
+
for (const l of liveLessons) {
|
|
183
|
+
if (l && typeof l.id === 'string' && l.id !== '')
|
|
184
|
+
liveIds.add(l.id);
|
|
185
|
+
}
|
|
186
|
+
newCount = liveIds.size;
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
existingCount = countH3Items(existingContent);
|
|
190
|
+
newCount = countH3Items(markdown);
|
|
191
|
+
}
|
|
192
|
+
if (newCount < existingCount) {
|
|
193
|
+
const existingCid = parseExistingHeadCid(existingContent);
|
|
194
|
+
const msg = `pop brain snapshot would regress ${outPath}: ` +
|
|
195
|
+
`existing file has ${existingCount} items (head ${existingCid ?? '?'}), ` +
|
|
196
|
+
`local doc projects to ${newCount} items (head ${headCid ?? '?'}). ` +
|
|
197
|
+
`This usually means the local state lacks peer-merged content. ` +
|
|
198
|
+
`Run \`pop brain subscribe --doc ${docId}\` first to sync, ` +
|
|
199
|
+
`or pass \`--force\` to overwrite anyway.`;
|
|
200
|
+
if (output.isJsonMode()) {
|
|
201
|
+
output.json({
|
|
202
|
+
status: 'refused',
|
|
203
|
+
reason: 'regression',
|
|
204
|
+
docId,
|
|
205
|
+
path: outPath,
|
|
206
|
+
existingCount,
|
|
207
|
+
newCount,
|
|
208
|
+
existingHead: existingCid,
|
|
209
|
+
localHead: headCid,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
console.error(msg);
|
|
214
|
+
}
|
|
215
|
+
process.exitCode = 1;
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
(0, fs_1.writeFileSync)(outPath, markdown);
|
|
220
|
+
if (output.isJsonMode()) {
|
|
221
|
+
output.json({
|
|
222
|
+
status: 'ok',
|
|
223
|
+
docId,
|
|
224
|
+
headCid,
|
|
225
|
+
bytes: markdown.length,
|
|
226
|
+
path: outPath,
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
console.log(`Wrote ${markdown.length} bytes to ${outPath}`);
|
|
231
|
+
if (headCid)
|
|
232
|
+
console.log(`Head CID: ${headCid}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
236
|
+
output.error(err.message);
|
|
237
|
+
process.exitCode = 1;
|
|
238
|
+
}
|
|
239
|
+
finally {
|
|
240
|
+
await (0, brain_1.stopBrainNode)();
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pop brain status — show the local brain layer state.
|
|
3
|
+
*
|
|
4
|
+
* MVP step 1: initialize a local Helia node, print the peer ID and
|
|
5
|
+
* listening addresses, report connected peer count. No CRDT ops yet;
|
|
6
|
+
* this is the "environment works" smoke test.
|
|
7
|
+
*
|
|
8
|
+
* Future steps will add doc sync status, known brain documents, and
|
|
9
|
+
* per-document sync state.
|
|
10
|
+
*/
|
|
11
|
+
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
|
12
|
+
export declare const statusHandler: {
|
|
13
|
+
builder: (yargs: Argv) => Argv<{}>;
|
|
14
|
+
handler: (_argv: ArgumentsCamelCase<{}>) => Promise<void>;
|
|
15
|
+
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* pop brain status — show the local brain layer state.
|
|
4
|
+
*
|
|
5
|
+
* MVP step 1: initialize a local Helia node, print the peer ID and
|
|
6
|
+
* listening addresses, report connected peer count. No CRDT ops yet;
|
|
7
|
+
* this is the "environment works" smoke test.
|
|
8
|
+
*
|
|
9
|
+
* Future steps will add doc sync status, known brain documents, and
|
|
10
|
+
* per-document sync state.
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.statusHandler = void 0;
|
|
47
|
+
const brain_1 = require("../../lib/brain");
|
|
48
|
+
const brain_daemon_1 = require("../../lib/brain-daemon");
|
|
49
|
+
const output = __importStar(require("@poa-box/cli/lib/output"));
|
|
50
|
+
exports.statusHandler = {
|
|
51
|
+
builder: (yargs) => yargs,
|
|
52
|
+
handler: async (_argv) => {
|
|
53
|
+
// HB#364: when a daemon is running, query it via IPC instead of
|
|
54
|
+
// spinning up a second libp2p instance in the CLI process. The
|
|
55
|
+
// ephemeral-instance path produced misleading state (random listen
|
|
56
|
+
// port each call, 0 peers because the daemon owns the connections)
|
|
57
|
+
// and made cross-device onboarding debugging impossible. Fall back
|
|
58
|
+
// to the in-process libp2p path only when no daemon is running.
|
|
59
|
+
const daemonPid = (0, brain_daemon_1.getRunningDaemonPid)();
|
|
60
|
+
if (daemonPid !== null) {
|
|
61
|
+
try {
|
|
62
|
+
const daemonInfo = await (0, brain_daemon_1.sendIpcRequest)('status', {}, 5_000);
|
|
63
|
+
const info = {
|
|
64
|
+
peerId: daemonInfo.peerId,
|
|
65
|
+
peerIdSource: 'daemon',
|
|
66
|
+
peerKeyPath: daemonInfo.brainHome + '/peer-key.json',
|
|
67
|
+
listeningAddrs: daemonInfo.listenAddrs ?? [],
|
|
68
|
+
connectedPeers: daemonInfo.connections ?? 0,
|
|
69
|
+
bootstrapPeerCount: 0, // not exposed via IPC yet; pending #349 follow-up
|
|
70
|
+
subscribedTopics: daemonInfo.topics ?? [],
|
|
71
|
+
topicPeerCounts: {},
|
|
72
|
+
heliaVersion: 'daemon-owned',
|
|
73
|
+
blockstorePath: daemonInfo.brainHome + '/helia-blocks',
|
|
74
|
+
};
|
|
75
|
+
if (output.isJsonMode()) {
|
|
76
|
+
output.json({ status: 'ok', source: 'daemon', daemonPid, ...info });
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log('');
|
|
80
|
+
console.log(' Brain layer — P2P CRDT substrate (daemon-owned)');
|
|
81
|
+
console.log(' ' + '─'.repeat(60));
|
|
82
|
+
console.log(` Daemon PID: ${daemonPid}`);
|
|
83
|
+
console.log(` Peer ID: ${info.peerId}`);
|
|
84
|
+
console.log(` Connected peers: ${info.connectedPeers}`);
|
|
85
|
+
console.log(` Blockstore path: ${info.blockstorePath}`);
|
|
86
|
+
console.log('');
|
|
87
|
+
if (info.listeningAddrs.length > 0) {
|
|
88
|
+
console.log(' Listening on (use for POP_BRAIN_PEERS):');
|
|
89
|
+
for (const addr of info.listeningAddrs) {
|
|
90
|
+
console.log(` ${addr}`);
|
|
91
|
+
}
|
|
92
|
+
console.log('');
|
|
93
|
+
}
|
|
94
|
+
if (info.subscribedTopics.length > 0) {
|
|
95
|
+
console.log(' Subscribed topics:');
|
|
96
|
+
for (const t of info.subscribedTopics) {
|
|
97
|
+
console.log(` ${t}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
console.log('');
|
|
101
|
+
}
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
// IPC failed — fall through to in-process path and surface the
|
|
106
|
+
// daemon issue as a warning so operators see it.
|
|
107
|
+
if (!output.isJsonMode()) {
|
|
108
|
+
console.error(` [warn] daemon IPC at PID ${daemonPid} failed: ${err.message}`);
|
|
109
|
+
console.error(` [warn] falling back to in-process libp2p probe (may show stale state)`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const spin = output.spinner('Initializing brain node...');
|
|
114
|
+
spin.start();
|
|
115
|
+
try {
|
|
116
|
+
const info = await (0, brain_1.getBrainNodeInfo)();
|
|
117
|
+
spin.stop();
|
|
118
|
+
if (output.isJsonMode()) {
|
|
119
|
+
output.json({
|
|
120
|
+
status: 'ok',
|
|
121
|
+
...info,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log('');
|
|
126
|
+
console.log(' Brain layer — P2P CRDT substrate');
|
|
127
|
+
console.log(' ' + '─'.repeat(60));
|
|
128
|
+
console.log(` Helia version: ${info.heliaVersion}`);
|
|
129
|
+
console.log(` Peer ID: ${info.peerId}`);
|
|
130
|
+
console.log(` PeerId source: ${info.peerIdSource}`);
|
|
131
|
+
console.log(` Peer key file: ${info.peerKeyPath}`);
|
|
132
|
+
console.log(` Connected peers: ${info.connectedPeers}`);
|
|
133
|
+
console.log(` Bootstrap known: ${info.bootstrapPeerCount}`);
|
|
134
|
+
console.log(` Blockstore path: ${info.blockstorePath}`);
|
|
135
|
+
console.log('');
|
|
136
|
+
if (info.listeningAddrs.length > 0) {
|
|
137
|
+
console.log(' Listening on:');
|
|
138
|
+
for (const addr of info.listeningAddrs) {
|
|
139
|
+
console.log(` ${addr}`);
|
|
140
|
+
}
|
|
141
|
+
console.log('');
|
|
142
|
+
}
|
|
143
|
+
if (info.subscribedTopics.length > 0) {
|
|
144
|
+
console.log(' Subscribed topics:');
|
|
145
|
+
for (const t of info.subscribedTopics) {
|
|
146
|
+
const n = info.topicPeerCounts[t] ?? 0;
|
|
147
|
+
console.log(` ${t} (${n} peer${n === 1 ? '' : 's'})`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
console.log(' (no subscribed topics yet — run `pop brain subscribe --doc <id>` to listen)');
|
|
152
|
+
}
|
|
153
|
+
console.log('');
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
spin.stop();
|
|
158
|
+
output.error(err.message);
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
// Short-lived CLI invocation — clean shutdown so the process exits.
|
|
163
|
+
await (0, brain_1.stopBrainNode)();
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pop brain subscribe — keep the process alive and log incoming head-CID
|
|
3
|
+
* announcements on a doc's gossipsub topic. The manual test surface for
|
|
4
|
+
* MVP step 5.
|
|
5
|
+
*
|
|
6
|
+
* Typical 2-terminal smoke test:
|
|
7
|
+
* Terminal A: pop brain subscribe --doc pop.brain.shared
|
|
8
|
+
* Terminal B: (any write that calls applyBrainChange — step 7 ships
|
|
9
|
+
* the write CLI; until then, a direct script or test)
|
|
10
|
+
*
|
|
11
|
+
* Terminal A logs each announcement it sees: docId, CID, claimed author
|
|
12
|
+
* (not trusted — see brain.ts#BrainHeadAnnouncement), the peer it came
|
|
13
|
+
* from. Block fetch + load is step 6.
|
|
14
|
+
*/
|
|
15
|
+
import type { Argv, ArgumentsCamelCase } from 'yargs';
|
|
16
|
+
interface SubscribeArgs {
|
|
17
|
+
doc: string;
|
|
18
|
+
logOnly?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export declare const subscribeHandler: {
|
|
21
|
+
builder: (yargs: Argv) => Argv<{
|
|
22
|
+
doc: string;
|
|
23
|
+
} & {
|
|
24
|
+
"log-only": boolean;
|
|
25
|
+
}>;
|
|
26
|
+
handler: (argv: ArgumentsCamelCase<SubscribeArgs>) => Promise<void>;
|
|
27
|
+
};
|
|
28
|
+
export {};
|