@songsid/agend 2.0.11-beta.3 → 2.0.11-beta.31
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/channel/adapters/discord.d.ts +2 -0
- package/dist/channel/adapters/discord.js +40 -34
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/factory.d.ts +4 -0
- package/dist/channel/factory.js +1 -0
- package/dist/channel/factory.js.map +1 -1
- package/dist/channel/mcp-tools.js +3 -0
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/tool-router.js +29 -2
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/classic-channel-manager.d.ts +59 -12
- package/dist/classic-channel-manager.js +121 -30
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +94 -7
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.d.ts +20 -0
- package/dist/config-validator.js +154 -0
- package/dist/config-validator.js.map +1 -0
- package/dist/fleet-manager.d.ts +29 -5
- package/dist/fleet-manager.js +451 -233
- package/dist/fleet-manager.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +1 -1
- package/dist/instance-lifecycle.js +7 -6
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.d.ts +19 -0
- package/dist/locale.js +137 -0
- package/dist/locale.js.map +1 -0
- package/dist/outbound-schemas.d.ts +1 -0
- package/dist/outbound-schemas.js +1 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/quickstart.js +252 -16
- package/dist/quickstart.js.map +1 -1
- package/dist/scheduler/scheduler.d.ts +1 -1
- package/dist/scheduler/scheduler.js +15 -4
- package/dist/scheduler/scheduler.js.map +1 -1
- package/dist/service-installer.d.ts +1 -0
- package/dist/service-installer.js +2 -1
- package/dist/service-installer.js.map +1 -1
- package/dist/settings-api.d.ts +63 -0
- package/dist/settings-api.js +331 -0
- package/dist/settings-api.js.map +1 -0
- package/dist/topic-commands.d.ts +14 -0
- package/dist/topic-commands.js +78 -16
- package/dist/topic-commands.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/ui/settings.html +612 -0
- package/dist/ui/view.html +528 -0
- package/dist/view-api.d.ts +29 -0
- package/dist/view-api.js +370 -0
- package/dist/view-api.js.map +1 -0
- package/package.json +1 -1
- package/templates/systemd.service.ejs +2 -1
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings Web API (`/settings`) — CRUD over fleet.yaml + classicBot.yaml.
|
|
3
|
+
*
|
|
4
|
+
* GET /settings → static page
|
|
5
|
+
* GET /api/settings/fleet → fleet config as JSON
|
|
6
|
+
* GET /api/settings/classic → classicBot.yaml as JSON
|
|
7
|
+
* PUT /api/settings/fleet/defaults → merge fleet defaults
|
|
8
|
+
* PUT /api/settings/fleet/channels → replace channels[]
|
|
9
|
+
* POST /api/settings/fleet/instances/:name → create instance
|
|
10
|
+
* PATCH/api/settings/fleet/instances/:name → merge into instance
|
|
11
|
+
* DELETE /api/settings/fleet/instances/:name → remove instance
|
|
12
|
+
* PUT /api/settings/classic/defaults → merge classic defaults
|
|
13
|
+
* POST /api/settings/reload → SIGHUP hot-reload
|
|
14
|
+
*
|
|
15
|
+
* Auth: all routes require the web.token — enforced by the global web-token gate
|
|
16
|
+
* in fleet-manager BEFORE this handler runs (settings paths are not exempt), so
|
|
17
|
+
* no per-route auth is repeated here.
|
|
18
|
+
*
|
|
19
|
+
* Writes are validated first (config-validator): any error → 400 and nothing is
|
|
20
|
+
* written; warnings are non-blocking and returned alongside the result.
|
|
21
|
+
*/
|
|
22
|
+
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
23
|
+
import type { Logger } from "./logger.js";
|
|
24
|
+
import type { FleetConfig } from "./types.js";
|
|
25
|
+
/** Reserved schedule target for the fleet auto-update job (intercepted in handleScheduleTrigger). */
|
|
26
|
+
export declare const UPDATE_SCHEDULE_TARGET = "__fleet_update__";
|
|
27
|
+
interface SchedulerLike {
|
|
28
|
+
list(target?: string): Array<{
|
|
29
|
+
id: string;
|
|
30
|
+
cron: string;
|
|
31
|
+
message: string;
|
|
32
|
+
enabled: boolean;
|
|
33
|
+
last_triggered_at: string | null;
|
|
34
|
+
}>;
|
|
35
|
+
create(p: {
|
|
36
|
+
cron: string;
|
|
37
|
+
message: string;
|
|
38
|
+
source: string;
|
|
39
|
+
target: string;
|
|
40
|
+
reply_chat_id: string;
|
|
41
|
+
reply_thread_id: string | null;
|
|
42
|
+
label?: string;
|
|
43
|
+
}, allowReserved?: boolean): {
|
|
44
|
+
id: string;
|
|
45
|
+
};
|
|
46
|
+
update(id: string, p: {
|
|
47
|
+
cron?: string;
|
|
48
|
+
message?: string;
|
|
49
|
+
enabled?: boolean;
|
|
50
|
+
}): unknown;
|
|
51
|
+
}
|
|
52
|
+
export interface SettingsApiContext {
|
|
53
|
+
fleetConfig: FleetConfig | null;
|
|
54
|
+
configPath: string | null;
|
|
55
|
+
dataDir: string;
|
|
56
|
+
logger: Logger;
|
|
57
|
+
saveFleetConfig(): void;
|
|
58
|
+
scheduler?: SchedulerLike;
|
|
59
|
+
currentVersion?: string;
|
|
60
|
+
}
|
|
61
|
+
export declare function isSettingsPath(path: string): boolean;
|
|
62
|
+
export declare function handleSettingsRequest(req: IncomingMessage, res: ServerResponse, url: URL, ctx: SettingsApiContext): boolean;
|
|
63
|
+
export {};
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
2
|
+
import { join, dirname } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import yaml from "js-yaml";
|
|
5
|
+
import { validateFleetConfig, validateClassicBotConfig } from "./config-validator.js";
|
|
6
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
/** Reserved schedule target for the fleet auto-update job (intercepted in handleScheduleTrigger). */
|
|
8
|
+
export const UPDATE_SCHEDULE_TARGET = "__fleet_update__";
|
|
9
|
+
function json(res, code, body) {
|
|
10
|
+
res.writeHead(code, { "Content-Type": "application/json" });
|
|
11
|
+
res.end(JSON.stringify(body));
|
|
12
|
+
}
|
|
13
|
+
function readBody(req, maxBytes) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const chunks = [];
|
|
16
|
+
let size = 0;
|
|
17
|
+
req.on("data", (c) => {
|
|
18
|
+
size += c.length;
|
|
19
|
+
if (size > maxBytes) {
|
|
20
|
+
reject(new Error("payload too large"));
|
|
21
|
+
req.destroy();
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
chunks.push(c);
|
|
25
|
+
});
|
|
26
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
27
|
+
req.on("error", reject);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
export function isSettingsPath(path) {
|
|
31
|
+
return path === "/settings" || path.startsWith("/api/settings/");
|
|
32
|
+
}
|
|
33
|
+
const classicPath = (ctx) => join(ctx.dataDir, "classicBot.yaml");
|
|
34
|
+
function readClassic(ctx) {
|
|
35
|
+
const p = classicPath(ctx);
|
|
36
|
+
if (!existsSync(p))
|
|
37
|
+
return {};
|
|
38
|
+
try {
|
|
39
|
+
return yaml.load(readFileSync(p, "utf-8")) ?? {};
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
ctx.logger.warn({ err }, "settings: failed to parse classicBot.yaml");
|
|
43
|
+
return {};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const issueKey = (i) => i.path + "\u0000" + i.message;
|
|
47
|
+
/**
|
|
48
|
+
* Reject a write only if it INTRODUCES new validation errors. Pre-existing
|
|
49
|
+
* errors elsewhere in the config (common while a fleet is being assembled)
|
|
50
|
+
* must not block an unrelated edit — otherwise one bad channel would lock the
|
|
51
|
+
* user out of saving anything, which reads as "save didn't persist".
|
|
52
|
+
* Returns true (and responds 400) when the edit adds errors.
|
|
53
|
+
*/
|
|
54
|
+
function rejectIfWorse(res, before, after) {
|
|
55
|
+
const had = new Set(before.errors.map(issueKey));
|
|
56
|
+
const introduced = after.errors.filter(e => !had.has(issueKey(e)));
|
|
57
|
+
if (introduced.length) {
|
|
58
|
+
json(res, 400, { ok: false, errors: introduced, warnings: after.warnings });
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
/** Warnings to surface on a successful save: new warnings + any pre-existing errors (informational). */
|
|
64
|
+
function saveWarnings(before, after) {
|
|
65
|
+
const had = new Set(before.errors.map(issueKey));
|
|
66
|
+
const preExisting = after.errors.filter(e => had.has(issueKey(e))).map(e => ({ path: e.path, message: "pre-existing: " + e.message }));
|
|
67
|
+
return [...after.warnings, ...preExisting];
|
|
68
|
+
}
|
|
69
|
+
export function handleSettingsRequest(req, res, url, ctx) {
|
|
70
|
+
const path = url.pathname;
|
|
71
|
+
if (!isSettingsPath(path))
|
|
72
|
+
return false;
|
|
73
|
+
const method = req.method ?? "GET";
|
|
74
|
+
// ── Static page ──
|
|
75
|
+
if (method === "GET" && path === "/settings") {
|
|
76
|
+
try {
|
|
77
|
+
const html = readFileSync(join(__dirname, "ui", "settings.html"), "utf-8");
|
|
78
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
79
|
+
res.end(html);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
json(res, 500, { error: "settings.html not found" });
|
|
83
|
+
}
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
// ── Reads ──
|
|
87
|
+
if (method === "GET" && path === "/api/settings/fleet") {
|
|
88
|
+
json(res, 200, ctx.fleetConfig ?? {});
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (method === "GET" && path === "/api/settings/classic") {
|
|
92
|
+
json(res, 200, readClassic(ctx));
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
// ── Auto-update schedule (backed by schedules.db via a reserved target) ──
|
|
96
|
+
if (path === "/api/settings/update-schedule") {
|
|
97
|
+
const existing = ctx.scheduler?.list(UPDATE_SCHEDULE_TARGET)[0];
|
|
98
|
+
if (method === "GET") {
|
|
99
|
+
json(res, 200, {
|
|
100
|
+
enabled: existing?.enabled ?? false,
|
|
101
|
+
cron: existing?.cron ?? "0 4 * * *",
|
|
102
|
+
channel: existing?.message === "beta" ? "beta" : "stable",
|
|
103
|
+
currentVersion: ctx.currentVersion ?? null,
|
|
104
|
+
lastChecked: existing?.last_triggered_at ?? null,
|
|
105
|
+
});
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (method === "PUT") {
|
|
109
|
+
if (!ctx.scheduler) {
|
|
110
|
+
json(res, 503, { error: "scheduler not available" });
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
readBody(req, 64 * 1024).then(buf => {
|
|
114
|
+
let body;
|
|
115
|
+
try {
|
|
116
|
+
body = JSON.parse(buf.toString("utf-8") || "{}");
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return json(res, 400, { error: "invalid JSON" });
|
|
120
|
+
}
|
|
121
|
+
const cron = typeof body.cron === "string" && body.cron.trim() ? body.cron.trim() : "0 4 * * *";
|
|
122
|
+
const channel = body.channel === "beta" ? "beta" : "stable";
|
|
123
|
+
const enabled = body.enabled === true;
|
|
124
|
+
try {
|
|
125
|
+
if (existing)
|
|
126
|
+
ctx.scheduler.update(existing.id, { cron, message: channel, enabled });
|
|
127
|
+
else {
|
|
128
|
+
const created = ctx.scheduler.create({ cron, message: channel, source: "system", target: UPDATE_SCHEDULE_TARGET, reply_chat_id: "", reply_thread_id: null, label: "Auto Update" }, true);
|
|
129
|
+
if (!enabled)
|
|
130
|
+
ctx.scheduler.update(created.id, { enabled: false });
|
|
131
|
+
}
|
|
132
|
+
json(res, 200, { ok: true, enabled, cron, channel });
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
json(res, 400, { error: e.message });
|
|
136
|
+
}
|
|
137
|
+
}).catch(() => json(res, 400, { error: "bad request" }));
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
// Everything below mutates — needs an in-memory fleet config.
|
|
142
|
+
const cfg = ctx.fleetConfig;
|
|
143
|
+
// ── Fleet defaults ──
|
|
144
|
+
if (method === "PUT" && path === "/api/settings/fleet/defaults") {
|
|
145
|
+
if (!cfg) {
|
|
146
|
+
json(res, 503, { error: "fleet not loaded" });
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
readBody(req, 512 * 1024).then(buf => {
|
|
150
|
+
let body;
|
|
151
|
+
try {
|
|
152
|
+
body = JSON.parse(buf.toString("utf-8") || "{}");
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return json(res, 400, { error: "invalid JSON" });
|
|
156
|
+
}
|
|
157
|
+
if (typeof body !== "object" || body === null || Array.isArray(body))
|
|
158
|
+
return json(res, 400, { error: "expected an object" });
|
|
159
|
+
const merged = { ...cfg.defaults, ...body };
|
|
160
|
+
const before = validateFleetConfig(cfg);
|
|
161
|
+
const after = validateFleetConfig({ ...cfg, defaults: merged });
|
|
162
|
+
if (rejectIfWorse(res, before, after))
|
|
163
|
+
return;
|
|
164
|
+
cfg.defaults = merged;
|
|
165
|
+
ctx.saveFleetConfig();
|
|
166
|
+
json(res, 200, { ok: true, warnings: saveWarnings(before, after) });
|
|
167
|
+
}).catch(() => json(res, 400, { error: "bad request" }));
|
|
168
|
+
return true;
|
|
169
|
+
}
|
|
170
|
+
// ── Fleet channels (full replace) ──
|
|
171
|
+
if (method === "PUT" && path === "/api/settings/fleet/channels") {
|
|
172
|
+
if (!cfg) {
|
|
173
|
+
json(res, 503, { error: "fleet not loaded" });
|
|
174
|
+
return true;
|
|
175
|
+
}
|
|
176
|
+
readBody(req, 512 * 1024).then(buf => {
|
|
177
|
+
let body;
|
|
178
|
+
try {
|
|
179
|
+
body = JSON.parse(buf.toString("utf-8") || "[]");
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return json(res, 400, { error: "invalid JSON" });
|
|
183
|
+
}
|
|
184
|
+
if (!Array.isArray(body))
|
|
185
|
+
return json(res, 400, { error: "expected an array of channels" });
|
|
186
|
+
const next = { ...cfg, channels: body };
|
|
187
|
+
delete next.channel; // channels[] supersedes the legacy single channel
|
|
188
|
+
const before = validateFleetConfig(cfg);
|
|
189
|
+
const after = validateFleetConfig(next);
|
|
190
|
+
if (rejectIfWorse(res, before, after))
|
|
191
|
+
return;
|
|
192
|
+
cfg.channels = body;
|
|
193
|
+
delete cfg.channel;
|
|
194
|
+
ctx.saveFleetConfig();
|
|
195
|
+
json(res, 200, { ok: true, warnings: saveWarnings(before, after) });
|
|
196
|
+
}).catch(() => json(res, 400, { error: "bad request" }));
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
// ── Classic defaults ──
|
|
200
|
+
if (method === "PUT" && path === "/api/settings/classic/defaults") {
|
|
201
|
+
readBody(req, 512 * 1024).then(buf => {
|
|
202
|
+
let body;
|
|
203
|
+
try {
|
|
204
|
+
body = JSON.parse(buf.toString("utf-8") || "{}");
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return json(res, 400, { error: "invalid JSON" });
|
|
208
|
+
}
|
|
209
|
+
if (typeof body !== "object" || body === null || Array.isArray(body))
|
|
210
|
+
return json(res, 400, { error: "expected an object" });
|
|
211
|
+
const classic = readClassic(ctx);
|
|
212
|
+
const merged = { ...(classic.defaults ?? {}), ...body };
|
|
213
|
+
const before = validateClassicBotConfig(classic);
|
|
214
|
+
const after = validateClassicBotConfig({ ...classic, defaults: merged });
|
|
215
|
+
if (rejectIfWorse(res, before, after))
|
|
216
|
+
return;
|
|
217
|
+
classic.defaults = merged;
|
|
218
|
+
writeFileSync(classicPath(ctx), yaml.dump(classic, { lineWidth: -1 }));
|
|
219
|
+
ctx.logger.info("settings: updated classicBot defaults");
|
|
220
|
+
json(res, 200, { ok: true, warnings: saveWarnings(before, after) });
|
|
221
|
+
}).catch(() => json(res, 400, { error: "bad request" }));
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
// ── Reload (SIGHUP) ──
|
|
225
|
+
if (method === "POST" && path === "/api/settings/reload") {
|
|
226
|
+
ctx.logger.info("settings: reload requested — sending SIGHUP");
|
|
227
|
+
try {
|
|
228
|
+
process.kill(process.pid, "SIGHUP");
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
ctx.logger.warn({ err }, "settings: SIGHUP failed");
|
|
232
|
+
}
|
|
233
|
+
json(res, 200, { ok: true });
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
// ── Instances (create / patch / delete) ──
|
|
237
|
+
const validName = (n) => !!n && /^[^\\/\x00]+$/.test(n);
|
|
238
|
+
// Create-or-merge an instance, blocking only on newly-introduced errors.
|
|
239
|
+
const commitInstance = (name, exists, body) => {
|
|
240
|
+
if (typeof body !== "object" || body === null || Array.isArray(body)) {
|
|
241
|
+
json(res, 400, { error: "expected an object" });
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
const base = (exists ? cfg.instances[name] : {});
|
|
245
|
+
const mergedInst = { ...base, ...body };
|
|
246
|
+
const before = validateFleetConfig(cfg);
|
|
247
|
+
const after = validateFleetConfig({ ...cfg, instances: { ...cfg.instances, [name]: mergedInst } });
|
|
248
|
+
if (rejectIfWorse(res, before, after))
|
|
249
|
+
return;
|
|
250
|
+
cfg.instances[name] = mergedInst;
|
|
251
|
+
ctx.saveFleetConfig();
|
|
252
|
+
json(res, 200, { ok: true, warnings: saveWarnings(before, after) });
|
|
253
|
+
};
|
|
254
|
+
// POST /api/settings/fleet/instances — create, name taken from the body.
|
|
255
|
+
if (method === "POST" && path === "/api/settings/fleet/instances") {
|
|
256
|
+
if (!cfg) {
|
|
257
|
+
json(res, 503, { error: "fleet not loaded" });
|
|
258
|
+
return true;
|
|
259
|
+
}
|
|
260
|
+
readBody(req, 512 * 1024).then(buf => {
|
|
261
|
+
let body;
|
|
262
|
+
try {
|
|
263
|
+
body = JSON.parse(buf.toString("utf-8") || "{}");
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return json(res, 400, { error: "invalid JSON" });
|
|
267
|
+
}
|
|
268
|
+
if (typeof body !== "object" || body === null || Array.isArray(body))
|
|
269
|
+
return json(res, 400, { error: "expected an object" });
|
|
270
|
+
const name = typeof body.name === "string" ? body.name.trim() : "";
|
|
271
|
+
if (!validName(name))
|
|
272
|
+
return json(res, 400, { error: "missing or invalid instance name (provide `name` in the body)" });
|
|
273
|
+
if (cfg.instances[name])
|
|
274
|
+
return json(res, 409, { error: "instance already exists" });
|
|
275
|
+
const { name: _n, ...instBody } = body;
|
|
276
|
+
commitInstance(name, false, instBody);
|
|
277
|
+
}).catch(() => json(res, 400, { error: "bad request" }));
|
|
278
|
+
return true;
|
|
279
|
+
}
|
|
280
|
+
// /api/settings/fleet/instances/:name — create / patch / delete.
|
|
281
|
+
const instPrefix = "/api/settings/fleet/instances/";
|
|
282
|
+
if (path.startsWith(instPrefix)) {
|
|
283
|
+
if (!cfg) {
|
|
284
|
+
json(res, 503, { error: "fleet not loaded" });
|
|
285
|
+
return true;
|
|
286
|
+
}
|
|
287
|
+
const name = decodeURIComponent(path.slice(instPrefix.length));
|
|
288
|
+
if (!validName(name)) {
|
|
289
|
+
json(res, 400, { error: "invalid instance name" });
|
|
290
|
+
return true;
|
|
291
|
+
}
|
|
292
|
+
if (method === "DELETE") {
|
|
293
|
+
if (!cfg.instances[name]) {
|
|
294
|
+
json(res, 404, { error: "instance not found" });
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
// DELETE never blocks on validation; surface any resulting warnings.
|
|
298
|
+
delete cfg.instances[name];
|
|
299
|
+
ctx.saveFleetConfig();
|
|
300
|
+
json(res, 200, { ok: true, warnings: validateFleetConfig(cfg).warnings });
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
if (method === "POST" || method === "PATCH") {
|
|
304
|
+
const exists = !!cfg.instances[name];
|
|
305
|
+
if (method === "POST" && exists) {
|
|
306
|
+
json(res, 409, { error: "instance already exists" });
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
if (method === "PATCH" && !exists) {
|
|
310
|
+
json(res, 404, { error: "instance not found" });
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
readBody(req, 512 * 1024).then(buf => {
|
|
314
|
+
let body;
|
|
315
|
+
try {
|
|
316
|
+
body = JSON.parse(buf.toString("utf-8") || "{}");
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return json(res, 400, { error: "invalid JSON" });
|
|
320
|
+
}
|
|
321
|
+
commitInstance(name, exists, body);
|
|
322
|
+
}).catch(() => json(res, 400, { error: "bad request" }));
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
json(res, 405, { error: "method not allowed" });
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
328
|
+
json(res, 404, { error: "not found" });
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
//# sourceMappingURL=settings-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings-api.js","sourceRoot":"","sources":["../src/settings-api.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,IAAI,MAAM,SAAS,CAAC;AAG3B,OAAO,EAAE,mBAAmB,EAAE,wBAAwB,EAAyB,MAAM,uBAAuB,CAAC;AAE7G,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAE1D,qGAAqG;AACrG,MAAM,CAAC,MAAM,sBAAsB,GAAG,kBAAkB,CAAC;AAkBzD,SAAS,IAAI,CAAC,GAAmB,EAAE,IAAY,EAAE,IAAa;IAC5D,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;IAC5D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;AAChC,CAAC;AAED,SAAS,QAAQ,CAAC,GAAoB,EAAE,QAAgB;IACtD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,MAAM,GAAa,EAAE,CAAC;QAC5B,IAAI,IAAI,GAAG,CAAC,CAAC;QACb,GAAG,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,CAAS,EAAE,EAAE;YAC3B,IAAI,IAAI,CAAC,CAAC,MAAM,CAAC;YACjB,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;gBAAC,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC,CAAC;gBAAC,GAAG,CAAC,OAAO,EAAE,CAAC;gBAAC,OAAO;YAAC,CAAC;YACvF,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACpD,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,GAAuB,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,iBAAiB,CAAC,CAAC;AAEtF,SAAS,WAAW,CAAC,GAAuB;IAC1C,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9B,IAAI,CAAC;QAAC,OAAQ,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC,CAA6B,IAAI,EAAE,CAAC;IAAC,CAAC;IACtF,OAAO,GAAG,EAAE,CAAC;QAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,2CAA2C,CAAC,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;AACnG,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,CAAoC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,QAAQ,GAAG,CAAC,CAAC,OAAO,CAAC;AAEzF;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,GAAmB,EAAE,MAAwB,EAAE,KAAuB;IAC3F,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;QACtB,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC5E,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wGAAwG;AACxG,SAAS,YAAY,CAAC,MAAwB,EAAE,KAAuB;IACrE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,gBAAgB,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACvI,OAAO,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,GAAG,WAAW,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,GAAoB,EACpB,GAAmB,EACnB,GAAQ,EACR,GAAuB;IAEvB,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC;IAC1B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,KAAK,CAAC;IAEnC,oBAAoB;IACpB,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QAC7C,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;YAC3E,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,CAAC,CAAC;YACnE,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,cAAc;IACd,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI,KAAK,qBAAqB,EAAE,CAAC;QACvD,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;QACzD,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4EAA4E;IAC5E,IAAI,IAAI,KAAK,+BAA+B,EAAE,CAAC;QAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;QAChE,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE;gBACb,OAAO,EAAE,QAAQ,EAAE,OAAO,IAAI,KAAK;gBACnC,IAAI,EAAE,QAAQ,EAAE,IAAI,IAAI,WAAW;gBACnC,OAAO,EAAE,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;gBACzD,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,IAAI;gBAC1C,WAAW,EAAE,QAAQ,EAAE,iBAAiB,IAAI,IAAI;aACjD,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;YACrB,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC;gBAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;gBAAC,OAAO,IAAI,CAAC;YAAC,CAAC;YAC1F,QAAQ,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;gBAClC,IAAI,IAA4D,CAAC;gBACjE,IAAI,CAAC;oBAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC;oBAAC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;gBAAC,CAAC;gBACrH,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC;gBAChG,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAC5D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,KAAK,IAAI,CAAC;gBACtC,IAAI,CAAC;oBACH,IAAI,QAAQ;wBAAE,GAAG,CAAC,SAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;yBACjF,CAAC;wBACJ,MAAM,OAAO,GAAG,GAAG,CAAC,SAAU,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,sBAAsB,EAAE,aAAa,EAAE,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,KAAK,EAAE,aAAa,EAAE,EAAE,IAAI,CAAC,CAAC;wBAC1L,IAAI,CAAC,OAAO;4BAAE,GAAG,CAAC,SAAU,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;oBACtE,CAAC;oBACD,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;gBACvD,CAAC;gBAAC,OAAO,CAAC,EAAE,CAAC;oBAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAG,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;gBAAC,CAAC;YAClE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;YACzD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,8DAA8D;IAC9D,MAAM,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC;IAE5B,uBAAuB;IACvB,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI,KAAK,8BAA8B,EAAE,CAAC;QAChE,IAAI,CAAC,GAAG,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QACzE,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YACnC,IAAI,IAA6B,CAAC;YAClC,IAAI,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;YAAC,CAAC;YACrH,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;YAC7H,MAAM,MAAM,GAAG,EAAE,GAAG,GAAG,CAAC,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,mBAAmB,CAAC,EAAE,GAAG,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;YAChE,IAAI,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC;gBAAE,OAAO;YAC9C,GAAG,CAAC,QAAQ,GAAG,MAA6B,CAAC;YAC7C,GAAG,CAAC,eAAe,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,sCAAsC;IACtC,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI,KAAK,8BAA8B,EAAE,CAAC;QAChE,IAAI,CAAC,GAAG,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QACzE,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YACnC,IAAI,IAAa,CAAC;YAClB,IAAI,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;YAAC,CAAC;YACrH,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;YAC5F,MAAM,IAAI,GAAG,EAAE,GAAG,GAAG,EAAE,QAAQ,EAAE,IAA+B,EAAE,CAAC;YACnE,OAAQ,IAA8B,CAAC,OAAO,CAAC,CAAC,kDAAkD;YAClG,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;YACxC,MAAM,KAAK,GAAG,mBAAmB,CAAC,IAAI,CAAC,CAAC;YACxC,IAAI,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC;gBAAE,OAAO;YAC9C,GAAG,CAAC,QAAQ,GAAG,IAA+B,CAAC;YAC/C,OAAQ,GAA6B,CAAC,OAAO,CAAC;YAC9C,GAAG,CAAC,eAAe,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,yBAAyB;IACzB,IAAI,MAAM,KAAK,KAAK,IAAI,IAAI,KAAK,gCAAgC,EAAE,CAAC;QAClE,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YACnC,IAAI,IAA6B,CAAC;YAClC,IAAI,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;YAAC,CAAC;YACrH,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;YAC7H,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;YACjC,MAAM,MAAM,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,QAAmC,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC;YACnF,MAAM,MAAM,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;YACjD,MAAM,KAAK,GAAG,wBAAwB,CAAC,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;YACzE,IAAI,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC;gBAAE,OAAO;YAC9C,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC;YAC1B,aAAa,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACvE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;YACzD,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wBAAwB;IACxB,IAAI,MAAM,KAAK,MAAM,IAAI,IAAI,KAAK,sBAAsB,EAAE,CAAC;QACzD,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,6CAA6C,CAAC,CAAC;QAC/D,IAAI,CAAC;YAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;QAAC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,yBAAyB,CAAC,CAAC;QAAC,CAAC;QACjH,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7B,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4CAA4C;IAC5C,MAAM,SAAS,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChE,yEAAyE;IACzE,MAAM,cAAc,GAAG,CAAC,IAAY,EAAE,MAAe,EAAE,IAAa,EAAQ,EAAE;QAC5E,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;YAAC,OAAO;QAAC,CAAC;QAClI,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAA4B,CAAC;QAC7E,MAAM,UAAU,GAAG,EAAE,GAAG,IAAI,EAAE,GAAI,IAAgC,EAAE,CAAC;QACrE,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAI,CAAC,CAAC;QACzC,MAAM,KAAK,GAAG,mBAAmB,CAAC,EAAE,GAAG,GAAI,EAAE,SAAS,EAAE,EAAE,GAAG,GAAI,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;QACrG,IAAI,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC;YAAE,OAAO;QAC9C,GAAI,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,UAAyD,CAAC;QACjF,GAAG,CAAC,eAAe,EAAE,CAAC;QACtB,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IACtE,CAAC,CAAC;IAEF,0EAA0E;IAC1E,IAAI,MAAM,KAAK,MAAM,IAAI,IAAI,KAAK,+BAA+B,EAAE,CAAC;QAClE,IAAI,CAAC,GAAG,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QACzE,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YACnC,IAAI,IAA6B,CAAC;YAClC,IAAI,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC;gBAAC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;YAAC,CAAC;YACrH,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;YAC7H,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,+DAA+D,EAAE,CAAC,CAAC;YACxH,IAAI,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;YACrF,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,QAAQ,EAAE,GAAG,IAAI,CAAC;YACvC,cAAc,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,kEAAkE;IAClE,MAAM,UAAU,GAAG,gCAAgC,CAAC;IACpD,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAChC,IAAI,CAAC,GAAG,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QACzE,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE,CAAC,CAAC;YAAC,OAAO,IAAI,CAAC;QAAC,CAAC;QAE1F,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxB,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;gBAAC,OAAO,IAAI,CAAC;YAAC,CAAC;YAC3F,qEAAqE;YACrE,OAAO,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC3B,GAAG,CAAC,eAAe,EAAE,CAAC;YACtB,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,mBAAmB,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;YAC1E,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,KAAK,OAAO,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,MAAM,KAAK,MAAM,IAAI,MAAM,EAAE,CAAC;gBAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE,CAAC,CAAC;gBAAC,OAAO,IAAI,CAAC;YAAC,CAAC;YACvG,IAAI,MAAM,KAAK,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;gBAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;gBAAC,OAAO,IAAI,CAAC;YAAC,CAAC;YACpG,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;gBACnC,IAAI,IAA6B,CAAC;gBAClC,IAAI,CAAC;oBAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC;oBAAC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC;gBAAC,CAAC;gBACrH,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;YACrC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC;YACzD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC;AACd,CAAC"}
|
package/dist/topic-commands.d.ts
CHANGED
|
@@ -28,6 +28,20 @@ export declare class TopicCommands {
|
|
|
28
28
|
private getReplyAdapter;
|
|
29
29
|
/** Parse and dispatch commands from the General topic */
|
|
30
30
|
handleGeneralCommand(msg: InboundMessage): Promise<boolean>;
|
|
31
|
+
/**
|
|
32
|
+
* Build the dashboard URL text (View / Settings / Web UI). The Settings/Web UI
|
|
33
|
+
* URLs carry the web token; when `htmlSpoiler` is set they're wrapped in a
|
|
34
|
+
* Telegram HTML spoiler (`<tg-spoiler>`) so the token isn't shown in the clear
|
|
35
|
+
* in a shared topic (the caller must send with format: "html"). /view is
|
|
36
|
+
* public, so it's never spoilered. DC uses the plain form (ephemeral reply).
|
|
37
|
+
*/
|
|
38
|
+
getDashboardText(htmlSpoiler?: boolean): string;
|
|
39
|
+
/**
|
|
40
|
+
* /dashboard (TG): admin-only. Replies directly in the topic; the token-
|
|
41
|
+
* bearing URLs are wrapped in a Telegram HTML spoiler so they aren't shown in
|
|
42
|
+
* the clear (the adapter supports plain/HTML, not MarkdownV2's `||…||`).
|
|
43
|
+
*/
|
|
44
|
+
private handleDashboardCommand;
|
|
31
45
|
/** Handle /ctx or /compact in any instance topic — returns true if handled */
|
|
32
46
|
handleInstanceCommand(msg: InboundMessage, instanceName: string): Promise<boolean>;
|
|
33
47
|
/** Get context usage text for an instance (shared by TG + DC) */
|
package/dist/topic-commands.js
CHANGED
|
@@ -6,6 +6,8 @@ import { createRequire } from "node:module";
|
|
|
6
6
|
const execAsync = promisify(exec);
|
|
7
7
|
import { DEFAULT_INSTANCE_CONFIG } from "./config.js";
|
|
8
8
|
import { formatCents } from "./cost-guard.js";
|
|
9
|
+
import { getAgendHome } from "./paths.js";
|
|
10
|
+
import { t } from "./locale.js";
|
|
9
11
|
/** Sanitize a directory name into a valid instance name. Keeps Unicode letters (incl. CJK). */
|
|
10
12
|
export function sanitizeInstanceName(name) {
|
|
11
13
|
const sanitized = name.toLowerCase().replace(/[^\p{L}\d-]/gu, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -42,7 +44,7 @@ export function parseContextPercent(pane) {
|
|
|
42
44
|
const lines = pane.split("\n");
|
|
43
45
|
for (let i = lines.length - 1; i >= 0; i--) {
|
|
44
46
|
const line = lines[i];
|
|
45
|
-
const m = line.match(/(\d+)
|
|
47
|
+
const m = line.match(/(\d+)%.*[!❯>]/) || line.match(/◔\s*(\d+)%/) || line.match(/\[(\d+)%\]/);
|
|
46
48
|
if (m)
|
|
47
49
|
return parseInt(m[1], 10);
|
|
48
50
|
}
|
|
@@ -86,8 +88,59 @@ export class TopicCommands {
|
|
|
86
88
|
await this.handleUpdateCommand(msg);
|
|
87
89
|
return true;
|
|
88
90
|
}
|
|
91
|
+
if (text === "/dashboard" || text.startsWith("/dashboard@")) {
|
|
92
|
+
await this.handleDashboardCommand(msg);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
89
95
|
return false;
|
|
90
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Build the dashboard URL text (View / Settings / Web UI). The Settings/Web UI
|
|
99
|
+
* URLs carry the web token; when `htmlSpoiler` is set they're wrapped in a
|
|
100
|
+
* Telegram HTML spoiler (`<tg-spoiler>`) so the token isn't shown in the clear
|
|
101
|
+
* in a shared topic (the caller must send with format: "html"). /view is
|
|
102
|
+
* public, so it's never spoilered. DC uses the plain form (ephemeral reply).
|
|
103
|
+
*/
|
|
104
|
+
getDashboardText(htmlSpoiler = false) {
|
|
105
|
+
const port = this.ctx.fleetConfig?.health_port ?? 19280;
|
|
106
|
+
const host = this.ctx.fleetConfig?.hostname || "localhost";
|
|
107
|
+
let token = "";
|
|
108
|
+
try {
|
|
109
|
+
token = readFileSync(join(getAgendHome(), "web.token"), "utf-8").trim();
|
|
110
|
+
}
|
|
111
|
+
catch { /* not started yet */ }
|
|
112
|
+
const base = `http://${host}:${port}`;
|
|
113
|
+
const hide = (u) => htmlSpoiler ? `<tg-spoiler>${u}</tg-spoiler>` : u;
|
|
114
|
+
return [
|
|
115
|
+
t("dashboard.title"),
|
|
116
|
+
"",
|
|
117
|
+
`• View: ${base}/view`,
|
|
118
|
+
`• Settings: ${hide(`${base}/settings?token=${token}`)}`,
|
|
119
|
+
`• Web UI: ${hide(`${base}/ui?token=${token}`)}`,
|
|
120
|
+
].join("\n");
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* /dashboard (TG): admin-only. Replies directly in the topic; the token-
|
|
124
|
+
* bearing URLs are wrapped in a Telegram HTML spoiler so they aren't shown in
|
|
125
|
+
* the clear (the adapter supports plain/HTML, not MarkdownV2's `||…||`).
|
|
126
|
+
*/
|
|
127
|
+
async handleDashboardCommand(msg) {
|
|
128
|
+
const adapter = this.getReplyAdapter(msg);
|
|
129
|
+
if (!adapter)
|
|
130
|
+
return;
|
|
131
|
+
const chatId = msg.chatId;
|
|
132
|
+
const threadId = msg.threadId;
|
|
133
|
+
const allowed = this.ctx.fleetConfig?.channel?.access?.allowed_users ?? [];
|
|
134
|
+
if (allowed.length === 0) {
|
|
135
|
+
await adapter.sendText(chatId, t("dashboard.disabled"), { threadId });
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (!allowed.some(u => String(u) === String(msg.userId))) {
|
|
139
|
+
await adapter.sendText(chatId, t("not_authorized"), { threadId });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
await adapter.sendText(chatId, this.getDashboardText(true), { threadId, format: "html" });
|
|
143
|
+
}
|
|
91
144
|
/** Handle /ctx or /compact in any instance topic — returns true if handled */
|
|
92
145
|
async handleInstanceCommand(msg, instanceName) {
|
|
93
146
|
const text = msg.text?.trim();
|
|
@@ -99,8 +152,8 @@ export class TopicCommands {
|
|
|
99
152
|
return false;
|
|
100
153
|
const isCollab = this.ctx.toggleFleetCollab(instanceName);
|
|
101
154
|
await adapter.sendText(msg.chatId, isCollab
|
|
102
|
-
? "
|
|
103
|
-
: "
|
|
155
|
+
? t("collab.on")
|
|
156
|
+
: t("collab.off"), { threadId: msg.threadId });
|
|
104
157
|
return true;
|
|
105
158
|
}
|
|
106
159
|
if (text === "/compact" || text.startsWith("/compact@")) {
|
|
@@ -116,7 +169,7 @@ export class TopicCommands {
|
|
|
116
169
|
if (!adapter)
|
|
117
170
|
return false;
|
|
118
171
|
const ok = this.ctx.cancelInstance(instanceName);
|
|
119
|
-
await adapter.sendText(msg.chatId, ok ?
|
|
172
|
+
await adapter.sendText(msg.chatId, ok ? t("cancel.sent", instanceName) : t("cancel.not_running", instanceName), { threadId: msg.threadId });
|
|
120
173
|
return true;
|
|
121
174
|
}
|
|
122
175
|
if (text === "/save" || text.startsWith("/save ") || text.startsWith("/save@")) {
|
|
@@ -125,11 +178,11 @@ export class TopicCommands {
|
|
|
125
178
|
return false;
|
|
126
179
|
const filename = parseSaveFilename(text);
|
|
127
180
|
if (!filename) {
|
|
128
|
-
await adapter.sendText(msg.chatId, "
|
|
181
|
+
await adapter.sendText(msg.chatId, t("save.usage"), { threadId: msg.threadId });
|
|
129
182
|
return true;
|
|
130
183
|
}
|
|
131
184
|
if (!SAVE_FILENAME_RE.test(filename)) {
|
|
132
|
-
await adapter.sendText(msg.chatId, "
|
|
185
|
+
await adapter.sendText(msg.chatId, t("filename.invalid"), { threadId: msg.threadId });
|
|
133
186
|
return true;
|
|
134
187
|
}
|
|
135
188
|
const result = await this.sendSave(instanceName, filename);
|
|
@@ -173,8 +226,8 @@ export class TopicCommands {
|
|
|
173
226
|
catch { /* ignore */ }
|
|
174
227
|
}
|
|
175
228
|
return context != null
|
|
176
|
-
?
|
|
177
|
-
:
|
|
229
|
+
? `${t("ctx.used", context)}\n${t("ctx.backend", backend)}\n${t("ctx.instance", instanceName)}`
|
|
230
|
+
: `${t("ctx.unavailable")}\n${t("ctx.backend", backend)}\n${t("ctx.instance", instanceName)}`;
|
|
178
231
|
}
|
|
179
232
|
/** Send /compact to an instance's tmux pane */
|
|
180
233
|
async sendCompact(instanceName) {
|
|
@@ -207,10 +260,10 @@ export class TopicCommands {
|
|
|
207
260
|
const threadId = msg.threadId;
|
|
208
261
|
const allowed = this.ctx.fleetConfig?.channel?.access?.allowed_users ?? [];
|
|
209
262
|
if (allowed.length > 0 && !allowed.some(u => String(u) === String(msg.userId))) {
|
|
210
|
-
await adapter.sendText(chatId, "
|
|
263
|
+
await adapter.sendText(chatId, t("not_authorized"), { threadId });
|
|
211
264
|
return;
|
|
212
265
|
}
|
|
213
|
-
await adapter.sendText(chatId, "
|
|
266
|
+
await adapter.sendText(chatId, t("restart.graceful"), { threadId });
|
|
214
267
|
process.kill(process.pid, "SIGUSR2");
|
|
215
268
|
}
|
|
216
269
|
async handleStatusCommand(msg) {
|
|
@@ -314,14 +367,14 @@ export class TopicCommands {
|
|
|
314
367
|
// Access control — only allowed users can trigger update; empty = disabled
|
|
315
368
|
const allowed = this.ctx.fleetConfig?.channel?.access?.allowed_users ?? [];
|
|
316
369
|
if (allowed.length === 0) {
|
|
317
|
-
await adapter.sendText(chatId, "
|
|
370
|
+
await adapter.sendText(chatId, t("update.disabled"), { threadId });
|
|
318
371
|
return;
|
|
319
372
|
}
|
|
320
373
|
if (!allowed.some(u => String(u) === String(msg.userId))) {
|
|
321
|
-
await adapter.sendText(chatId, "
|
|
374
|
+
await adapter.sendText(chatId, t("not_authorized"), { threadId });
|
|
322
375
|
return;
|
|
323
376
|
}
|
|
324
|
-
await adapter.sendText(chatId, "
|
|
377
|
+
await adapter.sendText(chatId, t("update.running"), { threadId });
|
|
325
378
|
const currentVersion = createRequire(import.meta.url)("../package.json").version ?? "";
|
|
326
379
|
const updateCmd = currentVersion.includes("beta") ? "agend update --beta" : "agend update";
|
|
327
380
|
const { spawn } = await import("node:child_process");
|
|
@@ -336,10 +389,10 @@ export class TopicCommands {
|
|
|
336
389
|
const threadId = msg.threadId;
|
|
337
390
|
const allowed = this.ctx.fleetConfig?.channel?.access?.allowed_users ?? [];
|
|
338
391
|
if (allowed.length > 0 && !allowed.some(u => String(u) === String(msg.userId))) {
|
|
339
|
-
await adapter.sendText(chatId, "
|
|
392
|
+
await adapter.sendText(chatId, t("not_authorized"), { threadId });
|
|
340
393
|
return;
|
|
341
394
|
}
|
|
342
|
-
await adapter.sendText(chatId, "
|
|
395
|
+
await adapter.sendText(chatId, t("doctor.running"), { threadId });
|
|
343
396
|
try {
|
|
344
397
|
const { execSync } = await import("node:child_process");
|
|
345
398
|
const backend = this.ctx.fleetConfig?.defaults?.backend || "claude-code";
|
|
@@ -419,7 +472,15 @@ export class TopicCommands {
|
|
|
419
472
|
}
|
|
420
473
|
if (platformType === "discord") {
|
|
421
474
|
const ch = channels.find(c => c.type === "discord");
|
|
422
|
-
|
|
475
|
+
const gcid = ch?.options?.general_channel_id;
|
|
476
|
+
// A Discord general needs a real channel id — NOT the TG-convention
|
|
477
|
+
// "1", which makes the DC adapter throw fetching channel "1". Skip
|
|
478
|
+
// (leave unbound) if there's no valid channel to bind to.
|
|
479
|
+
if (gcid == null || !/^\d{17,}$/.test(String(gcid))) {
|
|
480
|
+
this.ctx.logger.warn({ name }, "Discord general has no valid general_channel_id — skipping topic bind (set channel.options.general_channel_id)");
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
config.topic_id = gcid;
|
|
423
484
|
}
|
|
424
485
|
else {
|
|
425
486
|
config.topic_id = 1;
|
|
@@ -463,6 +524,7 @@ export class TopicCommands {
|
|
|
463
524
|
commands: [
|
|
464
525
|
{ command: "status", description: "Show fleet status and costs" },
|
|
465
526
|
{ command: "sysinfo", description: "System diagnostics" },
|
|
527
|
+
{ command: "dashboard", description: "🔒 Get dashboard URLs" },
|
|
466
528
|
{ command: "ctx", description: "Show context usage" },
|
|
467
529
|
{ command: "compact", description: "Compact agent context" },
|
|
468
530
|
{ command: "restart", description: "🔒 Graceful restart all instances" },
|