A guild's existing 'endpoint' field is its client-facing URL (browser, remote openclaw plugin) — but in-deployment services on the same docker network can't always reach it. Concretely, dialectic-backend on compose_default network sees 'http://server.t3:7002' (from agent-supplied URL) resolve to dind-network IP which it can't route to. Broadcasts silently fail with 'connection refused'. Adds a second URL per guild: serviceEndpoint. Used by other backends in the same deployment (compose service name + internal port). Plumbed through: - GuildNode.serviceEndpoint (varchar 255 nullable; TypeORM auto-migrates) - GET /api/auth/me/guilds returns serviceEndpoint per row - GET /api/nodes (admin) returns serviceEndpoint - new cli: 'node set-service-endpoint --node-id X --endpoint URL' (admin-only via cli — same pattern as set-purpose) Pairs with Fabric.OpenclawPlugin's fabric-guild-list returning the new field + workflows teaching agents to use serviceEndpoint for announce_guild_base_url (NOT the client-facing endpoint). E2e verified: sim recruiter agent discovered sim-guild-1's serviceEndpoint=http://fabric-backend-guild:7002, plumbed it into dialectic_propose_topic, all 4 lifecycle broadcasts (signup_open, signup_closed, debating, completed) landed in the announce channel.
72 lines
2.4 KiB
TypeScript
72 lines
2.4 KiB
TypeScript
import {
|
|
Column,
|
|
CreateDateColumn,
|
|
Entity,
|
|
PrimaryGeneratedColumn,
|
|
UpdateDateColumn,
|
|
} from 'typeorm';
|
|
|
|
@Entity('guild_nodes')
|
|
export class GuildNode {
|
|
@PrimaryGeneratedColumn('uuid')
|
|
id!: string;
|
|
|
|
@Column({ unique: true })
|
|
nodeId!: string;
|
|
|
|
@Column()
|
|
name!: string;
|
|
|
|
@Column()
|
|
endpoint!: string;
|
|
|
|
// Service-to-service endpoint: how *another service inside the same
|
|
// deployment* reaches this guild. Distinct from `endpoint` because
|
|
// that one is the client-facing URL (browser, remote openclaw plugin,
|
|
// anything outside compose), which lives on a different network than
|
|
// services co-located with the guild backend.
|
|
//
|
|
// Concrete examples:
|
|
// - sim sim-guild-1: endpoint=http://server.t3:7002 (dind /etc/hosts alias),
|
|
// serviceEndpoint=http://fabric-backend-guild:7002 (compose service name)
|
|
// - prod t3-node: endpoint=https://fabric-api.hangman-lab.top (public CF),
|
|
// serviceEndpoint=http://backend-guild:7002 (compose service name)
|
|
//
|
|
// Used by dialectic-backend (and any other in-deployment service) to
|
|
// post broadcasts to a guild's announce channel. Plugin's
|
|
// fabric-guild-list returns this so agents can plumb the right URL
|
|
// into announce_guild_base_url on dialectic_propose_topic.
|
|
// Null when the admin hasn't filled it in (broadcasts will fail until
|
|
// it's set; cli: `node set-service-endpoint --node-id X --endpoint URL`).
|
|
@Column({ type: 'varchar', length: 255, nullable: true })
|
|
serviceEndpoint!: string | null;
|
|
|
|
// Free-form description of this guild's purpose — what it's used for,
|
|
// who its agents/users serve. Surfaced to agents via /auth/me/guilds so
|
|
// the cross-guild/cross-channel announce-target picker can find the
|
|
// right guild by intent ("which guild is about debate broadcasts?")
|
|
// without anything hard-coded into agent workflows. admin-only write
|
|
// (cli: `node set-purpose --node-id X --purpose Y`).
|
|
@Column({ type: 'text', nullable: true })
|
|
purpose!: string | null;
|
|
|
|
@Column({ type: 'varchar', length: 255, nullable: true })
|
|
apiKeyHash!: string | null;
|
|
|
|
@Column({
|
|
type: 'enum',
|
|
enum: ['active', 'offline', 'revoked'],
|
|
default: 'active',
|
|
})
|
|
status!: 'active' | 'offline' | 'revoked';
|
|
|
|
@Column({ type: 'datetime', nullable: true })
|
|
lastHeartbeatAt!: Date | null;
|
|
|
|
@CreateDateColumn()
|
|
createdAt!: Date;
|
|
|
|
@UpdateDateColumn()
|
|
updatedAt!: Date;
|
|
}
|