用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/kemalcr/kemal --skill kemal-sse命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Use when building, reviewing, debugging, testing, securing, or deploying web applications and HTTP APIs with the Kemal framework for Crystal. Covers Kemal routing, params, context, routers, filters, middleware, ECR, WebSockets, SSE, uploads, configuration, testing, and production concerns.
User authentication and session management in Kemal, following established project patterns.
Core Kemal development (routing verbs, parameters, modular router, version gates, response helpers).
正在显示 SKILL.md
| name | kemal-sse |
| description | Real-time Server-Sent Events (SSE) streaming in Kemal 1.12+, following established project patterns. |
| license | MIT |
This skill provides expert guidance on implementing real-time unidirectional event streaming using Kemal's built-in sse helper (available in Kemal 1.12.0+).
sse route helper and Kemal::EventStream (named events, id:, retry:): since Kemal 1.12.0.event/id, CRLF normalization in data and comments) and the retry field Int32 overflow fix: Kemal master only.SSE Route Definition (Kemal 1.12.0+): Use the sse helper block to define an SSE endpoint:
sse "/events" do |stream, env|
stream.send("hello world")
end
Sending Named Events & Security: Pass event, id, and optional retry (Time::Span) keyword arguments:
sse "/notifications" do |stream, env|
stream.send({"message" => "New alert"}.to_json, event: "alert", id: 101, retry: 5.seconds)
end
Security Note (Kemal Master): Kemal::EventStream on master prevents SSE injection by rejecting raw newlines in event and id arguments, and automatically normalizes CRLF sequences in data and comment.
Streaming Loops & Channels: Use Crystal channels or keep-alive loops for streaming events:
sse "/stream" do |stream, env|
loop do
sleep 1.seconds
stream.send(Time.utc.to_s, event: "time")
rescue IO::Error
break # Connection closed by client
end
end
require "kemal"
sse "/live-ticks" do |stream, env|
count = 0_i64
loop do
count += 1
stream.send(
{"count" => count, "timestamp" => Time.utc.to_s}.to_json,
event: "tick",
id: count
)
sleep 1.seconds
rescue IO::Error
# Client disconnected, gracefully terminate streaming loop
break
end
end
Kemal.run
require "kemal"
class SseHub
@streams = Array(Kemal::EventStream).new
@mutex = Mutex.new
def register(stream : Kemal::EventStream)
@mutex.synchronize { @streams << stream }
end
def unregister(stream : Kemal::EventStream)
@mutex.synchronize { @streams.delete(stream) }
end
def broadcast(data : String, event : String? = nil)
@mutex.synchronize do
@streams.reject! do |stream|
begin
stream.send(data, event: event)
false
rescue IO::Error
true # Remove closed connections
end
end
end
end
end
HUB = SseHub.new
sse "/subscribe" do |stream, env|
HUB.register(stream)
# Block while connection remains open
begin
sleep
ensure
HUB.unregister(stream)
end
end
post "/publish" do |env|
message = env.params.body["message"]? || ""
HUB.broadcast(message, event: "news")
env.json({status: "ok"})
end
Kemal.run
IO::Error or using an ensure block.Content-Type: text/event-stream and Cache-Control: no-cache headers for SSE routes..to_json before passing to stream.send(...).post "/publish"), follow the patterns in kemal-json.text/event-stream.