@cello-protocol/daemon 0.0.228 → 0.0.230

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 (48) hide show
  1. package/dist/channel-collect-tick.d.ts +36 -0
  2. package/dist/channel-collect-tick.d.ts.map +1 -0
  3. package/dist/channel-collect-tick.js +133 -0
  4. package/dist/channel-collect-tick.js.map +1 -0
  5. package/dist/channel-collector.d.ts +99 -0
  6. package/dist/channel-collector.d.ts.map +1 -0
  7. package/dist/channel-collector.js +249 -0
  8. package/dist/channel-collector.js.map +1 -0
  9. package/dist/channel-config-store.d.ts +33 -0
  10. package/dist/channel-config-store.d.ts.map +1 -0
  11. package/dist/channel-config-store.js +61 -0
  12. package/dist/channel-config-store.js.map +1 -0
  13. package/dist/channel-inbox-store.d.ts +46 -0
  14. package/dist/channel-inbox-store.d.ts.map +1 -0
  15. package/dist/channel-inbox-store.js +88 -0
  16. package/dist/channel-inbox-store.js.map +1 -0
  17. package/dist/channel-log-store.d.ts +14 -0
  18. package/dist/channel-log-store.d.ts.map +1 -1
  19. package/dist/channel-log-store.js +58 -0
  20. package/dist/channel-log-store.js.map +1 -1
  21. package/dist/channel-publish-handlers.d.ts +42 -0
  22. package/dist/channel-publish-handlers.d.ts.map +1 -0
  23. package/dist/channel-publish-handlers.js +212 -0
  24. package/dist/channel-publish-handlers.js.map +1 -0
  25. package/dist/channel-publish-wiring.d.ts +41 -0
  26. package/dist/channel-publish-wiring.d.ts.map +1 -0
  27. package/dist/channel-publish-wiring.js +124 -0
  28. package/dist/channel-publish-wiring.js.map +1 -0
  29. package/dist/channel-publisher.d.ts +189 -0
  30. package/dist/channel-publisher.d.ts.map +1 -0
  31. package/dist/channel-publisher.js +394 -0
  32. package/dist/channel-publisher.js.map +1 -0
  33. package/dist/channel-relay-client.d.ts +88 -0
  34. package/dist/channel-relay-client.d.ts.map +1 -0
  35. package/dist/channel-relay-client.js +180 -0
  36. package/dist/channel-relay-client.js.map +1 -0
  37. package/dist/channel-subscription-store.d.ts +66 -0
  38. package/dist/channel-subscription-store.d.ts.map +1 -0
  39. package/dist/channel-subscription-store.js +109 -0
  40. package/dist/channel-subscription-store.js.map +1 -0
  41. package/dist/daemon.d.ts.map +1 -1
  42. package/dist/daemon.js +14 -0
  43. package/dist/daemon.js.map +1 -1
  44. package/dist/gateway-config-handlers.d.ts +2 -2
  45. package/dist/gateway-config-handlers.d.ts.map +1 -1
  46. package/dist/gateway-config-handlers.js +37 -12
  47. package/dist/gateway-config-handlers.js.map +1 -1
  48. package/package.json +5 -5
