mirror of
https://github.com/anomalyco/opentui.git
synced 2026-09-19 01:26:03 +08:00
9ef4691410
## Summary Adds `@opentui/ssh`, renderer-agnostic ssh server package for serving apps to remote terminals. Highlights: - `createServer(config).use(...).serve(handler)` builder api with typed middleware context - open, public key, password, and keyboard-interactive auth, including verified public-key possession and `authorized_keys` allowlists - host key loading/generation, startup banner, logging middleware, session resize/close hooks, raw writes, idle/max timeouts, and graceful shutdown --------- Co-authored-by: opencode-agent <opencode-agent@opencode.ai> Co-authored-by: Sebastian Herrlinger <hasta84@gmail.com>
70 lines
2.3 KiB
TypeScript
70 lines
2.3 KiB
TypeScript
/**
|
|
* Example: authentication + type-flowing identity.
|
|
*
|
|
* bun run packages/ssh/examples/auth.ts
|
|
*
|
|
* Connect with any key (accepted and identified by fingerprint):
|
|
*
|
|
* ssh -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
|
* -i ~/.ssh/id_ed25519 guest@localhost
|
|
*
|
|
* ...or fall back to the password "swordfish":
|
|
*
|
|
* ssh -p 2222 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
|
|
* -o PreferredAuthentications=password guest@localhost
|
|
*
|
|
* Because `auth` mixes publickey and password, `session.identity` is a union:
|
|
* discriminate on `.method`; `.fingerprint` is only readable in the publickey branch.
|
|
*/
|
|
import { BoxRenderable, TextRenderable } from "@opentui/core"
|
|
import { createServer } from "../src/index.js"
|
|
|
|
const PORT = Number(process.env.PORT ?? 2222)
|
|
|
|
const server = createServer({
|
|
hostKey: { path: "./host_key" },
|
|
auth: {
|
|
publicKey: "any", // accept & identify any key
|
|
password: ({ password }) => password === "swordfish", // simple fallback
|
|
},
|
|
}).serve((session) => {
|
|
const { renderer, identity } = session
|
|
|
|
// identity is `publickey | password` here — narrow before touching fields.
|
|
const how =
|
|
identity.method === "publickey"
|
|
? `publickey · ${identity.fingerprint}` // ✅ fingerprint only exists on this branch
|
|
: "password"
|
|
|
|
renderer.setBackgroundColor("#0b1021")
|
|
const box = new BoxRenderable(renderer, {
|
|
width: "100%",
|
|
height: "100%",
|
|
border: true,
|
|
borderStyle: "rounded",
|
|
borderColor: "#8b5cf6",
|
|
title: " @opentui/ssh · auth ",
|
|
titleAlignment: "center",
|
|
flexDirection: "column",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
})
|
|
box.add(new TextRenderable(renderer, { content: `Hello, ${identity.username}! 👋`, fg: "#e2e8f0" }))
|
|
box.add(new TextRenderable(renderer, { content: `authenticated via ${how}`, fg: "#67e8f9" }))
|
|
box.add(new TextRenderable(renderer, { content: "Press q or Ctrl-C to quit.", fg: "#94a3b8" }))
|
|
renderer.root.add(box)
|
|
|
|
renderer.keyInput.on("keypress", (key) => {
|
|
if (key.name === "q" || (key.ctrl && key.name === "c")) session.end()
|
|
})
|
|
// The renderer is torn down for you on disconnect.
|
|
})
|
|
|
|
// listen() prints the startup banner itself (host, fingerprints, auth methods).
|
|
await server.listen(PORT)
|
|
|
|
process.on("SIGINT", async () => {
|
|
await server.close()
|
|
process.exit(0)
|
|
})
|