paperclip-plugin-mattermost 0.11.1 → 0.11.3
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/manifest.js +1 -1
- package/dist/worker.js +57 -15
- package/package.json +1 -1
package/dist/manifest.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
var manifest = {
|
|
3
3
|
id: "mattermost-bridge",
|
|
4
4
|
apiVersion: 1,
|
|
5
|
-
version: "0.11.
|
|
5
|
+
version: "0.11.3",
|
|
6
6
|
displayName: "Mattermost",
|
|
7
7
|
description: "Every agent becomes its own Mattermost bot. DM an agent or mention it in a channel and it answers in the thread, showing its steps as it works; follow-ups continue the conversation. Paperclip approvals arrive with Approve / Reject buttons.",
|
|
8
8
|
author: "Haseeb Azhar",
|
package/dist/worker.js
CHANGED
|
@@ -45281,7 +45281,7 @@ var MattermostClient = class {
|
|
|
45281
45281
|
body: body === void 0 ? void 0 : JSON.stringify(body),
|
|
45282
45282
|
signal: AbortSignal.timeout(3e4)
|
|
45283
45283
|
});
|
|
45284
|
-
if (!r.ok) throw new Error(`Mattermost ${method} ${path3}: ${r.status} ${(await r.text()).slice(0, 200)}`);
|
|
45284
|
+
if (!r.ok) throw Object.assign(new Error(`Mattermost ${method} ${path3}: ${r.status} ${(await r.text()).slice(0, 200)}`), { status: r.status });
|
|
45285
45285
|
return r.status === 204 ? null : await r.json();
|
|
45286
45286
|
}
|
|
45287
45287
|
async init() {
|
|
@@ -45541,18 +45541,19 @@ async function verifyUrl(url2, pluginKey) {
|
|
|
45541
45541
|
}
|
|
45542
45542
|
|
|
45543
45543
|
// src/roster.ts
|
|
45544
|
+
var notFound = (err) => err?.status === 404;
|
|
45544
45545
|
var TOKEN_DESCRIPTION = "paperclip-plugin";
|
|
45545
45546
|
var AgentBots = class {
|
|
45546
45547
|
members = /* @__PURE__ */ new Map();
|
|
45547
45548
|
manager;
|
|
45548
|
-
|
|
45549
|
+
newClient;
|
|
45549
45550
|
listAgents;
|
|
45550
45551
|
store;
|
|
45551
45552
|
log;
|
|
45552
45553
|
onPosted;
|
|
45553
|
-
constructor(manager, url2, listAgents, store, log, onPosted) {
|
|
45554
|
+
constructor(manager, url2, listAgents, store, log, onPosted, newClient) {
|
|
45554
45555
|
this.manager = manager;
|
|
45555
|
-
this.
|
|
45556
|
+
this.newClient = newClient ?? ((token) => new MattermostClient(url2, token, log));
|
|
45556
45557
|
this.listAgents = listAgents;
|
|
45557
45558
|
this.store = store;
|
|
45558
45559
|
this.log = log;
|
|
@@ -45578,12 +45579,32 @@ var AgentBots = class {
|
|
|
45578
45579
|
get size() {
|
|
45579
45580
|
return this.members.size;
|
|
45580
45581
|
}
|
|
45582
|
+
/**
|
|
45583
|
+
* The bot, or null only when Mattermost says it does not exist. Any other failure (the
|
|
45584
|
+
* server restarting, a timeout) throws, so the agent is skipped this round rather than
|
|
45585
|
+
* given a second bot: that is how Carma's Operations Agent became @operations-agent-agent
|
|
45586
|
+
* while @operations-agent, the one people mention, was left with nobody behind it.
|
|
45587
|
+
*/
|
|
45581
45588
|
async bot(id) {
|
|
45582
45589
|
try {
|
|
45583
45590
|
return await this.manager.request("GET", `/bots/${id}?include_deleted=true`);
|
|
45584
|
-
} catch {
|
|
45585
|
-
return null;
|
|
45591
|
+
} catch (err) {
|
|
45592
|
+
if (notFound(err)) return null;
|
|
45593
|
+
throw err;
|
|
45594
|
+
}
|
|
45595
|
+
}
|
|
45596
|
+
/** The bot at an agent's own username, if it is one of ours and no other agent uses it. */
|
|
45597
|
+
async reclaimable(agent, map2) {
|
|
45598
|
+
let user2;
|
|
45599
|
+
try {
|
|
45600
|
+
user2 = await this.manager.request("GET", `/users/username/${botUsername(agent.name)}`);
|
|
45601
|
+
} catch (err) {
|
|
45602
|
+
if (notFound(err)) return null;
|
|
45603
|
+
throw err;
|
|
45586
45604
|
}
|
|
45605
|
+
if (!user2.is_bot || Object.entries(map2).some(([other, id]) => other !== agent.id && id === user2.id)) return null;
|
|
45606
|
+
const bot = await this.bot(user2.id);
|
|
45607
|
+
return bot && bot.owner_id === this.manager.botUserId ? bot : null;
|
|
45587
45608
|
}
|
|
45588
45609
|
async createBot(agent) {
|
|
45589
45610
|
const base = botUsername(agent.name);
|
|
@@ -45614,9 +45635,19 @@ var AgentBots = class {
|
|
|
45614
45635
|
try {
|
|
45615
45636
|
let bot = map2[agent.id] ? await this.bot(map2[agent.id]) : null;
|
|
45616
45637
|
if (!bot) {
|
|
45617
|
-
|
|
45618
|
-
|
|
45619
|
-
|
|
45638
|
+
const mine = await this.reclaimable(agent, map2);
|
|
45639
|
+
bot = mine ?? await this.createBot(agent);
|
|
45640
|
+
this.log(mine ? "reused the agent's existing bot" : "created agent bot", { agent: agent.name, username: bot.username });
|
|
45641
|
+
} else if (bot.username !== botUsername(agent.name)) {
|
|
45642
|
+
const original = await this.reclaimable(agent, map2);
|
|
45643
|
+
if (original && original.user_id !== bot.user_id) {
|
|
45644
|
+
await this.manager.request("POST", `/bots/${bot.user_id}/disable`).catch(() => {
|
|
45645
|
+
});
|
|
45646
|
+
this.log("moved agent back to its own bot", { agent: agent.name, from: bot.username, to: original.username });
|
|
45647
|
+
bot = original;
|
|
45648
|
+
}
|
|
45649
|
+
}
|
|
45650
|
+
{
|
|
45620
45651
|
if (bot.delete_at > 0) await this.manager.request("POST", `/bots/${bot.user_id}/enable`);
|
|
45621
45652
|
if (bot.display_name !== agent.name) {
|
|
45622
45653
|
bot = await this.manager.request("PUT", `/bots/${bot.user_id}`, { username: bot.username, display_name: agent.name, description: agent.title || bot.description });
|
|
@@ -45644,7 +45675,7 @@ var AgentBots = class {
|
|
|
45644
45675
|
continue;
|
|
45645
45676
|
}
|
|
45646
45677
|
current?.client.close();
|
|
45647
|
-
const client =
|
|
45678
|
+
const client = this.newClient(await this.mintToken(bot.user_id));
|
|
45648
45679
|
await client.init();
|
|
45649
45680
|
client.connect(this.onPosted);
|
|
45650
45681
|
this.members.set(agent.id, { agentId: agent.id, agentName: agent.name, botUserId: bot.user_id, username: bot.username, chat: client, client });
|
|
@@ -45683,7 +45714,7 @@ var AgentBots = class {
|
|
|
45683
45714
|
var manifest = {
|
|
45684
45715
|
id: "mattermost-bridge",
|
|
45685
45716
|
apiVersion: 1,
|
|
45686
|
-
version: "0.11.
|
|
45717
|
+
version: "0.11.3",
|
|
45687
45718
|
displayName: "Mattermost",
|
|
45688
45719
|
description: "Every agent becomes its own Mattermost bot. DM an agent or mention it in a channel and it answers in the thread, showing its steps as it works; follow-ups continue the conversation. Paperclip approvals arrive with Approve / Reject buttons.",
|
|
45689
45720
|
author: "Haseeb Azhar",
|
|
@@ -45875,12 +45906,19 @@ var Tenant = class {
|
|
|
45875
45906
|
await manager.request("PUT", `/posts/${postId}/patch`, { message: message2, props: this.tag(props) });
|
|
45876
45907
|
},
|
|
45877
45908
|
async channel(name) {
|
|
45878
|
-
|
|
45909
|
+
const wanted = name.replace(/^[#~]/, "").trim();
|
|
45910
|
+
const teams = await manager.request("GET", "/users/me/teams");
|
|
45911
|
+
for (const team of teams) {
|
|
45879
45912
|
try {
|
|
45880
|
-
return (await manager.request("GET", `/teams/${team.id}/channels/name/${encodeURIComponent(
|
|
45913
|
+
return (await manager.request("GET", `/teams/${team.id}/channels/name/${encodeURIComponent(wanted.toLowerCase())}`)).id;
|
|
45881
45914
|
} catch {
|
|
45882
45915
|
}
|
|
45883
45916
|
}
|
|
45917
|
+
for (const team of teams) {
|
|
45918
|
+
const list = await manager.request("GET", `/teams/${team.id}/channels?per_page=200`).catch(() => []);
|
|
45919
|
+
const hit = list.find((ch) => ch.display_name.trim().toLowerCase() === wanted.toLowerCase());
|
|
45920
|
+
if (hit) return hit.id;
|
|
45921
|
+
}
|
|
45884
45922
|
return null;
|
|
45885
45923
|
},
|
|
45886
45924
|
async email(userId) {
|
|
@@ -45995,11 +46033,12 @@ var Tenant = class {
|
|
|
45995
46033
|
const who = req.user_name ? `@${req.user_name}` : "Someone";
|
|
45996
46034
|
await this.manager.request("PUT", `/posts/${req.post_id}/patch`, { props: chosenProps(choice.choice, who) }).catch(() => {
|
|
45997
46035
|
});
|
|
45998
|
-
|
|
46036
|
+
const bridge = this.bridge;
|
|
46037
|
+
detached(() => void bridge.handle({
|
|
45999
46038
|
post: { id: `choice:${req.post_id}`, user_id: req.user_id, channel_id: choice.channelId, root_id: choice.rootId, message: choice.choice },
|
|
46000
46039
|
channelType: choice.channelType,
|
|
46001
46040
|
channelName: choice.channelName
|
|
46002
|
-
});
|
|
46041
|
+
}));
|
|
46003
46042
|
return;
|
|
46004
46043
|
}
|
|
46005
46044
|
if (this.approvals) await this.notify(req, await this.approvals.click(req).catch(failed));
|
|
@@ -46044,6 +46083,9 @@ async function saveBoot(companyId, config2) {
|
|
|
46044
46083
|
await ctx.state.set(BOOT, all);
|
|
46045
46084
|
}
|
|
46046
46085
|
var plugin = definePlugin({
|
|
46086
|
+
// One worker serves every company, each with its own Mattermost (Tenant above).
|
|
46087
|
+
// Without this Paperclip refuses a second company's settings as a cross-tenant risk.
|
|
46088
|
+
multiCompanyConfig: true,
|
|
46047
46089
|
async setup(c) {
|
|
46048
46090
|
ctx = c;
|
|
46049
46091
|
ctx.data.register("setup", async (params) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paperclip-plugin-mattermost",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.3",
|
|
4
4
|
"description": "Paperclip agents in Mattermost: a bot per agent, live progress, files, approval and decision buttons, and per-person access, for each Paperclip company.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|