@devframes/plugin-code-server 0.0.1

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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-PRESENT Anthony Fu <https://github.com/antfu>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ # @devframes/plugin-code-server
2
+
3
+ > [!WARNING] Experimental
4
+ > This plugin is experimental and may change without a major version bump until
5
+ > it stabilizes.
6
+
7
+ Run [code-server](https://github.com/coder/code-server) (VS Code in the
8
+ browser) as a devframe panel. The plugin detects a local `code-server`
9
+ install, launches it on demand, and embeds the editor in an
10
+ auto-authenticated `<iframe>`.
11
+
12
+ ## How it works
13
+
14
+ - **Detection** — on startup it runs `code-server --version`. When the binary
15
+ is missing, the launcher renders install instructions and links instead of a
16
+ launch button.
17
+ - **Launch** — the launcher's button starts code-server as a managed child
18
+ process bound to a free port, scoped to the workspace. Readiness is probed
19
+ via code-server's `/healthz` endpoint.
20
+ - **Auto-auth** — code-server runs with password auth. The plugin generates a
21
+ random token, sets `HASHED_PASSWORD` to its SHA-256, and hands the matching
22
+ session cookie back to the already-authorized devframe client. The launcher
23
+ applies that cookie for the current host before loading the iframe, so the
24
+ editor opens already signed in — no code-server login page.
25
+
26
+ The editor iframe points at code-server's own origin
27
+ (`<protocol>//<host>:<port>/`), so WebSocket traffic flows directly without a
28
+ reverse proxy.
29
+
30
+ ## Usage
31
+
32
+ ### Standalone CLI
33
+
34
+ ```sh
35
+ npx @devframes/plugin-code-server # dev server + launcher
36
+ ```
37
+
38
+ ### Programmatic
39
+
40
+ ```ts
41
+ import { createCodeServerDevframe } from '@devframes/plugin-code-server'
42
+
43
+ export default createCodeServerDevframe({
44
+ // bin: 'code-server', // binary to detect/launch (default: PATH)
45
+ // serverPort: 8080, // force a port (default: free port near 8080)
46
+ // args: ['--disable-getting-started-override'],
47
+ })
48
+ ```
49
+
50
+ ### Vite host
51
+
52
+ ```ts
53
+ import { codeServerVite } from '@devframes/plugin-code-server/vite'
54
+
55
+ export default {
56
+ plugins: [codeServerVite()],
57
+ }
58
+ ```
59
+
60
+ ## RPC
61
+
62
+ | Function | Type | Purpose |
63
+ |----------|------|---------|
64
+ | `devframes-plugin-code-server:detect` | query | Re-probe for the binary; returns `{ installed, version, bin }`. |
65
+ | `devframes-plugin-code-server:status` | query | Current status + auth cookie when running. |
66
+ | `devframes-plugin-code-server:start` | action | Launch and wait for readiness. |
67
+ | `devframes-plugin-code-server:stop` | action | Stop the process. |
68
+
69
+ Status (minus the auth cookie) is mirrored into the
70
+ `devframes-plugin-code-server:state` shared state for reactive UIs.
71
+
72
+ ## UI
73
+
74
+ The launcher UI is a pure, state-driven view (`src/client/view.ts`) decoupled
75
+ from RPC, so every state renders in isolation. `mountCodeServer` wires the live
76
+ connection to it. Each UI state has a Storybook story:
77
+
78
+ ```sh
79
+ pnpm storybook # dev
80
+ pnpm build-storybook # static build
81
+ ```
82
+
83
+ Stories: connecting, not-installed, launch, launch-error, starting, running
84
+ (the running story mounts a mock editor instead of a live server).
package/bin.mjs ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ import process from 'node:process'
3
+ import { createCodeServerCli } from './dist/cli.mjs'
4
+
5
+ async function main() {
6
+ const cli = createCodeServerCli()
7
+ await cli.parse()
8
+ }
9
+
10
+ main().catch((error) => {
11
+ console.error(error)
12
+ process.exit(1)
13
+ })
package/dist/cli.d.mts ADDED
@@ -0,0 +1,12 @@
1
+ import { CodeServerOptions } from "./types.mjs";
2
+ import { CliHandle, CreateCliOptions } from "devframe/adapters/cli";
3
+
4
+ //#region src/cli.d.ts
5
+ /**
6
+ * Build a standalone CLI for the code-server panel — `dev` / `build` / `mcp`
7
+ * subcommands, backed by {@link createCodeServerDevframe}. Used by the package
8
+ * `bin` (`devframe-code-server`).
9
+ */
10
+ declare function createCodeServerCli(options?: CodeServerOptions, cliOptions?: CreateCliOptions): CliHandle;
11
+ //#endregion
12
+ export { createCodeServerCli };
package/dist/cli.mjs ADDED
@@ -0,0 +1,13 @@
1
+ import { n as createCodeServerDevframe } from "./src-BDRH3xta.mjs";
2
+ import { createCli } from "devframe/adapters/cli";
3
+ //#region src/cli.ts
4
+ /**
5
+ * Build a standalone CLI for the code-server panel — `dev` / `build` / `mcp`
6
+ * subcommands, backed by {@link createCodeServerDevframe}. Used by the package
7
+ * `bin` (`devframe-code-server`).
8
+ */
9
+ function createCodeServerCli(options = {}, cliOptions = {}) {
10
+ return createCli(createCodeServerDevframe(options), cliOptions);
11
+ }
12
+ //#endregion
13
+ export { createCodeServerCli };
@@ -0,0 +1,75 @@
1
+ import { CodeServerAuth, CodeServerDetection, CodeServerServerInfo, CodeServerSharedState, CodeServerStatus } from "../types.mjs";
2
+ import { o as STATE_KEY } from "../constants-ICLvdiRe.mjs";
3
+ import { DevframeRpcClient } from "devframe/client";
4
+
5
+ //#region src/client/view.d.ts
6
+ /** The data the view renders from. Pure — no RPC, no process knowledge. */
7
+ interface CodeServerViewState {
8
+ detection: CodeServerDetection;
9
+ server: CodeServerServerInfo;
10
+ /** Auth handoff; present only once the editor is reachable. */
11
+ auth?: CodeServerAuth;
12
+ /** A request (launch / re-check) is in flight. */
13
+ busy?: boolean;
14
+ }
15
+ /** User intents the view surfaces. */
16
+ interface CodeServerViewActions {
17
+ launch: () => void;
18
+ recheck: () => void;
19
+ }
20
+ interface CodeServerViewOptions {
21
+ actions?: Partial<CodeServerViewActions>;
22
+ /**
23
+ * Build the editor iframe URL from the running port. Defaults to the
24
+ * current page host so the cookie set on this origin reaches the iframe.
25
+ */
26
+ resolveEditorUrl?: (port: number) => string;
27
+ /**
28
+ * Apply code-server's session cookie before the iframe loads. Defaults to
29
+ * `document.cookie`. Storybook overrides this to a no-op.
30
+ */
31
+ applyAuth?: (auth: CodeServerAuth) => void;
32
+ }
33
+ /** Discrete UI states, one per Storybook story. */
34
+ type CodeServerPhase = 'connecting' | 'not-installed' | 'launch' | 'starting' | 'running';
35
+ interface CodeServerViewHandle {
36
+ /** Re-render from a new state (cheap; reuses the live iframe when unchanged). */
37
+ update: (state: CodeServerViewState) => void;
38
+ dispose: () => void;
39
+ }
40
+ /** Map a state to its discrete UI phase. */
41
+ declare function resolvePhase(state: CodeServerViewState): CodeServerPhase;
42
+ /**
43
+ * Create the code-server launcher view inside `container`. The view is a pure
44
+ * function of {@link CodeServerViewState}: callers (the SPA via
45
+ * `mountCodeServer`, or Storybook) push state with `update()` and wire intent
46
+ * through `options.actions`. It performs no RPC and owns no process state, so
47
+ * every UI phase can be rendered in isolation.
48
+ */
49
+ declare function createCodeServerView(container: HTMLElement, options?: CodeServerViewOptions): CodeServerViewHandle;
50
+ //#endregion
51
+ //#region src/client/index.d.ts
52
+ interface MountCodeServerOptions {
53
+ /** Pre-connected client. When omitted, `connectDevframe()` is awaited. */
54
+ rpc?: DevframeRpcClient;
55
+ }
56
+ interface CodeServerHandle {
57
+ rpc: DevframeRpcClient;
58
+ /** Stop the code-server process (no UI control is rendered over the editor). */
59
+ stop: () => Promise<void>;
60
+ dispose: () => void;
61
+ }
62
+ /**
63
+ * Mount the code-server launcher into `container`. Connects to the devframe
64
+ * backend, then drives a {@link createCodeServerView} from the live
65
+ * detection/server state: install instructions when the binary is missing, a
66
+ * launch screen when stopped, a progress state while starting, and the editor
67
+ * in a full-bleed, auto-authenticated iframe once running.
68
+ *
69
+ * The connection is assumed already authorized with devframe's auth, so the
70
+ * server-issued session cookie is applied transparently before the iframe
71
+ * loads — the user never sees code-server's login page.
72
+ */
73
+ declare function mountCodeServer(container: HTMLElement, options?: MountCodeServerOptions): Promise<CodeServerHandle>;
74
+ //#endregion
75
+ export { CodeServerHandle, type CodeServerPhase, type CodeServerServerInfo, type CodeServerSharedState, type CodeServerStatus, type CodeServerViewActions, type CodeServerViewHandle, type CodeServerViewOptions, type CodeServerViewState, MountCodeServerOptions, STATE_KEY, createCodeServerView, mountCodeServer, resolvePhase };
@@ -0,0 +1,354 @@
1
+ import { connectDevframe } from "devframe/client";
2
+ import { createIframePanes } from "iframe-pane";
3
+ //#region src/constants.ts
4
+ /**
5
+ * Shared-state key holding the serializable, secret-free server status and
6
+ * detection result. The authentication cookie is never published here — it
7
+ * is returned only from the `start` / `status` RPCs to the already-authorized
8
+ * client (see {@link CodeServerAuth}).
9
+ */
10
+ const STATE_KEY = "devframes-plugin-code-server:state";
11
+ //#endregion
12
+ //#region src/client/design.ts
13
+ function cx(...parts) {
14
+ return parts.filter(Boolean).join(" ");
15
+ }
16
+ function button({ variant = "primary", size = "md", class: extra } = {}) {
17
+ const variantClass = {
18
+ primary: "btn-primary",
19
+ secondary: "btn-action",
20
+ outline: "btn-action",
21
+ ghost: "inline-flex items-center justify-center gap-1.5 rounded px2 py1 op75 hover:op100 hover:bg-active transition disabled:pointer-events-none disabled:op30!",
22
+ destructive: "btn-action text-error border-error/30!",
23
+ link: "inline-flex items-center gap-1.5 color-active hover:underline underline-offset-2"
24
+ };
25
+ const sizeClass = size === "sm" ? variant === "primary" ? "text-sm px-2.5! py-1!" : "text-sm" : size === "lg" ? "text-base px-4! py-2!" : "";
26
+ return cx(variantClass[variant], sizeClass, extra);
27
+ }
28
+ function link(extra) {
29
+ return cx("color-active hover:underline underline-offset-2", extra);
30
+ }
31
+ function spinner(extra) {
32
+ return cx("inline-block size-4 rounded-full border-2 border-current border-t-transparent animate-spin", extra);
33
+ }
34
+ //#endregion
35
+ //#region src/client/view.ts
36
+ const DOCS_URL = "https://coder.com/docs/code-server/latest/install";
37
+ const REPO_URL = "https://github.com/coder/code-server";
38
+ const panes = createIframePanes();
39
+ let paneCounter = 0;
40
+ const INSTALL_COMMANDS = [
41
+ {
42
+ label: "Install script (Linux / macOS)",
43
+ command: "curl -fsSL https://code-server.dev/install.sh | sh"
44
+ },
45
+ {
46
+ label: "npm",
47
+ command: "npm install -g code-server"
48
+ },
49
+ {
50
+ label: "Homebrew",
51
+ command: "brew install code-server"
52
+ }
53
+ ];
54
+ /** Map a state to its discrete UI phase. */
55
+ function resolvePhase(state) {
56
+ const { detection, server, auth, busy } = state;
57
+ if (server.status === "running" && server.port != null) return auth ? "running" : "starting";
58
+ if (server.status === "starting" || busy && server.status !== "error") return "starting";
59
+ if (!detection.checked) return "connecting";
60
+ if (!detection.installed) return "not-installed";
61
+ return "launch";
62
+ }
63
+ function el(tag, className, text) {
64
+ const node = document.createElement(tag);
65
+ if (className) node.className = className;
66
+ if (text != null) node.textContent = text;
67
+ return node;
68
+ }
69
+ /** A Phosphor icon (UnoCSS presetIcons), matching the other devframe plugins. */
70
+ function icon(name, cls = "") {
71
+ return el("span", cx(name, "shrink-0", cls));
72
+ }
73
+ /** The uppercase "code-server" eyebrow, prefixed with the brand icon. */
74
+ function eyebrow() {
75
+ const p = el("p", "flex items-center gap-1.5 mb-2.5 text-xs tracking-wider uppercase color-muted");
76
+ p.append(icon("i-ph-code-duotone", "text-sm"), document.createTextNode("code-server"));
77
+ return p;
78
+ }
79
+ function defaultEditorUrl(port) {
80
+ return `${location.protocol}//${location.hostname}:${port}/`;
81
+ }
82
+ function defaultApplyAuth(auth) {
83
+ document.cookie = `${auth.cookieName}=${auth.cookieValue}; path=/; SameSite=Lax`;
84
+ }
85
+ /**
86
+ * Create the code-server launcher view inside `container`. The view is a pure
87
+ * function of {@link CodeServerViewState}: callers (the SPA via
88
+ * `mountCodeServer`, or Storybook) push state with `update()` and wire intent
89
+ * through `options.actions`. It performs no RPC and owns no process state, so
90
+ * every UI phase can be rendered in isolation.
91
+ */
92
+ function createCodeServerView(container, options = {}) {
93
+ const resolveEditorUrl = options.resolveEditorUrl ?? defaultEditorUrl;
94
+ const applyAuth = options.applyAuth ?? defaultApplyAuth;
95
+ const launch = () => options.actions?.launch?.();
96
+ const recheck = () => options.actions?.recheck?.();
97
+ const root = el("div", "absolute inset-0 flex flex-col of-hidden bg-base color-base font-sans");
98
+ container.append(root);
99
+ const paneId = `code-server-editor-${++paneCounter}`;
100
+ const editorTarget = el("div", "dcs-frame flex-1 w-full");
101
+ let editorUrl = null;
102
+ function shell(...children) {
103
+ panes.get(paneId)?.unmount();
104
+ editorUrl = null;
105
+ const center = el("div", "flex-1 flex items-center justify-center p-6");
106
+ const card = el("div", "w-full max-w-[560px]");
107
+ card.append(...children);
108
+ center.append(card);
109
+ root.replaceChildren(center);
110
+ }
111
+ function renderConnecting() {
112
+ const wrap = el("div", "flex items-center gap-2.5");
113
+ wrap.append(el("div", spinner("color-muted")), el("span", "text-sm color-muted", "Connecting to devframe…"));
114
+ shell(wrap);
115
+ }
116
+ function renderNotInstalled(busy) {
117
+ const nodes = [];
118
+ nodes.push(eyebrow());
119
+ nodes.push(el("h1", "mb-2 text-[1.35rem] font-semibold", "code-server is not installed"));
120
+ nodes.push(el("p", "mb-5 text-sm leading-relaxed color-muted", "Install code-server (VS Code in the browser) to open the editor here. Pick whichever fits your setup, then re-check."));
121
+ const install = el("div", "flex flex-col gap-3 mb-5");
122
+ for (const { label, command } of INSTALL_COMMANDS) {
123
+ const row = el("div");
124
+ row.append(el("label", "block mb-1 text-xs color-muted", label));
125
+ const cmd = el("div", "flex items-center gap-2 px-2.5 py-2 rounded-md border border-base bg-secondary");
126
+ cmd.append(el("code", "flex-1 of-x-auto whitespace-nowrap font-mono text-xs color-base", command));
127
+ const copy = el("button", button({
128
+ variant: "outline",
129
+ size: "sm",
130
+ class: "shrink-0 text-[11px]"
131
+ }), "Copy");
132
+ copy.onclick = () => {
133
+ navigator.clipboard?.writeText(command).then(() => {
134
+ copy.textContent = "Copied";
135
+ setTimeout(() => {
136
+ copy.textContent = "Copy";
137
+ }, 1200);
138
+ }).catch(() => {});
139
+ };
140
+ cmd.append(copy);
141
+ row.append(cmd);
142
+ install.append(row);
143
+ }
144
+ nodes.push(install);
145
+ const actions = el("div", "flex flex-wrap items-center gap-2.5");
146
+ const recheckBtn = el("button", button({ variant: "primary" }));
147
+ recheckBtn.append(icon("i-ph-arrows-clockwise", cx("size-4", busy && "animate-spin")), document.createTextNode(busy ? "Checking…" : "Re-check"));
148
+ recheckBtn.disabled = busy;
149
+ recheckBtn.onclick = recheck;
150
+ actions.append(recheckBtn);
151
+ actions.append(link$1("Installation docs", DOCS_URL));
152
+ actions.append(link$1("GitHub", REPO_URL));
153
+ nodes.push(actions);
154
+ shell(...nodes);
155
+ }
156
+ function renderLaunch(state) {
157
+ const { detection, server, busy } = state;
158
+ const nodes = [];
159
+ nodes.push(eyebrow());
160
+ nodes.push(el("h1", "mb-2 text-[1.35rem] font-semibold", "Launch the editor"));
161
+ nodes.push(el("p", "mb-5 text-sm leading-relaxed color-muted", "Start a code-server instance scoped to this workspace and open VS Code right here. The server runs with a generated password and the editor is signed in automatically."));
162
+ if (server.status === "error" && server.error) nodes.push(el("div", "mb-4 px-3 py-2.5 rounded-md border border-error/35 bg-error/10 text-error text-sm whitespace-pre-wrap break-words", server.error));
163
+ const meta = el("div", "flex flex-wrap gap-x-4 gap-y-1.5 mb-5 text-xs color-muted");
164
+ if (detection.version) {
165
+ const v = el("span");
166
+ v.append(document.createTextNode("version "), el("code", "font-mono color-base", detection.version));
167
+ meta.append(v);
168
+ }
169
+ const b = el("span");
170
+ b.append(document.createTextNode("binary "), el("code", "font-mono color-base", detection.bin));
171
+ meta.append(b);
172
+ nodes.push(meta);
173
+ const actions = el("div", "flex flex-wrap items-center gap-2.5");
174
+ const launchBtn = el("button", button({ variant: "primary" }));
175
+ if (busy) launchBtn.append(el("span", spinner()), document.createTextNode("Starting…"));
176
+ else launchBtn.append(icon("i-ph-rocket-launch-duotone", "size-4"), document.createTextNode("Launch code-server"));
177
+ launchBtn.disabled = busy ?? false;
178
+ launchBtn.onclick = launch;
179
+ actions.append(launchBtn);
180
+ nodes.push(actions);
181
+ shell(...nodes);
182
+ }
183
+ function renderStarting() {
184
+ const wrap = el("div", "flex items-center gap-2.5");
185
+ wrap.append(el("div", spinner("color-muted")), el("span", "text-sm color-muted", "Starting code-server…"));
186
+ shell(wrap);
187
+ }
188
+ function renderRunning(state) {
189
+ const url = resolveEditorUrl(state.server.port);
190
+ if (editorUrl !== url) {
191
+ if (state.auth) applyAuth(state.auth);
192
+ editorUrl = url;
193
+ }
194
+ const pane = panes.ensure(paneId, {
195
+ src: url,
196
+ attrs: { allow: "clipboard-read; clipboard-write; cross-origin-isolated" },
197
+ style: { border: "0" }
198
+ });
199
+ if (pane.iframe.src !== url) pane.iframe.src = url;
200
+ if (root.firstElementChild !== editorTarget) root.replaceChildren(editorTarget);
201
+ pane.mount(editorTarget);
202
+ }
203
+ function link$1(text, href) {
204
+ const a = el("a", link("inline-flex items-center gap-1 text-sm"));
205
+ a.append(document.createTextNode(text), icon("i-ph-arrow-square-out", "size-3.5"));
206
+ a.href = href;
207
+ a.target = "_blank";
208
+ a.rel = "noreferrer";
209
+ return a;
210
+ }
211
+ function update(state) {
212
+ switch (resolvePhase(state)) {
213
+ case "running":
214
+ renderRunning(state);
215
+ break;
216
+ case "starting":
217
+ renderStarting();
218
+ break;
219
+ case "connecting":
220
+ renderConnecting();
221
+ break;
222
+ case "not-installed":
223
+ renderNotInstalled(state.busy ?? false);
224
+ break;
225
+ case "launch":
226
+ renderLaunch(state);
227
+ break;
228
+ }
229
+ }
230
+ return {
231
+ update,
232
+ dispose() {
233
+ panes.get(paneId)?.dispose();
234
+ root.remove();
235
+ }
236
+ };
237
+ }
238
+ //#endregion
239
+ //#region src/client/index.ts
240
+ /**
241
+ * Mount the code-server launcher into `container`. Connects to the devframe
242
+ * backend, then drives a {@link createCodeServerView} from the live
243
+ * detection/server state: install instructions when the binary is missing, a
244
+ * launch screen when stopped, a progress state while starting, and the editor
245
+ * in a full-bleed, auto-authenticated iframe once running.
246
+ *
247
+ * The connection is assumed already authorized with devframe's auth, so the
248
+ * server-issued session cookie is applied transparently before the iframe
249
+ * loads — the user never sees code-server's login page.
250
+ */
251
+ async function mountCodeServer(container, options = {}) {
252
+ const rpc = options.rpc ?? await connectDevframe();
253
+ if (rpc.connectionMeta.backend === "websocket") await rpc.ensureTrusted(5e3).catch(() => {});
254
+ let detection = {
255
+ checked: false,
256
+ installed: false,
257
+ bin: "code-server"
258
+ };
259
+ let server = { status: "stopped" };
260
+ let auth;
261
+ let busy = false;
262
+ let disposed = false;
263
+ const view = createCodeServerView(container, { actions: {
264
+ launch,
265
+ recheck
266
+ } });
267
+ function sync() {
268
+ if (disposed) return;
269
+ view.update({
270
+ detection,
271
+ server,
272
+ auth,
273
+ busy
274
+ });
275
+ }
276
+ function applyResult(result) {
277
+ detection = result.detection;
278
+ server = result.server;
279
+ if (result.auth) auth = result.auth;
280
+ }
281
+ async function call(method, ...args) {
282
+ try {
283
+ return await rpc.call(method, ...args);
284
+ } catch (error) {
285
+ server = {
286
+ status: "error",
287
+ error: error instanceof Error ? error.message : String(error)
288
+ };
289
+ return;
290
+ }
291
+ }
292
+ async function launch() {
293
+ if (busy) return;
294
+ busy = true;
295
+ sync();
296
+ const result = await call("devframes-plugin-code-server:start", {});
297
+ if (result) applyResult(result);
298
+ busy = false;
299
+ sync();
300
+ }
301
+ async function stop() {
302
+ if (busy) return;
303
+ busy = true;
304
+ sync();
305
+ const result = await call("devframes-plugin-code-server:stop");
306
+ if (result) applyResult(result);
307
+ auth = void 0;
308
+ busy = false;
309
+ sync();
310
+ }
311
+ async function recheck() {
312
+ if (busy) return;
313
+ busy = true;
314
+ sync();
315
+ await call("devframes-plugin-code-server:detect");
316
+ busy = false;
317
+ sync();
318
+ }
319
+ sync();
320
+ const initial = await call("devframes-plugin-code-server:status");
321
+ if (initial) applyResult(initial);
322
+ sync();
323
+ const state = await rpc.sharedState.get(STATE_KEY, { initialValue: {
324
+ detection,
325
+ server
326
+ } });
327
+ const current = state.value();
328
+ detection = current.detection ?? detection;
329
+ server = current.server ?? server;
330
+ const off = state.on("updated", (full) => {
331
+ detection = full.detection ?? detection;
332
+ server = full.server ?? server;
333
+ if (server.status === "running" && !auth) {
334
+ call("devframes-plugin-code-server:status").then((result) => {
335
+ if (result?.auth) auth = result.auth;
336
+ sync();
337
+ });
338
+ return;
339
+ }
340
+ sync();
341
+ });
342
+ sync();
343
+ return {
344
+ rpc,
345
+ stop,
346
+ dispose() {
347
+ disposed = true;
348
+ off?.();
349
+ view.dispose();
350
+ }
351
+ };
352
+ }
353
+ //#endregion
354
+ export { STATE_KEY, createCodeServerView, mountCodeServer, resolvePhase };
@@ -0,0 +1,36 @@
1
+ //#region src/constants.d.ts
2
+ /** Stable devframe id for the code-server plugin. */
3
+ declare const PLUGIN_ID = "devframes-plugin-code-server";
4
+ /**
5
+ * Shared-state key holding the serializable, secret-free server status and
6
+ * detection result. The authentication cookie is never published here — it
7
+ * is returned only from the `start` / `status` RPCs to the already-authorized
8
+ * client (see {@link CodeServerAuth}).
9
+ */
10
+ declare const STATE_KEY = "devframes-plugin-code-server:state";
11
+ /** Default dev-server port for the plugin's own launcher SPA (standalone CLI). */
12
+ declare const DEFAULT_PORT = 9013;
13
+ /** Preferred port for the spawned code-server process (falls back if taken). */
14
+ declare const DEFAULT_CODE_SERVER_PORT = 8080;
15
+ /** How long to wait for code-server to answer its `/healthz` probe. */
16
+ declare const DEFAULT_START_TIMEOUT = 30000;
17
+ /** code-server's session cookie base name (see `getCookieSessionName`). */
18
+ declare const SESSION_COOKIE_BASE = "code-server-session";
19
+ /**
20
+ * Title for the read-only terminal session surfaced when the plugin is mounted
21
+ * in a hub (`ctx.terminals`). Mirrors the plugin's own name.
22
+ */
23
+ declare const TERMINAL_SESSION_TITLE = "Code Server";
24
+ /**
25
+ * Icon for that terminal session. Mirrors the plugin's declared icon so the
26
+ * hub's terminals panel shows the same glyph as the rest of the plugin.
27
+ */
28
+ declare const TERMINAL_SESSION_ICON = "ph:code-duotone";
29
+ /**
30
+ * Compute code-server's session cookie name for an optional `--cookie-suffix`.
31
+ * Mirrors code-server's own `getCookieSessionName` so the client sets the
32
+ * exact cookie the server reads.
33
+ */
34
+ declare function getCookieSessionName(suffix?: string): string;
35
+ //#endregion
36
+ export { SESSION_COOKIE_BASE as a, TERMINAL_SESSION_TITLE as c, PLUGIN_ID as i, getCookieSessionName as l, DEFAULT_PORT as n, STATE_KEY as o, DEFAULT_START_TIMEOUT as r, TERMINAL_SESSION_ICON as s, DEFAULT_CODE_SERVER_PORT as t };
@@ -0,0 +1,2 @@
1
+ import { a as SESSION_COOKIE_BASE, c as TERMINAL_SESSION_TITLE, i as PLUGIN_ID, l as getCookieSessionName, n as DEFAULT_PORT, o as STATE_KEY, r as DEFAULT_START_TIMEOUT, s as TERMINAL_SESSION_ICON, t as DEFAULT_CODE_SERVER_PORT } from "./constants-ICLvdiRe.mjs";
2
+ export { DEFAULT_CODE_SERVER_PORT, DEFAULT_PORT, DEFAULT_START_TIMEOUT, PLUGIN_ID, SESSION_COOKIE_BASE, STATE_KEY, TERMINAL_SESSION_ICON, TERMINAL_SESSION_TITLE, getCookieSessionName };
@@ -0,0 +1,38 @@
1
+ //#region src/constants.ts
2
+ /** Stable devframe id for the code-server plugin. */
3
+ const PLUGIN_ID = "devframes-plugin-code-server";
4
+ /**
5
+ * Shared-state key holding the serializable, secret-free server status and
6
+ * detection result. The authentication cookie is never published here — it
7
+ * is returned only from the `start` / `status` RPCs to the already-authorized
8
+ * client (see {@link CodeServerAuth}).
9
+ */
10
+ const STATE_KEY = "devframes-plugin-code-server:state";
11
+ /** Default dev-server port for the plugin's own launcher SPA (standalone CLI). */
12
+ const DEFAULT_PORT = 9013;
13
+ /** Preferred port for the spawned code-server process (falls back if taken). */
14
+ const DEFAULT_CODE_SERVER_PORT = 8080;
15
+ /** How long to wait for code-server to answer its `/healthz` probe. */
16
+ const DEFAULT_START_TIMEOUT = 3e4;
17
+ /** code-server's session cookie base name (see `getCookieSessionName`). */
18
+ const SESSION_COOKIE_BASE = "code-server-session";
19
+ /**
20
+ * Title for the read-only terminal session surfaced when the plugin is mounted
21
+ * in a hub (`ctx.terminals`). Mirrors the plugin's own name.
22
+ */
23
+ const TERMINAL_SESSION_TITLE = "Code Server";
24
+ /**
25
+ * Icon for that terminal session. Mirrors the plugin's declared icon so the
26
+ * hub's terminals panel shows the same glyph as the rest of the plugin.
27
+ */
28
+ const TERMINAL_SESSION_ICON = "ph:code-duotone";
29
+ /**
30
+ * Compute code-server's session cookie name for an optional `--cookie-suffix`.
31
+ * Mirrors code-server's own `getCookieSessionName` so the client sets the
32
+ * exact cookie the server reads.
33
+ */
34
+ function getCookieSessionName(suffix) {
35
+ return suffix ? `${SESSION_COOKIE_BASE}-${suffix.replace(/[^a-z0-9-]/gi, "-")}` : SESSION_COOKIE_BASE;
36
+ }
37
+ //#endregion
38
+ export { DEFAULT_CODE_SERVER_PORT, DEFAULT_PORT, DEFAULT_START_TIMEOUT, PLUGIN_ID, SESSION_COOKIE_BASE, STATE_KEY, TERMINAL_SESSION_ICON, TERMINAL_SESSION_TITLE, getCookieSessionName };