@@ -0,0 +1,61 @@
1
+ /** Seven days, matching the relay's own default retention. */
2
+ export const DEFAULT_RETENTION_SECONDS = 7 * 24 * 60 * 60;
3
+ export const CHANNEL_CONFIG_CREATE_SQL = `
4
+ CREATE TABLE IF NOT EXISTS channel_config (
5
+ channel_pubkey TEXT NOT NULL PRIMARY KEY,
6
+ access TEXT NOT NULL,
7
+ -- JSON array of multiaddrs, in the order the publisher chose them. Two is the design; the column
8
+ -- is a list because the count is the publisher's decision, not the schema's.
9
+ relays TEXT NOT NULL,
10
+ guidance TEXT NOT NULL DEFAULT '',
11
+ retention_seconds INTEGER NOT NULL DEFAULT ${String(DEFAULT_RETENTION_SECONDS)},
12
+ updated_at INTEGER NOT NULL
13
+ );
14
+ `;
15
+ export class ChannelConfigStore {
16
+ #db;
17
+ #logger;
18
+ constructor(db, logger) {
19
+ this.#db = db;
20
+ this.#logger = logger;
21
+ this.#db.exec(CHANNEL_CONFIG_CREATE_SQL);
22
+ }
23
+ get(channelHex) {
24
+ const row = this.#db
25
+ .prepare(`SELECT access, relays, guidance, retention_seconds FROM channel_config WHERE channel_pubkey = ?`)
26
+ .get(channelHex.toLowerCase());
27
+ if (!row)
28
+ return null;
29
+ let relays = [];
30
+ try {
31
+ const parsed = JSON.parse(row.relays);
32
+ if (Array.isArray(parsed))
33
+ relays = parsed.filter((r) => typeof r === "string");
34
+ }
35
+ catch {
36
+ // A corrupt relay list makes the channel unpublishable and says so, rather than publishing to
37
+ // an empty set and reporting success.
38
+ this.#logger.error("channel.config.relays_corrupt", { channel_pubkey: channelHex });
39
+ return null;
40
+ }
41
+ return {
42
+ access: row.access,
43
+ relays,
44
+ guidance: row.guidance,
45
+ retention_seconds: Number(row.retention_seconds),
46
+ };
47
+ }
48
+ set(channelHex, config, now) {
49
+ this.#db
50
+ .prepare(`INSERT INTO channel_config (channel_pubkey, access, relays, guidance, retention_seconds, updated_at)
51
+ VALUES (?, ?, ?, ?, ?, ?)
52
+ ON CONFLICT (channel_pubkey) DO UPDATE SET
53
+ access = excluded.access, relays = excluded.relays, guidance = excluded.guidance,
54
+ retention_seconds = excluded.retention_seconds, updated_at = excluded.updated_at`)
55
+ .run(channelHex.toLowerCase(), config.access, JSON.stringify(config.relays), config.guidance, config.retention_seconds, now);
56
+ this.#logger.info("channel.config.set", {
57
+ channel_pubkey: channelHex, access: config.access, relays: config.relays.length,
58
+ });
59
+ }
60
+ }
61
+ //# sourceMappingURL=channel-config-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-config-store.js","sourceRoot":"","sources":["../src/channel-config-store.ts"],"names":[],"mappings":"AAkBA,8DAA8D;AAC9D,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAE1D,MAAM,CAAC,MAAM,yBAAyB,GAAG;;;;;;;;kDAQS,MAAM,CAAC,yBAAyB,CAAC;;;CAGlF,CAAC;AASF,MAAM,OAAO,kBAAkB;IACpB,GAAG,CAAiB;IACpB,OAAO,CAAS;IAEzB,YAAY,EAAkB,EAAE,MAAc;QAC5C,IAAI,CAAC,GAAG,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC;QACtB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAC3C,CAAC;IAED,GAAG,CAAC,UAAkB;QACpB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG;aACjB,OAAO,CAAC,iGAAiG,CAAC;aAC1G,GAAG,CAAC,UAAU,CAAC,WAAW,EAAE,CAElB,CAAC;QACd,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QAEtB,IAAI,MAAM,GAAa,EAAE,CAAC;QAC1B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAY,CAAC;YACjD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;gBAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC;QAC/F,CAAC;QAAC,MAAM,CAAC;YACP,8FAA8F;YAC9F,sCAAsC;YACtC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE,CAAC,CAAC;YACpF,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO;YACL,MAAM,EAAE,GAAG,CAAC,MAAuB;YACnC,MAAM;YACN,QAAQ,EAAE,GAAG,CAAC,QAAQ;YACtB,iBAAiB,EAAE,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC;SACjD,CAAC;IACJ,CAAC;IAED,GAAG,CAAC,UAAkB,EAAE,MAAqB,EAAE,GAAW;QACxD,IAAI,CAAC,GAAG;aACL,OAAO,CACN;;;;4FAIoF,CACrF;aACA,GAAG,CACF,UAAU,CAAC,WAAW,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,EACtE,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,iBAAiB,EAAE,GAAG,CAC/C,CAAC;QACJ,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE;YACtC,cAAc,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM;SAChF,CAAC,CAAC;IACL,CAAC;CACF"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * ChannelInboxStore — the posts a SUBSCRIBER has collected and verified (M16 018-PUBCOLLECT).
3
+ *
4
+ * Distinct from `channel-log-store.ts`, which is the PUBLISHER's copy of what it sent. This is the
5
+ * reader's side: what arrived, from which relay, and — when two relays disagree — both versions.
6
+ *
7
+ * ⚠️ **A FORK IS STORED, NOT RESOLVED.** Two different posts at one number means the channel signed
8
+ * both, which is the one thing a channel key cannot take back. Keeping only the first seen would
9
+ * make whichever relay answered first the arbiter of what the channel said, and would destroy the
10
+ * evidence that it said two things. So both rows survive, and the position stops below them.
11
+ *
12
+ * Keyed on `agent_id`, never `agent_name`.
13
+ */
14
+ import type { DaemonDatabase } from "./sqlcipher-db.js";
15
+ import type { Logger } from "./types.js";
16
+ import { type BroadcastArtifact } from "@cello-protocol/protocol-types";
17
+ export declare const CHANNEL_INBOX_CREATE_SQL = "\n CREATE TABLE IF NOT EXISTS channel_inbox (\n agent_id TEXT NOT NULL,\n channel_pubkey TEXT NOT NULL,\n seq INTEGER NOT NULL,\n -- The post's own hash is part of the key, which is what lets a FORK keep both rows rather than\n -- one overwriting the other. A (agent, channel, seq) key would silently drop the evidence.\n post_hash TEXT NOT NULL,\n post_cbor BLOB NOT NULL,\n receipt_cbor BLOB,\n from_relay TEXT NOT NULL,\n collected_at INTEGER NOT NULL,\n PRIMARY KEY (agent_id, channel_pubkey, seq, post_hash)\n );\n";
18
+ export type ChannelInboxErrorCode = "post_invalid" | "inbox_row_corrupt";
19
+ export declare class ChannelInboxError extends Error {
20
+ readonly code: ChannelInboxErrorCode;
21
+ constructor(code: ChannelInboxErrorCode, message: string);
22
+ }
23
+ export declare class ChannelInboxStore {
24
+ #private;
25
+ constructor(db: DaemonDatabase, logger: Logger);
26
+ /**
27
+ * Store one verified post. Idempotent on (agent, channel, seq, hash): the same post arriving from
28
+ * the second relay is the ordinary case, not a conflict.
29
+ */
30
+ store(agentId: string, channelHex: string, post: BroadcastArtifact, opts: {
31
+ receiptCbor?: Uint8Array;
32
+ fromRelay: string;
33
+ collectedAt: number;
34
+ }): {
35
+ stored: boolean;
36
+ };
37
+ /** Posts in [fromSeq, toSeq], seq order. A forked number contributes every version it holds. */
38
+ range(agentId: string, channelHex: string, fromSeq: number, toSeq: number): BroadcastArtifact[];
39
+ /** Every version held at one number. Length > 1 is a fork. */
40
+ forksFor(agentId: string, channelHex: string, seq: number): BroadcastArtifact[];
41
+ /** The post numbers this subscriber holds, ascending and deduplicated. */
42
+ heldSeqs(agentId: string, channelHex: string): number[];
43
+ /** The numbers at which this subscriber holds more than one distinct post. */
44
+ forkedSeqs(agentId: string, channelHex: string): number[];
45
+ }
46
+ //# sourceMappingURL=channel-inbox-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-inbox-store.d.ts","sourceRoot":"","sources":["../src/channel-inbox-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AAExC,eAAO,MAAM,wBAAwB,ymBAcpC,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG,cAAc,GAAG,mBAAmB,CAAC;AAEzE,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;gBACzB,IAAI,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM;CAKzD;AAED,qBAAa,iBAAiB;;gBAIhB,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAM9C;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE;QACxE,WAAW,CAAC,EAAE,UAAU,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;KAClE,GAAG;QAAE,MAAM,EAAE,OAAO,CAAA;KAAE;IAgBvB,gGAAgG;IAChG,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAW/F,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAI/E,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;IAUvD,8EAA8E;IAC9E,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,EAAE;CAqB1D"}
@@ -0,0 +1,88 @@
1
+ import { broadcastPostHash, decodeBroadcastArtifact, encodeBroadcastArtifact, } from "@cello-protocol/protocol-types";
2
+ export const CHANNEL_INBOX_CREATE_SQL = `
3
+ CREATE TABLE IF NOT EXISTS channel_inbox (
4
+ agent_id TEXT NOT NULL,
5
+ channel_pubkey TEXT NOT NULL,
6
+ seq INTEGER NOT NULL,
7
+ -- The post's own hash is part of the key, which is what lets a FORK keep both rows rather than
8
+ -- one overwriting the other. A (agent, channel, seq) key would silently drop the evidence.
9
+ post_hash TEXT NOT NULL,
10
+ post_cbor BLOB NOT NULL,
11
+ receipt_cbor BLOB,
12
+ from_relay TEXT NOT NULL,
13
+ collected_at INTEGER NOT NULL,
14
+ PRIMARY KEY (agent_id, channel_pubkey, seq, post_hash)
15
+ );
16
+ `;
17
+ export class ChannelInboxError extends Error {
18
+ code;
19
+ constructor(code, message) {
20
+ super(`${code}: ${message}`);
21
+ this.name = "ChannelInboxError";
22
+ this.code = code;
23
+ }
24
+ }
25
+ export class ChannelInboxStore {
26
+ #db;
27
+ #logger;
28
+ constructor(db, logger) {
29
+ this.#db = db;
30
+ this.#logger = logger;
31
+ this.#db.exec(CHANNEL_INBOX_CREATE_SQL);
32
+ }
33
+ /**
34
+ * Store one verified post. Idempotent on (agent, channel, seq, hash): the same post arriving from
35
+ * the second relay is the ordinary case, not a conflict.
36
+ */
37
+ store(agentId, channelHex, post, opts) {
38
+ const cbor = encodeBroadcastArtifact(post);
39
+ const hashHex = Buffer.from(broadcastPostHash(post)).toString("hex");
40
+ const inserted = this.#db
41
+ .prepare(`INSERT OR IGNORE INTO channel_inbox
42
+ (agent_id, channel_pubkey, seq, post_hash, post_cbor, receipt_cbor, from_relay, collected_at)
43
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
44
+ .run(agentId, channelHex.toLowerCase(), post.seq, hashHex, Buffer.from(cbor), opts.receiptCbor ? Buffer.from(opts.receiptCbor) : null, opts.fromRelay, opts.collectedAt);
45
+ return { stored: Number(inserted.changes) > 0 };
46
+ }
47
+ /** Posts in [fromSeq, toSeq], seq order. A forked number contributes every version it holds. */
48
+ range(agentId, channelHex, fromSeq, toSeq) {
49
+ const rows = this.#db
50
+ .prepare(`SELECT seq, post_cbor FROM channel_inbox
51
+ WHERE agent_id = ? AND channel_pubkey = ? AND seq BETWEEN ? AND ?
52
+ ORDER BY seq ASC, post_hash ASC`)
53
+ .all(agentId, channelHex.toLowerCase(), fromSeq, toSeq);
54
+ return rows.map((r) => this.#decode(Number(r.seq), new Uint8Array(r.post_cbor)));
55
+ }
56
+ /** Every version held at one number. Length > 1 is a fork. */
57
+ forksFor(agentId, channelHex, seq) {
58
+ return this.range(agentId, channelHex, seq, seq);
59
+ }
60
+ /** The post numbers this subscriber holds, ascending and deduplicated. */
61
+ heldSeqs(agentId, channelHex) {
62
+ const rows = this.#db
63
+ .prepare(`SELECT DISTINCT seq FROM channel_inbox
64
+ WHERE agent_id = ? AND channel_pubkey = ? ORDER BY seq ASC`)
65
+ .all(agentId, channelHex.toLowerCase());
66
+ return rows.map((r) => Number(r.seq));
67
+ }
68
+ /** The numbers at which this subscriber holds more than one distinct post. */
69
+ forkedSeqs(agentId, channelHex) {
70
+ const rows = this.#db
71
+ .prepare(`SELECT seq FROM channel_inbox
72
+ WHERE agent_id = ? AND channel_pubkey = ?
73
+ GROUP BY seq HAVING COUNT(DISTINCT post_hash) > 1 ORDER BY seq ASC`)
74
+ .all(agentId, channelHex.toLowerCase());
75
+ return rows.map((r) => Number(r.seq));
76
+ }
77
+ #decode(seq, cbor) {
78
+ const decoded = decodeBroadcastArtifact(cbor);
79
+ if (!decoded.ok) {
80
+ // A stored row that no longer decodes is corruption, never something to skip past: the caller
81
+ // is about to show these to an agent.
82
+ this.#logger.error("channel.inbox.row_corrupt", { seq, reason: decoded.reason });
83
+ throw new ChannelInboxError("inbox_row_corrupt", `stored seq ${String(seq)} does not decode: ${decoded.reason}`);
84
+ }
85
+ return decoded.artifact;
86
+ }
87
+ }
88
+ //# sourceMappingURL=channel-inbox-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-inbox-store.js","sourceRoot":"","sources":["../src/channel-inbox-store.ts"],"names":[],"mappings":"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"}
@@ -48,6 +48,20 @@ export declare class ChannelLogStore {
48
48
  append(channelPubkeyHex: string, post: BroadcastArtifact, correlationId?: string): {
49
49
  seq: number;
50
50
  };
51
+ /**
52
+ * Replace the bytes at a position that NO relay has receipted yet (M16 018-PUBCOLLECT).
53
+ *
54
+ * The one caller is the clock-skew correction: a relay refused the post for its timestamp, the
55
+ * time is inside both signatures, so the corrected post is different bytes at the same number.
56
+ *
57
+ * ⚠️ **REFUSES ONCE ANY RECEIPT EXISTS, and that refusal is the point.** A receipt binds the post
58
+ * BY HASH. Replacing bytes a relay has already acknowledged would leave a receipt that proves
59
+ * nothing and a subscriber holding the old bytes at the same number as the new ones — which is
60
+ * indistinguishable from the publisher forking its own channel.
61
+ *
62
+ * ⚠️ It does NOT move `next_seq`. This is a correction to an existing position, not an append.
63
+ */
64
+ replaceUnreceipted(channelPubkeyHex: string, post: BroadcastArtifact, correlationId?: string): void;
51
65
  /**
52
66
  * Store one relay's receipt for a post this log holds. VERIFIES FIRST, against the stored post —
53
67
  * not against whatever the caller passed alongside it — so a receipt for a different post at the
@@ -1 +1 @@
1
- {"version":3,"file":"channel-log-store.d.ts","sourceRoot":"","sources":["../src/channel-log-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAUzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAE1F,eAAO,MAAM,sBAAsB,ijCAyBlC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC3B,iBAAiB,GAAG,cAAc,GAAG,gBAAgB,GAAG,cAAc,GAGtE,iBAAiB,GAGjB,aAAa,GAEb,kBAAkB,GAElB,iBAAiB,CAAC;AAEtB,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;gBACvB,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM;CAKvD;AAOD,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kFAAkF;IAClF,cAAc,EAAE,MAAM,CAAC;CACxB;AAID,qBAAa,eAAe;;gBAId,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAM9C,wEAAwE;IACxE,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAM7C,8FAA8F;IAC9F,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE;IAIvD,oFAAoF;IACpF,MAAM,CAAC,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE;IA8DlG;;;;;;;;;OASG;IACH,aAAa,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IA6DhG,uFAAuF;IACvF,WAAW,CAAC,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,EAAE;IActE,oFAAoF;IACpF,SAAS,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAQxF;;;;OAIG;IACH,YAAY,CAAC,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE;IAoC9E;;;OAGG;IACH,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,WAAW;CAyD5C"}
1
+ {"version":3,"file":"channel-log-store.d.ts","sourceRoot":"","sources":["../src/channel-log-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAUzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAE1F,eAAO,MAAM,sBAAsB,ijCAyBlC,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAC3B,iBAAiB,GAAG,cAAc,GAAG,gBAAgB,GAAG,cAAc,GAGtE,iBAAiB,GAGjB,aAAa,GAEb,kBAAkB,GAElB,iBAAiB,CAAC;AAEtB,qBAAa,eAAgB,SAAQ,KAAK;IACxC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;gBACvB,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,MAAM;CAKvD;AAOD,MAAM,WAAW,WAAW;IAC1B,+DAA+D;IAC/D,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kFAAkF;IAClF,cAAc,EAAE,MAAM,CAAC;CACxB;AAID,qBAAa,eAAe;;gBAId,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM;IAM9C,wEAAwE;IACxE,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAM7C,8FAA8F;IAC9F,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE;IAIvD,oFAAoF;IACpF,MAAM,CAAC,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE;IA8DlG;;;;;;;;;;;;OAYG;IACH,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IA+CnG;;;;;;;;;OASG;IACH,aAAa,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IA6DhG,uFAAuF;IACvF,WAAW,CAAC,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,gBAAgB,EAAE;IActE,oFAAoF;IACpF,SAAS,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,EAAE;IAQxF;;;;OAIG;IACH,YAAY,CAAC,gBAAgB,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE;IAoC9E;;;OAGG;IACH,IAAI,CAAC,gBAAgB,EAAE,MAAM,GAAG,WAAW;CAyD5C"}
@@ -110,6 +110,64 @@ export class ChannelLogStore {
110
110
  });
111
111
  return { seq: post.seq };
112
112
  }
113
+ /**
114
+ * Replace the bytes at a position that NO relay has receipted yet (M16 018-PUBCOLLECT).
115
+ *
116
+ * The one caller is the clock-skew correction: a relay refused the post for its timestamp, the
117
+ * time is inside both signatures, so the corrected post is different bytes at the same number.
118
+ *
119
+ * ⚠️ **REFUSES ONCE ANY RECEIPT EXISTS, and that refusal is the point.** A receipt binds the post
120
+ * BY HASH. Replacing bytes a relay has already acknowledged would leave a receipt that proves
121
+ * nothing and a subscriber holding the old bytes at the same number as the new ones — which is
122
+ * indistinguishable from the publisher forking its own channel.
123
+ *
124
+ * ⚠️ It does NOT move `next_seq`. This is a correction to an existing position, not an append.
125
+ */
126
+ replaceUnreceipted(channelPubkeyHex, post, correlationId) {
127
+ this.#db.exec("BEGIN IMMEDIATE");
128
+ try {
129
+ const receipted = this.#db
130
+ .prepare(`SELECT COUNT(*) AS n FROM channel_log_receipts WHERE channel_pubkey = ? AND seq = ?`)
131
+ .get(channelPubkeyHex, post.seq);
132
+ if (Number(receipted.n) > 0) {
133
+ throw new ChannelLogError("position_taken", `seq ${post.seq} has already been receipted and cannot be re-signed`);
134
+ }
135
+ // The same three checks `append` makes. A correction is still a post, and skipping them here
136
+ // would make this the one way to get unverifiable bytes into the log.
137
+ let cbor;
138
+ try {
139
+ cbor = encodeBroadcastArtifact(post);
140
+ }
141
+ catch (err) {
142
+ throw new ChannelLogError("post_invalid", extractErrorMessage(err));
143
+ }
144
+ const decoded = decodeBroadcastArtifact(cbor);
145
+ if (!decoded.ok)
146
+ throw new ChannelLogError("post_invalid", `${decoded.reason}: ${decoded.detail}`);
147
+ const verdict = verifyBroadcastArtifact(decoded.artifact);
148
+ if (!verdict.ok)
149
+ throw new ChannelLogError("post_invalid", verdict.reason);
150
+ if (hexOf(post.channel_pubkey) !== channelPubkeyHex) {
151
+ throw new ChannelLogError("post_invalid", `the post is signed by channel ${hexOf(post.channel_pubkey).slice(0, 16)}, not ${channelPubkeyHex.slice(0, 16)}`);
152
+ }
153
+ const updated = this.#db
154
+ .prepare(`UPDATE channel_log SET published_at = ?, title = ?, post_cbor = ?
155
+ WHERE channel_pubkey = ? AND seq = ?`)
156
+ .run(post.published_at, post.title, Buffer.from(cbor), channelPubkeyHex, post.seq);
157
+ if (Number(updated.changes) === 0) {
158
+ throw new ChannelLogError("seq_not_next", `seq ${post.seq} is not in this channel's log`);
159
+ }
160
+ this.#db.exec("COMMIT");
161
+ }
162
+ catch (err) {
163
+ this.#rollback(channelPubkeyHex, err);
164
+ throw err;
165
+ }
166
+ this.#logger.info("channel.post.resigned", {
167
+ ...(correlationId !== undefined ? { correlationId } : {}),
168
+ channel_pubkey: channelPubkeyHex, seq: post.seq, published_at: post.published_at,
169
+ });
170
+ }
113
171
  /**
114
172
  * Store one relay's receipt for a post this log holds. VERIFIES FIRST, against the stored post —
115
173
  * not against whatever the caller passed alongside it — so a receipt for a different post at the
@@ -1 +1 @@
1
- {"version":3,"file":"channel-log-store.js","sourceRoot":"","sources":["../src/channel-log-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"channel-log-store.js","sourceRoot":"","sources":["../src/channel-log-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * M16 018-PUBCOLLECT — the operator's IPC verbs for publishing on a channel.
3
+ *
4
+ * cello_channel_publish publish a post to the channel's relays
5
+ * cello_channel_info_set sign and deposit the channel's info record
6
+ * cello_channel_prune drop everything through a post number, oldest end only
7
+ * cello_channel_resend refill a relay that lost content, or fill a newly added one
8
+ *
9
+ * **IPC only — no MCP tools.** These are the publisher's own administration of a channel it holds
10
+ * the key to, and the caller is a human at a terminal, not an agent mid-conversation. The MCP
11
+ * surface for channels is a later tier; adding one here would put a publishing verb in reach of
12
+ * anything that can drive an agent's tools.
13
+ *
14
+ * Every verb answers `{ ok: false, reason, guidance }` rather than throwing, because the far side is
15
+ * a CLI that has to print something a person can act on.
16
+ */
17
+ import type { Logger } from "./types.js";
18
+ import type { ChannelPublisher } from "./channel-publisher.js";
19
+ import { type ChannelConfig } from "./channel-config-store.js";
20
+ type Handler = (params: Record<string, unknown> | undefined, connectionId: string) => Promise<unknown>;
21
+ export interface ChannelPublishDeps {
22
+ handlers: Map<string, Handler>;
23
+ logger: Logger;
24
+ getPublisher: (agentName: string) => ChannelPublisher | null;
25
+ /**
26
+ * Record the publisher's decisions for a channel it holds the key to. REFUSES when this daemon
27
+ * does not hold that channel's key — otherwise an operator could record relays for somebody
28
+ * else's channel and every later verb would fail with a key error instead of the real reason.
29
+ */
30
+ setChannelConfig: (agentName: string, channelHex: string, config: ChannelConfig) => {
31
+ ok: true;
32
+ } | {
33
+ ok: false;
34
+ reason: string;
35
+ guidance?: string;
36
+ };
37
+ /** The daemon's single agent-selection rule, injected rather than re-implemented here. */
38
+ resolveCurrentAgent: (connectionId: string, explicitAgent?: string) => string | null;
39
+ }
40
+ export declare function registerChannelPublishHandlers(deps: ChannelPublishDeps): void;
41
+ export {};
42
+ //# sourceMappingURL=channel-publish-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel-publish-handlers.d.ts","sourceRoot":"","sources":["../src/channel-publish-handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAA6B,KAAK,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE1F,KAAK,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAAE,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAEvG,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,gBAAgB,GAAG,IAAI,CAAC;IAC7D;;;;OAIG;IACH,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,KAC7E;QAAE,EAAE,EAAE,IAAI,CAAA;KAAE,GAAG;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,0FAA0F;IAC1F,mBAAmB,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAC;CACtF;AAkCD,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,kBAAkB,GAAG,IAAI,CAuL7E"}
@@ -0,0 +1,212 @@
1
+ import { DEFAULT_RETENTION_SECONDS } from "./channel-config-store.js";
2
+ function needAgent(deps, params, connectionId) {
3
+ const agentName = deps.resolveCurrentAgent(connectionId, params?.["agent"]);
4
+ if (agentName === null) {
5
+ return {
6
+ ok: false,
7
+ answer: {
8
+ ok: false, reason: "no_current_agent",
9
+ guidance: "Name the agent, or select one for this connection with cello_use_agent.",
10
+ },
11
+ };
12
+ }
13
+ return { ok: true, agentName };
14
+ }
15
+ function needChannel(params) {
16
+ const raw = params?.["channel"];
17
+ // 64 hex characters, checked here rather than deeper: a malformed pubkey that reaches the log
18
+ // creates a channel row under a key nothing will ever publish to again.
19
+ if (typeof raw !== "string" || !/^[0-9a-fA-F]{64}$/.test(raw)) {
20
+ return {
21
+ ok: false,
22
+ answer: {
23
+ ok: false, reason: "bad_channel",
24
+ guidance: "Pass the channel's 64-character hex public key as `channel`.",
25
+ },
26
+ };
27
+ }
28
+ return { ok: true, channelHex: raw.toLowerCase() };
29
+ }
30
+ export function registerChannelPublishHandlers(deps) {
31
+ const { handlers, logger } = deps;
32
+ handlers.set("cello_channel_publish", async (params, connectionId) => {
33
+ const agent = needAgent(deps, params, connectionId);
34
+ if (!agent.ok)
35
+ return agent.answer;
36
+ const channel = needChannel(params);
37
+ if (!channel.ok)
38
+ return channel.answer;
39
+ const title = params?.["title"];
40
+ const body = params?.["body"];
41
+ if (typeof title !== "string" || typeof body !== "string") {
42
+ return { ok: false, reason: "bad_post", guidance: "Pass a `title` and a `body`, both strings." };
43
+ }
44
+ const publisher = deps.getPublisher(agent.agentName);
45
+ if (!publisher) {
46
+ return { ok: false, reason: "channel_unknown", guidance: `${agent.agentName} is not a channel this daemon publishes for.` };
47
+ }
48
+ const result = await publisher.publish(agent.agentName, channel.channelHex, title, body);
49
+ if (result.ok) {
50
+ return {
51
+ ok: true, seq: result.seq,
52
+ relays_ok: result.deposited.filter((d) => d.ok).map((d) => d.relay),
53
+ relays_failed: result.deposited.filter((d) => !d.ok).map((d) => d.relay),
54
+ };
55
+ }
56
+ logger.info("channel.publish.refused", { channel_pubkey: channel.channelHex, reason: result.reason });
57
+ return {
58
+ ok: false, reason: result.reason, detail: result.detail,
59
+ // `no_relay_accepted` is the one refusal where the post SURVIVES, and a caller that did not
60
+ // know that would publish it again and burn a second post number on the same content.
61
+ guidance: result.reason === "no_relay_accepted"
62
+ // `cello channel resend`, NOT the handler's own name: these verbs are terminal-only, so a
63
+ // `cello_*` token here would hand the operator a command that does not exist on any surface
64
+ // they can reach.
65
+ ? "No relay took the post. It is in your log — retry with 'cello channel resend' rather than publishing it again."
66
+ : undefined,
67
+ };
68
+ });
69
+ /**
70
+ * Record what this publisher has decided about its own channel: which relays it publishes to,
71
+ * whether it is public, what it is for, how long posts are kept.
72
+ *
73
+ * ⚠️ **NOTHING ELSE WRITES THIS, AND WITHOUT IT NO CHANNEL CAN PUBLISH AT ALL.** The publisher
74
+ * reads the relay pair from here; with no row, every verb answers `channel_unknown` for ever. The
75
+ * first version of this unit shipped the four verbs with no way to create the row they all read.
76
+ *
77
+ * It does NOT deposit anything. `cello_channel_info_set` signs and publishes the description to
78
+ * the relays; this is the local decision it is signed from. Keeping them apart means a publisher
79
+ * can change its mind and re-publish, rather than the relays' copy being the only record.
80
+ */
81
+ handlers.set("cello_channel_config", async (params, connectionId) => {
82
+ const agent = needAgent(deps, params, connectionId);
83
+ if (!agent.ok)
84
+ return agent.answer;
85
+ const channel = needChannel(params);
86
+ if (!channel.ok)
87
+ return channel.answer;
88
+ const rawRelays = params?.["relays"];
89
+ const relays = Array.isArray(rawRelays) ? rawRelays.filter((r) => typeof r === "string") : [];
90
+ if (relays.length === 0 || relays.length !== rawRelays.length) {
91
+ return {
92
+ ok: false, reason: "bad_relays",
93
+ guidance: "Pass `relays`: the multiaddrs this channel publishes to. Two is the design — one is a single point of failure, and the subscriber takes the union of both.",
94
+ };
95
+ }
96
+ const access = params?.["access"];
97
+ if (access !== "public" && access !== "open" && access !== "invite_only") {
98
+ return {
99
+ ok: false, reason: "bad_access",
100
+ guidance: "Pass `access`: public (anyone can read), open (anyone may ask to join) or invite_only.",
101
+ };
102
+ }
103
+ const guidance = typeof params?.["guidance"] === "string" ? params["guidance"] : "";
104
+ const retention = params?.["retention_seconds"];
105
+ const retention_seconds = typeof retention === "number" && Number.isSafeInteger(retention) && retention > 0
106
+ ? retention
107
+ : DEFAULT_RETENTION_SECONDS;
108
+ const saved = deps.setChannelConfig(agent.agentName, channel.channelHex, {
109
+ access, relays, guidance, retention_seconds,
110
+ });
111
+ if (!saved.ok)
112
+ return { ok: false, reason: saved.reason, guidance: saved.guidance };
113
+ logger.info("channel.config.recorded", {
114
+ channel_pubkey: channel.channelHex, access, relays: relays.length,
115
+ });
116
+ return {
117
+ ok: true, channel: channel.channelHex, access, relays, retention_seconds,
118
+ guidance: "Recorded locally. Run 'cello channel info-set' to publish the description so subscribers can find the channel.",
119
+ };
120
+ });
121
+ handlers.set("cello_channel_info_set", async (params, connectionId) => {
122
+ const agent = needAgent(deps, params, connectionId);
123
+ if (!agent.ok)
124
+ return agent.answer;
125
+ const channel = needChannel(params);
126
+ if (!channel.ok)
127
+ return channel.answer;
128
+ const publisher = deps.getPublisher(agent.agentName);
129
+ if (!publisher)
130
+ return { ok: false, reason: "channel_unknown" };
131
+ const result = await publisher.publishInfo(agent.agentName, channel.channelHex);
132
+ if (!result.ok) {
133
+ return {
134
+ ok: false, reason: result.reason, detail: result.detail,
135
+ guidance: result.reason === "no_relay_accepted"
136
+ ? "No relay took the channel's description, so nobody can discover this channel yet. Check the relays are reachable and run it again."
137
+ : undefined,
138
+ };
139
+ }
140
+ return {
141
+ ok: true,
142
+ bytes: result.info_cbor.length,
143
+ // WHICH relays hold it, not just that it was signed. A subscriber can only find this channel
144
+ // through a relay that actually took the record.
145
+ relays_ok: result.relays.filter((r) => r.ok).map((r) => r.relay),
146
+ relays_failed: result.relays.filter((r) => !r.ok).map((r) => ({ relay: r.relay, reason: r.reason })),
147
+ };
148
+ });
149
+ handlers.set("cello_channel_prune", async (params, connectionId) => {
150
+ const agent = needAgent(deps, params, connectionId);
151
+ if (!agent.ok)
152
+ return agent.answer;
153
+ const channel = needChannel(params);
154
+ if (!channel.ok)
155
+ return channel.answer;
156
+ const through = params?.["through_seq"];
157
+ if (typeof through !== "number" || !Number.isSafeInteger(through) || through < 1) {
158
+ return { ok: false, reason: "bad_seq", guidance: "Pass `through_seq`: the last post number to drop." };
159
+ }
160
+ const publisher = deps.getPublisher(agent.agentName);
161
+ if (!publisher)
162
+ return { ok: false, reason: "channel_unknown" };
163
+ const result = await publisher.pruneChannel(agent.agentName, channel.channelHex, through);
164
+ const stillHolding = result.relays.filter((r) => !r.ok);
165
+ return {
166
+ ok: true,
167
+ pruned: result.pruned,
168
+ relays: result.relays,
169
+ // The log is pruned either way — that part is local and cannot fail halfway. A relay that did
170
+ // not drop its copy KEEPS SERVING those posts, and saying so is the difference between an
171
+ // operator who knows their content is still out there and one who believes it is gone.
172
+ guidance: stillHolding.length > 0
173
+ ? `Your own copy is pruned. ${String(stillHolding.length)} relay(s) did not drop theirs and will keep serving those posts until retention expires.`
174
+ : undefined,
175
+ };
176
+ });
177
+ handlers.set("cello_channel_resend", async (params, connectionId) => {
178
+ const agent = needAgent(deps, params, connectionId);
179
+ if (!agent.ok)
180
+ return agent.answer;
181
+ const channel = needChannel(params);
182
+ if (!channel.ok)
183
+ return channel.answer;
184
+ const publisher = deps.getPublisher(agent.agentName);
185
+ if (!publisher)
186
+ return { ok: false, reason: "channel_unknown" };
187
+ /**
188
+ * ⚠️ **NO RELAY NAMED MEANS ALL OF THEM**, and that is the ordinary case. Requiring the operator
189
+ * to type a multiaddr made this verb impossible to run from the terminal, which left a relay
190
+ * that lost content with no repair path at all — the one job this verb has. Naming a relay
191
+ * stays available for the case where only one needs refilling.
192
+ */
193
+ const raw = params?.["relay"];
194
+ if (raw !== undefined && (typeof raw !== "string" || raw.length === 0)) {
195
+ return { ok: false, reason: "bad_relay", guidance: "Pass `relay` as a multiaddr, or leave it out to refill every relay." };
196
+ }
197
+ const targets = typeof raw === "string" ? [raw] : publisher.relaysFor(channel.channelHex);
198
+ if (targets.length === 0) {
199
+ return {
200
+ ok: false, reason: "channel_unknown",
201
+ guidance: "This daemon has no relays recorded for that channel, so there is nothing to refill.",
202
+ };
203
+ }
204
+ const per = [];
205
+ for (const target of targets) {
206
+ const result = await publisher.resendMissing(agent.agentName, channel.channelHex, target);
207
+ per.push({ relay: target, deposited: result.deposited });
208
+ }
209
+ return { ok: true, deposited: per.reduce((n, r) => n + r.deposited, 0), relays: per };
210
+ });
211
+ }
212
+ //# sourceMappingURL=channel-publish-handlers.js.map