mirror of
https://github.com/aljazceru/dvmcp.git
synced 2025-12-17 13:24:24 +01:00
feat: implement NIP-09 event deletion for service announcements (v0.1.19)
This commit is contained in:
@@ -8,6 +8,7 @@ A bridge implementation that connects Model Context Protocol (MCP) servers to No
|
|||||||
- Automatic service announcement using NIP-89
|
- Automatic service announcement using NIP-89
|
||||||
- Tool discovery and execution through DVM kind:5910/6910 events
|
- Tool discovery and execution through DVM kind:5910/6910 events
|
||||||
- Job status updates and payment handling via kind:7000 events
|
- Job status updates and payment handling via kind:7000 events
|
||||||
|
- Service announcement deletion using NIP-09
|
||||||
- Comprehensive error handling
|
- Comprehensive error handling
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
@@ -47,6 +48,22 @@ For production:
|
|||||||
bun run start
|
bun run start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Deleting Service Announcements
|
||||||
|
|
||||||
|
To remove your service announcements from relays when shutting down or taking your service offline, you can use the `--delete-announcement` flag:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run start --delete-announcement
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also provide an optional reason for the deletion:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run start --delete-announcement --reason "Service maintenance in progress"
|
||||||
|
```
|
||||||
|
|
||||||
|
This will send a NIP-09 deletion event (kind 5) to all connected relays, instructing them to remove your previously published service announcements.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Run the test suite:
|
Run the test suite:
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
import { argv } from 'process';
|
import { argv } from 'process';
|
||||||
import type { Config } from './src/types';
|
import type { Config } from './src/types';
|
||||||
import { setConfigPath } from './src/config.js';
|
import { setConfigPath } from './src/config.js';
|
||||||
|
import { DVMBridge } from './src/dvm-bridge.js';
|
||||||
|
|
||||||
const defaultConfigPath = join(process.cwd(), 'config.dvmcp.yml');
|
const defaultConfigPath = join(process.cwd(), 'config.dvmcp.yml');
|
||||||
let configPath = defaultConfigPath;
|
let configPath = defaultConfigPath;
|
||||||
@@ -118,9 +119,38 @@ const runApp = async () => {
|
|||||||
await main.default();
|
await main.default();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteAnnouncement = async () => {
|
||||||
|
// Get optional reason from command line arguments
|
||||||
|
const reasonIndex = argv.indexOf('--reason');
|
||||||
|
const reason = reasonIndex !== -1 && argv[reasonIndex + 1] ? argv[reasonIndex + 1] : undefined;
|
||||||
|
|
||||||
|
// Import DVMBridge and perform deletion
|
||||||
|
const bridge = new DVMBridge();
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`${CONFIG_EMOJIS.INFO} Deleting service announcement...`);
|
||||||
|
await bridge.deleteAnnouncement(reason);
|
||||||
|
console.log(`${CONFIG_EMOJIS.SUCCESS} Service announcement deleted successfully`);
|
||||||
|
process.exit(0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`${CONFIG_EMOJIS.INFO} Failed to delete service announcement:`, error);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const cliMain = async () => {
|
const cliMain = async () => {
|
||||||
if (argv.includes('--configure')) {
|
if (argv.includes('--configure')) {
|
||||||
await configure();
|
await configure();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argv.includes('--delete-announcement')) {
|
||||||
|
if (!existsSync(configPath)) {
|
||||||
|
console.error(`${CONFIG_EMOJIS.INFO} No configuration file found at ${configPath}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
await deleteAnnouncement();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!existsSync(configPath)) {
|
if (!existsSync(configPath)) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@dvmcp/bridge",
|
"name": "@dvmcp/bridge",
|
||||||
"version": "0.1.18",
|
"version": "0.1.19",
|
||||||
"description": "Bridge connecting MCP servers to Nostr's DVM ecosystem",
|
"description": "Bridge connecting MCP servers to Nostr's DVM ecosystem",
|
||||||
"module": "index.ts",
|
"module": "index.ts",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
DVM_ANNOUNCEMENT_KIND,
|
DVM_ANNOUNCEMENT_KIND,
|
||||||
TOOL_REQUEST_KIND,
|
TOOL_REQUEST_KIND,
|
||||||
} from '@dvmcp/commons/constants';
|
} from '@dvmcp/commons/constants';
|
||||||
|
import type { Event } from 'nostr-tools/pure';
|
||||||
|
|
||||||
export const keyManager = createKeyManager(CONFIG.nostr.privateKey);
|
export const keyManager = createKeyManager(CONFIG.nostr.privateKey);
|
||||||
|
|
||||||
@@ -57,4 +58,37 @@ export class NostrAnnouncer {
|
|||||||
async updateAnnouncement() {
|
async updateAnnouncement() {
|
||||||
await Promise.all([this.announceService(), this.announceRelayList()]);
|
await Promise.all([this.announceService(), this.announceRelayList()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the service announcement from relays using NIP-09
|
||||||
|
* @param reason Optional reason for deletion
|
||||||
|
* @returns The deletion event that was published
|
||||||
|
*/
|
||||||
|
async deleteAnnouncement(reason: string = 'Service offline'): Promise<Event> {
|
||||||
|
// First, query the relays to find our announcement event
|
||||||
|
const announcementFilter = {
|
||||||
|
kinds: [DVM_ANNOUNCEMENT_KIND],
|
||||||
|
authors: [keyManager.getPublicKey()],
|
||||||
|
};
|
||||||
|
|
||||||
|
const events = await this.relayHandler.queryEvents(announcementFilter);
|
||||||
|
|
||||||
|
// Create the deletion event (NIP-09)
|
||||||
|
const deletionEvent = keyManager.signEvent({
|
||||||
|
...keyManager.createEventTemplate(5), // kind 5 is for deletion requests
|
||||||
|
content: reason,
|
||||||
|
tags: [
|
||||||
|
// Add tags for each event to be deleted
|
||||||
|
...events.map(event => ['e', event.id]),
|
||||||
|
// Add the kind of events being deleted
|
||||||
|
['k', `${DVM_ANNOUNCEMENT_KIND}`],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Publish the deletion event
|
||||||
|
await this.relayHandler.publishEvent(deletionEvent);
|
||||||
|
console.log(`Published deletion event for service announcement`);
|
||||||
|
|
||||||
|
return deletionEvent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,6 +83,23 @@ export class DVMBridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the service announcement from relays
|
||||||
|
* @param reason Optional reason for deletion
|
||||||
|
* @returns The deletion event that was published
|
||||||
|
*/
|
||||||
|
async deleteAnnouncement(reason?: string) {
|
||||||
|
console.log('Deleting service announcement from relays...');
|
||||||
|
try {
|
||||||
|
const deletionEvent = await this.nostrAnnouncer.deleteAnnouncement(reason);
|
||||||
|
console.log('Service announcement deleted successfully');
|
||||||
|
return deletionEvent;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting service announcement:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async handleRequest(event: Event) {
|
private async handleRequest(event: Event) {
|
||||||
try {
|
try {
|
||||||
if (this.isWhitelisted(event.pubkey)) {
|
if (this.isWhitelisted(event.pubkey)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user