Skip to content

Commit c6d838b

Browse files
committed
web: --api-url, and how to try it against a real backend
The extension already supports an API host that is not the account origin (levelcode.cloud.apiUrl); the build can now bake it in, which is what a local account site with the backend behind a tunnel needs. docs/WEB.md says how to run the real stack locally and what to look for, and what was and was not run.
1 parent 9c86c94 commit c6d838b

2 files changed

Lines changed: 46 additions & 5 deletions

File tree

‎docs/WEB.md‎

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ node web/serve.mjs --dist dist-web # http://127.0.0.1:8800
140140
| `build-web.mjs` flag | Meaning |
141141
| --- | --- |
142142
| `--account <origin>` (`LEVELCODE_WEB_ACCOUNT`) | The LevelCode Cloud origin: where sign-in happens, and where the gateway is. Baked into `index.html`. |
143+
| `--api-url <origin>` (`LEVELCODE_WEB_API_URL`) | Only when the API is not on the account origin (production has one host for both): a local account site with the backend behind a tunnel, say. The gateway must be `https`. |
143144
| `--webview-origin 'https://{{uuid}}.view.example.com'` (`LEVELCODE_WEB_WEBVIEW_ORIGIN`) | Self-host the webview origin. Without it the page uses Code-OSS's CDN at this build's own commit. Needs wildcard DNS and TLS — see below. |
144145
| `--ext-host-origin 'https://{{uuid}}.ext.example.com'` (`LEVELCODE_WEB_EXTHOST_ORIGIN`) | Run the extension host on an origin of its own. Optional while no third-party code is loaded. |
145146
| `--static <dir>` | Use a prebuilt client (skips the gulp build). |
@@ -271,16 +272,53 @@ webview from Code-OSS's CDN), and against the development server with the webvie
271272
host on origins of their own (`--product` with `webviewContentExternalBaseUrlTemplate` and
272273
`webEndpointUrlTemplate`).
273274

275+
### Trying it yourself, against a real backend
276+
277+
The repo's checks use a stand-in for the account site. To use the real one, locally:
278+
279+
1. **Backend** — the thin.ly branch with the web edition, its usual dev server and https tunnel, plus
280+
`LEVELCODE_WEB_EDITOR_ORIGINS=http://localhost:8800` and `LEVELCODE_WEB_EDITOR_URL=http://localhost:8800/`.
281+
(The gateway refuses plain http, so the editor's API host has to be the tunnel.)
282+
2. **Account site** — the onetime branch: `npm run dev` (it proxies to the backend), at `http://localhost:5173/ai`.
283+
3. **Editor:**
284+
285+
```bash
286+
node scripts/build-web.mjs --account http://localhost:5173 --api-url https://<your tunnel> --out dist-web
287+
node web/serve.mjs --dist dist-web # http://localhost:8800
288+
```
289+
290+
4. **What to do, and what you should see:**
291+
- Signed out everywhere: open `http://localhost:8800/?signin=1`. The tab goes to the account site's
292+
sign-in page ("Connect the editor to your account…"). Sign in with an email code. The tab comes back to
293+
the editor with a clean address, a "Signed in to LevelCode." message, and the model and plan in the chat footer.
294+
- Signed in on the account site first: open `http://localhost:5173/ai/account` and use **Open in browser**.
295+
The editor opens, the tab flashes through the login page ("Opening LevelCode…") and is back signed in,
296+
with nothing typed.
297+
- Ask the agent: *Create a file named hello.txt containing: hello.* The file appears in the Explorer.
298+
- **LevelCode: Open Folder from Your Computer…** (palette) in Chrome or Edge: pick a folder, ask the agent
299+
to read a file of it and write another.
300+
- A second tab of the editor opens already signed in (it shares the session).
301+
302+
Look at the browser's network panel while you do it: every call to the API should be `200`, with no CORS
303+
errors in the console. A CORS error means the editor's origin is not in `LEVELCODE_WEB_EDITOR_ORIGINS`.
304+
305+
This was run once, by a hand-driven script, against Rails from the thin.ly branch in test mode (email and
306+
Stripe stubbed, a throwaway database): email-code sign-in from the editor, sign-in from an already
307+
signed-in browser, a gateway chat, the agent writing a file, and a folder opened through a real
308+
`FileSystemDirectoryHandle` (the origin-private file system stands in for the OS picker). That script needs
309+
the other repository's internals and is not part of this repo's checks.
310+
274311
### What was not run
275312

276313
Say these out loud rather than discover them in production:
277314

278315
- **A real browser other than headless Chrome.** Safari and Firefox are untested; so are phones.
279-
- **The File System Access path** (open a folder from the computer): it needs a user gesture and a
280-
picker that headless Chrome does not offer. The code path is the editor's own (`showDirectoryPicker`),
281-
and the agent's tools reach it through `vscode.workspace.fs` as for the scratch workspace.
282-
- **The real account SPA and the real Rails app** in the same-tab flow. The stand-in plays "already
283-
signed in there"; the backend's rules are its own specs, run in a separate checkout.
316+
- **The operating system's folder picker itself.** It needs a person. Everything after it — the command, the
317+
workbench's `file` provider on a real `FileSystemDirectoryHandle`, the agent's list, read and write — is run
318+
(`web/test/e2e.mjs` section 5), with the origin-private file system standing in for the picker. Permission
319+
handling on a *persisted* handle after a reload (the browser asks again) is the browser's and was not run.
320+
- **A production account site.** The real sign-in page and Rails were run locally (above), not the deployed
321+
ones; the backend's rules are its own specs.
284322
- **Another provider's CORS.** Anthropic's browser-access header is set; whether a given provider
285323
answers a page's request is that provider's.
286324
- **Production hosting:** DNS, a wildcard certificate, the CDN in front, the real headers on the real host.

‎web/lib/config.mjs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export const WEB_DIR = path.dirname(path.dirname(fileURLToPath(import.meta.url))
99
/**
1010
* @param {object} o
1111
* @param {string} [o.account] LevelCode Cloud origin the editor signs in to and calls (the gateway lives there).
12+
* @param {string} [o.apiUrl] The API host, when it is not the account origin (the extension's levelcode.cloud.apiUrl):
13+
* a tunnel to a local backend, say. The gateway must be https.
1214
* @param {string[]} [o.extensions] Folder names, under /extensions, of the extensions loaded as built-ins.
1315
* @param {string} [o.staticBase] Where the Code-OSS web build is mounted.
1416
* @param {object} [o.productConfiguration] Overrides merged over the product the build embeds.
@@ -39,6 +41,7 @@ export function webConfig(o = {}) {
3941
}, o.productConfiguration),
4042
configurationDefaults: Object.assign({
4143
'levelcode.cloud.endpoint': account,
44+
...(o.apiUrl ? { 'levelcode.cloud.apiUrl': trimSlash(o.apiUrl) } : {}),
4245
// LevelCode is the assistant here; the stock Copilot surfaces (status item, chat view, command
4346
// center) are for an account this editor does not use. The desktop app sets the same.
4447
'chat.disableAIFeatures': true,

0 commit comments

Comments
 (0)