@openacp/cli 2026.327.1 → 2026.327.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/{adapter-Z435XYBQ.js → adapter-LC2QSDAS.js} +4 -5
- package/dist/{adapter-PQGHVG4K.js → adapter-Y55NXX6I.js} +2 -2
- package/dist/{api-server-5VEESFOT.js → api-server-7G3ZUZRM.js} +2 -2
- package/dist/{api-server-2I7B3MXR.js → api-server-CAYNPUF2.js} +2 -2
- package/dist/{chunk-5YW56UJK.js → chunk-2YCW3QDV.js} +21 -5
- package/dist/chunk-2YCW3QDV.js.map +1 -0
- package/dist/{chunk-366FOUJG.js → chunk-36YQ44D7.js} +2 -2
- package/dist/{chunk-UB2QB6DE.js → chunk-3ASUU6WW.js} +2 -2
- package/dist/{chunk-ZPTM4NGK.js → chunk-4GMLGCF2.js} +2 -2
- package/dist/{chunk-CFM4GJ74.js → chunk-HUWOFP2H.js} +7 -11
- package/dist/chunk-HUWOFP2H.js.map +1 -0
- package/dist/{chunk-P4SNGQNI.js → chunk-KMMEFXIE.js} +2 -2
- package/dist/{chunk-FDLQ5M6W.js → chunk-LP45RCA4.js} +568 -23
- package/dist/chunk-LP45RCA4.js.map +1 -0
- package/dist/{chunk-L7YNNBI5.js → chunk-QAQDGPB4.js} +1 -75
- package/dist/chunk-QAQDGPB4.js.map +1 -0
- package/dist/{chunk-V2M243KZ.js → chunk-TRXBJEZ5.js} +55 -53
- package/dist/chunk-TRXBJEZ5.js.map +1 -0
- package/dist/{chunk-7KGWYNWE.js → chunk-UMT7RU77.js} +45 -118
- package/dist/chunk-UMT7RU77.js.map +1 -0
- package/dist/{chunk-QUXTZU36.js → chunk-XIBG7LSL.js} +137 -3
- package/dist/chunk-XIBG7LSL.js.map +1 -0
- package/dist/cli.js +24 -17
- package/dist/cli.js.map +1 -1
- package/dist/{config-editor-2GYL2SSZ.js → config-editor-3IKBPZA7.js} +2 -2
- package/dist/{core-plugins-I6UPXQBL.js → core-plugins-ROU4GPLT.js} +6 -7
- package/dist/{dev-loader-RDC5E2CW.js → dev-loader-DRU3R7ZM.js} +7 -18
- package/dist/dev-loader-DRU3R7ZM.js.map +1 -0
- package/dist/{doctor-H72BZOPA.js → doctor-QZQAP46W.js} +2 -2
- package/dist/index.d.ts +115 -5
- package/dist/index.js +45 -18
- package/dist/index.js.map +1 -1
- package/dist/{integrate-APK4OEQF.js → integrate-G6CVXTGT.js} +2 -3
- package/dist/integrate-G6CVXTGT.js.map +1 -0
- package/dist/{main-EJBK65NS.js → main-UVTZ46WP.js} +20 -147
- package/dist/main-UVTZ46WP.js.map +1 -0
- package/dist/{plugin-create-LCXXNDK6.js → plugin-create-5HQRF2ID.js} +19 -2
- package/dist/plugin-create-5HQRF2ID.js.map +1 -0
- package/dist/plugin-installer-GQ2P3Q3E.js +23 -0
- package/dist/plugin-installer-GQ2P3Q3E.js.map +1 -0
- package/dist/{post-upgrade-2MG3VUDV.js → post-upgrade-3ADZRMYJ.js} +2 -2
- package/dist/{setup-N7KT56O7.js → setup-EYAFK2WI.js} +77 -50
- package/dist/setup-EYAFK2WI.js.map +1 -0
- package/dist/{slack-KH7E3VBS.js → slack-37ZWBDUI.js} +2 -2
- package/dist/{telegram-QWMJU3A6.js → telegram-2ZCCCZIY.js} +2 -2
- package/dist/{tunnel-M47I7H4B.js → tunnel-45HA72MB.js} +2 -2
- package/dist/{tunnel-service-WADYHREX.js → tunnel-service-QJPUYEKU.js} +11 -3
- package/dist/tunnel-service-QJPUYEKU.js.map +1 -0
- package/package.json +1 -2
- package/dist/action-detect-QPA775HB.js +0 -16
- package/dist/adapter-IZNL6AK2.js +0 -2394
- package/dist/adapter-IZNL6AK2.js.map +0 -1
- package/dist/admin-GBPZFFAU.js +0 -23
- package/dist/agents-BWU4MRRD.js +0 -15
- package/dist/chunk-4KGLKKQK.js +0 -298
- package/dist/chunk-4KGLKKQK.js.map +0 -1
- package/dist/chunk-5RO42TWV.js +0 -150
- package/dist/chunk-5RO42TWV.js.map +0 -1
- package/dist/chunk-5YW56UJK.js.map +0 -1
- package/dist/chunk-5ZOFBTOR.js +0 -553
- package/dist/chunk-5ZOFBTOR.js.map +0 -1
- package/dist/chunk-6RXVEXF3.js +0 -23
- package/dist/chunk-6RXVEXF3.js.map +0 -1
- package/dist/chunk-7KGWYNWE.js.map +0 -1
- package/dist/chunk-CFM4GJ74.js.map +0 -1
- package/dist/chunk-FDLQ5M6W.js.map +0 -1
- package/dist/chunk-GJOY37U7.js +0 -265
- package/dist/chunk-GJOY37U7.js.map +0 -1
- package/dist/chunk-HVBNCPAY.js +0 -71
- package/dist/chunk-HVBNCPAY.js.map +0 -1
- package/dist/chunk-I3CGU5W7.js +0 -134
- package/dist/chunk-I3CGU5W7.js.map +0 -1
- package/dist/chunk-L7YNNBI5.js.map +0 -1
- package/dist/chunk-MTSDOSXS.js +0 -219
- package/dist/chunk-MTSDOSXS.js.map +0 -1
- package/dist/chunk-NAM4ERUW.js +0 -203
- package/dist/chunk-NAM4ERUW.js.map +0 -1
- package/dist/chunk-O5RG4YZY.js +0 -122
- package/dist/chunk-O5RG4YZY.js.map +0 -1
- package/dist/chunk-QUXTZU36.js.map +0 -1
- package/dist/chunk-V2M243KZ.js.map +0 -1
- package/dist/chunk-VO3A2NI4.js +0 -145
- package/dist/chunk-VO3A2NI4.js.map +0 -1
- package/dist/dev-loader-RDC5E2CW.js.map +0 -1
- package/dist/discord-DXDTGVGS.js +0 -8
- package/dist/doctor-H72BZOPA.js.map +0 -1
- package/dist/doctor-RF6BHMCC.js +0 -15
- package/dist/doctor-RF6BHMCC.js.map +0 -1
- package/dist/integrate-APK4OEQF.js.map +0 -1
- package/dist/main-EJBK65NS.js.map +0 -1
- package/dist/new-session-HFO5GHSZ.js +0 -17
- package/dist/new-session-HFO5GHSZ.js.map +0 -1
- package/dist/plugin-create-LCXXNDK6.js.map +0 -1
- package/dist/session-KZFA6Z26.js +0 -20
- package/dist/session-KZFA6Z26.js.map +0 -1
- package/dist/settings-MFYM7CZO.js +0 -14
- package/dist/settings-MFYM7CZO.js.map +0 -1
- package/dist/setup-N7KT56O7.js.map +0 -1
- package/dist/slack-KH7E3VBS.js.map +0 -1
- package/dist/telegram-QWMJU3A6.js.map +0 -1
- package/dist/tunnel-M47I7H4B.js.map +0 -1
- package/dist/tunnel-service-WADYHREX.js.map +0 -1
- package/dist/validators-6CLEZUBD.js +0 -8
- package/dist/validators-6CLEZUBD.js.map +0 -1
- /package/dist/{action-detect-QPA775HB.js.map → adapter-LC2QSDAS.js.map} +0 -0
- /package/dist/{adapter-PQGHVG4K.js.map → adapter-Y55NXX6I.js.map} +0 -0
- /package/dist/{adapter-Z435XYBQ.js.map → api-server-7G3ZUZRM.js.map} +0 -0
- /package/dist/{admin-GBPZFFAU.js.map → api-server-CAYNPUF2.js.map} +0 -0
- /package/dist/{chunk-366FOUJG.js.map → chunk-36YQ44D7.js.map} +0 -0
- /package/dist/{chunk-UB2QB6DE.js.map → chunk-3ASUU6WW.js.map} +0 -0
- /package/dist/{chunk-ZPTM4NGK.js.map → chunk-4GMLGCF2.js.map} +0 -0
- /package/dist/{chunk-P4SNGQNI.js.map → chunk-KMMEFXIE.js.map} +0 -0
- /package/dist/{agents-BWU4MRRD.js.map → config-editor-3IKBPZA7.js.map} +0 -0
- /package/dist/{api-server-2I7B3MXR.js.map → core-plugins-ROU4GPLT.js.map} +0 -0
- /package/dist/{api-server-5VEESFOT.js.map → doctor-QZQAP46W.js.map} +0 -0
- /package/dist/{post-upgrade-2MG3VUDV.js.map → post-upgrade-3ADZRMYJ.js.map} +0 -0
- /package/dist/{config-editor-2GYL2SSZ.js.map → slack-37ZWBDUI.js.map} +0 -0
- /package/dist/{core-plugins-I6UPXQBL.js.map → telegram-2ZCCCZIY.js.map} +0 -0
- /package/dist/{discord-DXDTGVGS.js.map → tunnel-45HA72MB.js.map} +0 -0
package/dist/chunk-4KGLKKQK.js
DELETED
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getConfigValue,
|
|
3
|
-
getSafeFields,
|
|
4
|
-
isHotReloadable,
|
|
5
|
-
resolveOptions
|
|
6
|
-
} from "./chunk-ODUM3D6X.js";
|
|
7
|
-
import {
|
|
8
|
-
log
|
|
9
|
-
} from "./chunk-XMMAGAT4.js";
|
|
10
|
-
|
|
11
|
-
// src/plugins/discord/commands/settings.ts
|
|
12
|
-
import {
|
|
13
|
-
ActionRowBuilder,
|
|
14
|
-
ButtonBuilder,
|
|
15
|
-
ButtonStyle
|
|
16
|
-
} from "discord.js";
|
|
17
|
-
function formatFieldLabel(field, value) {
|
|
18
|
-
const icons = {
|
|
19
|
-
agent: "\u{1F916}",
|
|
20
|
-
logging: "\u{1F4DD}",
|
|
21
|
-
tunnel: "\u{1F517}",
|
|
22
|
-
security: "\u{1F512}",
|
|
23
|
-
workspace: "\u{1F4C1}",
|
|
24
|
-
storage: "\u{1F4BE}",
|
|
25
|
-
speech: "\u{1F3A4}"
|
|
26
|
-
};
|
|
27
|
-
const icon = icons[field.group] ?? "\u2699\uFE0F";
|
|
28
|
-
if (field.type === "toggle") {
|
|
29
|
-
return `${icon} ${field.displayName}: ${value ? "ON" : "OFF"}`;
|
|
30
|
-
}
|
|
31
|
-
const displayValue = value === null || value === void 0 ? "Not set" : String(value);
|
|
32
|
-
return `${icon} ${field.displayName}: ${displayValue}`;
|
|
33
|
-
}
|
|
34
|
-
var SETTINGS_PAGE_SIZE = 4;
|
|
35
|
-
function buildSettingsRows(adapter, page = 0) {
|
|
36
|
-
const config = adapter.core.configManager.get();
|
|
37
|
-
const fields = getSafeFields();
|
|
38
|
-
const totalPages = Math.ceil(fields.length / SETTINGS_PAGE_SIZE);
|
|
39
|
-
const start = page * SETTINGS_PAGE_SIZE;
|
|
40
|
-
const pageFields = fields.slice(start, start + SETTINGS_PAGE_SIZE);
|
|
41
|
-
const rows = [];
|
|
42
|
-
for (const field of pageFields) {
|
|
43
|
-
const value = getConfigValue(config, field.path);
|
|
44
|
-
const label = formatFieldLabel(field, value);
|
|
45
|
-
let customId;
|
|
46
|
-
if (field.type === "toggle") {
|
|
47
|
-
customId = `s:toggle:${field.path}`;
|
|
48
|
-
} else if (field.type === "select") {
|
|
49
|
-
customId = `s:select:${field.path}`;
|
|
50
|
-
} else {
|
|
51
|
-
customId = `s:input:${field.path}`;
|
|
52
|
-
}
|
|
53
|
-
rows.push(
|
|
54
|
-
new ActionRowBuilder().addComponents(
|
|
55
|
-
new ButtonBuilder().setCustomId(customId).setLabel(label.slice(0, 80)).setStyle(ButtonStyle.Secondary)
|
|
56
|
-
)
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
if (totalPages > 1) {
|
|
60
|
-
const navRow = new ActionRowBuilder();
|
|
61
|
-
if (page > 0) {
|
|
62
|
-
navRow.addComponents(
|
|
63
|
-
new ButtonBuilder().setCustomId(`s:page:${page - 1}`).setLabel("\u25C0\uFE0F Previous").setStyle(ButtonStyle.Primary)
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
navRow.addComponents(
|
|
67
|
-
new ButtonBuilder().setCustomId("s:pageinfo").setLabel(`Page ${page + 1}/${totalPages}`).setStyle(ButtonStyle.Secondary).setDisabled(true)
|
|
68
|
-
);
|
|
69
|
-
if (page < totalPages - 1) {
|
|
70
|
-
navRow.addComponents(
|
|
71
|
-
new ButtonBuilder().setCustomId(`s:page:${page + 1}`).setLabel("Next \u25B6\uFE0F").setStyle(ButtonStyle.Primary)
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
rows.push(navRow);
|
|
75
|
-
}
|
|
76
|
-
return rows;
|
|
77
|
-
}
|
|
78
|
-
async function handleSettings(interaction, adapter) {
|
|
79
|
-
await interaction.deferReply({ ephemeral: true });
|
|
80
|
-
const rows = buildSettingsRows(adapter);
|
|
81
|
-
await interaction.editReply({
|
|
82
|
-
content: "**\u2699\uFE0F Settings**\nTap to change:",
|
|
83
|
-
components: rows
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
async function showSettingsInfo(interaction, adapter) {
|
|
87
|
-
const rows = buildSettingsRows(adapter);
|
|
88
|
-
await interaction.followUp({
|
|
89
|
-
content: "**\u2699\uFE0F Settings**\nTap to change:",
|
|
90
|
-
components: rows,
|
|
91
|
-
ephemeral: true
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
async function handleSettingsButton(interaction, adapter) {
|
|
95
|
-
const { customId } = interaction;
|
|
96
|
-
try {
|
|
97
|
-
if (customId.startsWith("s:toggle:")) {
|
|
98
|
-
const fieldPath = customId.replace("s:toggle:", "");
|
|
99
|
-
const config = adapter.core.configManager.get();
|
|
100
|
-
const currentValue = getConfigValue(config, fieldPath);
|
|
101
|
-
const newValue = !currentValue;
|
|
102
|
-
const updates = buildNestedUpdate(fieldPath, newValue);
|
|
103
|
-
await adapter.core.configManager.save(updates, fieldPath);
|
|
104
|
-
const toast = isHotReloadable(fieldPath) ? `\u2705 ${fieldPath} = ${newValue}` : `\u2705 ${fieldPath} = ${newValue} (restart needed)`;
|
|
105
|
-
try {
|
|
106
|
-
await interaction.update({
|
|
107
|
-
content: "**\u2699\uFE0F Settings**\nTap to change:",
|
|
108
|
-
components: buildSettingsRows(adapter)
|
|
109
|
-
});
|
|
110
|
-
} catch {
|
|
111
|
-
}
|
|
112
|
-
try {
|
|
113
|
-
await interaction.followUp({ content: toast, ephemeral: true });
|
|
114
|
-
} catch {
|
|
115
|
-
}
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
if (customId.startsWith("s:select:")) {
|
|
119
|
-
const fieldPath = customId.replace("s:select:", "");
|
|
120
|
-
const config = adapter.core.configManager.get();
|
|
121
|
-
const fieldDef = getSafeFields().find((f) => f.path === fieldPath);
|
|
122
|
-
if (!fieldDef) return;
|
|
123
|
-
const options = resolveOptions(fieldDef, config) ?? [];
|
|
124
|
-
const currentValue = getConfigValue(config, fieldPath);
|
|
125
|
-
const rows = [];
|
|
126
|
-
let currentRow = new ActionRowBuilder();
|
|
127
|
-
let count = 0;
|
|
128
|
-
for (const opt of options) {
|
|
129
|
-
const marker = opt === String(currentValue) ? " \u2713" : "";
|
|
130
|
-
currentRow.addComponents(
|
|
131
|
-
new ButtonBuilder().setCustomId(`s:pick:${fieldPath}:${opt}`).setLabel(`${opt}${marker}`.slice(0, 80)).setStyle(opt === String(currentValue) ? ButtonStyle.Success : ButtonStyle.Secondary)
|
|
132
|
-
);
|
|
133
|
-
count++;
|
|
134
|
-
if (count % 3 === 0) {
|
|
135
|
-
rows.push(currentRow);
|
|
136
|
-
currentRow = new ActionRowBuilder();
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (currentRow.components.length > 0) {
|
|
140
|
-
rows.push(currentRow);
|
|
141
|
-
}
|
|
142
|
-
const backRow = new ActionRowBuilder().addComponents(
|
|
143
|
-
new ButtonBuilder().setCustomId("s:back").setLabel("\u25C0\uFE0F Back").setStyle(ButtonStyle.Primary)
|
|
144
|
-
);
|
|
145
|
-
rows.push(backRow);
|
|
146
|
-
try {
|
|
147
|
-
await interaction.update({
|
|
148
|
-
content: `**\u2699\uFE0F ${fieldDef.displayName}**
|
|
149
|
-
Select a value:`,
|
|
150
|
-
components: rows.slice(0, 5)
|
|
151
|
-
});
|
|
152
|
-
} catch {
|
|
153
|
-
}
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
if (customId.startsWith("s:pick:")) {
|
|
157
|
-
const parts = customId.replace("s:pick:", "").split(":");
|
|
158
|
-
const fieldPath = parts.slice(0, -1).join(":");
|
|
159
|
-
const newValue = parts[parts.length - 1];
|
|
160
|
-
if (fieldPath === "speech.stt.provider") {
|
|
161
|
-
const config = adapter.core.configManager.get();
|
|
162
|
-
const providerConfig = config.speech?.stt?.providers?.[newValue];
|
|
163
|
-
if (!providerConfig?.apiKey) {
|
|
164
|
-
const assistantSessionId = adapter.getAssistantSessionId();
|
|
165
|
-
if (assistantSessionId) {
|
|
166
|
-
const assistantSession = adapter.core.sessionManager.getSession(assistantSessionId);
|
|
167
|
-
if (assistantSession) {
|
|
168
|
-
const prompt = `User wants to enable ${newValue} as Speech-to-Text provider, but no API key is configured yet. Guide them to get a ${newValue} API key and set it up. After they provide the key, run both commands: \`openacp config set speech.stt.providers.${newValue}.apiKey <key>\` and \`openacp config set speech.stt.provider ${newValue}\``;
|
|
169
|
-
await assistantSession.enqueuePrompt(prompt);
|
|
170
|
-
try {
|
|
171
|
-
await interaction.update({
|
|
172
|
-
content: "**\u2699\uFE0F Settings**\nTap to change:",
|
|
173
|
-
components: buildSettingsRows(adapter)
|
|
174
|
-
});
|
|
175
|
-
} catch {
|
|
176
|
-
}
|
|
177
|
-
try {
|
|
178
|
-
await interaction.followUp({ content: "\u{1F511} API key needed \u2014 check the Assistant thread.", ephemeral: true });
|
|
179
|
-
} catch {
|
|
180
|
-
}
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
try {
|
|
185
|
-
await interaction.update({
|
|
186
|
-
content: "**\u2699\uFE0F Settings**\nTap to change:",
|
|
187
|
-
components: buildSettingsRows(adapter)
|
|
188
|
-
});
|
|
189
|
-
} catch {
|
|
190
|
-
}
|
|
191
|
-
try {
|
|
192
|
-
await interaction.followUp({ content: `\u26A0\uFE0F Set API key first: \`openacp config set speech.stt.providers.${newValue}.apiKey <key>\``, ephemeral: true });
|
|
193
|
-
} catch {
|
|
194
|
-
}
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
const updates = buildNestedUpdate(fieldPath, newValue);
|
|
199
|
-
await adapter.core.configManager.save(updates, fieldPath);
|
|
200
|
-
try {
|
|
201
|
-
await interaction.update({
|
|
202
|
-
content: "**\u2699\uFE0F Settings**\nTap to change:",
|
|
203
|
-
components: buildSettingsRows(adapter)
|
|
204
|
-
});
|
|
205
|
-
} catch {
|
|
206
|
-
}
|
|
207
|
-
try {
|
|
208
|
-
await interaction.followUp({ content: `\u2705 ${fieldPath} = ${newValue}`, ephemeral: true });
|
|
209
|
-
} catch {
|
|
210
|
-
}
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
if (customId.startsWith("s:input:")) {
|
|
214
|
-
const fieldPath = customId.replace("s:input:", "");
|
|
215
|
-
const config = adapter.core.configManager.get();
|
|
216
|
-
const fieldDef = getSafeFields().find((f) => f.path === fieldPath);
|
|
217
|
-
if (!fieldDef) return;
|
|
218
|
-
const currentValue = getConfigValue(config, fieldPath);
|
|
219
|
-
const assistantSessionId = adapter.getAssistantSessionId();
|
|
220
|
-
if (!assistantSessionId) {
|
|
221
|
-
try {
|
|
222
|
-
await interaction.reply({ content: "\u26A0\uFE0F Assistant is not available.", ephemeral: true });
|
|
223
|
-
} catch {
|
|
224
|
-
}
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
const assistantSession = adapter.core.sessionManager.getSession(assistantSessionId);
|
|
228
|
-
if (!assistantSession) {
|
|
229
|
-
try {
|
|
230
|
-
await interaction.reply({ content: "\u26A0\uFE0F Assistant session not found.", ephemeral: true });
|
|
231
|
-
} catch {
|
|
232
|
-
}
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
try {
|
|
236
|
-
await interaction.deferUpdate();
|
|
237
|
-
} catch {
|
|
238
|
-
}
|
|
239
|
-
const prompt = `User wants to change ${fieldDef.displayName} (config path: ${fieldPath}). Current value: ${JSON.stringify(currentValue)}. Ask them for the new value and apply it using: openacp config set ${fieldPath} <value>`;
|
|
240
|
-
await assistantSession.enqueuePrompt(prompt);
|
|
241
|
-
try {
|
|
242
|
-
await interaction.followUp({ content: `Delegating to assistant \u2014 check the Assistant thread.`, ephemeral: true });
|
|
243
|
-
} catch {
|
|
244
|
-
}
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
if (customId.startsWith("s:page:")) {
|
|
248
|
-
const page = parseInt(customId.replace("s:page:", ""), 10);
|
|
249
|
-
try {
|
|
250
|
-
await interaction.update({
|
|
251
|
-
content: "**\u2699\uFE0F Settings**\nTap to change:",
|
|
252
|
-
components: buildSettingsRows(adapter, page)
|
|
253
|
-
});
|
|
254
|
-
} catch {
|
|
255
|
-
}
|
|
256
|
-
return;
|
|
257
|
-
}
|
|
258
|
-
if (customId === "s:back") {
|
|
259
|
-
try {
|
|
260
|
-
await interaction.update({
|
|
261
|
-
content: "**\u2699\uFE0F Settings**\nTap to change:",
|
|
262
|
-
components: buildSettingsRows(adapter)
|
|
263
|
-
});
|
|
264
|
-
} catch {
|
|
265
|
-
}
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
log.warn({ customId }, "[discord-settings] Unhandled settings button");
|
|
269
|
-
} catch (err) {
|
|
270
|
-
log.error({ err, customId }, "[discord-settings] Settings button handler failed");
|
|
271
|
-
try {
|
|
272
|
-
if (!interaction.replied && !interaction.deferred) {
|
|
273
|
-
await interaction.reply({ content: "\u274C Settings action failed.", ephemeral: true });
|
|
274
|
-
} else {
|
|
275
|
-
await interaction.followUp({ content: "\u274C Settings action failed.", ephemeral: true });
|
|
276
|
-
}
|
|
277
|
-
} catch {
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function buildNestedUpdate(dotPath, value) {
|
|
282
|
-
const parts = dotPath.split(".");
|
|
283
|
-
const result = {};
|
|
284
|
-
let target = result;
|
|
285
|
-
for (let i = 0; i < parts.length - 1; i++) {
|
|
286
|
-
target[parts[i]] = {};
|
|
287
|
-
target = target[parts[i]];
|
|
288
|
-
}
|
|
289
|
-
target[parts[parts.length - 1]] = value;
|
|
290
|
-
return result;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
export {
|
|
294
|
-
handleSettings,
|
|
295
|
-
showSettingsInfo,
|
|
296
|
-
handleSettingsButton
|
|
297
|
-
};
|
|
298
|
-
//# sourceMappingURL=chunk-4KGLKKQK.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugins/discord/commands/settings.ts"],"sourcesContent":["import {\n ActionRowBuilder,\n ButtonBuilder,\n ButtonStyle,\n} from 'discord.js'\nimport type { ChatInputCommandInteraction, ButtonInteraction } from 'discord.js'\nimport { log } from '../../../core/utils/log.js'\nimport { getSafeFields, resolveOptions, getConfigValue, isHotReloadable, type ConfigFieldDef } from '../../../core/config/config-registry.js'\nimport type { DiscordAdapter } from '../adapter.js'\n\nfunction formatFieldLabel(field: ConfigFieldDef, value: unknown): string {\n const icons: Record<string, string> = {\n agent: '🤖', logging: '📝', tunnel: '🔗',\n security: '🔒', workspace: '📁', storage: '💾', speech: '🎤',\n }\n const icon = icons[field.group] ?? '⚙️'\n\n if (field.type === 'toggle') {\n return `${icon} ${field.displayName}: ${value ? 'ON' : 'OFF'}`\n }\n const displayValue = value === null || value === undefined ? 'Not set' : String(value)\n return `${icon} ${field.displayName}: ${displayValue}`\n}\n\nconst SETTINGS_PAGE_SIZE = 4 // 4 field rows + 1 navigation row = 5 max\n\nfunction buildSettingsRows(adapter: DiscordAdapter, page = 0): ActionRowBuilder<ButtonBuilder>[] {\n const config = adapter.core.configManager.get()\n const fields = getSafeFields()\n const totalPages = Math.ceil(fields.length / SETTINGS_PAGE_SIZE)\n const start = page * SETTINGS_PAGE_SIZE\n const pageFields = fields.slice(start, start + SETTINGS_PAGE_SIZE)\n\n const rows: ActionRowBuilder<ButtonBuilder>[] = []\n\n for (const field of pageFields) {\n const value = getConfigValue(config, field.path)\n const label = formatFieldLabel(field, value)\n\n let customId: string\n if (field.type === 'toggle') {\n customId = `s:toggle:${field.path}`\n } else if (field.type === 'select') {\n customId = `s:select:${field.path}`\n } else {\n customId = `s:input:${field.path}`\n }\n\n rows.push(\n new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder()\n .setCustomId(customId)\n .setLabel(label.slice(0, 80))\n .setStyle(ButtonStyle.Secondary),\n ),\n )\n }\n\n // Navigation row (if more than 1 page)\n if (totalPages > 1) {\n const navRow = new ActionRowBuilder<ButtonBuilder>()\n if (page > 0) {\n navRow.addComponents(\n new ButtonBuilder()\n .setCustomId(`s:page:${page - 1}`)\n .setLabel('◀️ Previous')\n .setStyle(ButtonStyle.Primary),\n )\n }\n navRow.addComponents(\n new ButtonBuilder()\n .setCustomId('s:pageinfo')\n .setLabel(`Page ${page + 1}/${totalPages}`)\n .setStyle(ButtonStyle.Secondary)\n .setDisabled(true),\n )\n if (page < totalPages - 1) {\n navRow.addComponents(\n new ButtonBuilder()\n .setCustomId(`s:page:${page + 1}`)\n .setLabel('Next ▶️')\n .setStyle(ButtonStyle.Primary),\n )\n }\n rows.push(navRow)\n }\n\n return rows\n}\n\nexport async function handleSettings(\n interaction: ChatInputCommandInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n await interaction.deferReply({ ephemeral: true })\n\n const rows = buildSettingsRows(adapter)\n await interaction.editReply({\n content: '**⚙️ Settings**\\nTap to change:',\n components: rows,\n })\n}\n\nexport async function showSettingsInfo(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const rows = buildSettingsRows(adapter)\n await interaction.followUp({\n content: '**⚙️ Settings**\\nTap to change:',\n components: rows,\n ephemeral: true,\n })\n}\n\nexport async function handleSettingsButton(\n interaction: ButtonInteraction,\n adapter: DiscordAdapter,\n): Promise<void> {\n const { customId } = interaction\n\n try {\n // Toggle buttons\n if (customId.startsWith('s:toggle:')) {\n const fieldPath = customId.replace('s:toggle:', '')\n const config = adapter.core.configManager.get()\n const currentValue = getConfigValue(config, fieldPath)\n const newValue = !currentValue\n\n const updates = buildNestedUpdate(fieldPath, newValue)\n await adapter.core.configManager.save(updates, fieldPath)\n\n const toast = isHotReloadable(fieldPath)\n ? `✅ ${fieldPath} = ${newValue}`\n : `✅ ${fieldPath} = ${newValue} (restart needed)`\n\n try {\n await interaction.update({\n content: '**⚙️ Settings**\\nTap to change:',\n components: buildSettingsRows(adapter),\n })\n } catch { /* ignore */ }\n\n try { await interaction.followUp({ content: toast, ephemeral: true }) } catch { /* ignore */ }\n return\n }\n\n // Select buttons — show options\n if (customId.startsWith('s:select:')) {\n const fieldPath = customId.replace('s:select:', '')\n const config = adapter.core.configManager.get()\n const fieldDef = getSafeFields().find((f) => f.path === fieldPath)\n if (!fieldDef) return\n\n const options = resolveOptions(fieldDef, config) ?? []\n const currentValue = getConfigValue(config, fieldPath)\n\n const rows: ActionRowBuilder<ButtonBuilder>[] = []\n let currentRow = new ActionRowBuilder<ButtonBuilder>()\n let count = 0\n\n for (const opt of options) {\n const marker = opt === String(currentValue) ? ' ✓' : ''\n currentRow.addComponents(\n new ButtonBuilder()\n .setCustomId(`s:pick:${fieldPath}:${opt}`)\n .setLabel(`${opt}${marker}`.slice(0, 80))\n .setStyle(opt === String(currentValue) ? ButtonStyle.Success : ButtonStyle.Secondary),\n )\n count++\n if (count % 3 === 0) {\n rows.push(currentRow)\n currentRow = new ActionRowBuilder<ButtonBuilder>()\n }\n }\n\n if (currentRow.components.length > 0) {\n rows.push(currentRow)\n }\n\n // Add back button\n const backRow = new ActionRowBuilder<ButtonBuilder>().addComponents(\n new ButtonBuilder()\n .setCustomId('s:back')\n .setLabel('◀️ Back')\n .setStyle(ButtonStyle.Primary),\n )\n rows.push(backRow)\n\n try {\n await interaction.update({\n content: `**⚙️ ${fieldDef.displayName}**\\nSelect a value:`,\n components: rows.slice(0, 5),\n })\n } catch { /* ignore */ }\n return\n }\n\n // Pick buttons — apply selected value\n if (customId.startsWith('s:pick:')) {\n const parts = customId.replace('s:pick:', '').split(':')\n const fieldPath = parts.slice(0, -1).join(':')\n const newValue = parts[parts.length - 1]\n\n // For speech.stt.provider: check if API key is configured\n if (fieldPath === 'speech.stt.provider') {\n const config = adapter.core.configManager.get()\n const providerConfig = config.speech?.stt?.providers?.[newValue]\n if (!providerConfig?.apiKey) {\n // No API key — delegate to assistant\n const assistantSessionId = adapter.getAssistantSessionId()\n if (assistantSessionId) {\n const assistantSession = adapter.core.sessionManager.getSession(assistantSessionId)\n if (assistantSession) {\n const prompt = `User wants to enable ${newValue} as Speech-to-Text provider, but no API key is configured yet. Guide them to get a ${newValue} API key and set it up. After they provide the key, run both commands: \\`openacp config set speech.stt.providers.${newValue}.apiKey <key>\\` and \\`openacp config set speech.stt.provider ${newValue}\\``\n await assistantSession.enqueuePrompt(prompt)\n\n try {\n await interaction.update({\n content: '**⚙️ Settings**\\nTap to change:',\n components: buildSettingsRows(adapter),\n })\n } catch { /* ignore */ }\n try { await interaction.followUp({ content: '🔑 API key needed — check the Assistant thread.', ephemeral: true }) } catch { /* ignore */ }\n return\n }\n }\n\n // No assistant — just warn\n try {\n await interaction.update({\n content: '**⚙️ Settings**\\nTap to change:',\n components: buildSettingsRows(adapter),\n })\n } catch { /* ignore */ }\n try { await interaction.followUp({ content: `⚠️ Set API key first: \\`openacp config set speech.stt.providers.${newValue}.apiKey <key>\\``, ephemeral: true }) } catch { /* ignore */ }\n return\n }\n }\n\n const updates = buildNestedUpdate(fieldPath, newValue)\n await adapter.core.configManager.save(updates, fieldPath)\n\n try {\n await interaction.update({\n content: '**⚙️ Settings**\\nTap to change:',\n components: buildSettingsRows(adapter),\n })\n } catch { /* ignore */ }\n try { await interaction.followUp({ content: `✅ ${fieldPath} = ${newValue}`, ephemeral: true }) } catch { /* ignore */ }\n return\n }\n\n // Input buttons — delegate to assistant\n if (customId.startsWith('s:input:')) {\n const fieldPath = customId.replace('s:input:', '')\n const config = adapter.core.configManager.get()\n const fieldDef = getSafeFields().find((f) => f.path === fieldPath)\n if (!fieldDef) return\n\n const currentValue = getConfigValue(config, fieldPath)\n const assistantSessionId = adapter.getAssistantSessionId()\n\n if (!assistantSessionId) {\n try { await interaction.reply({ content: '⚠️ Assistant is not available.', ephemeral: true }) } catch { /* ignore */ }\n return\n }\n\n const assistantSession = adapter.core.sessionManager.getSession(assistantSessionId)\n if (!assistantSession) {\n try { await interaction.reply({ content: '⚠️ Assistant session not found.', ephemeral: true }) } catch { /* ignore */ }\n return\n }\n\n try { await interaction.deferUpdate() } catch { /* ignore */ }\n\n const prompt = `User wants to change ${fieldDef.displayName} (config path: ${fieldPath}). Current value: ${JSON.stringify(currentValue)}. Ask them for the new value and apply it using: openacp config set ${fieldPath} <value>`\n await assistantSession.enqueuePrompt(prompt)\n\n try { await interaction.followUp({ content: `Delegating to assistant — check the Assistant thread.`, ephemeral: true }) } catch { /* ignore */ }\n return\n }\n\n // Page navigation\n if (customId.startsWith('s:page:')) {\n const page = parseInt(customId.replace('s:page:', ''), 10)\n try {\n await interaction.update({\n content: '**⚙️ Settings**\\nTap to change:',\n components: buildSettingsRows(adapter, page),\n })\n } catch { /* ignore */ }\n return\n }\n\n // Back button — return to page 0\n if (customId === 's:back') {\n try {\n await interaction.update({\n content: '**⚙️ Settings**\\nTap to change:',\n components: buildSettingsRows(adapter),\n })\n } catch { /* ignore */ }\n return\n }\n\n log.warn({ customId }, '[discord-settings] Unhandled settings button')\n } catch (err) {\n log.error({ err, customId }, '[discord-settings] Settings button handler failed')\n try {\n if (!interaction.replied && !interaction.deferred) {\n await interaction.reply({ content: '❌ Settings action failed.', ephemeral: true })\n } else {\n await interaction.followUp({ content: '❌ Settings action failed.', ephemeral: true })\n }\n } catch { /* ignore */ }\n }\n}\n\nfunction buildNestedUpdate(dotPath: string, value: unknown): Record<string, unknown> {\n const parts = dotPath.split('.')\n const result: Record<string, unknown> = {}\n let target = result\n for (let i = 0; i < parts.length - 1; i++) {\n target[parts[i]] = {}\n target = target[parts[i]] as Record<string, unknown>\n }\n target[parts[parts.length - 1]] = value\n return result\n}\n"],"mappings":"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","names":[]}
|
package/dist/chunk-5RO42TWV.js
DELETED
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
// src/plugins/discord/index.ts
|
|
2
|
-
function createDiscordPlugin() {
|
|
3
|
-
let adapter = null;
|
|
4
|
-
return {
|
|
5
|
-
name: "@openacp/discord",
|
|
6
|
-
version: "1.0.0",
|
|
7
|
-
description: "Discord adapter with forum threads",
|
|
8
|
-
essential: true,
|
|
9
|
-
pluginDependencies: {
|
|
10
|
-
"@openacp/security": "^1.0.0",
|
|
11
|
-
"@openacp/notifications": "^1.0.0"
|
|
12
|
-
},
|
|
13
|
-
optionalPluginDependencies: {
|
|
14
|
-
"@openacp/speech": "^1.0.0"
|
|
15
|
-
},
|
|
16
|
-
permissions: ["services:register", "kernel:access", "events:read"],
|
|
17
|
-
async install(ctx) {
|
|
18
|
-
const { terminal, settings, legacyConfig } = ctx;
|
|
19
|
-
if (legacyConfig) {
|
|
20
|
-
const ch = legacyConfig.channels;
|
|
21
|
-
const discordCfg = ch?.discord;
|
|
22
|
-
if (discordCfg?.botToken) {
|
|
23
|
-
await settings.setAll({
|
|
24
|
-
botToken: discordCfg.botToken,
|
|
25
|
-
guildId: discordCfg.guildId,
|
|
26
|
-
forumChannelId: discordCfg.forumChannelId ?? null,
|
|
27
|
-
notificationChannelId: discordCfg.notificationChannelId ?? null,
|
|
28
|
-
assistantThreadId: discordCfg.assistantThreadId ?? null
|
|
29
|
-
});
|
|
30
|
-
terminal.log.success("Discord settings migrated from legacy config");
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
const { validateDiscordToken } = await import("./validators-6CLEZUBD.js");
|
|
35
|
-
terminal.note(
|
|
36
|
-
"1. Create app at https://discord.com/developers/applications\n2. Go to Bot > Reset Token > copy it\n3. Enable Message Content Intent (Bot > Privileged Intents)\n4. OAuth2 > URL Generator > scopes: bot + applications.commands\n5. Bot Permissions: Manage Channels, Send Messages, Manage Threads, Attach Files\n6. Open generated URL > invite bot to your server",
|
|
37
|
-
"Discord Setup"
|
|
38
|
-
);
|
|
39
|
-
let botToken = "";
|
|
40
|
-
while (true) {
|
|
41
|
-
botToken = await terminal.text({
|
|
42
|
-
message: "Bot token (from Discord Developer Portal):",
|
|
43
|
-
validate: (val) => {
|
|
44
|
-
if (!val.trim()) return "Token cannot be empty";
|
|
45
|
-
return void 0;
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
botToken = botToken.trim();
|
|
49
|
-
const spin = terminal.spinner();
|
|
50
|
-
spin.start("Validating token...");
|
|
51
|
-
const result = await validateDiscordToken(botToken);
|
|
52
|
-
if (result.ok) {
|
|
53
|
-
spin.stop(`Connected as @${result.username} (id: ${result.id})`);
|
|
54
|
-
break;
|
|
55
|
-
}
|
|
56
|
-
spin.fail(result.error);
|
|
57
|
-
const action = await terminal.select({
|
|
58
|
-
message: "What to do?",
|
|
59
|
-
options: [
|
|
60
|
-
{ label: "Re-enter token", value: "retry" },
|
|
61
|
-
{ label: "Use as-is (skip validation)", value: "skip" }
|
|
62
|
-
]
|
|
63
|
-
});
|
|
64
|
-
if (action === "skip") break;
|
|
65
|
-
}
|
|
66
|
-
const guildId = await terminal.text({
|
|
67
|
-
message: "Guild (server) ID:",
|
|
68
|
-
validate: (val) => {
|
|
69
|
-
const trimmed = val.trim();
|
|
70
|
-
if (!trimmed) return "Guild ID cannot be empty";
|
|
71
|
-
if (!/^\d{17,20}$/.test(trimmed)) return "Guild ID must be a numeric Discord snowflake (17-20 digits)";
|
|
72
|
-
return void 0;
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
await settings.setAll({
|
|
76
|
-
botToken,
|
|
77
|
-
guildId: guildId.trim(),
|
|
78
|
-
forumChannelId: null,
|
|
79
|
-
notificationChannelId: null,
|
|
80
|
-
assistantThreadId: null
|
|
81
|
-
});
|
|
82
|
-
terminal.log.success("Discord settings saved");
|
|
83
|
-
},
|
|
84
|
-
async configure(ctx) {
|
|
85
|
-
const { terminal, settings } = ctx;
|
|
86
|
-
const current = await settings.getAll();
|
|
87
|
-
const choice = await terminal.select({
|
|
88
|
-
message: "What to configure?",
|
|
89
|
-
options: [
|
|
90
|
-
{ value: "token", label: "Change bot token" },
|
|
91
|
-
{ value: "guildId", label: "Change guild ID" },
|
|
92
|
-
{ value: "done", label: "Done" }
|
|
93
|
-
]
|
|
94
|
-
});
|
|
95
|
-
if (choice === "token") {
|
|
96
|
-
const token = await terminal.text({
|
|
97
|
-
message: "New bot token:",
|
|
98
|
-
validate: (v) => !v.trim() ? "Token cannot be empty" : void 0
|
|
99
|
-
});
|
|
100
|
-
await settings.set("botToken", token.trim());
|
|
101
|
-
terminal.log.success("Bot token updated");
|
|
102
|
-
} else if (choice === "guildId") {
|
|
103
|
-
const val = await terminal.text({
|
|
104
|
-
message: "New guild ID:",
|
|
105
|
-
defaultValue: current.guildId ?? "",
|
|
106
|
-
validate: (v) => {
|
|
107
|
-
const trimmed = v.trim();
|
|
108
|
-
if (!trimmed) return "Guild ID cannot be empty";
|
|
109
|
-
if (!/^\d{17,20}$/.test(trimmed)) return "Guild ID must be a numeric Discord snowflake (17-20 digits)";
|
|
110
|
-
return void 0;
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
await settings.set("guildId", val.trim());
|
|
114
|
-
terminal.log.success("Guild ID updated");
|
|
115
|
-
}
|
|
116
|
-
},
|
|
117
|
-
async uninstall(ctx, opts) {
|
|
118
|
-
if (opts.purge) {
|
|
119
|
-
await ctx.settings.clear();
|
|
120
|
-
ctx.terminal.log.success("Discord settings cleared");
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
async setup(ctx) {
|
|
124
|
-
const config = ctx.pluginConfig;
|
|
125
|
-
if (!config.botToken || !config.guildId) {
|
|
126
|
-
ctx.log.info("Discord disabled (missing botToken or guildId)");
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const { DiscordAdapter } = await import("./adapter-IZNL6AK2.js");
|
|
130
|
-
adapter = new DiscordAdapter(ctx.core, {
|
|
131
|
-
...config,
|
|
132
|
-
enabled: true,
|
|
133
|
-
maxMessageLength: 2e3
|
|
134
|
-
});
|
|
135
|
-
ctx.registerService("adapter:discord", adapter);
|
|
136
|
-
ctx.log.info("Discord adapter registered");
|
|
137
|
-
},
|
|
138
|
-
async teardown() {
|
|
139
|
-
if (adapter) {
|
|
140
|
-
await adapter.stop();
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
var discord_default = createDiscordPlugin();
|
|
146
|
-
|
|
147
|
-
export {
|
|
148
|
-
discord_default
|
|
149
|
-
};
|
|
150
|
-
//# sourceMappingURL=chunk-5RO42TWV.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/plugins/discord/index.ts"],"sourcesContent":["import type { OpenACPPlugin, InstallContext } from '../../core/plugin/types.js'\nimport type { OpenACPCore } from '../../core/core.js'\nimport type { DiscordChannelConfig } from './types.js'\n\nfunction createDiscordPlugin(): OpenACPPlugin {\n let adapter: { stop(): Promise<void> } | null = null\n\n return {\n name: '@openacp/discord',\n version: '1.0.0',\n description: 'Discord adapter with forum threads',\n essential: true,\n pluginDependencies: {\n '@openacp/security': '^1.0.0',\n '@openacp/notifications': '^1.0.0',\n },\n optionalPluginDependencies: {\n '@openacp/speech': '^1.0.0',\n },\n permissions: ['services:register', 'kernel:access', 'events:read'],\n\n async install(ctx: InstallContext) {\n const { terminal, settings, legacyConfig } = ctx\n\n // Migrate from legacy config if present\n if (legacyConfig) {\n const ch = legacyConfig.channels as Record<string, unknown> | undefined\n const discordCfg = ch?.discord as Record<string, unknown> | undefined\n if (discordCfg?.botToken) {\n await settings.setAll({\n botToken: discordCfg.botToken,\n guildId: discordCfg.guildId,\n forumChannelId: discordCfg.forumChannelId ?? null,\n notificationChannelId: discordCfg.notificationChannelId ?? null,\n assistantThreadId: discordCfg.assistantThreadId ?? null,\n })\n terminal.log.success('Discord settings migrated from legacy config')\n return\n }\n }\n\n // Interactive setup via terminal\n const { validateDiscordToken } = await import('./validators.js')\n\n terminal.note(\n '1. Create app at https://discord.com/developers/applications\\n' +\n '2. Go to Bot > Reset Token > copy it\\n' +\n '3. Enable Message Content Intent (Bot > Privileged Intents)\\n' +\n '4. OAuth2 > URL Generator > scopes: bot + applications.commands\\n' +\n '5. Bot Permissions: Manage Channels, Send Messages, Manage Threads, Attach Files\\n' +\n '6. Open generated URL > invite bot to your server',\n 'Discord Setup',\n )\n\n let botToken = ''\n while (true) {\n botToken = await terminal.text({\n message: 'Bot token (from Discord Developer Portal):',\n validate: (val) => {\n if (!val.trim()) return 'Token cannot be empty'\n return undefined\n },\n })\n botToken = botToken.trim()\n\n const spin = terminal.spinner()\n spin.start('Validating token...')\n const result = await validateDiscordToken(botToken)\n if (result.ok) {\n spin.stop(`Connected as @${result.username} (id: ${result.id})`)\n break\n }\n spin.fail(result.error)\n const action = await terminal.select({\n message: 'What to do?',\n options: [\n { label: 'Re-enter token', value: 'retry' },\n { label: 'Use as-is (skip validation)', value: 'skip' },\n ],\n })\n if (action === 'skip') break\n }\n\n const guildId = await terminal.text({\n message: 'Guild (server) ID:',\n validate: (val) => {\n const trimmed = val.trim()\n if (!trimmed) return 'Guild ID cannot be empty'\n if (!/^\\d{17,20}$/.test(trimmed)) return 'Guild ID must be a numeric Discord snowflake (17-20 digits)'\n return undefined\n },\n })\n\n await settings.setAll({\n botToken,\n guildId: guildId.trim(),\n forumChannelId: null,\n notificationChannelId: null,\n assistantThreadId: null,\n })\n terminal.log.success('Discord settings saved')\n },\n\n async configure(ctx: InstallContext) {\n const { terminal, settings } = ctx\n const current = await settings.getAll()\n\n const choice = await terminal.select({\n message: 'What to configure?',\n options: [\n { value: 'token', label: 'Change bot token' },\n { value: 'guildId', label: 'Change guild ID' },\n { value: 'done', label: 'Done' },\n ],\n })\n\n if (choice === 'token') {\n const token = await terminal.text({\n message: 'New bot token:',\n validate: (v) => (!v.trim() ? 'Token cannot be empty' : undefined),\n })\n await settings.set('botToken', token.trim())\n terminal.log.success('Bot token updated')\n } else if (choice === 'guildId') {\n const val = await terminal.text({\n message: 'New guild ID:',\n defaultValue: (current.guildId as string) ?? '',\n validate: (v) => {\n const trimmed = v.trim()\n if (!trimmed) return 'Guild ID cannot be empty'\n if (!/^\\d{17,20}$/.test(trimmed)) return 'Guild ID must be a numeric Discord snowflake (17-20 digits)'\n return undefined\n },\n })\n await settings.set('guildId', val.trim())\n terminal.log.success('Guild ID updated')\n }\n },\n\n async uninstall(ctx: InstallContext, opts: { purge: boolean }) {\n if (opts.purge) {\n await ctx.settings.clear()\n ctx.terminal.log.success('Discord settings cleared')\n }\n },\n\n async setup(ctx) {\n const config = ctx.pluginConfig as Record<string, unknown>\n if (!config.botToken || !config.guildId) {\n ctx.log.info('Discord disabled (missing botToken or guildId)')\n return\n }\n\n const { DiscordAdapter } = await import('./adapter.js')\n // config is a Record<string, unknown> from pluginConfig; at runtime it\n // contains all DiscordChannelConfig fields populated from the migrated config.\n adapter = new DiscordAdapter(ctx.core as OpenACPCore, {\n ...config,\n enabled: true,\n maxMessageLength: 2000,\n } as unknown as DiscordChannelConfig)\n\n ctx.registerService('adapter:discord', adapter)\n ctx.log.info('Discord adapter registered')\n },\n\n async teardown() {\n if (adapter) {\n await adapter.stop()\n }\n },\n }\n}\n\nexport default createDiscordPlugin()\n"],"mappings":"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","names":[]}
|