arcane-os 0.7.1 → 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 +21 -0
- package/README.md +23 -15
- package/docs/architecture.md +11 -1
- package/docs/reference/ai/browser-speech.md +9 -7
- package/docs/reference/cli.md +42 -7
- package/docs/reference/inventory/runtime-components.json +2 -2
- package/docs/reference/runtime-components.md +10 -8
- package/docs/reference/sdk-api.md +28 -4
- package/package.json +1 -1
- package/runtime/arcane/components/browser-ai-setup.html +4 -14
- 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,26 @@
|
|
|
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
|
+
|
|
17
|
+
## 0.7.2
|
|
18
|
+
|
|
19
|
+
- Keep both supported browser NPU and GPU flag address copy controls available
|
|
20
|
+
regardless of API availability, GPU classification, or detection failure.
|
|
21
|
+
Use one short explanation per flag and one shared paste/enable/relaunch
|
|
22
|
+
instruction. Preserve browser-reported statuses and the GPU-only chat notice.
|
|
23
|
+
|
|
3
24
|
## 0.7.1
|
|
4
25
|
|
|
5
26
|
- Make Profile NPU and GPU setup concise and copy-only, with **Copy NPU flag
|
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
|
|
@@ -144,8 +144,8 @@ name. It does not load a model or create a GPU device or WebNN context:
|
|
|
144
144
|
</html-import>
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
**NPU setup** provides a **Copy NPU flag address** button regardless of WebNN
|
|
148
|
+
availability, with one short explanation. Chrome uses
|
|
149
149
|
`chrome://flags/#web-machine-learning-neural-network`; Edge uses
|
|
150
150
|
`edge://flags/#web-machine-learning-neural-network`. Unrecognized browsers
|
|
151
151
|
receive explicit Chrome and Edge choices. A failed copy shows the complete
|
|
@@ -153,9 +153,11 @@ address as selectable text; the component does not open internal browser pages.
|
|
|
153
153
|
The [ONNX Runtime WebNN guide](https://onnxruntime.ai/docs/tutorials/web/ep-webnn.html)
|
|
154
154
|
documents the **Enables WebNN API** flag and model/operator requirements.
|
|
155
155
|
|
|
156
|
-
The **GPU performance** section shows a short adapter status
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
The **GPU performance** section shows a short adapter status and a **Copy GPU
|
|
157
|
+
flag address** button on desktop Windows Chromium browsers. The button remains
|
|
158
|
+
available for every adapter class, unavailable API, or detection failure. One
|
|
159
|
+
shared instruction explains pasting, enabling, saving work, then closing the
|
|
160
|
+
browser and reopening it, with the final step underlined and emphasized. Identified
|
|
159
161
|
Chrome uses `chrome://flags/#force-high-performance-gpu`; other recognized
|
|
160
162
|
Chromium browsers use their corresponding internal scheme. **Refresh**
|
|
161
163
|
requests a new availability result; concurrent
|
|
@@ -166,9 +168,9 @@ settings update. Component readiness does not wait for adapter detection.
|
|
|
166
168
|
Adapter selection uses `powerPreference: "high-performance"` as a hint, not proof
|
|
167
169
|
of the selected GPU's performance or the flag's current state. When the browser
|
|
168
170
|
explicitly reports a discrete GPU, Profile says **Already using the performance
|
|
169
|
-
GPU.**
|
|
171
|
+
GPU.** Explicit integrated or software/fallback
|
|
170
172
|
metadata is reported as such. Missing type metadata shows **GPU available.**
|
|
171
|
-
|
|
173
|
+
and leaves performance selection unconfirmed; a vendor name is not a GPU
|
|
172
174
|
classification. The component cannot read the browser flag or prove that a
|
|
173
175
|
model is executing on the adapter. See the
|
|
174
176
|
[component contract](../runtime-components.md#browser-ai-setuphtml) for result
|
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
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"repository": "https://github.com/TheWizardNexus/arcane-os-sdk.git",
|
|
6
6
|
"branch": "main",
|
|
7
7
|
"path": "runtime/arcane/components",
|
|
8
|
-
"sdkVersion": "0.7.
|
|
8
|
+
"sdkVersion": "0.7.2"
|
|
9
9
|
},
|
|
10
10
|
"componentCount": 40,
|
|
11
11
|
"loader": "/arcane/modules/HTMLImport.js",
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
],
|
|
99
99
|
"availability": "Browser and supported native WebViews",
|
|
100
100
|
"transport": "HTMLImport + DOM; Clipboard API and WebGPU adapter request. Settings addresses use Copy controls and appear as text if copying fails; open() copies the NPU address without navigation or an alert.",
|
|
101
|
-
"normalization": "API presence, adapter availability, explicit browser GPU class, and copy instructions normalized. A reported discrete GPU shows Already using the performance GPU.
|
|
101
|
+
"normalization": "API presence, adapter availability, explicit browser GPU class, and copy instructions normalized. Supported browser NPU and GPU flag copy controls remain available regardless of API availability, adapter class, or detection failure. A reported discrete GPU shows Already using the performance GPU. Absent type metadata shows GPU available and leaves performance selection unconfirmed. Mounting starts one adapter request; checkGpu() coalesces concurrent calls, while refresh() remains synchronous. No GPU device, model, or WebNN context is created. Adapter metadata does not establish browser flag state, the fastest physical GPU, or another page's model execution."
|
|
102
102
|
},
|
|
103
103
|
{
|
|
104
104
|
"file": "runtime/arcane/components/calculator.html",
|
|
@@ -159,11 +159,14 @@ Slots: `title`, `subtitle`, `identity`, `messages/message`, `composer`, `actions
|
|
|
159
159
|
### Overview
|
|
160
160
|
|
|
161
161
|
Displays a short WebNN availability status and the detected WebGPU adapter.
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
when an integrated or software adapter is explicitly reported,
|
|
162
|
+
Chrome and Edge receive a **Copy NPU flag address** button for their own browser;
|
|
163
|
+
unidentified browsers show both choices. Desktop Windows Chromium browsers
|
|
164
|
+
receive a **Copy GPU flag address** button for the matching browser,
|
|
166
165
|
including `chrome://flags/#force-high-performance-gpu` for identified Chrome.
|
|
166
|
+
Both controls remain available regardless of API availability, adapter class,
|
|
167
|
+
or detection failure. Each flag has one short explanation, followed by a shared
|
|
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.
|
|
167
170
|
Settings addresses use Copy controls and appear as selectable text if copying
|
|
168
171
|
fails. The component does not attempt navigation to internal browser pages.
|
|
169
172
|
|
|
@@ -189,9 +192,8 @@ or `null`, `adapterType` as the browser's string or `null`, and
|
|
|
189
192
|
`performanceStatus`: `"discrete"`, `"integrated"`, `"fallback"`, or `"unknown"`.
|
|
190
193
|
Only the explicit browser type or fallback field establishes that status;
|
|
191
194
|
vendor names and the requested power preference do not. A discrete result
|
|
192
|
-
shows **Already using the performance GPU.**
|
|
193
|
-
|
|
194
|
-
performance selection remains unconfirmed in the result. Adapter selection does not
|
|
195
|
+
shows **Already using the performance GPU.** An unknown class shows **GPU available.**
|
|
196
|
+
and leaves performance selection unconfirmed. Adapter selection does not
|
|
195
197
|
prove that a browser flag is enabled, that the fastest GPU was selected, or that
|
|
196
198
|
a model is using it. Failure is displayed and logged, and the promise
|
|
197
199
|
resolves to `false`; destruction also yields `false` and suppresses late UI
|
|
@@ -209,7 +211,7 @@ explicitly and invoke it from a user action. It returns a promise resolving to
|
|
|
209
211
|
`true` only after clipboard success, or `false` for destruction, an unsupported
|
|
210
212
|
target, or copy failure. The complete address remains selectable if clipboard
|
|
211
213
|
access fails. On-screen instructions explain pasting it into the address bar,
|
|
212
|
-
enabling WebNN, and
|
|
214
|
+
enabling WebNN, saving work, then closing the browser and reopening it.
|
|
213
215
|
|
|
214
216
|
`destroy()` aborts owned listeners, disposes the event source, marks `ready`
|
|
215
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
|
@@ -58,9 +58,10 @@
|
|
|
58
58
|
<p><span id="gpuAvailability" class="arcane-badge gpu-status" role="status" aria-live="polite"></span></p>
|
|
59
59
|
<p id="gpuAdapterName" class="arcane-help"></p>
|
|
60
60
|
<div id="gpuSetup" hidden>
|
|
61
|
-
<p
|
|
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, <u><em>then close the browser and reopen it</em></u>.</p>
|
|
64
65
|
<details>
|
|
65
66
|
<summary>Setup help</summary>
|
|
66
67
|
<p><a href="https://onnxruntime.ai/docs/tutorials/web/ep-webnn.html">WebNN setup documentation</a></p>
|
|
@@ -101,7 +102,6 @@
|
|
|
101
102
|
let copyTask=null;
|
|
102
103
|
let copyingAddress = null;
|
|
103
104
|
let gpuTask=null;
|
|
104
|
-
let gpuResult = null;
|
|
105
105
|
let destroyed=false;
|
|
106
106
|
|
|
107
107
|
host.refresh=refresh;
|
|
@@ -115,10 +115,9 @@
|
|
|
115
115
|
root.querySelector('#webnnAvailability').textContent = settings.webnnAvailable
|
|
116
116
|
? 'WebNN available.'
|
|
117
117
|
: 'WebNN unavailable.';
|
|
118
|
-
root.querySelector('#npuSetup').hidden = settings.webnnAvailable;
|
|
119
118
|
const unknownBrowser = settings.browserId === 'unknown';
|
|
120
119
|
root.querySelector('#browserInstructions').textContent = settings.webnnFlagsURL || unknownBrowser
|
|
121
|
-
? '
|
|
120
|
+
? '“Enables WebNN API” allows NPU use when supported.'
|
|
122
121
|
: 'See Setup help for this browser.';
|
|
123
122
|
for (const choice of choices) {
|
|
124
123
|
choice.row.hidden = !unknownBrowser && choice.target.url !== settings.webnnFlagsURL;
|
|
@@ -129,12 +128,7 @@
|
|
|
129
128
|
}
|
|
130
129
|
|
|
131
130
|
function renderGpuGuidance(settings = getBrowserDeviceSettings()) {
|
|
132
|
-
|
|
133
|
-
const nonperformance = gpuResult?.available && (
|
|
134
|
-
gpuResult.performanceStatus === 'integrated'
|
|
135
|
-
|| gpuResult.performanceStatus === 'fallback'
|
|
136
|
-
);
|
|
137
|
-
root.querySelector('#gpuSetup').hidden = !nonperformance || !gpuTarget;
|
|
131
|
+
root.querySelector('#gpuSetup').hidden = !settings.highPerformanceGpu;
|
|
138
132
|
}
|
|
139
133
|
|
|
140
134
|
function checkGpu() {
|
|
@@ -156,7 +150,6 @@
|
|
|
156
150
|
try {
|
|
157
151
|
const result = await detectBrowserGpu();
|
|
158
152
|
if (destroyed) return false;
|
|
159
|
-
gpuResult = result;
|
|
160
153
|
if (!result.available) {
|
|
161
154
|
gpuAvailability.textContent = 'GPU unavailable.';
|
|
162
155
|
gpuAvailability.dataset.status = 'warning';
|
|
@@ -174,14 +167,11 @@
|
|
|
174
167
|
gpuAvailability.dataset.status = 'unknown';
|
|
175
168
|
}
|
|
176
169
|
gpuAdapterName.textContent = result.available ? result.name : '';
|
|
177
|
-
renderGpuGuidance();
|
|
178
170
|
return result;
|
|
179
171
|
} catch (error) {
|
|
180
172
|
if (!destroyed) {
|
|
181
|
-
gpuResult = null;
|
|
182
173
|
gpuAvailability.textContent = 'GPU detection failed. Select Refresh to retry.';
|
|
183
174
|
gpuAvailability.dataset.status = 'error';
|
|
184
|
-
renderGpuGuidance();
|
|
185
175
|
arcaneLogging.error('Unable to detect the browser GPU adapter.', error);
|
|
186
176
|
}
|
|
187
177
|
return false;
|
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
|
});
|