Shows Portly's running ports, most recently started first, in the status line

This repository contains Claude Code configuration files and automatically syncs with the upstream repository.
This repository automatically syncs from Melvynx/aiblueprint-cli-premium once per day at midnight UTC.
mainTo trigger a sync manually:
main branch and click "Run workflow"hooks/register.ts 108 lines1import type { EngineInterface, Register } from 'claude-code'
2
3// Portly's local API (macOS app first, Linux daemon second). Querying it
4// directly never launches Portly.app the way the `portly` CLI does.
5const API_PORTS = [7738, 7737]
6const POLL_MS = 4000
7const MAX_SHOWN = 6
8
9type PortlyServer = {
10 name: string
11 projectName: string
12 port?: number
13 state: string
14 healthy?: boolean
15 startedAt?: string | null
16}
17
18type PortlyStatus = {
19 data?: {
20 projects?: { servers?: PortlyServer[] }[]
21 temporaryServers?: PortlyServer[]
22 }
23}
24
25let apiPort = API_PORTS[0] as number
26let isEnabled = true
27let shown: string | undefined
28
29const isActive = (server: PortlyServer) =>
30 server.port !== undefined && (server.state === 'running' || server.state === 'starting')
31
32const slug = (text: string) =>
33 text.trim().toLowerCase().replace(/[^a-z0-9.]+/g, '-').replace(/^-|-$/g, '')
34
35// `app:port` for each running server, most recently started first; `…` while
36// not yet healthy. A project with several running servers names each one.
37function format(status: PortlyStatus) {
38 const active = [
39 ...(status.data?.projects ?? []).flatMap(project => project.servers ?? []),
40 ...(status.data?.temporaryServers ?? []),
41 ]
42 .filter(isActive)
43 .sort((a, b) => Date.parse(b.startedAt ?? '') - Date.parse(a.startedAt ?? '') || 0)
44
45 if (active.length === 0) return undefined
46
47 const perProject = new Map<string, number>()
48 for (const one of active) {
49 perProject.set(one.projectName, (perProject.get(one.projectName) ?? 0) + 1)
50 }
51 const label = (one: PortlyServer) =>
52 (perProject.get(one.projectName) ?? 0) > 1
53 ? `${slug(one.projectName)}/${slug(one.name)}`
54 : slug(one.projectName)
55
56 const ports = active
57 .slice(0, MAX_SHOWN)
58 .map(one => `${label(one)}:${one.port}${one.healthy ? '' : '…'}`)
59 const more = active.length > MAX_SHOWN ? ` +${active.length - MAX_SHOWN}` : ''
60
61 return `${ports.join(' · ')}${more}`
62}
63
64async function fetchStatus($: EngineInterface) {
65 for (const candidate of [apiPort, ...API_PORTS.filter(one => one !== apiPort)]) {
66 try {
67 const response = await $.http.fetch(`http://127.0.0.1:${candidate}/status`)
68 if (!response.ok) continue
69 apiPort = candidate
70 return JSON.parse(response.text) as PortlyStatus
71 } catch {
72 // Portly not listening on this port.
73 }
74 }
75 return undefined
76}
77
78async function refresh($: EngineInterface) {
79 const status = isEnabled ? await fetchStatus($) : undefined
80 const text = status ? format(status) : undefined
81 if (text === shown) return
82 shown = text
83 $.ui.status(text)
84}
85
86export const register: Register = on => {
87 on('session.start', async ($, e, next) => {
88 isEnabled = (await $.store.get('isEnabled')) !== false
89 shown = undefined
90 await $.command.register({
91 name: 'portly-ports',
92 description: 'Toggle Portly ports in the status line',
93 })
94 void refresh($)
95 $.clock.every(POLL_MS, () => void refresh($))
96
97 return next(e)
98 })
99
100 on('command.run', { command: 'portly-ports' }, async $ => {
101 isEnabled = !isEnabled
102 await $.store.set('isEnabled', isEnabled)
103 await refresh($)
104
105 return { text: `Portly ports in the status line: ${isEnabled ? 'on' : 'off'}.` }
106 })
107}
108