codehost 0.1.1 → 0.4.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/.github/workflows/deploy.yml +30 -0
- package/.github/workflows/release.yaml +39 -0
- package/.releaserc.json +17 -0
- package/CHANGELOG.md +41 -0
- package/README.md +5 -0
- package/TODO.md +93 -0
- package/docs/vscode-cdn-proxy.md +117 -0
- package/package.json +12 -1
- package/public/_redirects +10 -0
- package/public/install.ps1 +31 -15
- package/public/install.sh +40 -15
- package/src/cli/commands/dev.ts +110 -0
- package/src/cli/commands/expose.ts +93 -0
- package/src/cli/commands/list.ts +2 -2
- package/src/cli/commands/serve.ts +29 -55
- package/src/cli/commands/setup.ts +9 -6
- package/src/cli/commands/stop.ts +2 -2
- package/src/cli/daemonize.ts +15 -4
- package/src/cli/git.ts +53 -0
- package/src/cli/index.ts +4 -0
- package/src/cli/open-url.ts +47 -0
- package/src/cli/oxmgr-heal.ts +175 -0
- package/src/cli/oxmgr.ts +104 -36
- package/src/cli/run-server.ts +78 -0
- package/src/cli/self-update.ts +89 -0
- package/src/cli/tunnel.ts +30 -4
- package/src/cli/vscode.ts +4 -2
- package/src/shared/repo.test.ts +37 -0
- package/src/shared/repo.ts +123 -0
- package/src/shared/signaling.ts +18 -2
- package/src/web/config.ts +44 -7
- package/src/web/discovery.tsx +131 -6
- package/src/web/history.ts +58 -0
- package/src/web/sw.ts +36 -1
- package/worker/index.ts +49 -1
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Deploy
|
|
2
|
+
|
|
3
|
+
# Auto-build + deploy the Cloudflare Pages site and signaling Worker on push to
|
|
4
|
+
# main (the "git integration / auto build" for codehost.dev). Needs repo
|
|
5
|
+
# secrets CLOUDFLARE_API_TOKEN (Workers Scripts:Edit + Pages:Edit) and
|
|
6
|
+
# CLOUDFLARE_ACCOUNT_ID.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: deploy
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
deploy:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
env:
|
|
19
|
+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
20
|
+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: oven-sh/setup-bun@v2
|
|
24
|
+
- run: bun install
|
|
25
|
+
- run: bun run build
|
|
26
|
+
- name: Deploy Pages (app + service worker)
|
|
27
|
+
run: bunx wrangler pages deploy dist/public --project-name codehost --branch main --commit-dirty=true
|
|
28
|
+
- name: Deploy signaling Worker
|
|
29
|
+
run: bunx wrangler deploy
|
|
30
|
+
working-directory: worker
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publishes to npm via semantic-release using npm trusted publishing (OIDC) —
|
|
4
|
+
# no NPM_TOKEN. Requires: a trusted publisher configured on npm for this repo +
|
|
5
|
+
# this workflow file (release.yaml), id-token: write, semantic-release v25 /
|
|
6
|
+
# @semantic-release/npm v13, and npm >= 11.5.1.
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
branches: [main]
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: write # semantic-release commits the version bump + tag
|
|
13
|
+
issues: write
|
|
14
|
+
pull-requests: write
|
|
15
|
+
id-token: write # OIDC token for npm trusted publishing
|
|
16
|
+
|
|
17
|
+
concurrency:
|
|
18
|
+
group: release
|
|
19
|
+
cancel-in-progress: false
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
release:
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
with:
|
|
27
|
+
fetch-depth: 0
|
|
28
|
+
persist-credentials: false
|
|
29
|
+
# Provides node + npm; do NOT set registry-url (it writes an .npmrc that
|
|
30
|
+
# breaks semantic-release's auth). Upgrade npm for OIDC support.
|
|
31
|
+
- uses: actions/setup-node@v4
|
|
32
|
+
with:
|
|
33
|
+
node-version: 24
|
|
34
|
+
- run: npm install -g npm@latest
|
|
35
|
+
- uses: oven-sh/setup-bun@v2
|
|
36
|
+
- run: bun install
|
|
37
|
+
- run: bunx semantic-release
|
|
38
|
+
env:
|
|
39
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
package/.releaserc.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"branches": ["main"],
|
|
3
|
+
"plugins": [
|
|
4
|
+
"@semantic-release/commit-analyzer",
|
|
5
|
+
"@semantic-release/release-notes-generator",
|
|
6
|
+
"@semantic-release/changelog",
|
|
7
|
+
"@semantic-release/npm",
|
|
8
|
+
[
|
|
9
|
+
"@semantic-release/git",
|
|
10
|
+
{
|
|
11
|
+
"assets": ["package.json", "CHANGELOG.md"],
|
|
12
|
+
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"@semantic-release/github"
|
|
16
|
+
]
|
|
17
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# [0.4.0](https://github.com/snomiao/codehost/compare/v0.3.1...v0.4.0) (2026-06-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* normalize Windows serve path to POSIX-drive form for VS Code web ([8add2d6](https://github.com/snomiao/codehost/commit/8add2d66a6b5ae765f91386f738ad38abcb90057))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* setup.sh/ps1 installer aliases + self-update to latest on serve/setup ([93a5d64](https://github.com/snomiao/codehost/commit/93a5d64170c5851ca0f488348975dae46f467b5e))
|
|
12
|
+
|
|
13
|
+
## [0.3.1](https://github.com/snomiao/codehost/compare/v0.3.0...v0.3.1) (2026-06-07)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* add repository metadata so npm provenance publish succeeds ([e794bc2](https://github.com/snomiao/codehost/commit/e794bc2cef66276a53ca9fca9bece1a056bef703))
|
|
19
|
+
|
|
20
|
+
# [0.3.0](https://github.com/snomiao/codehost/compare/v0.2.0...v0.3.0) (2026-06-07)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* open token URL after setup/serve and auto-connect a single server ([b6183a2](https://github.com/snomiao/codehost/commit/b6183a2dc35e3e2ce1b4dffa8d4228fab377f4e3))
|
|
26
|
+
|
|
27
|
+
# [0.2.0](https://github.com/snomiao/codehost/compare/v0.1.1...v0.2.0) (2026-06-05)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
### Bug Fixes
|
|
31
|
+
|
|
32
|
+
* oxmgr works under bun/bunx and on Windows (no Node, no global install) ([b339b31](https://github.com/snomiao/codehost/commit/b339b319c7147eee4e99c5abe977ace3146e41d6))
|
|
33
|
+
* tunnel VS Code's resource URLs via the real host, not 127.0.0.1 ([c9d22e2](https://github.com/snomiao/codehost/commit/c9d22e27946464252cd753424f0e267eb664fb93))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Features
|
|
37
|
+
|
|
38
|
+
* `-d` enables login auto-start via oxmgr's service integration ([623e022](https://github.com/snomiao/codehost/commit/623e02291cf0d1bc4c71c0344b333e93e72783db))
|
|
39
|
+
* `codehost expose <port>` — tunnel any local HTTP/WS server over WebRTC ([1ec57f4](https://github.com/snomiao/codehost/commit/1ec57f4f8d221b699c8f713b2eb6f0e2187665b7))
|
|
40
|
+
* deep-link to a live workspace via codehost.dev/gh/<owner>/<repo>/tree/<branch> ([1567ba7](https://github.com/snomiao/codehost/commit/1567ba7e64b821d5dea989235db63c5f22d9b4c9))
|
|
41
|
+
* proxy VS Code's CORS-less CDN through the signaling Worker ([0a362ee](https://github.com/snomiao/codehost/commit/0a362ee0d0c3247963da72bcc3b12365711c6d4a))
|
package/README.md
CHANGED
|
@@ -32,6 +32,11 @@ curl -fsSL https://codehost.dev/install.sh | sh
|
|
|
32
32
|
powershell -c "irm codehost.dev/install.ps1 | iex"
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
`/setup.sh` and `/setup.ps1` are aliases of the same script. Re-running it any
|
|
36
|
+
time **upgrades you to the latest codehost** (`bun add -g codehost@latest`), so
|
|
37
|
+
it doubles as the updater — and it bootstraps Bun by absolute path, so it won't
|
|
38
|
+
bail just because your shell hasn't picked Bun up on `PATH` yet.
|
|
39
|
+
|
|
35
40
|
Already have Bun? `bun add -g codehost && codehost setup` does the same. Or, for
|
|
36
41
|
a specific directory/token in one shot:
|
|
37
42
|
|
package/TODO.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# codehost — TODO / future features
|
|
2
|
+
|
|
3
|
+
Parked ideas, roughly in priority order. The in-flight deep-link feature
|
|
4
|
+
(`/gh/<owner>/<repo>/tree/<branch>` + `serve`/`dev` split) is tracked in the active
|
|
5
|
+
plan, not here.
|
|
6
|
+
|
|
7
|
+
## Account login / device auth (Tailscale-style)
|
|
8
|
+
|
|
9
|
+
Replace/augment bearer tokens with account identity.
|
|
10
|
+
|
|
11
|
+
- CLI: `codehost serve --login=you@gmail.com` runs a device-authorization flow — prints a
|
|
12
|
+
short code + URL; you approve in `codehost.dev` while signed in (Google via **Firebase
|
|
13
|
+
Auth**) as that account. The daemon then holds a credential proving the same identity.
|
|
14
|
+
- Web: sign in with Google (Firebase) on codehost.dev.
|
|
15
|
+
- Signaling Worker: on room join, verify a Firebase ID token / signed JWT for the account
|
|
16
|
+
instead of (or alongside) the raw token. Personal room is keyed by account, auto-joined
|
|
17
|
+
by both the daemon and the web client — **no token copy-paste**.
|
|
18
|
+
- Benefits over tokens: revocable per device, per-account isolation, no shared secret to
|
|
19
|
+
leak. Like Tailscale authenticating devices.
|
|
20
|
+
- Keep **token rooms** too (anonymous quick-share); **login rooms** are the "my own
|
|
21
|
+
devices" path. The two can coexist.
|
|
22
|
+
- Open questions: Firebase project + Google OAuth client; how the CLI obtains a credential
|
|
23
|
+
(device-code OAuth vs. a Firebase custom-token minted by the Worker after browser
|
|
24
|
+
approval); revocation UI; mapping account -> room id.
|
|
25
|
+
|
|
26
|
+
## Port / service forwarding — `[port|service].f.codehost.dev`
|
|
27
|
+
|
|
28
|
+
Expose a dev server running on the daemon (the tunnel already proxies HTTP to
|
|
29
|
+
`127.0.0.1:<x>`).
|
|
30
|
+
|
|
31
|
+
- Needs wildcard DNS `*.f.codehost.dev`, a tiny bootstrap page + its own per-subdomain
|
|
32
|
+
Service Worker, and a `subdomain -> (room, peer, port)` mapping with token/identity
|
|
33
|
+
scoping.
|
|
34
|
+
- **Opt-in** registration from a nav/settings panel — do **not** auto-expose every bound
|
|
35
|
+
port (security footgun).
|
|
36
|
+
|
|
37
|
+
## Containerized dev environments — `codehost docker up [path]`
|
|
38
|
+
|
|
39
|
+
Run the workspace inside a container instead of on the host (devcontainer / Codespaces
|
|
40
|
+
style).
|
|
41
|
+
|
|
42
|
+
- `bunx codehost docker up [path]` mounts `path` into a container, sets up VS Code
|
|
43
|
+
`serve-web` + the repo's runtime inside (reuse the self-healing `code`/native installers;
|
|
44
|
+
honor the repo's `.devcontainer/devcontainer.json` when present), and runs the codehost
|
|
45
|
+
daemon in-container.
|
|
46
|
+
- Access at `codehost.dev/dev/<token>` (or `/gh/...`) via a generated or passed-in token.
|
|
47
|
+
- Lifecycle: `docker up` / `down` / `ps`. Composes with port forwarding above for the
|
|
48
|
+
container's services.
|
|
49
|
+
- Wins: isolation for untrusted repos, reproducible runtimes, parallel throwaway envs, no
|
|
50
|
+
host pollution.
|
|
51
|
+
- Open questions: base image (preinstalled `code`+bun vs. self-heal on first run), volume
|
|
52
|
+
mount perf, how the container daemon gets the token/identity, resource limits.
|
|
53
|
+
|
|
54
|
+
## agent-yes web terminal UI (over `codehost expose`)
|
|
55
|
+
|
|
56
|
+
Make `codehost.dev/vs/<peerId>/` actually usable for an exposed agent-yes, not just the raw API.
|
|
57
|
+
|
|
58
|
+
- Add an HTML/JS terminal UI served by agent-yes's `ts/serve.ts` at `GET /`: xterm in the
|
|
59
|
+
browser, output via `EventSource('./api/tail/<kw>')` (SSE), input via `POST ./api/send`.
|
|
60
|
+
- **Use relative paths** (`./api/...`) so it works under the tunnel's `/vs/<peerId>/` prefix
|
|
61
|
+
(the prefix is stripped for the server, but the page's own URLs must stay relative).
|
|
62
|
+
- Reference: **snomiao/wtx** (cloned to lib/wtx) — a Bun PTY WebSocket server with replay
|
|
63
|
+
buffer + xterm client. Note wtx uses **WebSocket**; agent-yes uses **SSE + POST** — reuse
|
|
64
|
+
wtx's xterm/client setup but keep agent-yes's transport (or align them).
|
|
65
|
+
- Belongs in the agent-yes repo (it's served by agent-yes), enabled once `codehost expose
|
|
66
|
+
7432` is running.
|
|
67
|
+
|
|
68
|
+
## Real-time collaboration / presence (multiplayer cursors)
|
|
69
|
+
|
|
70
|
+
Multiple people open the same workspace (different Chrome profiles / accounts) and see each
|
|
71
|
+
other's cursors + selections live.
|
|
72
|
+
|
|
73
|
+
- `serve-web` is single-session: today multiple viewers get independent workbenches with no
|
|
74
|
+
shared awareness. Needs a layer on top.
|
|
75
|
+
- Realistic path: a **presence/awareness protocol** — each viewer broadcasts identity +
|
|
76
|
+
cursor/selection over the room substrate we already have (signaling / data channel),
|
|
77
|
+
rendered as remote-cursor decorations via an injected VS Code extension.
|
|
78
|
+
- Full concurrent co-editing (CRDT/OT, à la Yjs) is a much bigger lift; MS **Live Share** is
|
|
79
|
+
the off-the-shelf alternative but brings its own backend/auth.
|
|
80
|
+
- Depends on identity (see Account login above) to label who's who.
|
|
81
|
+
|
|
82
|
+
## Deep-link feature follow-ups (after v1)
|
|
83
|
+
|
|
84
|
+
- Root daemon **enumerates / existence-checks** the repos under its root (vs. v1's
|
|
85
|
+
optimistic `?folder=`): nicer discovery list + accurate matching.
|
|
86
|
+
- **Clone-on-demand:** a root daemon `git clone`s `gh/owner/repo` into the root if absent,
|
|
87
|
+
then opens it (codespaces-like).
|
|
88
|
+
- **Live cross-room search:** fan out across all joined rooms for a repo with no history
|
|
89
|
+
(multiple concurrent `SignalingClient`s), instead of v1's history-driven single room.
|
|
90
|
+
- **Providers beyond GitHub** (`/gl/...`, self-hosted) via the `provider` field already in
|
|
91
|
+
`parseDeepLink`.
|
|
92
|
+
- Reflect the active repo back into the URL while browsing; chooser UI when several
|
|
93
|
+
machines/rooms serve the same repo.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# VS Code CDN proxy (CORS fix)
|
|
2
|
+
|
|
3
|
+
Status: implemented. Worker side verified; in-browser leg pending a live check.
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
When VS Code runs inside the codehost iframe (origin `https://codehost.dev`, served
|
|
8
|
+
over the WebRTC tunnel), its workbench fetches configuration from Microsoft's
|
|
9
|
+
product CDN — e.g. `_fetchChatControlData` does:
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
fetch('https://main.vscode-cdn.net/extensions/chat.json')
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
That CDN sends **no `Access-Control-Allow-Origin` header**, so the browser blocks the
|
|
16
|
+
cross-origin read:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
Access to fetch at 'https://main.vscode-cdn.net/extensions/chat.json' from origin
|
|
20
|
+
'https://codehost.dev' has been blocked by CORS policy: No 'Access-Control-Allow-Origin'
|
|
21
|
+
header is present on the requested resource.
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Our Service Worker only handled same-origin requests (it early-returned on
|
|
25
|
+
cross-origin), so these went straight to the CDN and failed. Chat / built-in-extension
|
|
26
|
+
control data never loaded and the console filled with CORS errors.
|
|
27
|
+
|
|
28
|
+
A Service Worker **alone cannot fix this**: it runs in the browser, bound by the same
|
|
29
|
+
CORS rules, and cannot turn a no-CORS cross-origin body into a readable one. The fix
|
|
30
|
+
needs a server-side proxy that re-serves the bytes with permissive CORS, plus a small
|
|
31
|
+
SW change to route the request there.
|
|
32
|
+
|
|
33
|
+
## Decision
|
|
34
|
+
|
|
35
|
+
Reuse the already-deployed **signaling Worker** (`worker/index.ts`, served at
|
|
36
|
+
`signal.<page-host>`) as a thin, allow-listed CDN proxy. The Service Worker rewrites
|
|
37
|
+
blocked CDN requests to it.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
VS Code iframe (codehost.dev) signaling Worker (signal.codehost.dev)
|
|
41
|
+
fetch main.vscode-cdn.net/extensions/chat.json
|
|
42
|
+
│ (cross-origin, CORS-blocked)
|
|
43
|
+
▼
|
|
44
|
+
Service Worker (sw.ts)
|
|
45
|
+
host ends with .vscode-cdn.net ? ──rewrite──► GET /cdn/main.vscode-cdn.net/extensions/chat.json
|
|
46
|
+
cache.match() first, else fetch + cache.put() │ allow-list check (.vscode-cdn.net)
|
|
47
|
+
▲ │ fetch upstream (server-side, not CORS-bound)
|
|
48
|
+
└──────────── readable response ◄────────────────┘ + Access-Control-Allow-Origin: *
|
|
49
|
+
+ preserve content-type, edge-cache
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Self-host constraint (important):** the SW targets the **derived** signaling host
|
|
53
|
+
(`signal.<current page host>`), never a hardcoded `signal.codehost.dev`. A self-hoster
|
|
54
|
+
who serves the page + Worker on their own domain is automatically proxied by their own
|
|
55
|
+
Worker at `signal.<their-domain>/cdn/...`, with no code changes. See
|
|
56
|
+
`cdnProxyBase(hostname, protocol)` in `src/web/config.ts`.
|
|
57
|
+
|
|
58
|
+
## Alternatives considered
|
|
59
|
+
|
|
60
|
+
- **Cloudflare Pages Function on `codehost.dev`** — same-origin and clean, but adds
|
|
61
|
+
Pages Functions to the currently pure-static build/deploy for no gain over reusing the
|
|
62
|
+
Worker we already run.
|
|
63
|
+
- **Daemon proxy over the WebRTC tunnel** — most self-host-robust (the daemon is always
|
|
64
|
+
the user's own machine, works even on networks where the browser can't reach the edge)
|
|
65
|
+
and zero hosted cost. Rejected as the default because it is slower (MS → daemon →
|
|
66
|
+
WebRTC → browser), cannot share a cache across users, adds CDN bytes to the WebRTC
|
|
67
|
+
datachannel, and allow-list changes must ship a new CLI to every user. The Worker wins
|
|
68
|
+
on latency, shared edge caching, and one-deploy allow-list updates. Cost is ~$0:
|
|
69
|
+
Cloudflare bills no egress, and these assets are tiny, public, and cacheable.
|
|
70
|
+
|
|
71
|
+
If a fully air-gapped / browser-can't-reach-the-edge deployment ever matters, the daemon
|
|
72
|
+
proxy is the fallback to revisit.
|
|
73
|
+
|
|
74
|
+
## Security
|
|
75
|
+
|
|
76
|
+
- The Worker is the **authoritative allow-list**: only hosts ending in `.vscode-cdn.net`
|
|
77
|
+
are proxied (`CDN_HOST_SUFFIX` in `worker/index.ts`). Anything else → `403`. The
|
|
78
|
+
leading dot prevents look-alikes like `notvscode-cdn.net`.
|
|
79
|
+
- **GET/HEAD only** (others → `405`). It forwards a path under the chosen host and never
|
|
80
|
+
follows attacker-controlled hostnames, so it is not an open proxy.
|
|
81
|
+
- The SW's suffix check (`isProxiableCdnHost` in `src/web/config.ts`) is only an
|
|
82
|
+
optimization deciding what to rewrite; if it ever drifts from the Worker's list, the
|
|
83
|
+
worst case is a `403` or an unproxied request — the Worker stays the real gate.
|
|
84
|
+
|
|
85
|
+
## Extending the allow-list
|
|
86
|
+
|
|
87
|
+
If the workbench starts pulling from another host (e.g. `update.code.visualstudio.com`),
|
|
88
|
+
widen the gate in two places and redeploy (one Worker + one Pages deploy — no CLI
|
|
89
|
+
change):
|
|
90
|
+
|
|
91
|
+
- `worker/index.ts` — `CDN_HOST_SUFFIX` (or make it a small list of suffixes).
|
|
92
|
+
- `src/web/config.ts` — `VSCODE_CDN_SUFFIX` / `isProxiableCdnHost`.
|
|
93
|
+
|
|
94
|
+
The extension **marketplace** (install/search) is a separate, larger concern (auth,
|
|
95
|
+
large payloads) and is intentionally out of scope here.
|
|
96
|
+
|
|
97
|
+
## Files
|
|
98
|
+
|
|
99
|
+
- `worker/index.ts` — `/cdn/<host>/<path>` route + `handleCdnProxy` (allow-list, CORS,
|
|
100
|
+
`caches.default` edge cache, `content-type` passthrough, `cache-control`).
|
|
101
|
+
- `src/web/sw.ts` — cross-origin branch → `proxyCdn` (rewrite to the derived `/cdn`
|
|
102
|
+
base, Cache API per-browser caching).
|
|
103
|
+
- `src/web/config.ts` — `cdnProxyBase`, `isProxiableCdnHost`, `VSCODE_CDN_SUFFIX`
|
|
104
|
+
(shared host derivation, reused by `getSignalUrl`).
|
|
105
|
+
|
|
106
|
+
## Verification
|
|
107
|
+
|
|
108
|
+
- **Worker, direct (done):**
|
|
109
|
+
- `curl -i https://signal.codehost.dev/cdn/main.vscode-cdn.net/extensions/chat.json`
|
|
110
|
+
→ `200`, `content-type: application/json`, `access-control-allow-origin: *`,
|
|
111
|
+
`cache-control: public, max-age=3600`, real JSON body.
|
|
112
|
+
- `…/cdn/evil.example.com/x` → `403`; `POST …` → `405`.
|
|
113
|
+
- **In-browser (pending live check):** open `codehost.dev` (SW active), then from the
|
|
114
|
+
page `fetch('https://main.vscode-cdn.net/extensions/chat.json')` should resolve `200`
|
|
115
|
+
(served via `signal.<host>/cdn/...`) instead of throwing a CORS error; a second call is
|
|
116
|
+
served from the SW cache. Then load VS Code in the iframe and confirm the `chat.json`
|
|
117
|
+
CORS error is gone from the console.
|
package/package.json
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codehost",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/snomiao/codehost.git"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://codehost.dev",
|
|
10
|
+
"bugs": "https://github.com/snomiao/codehost/issues",
|
|
5
11
|
"bin": {
|
|
6
12
|
"codehost": "./src/cli/index.ts"
|
|
7
13
|
},
|
|
@@ -14,6 +20,7 @@
|
|
|
14
20
|
"deploy:signal": "cd worker && wrangler deploy",
|
|
15
21
|
"deploy:pages": "vite build && wrangler pages deploy dist/public --project-name codehost",
|
|
16
22
|
"start": "bun src/server.ts",
|
|
23
|
+
"test": "bun test",
|
|
17
24
|
"typecheck": "tsc --noEmit && tsc --noEmit -p src/web/tsconfig.sw.json && tsc --noEmit -p worker/tsconfig.json"
|
|
18
25
|
},
|
|
19
26
|
"dependencies": {
|
|
@@ -27,11 +34,15 @@
|
|
|
27
34
|
"bun-pty": "^0.4.8",
|
|
28
35
|
"hono": "^4.12.16",
|
|
29
36
|
"node-datachannel": "^0.32.3",
|
|
37
|
+
"oxmgr": "^0.4.0",
|
|
30
38
|
"react": "^19.1.1",
|
|
31
39
|
"react-dom": "^19.1.1",
|
|
32
40
|
"yargs": "^17.7.2"
|
|
33
41
|
},
|
|
34
42
|
"devDependencies": {
|
|
43
|
+
"@semantic-release/changelog": "^6.0.3",
|
|
44
|
+
"@semantic-release/git": "^10.0.1",
|
|
45
|
+
"semantic-release": "^25.0.0",
|
|
35
46
|
"@types/bun": "^1.3.0",
|
|
36
47
|
"@types/react": "^19.1.9",
|
|
37
48
|
"@types/react-dom": "^19.1.7",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Friendly aliases for the installer: /setup.sh and /setup.ps1 serve the same
|
|
2
|
+
# scripts as /install.*. Must precede the SPA catch-all below (first match wins).
|
|
3
|
+
/setup.sh /install.sh 200
|
|
4
|
+
/setup.ps1 /install.ps1 200
|
|
5
|
+
|
|
6
|
+
# SPA fallback: deep links like /gh/<owner>/<repo>/tree/<branch> and /dev/<path>
|
|
7
|
+
# have no static file, so serve the app and let it route client-side. Cloudflare
|
|
8
|
+
# Pages serves existing files first (/assets/*, /sw.js, /install.*), so only
|
|
9
|
+
# unmatched paths hit this rule. /vs/* is handled by the Service Worker at runtime.
|
|
10
|
+
/* /index.html 200
|
package/public/install.ps1
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
# codehost installer — https://codehost.dev
|
|
1
|
+
# codehost installer / updater — https://codehost.dev
|
|
2
2
|
#
|
|
3
3
|
# powershell -c "irm codehost.dev/install.ps1 | iex"
|
|
4
|
+
# powershell -c "irm codehost.dev/setup.ps1 | iex" # same script, friendlier name
|
|
4
5
|
#
|
|
5
|
-
# Ensures Bun is installed, installs the `codehost` CLI globally
|
|
6
|
-
# the native WebRTC binary via Bun's lifecycle
|
|
7
|
-
# setup` to pick a token, install VS Code, and
|
|
6
|
+
# Ensures Bun is installed, installs/UPGRADES the `codehost` CLI globally to the
|
|
7
|
+
# latest release (which fetches the native WebRTC binary via Bun's lifecycle
|
|
8
|
+
# scripts), then runs `codehost setup` to pick a token, install VS Code, and
|
|
9
|
+
# start a server daemon. Safe to re-run any time — it always lands you on the
|
|
10
|
+
# newest codehost.
|
|
8
11
|
#
|
|
9
|
-
#
|
|
12
|
+
# Bun is the runtime and can't be skipped — we bootstrap it and then invoke
|
|
13
|
+
# everything by absolute path, so a not-yet-reloaded shell PATH never makes the
|
|
14
|
+
# install "fail" after Bun is actually present.
|
|
15
|
+
#
|
|
16
|
+
# Env override: $env:CODEHOST_NO_SETUP = "1" -> install/update only, skip setup.
|
|
10
17
|
|
|
11
18
|
$ErrorActionPreference = "Stop"
|
|
12
19
|
|
|
@@ -15,29 +22,38 @@ function Fail($m) { Write-Host "[codehost] $m" -ForegroundColor Red; exit 1 }
|
|
|
15
22
|
|
|
16
23
|
# Bun installs its global bin under %USERPROFILE%\.bun\bin by default.
|
|
17
24
|
$bunBin = Join-Path $env:USERPROFILE ".bun\bin"
|
|
25
|
+
$bunExe = Join-Path $bunBin "bun.exe"
|
|
18
26
|
if (Test-Path $bunBin) { $env:Path = "$bunBin;$env:Path" }
|
|
19
27
|
|
|
20
|
-
if (-not (
|
|
28
|
+
if (-not (Test-Path $bunExe)) {
|
|
21
29
|
Info "Bun not found - installing from bun.sh..."
|
|
22
30
|
Invoke-RestMethod https://bun.sh/install.ps1 | Invoke-Expression
|
|
23
31
|
if (Test-Path $bunBin) { $env:Path = "$bunBin;$env:Path" }
|
|
24
32
|
}
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
# Prefer the absolute path; fall back to a PATH lookup in case Bun honored a
|
|
35
|
+
# custom install dir. Either way we never bail just because PATH wasn't reloaded.
|
|
36
|
+
if (-not (Test-Path $bunExe)) {
|
|
37
|
+
$cmd = Get-Command bun -ErrorAction SilentlyContinue
|
|
38
|
+
if ($cmd) { $bunExe = $cmd.Source }
|
|
39
|
+
else { Fail "Bun was installed but bun.exe wasn't found at $bunBin. Open a new terminal and re-run." }
|
|
28
40
|
}
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
|
|
42
|
+
# `@latest` makes every re-run an upgrade, so users always end up on the newest
|
|
43
|
+
# codehost instead of a stale globally-pinned copy.
|
|
44
|
+
Info "installing the latest codehost CLI (bun add -g codehost@latest)..."
|
|
45
|
+
& $bunExe add -g codehost@latest
|
|
32
46
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
47
|
+
# Resolve the codehost shim Bun just wrote (extension varies on Windows).
|
|
48
|
+
$codehostCmd = Get-Command codehost -ErrorAction SilentlyContinue
|
|
49
|
+
if ($codehostCmd) { $codehostExe = $codehostCmd.Source }
|
|
50
|
+
elseif (Test-Path (Join-Path $bunBin "codehost.exe")) { $codehostExe = Join-Path $bunBin "codehost.exe" }
|
|
51
|
+
else { Fail "codehost installed but not found in $bunBin. Add it to PATH and run: codehost setup" }
|
|
36
52
|
|
|
37
53
|
if ($env:CODEHOST_NO_SETUP -eq "1") {
|
|
38
|
-
Info "installed. Run ``codehost setup`` in the directory you want to serve."
|
|
54
|
+
Info "installed/updated. Run ``codehost setup`` in the directory you want to serve."
|
|
39
55
|
exit 0
|
|
40
56
|
}
|
|
41
57
|
|
|
42
58
|
Info "running ``codehost setup``..."
|
|
43
|
-
|
|
59
|
+
& $codehostExe setup
|
package/public/install.sh
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
# codehost installer — https://codehost.dev
|
|
2
|
+
# codehost installer / updater — https://codehost.dev
|
|
3
3
|
#
|
|
4
4
|
# curl -fsSL https://codehost.dev/install.sh | sh
|
|
5
|
+
# curl -fsSL https://codehost.dev/setup.sh | sh # same script, friendlier name
|
|
5
6
|
#
|
|
6
|
-
# Ensures Bun is installed, installs the `codehost` CLI globally
|
|
7
|
-
# the native WebRTC binary via Bun's lifecycle
|
|
8
|
-
# setup` to pick a token, install VS Code, and
|
|
7
|
+
# Ensures Bun is installed, installs/UPGRADES the `codehost` CLI globally to the
|
|
8
|
+
# latest release (which fetches the native WebRTC binary via Bun's lifecycle
|
|
9
|
+
# scripts), then runs `codehost setup` to pick a token, install VS Code, and
|
|
10
|
+
# start a server daemon. Safe to re-run any time — it always lands you on the
|
|
11
|
+
# newest codehost.
|
|
12
|
+
#
|
|
13
|
+
# Bun is the runtime (the CLI is TypeScript run by Bun + a native addon), so it
|
|
14
|
+
# can't be skipped — but we bootstrap it and then invoke everything by absolute
|
|
15
|
+
# path, so a not-yet-reloaded shell PATH never makes the install "fail" after
|
|
16
|
+
# Bun is actually present.
|
|
9
17
|
#
|
|
10
18
|
# Env overrides:
|
|
11
|
-
# CODEHOST_NO_SETUP=1 install only; don't run `codehost setup`
|
|
19
|
+
# CODEHOST_NO_SETUP=1 install/update only; don't run `codehost setup`
|
|
12
20
|
set -eu
|
|
13
21
|
|
|
14
22
|
info() { printf '\033[1;36m[codehost]\033[0m %s\n' "$1"; }
|
|
@@ -20,7 +28,18 @@ BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
|
|
|
20
28
|
export BUN_INSTALL
|
|
21
29
|
export PATH="$BUN_INSTALL/bin:$PATH"
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
# Resolve a usable bun binary: one already on PATH, or the managed install.
|
|
32
|
+
# Echoes the absolute path (empty if none) so callers can invoke it directly.
|
|
33
|
+
bun_bin() {
|
|
34
|
+
if command -v bun >/dev/null 2>&1; then
|
|
35
|
+
command -v bun
|
|
36
|
+
elif [ -x "$BUN_INSTALL/bin/bun" ]; then
|
|
37
|
+
printf '%s\n' "$BUN_INSTALL/bin/bun"
|
|
38
|
+
fi
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
BUN="$(bun_bin)"
|
|
42
|
+
if [ -z "$BUN" ]; then
|
|
24
43
|
info "Bun not found — installing from bun.sh…"
|
|
25
44
|
if command -v curl >/dev/null 2>&1; then
|
|
26
45
|
curl -fsSL https://bun.sh/install | bash
|
|
@@ -30,26 +49,32 @@ if ! command -v bun >/dev/null 2>&1; then
|
|
|
30
49
|
err "need curl or wget to install Bun. Install one and re-run."
|
|
31
50
|
exit 1
|
|
32
51
|
fi
|
|
33
|
-
|
|
52
|
+
BUN="$(bun_bin)"
|
|
34
53
|
fi
|
|
35
54
|
|
|
36
|
-
if
|
|
37
|
-
err "Bun
|
|
55
|
+
if [ -z "$BUN" ]; then
|
|
56
|
+
err "Bun was installed but its binary isn't at $BUN_INSTALL/bin/bun. Set BUN_INSTALL to its location and re-run."
|
|
38
57
|
exit 1
|
|
39
58
|
fi
|
|
40
59
|
|
|
41
|
-
|
|
42
|
-
|
|
60
|
+
# `@latest` makes every re-run an upgrade, so users always end up on the newest
|
|
61
|
+
# codehost instead of a stale globally-pinned copy.
|
|
62
|
+
info "installing the latest codehost CLI ($BUN add -g codehost@latest)…"
|
|
63
|
+
"$BUN" add -g codehost@latest
|
|
43
64
|
|
|
44
|
-
|
|
45
|
-
|
|
65
|
+
CODEHOST="$BUN_INSTALL/bin/codehost"
|
|
66
|
+
if [ ! -x "$CODEHOST" ]; then
|
|
67
|
+
CODEHOST="$(command -v codehost || true)"
|
|
68
|
+
fi
|
|
69
|
+
if [ -z "$CODEHOST" ] || [ ! -x "$CODEHOST" ]; then
|
|
70
|
+
err "codehost installed but not found under $BUN_INSTALL/bin. Add it to PATH and run: codehost setup"
|
|
46
71
|
exit 1
|
|
47
72
|
fi
|
|
48
73
|
|
|
49
74
|
if [ "${CODEHOST_NO_SETUP:-}" = "1" ]; then
|
|
50
|
-
info "installed. Run \`codehost setup\` in the directory you want to serve."
|
|
75
|
+
info "installed/updated. Run \`codehost setup\` in the directory you want to serve."
|
|
51
76
|
exit 0
|
|
52
77
|
fi
|
|
53
78
|
|
|
54
79
|
info "running \`codehost setup\`…"
|
|
55
|
-
exec
|
|
80
|
+
exec "$CODEHOST" setup
|