@openacp/cli 0.6.10 → 2026.41.2
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/README.md +34 -16
- package/dist/cli.d.ts +11 -0
- package/dist/cli.js +28362 -449
- package/dist/cli.js.map +1 -1
- package/dist/data/registry-snapshot.json +1 -1
- package/dist/index.d.ts +1930 -467
- package/dist/index.js +17331 -102
- package/dist/index.js.map +1 -1
- package/package.json +13 -7
- package/dist/action-detect-P7ZE4NEM.js +0 -16
- package/dist/action-detect-P7ZE4NEM.js.map +0 -1
- package/dist/adapter-ZOANORGM.js +0 -799
- package/dist/adapter-ZOANORGM.js.map +0 -1
- package/dist/admin-6SYB6XCZ.js +0 -23
- package/dist/admin-6SYB6XCZ.js.map +0 -1
- package/dist/agent-catalog-FC3HGDEQ.js +0 -11
- package/dist/agent-catalog-FC3HGDEQ.js.map +0 -1
- package/dist/agent-dependencies-4OWBMZWZ.js +0 -24
- package/dist/agent-dependencies-4OWBMZWZ.js.map +0 -1
- package/dist/agent-registry-WT4NXPYG.js +0 -9
- package/dist/agent-registry-WT4NXPYG.js.map +0 -1
- package/dist/agent-store-VZLFPTZU.js +0 -9
- package/dist/agent-store-VZLFPTZU.js.map +0 -1
- package/dist/agents-QO7DKARJ.js +0 -15
- package/dist/agents-QO7DKARJ.js.map +0 -1
- package/dist/api-client-CFQT5U7D.js +0 -14
- package/dist/api-client-CFQT5U7D.js.map +0 -1
- package/dist/autostart-X33OGMX6.js +0 -23
- package/dist/autostart-X33OGMX6.js.map +0 -1
- package/dist/chunk-2CJ46J3C.js +0 -154
- package/dist/chunk-2CJ46J3C.js.map +0 -1
- package/dist/chunk-2HMQOC7N.js +0 -134
- package/dist/chunk-2HMQOC7N.js.map +0 -1
- package/dist/chunk-33RP6K2O.js +0 -435
- package/dist/chunk-33RP6K2O.js.map +0 -1
- package/dist/chunk-34M4OS5P.js +0 -83
- package/dist/chunk-34M4OS5P.js.map +0 -1
- package/dist/chunk-4CTX774K.js +0 -265
- package/dist/chunk-4CTX774K.js.map +0 -1
- package/dist/chunk-7QJS2XBD.js +0 -92
- package/dist/chunk-7QJS2XBD.js.map +0 -1
- package/dist/chunk-BNLGTZ34.js +0 -122
- package/dist/chunk-BNLGTZ34.js.map +0 -1
- package/dist/chunk-CS3KCJ5D.js +0 -4788
- package/dist/chunk-CS3KCJ5D.js.map +0 -1
- package/dist/chunk-GAK6PIBW.js +0 -224
- package/dist/chunk-GAK6PIBW.js.map +0 -1
- package/dist/chunk-I7WC6E5S.js +0 -71
- package/dist/chunk-I7WC6E5S.js.map +0 -1
- package/dist/chunk-J4SJTKIK.js +0 -203
- package/dist/chunk-J4SJTKIK.js.map +0 -1
- package/dist/chunk-JHYXKVV2.js +0 -183
- package/dist/chunk-JHYXKVV2.js.map +0 -1
- package/dist/chunk-JKBFUAJK.js +0 -282
- package/dist/chunk-JKBFUAJK.js.map +0 -1
- package/dist/chunk-KIRH7TUJ.js +0 -219
- package/dist/chunk-KIRH7TUJ.js.map +0 -1
- package/dist/chunk-LBIKITQT.js +0 -22
- package/dist/chunk-LBIKITQT.js.map +0 -1
- package/dist/chunk-LCRLAV4G.js +0 -1085
- package/dist/chunk-LCRLAV4G.js.map +0 -1
- package/dist/chunk-LGP2YGRL.js +0 -4880
- package/dist/chunk-LGP2YGRL.js.map +0 -1
- package/dist/chunk-MKHUZLII.js +0 -738
- package/dist/chunk-MKHUZLII.js.map +0 -1
- package/dist/chunk-NAMYZIS5.js +0 -1
- package/dist/chunk-NAMYZIS5.js.map +0 -1
- package/dist/chunk-NVPG6JCL.js +0 -724
- package/dist/chunk-NVPG6JCL.js.map +0 -1
- package/dist/chunk-O7CPGUAI.js +0 -298
- package/dist/chunk-O7CPGUAI.js.map +0 -1
- package/dist/chunk-OWP7RZ62.js +0 -697
- package/dist/chunk-OWP7RZ62.js.map +0 -1
- package/dist/chunk-S64CB6J3.js +0 -98
- package/dist/chunk-S64CB6J3.js.map +0 -1
- package/dist/chunk-UKT3G5IA.js +0 -484
- package/dist/chunk-UKT3G5IA.js.map +0 -1
- package/dist/chunk-V5GZQEIY.js +0 -101
- package/dist/chunk-V5GZQEIY.js.map +0 -1
- package/dist/chunk-VOIJ6OY4.js +0 -63
- package/dist/chunk-VOIJ6OY4.js.map +0 -1
- package/dist/chunk-VUNV25KB.js +0 -16
- package/dist/chunk-VUNV25KB.js.map +0 -1
- package/dist/chunk-W3EYKZNQ.js +0 -45
- package/dist/chunk-W3EYKZNQ.js.map +0 -1
- package/dist/chunk-WTZDAYZX.js +0 -172
- package/dist/chunk-WTZDAYZX.js.map +0 -1
- package/dist/chunk-XANPHG7W.js +0 -145
- package/dist/chunk-XANPHG7W.js.map +0 -1
- package/dist/config-6S355X75.js +0 -15
- package/dist/config-6S355X75.js.map +0 -1
- package/dist/config-editor-QQTZMWGD.js +0 -13
- package/dist/config-editor-QQTZMWGD.js.map +0 -1
- package/dist/config-registry-AHYI4MYL.js +0 -18
- package/dist/config-registry-AHYI4MYL.js.map +0 -1
- package/dist/daemon-4CS6HMB5.js +0 -30
- package/dist/daemon-4CS6HMB5.js.map +0 -1
- package/dist/discord-OMC52Y54.js +0 -2239
- package/dist/discord-OMC52Y54.js.map +0 -1
- package/dist/dist-UHQK5CXN.js +0 -21151
- package/dist/dist-UHQK5CXN.js.map +0 -1
- package/dist/doctor-HZZ5BSHB.js +0 -10
- package/dist/doctor-HZZ5BSHB.js.map +0 -1
- package/dist/doctor-OLYBO3V3.js +0 -15
- package/dist/doctor-OLYBO3V3.js.map +0 -1
- package/dist/install-cloudflared-Z7VCGOVG.js +0 -33
- package/dist/install-cloudflared-Z7VCGOVG.js.map +0 -1
- package/dist/install-jq-HUYSQWKR.js +0 -32
- package/dist/install-jq-HUYSQWKR.js.map +0 -1
- package/dist/integrate-PNEHRY2I.js +0 -373
- package/dist/integrate-PNEHRY2I.js.map +0 -1
- package/dist/log-NXABYJTT.js +0 -24
- package/dist/log-NXABYJTT.js.map +0 -1
- package/dist/main-XOZCLFUK.js +0 -238
- package/dist/main-XOZCLFUK.js.map +0 -1
- package/dist/menu-YY5MKHEK.js +0 -16
- package/dist/menu-YY5MKHEK.js.map +0 -1
- package/dist/new-session-FEO4J4VU.js +0 -17
- package/dist/new-session-FEO4J4VU.js.map +0 -1
- package/dist/post-upgrade-CJG5I7M2.js +0 -80
- package/dist/post-upgrade-CJG5I7M2.js.map +0 -1
- package/dist/session-IUSI7P5S.js +0 -20
- package/dist/session-IUSI7P5S.js.map +0 -1
- package/dist/settings-RQPAM4KC.js +0 -14
- package/dist/settings-RQPAM4KC.js.map +0 -1
- package/dist/setup-XHS4OMPM.js +0 -37
- package/dist/setup-XHS4OMPM.js.map +0 -1
- package/dist/suggest-7D6B542M.js +0 -38
- package/dist/suggest-7D6B542M.js.map +0 -1
- package/dist/tunnel-service-CJLUH6SZ.js +0 -1174
- package/dist/tunnel-service-CJLUH6SZ.js.map +0 -1
- package/dist/version-NQZBM5M7.js +0 -16
- package/dist/version-NQZBM5M7.js.map +0 -1
package/dist/chunk-4CTX774K.js
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
deleteSessionThread
|
|
3
|
-
} from "./chunk-BNLGTZ34.js";
|
|
4
|
-
import {
|
|
5
|
-
log
|
|
6
|
-
} from "./chunk-GAK6PIBW.js";
|
|
7
|
-
|
|
8
|
-
// src/adapters/discord/commands/session.ts
|
|
9
|
-
import {
|
|
10
|
-
ActionRowBuilder,
|
|
11
|
-
ButtonBuilder,
|
|
12
|
-
ButtonStyle
|
|
13
|
-
} from "discord.js";
|
|
14
|
-
var STATUS_EMOJI = {
|
|
15
|
-
active: "\u{1F7E2}",
|
|
16
|
-
initializing: "\u{1F7E1}",
|
|
17
|
-
finished: "\u2705",
|
|
18
|
-
error: "\u274C",
|
|
19
|
-
cancelled: "\u26D4"
|
|
20
|
-
};
|
|
21
|
-
var STATUS_ORDER = {
|
|
22
|
-
active: 0,
|
|
23
|
-
initializing: 1,
|
|
24
|
-
error: 2,
|
|
25
|
-
finished: 3,
|
|
26
|
-
cancelled: 4
|
|
27
|
-
};
|
|
28
|
-
async function handleCancel(interaction, adapter) {
|
|
29
|
-
await interaction.deferReply({ ephemeral: true });
|
|
30
|
-
const channelId = interaction.channelId;
|
|
31
|
-
const session = adapter.core.sessionManager.getSessionByThread("discord", channelId);
|
|
32
|
-
if (session) {
|
|
33
|
-
log.info({ sessionId: session.id }, "[discord-session] Cancel command");
|
|
34
|
-
await session.abortPrompt();
|
|
35
|
-
await interaction.editReply("\u26D4 Session cancelled.");
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
const record = adapter.core.sessionManager.getRecordByThread("discord", channelId);
|
|
39
|
-
if (record && record.status !== "cancelled" && record.status !== "error") {
|
|
40
|
-
log.info({ sessionId: record.sessionId }, "[discord-session] Cancel command (from store)");
|
|
41
|
-
await adapter.core.sessionManager.cancelSession(record.sessionId);
|
|
42
|
-
await interaction.editReply("\u26D4 Session cancelled.");
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
await interaction.editReply("No active session in this channel.");
|
|
46
|
-
}
|
|
47
|
-
async function handleStatus(interaction, adapter) {
|
|
48
|
-
await interaction.deferReply({ ephemeral: true });
|
|
49
|
-
const channelId = interaction.channelId;
|
|
50
|
-
const session = adapter.core.sessionManager.getSessionByThread("discord", channelId);
|
|
51
|
-
if (session) {
|
|
52
|
-
await interaction.editReply(
|
|
53
|
-
`**Session:** ${session.name || session.id}
|
|
54
|
-
**Agent:** ${session.agentName}
|
|
55
|
-
**Status:** ${session.status}
|
|
56
|
-
**Workspace:** \`${session.workingDirectory}\`
|
|
57
|
-
**Queue:** ${session.queueDepth} pending`
|
|
58
|
-
);
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
const record = adapter.core.sessionManager.getRecordByThread("discord", channelId);
|
|
62
|
-
if (record) {
|
|
63
|
-
await interaction.editReply(
|
|
64
|
-
`**Session:** ${record.name || record.sessionId}
|
|
65
|
-
**Agent:** ${record.agentName}
|
|
66
|
-
**Status:** ${record.status} (not loaded)
|
|
67
|
-
**Workspace:** \`${record.workingDir}\``
|
|
68
|
-
);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
const sessions = adapter.core.sessionManager.listSessions("discord");
|
|
72
|
-
const active = sessions.filter(
|
|
73
|
-
(s) => s.status === "active" || s.status === "initializing"
|
|
74
|
-
);
|
|
75
|
-
await interaction.editReply(
|
|
76
|
-
`**OpenACP Status**
|
|
77
|
-
Active sessions: ${active.length}
|
|
78
|
-
Total sessions: ${sessions.length}`
|
|
79
|
-
);
|
|
80
|
-
}
|
|
81
|
-
async function handleSessions(interaction, adapter) {
|
|
82
|
-
await interaction.deferReply({ ephemeral: true });
|
|
83
|
-
try {
|
|
84
|
-
const allRecords = adapter.core.sessionManager.listRecords();
|
|
85
|
-
const records = allRecords.filter((r) => {
|
|
86
|
-
const platform = r.platform;
|
|
87
|
-
return !!platform?.topicId;
|
|
88
|
-
});
|
|
89
|
-
const headlessCount = allRecords.length - records.length;
|
|
90
|
-
if (records.length === 0) {
|
|
91
|
-
const extra = headlessCount > 0 ? ` (${headlessCount} headless hidden)` : "";
|
|
92
|
-
await interaction.editReply(`No sessions found.${extra}`);
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
records.sort(
|
|
96
|
-
(a, b) => (STATUS_ORDER[a.status] ?? 5) - (STATUS_ORDER[b.status] ?? 5)
|
|
97
|
-
);
|
|
98
|
-
const MAX_DISPLAY = 25;
|
|
99
|
-
const displayed = records.slice(0, MAX_DISPLAY);
|
|
100
|
-
const lines = displayed.map((r) => {
|
|
101
|
-
const emoji = STATUS_EMOJI[r.status] || "\u26AA";
|
|
102
|
-
const name = r.name?.trim() || `${r.agentName} session`;
|
|
103
|
-
return `${emoji} **${name}** \`[${r.status}]\``;
|
|
104
|
-
});
|
|
105
|
-
const header = `**Sessions: ${records.length}**` + (headlessCount > 0 ? ` (${headlessCount} headless hidden)` : "");
|
|
106
|
-
const truncated = records.length > MAX_DISPLAY ? `
|
|
107
|
-
|
|
108
|
-
*...and ${records.length - MAX_DISPLAY} more*` : "";
|
|
109
|
-
const finishedCount = allRecords.filter((r) => r.status === "finished").length;
|
|
110
|
-
const errorCount = allRecords.filter(
|
|
111
|
-
(r) => r.status === "error" || r.status === "cancelled"
|
|
112
|
-
).length;
|
|
113
|
-
const rows = [];
|
|
114
|
-
if (finishedCount + errorCount > 0) {
|
|
115
|
-
const cleanupRow = new ActionRowBuilder();
|
|
116
|
-
if (finishedCount > 0) {
|
|
117
|
-
cleanupRow.addComponents(
|
|
118
|
-
new ButtonBuilder().setCustomId("m:cleanup:finished").setLabel(`Cleanup finished (${finishedCount})`).setStyle(ButtonStyle.Secondary)
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
if (errorCount > 0) {
|
|
122
|
-
cleanupRow.addComponents(
|
|
123
|
-
new ButtonBuilder().setCustomId("m:cleanup:errors").setLabel(`Cleanup errors (${errorCount})`).setStyle(ButtonStyle.Secondary)
|
|
124
|
-
);
|
|
125
|
-
}
|
|
126
|
-
rows.push(cleanupRow);
|
|
127
|
-
const cleanupAllRow = new ActionRowBuilder();
|
|
128
|
-
cleanupAllRow.addComponents(
|
|
129
|
-
new ButtonBuilder().setCustomId("m:cleanup:all").setLabel(`Cleanup all non-active (${finishedCount + errorCount})`).setStyle(ButtonStyle.Secondary)
|
|
130
|
-
);
|
|
131
|
-
rows.push(cleanupAllRow);
|
|
132
|
-
}
|
|
133
|
-
await interaction.editReply({
|
|
134
|
-
content: `${header}
|
|
135
|
-
|
|
136
|
-
${lines.join("\n")}${truncated}`,
|
|
137
|
-
components: rows
|
|
138
|
-
});
|
|
139
|
-
} catch (err) {
|
|
140
|
-
log.error({ err }, "[discord-session] handleSessions error");
|
|
141
|
-
await interaction.editReply("\u274C Failed to list sessions.").catch(() => {
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
async function handleHandoff(interaction, adapter) {
|
|
146
|
-
await interaction.deferReply({ ephemeral: true });
|
|
147
|
-
const channelId = interaction.channelId;
|
|
148
|
-
const session = adapter.core.sessionManager.getSessionByThread("discord", channelId);
|
|
149
|
-
if (!session) {
|
|
150
|
-
const record = adapter.core.sessionManager.getRecordByThread("discord", channelId);
|
|
151
|
-
if (!record) {
|
|
152
|
-
await interaction.editReply("No session found in this channel.");
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const cmd2 = `openacp agents run ${record.agentName} --resume ${record.agentSessionId} -- --continue`;
|
|
156
|
-
await interaction.editReply(
|
|
157
|
-
`**Resume in terminal:**
|
|
158
|
-
\`\`\`
|
|
159
|
-
${cmd2}
|
|
160
|
-
\`\`\`
|
|
161
|
-
|
|
162
|
-
*Run this from your project directory:* \`${record.workingDir}\``
|
|
163
|
-
);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
const cmd = `openacp agents run ${session.agentName} --resume ${session.agentSessionId} -- --continue`;
|
|
167
|
-
await interaction.editReply(
|
|
168
|
-
`**Resume in terminal:**
|
|
169
|
-
\`\`\`
|
|
170
|
-
${cmd}
|
|
171
|
-
\`\`\`
|
|
172
|
-
|
|
173
|
-
*Run this from your project directory:* \`${session.workingDirectory}\``
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
async function executeCancelSession(interaction, adapter) {
|
|
177
|
-
const sessions = adapter.core.sessionManager.listSessions("discord").filter((s) => s.status === "active").sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
178
|
-
const session = sessions[0];
|
|
179
|
-
if (!session) {
|
|
180
|
-
await interaction.reply({ content: "No active sessions to cancel.", ephemeral: true });
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
await session.abortPrompt();
|
|
184
|
-
await interaction.reply({ content: `\u26D4 Cancelled session: **${session.name || session.id}**`, ephemeral: true });
|
|
185
|
-
}
|
|
186
|
-
async function handleCleanupButton(interaction, adapter) {
|
|
187
|
-
const { customId } = interaction;
|
|
188
|
-
switch (customId) {
|
|
189
|
-
case "m:cleanup:all":
|
|
190
|
-
await interaction.deferReply({ ephemeral: true });
|
|
191
|
-
await runCleanup(interaction, adapter, ["finished", "error", "cancelled"]);
|
|
192
|
-
break;
|
|
193
|
-
case "m:cleanup:finished":
|
|
194
|
-
await interaction.deferReply({ ephemeral: true });
|
|
195
|
-
await runCleanup(interaction, adapter, ["finished"]);
|
|
196
|
-
break;
|
|
197
|
-
case "m:cleanup:errors":
|
|
198
|
-
await interaction.deferReply({ ephemeral: true });
|
|
199
|
-
await runCleanup(interaction, adapter, ["error", "cancelled"]);
|
|
200
|
-
break;
|
|
201
|
-
case "m:cleanup:confirm":
|
|
202
|
-
await interaction.deferReply({ ephemeral: true });
|
|
203
|
-
await runCleanup(interaction, adapter, ["finished", "error", "cancelled", "active", "initializing"]);
|
|
204
|
-
break;
|
|
205
|
-
case "m:cleanup:cancel":
|
|
206
|
-
try {
|
|
207
|
-
await interaction.update({ components: [] });
|
|
208
|
-
} catch {
|
|
209
|
-
}
|
|
210
|
-
break;
|
|
211
|
-
default:
|
|
212
|
-
try {
|
|
213
|
-
await interaction.reply({ content: "Unknown cleanup action.", ephemeral: true });
|
|
214
|
-
} catch {
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
async function runCleanup(interaction, adapter, statuses) {
|
|
219
|
-
const allRecords = adapter.core.sessionManager.listRecords();
|
|
220
|
-
const cleanable = allRecords.filter((r) => statuses.includes(r.status));
|
|
221
|
-
if (cleanable.length === 0) {
|
|
222
|
-
await interaction.editReply("Nothing to clean up.");
|
|
223
|
-
return;
|
|
224
|
-
}
|
|
225
|
-
let deleted = 0;
|
|
226
|
-
let failed = 0;
|
|
227
|
-
for (const record of cleanable) {
|
|
228
|
-
try {
|
|
229
|
-
if (record.status === "active" || record.status === "initializing") {
|
|
230
|
-
try {
|
|
231
|
-
await adapter.core.sessionManager.cancelSession(record.sessionId);
|
|
232
|
-
} catch (err) {
|
|
233
|
-
log.warn({ err, sessionId: record.sessionId }, "[discord-session] Failed to cancel session during cleanup");
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
const platform = record.platform;
|
|
237
|
-
const threadId = platform?.threadId ?? (platform?.topicId != null ? String(platform.topicId) : void 0);
|
|
238
|
-
if (threadId) {
|
|
239
|
-
try {
|
|
240
|
-
await deleteSessionThread(adapter.getGuild(), threadId);
|
|
241
|
-
} catch (err) {
|
|
242
|
-
log.warn({ err, sessionId: record.sessionId, threadId }, "[discord-session] Failed to delete thread during cleanup");
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
await adapter.core.sessionManager.removeRecord(record.sessionId);
|
|
246
|
-
deleted++;
|
|
247
|
-
} catch (err) {
|
|
248
|
-
log.error({ err, sessionId: record.sessionId }, "[discord-session] Failed to cleanup session");
|
|
249
|
-
failed++;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
await interaction.editReply(
|
|
253
|
-
`\u{1F5D1} Cleaned up **${deleted}** sessions${failed > 0 ? ` (${failed} failed)` : ""}.`
|
|
254
|
-
);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
export {
|
|
258
|
-
handleCancel,
|
|
259
|
-
handleStatus,
|
|
260
|
-
handleSessions,
|
|
261
|
-
handleHandoff,
|
|
262
|
-
executeCancelSession,
|
|
263
|
-
handleCleanupButton
|
|
264
|
-
};
|
|
265
|
-
//# sourceMappingURL=chunk-4CTX774K.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/discord/commands/session.ts"],"sourcesContent":["import {\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n} from 'discord.js'\nimport type { ChatInputCommandInteraction, ButtonInteraction } from 'discord.js'\nimport type { Session } from '../../../core/session.js'\nimport { log } from '../../../core/log.js'\nimport { deleteSessionThread } from '../forums.js'\nimport type { DiscordAdapter } from '../adapter.js'\n\nconst STATUS_EMOJI: Record<string, string> = {\n active: '🟢',\n initializing: '🟡',\n finished: '✅',\n error: '❌',\n cancelled: '⛔',\n}\n\nconst STATUS_ORDER: Record<string, number> = {\n active: 0,\n initializing: 1,\n error: 2,\n finished: 3,\n cancelled: 4,\n}\n\nexport async function handleCancel(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n const channelId = interaction.channelId\n const session = adapter.core.sessionManager.getSessionByThread('discord', channelId)\n\n if (session) {\n log.info({ sessionId: session.id }, '[discord-session] Cancel command')\n await session.abortPrompt()\n await interaction.editReply('⛔ Session cancelled.')\n return\n }\n\n // Fallback: cancel from store when session not in memory\n const record = adapter.core.sessionManager.getRecordByThread('discord', channelId)\n if (record && record.status !== 'cancelled' && record.status !== 'error') {\n log.info({ sessionId: record.sessionId }, '[discord-session] Cancel command (from store)')\n await adapter.core.sessionManager.cancelSession(record.sessionId)\n await interaction.editReply('⛔ Session cancelled.')\n return\n }\n\n await interaction.editReply('No active session in this channel.')\n}\n\nexport async function handleStatus(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n const channelId = interaction.channelId\n const session = adapter.core.sessionManager.getSessionByThread('discord', channelId)\n\n if (session) {\n await interaction.editReply(\n `**Session:** ${session.name || session.id}\\n` +\n `**Agent:** ${session.agentName}\\n` +\n `**Status:** ${session.status}\\n` +\n `**Workspace:** \\`${session.workingDirectory}\\`\\n` +\n `**Queue:** ${session.queueDepth} pending`,\n )\n return\n }\n\n // Try stored record\n const record = adapter.core.sessionManager.getRecordByThread('discord', channelId)\n if (record) {\n await interaction.editReply(\n `**Session:** ${record.name || record.sessionId}\\n` +\n `**Agent:** ${record.agentName}\\n` +\n `**Status:** ${record.status} (not loaded)\\n` +\n `**Workspace:** \\`${record.workingDir}\\``,\n )\n return\n }\n\n // Global status\n const sessions = adapter.core.sessionManager.listSessions('discord')\n const active = sessions.filter(\n (s: Session) => s.status === 'active' || s.status === 'initializing',\n )\n await interaction.editReply(\n `**OpenACP Status**\\n` +\n `Active sessions: ${active.length}\\n` +\n `Total sessions: ${sessions.length}`,\n )\n}\n\nexport async function handleSessions(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n try {\n const allRecords = adapter.core.sessionManager.listRecords()\n\n // Only show sessions that have a Discord thread\n const records = allRecords.filter((r: any) => {\n const platform = r.platform as { topicId?: string | number }\n return !!platform?.topicId\n })\n const headlessCount = allRecords.length - records.length\n\n if (records.length === 0) {\n const extra = headlessCount > 0 ? ` (${headlessCount} headless hidden)` : ''\n await interaction.editReply(`No sessions found.${extra}`)\n return\n }\n\n records.sort(\n (a: any, b: any) => (STATUS_ORDER[a.status] ?? 5) - (STATUS_ORDER[b.status] ?? 5),\n )\n\n const MAX_DISPLAY = 25\n const displayed = records.slice(0, MAX_DISPLAY)\n\n const lines = displayed.map((r: any) => {\n const emoji = STATUS_EMOJI[r.status] || '⚪'\n const name = r.name?.trim() || `${r.agentName} session`\n return `${emoji} **${name}** \\`[${r.status}]\\``\n })\n\n const header =\n `**Sessions: ${records.length}**` +\n (headlessCount > 0 ? ` (${headlessCount} headless hidden)` : '')\n const truncated =\n records.length > MAX_DISPLAY\n ? `\\n\\n*...and ${records.length - MAX_DISPLAY} more*`\n : ''\n\n // Cleanup buttons\n const finishedCount = allRecords.filter((r: any) => r.status === 'finished').length\n const errorCount = allRecords.filter(\n (r: any) => r.status === 'error' || r.status === 'cancelled',\n ).length\n\n const rows: ActionRowBuilder<ButtonBuilder>[] = []\n\n if (finishedCount + errorCount > 0) {\n const cleanupRow = new ActionRowBuilder<ButtonBuilder>()\n if (finishedCount > 0) {\n cleanupRow.addComponents(\n new ButtonBuilder()\n .setCustomId('m:cleanup:finished')\n .setLabel(`Cleanup finished (${finishedCount})`)\n .setStyle(ButtonStyle.Secondary),\n )\n }\n if (errorCount > 0) {\n cleanupRow.addComponents(\n new ButtonBuilder()\n .setCustomId('m:cleanup:errors')\n .setLabel(`Cleanup errors (${errorCount})`)\n .setStyle(ButtonStyle.Secondary),\n )\n }\n rows.push(cleanupRow)\n\n const cleanupAllRow = new ActionRowBuilder<ButtonBuilder>()\n cleanupAllRow.addComponents(\n new ButtonBuilder()\n .setCustomId('m:cleanup:all')\n .setLabel(`Cleanup all non-active (${finishedCount + errorCount})`)\n .setStyle(ButtonStyle.Secondary),\n )\n rows.push(cleanupAllRow)\n }\n\n await interaction.editReply({\n content: `${header}\\n\\n${lines.join('\\n')}${truncated}`,\n components: rows,\n })\n } catch (err) {\n log.error({ err }, '[discord-session] handleSessions error')\n await interaction.editReply('❌ Failed to list sessions.').catch(() => {})\n }\n}\n\nexport async function handleHandoff(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n const channelId = interaction.channelId\n const session = adapter.core.sessionManager.getSessionByThread('discord', channelId)\n\n if (!session) {\n const record = adapter.core.sessionManager.getRecordByThread('discord', channelId)\n if (!record) {\n await interaction.editReply('No session found in this channel.')\n return\n }\n const cmd = `openacp agents run ${record.agentName} --resume ${record.agentSessionId} -- --continue`\n await interaction.editReply(\n `**Resume in terminal:**\\n\\`\\`\\`\\n${cmd}\\n\\`\\`\\`\\n\\n*Run this from your project directory:* \\`${record.workingDir}\\``,\n )\n return\n }\n\n const cmd = `openacp agents run ${session.agentName} --resume ${session.agentSessionId} -- --continue`\n await interaction.editReply(\n `**Resume in terminal:**\\n\\`\\`\\`\\n${cmd}\\n\\`\\`\\`\\n\\n*Run this from your project directory:* \\`${session.workingDirectory}\\``,\n )\n}\n\nexport async function executeCancelSession(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const sessions: Session[] = adapter.core.sessionManager\n .listSessions('discord')\n .filter((s: Session) => s.status === 'active')\n .sort((a: Session, b: Session) => b.createdAt.getTime() - a.createdAt.getTime())\n\n const session = sessions[0]\n if (!session) {\n await interaction.reply({ content: 'No active sessions to cancel.', ephemeral: true })\n return\n }\n\n await session.abortPrompt()\n await interaction.reply({ content: `⛔ Cancelled session: **${session.name || session.id}**`, ephemeral: true })\n}\n\nexport async function handleCleanupButton(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const { customId } = interaction\n\n switch (customId) {\n case 'm:cleanup:all':\n await interaction.deferReply({ ephemeral: true })\n await runCleanup(interaction, adapter, ['finished', 'error', 'cancelled'])\n break\n\n case 'm:cleanup:finished':\n await interaction.deferReply({ ephemeral: true })\n await runCleanup(interaction, adapter, ['finished'])\n break\n\n case 'm:cleanup:errors':\n await interaction.deferReply({ ephemeral: true })\n await runCleanup(interaction, adapter, ['error', 'cancelled'])\n break\n\n case 'm:cleanup:confirm':\n await interaction.deferReply({ ephemeral: true })\n await runCleanup(interaction, adapter, ['finished', 'error', 'cancelled', 'active', 'initializing'])\n break\n\n case 'm:cleanup:cancel':\n try { await interaction.update({ components: [] }) } catch { /* ignore */ }\n break\n\n default:\n // Unknown cleanup variant — ignore\n try { await interaction.reply({ content: 'Unknown cleanup action.', ephemeral: true }) } catch { /* ignore */ }\n }\n}\n\nasync function runCleanup(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n statuses: string[],\n): Promise<void> {\n const allRecords = adapter.core.sessionManager.listRecords()\n const cleanable = allRecords.filter((r: any) => statuses.includes(r.status))\n\n if (cleanable.length === 0) {\n await interaction.editReply('Nothing to clean up.')\n return\n }\n\n let deleted = 0\n let failed = 0\n\n for (const record of cleanable) {\n try {\n // Cancel active sessions first\n if (record.status === 'active' || record.status === 'initializing') {\n try {\n await adapter.core.sessionManager.cancelSession(record.sessionId)\n } catch (err) {\n log.warn({ err, sessionId: record.sessionId }, '[discord-session] Failed to cancel session during cleanup')\n }\n }\n\n const platform = record.platform as { topicId?: string | number; threadId?: string } | undefined\n const threadId = platform?.threadId ?? (platform?.topicId != null ? String(platform.topicId) : undefined)\n if (threadId) {\n try {\n await deleteSessionThread(adapter.getGuild(), threadId)\n } catch (err) {\n log.warn({ err, sessionId: record.sessionId, threadId }, '[discord-session] Failed to delete thread during cleanup')\n }\n }\n await adapter.core.sessionManager.removeRecord(record.sessionId)\n deleted++\n } catch (err) {\n log.error({ err, sessionId: record.sessionId }, '[discord-session] Failed to cleanup session')\n failed++\n }\n }\n\n await interaction.editReply(\n `🗑 Cleaned up **${deleted}** sessions${failed > 0 ? ` (${failed} failed)` : ''}.`,\n )\n}\n"],"mappings":"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","names":["cmd"]}
|
package/dist/chunk-7QJS2XBD.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
// src/adapters/telegram/commands/menu.ts
|
|
2
|
-
import { InlineKeyboard } from "grammy";
|
|
3
|
-
function buildMenuKeyboard() {
|
|
4
|
-
return new InlineKeyboard().text("\u{1F195} New Session", "m:new").text("\u{1F4CB} Sessions", "m:topics").row().text("\u{1F4CA} Status", "m:status").text("\u{1F916} Agents", "m:agents").row().text("\u2699\uFE0F Settings", "m:settings").text("\u{1F517} Integrate", "m:integrate").row().text("\u{1F504} Restart", "m:restart").text("\u2B06\uFE0F Update", "m:update").row().text("\u2753 Help", "m:help").text("\u{1FA7A} Doctor", "m:doctor");
|
|
5
|
-
}
|
|
6
|
-
async function handleMenu(ctx) {
|
|
7
|
-
await ctx.reply(`<b>OpenACP Menu</b>
|
|
8
|
-
Choose an action:`, {
|
|
9
|
-
parse_mode: "HTML",
|
|
10
|
-
reply_markup: buildMenuKeyboard()
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
async function handleHelp(ctx) {
|
|
14
|
-
await ctx.reply(
|
|
15
|
-
`\u{1F4D6} <b>OpenACP Help</b>
|
|
16
|
-
|
|
17
|
-
\u{1F680} <b>Getting Started</b>
|
|
18
|
-
Tap \u{1F195} New Session to start coding with AI.
|
|
19
|
-
Each session gets its own topic \u2014 chat there to work with the agent.
|
|
20
|
-
|
|
21
|
-
\u{1F4A1} <b>Common Tasks</b>
|
|
22
|
-
/new [agent] [workspace] \u2014 Create new session
|
|
23
|
-
/cancel \u2014 Cancel session (in session topic)
|
|
24
|
-
/status \u2014 Show session or system status
|
|
25
|
-
/sessions \u2014 List all sessions
|
|
26
|
-
/agents \u2014 Browse & install agents
|
|
27
|
-
/install <name> \u2014 Install an agent
|
|
28
|
-
|
|
29
|
-
\u2699\uFE0F <b>System</b>
|
|
30
|
-
/restart \u2014 Restart OpenACP
|
|
31
|
-
/update \u2014 Update to latest version
|
|
32
|
-
/integrate \u2014 Manage agent integrations
|
|
33
|
-
/menu \u2014 Show action menu
|
|
34
|
-
|
|
35
|
-
\u{1F512} <b>Session Options</b>
|
|
36
|
-
/enable_dangerous \u2014 Auto-approve permissions
|
|
37
|
-
/disable_dangerous \u2014 Restore permission prompts
|
|
38
|
-
/handoff \u2014 Continue session in terminal
|
|
39
|
-
/archive \u2014 Archive session topic
|
|
40
|
-
/clear \u2014 Clear assistant history
|
|
41
|
-
|
|
42
|
-
\u{1F4AC} Need help? Just ask me in this topic!`,
|
|
43
|
-
{ parse_mode: "HTML" }
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
async function handleClear(ctx, assistant) {
|
|
47
|
-
if (!assistant) {
|
|
48
|
-
await ctx.reply("\u26A0\uFE0F Assistant is not available.", { parse_mode: "HTML" });
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
const threadId = ctx.message?.message_thread_id;
|
|
52
|
-
if (threadId !== assistant.topicId) {
|
|
53
|
-
await ctx.reply("\u2139\uFE0F /clear only works in the Assistant topic.", { parse_mode: "HTML" });
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
await ctx.reply("\u{1F504} Clearing assistant history...", { parse_mode: "HTML" });
|
|
57
|
-
try {
|
|
58
|
-
await assistant.respawn();
|
|
59
|
-
await ctx.reply("\u2705 Assistant history cleared.", { parse_mode: "HTML" });
|
|
60
|
-
} catch (err) {
|
|
61
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
62
|
-
await ctx.reply(`\u274C Failed to clear: <code>${message}</code>`, { parse_mode: "HTML" });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
var TELEGRAM_MSG_LIMIT = 4096;
|
|
66
|
-
function buildSkillMessages(commands) {
|
|
67
|
-
const sorted = [...commands].sort((a, b) => a.name.localeCompare(b.name));
|
|
68
|
-
const header = "\u{1F6E0} <b>Available Skills</b>\n";
|
|
69
|
-
const lines = sorted.map((c) => `<code>/${c.name}</code>`);
|
|
70
|
-
const messages = [];
|
|
71
|
-
let current = header;
|
|
72
|
-
for (const line of lines) {
|
|
73
|
-
const candidate = current + "\n" + line;
|
|
74
|
-
if (candidate.length > TELEGRAM_MSG_LIMIT) {
|
|
75
|
-
messages.push(current);
|
|
76
|
-
current = line;
|
|
77
|
-
} else {
|
|
78
|
-
current = candidate;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
if (current) messages.push(current);
|
|
82
|
-
return messages;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export {
|
|
86
|
-
buildMenuKeyboard,
|
|
87
|
-
handleMenu,
|
|
88
|
-
handleHelp,
|
|
89
|
-
handleClear,
|
|
90
|
-
buildSkillMessages
|
|
91
|
-
};
|
|
92
|
-
//# sourceMappingURL=chunk-7QJS2XBD.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/telegram/commands/menu.ts"],"sourcesContent":["import type { Context } from \"grammy\";\nimport { InlineKeyboard } from \"grammy\";\nimport type { AgentCommand } from \"../../../core/index.js\";\nimport type { CommandsAssistantContext } from \"../types.js\";\n\nexport function buildMenuKeyboard(): InlineKeyboard {\n return new InlineKeyboard()\n .text(\"🆕 New Session\", \"m:new\")\n .text(\"📋 Sessions\", \"m:topics\")\n .row()\n .text(\"📊 Status\", \"m:status\")\n .text(\"🤖 Agents\", \"m:agents\")\n .row()\n .text(\"⚙️ Settings\", \"m:settings\")\n .text(\"🔗 Integrate\", \"m:integrate\")\n .row()\n .text(\"🔄 Restart\", \"m:restart\")\n .text(\"⬆️ Update\", \"m:update\")\n .row()\n .text(\"❓ Help\", \"m:help\")\n .text(\"🩺 Doctor\", \"m:doctor\");\n}\n\nexport async function handleMenu(ctx: Context): Promise<void> {\n await ctx.reply(`<b>OpenACP Menu</b>\\nChoose an action:`, {\n parse_mode: \"HTML\",\n reply_markup: buildMenuKeyboard(),\n });\n}\n\nexport async function handleHelp(ctx: Context): Promise<void> {\n await ctx.reply(\n `📖 <b>OpenACP Help</b>\\n\\n` +\n `🚀 <b>Getting Started</b>\\n` +\n `Tap 🆕 New Session to start coding with AI.\\n` +\n `Each session gets its own topic — chat there to work with the agent.\\n\\n` +\n `💡 <b>Common Tasks</b>\\n` +\n `/new [agent] [workspace] — Create new session\\n` +\n `/cancel — Cancel session (in session topic)\\n` +\n `/status — Show session or system status\\n` +\n `/sessions — List all sessions\\n` +\n `/agents — Browse & install agents\\n` +\n `/install <name> — Install an agent\\n\\n` +\n `⚙️ <b>System</b>\\n` +\n `/restart — Restart OpenACP\\n` +\n `/update — Update to latest version\\n` +\n `/integrate — Manage agent integrations\\n` +\n `/menu — Show action menu\\n\\n` +\n `🔒 <b>Session Options</b>\\n` +\n `/enable_dangerous — Auto-approve permissions\\n` +\n `/disable_dangerous — Restore permission prompts\\n` +\n `/handoff — Continue session in terminal\\n` +\n `/archive — Archive session topic\\n` +\n `/clear — Clear assistant history\\n\\n` +\n `💬 Need help? Just ask me in this topic!`,\n { parse_mode: \"HTML\" },\n );\n}\n\nexport async function handleClear(ctx: Context, assistant?: CommandsAssistantContext): Promise<void> {\n if (!assistant) {\n await ctx.reply(\"⚠️ Assistant is not available.\", { parse_mode: \"HTML\" });\n return;\n }\n\n const threadId = ctx.message?.message_thread_id;\n if (threadId !== assistant.topicId) {\n await ctx.reply(\"ℹ️ /clear only works in the Assistant topic.\", { parse_mode: \"HTML\" });\n return;\n }\n\n await ctx.reply(\"🔄 Clearing assistant history...\", { parse_mode: \"HTML\" });\n\n try {\n await assistant.respawn();\n await ctx.reply(\"✅ Assistant history cleared.\", { parse_mode: \"HTML\" });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n await ctx.reply(`❌ Failed to clear: <code>${message}</code>`, { parse_mode: \"HTML\" });\n }\n}\n\nconst TELEGRAM_MSG_LIMIT = 4096;\n\n/**\n * Build plain-text skill command messages. Each command is on its own line\n * wrapped in <code> for tap-to-copy. If the list exceeds Telegram's message\n * limit, it is split into multiple messages (cut at line boundaries).\n */\nexport function buildSkillMessages(commands: AgentCommand[]): string[] {\n const sorted = [...commands].sort((a, b) => a.name.localeCompare(b.name));\n const header = \"🛠 <b>Available Skills</b>\\n\";\n const lines = sorted.map((c) => `<code>/${c.name}</code>`);\n\n const messages: string[] = [];\n let current = header;\n\n for (const line of lines) {\n const candidate = current + \"\\n\" + line;\n if (candidate.length > TELEGRAM_MSG_LIMIT) {\n messages.push(current);\n current = line;\n } else {\n current = candidate;\n }\n }\n if (current) messages.push(current);\n return messages;\n}\n"],"mappings":";AACA,SAAS,sBAAsB;AAIxB,SAAS,oBAAoC;AAClD,SAAO,IAAI,eAAe,EACvB,KAAK,yBAAkB,OAAO,EAC9B,KAAK,sBAAe,UAAU,EAC9B,IAAI,EACJ,KAAK,oBAAa,UAAU,EAC5B,KAAK,oBAAa,UAAU,EAC5B,IAAI,EACJ,KAAK,yBAAe,YAAY,EAChC,KAAK,uBAAgB,aAAa,EAClC,IAAI,EACJ,KAAK,qBAAc,WAAW,EAC9B,KAAK,uBAAa,UAAU,EAC5B,IAAI,EACJ,KAAK,eAAU,QAAQ,EACvB,KAAK,oBAAa,UAAU;AACjC;AAEA,eAAsB,WAAW,KAA6B;AAC5D,QAAM,IAAI,MAAM;AAAA,oBAA0C;AAAA,IACxD,YAAY;AAAA,IACZ,cAAc,kBAAkB;AAAA,EAClC,CAAC;AACH;AAEA,eAAsB,WAAW,KAA6B;AAC5D,QAAM,IAAI;AAAA,IACR;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAuBA,EAAE,YAAY,OAAO;AAAA,EACvB;AACF;AAEA,eAAsB,YAAY,KAAc,WAAqD;AACnG,MAAI,CAAC,WAAW;AACd,UAAM,IAAI,MAAM,4CAAkC,EAAE,YAAY,OAAO,CAAC;AACxE;AAAA,EACF;AAEA,QAAM,WAAW,IAAI,SAAS;AAC9B,MAAI,aAAa,UAAU,SAAS;AAClC,UAAM,IAAI,MAAM,0DAAgD,EAAE,YAAY,OAAO,CAAC;AACtF;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,2CAAoC,EAAE,YAAY,OAAO,CAAC;AAE1E,MAAI;AACF,UAAM,UAAU,QAAQ;AACxB,UAAM,IAAI,MAAM,qCAAgC,EAAE,YAAY,OAAO,CAAC;AAAA,EACxE,SAAS,KAAK;AACZ,UAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAM,IAAI,MAAM,iCAA4B,OAAO,WAAW,EAAE,YAAY,OAAO,CAAC;AAAA,EACtF;AACF;AAEA,IAAM,qBAAqB;AAOpB,SAAS,mBAAmB,UAAoC;AACrE,QAAM,SAAS,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AACxE,QAAM,SAAS;AACf,QAAM,QAAQ,OAAO,IAAI,CAAC,MAAM,UAAU,EAAE,IAAI,SAAS;AAEzD,QAAM,WAAqB,CAAC;AAC5B,MAAI,UAAU;AAEd,aAAW,QAAQ,OAAO;AACxB,UAAM,YAAY,UAAU,OAAO;AACnC,QAAI,UAAU,SAAS,oBAAoB;AACzC,eAAS,KAAK,OAAO;AACrB,gBAAU;AAAA,IACZ,OAAO;AACL,gBAAU;AAAA,IACZ;AAAA,EACF;AACA,MAAI,QAAS,UAAS,KAAK,OAAO;AAClC,SAAO;AACT;","names":[]}
|
package/dist/chunk-BNLGTZ34.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
log
|
|
3
|
-
} from "./chunk-GAK6PIBW.js";
|
|
4
|
-
|
|
5
|
-
// src/adapters/discord/forums.ts
|
|
6
|
-
import { ChannelType } from "discord.js";
|
|
7
|
-
async function ensureForums(guild, config, saveConfig) {
|
|
8
|
-
let forumChannelId = config.forumChannelId;
|
|
9
|
-
let notificationChannelId = config.notificationChannelId;
|
|
10
|
-
let forumChannel = null;
|
|
11
|
-
if (forumChannelId) {
|
|
12
|
-
try {
|
|
13
|
-
const ch = guild.channels.cache.get(forumChannelId) ?? await guild.channels.fetch(forumChannelId);
|
|
14
|
-
if (ch && (ch.type === ChannelType.GuildForum || ch.type === ChannelType.GuildText)) {
|
|
15
|
-
forumChannel = ch;
|
|
16
|
-
log.info({ forumChannelId, type: ch.type }, "[forums] Reusing existing sessions channel");
|
|
17
|
-
}
|
|
18
|
-
} catch {
|
|
19
|
-
log.warn({ forumChannelId }, "[forums] Saved sessions channel not found, recreating...");
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
if (!forumChannel) {
|
|
23
|
-
if (guild.features.includes("COMMUNITY")) {
|
|
24
|
-
const channel = await guild.channels.create({
|
|
25
|
-
name: "openacp-sessions",
|
|
26
|
-
type: ChannelType.GuildForum
|
|
27
|
-
});
|
|
28
|
-
forumChannel = channel;
|
|
29
|
-
log.info({ forumChannelId: channel.id }, "[forums] Created forum channel");
|
|
30
|
-
} else {
|
|
31
|
-
const channel = await guild.channels.create({
|
|
32
|
-
name: "openacp-sessions",
|
|
33
|
-
type: ChannelType.GuildText
|
|
34
|
-
});
|
|
35
|
-
forumChannel = channel;
|
|
36
|
-
log.info({ forumChannelId: channel.id }, "[forums] Created text channel (Community mode not enabled, using threads fallback)");
|
|
37
|
-
}
|
|
38
|
-
await saveConfig({ channels: { discord: { forumChannelId: forumChannel.id } } });
|
|
39
|
-
}
|
|
40
|
-
let notificationChannel = null;
|
|
41
|
-
if (notificationChannelId) {
|
|
42
|
-
try {
|
|
43
|
-
const ch = guild.channels.cache.get(notificationChannelId) ?? await guild.channels.fetch(notificationChannelId);
|
|
44
|
-
if (ch && ch.type === ChannelType.GuildText) {
|
|
45
|
-
notificationChannel = ch;
|
|
46
|
-
log.info({ notificationChannelId }, "[forums] Reusing existing notification channel");
|
|
47
|
-
}
|
|
48
|
-
} catch {
|
|
49
|
-
log.warn({ notificationChannelId }, "[forums] Saved notification channel not found, recreating...");
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
if (!notificationChannel) {
|
|
53
|
-
const channel = await guild.channels.create({
|
|
54
|
-
name: "openacp-notifications",
|
|
55
|
-
type: ChannelType.GuildText
|
|
56
|
-
});
|
|
57
|
-
notificationChannel = channel;
|
|
58
|
-
await saveConfig({ channels: { discord: { notificationChannelId: channel.id } } });
|
|
59
|
-
log.info({ notificationChannelId: channel.id }, "[forums] Created notification channel");
|
|
60
|
-
}
|
|
61
|
-
return { forumChannel, notificationChannel };
|
|
62
|
-
}
|
|
63
|
-
async function createSessionThread(forumChannel, name) {
|
|
64
|
-
if (forumChannel.type === ChannelType.GuildForum) {
|
|
65
|
-
const thread2 = await forumChannel.threads.create({
|
|
66
|
-
name,
|
|
67
|
-
message: { content: "\u23F3 Setting up..." }
|
|
68
|
-
});
|
|
69
|
-
return thread2;
|
|
70
|
-
}
|
|
71
|
-
const textChannel = forumChannel;
|
|
72
|
-
const msg = await textChannel.send({ content: `\u{1F4C2} **${name}** \u2014 \u23F3 Setting up...` });
|
|
73
|
-
const thread = await msg.startThread({ name });
|
|
74
|
-
return thread;
|
|
75
|
-
}
|
|
76
|
-
async function renameSessionThread(guild, threadId, newName) {
|
|
77
|
-
try {
|
|
78
|
-
const channel = guild.channels.cache.get(threadId) ?? await guild.channels.fetch(threadId);
|
|
79
|
-
if (channel && "setName" in channel) {
|
|
80
|
-
await channel.setName(newName);
|
|
81
|
-
}
|
|
82
|
-
} catch {
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
async function deleteSessionThread(guild, threadId) {
|
|
86
|
-
try {
|
|
87
|
-
const channel = guild.channels.cache.get(threadId) ?? await guild.channels.fetch(threadId);
|
|
88
|
-
if (channel && channel.isThread()) {
|
|
89
|
-
const thread = channel;
|
|
90
|
-
if (!thread.archived) {
|
|
91
|
-
await thread.setArchived(true);
|
|
92
|
-
}
|
|
93
|
-
if (!thread.locked) {
|
|
94
|
-
await thread.setLocked(true);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
} catch {
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
async function ensureUnarchived(thread) {
|
|
101
|
-
if (thread.archived) {
|
|
102
|
-
try {
|
|
103
|
-
await thread.setArchived(false);
|
|
104
|
-
} catch (err) {
|
|
105
|
-
log.warn({ err, threadId: thread.id }, "[forums] Failed to unarchive thread");
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
function buildDeepLink(guildId, channelId, messageId) {
|
|
110
|
-
const base = `https://discord.com/channels/${guildId}/${channelId}`;
|
|
111
|
-
return messageId ? `${base}/${messageId}` : base;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export {
|
|
115
|
-
ensureForums,
|
|
116
|
-
createSessionThread,
|
|
117
|
-
renameSessionThread,
|
|
118
|
-
deleteSessionThread,
|
|
119
|
-
ensureUnarchived,
|
|
120
|
-
buildDeepLink
|
|
121
|
-
};
|
|
122
|
-
//# sourceMappingURL=chunk-BNLGTZ34.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/discord/forums.ts"],"sourcesContent":["import { ChannelType } from 'discord.js'\nimport type { ForumChannel, ThreadChannel, Guild, TextChannel } from 'discord.js'\nimport { log } from '../../core/log.js'\n\n// ─── ensureForums ─────────────────────────────────────────────────────────────\n\n/**\n * Ensures both the forum channel and notification channel exist.\n * Creates them if their IDs are null, then persists the IDs via saveConfig.\n *\n * saveConfig uses nested object path: { channels: { discord: { forumChannelId: ... } } }\n */\nexport async function ensureForums(\n guild: Guild,\n config: {\n forumChannelId: string | null\n notificationChannelId: string | null\n },\n saveConfig: (updates: Record<string, unknown>) => Promise<void>,\n): Promise<{ forumChannel: ForumChannel | TextChannel; notificationChannel: TextChannel }> {\n let forumChannelId = config.forumChannelId\n let notificationChannelId = config.notificationChannelId\n\n // Ensure forum/sessions channel exists — fetch existing or create new\n let forumChannel: ForumChannel | TextChannel | null = null\n if (forumChannelId) {\n try {\n const ch = guild.channels.cache.get(forumChannelId)\n ?? await guild.channels.fetch(forumChannelId)\n if (ch && (ch.type === ChannelType.GuildForum || ch.type === ChannelType.GuildText)) {\n forumChannel = ch as ForumChannel | TextChannel\n log.info({ forumChannelId, type: ch.type }, '[forums] Reusing existing sessions channel')\n }\n } catch {\n log.warn({ forumChannelId }, '[forums] Saved sessions channel not found, recreating...')\n }\n }\n if (!forumChannel) {\n // Prefer Forum Channel (requires Community mode), fallback to Text Channel with threads\n if (guild.features.includes('COMMUNITY')) {\n const channel = await guild.channels.create({\n name: 'openacp-sessions',\n type: ChannelType.GuildForum,\n })\n forumChannel = channel as ForumChannel\n log.info({ forumChannelId: channel.id }, '[forums] Created forum channel')\n } else {\n const channel = await guild.channels.create({\n name: 'openacp-sessions',\n type: ChannelType.GuildText,\n })\n forumChannel = channel as TextChannel\n log.info({ forumChannelId: channel.id }, '[forums] Created text channel (Community mode not enabled, using threads fallback)')\n }\n await saveConfig({ channels: { discord: { forumChannelId: forumChannel.id } } })\n }\n\n // Ensure notification channel exists — fetch existing or create new\n let notificationChannel: TextChannel | null = null\n if (notificationChannelId) {\n try {\n const ch = guild.channels.cache.get(notificationChannelId)\n ?? await guild.channels.fetch(notificationChannelId)\n if (ch && ch.type === ChannelType.GuildText) {\n notificationChannel = ch as TextChannel\n log.info({ notificationChannelId }, '[forums] Reusing existing notification channel')\n }\n } catch {\n log.warn({ notificationChannelId }, '[forums] Saved notification channel not found, recreating...')\n }\n }\n if (!notificationChannel) {\n const channel = await guild.channels.create({\n name: 'openacp-notifications',\n type: ChannelType.GuildText,\n })\n notificationChannel = channel as TextChannel\n await saveConfig({ channels: { discord: { notificationChannelId: channel.id } } })\n log.info({ notificationChannelId: channel.id }, '[forums] Created notification channel')\n }\n\n return { forumChannel, notificationChannel }\n}\n\n// ─── createSessionThread ──────────────────────────────────────────────────────\n\n/**\n * Creates a new thread for a session.\n * - Forum Channel: creates a forum post (thread with initial message)\n * - Text Channel: creates a public thread\n */\nexport async function createSessionThread(\n forumChannel: ForumChannel | TextChannel,\n name: string,\n): Promise<ThreadChannel> {\n if (forumChannel.type === ChannelType.GuildForum) {\n // Forum channel: create a post (thread with initial message)\n const thread = await (forumChannel as ForumChannel).threads.create({\n name,\n message: { content: '⏳ Setting up...' },\n })\n return thread\n }\n\n // Text channel fallback: send a message first, then create a thread on it\n const textChannel = forumChannel as TextChannel\n const msg = await textChannel.send({ content: `📂 **${name}** — ⏳ Setting up...` })\n const thread = await msg.startThread({ name })\n return thread\n}\n\n// ─── renameSessionThread ──────────────────────────────────────────────────────\n\n/**\n * Fetches and renames a thread. Ignores all errors (thread may be deleted/archived).\n */\nexport async function renameSessionThread(\n guild: Guild,\n threadId: string,\n newName: string,\n): Promise<void> {\n try {\n const channel = guild.channels.cache.get(threadId)\n ?? await guild.channels.fetch(threadId)\n if (channel && 'setName' in channel) {\n await (channel as ThreadChannel).setName(newName)\n }\n } catch {\n // Ignore — thread may be deleted or archived\n }\n}\n\n// ─── deleteSessionThread ──────────────────────────────────────────────────────\n\n/**\n * Archives and locks a thread instead of permanently deleting it.\n * Unlike Telegram (which just closes a topic), Discord delete is permanent\n * and destroys all messages. Archiving preserves the conversation history.\n */\nexport async function deleteSessionThread(\n guild: Guild,\n threadId: string,\n): Promise<void> {\n try {\n const channel = guild.channels.cache.get(threadId)\n ?? await guild.channels.fetch(threadId)\n if (channel && channel.isThread()) {\n const thread = channel as ThreadChannel\n if (!thread.archived) {\n await thread.setArchived(true)\n }\n if (!thread.locked) {\n await thread.setLocked(true)\n }\n }\n } catch {\n // Ignore — thread may already be deleted or inaccessible\n }\n}\n\n// ─── ensureUnarchived ─────────────────────────────────────────────────────────\n\n/**\n * If the thread is archived, unarchives it.\n */\nexport async function ensureUnarchived(thread: ThreadChannel): Promise<void> {\n if (thread.archived) {\n try {\n await thread.setArchived(false)\n } catch (err) {\n log.warn({ err, threadId: thread.id }, '[forums] Failed to unarchive thread')\n }\n }\n}\n\n// ─── buildDeepLink ────────────────────────────────────────────────────────────\n\n/**\n * Builds a Discord deep link URL to a channel/thread, optionally to a specific message.\n */\nexport function buildDeepLink(\n guildId: string,\n channelId: string,\n messageId?: string,\n): string {\n const base = `https://discord.com/channels/${guildId}/${channelId}`\n return messageId ? `${base}/${messageId}` : base\n}\n"],"mappings":"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","names":["thread"]}
|