arcane-os 0.7.2 → 0.7.3
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/README.md +23 -15
- package/docs/architecture.md +11 -1
- package/docs/reference/ai/browser-speech.md +2 -1
- package/docs/reference/cli.md +42 -7
- package/docs/reference/runtime-components.md +3 -2
- package/docs/reference/sdk-api.md +28 -4
- package/package.json +1 -1
- package/runtime/arcane/components/browser-ai-setup.html +1 -1
- package/src/cli/main.mjs +19 -2
- package/src/dev-server.mjs +63 -6
- package/src/toolchain.mjs +4 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.3
|
|
4
|
+
|
|
5
|
+
- Correct public development serving for OPFS/DBOPFS and other browser APIs
|
|
6
|
+
requiring a secure context. `npm run dev -- --app <id> --public` now serves
|
|
7
|
+
HTTPS using a workspace-local development certificate pair, with explicit
|
|
8
|
+
`--cert`/`--key` paths and `--https` available for any selected app.
|
|
9
|
+
- Add HTTPS transport to the shared development server and report the actual
|
|
10
|
+
protocol in local and network URLs. Preserve ordinary HTTP localhost,
|
|
11
|
+
selected source/runtime routes, refresh behavior, and owned cancellation.
|
|
12
|
+
- Document certificate setup and Android CA installation. Keep TLS material
|
|
13
|
+
out of operation events and report missing certificates before binding.
|
|
14
|
+
- Clarify the browser accelerator setup instruction to close and reopen the
|
|
15
|
+
browser after changing a flag, with that final step underlined and emphasized.
|
|
16
|
+
|
|
3
17
|
## 0.7.2
|
|
4
18
|
|
|
5
19
|
- Keep both supported browser NPU and GPU flag address copy controls available
|
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
|
|
|
19
19
|
`arcane/` runtime. Both profiles preserve the same app URLs, theme, packaging,
|
|
20
20
|
event, cancellation, and browser run contracts.
|
|
21
21
|
|
|
22
|
-
This checkout defines the `0.7.
|
|
22
|
+
This checkout defines the `0.7.3` SDK contract. Applications pin one exact npm
|
|
23
23
|
version and lockfile; registry state is deliberately not baked into application
|
|
24
24
|
artifacts.
|
|
25
25
|
|
|
@@ -35,7 +35,7 @@ Create one browser application, install its pinned SDK, and start its source
|
|
|
35
35
|
server:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
npx arcane-os@0.7.
|
|
38
|
+
npx arcane-os@0.7.3 new hello-speech --path ./hello-speech --target browser
|
|
39
39
|
cd hello-speech
|
|
40
40
|
npm install
|
|
41
41
|
npm run dev
|
|
@@ -49,12 +49,20 @@ npm run dev -- --app hello-speech --public
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
Replace `hello-speech` with any app in the workspace. The server binds to
|
|
52
|
-
`0.0.0.0` and
|
|
53
|
-
|
|
54
|
-
address, and
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
`0.0.0.0` and serves HTTPS using `.arcane/dev/server-cert.pem` and
|
|
53
|
+
`.arcane/dev/server-key.pem` in the workspace. Supply a certificate covering the
|
|
54
|
+
LAN address or hostname you will open, and trust its issuing development CA on
|
|
55
|
+
each device. Keep these local files ignored by Git. The same certificate pair
|
|
56
|
+
works for any selected app in that workspace. The command reports missing TLS
|
|
57
|
+
files instead of starting an HTTP listener.
|
|
58
|
+
|
|
59
|
+
Plain `npm run dev` keeps its HTTP localhost default. An explicit `--host`
|
|
60
|
+
overrides the public bind address, and `--port` selects the port. Use `--https`
|
|
61
|
+
for HTTPS on localhost, or `--cert <file> --key <file>` for an explicit PEM pair;
|
|
62
|
+
relative paths resolve from the workspace. The server prints HTTPS network URLs
|
|
63
|
+
for public mode. Network reachability depends on the machine's firewall and
|
|
64
|
+
network. See the [development HTTPS setup](docs/reference/cli.md#development-https-setup)
|
|
65
|
+
for device trust and DBOPFS's secure-context requirement.
|
|
58
66
|
|
|
59
67
|
Open the URL printed by the server. The generated page owns its import map and
|
|
60
68
|
Arcane theme; its application module is `apps/hello-speech/modules/App.js`.
|
|
@@ -378,7 +386,7 @@ uses the same controller for automatic memory extraction.
|
|
|
378
386
|
Create a new repository-shaped Arcane application with the exact stable SDK:
|
|
379
387
|
|
|
380
388
|
```bash
|
|
381
|
-
npx arcane-os@0.7.
|
|
389
|
+
npx arcane-os@0.7.3 new my-app --path ./my-app --target portable --git
|
|
382
390
|
cd my-app
|
|
383
391
|
npm install
|
|
384
392
|
npm run dev
|
|
@@ -388,7 +396,7 @@ To enroll an existing repository, install the exact SDK and initialize only
|
|
|
388
396
|
missing Arcane files:
|
|
389
397
|
|
|
390
398
|
```bash
|
|
391
|
-
npm install --save-dev --save-exact arcane-os@0.7.
|
|
399
|
+
npm install --save-dev --save-exact arcane-os@0.7.3
|
|
392
400
|
npm exec -- arcane init my-app --target portable
|
|
393
401
|
```
|
|
394
402
|
|
|
@@ -404,7 +412,7 @@ npm exec -- arcane-os targets
|
|
|
404
412
|
No global SDK install or standalone Arcane CLI is required. The application
|
|
405
413
|
repository's exact npm dependency and lockfile own the CLI and toolchain version.
|
|
406
414
|
|
|
407
|
-
Use `npx arcane-os@0.7.
|
|
415
|
+
Use `npx arcane-os@0.7.3` for the initial bootstrap because it names this npm
|
|
408
416
|
package explicitly; bare `npx arcane` outside an installed project could resolve
|
|
409
417
|
a different package. Both installed commands invoke the same headless toolchain.
|
|
410
418
|
Project-local npm scripts use the SDK pinned by that app's `package-lock.json`,
|
|
@@ -424,7 +432,7 @@ node ./bin/arcane.mjs new local-app --path ../local-app --target portable --git
|
|
|
424
432
|
|
|
425
433
|
# From the generated app repository
|
|
426
434
|
cd ../local-app
|
|
427
|
-
npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.7.
|
|
435
|
+
npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.7.3.tgz
|
|
428
436
|
npm ci
|
|
429
437
|
```
|
|
430
438
|
|
|
@@ -433,7 +441,7 @@ same location. The lockfile retains the selected package dependency while
|
|
|
433
441
|
Arcane uses the installed package name and version. Local directory `file:` dependencies are not
|
|
434
442
|
accepted because npm may install them as links; use a packed `.tgz`. A GitHub
|
|
435
443
|
runner also needs that tarball at the locked path. After publication, replace
|
|
436
|
-
the local declaration with the exact `arcane-os@0.7.
|
|
444
|
+
the local declaration with the exact `arcane-os@0.7.3` registry package and
|
|
437
445
|
commit the regenerated lock.
|
|
438
446
|
|
|
439
447
|
Generated repositories use `npm ci --ignore-scripts` in CI. Run dependency
|
|
@@ -490,7 +498,7 @@ arcane new <id> [--path <directory>] [--display-name <name>] [--target <target>]
|
|
|
490
498
|
arcane init [id] [--workspace <directory>] [--display-name <name>] [--target <target>]
|
|
491
499
|
arcane doctor [--workspace <directory>] [--arcane-root <directory>]
|
|
492
500
|
arcane import-map [--workspace <directory>] [--app <id>]
|
|
493
|
-
arcane dev [--app <id>] [--public] [--host <address>] [--port 8000]
|
|
501
|
+
arcane dev [--app <id>] [--public] [--https] [--cert <file> --key <file>] [--host <address>] [--port 8000]
|
|
494
502
|
arcane test [--app <id>] [--scope app]
|
|
495
503
|
arcane test --scope shared --test-file <repo-relative.test.mjs>
|
|
496
504
|
arcane check [--app <id>] [--scope app] [--skip-tests]
|
|
@@ -572,7 +580,7 @@ package installation, or assertions.
|
|
|
572
580
|
|
|
573
581
|
## Current target support
|
|
574
582
|
|
|
575
|
-
Version `0.7.
|
|
583
|
+
Version `0.7.3` exposes one browser target and five explicitly paired
|
|
576
584
|
native development targets: a non-runnable portable directory, a
|
|
577
585
|
Windows x64 unsigned-local-test EXE bundle, Linux x64 and Linux ARM64
|
|
578
586
|
unsigned-local-test DEBs, and an Android development-signed APK. The
|
package/docs/architecture.md
CHANGED
|
@@ -116,6 +116,16 @@ files into `dist`, or restarting the server. Restarting is not a content
|
|
|
116
116
|
synchronization step; when a refresh is stale, first verify the command, URL,
|
|
117
117
|
workspace, selected app, and resolved source route.
|
|
118
118
|
|
|
119
|
+
The shared dev server owns HTTP and HTTPS transport for the same selected
|
|
120
|
+
routes. `arcane dev --public` selects HTTPS on the IPv4 wildcard address;
|
|
121
|
+
explicit `--host` controls the bind address, while `--https` selects HTTPS
|
|
122
|
+
without changing it. Public/HTTPS CLI startup reads one workspace-local PEM
|
|
123
|
+
pair before binding. The certificate covers the device-facing address, and
|
|
124
|
+
each client trusts its issuing CA through that platform's certificate setup.
|
|
125
|
+
This supplies the secure origin required by OPFS/DBOPFS on LAN devices. The
|
|
126
|
+
server does not install trust, generate certificates, or expose private TLS
|
|
127
|
+
material through CLI events. Ordinary localhost development remains HTTP.
|
|
128
|
+
|
|
119
129
|
Development is an intentionally fast feedback loop. Keep each increment small
|
|
120
130
|
and independently understandable so its effect has one clear cause and a
|
|
121
131
|
mistake can be isolated without untangling unrelated work. A development
|
|
@@ -279,7 +289,7 @@ paths are withheld from the native provider. The provider copies the complete
|
|
|
279
289
|
selected release rather than accepting an unrelated source path. Verification
|
|
280
290
|
is a separate explicit operation for a selected release artifact.
|
|
281
291
|
|
|
282
|
-
The SDK `0.7.
|
|
292
|
+
The SDK `0.7.3` runtime requires Arcane `0.8.12` or newer. Compatibility
|
|
283
293
|
is contractual rather than exact-version pinning: the prepared Core must meet
|
|
284
294
|
the highest minimum declared by the runtime, selected app, and bundled app
|
|
285
295
|
dependencies; keep each app's Arcane protocol generation; and provide every
|
|
@@ -156,7 +156,8 @@ documents the **Enables WebNN API** flag and model/operator requirements.
|
|
|
156
156
|
The **GPU performance** section shows a short adapter status and a **Copy GPU
|
|
157
157
|
flag address** button on desktop Windows Chromium browsers. The button remains
|
|
158
158
|
available for every adapter class, unavailable API, or detection failure. One
|
|
159
|
-
shared instruction explains pasting, enabling, saving work,
|
|
159
|
+
shared instruction explains pasting, enabling, saving work, then closing the
|
|
160
|
+
browser and reopening it, with the final step underlined and emphasized. Identified
|
|
160
161
|
Chrome uses `chrome://flags/#force-high-performance-gpu`; other recognized
|
|
161
162
|
Chromium browsers use their corresponding internal scheme. **Refresh**
|
|
162
163
|
requests a new availability result; concurrent
|
package/docs/reference/cli.md
CHANGED
|
@@ -49,7 +49,9 @@ meaning and cardinality rules:
|
|
|
49
49
|
| `--app` | app id | Workspace/app operations except shared scope and `verify-bundle`; also the exact `mail serve` caller id. |
|
|
50
50
|
| `--arcane-root` | directory | `doctor`, native `build`/`run`, `native-doctor`, `native-prepare` |
|
|
51
51
|
| `--host` / `--port` | host / integer 0–65535 | Browser `dev`/`run` default to `127.0.0.1:8000`; `mail serve` defaults to `127.0.0.1:8025` and admits numeric loopback only. |
|
|
52
|
-
| `--public` | flag | `dev`; binds to `0.0.0.0` unless `--host` explicitly selects another address. |
|
|
52
|
+
| `--public` | flag | `dev`; serves HTTPS and binds to `0.0.0.0` unless `--host` explicitly selects another address. |
|
|
53
|
+
| `--https` | flag | `dev`; serves HTTPS with the configured or default workspace PEM pair. |
|
|
54
|
+
| `--cert` / `--key` | PEM file paths | `dev`; supply both to select HTTPS with an explicit certificate chain and private key. Relative paths resolve from the workspace. |
|
|
53
55
|
| `--target` | target id | `new`, `init`, native diagnostics, `build`, `run` |
|
|
54
56
|
| `--format` / `--signing` | target-supported values | Native diagnostics, `build`, `run` |
|
|
55
57
|
| `--output-root` | directory | Native `build` and `run` |
|
|
@@ -310,7 +312,7 @@ npm exec -- arcane upgrade --workspace . --app hello-world
|
|
|
310
312
|
|
|
311
313
|
Starts one development server for one selected app and maps the exact
|
|
312
314
|
workspace/runtime routes. It defaults to localhost; `--public` enables access
|
|
313
|
-
from other devices on the network.
|
|
315
|
+
from other devices on the network over HTTPS.
|
|
314
316
|
|
|
315
317
|
For an external workspace, the server exposes the selected projected
|
|
316
318
|
`arcane/` root, including `arcane/sdk` and `arcane/dependencies`, alongside the
|
|
@@ -319,7 +321,7 @@ The explicit live-source SDK mapping remains unchanged and does not replace the
|
|
|
319
321
|
installed projection.
|
|
320
322
|
|
|
321
323
|
```text
|
|
322
|
-
arcane dev [--app <id>] [--public] [--host <address>] [--port 8000]
|
|
324
|
+
arcane dev [--app <id>] [--public] [--https] [--cert <file> --key <file>] [--host <address>] [--port 8000]
|
|
323
325
|
```
|
|
324
326
|
|
|
325
327
|
### Lifecycle
|
|
@@ -333,10 +335,43 @@ device, since `localhost` refers to that device and `0.0.0.0` is a bind address.
|
|
|
333
335
|
Network URLs come from one interface snapshot at startup and do not establish
|
|
334
336
|
remote reachability through the machine's firewall or network.
|
|
335
337
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
338
|
+
Public mode also selects HTTPS. Plain localhost development stays HTTP.
|
|
339
|
+
`--https` selects HTTPS without changing the bind address; supplying both
|
|
340
|
+
`--cert` and `--key` also selects HTTPS. The command does not configure a
|
|
341
|
+
firewall, router forwarding, or an internet tunnel.
|
|
342
|
+
|
|
343
|
+
### Development HTTPS setup
|
|
344
|
+
|
|
345
|
+
Before starting public mode, place the development server's PEM certificate
|
|
346
|
+
chain at `.arcane/dev/server-cert.pem` and its PEM private key at
|
|
347
|
+
`.arcane/dev/server-key.pem`, relative to the workspace. Alternatively, pass
|
|
348
|
+
`--cert <file> --key <file>` together. The certificate must cover the LAN IP
|
|
349
|
+
address or hostname opened by each device. Certificate creation and renewal
|
|
350
|
+
belong to the developer's certificate tooling; the server does not generate a
|
|
351
|
+
CA or alter device trust stores. Keep `.arcane/dev/` ignored by Git and keep the
|
|
352
|
+
private key on the development computer.
|
|
353
|
+
|
|
354
|
+
The server reads the selected pair once asynchronously per startup, before
|
|
355
|
+
binding. Missing files or certificate/key parse errors produce a startup error; public
|
|
356
|
+
mode never silently falls back to HTTP. Certificate/key contents are not
|
|
357
|
+
included in operation events or JSON/NDJSON output. Restart the server after
|
|
358
|
+
replacing its certificate pair; ordinary app source edits still appear on
|
|
359
|
+
refresh without restarting.
|
|
360
|
+
|
|
361
|
+
DBOPFS uses [OPFS, which requires a secure context](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/getDirectory).
|
|
362
|
+
HTTP localhost is a special case on the device running the server; a LAN HTTP
|
|
363
|
+
address does not receive that exception. For HTTPS issued by a development CA,
|
|
364
|
+
each accessing device must trust that CA and use an address covered by the
|
|
365
|
+
server certificate.
|
|
366
|
+
|
|
367
|
+
On Android Chrome, transfer only the public CA certificate to the phone. In
|
|
368
|
+
Android Settings, open the security settings, then **Encryption & credentials
|
|
369
|
+
> Install a certificate > CA certificate**, and select that public certificate.
|
|
370
|
+
Samsung devices may label the entry **Install from device storage**. Menu names
|
|
371
|
+
vary by device; Google's [Android CA installation instructions](https://support.google.com/device-usage-study-help/answer/15713321?co=GENIE.Platform%3DAndroid&hl=en)
|
|
372
|
+
show these paths. Then open the printed HTTPS network URL in Chrome. The CA
|
|
373
|
+
installation is a device action; server readiness does not prove Android trust
|
|
374
|
+
or remote reachability.
|
|
340
375
|
|
|
341
376
|
### Example
|
|
342
377
|
|
|
@@ -165,7 +165,8 @@ receive a **Copy GPU flag address** button for the matching browser,
|
|
|
165
165
|
including `chrome://flags/#force-high-performance-gpu` for identified Chrome.
|
|
166
166
|
Both controls remain available regardless of API availability, adapter class,
|
|
167
167
|
or detection failure. Each flag has one short explanation, followed by a shared
|
|
168
|
-
paste, enable, save,
|
|
168
|
+
instruction: paste the address, enable the flag, save work, then close the
|
|
169
|
+
browser and reopen it. The closing and reopening instruction is underlined and emphasized.
|
|
169
170
|
Settings addresses use Copy controls and appear as selectable text if copying
|
|
170
171
|
fails. The component does not attempt navigation to internal browser pages.
|
|
171
172
|
|
|
@@ -210,7 +211,7 @@ explicitly and invoke it from a user action. It returns a promise resolving to
|
|
|
210
211
|
`true` only after clipboard success, or `false` for destruction, an unsupported
|
|
211
212
|
target, or copy failure. The complete address remains selectable if clipboard
|
|
212
213
|
access fails. On-screen instructions explain pasting it into the address bar,
|
|
213
|
-
enabling WebNN, and
|
|
214
|
+
enabling WebNN, saving work, then closing the browser and reopening it.
|
|
214
215
|
|
|
215
216
|
`destroy()` aborts owned listeners, disposes the event source, marks `ready`
|
|
216
217
|
false, and suppresses UI updates from pending clipboard and adapter operations.
|
|
@@ -3461,17 +3461,30 @@ async startDevServer(options={})
|
|
|
3461
3461
|
|
|
3462
3462
|
Import it from `arcane-os`. Source mode accepts
|
|
3463
3463
|
`{workspaceRoot=process.cwd(), appId, mode='source', host='127.0.0.1', port=0,
|
|
3464
|
-
signal, onEvent}` and serves one validated
|
|
3465
|
-
complete SDK or integrated runtime. Packaged mode uses
|
|
3464
|
+
https=false, certPath, keyPath, tls, signal, onEvent}` and serves one validated
|
|
3465
|
+
workspace application plus its complete SDK or integrated runtime. Packaged mode uses
|
|
3466
3466
|
`{mode:'packaged', releaseRoot, host, port, signal, onEvent}` and serves the
|
|
3467
3467
|
complete selected release files. `host` defaults to `127.0.0.1` and accepts an
|
|
3468
3468
|
explicit network address or hostname. Use `0.0.0.0` for all IPv4 interfaces or
|
|
3469
3469
|
`::` for the platform's IPv6 wildcard listener; port `0` asks the operating
|
|
3470
3470
|
system for an available port.
|
|
3471
3471
|
|
|
3472
|
+
`https:true` reads `.arcane/dev/server-cert.pem` and
|
|
3473
|
+
`.arcane/dev/server-key.pem` relative to `workspaceRoot` unless explicit
|
|
3474
|
+
`certPath` and `keyPath` are supplied together. The path pair also selects
|
|
3475
|
+
HTTPS without `https:true`; relative paths resolve from the workspace. A
|
|
3476
|
+
direct `tls` object instead supplies Node HTTPS server options, including
|
|
3477
|
+
`cert` and `key`, without reading certificate files. Keep private material
|
|
3478
|
+
server-side. Missing PEM files and Node certificate/key parse errors reject
|
|
3479
|
+
startup without falling back to HTTP. Node owns TLS option handling and the
|
|
3480
|
+
handshake; browser trust and address matching are evaluated when a client
|
|
3481
|
+
connects. The CLI's `--public` selects HTTPS and the wildcard bind;
|
|
3482
|
+
the API's `host` option alone changes only the bind address.
|
|
3483
|
+
|
|
3472
3484
|
The promise settles after the listener is ready and resolves to
|
|
3473
|
-
`{server, mode, workspaceRoot, appId, host, port, origin, cleanUrl, url,
|
|
3474
|
-
networkUrls, close, closed, lifecycle}`. `server` is the raw Node HTTP
|
|
3485
|
+
`{server, protocol, mode, workspaceRoot, appId, host, port, origin, cleanUrl, url,
|
|
3486
|
+
networkUrls, close, closed, lifecycle}`. `server` is the raw Node HTTP or HTTPS
|
|
3487
|
+
server; `protocol` is `'http:'` or `'https:'`.
|
|
3475
3488
|
`url` and `cleanUrl` are the same application URL. Wildcard listeners use
|
|
3476
3489
|
`localhost` in that local URL; `host` retains the actual bound address.
|
|
3477
3490
|
`networkUrls` lists application URLs for applicable non-loopback interface
|
|
@@ -3480,6 +3493,12 @@ not evidence of reachability from another device. The server adds no session
|
|
|
3480
3493
|
capability or authentication. In packaged mode, `workspaceRoot` and `appId`
|
|
3481
3494
|
are `null`.
|
|
3482
3495
|
|
|
3496
|
+
All returned application URLs use the selected transport's scheme. The server
|
|
3497
|
+
reads one PEM pair per startup and does not create certificates or modify trust
|
|
3498
|
+
stores. Each client must trust the issuing CA and open an address covered by the
|
|
3499
|
+
server certificate. Lifecycle events and CLI summaries exclude TLS options and
|
|
3500
|
+
private key contents. See [development HTTPS setup](cli.md#development-https-setup).
|
|
3501
|
+
|
|
3483
3502
|
Starting the server opens the selected listener and emits awaited,
|
|
3484
3503
|
backpressured `server.starting` and `server.started` events. Request failures
|
|
3485
3504
|
emit `server.request.failed`; shutdown emits `server.stopped` after owned
|
|
@@ -3881,6 +3900,11 @@ async function usedescribeTargets(...arguments_) {
|
|
|
3881
3900
|
|
|
3882
3901
|
Starts one owned browser development server for the selected application.
|
|
3883
3902
|
|
|
3903
|
+
`https`, `certPath`, `keyPath`, and `tls` follow the
|
|
3904
|
+
[`startDevServer()` TLS contract](#startdevserver), alongside `host` and `port`.
|
|
3905
|
+
The operation refreshes the selected app's managed import maps once, then owns
|
|
3906
|
+
one source listener and returns its protocol, URLs, and shutdown lifecycle.
|
|
3907
|
+
|
|
3884
3908
|
### Signature and result
|
|
3885
3909
|
|
|
3886
3910
|
```text
|
package/package.json
CHANGED
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
<p>“Force High Performance GPU” prefers your faster GPU.</p>
|
|
62
62
|
<button id="copyGpuAddress" class="arcane-button arcane-button--secondary" type="button">Copy GPU flag address</button>
|
|
63
63
|
</div>
|
|
64
|
-
<p>Paste a copied address into your browser's address bar. Enable the flag, save your work, and
|
|
64
|
+
<p>Paste a copied address into your browser's address bar. Enable the flag, save your work, <u><em>then close the browser and reopen it</em></u>.</p>
|
|
65
65
|
<details>
|
|
66
66
|
<summary>Setup help</summary>
|
|
67
67
|
<p><a href="https://onnxruntime.ai/docs/tutorials/web/ep-webnn.html">WebNN setup documentation</a></p>
|
package/src/cli/main.mjs
CHANGED
|
@@ -18,6 +18,8 @@ const VALUE_OPTIONS=new Set([
|
|
|
18
18
|
'arcane-root',
|
|
19
19
|
'host',
|
|
20
20
|
'port',
|
|
21
|
+
'cert',
|
|
22
|
+
'key',
|
|
21
23
|
'target',
|
|
22
24
|
'format',
|
|
23
25
|
'signing',
|
|
@@ -36,6 +38,7 @@ const VALUE_OPTIONS=new Set([
|
|
|
36
38
|
const FLAG_OPTIONS=new Set([
|
|
37
39
|
'git',
|
|
38
40
|
'public',
|
|
41
|
+
'https',
|
|
39
42
|
'skip-tests',
|
|
40
43
|
'dry-run',
|
|
41
44
|
'require-local-ai',
|
|
@@ -61,7 +64,7 @@ Usage:
|
|
|
61
64
|
${CLI_NAME} upgrade [--workspace <directory>] [--app <id>]
|
|
62
65
|
${CLI_NAME} doctor [--workspace <directory>] [--arcane-root <directory>]
|
|
63
66
|
${CLI_NAME} import-map [--workspace <directory>] [--app <id>]
|
|
64
|
-
${CLI_NAME} dev [--app <id>] [--public] [--host <address>] [--port 8000] [--sdk-runtime-source <sdk-root>]
|
|
67
|
+
${CLI_NAME} dev [--app <id>] [--public] [--https] [--cert <pem>] [--key <pem>] [--host <address>] [--port 8000] [--sdk-runtime-source <sdk-root>]
|
|
65
68
|
${CLI_NAME} test [--app <id>] [--scope app]
|
|
66
69
|
${CLI_NAME} test --scope shared --test-file <repo-relative.test.mjs>
|
|
67
70
|
${CLI_NAME} check [--app <id>] [--scope app] [--skip-tests]
|
|
@@ -84,7 +87,9 @@ Usage:
|
|
|
84
87
|
${CLI_NAME} mail serve --profile <profile> --from <address> --app <id> --origin <origin> [--allow-to <addresses>] [--app-key-stdin] [--host 127.0.0.1] [--port 8025] [--request-timeout <ms>]
|
|
85
88
|
|
|
86
89
|
Development:
|
|
87
|
-
--public
|
|
90
|
+
--public Serve HTTPS on all IPv4 interfaces (0.0.0.0) and print network URLs.
|
|
91
|
+
--https Use HTTPS with .arcane/dev/server-cert.pem and server-key.pem.
|
|
92
|
+
--cert <pem> --key <pem> Use an existing certificate pair; paths are relative to the workspace.
|
|
88
93
|
--host <address> Override the bind address; takes precedence over --public.
|
|
89
94
|
--sdk-runtime-source <sdk-root> Dev-only live SDK checkout; omitted preserves the workspace runtime mode.
|
|
90
95
|
|
|
@@ -451,6 +456,9 @@ function operationOptions(command,parsed,cwd){
|
|
|
451
456
|
if(flags.has('public')&&command!=='dev'){
|
|
452
457
|
usage('--public is supported only by dev.');
|
|
453
458
|
}
|
|
459
|
+
if((flags.has('https')||values.cert!==undefined||values.key!==undefined)&&command!=='dev'){
|
|
460
|
+
usage('--https, --cert, and --key are supported only by dev.');
|
|
461
|
+
}
|
|
454
462
|
if(flags.has('overwrite')&&command!=='bundle'){
|
|
455
463
|
usage('--overwrite is supported only by bundle.');
|
|
456
464
|
}
|
|
@@ -510,10 +518,18 @@ function operationOptions(command,parsed,cwd){
|
|
|
510
518
|
}
|
|
511
519
|
if(command==='dev'){
|
|
512
520
|
noExtraPositionals(command,positionals);
|
|
521
|
+
if((values.cert===undefined)!==(values.key===undefined)){
|
|
522
|
+
usage('HTTPS development requires --cert and --key together.');
|
|
523
|
+
}
|
|
513
524
|
return {
|
|
514
525
|
...common,
|
|
515
526
|
host:values.host??(flags.has('public')?'0.0.0.0':'127.0.0.1'),
|
|
516
527
|
port:readPort(values.port,8000),
|
|
528
|
+
https:flags.has('public')||flags.has('https')||values.cert!==undefined,
|
|
529
|
+
...(values.cert===undefined?{}:{
|
|
530
|
+
certPath:path.resolve(workspaceRoot,values.cert),
|
|
531
|
+
keyPath:path.resolve(workspaceRoot,values.key)
|
|
532
|
+
}),
|
|
517
533
|
...(values['sdk-runtime-source']===undefined?{}:{
|
|
518
534
|
sdkRuntimeSourceRoot:path.resolve(cwd,values['sdk-runtime-source'])
|
|
519
535
|
})
|
|
@@ -837,6 +853,7 @@ function serverSummary(result){
|
|
|
837
853
|
host:result.host,
|
|
838
854
|
port:result.port,
|
|
839
855
|
url:result.url,
|
|
856
|
+
...(result.protocol===undefined?{}:{protocol:result.protocol}),
|
|
840
857
|
...(result.networkUrls===undefined?{}:{networkUrls:result.networkUrls}),
|
|
841
858
|
...(result.callerAuthentication
|
|
842
859
|
?{callerAuthentication:result.callerAuthentication}
|
package/src/dev-server.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import Is from 'strong-type';
|
|
|
2
2
|
import {constants as FS_CONSTANTS} from 'node:fs';
|
|
3
3
|
import {lstat,open,readFile,realpath} from 'node:fs/promises';
|
|
4
4
|
import http from 'node:http';
|
|
5
|
+
import https from 'node:https';
|
|
5
6
|
import os from 'node:os';
|
|
6
7
|
import path from 'node:path';
|
|
7
8
|
import {resolveWorkspace} from './workspace.mjs';
|
|
@@ -494,7 +495,7 @@ function browserHostname(host){
|
|
|
494
495
|
return host.includes(':')?`[${host}]`:host;
|
|
495
496
|
}
|
|
496
497
|
|
|
497
|
-
function networkUrlsForAddress(address,startPath){
|
|
498
|
+
function networkUrlsForAddress(address,startPath,protocol){
|
|
498
499
|
if(address.address==='127.0.0.1'||address.address==='::1')return [];
|
|
499
500
|
const allIPv4=address.address==='0.0.0.0';
|
|
500
501
|
const allInterfaces=address.address==='::';
|
|
@@ -507,12 +508,49 @@ function networkUrlsForAddress(address,startPath){
|
|
|
507
508
|
if(entry.family==='IPv6'&&entry.scopeid)continue;
|
|
508
509
|
if(allIPv4&&entry.family!=='IPv4')continue;
|
|
509
510
|
if(!allIPv4&&!allInterfaces&&entry.address!==address.address)continue;
|
|
510
|
-
urls.add(
|
|
511
|
+
urls.add(`${protocol}//${browserHostname(entry.address)}:${address.port}${startPath}`);
|
|
511
512
|
}
|
|
512
513
|
}
|
|
513
514
|
return [...urls];
|
|
514
515
|
}
|
|
515
516
|
|
|
517
|
+
async function resolveDevelopmentTls({workspaceRoot,https:useHttps,tls,certPath,keyPath,signal}){
|
|
518
|
+
if(tls!==undefined&&tls!==null&&tls!==false){
|
|
519
|
+
if(!is.object(tls)||is.array(tls)){
|
|
520
|
+
fail('Development server tls must be a Node HTTPS options object.','ARCANE_USAGE');
|
|
521
|
+
}
|
|
522
|
+
return {options:tls};
|
|
523
|
+
}
|
|
524
|
+
if(certPath===undefined&&keyPath===undefined&&!useHttps)return undefined;
|
|
525
|
+
if((certPath===undefined)!==(keyPath===undefined)){
|
|
526
|
+
fail('HTTPS development requires both certPath and keyPath when either is supplied.','ARCANE_USAGE');
|
|
527
|
+
}
|
|
528
|
+
const certificatePath=path.resolve(workspaceRoot,certPath??'.arcane/dev/server-cert.pem');
|
|
529
|
+
const privateKeyPath=path.resolve(workspaceRoot,keyPath??'.arcane/dev/server-key.pem');
|
|
530
|
+
try{
|
|
531
|
+
const reads=await Promise.allSettled([
|
|
532
|
+
readFile(certificatePath,{signal}),
|
|
533
|
+
readFile(privateKeyPath,{signal})
|
|
534
|
+
]);
|
|
535
|
+
for(const read of reads){
|
|
536
|
+
if(read.status==='rejected')throw read.reason;
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
options:{cert:reads[0].value,key:reads[1].value},
|
|
540
|
+
privateKeyPath:await realpath(privateKeyPath)
|
|
541
|
+
};
|
|
542
|
+
}catch(error){
|
|
543
|
+
if(error.code==='ENOENT'){
|
|
544
|
+
fail(
|
|
545
|
+
`HTTPS development requires a certificate at ${certificatePath} and a key at ${privateKeyPath}. `
|
|
546
|
+
+'Provide that pair or select existing files with --cert and --key.',
|
|
547
|
+
'ARCANE_DEV_TLS_MISSING'
|
|
548
|
+
);
|
|
549
|
+
}
|
|
550
|
+
throw error;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
516
554
|
async function startOwnedDevServer({
|
|
517
555
|
workspaceRoot=process.cwd(),
|
|
518
556
|
appId,
|
|
@@ -520,6 +558,10 @@ async function startOwnedDevServer({
|
|
|
520
558
|
releaseRoot,
|
|
521
559
|
host='127.0.0.1',
|
|
522
560
|
port=0,
|
|
561
|
+
https:useHttps=false,
|
|
562
|
+
tls,
|
|
563
|
+
certPath,
|
|
564
|
+
keyPath,
|
|
523
565
|
signal,
|
|
524
566
|
sdkRuntimeSourceRoot
|
|
525
567
|
}={},events,releaseSignal){
|
|
@@ -543,6 +585,11 @@ async function startOwnedDevServer({
|
|
|
543
585
|
appId,
|
|
544
586
|
...(requestedRuntimeMode?{runtimeMode:requestedRuntimeMode}:{})
|
|
545
587
|
});
|
|
588
|
+
const selectedTls=await resolveDevelopmentTls({
|
|
589
|
+
workspaceRoot,https:useHttps,tls,certPath,keyPath,signal
|
|
590
|
+
});
|
|
591
|
+
const protocol=selectedTls?'https:':'http:';
|
|
592
|
+
throwIfAborted(signal);
|
|
546
593
|
const routeSet=mode==='source'
|
|
547
594
|
?await sourceRoutes(workspaceRoot,appId,{
|
|
548
595
|
sdkRuntimeSourceRoot,
|
|
@@ -570,7 +617,7 @@ async function startOwnedDevServer({
|
|
|
570
617
|
const resourcePaths=new Set([routeSet.startPath]);
|
|
571
618
|
const requestTasks=new Set();
|
|
572
619
|
const runFileWork=createFileWorkLimiter();
|
|
573
|
-
|
|
620
|
+
function serveDevelopmentRequest(request,response){
|
|
574
621
|
let task;
|
|
575
622
|
task=(async()=>{
|
|
576
623
|
if(request.method!=='GET'&&request.method!=='HEAD'){
|
|
@@ -595,6 +642,11 @@ async function startOwnedDevServer({
|
|
|
595
642
|
await runFileWork(async()=>{
|
|
596
643
|
const opened=await openSafeFile(mapping.root,relative);
|
|
597
644
|
if(!opened){deny(response,404,'Not found.');return;}
|
|
645
|
+
if(selectedTls?.privateKeyPath
|
|
646
|
+
&&canonicalLocationKey(opened.candidate)===canonicalLocationKey(selectedTls.privateKeyPath)){
|
|
647
|
+
deny(response,404,'Not found.');
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
598
650
|
const extension=path.extname(opened.candidate).toLowerCase();
|
|
599
651
|
const html=extension==='.html'||extension==='.htm';
|
|
600
652
|
let managedDocument=false;
|
|
@@ -648,7 +700,10 @@ async function startOwnedDevServer({
|
|
|
648
700
|
requestTasks.delete(task);
|
|
649
701
|
});
|
|
650
702
|
requestTasks.add(task);
|
|
651
|
-
}
|
|
703
|
+
}
|
|
704
|
+
const server=selectedTls
|
|
705
|
+
?https.createServer(selectedTls.options,serveDevelopmentRequest)
|
|
706
|
+
:http.createServer(serveDevelopmentRequest);
|
|
652
707
|
await listen(server,{host,port,signal});
|
|
653
708
|
const address=server.address();
|
|
654
709
|
if(!address||is.string(address)){
|
|
@@ -658,7 +713,7 @@ async function startOwnedDevServer({
|
|
|
658
713
|
const visibleHost=address.address==='0.0.0.0'||address.address==='::'
|
|
659
714
|
?'localhost'
|
|
660
715
|
:browserHostname(address.address);
|
|
661
|
-
const endpoint=new URL(
|
|
716
|
+
const endpoint=new URL(`${protocol}//${visibleHost}:${address.port}`);
|
|
662
717
|
const origin=endpoint.origin;
|
|
663
718
|
const cleanUrl=`${origin}${routeSet.startPath}`;
|
|
664
719
|
const url=cleanUrl;
|
|
@@ -742,6 +797,7 @@ async function startOwnedDevServer({
|
|
|
742
797
|
server.once('close',()=>{void finishLifecycle();});
|
|
743
798
|
const result={
|
|
744
799
|
server,
|
|
800
|
+
protocol,
|
|
745
801
|
mode,
|
|
746
802
|
workspaceRoot:routeSet.workspaceRoot,
|
|
747
803
|
appId:routeSet.appId,
|
|
@@ -760,9 +816,10 @@ async function startOwnedDevServer({
|
|
|
760
816
|
lifecycle
|
|
761
817
|
};
|
|
762
818
|
try{
|
|
763
|
-
result.networkUrls=networkUrlsForAddress(address,routeSet.startPath);
|
|
819
|
+
result.networkUrls=networkUrlsForAddress(address,routeSet.startPath,protocol);
|
|
764
820
|
await events.send({
|
|
765
821
|
type:'server.started',
|
|
822
|
+
protocol,
|
|
766
823
|
mode,
|
|
767
824
|
host:result.host,
|
|
768
825
|
port:result.port,
|
package/src/toolchain.mjs
CHANGED
|
@@ -466,6 +466,10 @@ export async function developApplication(options={}){
|
|
|
466
466
|
}),
|
|
467
467
|
host:options.host,
|
|
468
468
|
port:options.port,
|
|
469
|
+
https:options.https,
|
|
470
|
+
tls:options.tls,
|
|
471
|
+
certPath:options.certPath,
|
|
472
|
+
keyPath:options.keyPath,
|
|
469
473
|
signal:options.signal,
|
|
470
474
|
onEvent:options.onEvent
|
|
471
475
|
});
|