@agentconnect.md/daemon 1.0.0-rc.59 → 1.0.0-rc.60
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 +111 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13132,6 +13132,22 @@ const IntegrationSpec = object({
|
|
|
13132
13132
|
const IntegrationUpsert = IntegrationSpec;
|
|
13133
13133
|
/** C→D EVT — remove an integration from the daemon. */
|
|
13134
13134
|
const IntegrationRemove = object({ integrationId: string().uuid() });
|
|
13135
|
+
/** One channel the bot is currently a member of (metadata only — no messages). */
|
|
13136
|
+
const IntegrationChannel = object({
|
|
13137
|
+
id: string(),
|
|
13138
|
+
name: string().optional(),
|
|
13139
|
+
isPrivate: boolean().optional()
|
|
13140
|
+
});
|
|
13141
|
+
/**
|
|
13142
|
+
* D→C EVT — the FULL set of channels the integration's bot is a member of
|
|
13143
|
+
* (fire-and-forget, latest-wins). Emitted on socket start and whenever the bot
|
|
13144
|
+
* is invited to / removed from a channel, so the console can offer per-channel
|
|
13145
|
+
* trigger config. Channel names are control metadata, never message content.
|
|
13146
|
+
*/
|
|
13147
|
+
const IntegrationChannels = object({
|
|
13148
|
+
integrationId: string().uuid(),
|
|
13149
|
+
channels: array(IntegrationChannel)
|
|
13150
|
+
});
|
|
13135
13151
|
//#endregion
|
|
13136
13152
|
//#region ../protocol/dist/frames/register.js
|
|
13137
13153
|
/**
|
|
@@ -13284,6 +13300,7 @@ const FRAME_SCHEMAS = {
|
|
|
13284
13300
|
"cron/remove": CronRemove,
|
|
13285
13301
|
"integration/upsert": IntegrationUpsert,
|
|
13286
13302
|
"integration/remove": IntegrationRemove,
|
|
13303
|
+
"integration/channels": IntegrationChannels,
|
|
13287
13304
|
"secrets/request": SecretsRequest,
|
|
13288
13305
|
"secrets/grant": SecretsGrant,
|
|
13289
13306
|
"secrets/renew": SecretsRenew,
|
|
@@ -13408,6 +13425,7 @@ discriminatedUnion("type", [
|
|
|
13408
13425
|
frame("cron/remove", FRAME_SCHEMAS["cron/remove"]),
|
|
13409
13426
|
frame("integration/upsert", FRAME_SCHEMAS["integration/upsert"]),
|
|
13410
13427
|
frame("integration/remove", FRAME_SCHEMAS["integration/remove"]),
|
|
13428
|
+
frame("integration/channels", FRAME_SCHEMAS["integration/channels"]),
|
|
13411
13429
|
frame("secrets/request", FRAME_SCHEMAS["secrets/request"]),
|
|
13412
13430
|
frame("secrets/grant", FRAME_SCHEMAS["secrets/grant"]),
|
|
13413
13431
|
frame("secrets/renew", FRAME_SCHEMAS["secrets/renew"]),
|
|
@@ -80633,6 +80651,16 @@ var SlackConnection = class {
|
|
|
80633
80651
|
if (!ev.channel) return;
|
|
80634
80652
|
deliver(ev, "app_mention");
|
|
80635
80653
|
});
|
|
80654
|
+
this.app.event("member_joined_channel", async ({ event }) => {
|
|
80655
|
+
const ev = event;
|
|
80656
|
+
if (ev.user !== this.botUserId) return;
|
|
80657
|
+
log?.debug(`slack: bot joined channel ${ev.channel ?? "?"}`);
|
|
80658
|
+
this.deps.onChannelsChanged?.();
|
|
80659
|
+
});
|
|
80660
|
+
for (const type of ["channel_left", "group_left"]) this.app.event(type, async ({ event }) => {
|
|
80661
|
+
log?.debug(`slack: bot left channel ${event.channel ?? "?"} (${type})`);
|
|
80662
|
+
this.deps.onChannelsChanged?.();
|
|
80663
|
+
});
|
|
80636
80664
|
log?.debug("slack: app.start → opening Socket Mode WebSocket (wss://…slack.com)…");
|
|
80637
80665
|
await this.app.start();
|
|
80638
80666
|
log?.debug("slack: app.start resolved → socket established");
|
|
@@ -80750,6 +80778,39 @@ var SlackConnection = class {
|
|
|
80750
80778
|
isBot: p.isBot
|
|
80751
80779
|
})).catch(() => ({ id }))));
|
|
80752
80780
|
}
|
|
80781
|
+
/**
|
|
80782
|
+
* The channels this bot is a MEMBER of (users.conversations, cursor-paginated) —
|
|
80783
|
+
* the membership snapshot behind the console's per-channel trigger config. DMs /
|
|
80784
|
+
* group DMs are excluded (they are not configurable channels). Returns null on
|
|
80785
|
+
* any API failure so the caller never mistakes an error for "left all channels".
|
|
80786
|
+
*/
|
|
80787
|
+
async listBotChannels() {
|
|
80788
|
+
const out = [];
|
|
80789
|
+
let cursor;
|
|
80790
|
+
try {
|
|
80791
|
+
do {
|
|
80792
|
+
const res = await this.app.client.users.conversations({
|
|
80793
|
+
types: "public_channel,private_channel",
|
|
80794
|
+
exclude_archived: true,
|
|
80795
|
+
limit: 200,
|
|
80796
|
+
...cursor ? { cursor } : {}
|
|
80797
|
+
});
|
|
80798
|
+
for (const c of res.channels ?? []) {
|
|
80799
|
+
if (!c.id || c.is_im || c.is_mpim) continue;
|
|
80800
|
+
out.push({
|
|
80801
|
+
id: c.id,
|
|
80802
|
+
...c.name ? { name: c.name } : {},
|
|
80803
|
+
...c.is_private ? { isPrivate: true } : {}
|
|
80804
|
+
});
|
|
80805
|
+
}
|
|
80806
|
+
cursor = res.response_metadata?.next_cursor || void 0;
|
|
80807
|
+
} while (cursor);
|
|
80808
|
+
} catch (err) {
|
|
80809
|
+
this.deps.log?.debug(`slack: users.conversations failed: ${err.message}`);
|
|
80810
|
+
return null;
|
|
80811
|
+
}
|
|
80812
|
+
return out;
|
|
80813
|
+
}
|
|
80753
80814
|
async listChannels() {
|
|
80754
80815
|
return ((await this.app.client.conversations.list({
|
|
80755
80816
|
types: "public_channel,private_channel",
|
|
@@ -82098,6 +82159,16 @@ var CpClient = class {
|
|
|
82098
82159
|
if (this.state !== "READY" && this.state !== "DRAINING") return;
|
|
82099
82160
|
this.transport?.send(encode(buildEnvelope("usage/report", report)));
|
|
82100
82161
|
}
|
|
82162
|
+
/**
|
|
82163
|
+
* Report an integration's channel-membership snapshot (D→C `integration/channels`
|
|
82164
|
+
* EVT, fire-and-forget, latest-wins). No-op unless READY/DRAINING — the daemon
|
|
82165
|
+
* re-emits its cached snapshots on each (re)connect (see onReady in daemon.ts),
|
|
82166
|
+
* so a dropped report only delays the console's channel list, never loses it.
|
|
82167
|
+
*/
|
|
82168
|
+
emitIntegrationChannels(snapshot) {
|
|
82169
|
+
if (this.state !== "READY" && this.state !== "DRAINING") return;
|
|
82170
|
+
this.transport?.send(encode(buildEnvelope("integration/channels", snapshot)));
|
|
82171
|
+
}
|
|
82101
82172
|
request(type, payload) {
|
|
82102
82173
|
const frame = buildEnvelope(type, payload);
|
|
82103
82174
|
return this.correlator.request(frame, (e) => this.transport.send(e));
|
|
@@ -82770,6 +82841,7 @@ var Daemon = class Daemon {
|
|
|
82770
82841
|
lastProbeAtMs = 0;
|
|
82771
82842
|
cpClient;
|
|
82772
82843
|
cpCrons;
|
|
82844
|
+
channelSnapshots = /* @__PURE__ */ new Map();
|
|
82773
82845
|
cpAgents;
|
|
82774
82846
|
cpIntegrations;
|
|
82775
82847
|
botUserIds = {};
|
|
@@ -82894,6 +82966,7 @@ var Daemon = class Daemon {
|
|
|
82894
82966
|
group,
|
|
82895
82967
|
newTraceId: () => randomUUID(),
|
|
82896
82968
|
onMessage: (msg) => this.onInbound(msg),
|
|
82969
|
+
onChannelsChanged: () => void this.refreshChannels(conn),
|
|
82897
82970
|
log: this.log,
|
|
82898
82971
|
boltDebug: cfg.logging.level === "debug" || cfg.logging.level === "trace"
|
|
82899
82972
|
});
|
|
@@ -82905,6 +82978,7 @@ var Daemon = class Daemon {
|
|
|
82905
82978
|
this.connByIntegration.set(integrationId, conn);
|
|
82906
82979
|
}
|
|
82907
82980
|
this.connections.push(conn);
|
|
82981
|
+
this.refreshChannels(conn);
|
|
82908
82982
|
}
|
|
82909
82983
|
let cronCount = 0;
|
|
82910
82984
|
for (const a of agents) for (const c of a.crons) {
|
|
@@ -83051,11 +83125,14 @@ var Daemon = class Daemon {
|
|
|
83051
83125
|
const existing = [...this.connByIntegration.values()].find((c) => c.appToken === group.appToken);
|
|
83052
83126
|
if (existing) {
|
|
83053
83127
|
if (existing.botToken !== group.botToken) this.log.warn(`slack: botToken changed for appToken (socket bot user ${existing.botUserId}) — deferred: the live socket keeps the old botToken until it is closed+reopened`);
|
|
83128
|
+
let bound = false;
|
|
83054
83129
|
for (const { integrationId } of group.integrations) if (this.connByIntegration.get(integrationId) !== existing) {
|
|
83055
83130
|
this.botUserIds[integrationId] = existing.botUserId;
|
|
83056
83131
|
this.connByIntegration.set(integrationId, existing);
|
|
83057
83132
|
this.log.info(`slack: bound integration ${integrationId} onto existing socket (appToken reuse)`);
|
|
83133
|
+
bound = true;
|
|
83058
83134
|
}
|
|
83135
|
+
if (bound) this.refreshChannels(existing);
|
|
83059
83136
|
continue;
|
|
83060
83137
|
}
|
|
83061
83138
|
try {
|
|
@@ -83063,6 +83140,7 @@ var Daemon = class Daemon {
|
|
|
83063
83140
|
group,
|
|
83064
83141
|
newTraceId: () => randomUUID(),
|
|
83065
83142
|
onMessage: (msg) => this.onInbound(msg),
|
|
83143
|
+
onChannelsChanged: () => void this.refreshChannels(conn),
|
|
83066
83144
|
log: this.log,
|
|
83067
83145
|
boltDebug: this.cfg.logging.level === "debug" || this.cfg.logging.level === "trace"
|
|
83068
83146
|
});
|
|
@@ -83074,11 +83152,37 @@ var Daemon = class Daemon {
|
|
|
83074
83152
|
this.connByIntegration.set(integrationId, conn);
|
|
83075
83153
|
}
|
|
83076
83154
|
this.connections.push(conn);
|
|
83155
|
+
this.refreshChannels(conn);
|
|
83077
83156
|
} catch (err) {
|
|
83078
83157
|
this.log.error(`slack: failed to open runtime socket for appToken — leaving existing sockets intact: ${formatErr(err)}`);
|
|
83079
83158
|
}
|
|
83080
83159
|
}
|
|
83081
83160
|
}
|
|
83161
|
+
/**
|
|
83162
|
+
* Re-list the channels this connection's bot is a member of and report the
|
|
83163
|
+
* snapshot to the CP for every integration bound to the connection (one bot ⇒
|
|
83164
|
+
* one membership set, fanned out per integrationId). Best-effort + never throws:
|
|
83165
|
+
* a Slack API failure keeps the previous snapshot (listBotChannels returns null),
|
|
83166
|
+
* and the emit is a no-op while the CP is down — the cached snapshot is re-emitted
|
|
83167
|
+
* on the next CP (re)connect (see startCpClient's onReady).
|
|
83168
|
+
*/
|
|
83169
|
+
async refreshChannels(conn) {
|
|
83170
|
+
try {
|
|
83171
|
+
const channels = await conn.listBotChannels();
|
|
83172
|
+
if (!channels) return;
|
|
83173
|
+
for (const [integrationId, c] of this.connByIntegration) {
|
|
83174
|
+
if (c !== conn) continue;
|
|
83175
|
+
this.channelSnapshots.set(integrationId, channels);
|
|
83176
|
+
this.cpClient?.emitIntegrationChannels({
|
|
83177
|
+
integrationId,
|
|
83178
|
+
channels
|
|
83179
|
+
});
|
|
83180
|
+
}
|
|
83181
|
+
this.log.debug(`slack: channel snapshot for bot ${conn.botUserId}: ${channels.length} channel(s)`);
|
|
83182
|
+
} catch (err) {
|
|
83183
|
+
this.log.debug(`slack: channel snapshot refresh failed: ${formatErr(err)}`);
|
|
83184
|
+
}
|
|
83185
|
+
}
|
|
83082
83186
|
ensureHost(agentId, cfg) {
|
|
83083
83187
|
let host = this.hosts.get(agentId);
|
|
83084
83188
|
if (host) return host;
|
|
@@ -83768,7 +83872,13 @@ var Daemon = class Daemon {
|
|
|
83768
83872
|
features: []
|
|
83769
83873
|
}),
|
|
83770
83874
|
runtimeProfiles: () => Object.keys(this.runtimes).map((id) => this.runtimeProfileFor(id)),
|
|
83771
|
-
onReady: () =>
|
|
83875
|
+
onReady: () => {
|
|
83876
|
+
this.probeRuntimesAndEmit();
|
|
83877
|
+
for (const [integrationId, channels] of this.channelSnapshots) this.cpClient?.emitIntegrationChannels({
|
|
83878
|
+
integrationId,
|
|
83879
|
+
channels
|
|
83880
|
+
});
|
|
83881
|
+
},
|
|
83772
83882
|
localState: () => ({
|
|
83773
83883
|
assignments: [],
|
|
83774
83884
|
crons: this.cpCrons?.ids() ?? [],
|