codehost 0.18.2 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/package.json +1 -1
- package/src/cli/commands/serve.ts +8 -2
- package/src/shared/signaling.ts +4 -0
- package/src/web/discovery.tsx +49 -13
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [0.20.0](https://github.com/snomiao/codehost/compare/v0.19.0...v0.20.0) (2026-06-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **web:** edit a host's .codehost config from the site — advertised as a ⚙ workspace entry ([7c97b87](https://github.com/snomiao/codehost/commit/7c97b870715d6879cee39dab286cc7b8d45f08a6))
|
|
7
|
+
|
|
8
|
+
# [0.19.0](https://github.com/snomiao/codehost/compare/v0.18.2...v0.19.0) (2026-06-11)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **web:** GitHub-URL header/title for the open workspace; agent chips link into the agent-yes console ([83d62bc](https://github.com/snomiao/codehost/commit/83d62bc2ab3e70722f8a4f0f817541bea7cf69a2))
|
|
14
|
+
|
|
1
15
|
## [0.18.2](https://github.com/snomiao/codehost/compare/v0.18.1...v0.18.2) (2026-06-11)
|
|
2
16
|
|
|
3
17
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
1
|
+
import { existsSync, mkdirSync } from "node:fs";
|
|
2
2
|
import { homedir, hostname } from "node:os";
|
|
3
|
-
import { resolve } from "node:path";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
4
|
import type { CommandModule } from "yargs";
|
|
5
5
|
import type { PeerMeta } from "../../shared/signaling";
|
|
6
6
|
import { DEFAULT_LAYOUT, GITHUB_HOST, toPosixPath } from "../../shared/repo";
|
|
@@ -150,6 +150,12 @@ export const serveCommand: CommandModule<{}, ServeArgs> = {
|
|
|
150
150
|
// Layout-enumerated checkouts plus directories other `codehost dev` runs
|
|
151
151
|
// registered with this host daemon (git-identified best-effort).
|
|
152
152
|
const workspaces = enumerateWorkspaces(dir, layout);
|
|
153
|
+
// The config dir itself is editable from the site (rendered as ⚙, opens
|
|
154
|
+
// in the editor) — advertised so its /host/<host>/<path> link resolves.
|
|
155
|
+
const configDir = join(dir, ".codehost");
|
|
156
|
+
if (existsSync(configDir)) {
|
|
157
|
+
workspaces.push({ path: toPosixPath(configDir), config: true });
|
|
158
|
+
}
|
|
153
159
|
for (const w of readRegisteredWorkspaces()) {
|
|
154
160
|
const path = toPosixPath(w.path);
|
|
155
161
|
if (workspaces.some((x) => x.path === path)) continue;
|
package/src/shared/signaling.ts
CHANGED
|
@@ -30,6 +30,10 @@ export interface WorkspaceInfo {
|
|
|
30
30
|
repo?: string;
|
|
31
31
|
/** Branch from the layout path, e.g. "main". */
|
|
32
32
|
branch?: string;
|
|
33
|
+
/** This entry is the daemon's `.codehost/` config dir (setup.sh etc.), not a
|
|
34
|
+
* repo checkout — clients render it as a settings affordance, openable in
|
|
35
|
+
* the editor like any workspace. */
|
|
36
|
+
config?: boolean;
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
/** Metadata a `codehost serve`/`dev` daemon advertises about itself. */
|
package/src/web/discovery.tsx
CHANGED
|
@@ -58,6 +58,18 @@ function folderQuery(folder?: string): string {
|
|
|
58
58
|
return folder ? `?folder=${encodeURIComponent(folder)}` : "";
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
+
/** Human label for a connected workspace: its GitHub-style URL when the share
|
|
62
|
+
* path is repo-shaped (/gh/owner/repo -> github.com/owner/repo, /git/<host>/…
|
|
63
|
+
* -> <host>/…), else the deep-link path as-is. */
|
|
64
|
+
function shareLabel(path: string | null): string | null {
|
|
65
|
+
if (!path) return null;
|
|
66
|
+
const gh = path.match(/^\/gh\/(.+)$/);
|
|
67
|
+
if (gh) return `github.com/${gh[1]}`;
|
|
68
|
+
const git = path.match(/^\/git\/(.+)$/);
|
|
69
|
+
if (git) return git[1];
|
|
70
|
+
return path;
|
|
71
|
+
}
|
|
72
|
+
|
|
61
73
|
/**
|
|
62
74
|
* Find which of the user's saved rooms hosts a server matching a token-less deep
|
|
63
75
|
* link. Opens a short-lived viewer connection to each candidate room in
|
|
@@ -292,6 +304,13 @@ export function Discovery() {
|
|
|
292
304
|
tryAutoConnect();
|
|
293
305
|
}, [serversByRoom, tokens]);
|
|
294
306
|
|
|
307
|
+
// Mirror the open workspace into the tab title (GitHub-style URL), so tabs
|
|
308
|
+
// read as "github.com/owner/repo/tree/main — codehost", not all "Codehost".
|
|
309
|
+
useEffect(() => {
|
|
310
|
+
const label = connState === "connected" ? shareLabel(sharePathRef.current) : null;
|
|
311
|
+
document.title = label ? `${label} — codehost` : "Codehost";
|
|
312
|
+
}, [connState]);
|
|
313
|
+
|
|
295
314
|
// Keep the connection in sync with the URL as servers come and go: reconnect
|
|
296
315
|
// when the workspace named by the address bar (re)appears in a room — covers a
|
|
297
316
|
// daemon restart or a dropped channel while the tab stays open.
|
|
@@ -691,8 +710,10 @@ export function Discovery() {
|
|
|
691
710
|
// Group workspaces by machine: the stable hostId when the daemon advertises
|
|
692
711
|
// one, else the hostname string (older daemons), else the peer stands alone.
|
|
693
712
|
// Agents are machine-level (advertised by the host's root daemon) — collect
|
|
694
|
-
// them per group, deduped by pid across peers
|
|
695
|
-
|
|
713
|
+
// them per group, deduped by pid across peers, each remembering its room so
|
|
714
|
+
// a click can hand the agent-yes console the right token.
|
|
715
|
+
type RoomedAgent = AgentInfo & { room: string };
|
|
716
|
+
const hostGroups: { key: string; label: string; items: typeof filtered; agents: RoomedAgent[] }[] = [];
|
|
696
717
|
for (const t of filtered) {
|
|
697
718
|
const key = t.server.meta?.hostId ?? t.server.meta?.host ?? t.server.peerId;
|
|
698
719
|
let group = hostGroups.find((g) => g.key === key);
|
|
@@ -702,7 +723,7 @@ export function Discovery() {
|
|
|
702
723
|
}
|
|
703
724
|
group.items.push(t);
|
|
704
725
|
for (const a of t.server.meta?.agents ?? []) {
|
|
705
|
-
if (!group.agents.some((x) => x.pid === a.pid)) group.agents.push(a);
|
|
726
|
+
if (!group.agents.some((x) => x.pid === a.pid)) group.agents.push({ ...a, room: t.room });
|
|
706
727
|
}
|
|
707
728
|
}
|
|
708
729
|
const toggleTag = (t: string) =>
|
|
@@ -765,8 +786,15 @@ export function Discovery() {
|
|
|
765
786
|
<header style={styles.header}>
|
|
766
787
|
<span style={styles.brand}>codehost</span>
|
|
767
788
|
<span style={styles.dim}>·</span>
|
|
768
|
-
<span
|
|
769
|
-
|
|
789
|
+
<span
|
|
790
|
+
style={styles.cwd}
|
|
791
|
+
title={`${activeServer?.meta?.name ?? ""} ${activeServer?.meta?.cwd ?? ""}`.trim()}
|
|
792
|
+
>
|
|
793
|
+
{shareLabel(sharePathRef.current) ??
|
|
794
|
+
activeServer?.meta?.cwd ??
|
|
795
|
+
activeServer?.meta?.name ??
|
|
796
|
+
activePeerId?.slice(0, 8)}
|
|
797
|
+
</span>
|
|
770
798
|
<span style={{ flex: 1 }} />
|
|
771
799
|
<button
|
|
772
800
|
style={styles.shareBtn}
|
|
@@ -959,14 +987,20 @@ export function Discovery() {
|
|
|
959
987
|
{g.agents.length > 0 && (
|
|
960
988
|
<div style={styles.agentRow}>
|
|
961
989
|
{g.agents.map((a) => (
|
|
962
|
-
<
|
|
990
|
+
<a
|
|
963
991
|
key={a.pid}
|
|
964
992
|
style={styles.agentChip}
|
|
965
|
-
title={`${a.cwd}${a.title ? `\n${a.title}` : ""}`}
|
|
993
|
+
title={`${a.cwd}${a.title ? `\n${a.title}` : ""}\nopen in the agent-yes console`}
|
|
994
|
+
// Tail & send live in the agent-yes console — it joins
|
|
995
|
+
// this same room as a viewer (token rides the fragment,
|
|
996
|
+
// never sent to a server) and auto-selects the pid.
|
|
997
|
+
href={`https://agent-yes.com/?pid=${a.pid}#ch:${encodeURIComponent(a.room)}`}
|
|
998
|
+
target="_blank"
|
|
999
|
+
rel="noopener"
|
|
966
1000
|
>
|
|
967
1001
|
<span style={{ color: a.state === "active" ? "#4ec9b0" : "#777" }}>●</span> {a.tool}{" "}
|
|
968
1002
|
{a.pid}
|
|
969
|
-
</
|
|
1003
|
+
</a>
|
|
970
1004
|
))}
|
|
971
1005
|
</div>
|
|
972
1006
|
)}
|
|
@@ -991,11 +1025,13 @@ export function Discovery() {
|
|
|
991
1025
|
key={w.path}
|
|
992
1026
|
style={styles.wsLink}
|
|
993
1027
|
onClick={() => openWorkspace(s, w)}
|
|
994
|
-
title={w.path}
|
|
1028
|
+
title={w.config ? `edit this host's provisioning config\n${w.path}` : w.path}
|
|
995
1029
|
>
|
|
996
|
-
{w.
|
|
997
|
-
?
|
|
998
|
-
: w.
|
|
1030
|
+
{w.config
|
|
1031
|
+
? "⚙ .codehost (setup.sh, config.yaml)"
|
|
1032
|
+
: w.repo
|
|
1033
|
+
? `${w.repo.split("/").slice(1).join("/")}${w.branch ? ` @${w.branch}` : ""}`
|
|
1034
|
+
: w.path}
|
|
999
1035
|
</button>
|
|
1000
1036
|
))}
|
|
1001
1037
|
</div>
|
|
@@ -1081,7 +1117,7 @@ const styles: Record<string, React.CSSProperties> = {
|
|
|
1081
1117
|
agentRow: { display: "flex", flexWrap: "wrap", gap: 6, margin: "0 0 8px" },
|
|
1082
1118
|
agentChip: {
|
|
1083
1119
|
fontFamily: "monospace", fontSize: 11.5, padding: "2px 8px", borderRadius: 999,
|
|
1084
|
-
border: "1px solid #3d3d3d", color: "#9aa4af",
|
|
1120
|
+
border: "1px solid #3d3d3d", color: "#9aa4af", textDecoration: "none", cursor: "pointer",
|
|
1085
1121
|
},
|
|
1086
1122
|
card: { display: "flex", alignItems: "center", gap: 12, background: "#252525", border: "1px solid #3d3d3d", borderRadius: 8, padding: "12px 14px" },
|
|
1087
1123
|
cardMain: { flex: 1, minWidth: 0 },
|