@sechroom/cli 2026.7.6 → 2026.7.7
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/dist/index.js +89 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1544,12 +1544,16 @@ function registerChannel(program2) {
|
|
|
1544
1544
|
const cfg = resolveConfig(cmd.optsWithGlobals());
|
|
1545
1545
|
const filter = readFilter(opts);
|
|
1546
1546
|
const sub = await ensureSubscription(cfg, opts.name, filter);
|
|
1547
|
-
const
|
|
1548
|
-
|
|
1549
|
-
|
|
1547
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1548
|
+
const deliver = makeDeliver(
|
|
1549
|
+
filter,
|
|
1550
|
+
seen,
|
|
1551
|
+
(payload) => process.stdout.write(
|
|
1550
1552
|
(typeof payload === "string" ? payload : JSON.stringify(payload)) + "\n"
|
|
1551
|
-
)
|
|
1552
|
-
|
|
1553
|
+
)
|
|
1554
|
+
);
|
|
1555
|
+
const conn = await openConnection(cfg, deliver);
|
|
1556
|
+
await reconcile(cfg, filter, deliver);
|
|
1553
1557
|
if (json) {
|
|
1554
1558
|
emit(
|
|
1555
1559
|
{
|
|
@@ -1586,8 +1590,8 @@ function registerChannel(program2) {
|
|
|
1586
1590
|
);
|
|
1587
1591
|
await mcp.connect(new StdioServerTransport());
|
|
1588
1592
|
await ensureSubscription(cfg, opts.name, filter);
|
|
1589
|
-
const
|
|
1590
|
-
|
|
1593
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1594
|
+
const deliver = makeDeliver(filter, seen, (payload) => {
|
|
1591
1595
|
const { content, meta } = summarizeEvent(payload);
|
|
1592
1596
|
void mcp.notification({
|
|
1593
1597
|
method: "notifications/claude/channel",
|
|
@@ -1597,6 +1601,8 @@ function registerChannel(program2) {
|
|
|
1597
1601
|
`))
|
|
1598
1602
|
);
|
|
1599
1603
|
});
|
|
1604
|
+
const conn = await openConnection(cfg, deliver);
|
|
1605
|
+
await reconcile(cfg, filter, deliver);
|
|
1600
1606
|
process.stderr.write(
|
|
1601
1607
|
style.dim(
|
|
1602
1608
|
`sechroom channel (mcp) \u2014 tenant ${cfg.tenant}, tags [${filter.tags.join(", ")}]
|
|
@@ -1760,6 +1766,82 @@ function shouldDeliver(payload, filter) {
|
|
|
1760
1766
|
}
|
|
1761
1767
|
return true;
|
|
1762
1768
|
}
|
|
1769
|
+
function makeDeliver(filter, seen, forward) {
|
|
1770
|
+
return (payload) => {
|
|
1771
|
+
if (!shouldDeliver(payload, filter)) return;
|
|
1772
|
+
const { memoryId } = parseEvent(payload);
|
|
1773
|
+
if (memoryId) {
|
|
1774
|
+
if (seen.has(memoryId)) return;
|
|
1775
|
+
seen.add(memoryId);
|
|
1776
|
+
}
|
|
1777
|
+
forward(payload);
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
async function reconcile(cfg, filter, deliver) {
|
|
1781
|
+
if (filter.workspaceScope.length === 0) {
|
|
1782
|
+
process.stderr.write(
|
|
1783
|
+
style.dim(
|
|
1784
|
+
"channel: no --workspace to reconcile against; live feed only (a dropped dispatch won't be recovered).\n"
|
|
1785
|
+
)
|
|
1786
|
+
);
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
if (filter.tags.length === 0) return;
|
|
1790
|
+
let token;
|
|
1791
|
+
try {
|
|
1792
|
+
token = await requireToken(cfg);
|
|
1793
|
+
} catch {
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
const qs = `filterTags=${encodeURIComponent(filter.tags.join(","))}&limit=100`;
|
|
1797
|
+
let recovered = 0;
|
|
1798
|
+
for (const ws of filter.workspaceScope) {
|
|
1799
|
+
try {
|
|
1800
|
+
const resp = await fetch(
|
|
1801
|
+
`${cfg.baseUrl}/workspaces/${encodeURIComponent(ws)}/memories/feed?${qs}`,
|
|
1802
|
+
{
|
|
1803
|
+
headers: {
|
|
1804
|
+
authorization: `Bearer ${token}`,
|
|
1805
|
+
tenant: cfg.tenant,
|
|
1806
|
+
"x-sechroom-surface": "cli"
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
);
|
|
1810
|
+
if (!resp.ok) {
|
|
1811
|
+
process.stderr.write(
|
|
1812
|
+
err(
|
|
1813
|
+
`channel: reconcile query for ${ws} failed (HTTP ${resp.status})
|
|
1814
|
+
`
|
|
1815
|
+
)
|
|
1816
|
+
);
|
|
1817
|
+
continue;
|
|
1818
|
+
}
|
|
1819
|
+
const data = await resp.json();
|
|
1820
|
+
for (const m of data.results ?? []) {
|
|
1821
|
+
if (!m.id) continue;
|
|
1822
|
+
deliver({
|
|
1823
|
+
eventType: "reconcile",
|
|
1824
|
+
memoryId: m.id,
|
|
1825
|
+
workspaceId: ws,
|
|
1826
|
+
tags: m.tags ?? []
|
|
1827
|
+
});
|
|
1828
|
+
recovered++;
|
|
1829
|
+
}
|
|
1830
|
+
} catch (e) {
|
|
1831
|
+
process.stderr.write(
|
|
1832
|
+
err(`channel: reconcile error for ${ws}: ${String(e)}
|
|
1833
|
+
`)
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
if (recovered > 0)
|
|
1838
|
+
process.stderr.write(
|
|
1839
|
+
style.dim(
|
|
1840
|
+
`channel: reconciled ${recovered} already-queued event(s) on connect.
|
|
1841
|
+
`
|
|
1842
|
+
)
|
|
1843
|
+
);
|
|
1844
|
+
}
|
|
1763
1845
|
function facetedTagMatch(eventTags, filterTags) {
|
|
1764
1846
|
const have = new Set(eventTags);
|
|
1765
1847
|
const groups = /* @__PURE__ */ new Map();
|
package/package.json
CHANGED