Guild-global slash-command catalog (one row per node guild). The
OpenClaw plugin PUTs the native-command specs (same data Discord
registers as slash commands); the frontend GETs it for / autocomplete.
- GuildCommand entity (guild_id unique, commands json, updatedAt)
- PUT /api/commands -> idempotent full replace (any authed agent/user)
- GET /api/commands -> { commands, updatedAt } (authed)
- stored verbatim (NativeCommandSpec-shaped); execution path unchanged:
a /<cmd> message is delivered as a normal message -> plugin ->
OpenClaw command system (only /no-reply, /force-proceed stay
server-intercepted).
Verified: PUT->{ok,count}, GET round-trips args/choices, no-auth->401.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { Repository } from 'typeorm';
|
|
import { GuildCommand } from '../entities/guild-command.entity.js';
|
|
|
|
// This node's guild id (one guild per node).
|
|
function guildId(): string {
|
|
return process.env.FABRIC_BACKEND_GUILD_NODE_ID ?? 'guild';
|
|
}
|
|
|
|
@Injectable()
|
|
export class CommandsService {
|
|
constructor(
|
|
@InjectRepository(GuildCommand)
|
|
private readonly repo: Repository<GuildCommand>,
|
|
) {}
|
|
|
|
// Replace the whole guild-global slash-command catalog (idempotent;
|
|
// the plugin re-PUTs the full set on every gateway start).
|
|
async sync(commands: unknown[]): Promise<{ status: string; count: number }> {
|
|
const gid = guildId();
|
|
let row = await this.repo.findOne({ where: { guildId: gid } });
|
|
if (row) {
|
|
row.commands = commands;
|
|
} else {
|
|
row = this.repo.create({ guildId: gid, commands });
|
|
}
|
|
await this.repo.save(row);
|
|
return { status: 'ok', count: commands.length };
|
|
}
|
|
|
|
async list(): Promise<{ commands: unknown[]; updatedAt: string | null }> {
|
|
const row = await this.repo.findOne({ where: { guildId: guildId() } });
|
|
return {
|
|
commands: row?.commands ?? [],
|
|
updatedAt: row?.updatedAt ? row.updatedAt.toISOString() : null,
|
|
};
|
|
}
|
|
}
|